michael@0: michael@0: /* michael@0: * Copyright 2006 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #ifndef SkTemplates_DEFINED michael@0: #define SkTemplates_DEFINED michael@0: michael@0: #include "SkTypes.h" michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: /** \file SkTemplates.h michael@0: michael@0: This file contains light-weight template classes for type-safe and exception-safe michael@0: resource management. michael@0: */ michael@0: michael@0: /** michael@0: * Marks a local variable as known to be unused (to avoid warnings). michael@0: * Note that this does *not* prevent the local variable from being optimized away. michael@0: */ michael@0: template inline void sk_ignore_unused_variable(const T&) { } michael@0: michael@0: /** michael@0: * SkTIsConst::value is true if the type T is const. michael@0: * The type T is constrained not to be an array or reference type. michael@0: */ michael@0: template struct SkTIsConst { michael@0: static T* t; michael@0: static uint16_t test(const volatile void*); michael@0: static uint32_t test(volatile void *); michael@0: static const bool value = (sizeof(uint16_t) == sizeof(test(t))); michael@0: }; michael@0: michael@0: ///@{ michael@0: /** SkTConstType::type will be 'const T' if CONST is true, 'T' otherwise. */ michael@0: template struct SkTConstType { michael@0: typedef T type; michael@0: }; michael@0: template struct SkTConstType { michael@0: typedef const T type; michael@0: }; michael@0: ///@} michael@0: michael@0: /** michael@0: * Returns a pointer to a D which comes immediately after S[count]. michael@0: */ michael@0: template static D* SkTAfter(S* ptr, size_t count = 1) { michael@0: return reinterpret_cast(ptr + count); michael@0: } michael@0: michael@0: /** michael@0: * Returns a pointer to a D which comes byteOffset bytes after S. michael@0: */ michael@0: template static D* SkTAddOffset(S* ptr, size_t byteOffset) { michael@0: // The intermediate char* has the same const-ness as D as this produces better error messages. michael@0: // This relies on the fact that reinterpret_cast can add constness, but cannot remove it. michael@0: return reinterpret_cast( michael@0: reinterpret_cast::value>::type*>(ptr) + byteOffset michael@0: ); michael@0: } michael@0: michael@0: /** SkTSetBit::value is a T with the Nth bit set. */ michael@0: template struct SkTSetBit { michael@0: static const T value = static_cast(1) << N; michael@0: SK_COMPILE_ASSERT(sizeof(T)*CHAR_BIT > N, SkTSetBit_N_too_large); michael@0: SK_COMPILE_ASSERT(std::numeric_limits::is_integer, SkTSetBit_T_must_be_integer); michael@0: SK_COMPILE_ASSERT(!std::numeric_limits::is_signed, SkTSetBit_T_must_be_unsigned); michael@0: SK_COMPILE_ASSERT(std::numeric_limits::radix == 2, SkTSetBit_T_radix_must_be_2); michael@0: }; michael@0: michael@0: /** \class SkAutoTCallVProc michael@0: michael@0: Call a function when this goes out of scope. The template uses two michael@0: parameters, the object, and a function that is to be called in the destructor. michael@0: If detach() is called, the object reference is set to null. If the object michael@0: reference is null when the destructor is called, we do not call the michael@0: function. michael@0: */ michael@0: template class SkAutoTCallVProc : SkNoncopyable { michael@0: public: michael@0: SkAutoTCallVProc(T* obj): fObj(obj) {} michael@0: ~SkAutoTCallVProc() { if (fObj) P(fObj); } michael@0: T* detach() { T* obj = fObj; fObj = NULL; return obj; } michael@0: private: michael@0: T* fObj; michael@0: }; michael@0: michael@0: /** \class SkAutoTCallIProc michael@0: michael@0: Call a function when this goes out of scope. The template uses two michael@0: parameters, the object, and a function that is to be called in the destructor. michael@0: If detach() is called, the object reference is set to null. If the object michael@0: reference is null when the destructor is called, we do not call the michael@0: function. michael@0: */ michael@0: template class SkAutoTCallIProc : SkNoncopyable { michael@0: public: michael@0: SkAutoTCallIProc(T* obj): fObj(obj) {} michael@0: ~SkAutoTCallIProc() { if (fObj) P(fObj); } michael@0: T* detach() { T* obj = fObj; fObj = NULL; return obj; } michael@0: private: michael@0: T* fObj; michael@0: }; michael@0: michael@0: /** \class SkAutoTDelete michael@0: An SkAutoTDelete is like a T*, except that the destructor of SkAutoTDelete michael@0: automatically deletes the pointer it holds (if any). That is, SkAutoTDelete michael@0: owns the T object that it points to. Like a T*, an SkAutoTDelete may hold michael@0: either NULL or a pointer to a T object. Also like T*, SkAutoTDelete is michael@0: thread-compatible, and once you dereference it, you get the threadsafety michael@0: guarantees of T. michael@0: michael@0: The size of a SkAutoTDelete is small: sizeof(SkAutoTDelete) == sizeof(T*) michael@0: */ michael@0: template class SkAutoTDelete : SkNoncopyable { michael@0: public: michael@0: SkAutoTDelete(T* obj = NULL) : fObj(obj) {} michael@0: ~SkAutoTDelete() { SkDELETE(fObj); } michael@0: michael@0: T* get() const { return fObj; } michael@0: T& operator*() const { SkASSERT(fObj); return *fObj; } michael@0: T* operator->() const { SkASSERT(fObj); return fObj; } michael@0: michael@0: void reset(T* obj) { michael@0: if (fObj != obj) { michael@0: SkDELETE(fObj); michael@0: fObj = obj; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Delete the owned object, setting the internal pointer to NULL. michael@0: */ michael@0: void free() { michael@0: SkDELETE(fObj); michael@0: fObj = NULL; michael@0: } michael@0: michael@0: /** michael@0: * Transfer ownership of the object to the caller, setting the internal michael@0: * pointer to NULL. Note that this differs from get(), which also returns michael@0: * the pointer, but it does not transfer ownership. michael@0: */ michael@0: T* detach() { michael@0: T* obj = fObj; michael@0: fObj = NULL; michael@0: return obj; michael@0: } michael@0: michael@0: void swap(SkAutoTDelete* that) { michael@0: SkTSwap(fObj, that->fObj); michael@0: } michael@0: michael@0: private: michael@0: T* fObj; michael@0: }; michael@0: michael@0: // Calls ~T() in the destructor. michael@0: template class SkAutoTDestroy : SkNoncopyable { michael@0: public: michael@0: SkAutoTDestroy(T* obj = NULL) : fObj(obj) {} michael@0: ~SkAutoTDestroy() { michael@0: if (NULL != fObj) { michael@0: fObj->~T(); michael@0: } michael@0: } michael@0: michael@0: T* get() const { return fObj; } michael@0: T& operator*() const { SkASSERT(fObj); return *fObj; } michael@0: T* operator->() const { SkASSERT(fObj); return fObj; } michael@0: michael@0: private: michael@0: T* fObj; michael@0: }; michael@0: michael@0: template class SkAutoTDeleteArray : SkNoncopyable { michael@0: public: michael@0: SkAutoTDeleteArray(T array[]) : fArray(array) {} michael@0: ~SkAutoTDeleteArray() { SkDELETE_ARRAY(fArray); } michael@0: michael@0: T* get() const { return fArray; } michael@0: void free() { SkDELETE_ARRAY(fArray); fArray = NULL; } michael@0: T* detach() { T* array = fArray; fArray = NULL; return array; } michael@0: michael@0: void reset(T array[]) { michael@0: if (fArray != array) { michael@0: SkDELETE_ARRAY(fArray); michael@0: fArray = array; michael@0: } michael@0: } michael@0: michael@0: private: michael@0: T* fArray; michael@0: }; michael@0: michael@0: /** Allocate an array of T elements, and free the array in the destructor michael@0: */ michael@0: template class SkAutoTArray : SkNoncopyable { michael@0: public: michael@0: SkAutoTArray() { michael@0: fArray = NULL; michael@0: SkDEBUGCODE(fCount = 0;) michael@0: } michael@0: /** Allocate count number of T elements michael@0: */ michael@0: explicit SkAutoTArray(int count) { michael@0: SkASSERT(count >= 0); michael@0: fArray = NULL; michael@0: if (count) { michael@0: fArray = SkNEW_ARRAY(T, count); michael@0: } michael@0: SkDEBUGCODE(fCount = count;) michael@0: } michael@0: michael@0: /** Reallocates given a new count. Reallocation occurs even if new count equals old count. michael@0: */ michael@0: void reset(int count) { michael@0: SkDELETE_ARRAY(fArray); michael@0: SkASSERT(count >= 0); michael@0: fArray = NULL; michael@0: if (count) { michael@0: fArray = SkNEW_ARRAY(T, count); michael@0: } michael@0: SkDEBUGCODE(fCount = count;) michael@0: } michael@0: michael@0: ~SkAutoTArray() { michael@0: SkDELETE_ARRAY(fArray); michael@0: } michael@0: michael@0: /** Return the array of T elements. Will be NULL if count == 0 michael@0: */ michael@0: T* get() const { return fArray; } michael@0: michael@0: /** Return the nth element in the array michael@0: */ michael@0: T& operator[](int index) const { michael@0: SkASSERT((unsigned)index < (unsigned)fCount); michael@0: return fArray[index]; michael@0: } michael@0: michael@0: private: michael@0: T* fArray; michael@0: SkDEBUGCODE(int fCount;) michael@0: }; michael@0: michael@0: /** Wraps SkAutoTArray, with room for up to N elements preallocated michael@0: */ michael@0: template class SkAutoSTArray : SkNoncopyable { michael@0: public: michael@0: /** Initialize with no objects */ michael@0: SkAutoSTArray() { michael@0: fArray = NULL; michael@0: fCount = 0; michael@0: } michael@0: michael@0: /** Allocate count number of T elements michael@0: */ michael@0: SkAutoSTArray(int count) { michael@0: fArray = NULL; michael@0: fCount = 0; michael@0: this->reset(count); michael@0: } michael@0: michael@0: ~SkAutoSTArray() { michael@0: this->reset(0); michael@0: } michael@0: michael@0: /** Destroys previous objects in the array and default constructs count number of objects */ michael@0: void reset(int count) { michael@0: T* start = fArray; michael@0: T* iter = start + fCount; michael@0: while (iter > start) { michael@0: (--iter)->~T(); michael@0: } michael@0: michael@0: if (fCount != count) { michael@0: if (fCount > N) { michael@0: // 'fArray' was allocated last time so free it now michael@0: SkASSERT((T*) fStorage != fArray); michael@0: sk_free(fArray); michael@0: } michael@0: michael@0: if (count > N) { michael@0: fArray = (T*) sk_malloc_throw(count * sizeof(T)); michael@0: } else if (count > 0) { michael@0: fArray = (T*) fStorage; michael@0: } else { michael@0: fArray = NULL; michael@0: } michael@0: michael@0: fCount = count; michael@0: } michael@0: michael@0: iter = fArray; michael@0: T* stop = fArray + count; michael@0: while (iter < stop) { michael@0: SkNEW_PLACEMENT(iter++, T); michael@0: } michael@0: } michael@0: michael@0: /** Return the number of T elements in the array michael@0: */ michael@0: int count() const { return fCount; } michael@0: michael@0: /** Return the array of T elements. Will be NULL if count == 0 michael@0: */ michael@0: T* get() const { return fArray; } michael@0: michael@0: /** Return the nth element in the array michael@0: */ michael@0: T& operator[](int index) const { michael@0: SkASSERT(index < fCount); michael@0: return fArray[index]; michael@0: } michael@0: michael@0: private: michael@0: int fCount; michael@0: T* fArray; michael@0: // since we come right after fArray, fStorage should be properly aligned michael@0: char fStorage[N * sizeof(T)]; michael@0: }; michael@0: michael@0: /** Manages an array of T elements, freeing the array in the destructor. michael@0: * Does NOT call any constructors/destructors on T (T must be POD). michael@0: */ michael@0: template class SkAutoTMalloc : SkNoncopyable { michael@0: public: michael@0: /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */ michael@0: explicit SkAutoTMalloc(T* ptr = NULL) { michael@0: fPtr = ptr; michael@0: } michael@0: michael@0: /** Allocates space for 'count' Ts. */ michael@0: explicit SkAutoTMalloc(size_t count) { michael@0: fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); michael@0: } michael@0: michael@0: ~SkAutoTMalloc() { michael@0: sk_free(fPtr); michael@0: } michael@0: michael@0: /** Resize the memory area pointed to by the current ptr preserving contents. */ michael@0: void realloc(size_t count) { michael@0: fPtr = reinterpret_cast(sk_realloc_throw(fPtr, count * sizeof(T))); michael@0: } michael@0: michael@0: /** Resize the memory area pointed to by the current ptr without preserving contents. */ michael@0: void reset(size_t count) { michael@0: sk_free(fPtr); michael@0: fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); michael@0: } michael@0: michael@0: T* get() const { return fPtr; } michael@0: michael@0: operator T*() { michael@0: return fPtr; michael@0: } michael@0: michael@0: operator const T*() const { michael@0: return fPtr; michael@0: } michael@0: michael@0: T& operator[](int index) { michael@0: return fPtr[index]; michael@0: } michael@0: michael@0: const T& operator[](int index) const { michael@0: return fPtr[index]; michael@0: } michael@0: michael@0: /** michael@0: * Transfer ownership of the ptr to the caller, setting the internal michael@0: * pointer to NULL. Note that this differs from get(), which also returns michael@0: * the pointer, but it does not transfer ownership. michael@0: */ michael@0: T* detach() { michael@0: T* ptr = fPtr; michael@0: fPtr = NULL; michael@0: return ptr; michael@0: } michael@0: michael@0: private: michael@0: T* fPtr; michael@0: }; michael@0: michael@0: template class SkAutoSTMalloc : SkNoncopyable { michael@0: public: michael@0: SkAutoSTMalloc() { michael@0: fPtr = NULL; michael@0: } michael@0: michael@0: SkAutoSTMalloc(size_t count) { michael@0: if (count > N) { michael@0: fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); michael@0: } else if (count) { michael@0: fPtr = fTStorage; michael@0: } else { michael@0: fPtr = NULL; michael@0: } michael@0: } michael@0: michael@0: ~SkAutoSTMalloc() { michael@0: if (fPtr != fTStorage) { michael@0: sk_free(fPtr); michael@0: } michael@0: } michael@0: michael@0: // doesn't preserve contents michael@0: T* reset(size_t count) { michael@0: if (fPtr != fTStorage) { michael@0: sk_free(fPtr); michael@0: } michael@0: if (count > N) { michael@0: fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); michael@0: } else if (count) { michael@0: fPtr = fTStorage; michael@0: } else { michael@0: fPtr = NULL; michael@0: } michael@0: return fPtr; michael@0: } michael@0: michael@0: T* get() const { return fPtr; } michael@0: michael@0: operator T*() { michael@0: return fPtr; michael@0: } michael@0: michael@0: operator const T*() const { michael@0: return fPtr; michael@0: } michael@0: michael@0: T& operator[](int index) { michael@0: return fPtr[index]; michael@0: } michael@0: michael@0: const T& operator[](int index) const { michael@0: return fPtr[index]; michael@0: } michael@0: michael@0: private: michael@0: T* fPtr; michael@0: union { michael@0: uint32_t fStorage32[(N*sizeof(T) + 3) >> 2]; michael@0: T fTStorage[1]; // do NOT want to invoke T::T() michael@0: }; michael@0: }; michael@0: michael@0: /** michael@0: * Reserves memory that is aligned on double and pointer boundaries. michael@0: * Hopefully this is sufficient for all practical purposes. michael@0: */ michael@0: template class SkAlignedSStorage : SkNoncopyable { michael@0: public: michael@0: void* get() { return fData; } michael@0: private: michael@0: union { michael@0: void* fPtr; michael@0: double fDouble; michael@0: char fData[N]; michael@0: }; michael@0: }; michael@0: michael@0: /** michael@0: * Reserves memory that is aligned on double and pointer boundaries. michael@0: * Hopefully this is sufficient for all practical purposes. Otherwise, michael@0: * we have to do some arcane trickery to determine alignment of non-POD michael@0: * types. Lifetime of the memory is the lifetime of the object. michael@0: */ michael@0: template class SkAlignedSTStorage : SkNoncopyable { michael@0: public: michael@0: /** michael@0: * Returns void* because this object does not initialize the michael@0: * memory. Use placement new for types that require a cons. michael@0: */ michael@0: void* get() { return fStorage.get(); } michael@0: private: michael@0: SkAlignedSStorage fStorage; michael@0: }; michael@0: michael@0: #endif