123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- /*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #ifndef SkTemplates_DEFINED
- #define SkTemplates_DEFINED
- #include "include/core/SkTypes.h"
- #include "include/private/SkMalloc.h"
- #include "include/private/SkTLogic.h"
- #include <string.h>
- #include <array>
- #include <cstddef>
- #include <memory>
- #include <new>
- #include <utility>
- /** \file SkTemplates.h
- This file contains light-weight template classes for type-safe and exception-safe
- resource management.
- */
- /**
- * Marks a local variable as known to be unused (to avoid warnings).
- * Note that this does *not* prevent the local variable from being optimized away.
- */
- template<typename T> inline void sk_ignore_unused_variable(const T&) { }
- /**
- * Returns a pointer to a D which comes immediately after S[count].
- */
- template <typename D, typename S> static D* SkTAfter(S* ptr, size_t count = 1) {
- return reinterpret_cast<D*>(ptr + count);
- }
- /**
- * Returns a pointer to a D which comes byteOffset bytes after S.
- */
- template <typename D, typename S> static D* SkTAddOffset(S* ptr, size_t byteOffset) {
- // The intermediate char* has the same cv-ness as D as this produces better error messages.
- // This relies on the fact that reinterpret_cast can add constness, but cannot remove it.
- return reinterpret_cast<D*>(reinterpret_cast<sknonstd::same_cv_t<char, D>*>(ptr) + byteOffset);
- }
- template <typename R, typename T, R (*P)(T*)> struct SkFunctionWrapper {
- R operator()(T* t) { return P(t); }
- };
- /** \class SkAutoTCallVProc
- Call a function when this goes out of scope. The template uses two
- parameters, the object, and a function that is to be called in the destructor.
- If release() is called, the object reference is set to null. If the object
- reference is null when the destructor is called, we do not call the
- function.
- */
- template <typename T, void (*P)(T*)> class SkAutoTCallVProc
- : public std::unique_ptr<T, SkFunctionWrapper<void, T, P>> {
- public:
- SkAutoTCallVProc(T* obj): std::unique_ptr<T, SkFunctionWrapper<void, T, P>>(obj) {}
- operator T*() const { return this->get(); }
- };
- /** Allocate an array of T elements, and free the array in the destructor
- */
- template <typename T> class SkAutoTArray {
- public:
- SkAutoTArray() {}
- /** Allocate count number of T elements
- */
- explicit SkAutoTArray(int count) {
- SkASSERT(count >= 0);
- if (count) {
- fArray.reset(new T[count]);
- }
- SkDEBUGCODE(fCount = count;)
- }
- SkAutoTArray(SkAutoTArray&& other) : fArray(std::move(other.fArray)) {
- SkDEBUGCODE(fCount = other.fCount; other.fCount = 0;)
- }
- SkAutoTArray& operator=(SkAutoTArray&& other) {
- if (this != &other) {
- fArray = std::move(other.fArray);
- SkDEBUGCODE(fCount = other.fCount; other.fCount = 0;)
- }
- return *this;
- }
- /** Reallocates given a new count. Reallocation occurs even if new count equals old count.
- */
- void reset(int count) { *this = SkAutoTArray(count); }
- /** Return the array of T elements. Will be NULL if count == 0
- */
- T* get() const { return fArray.get(); }
- /** Return the nth element in the array
- */
- T& operator[](int index) const {
- SkASSERT((unsigned)index < (unsigned)fCount);
- return fArray[index];
- }
- private:
- std::unique_ptr<T[]> fArray;
- SkDEBUGCODE(int fCount = 0;)
- };
- /** Wraps SkAutoTArray, with room for kCountRequested elements preallocated.
- */
- template <int kCountRequested, typename T> class SkAutoSTArray {
- public:
- SkAutoSTArray(SkAutoSTArray&&) = delete;
- SkAutoSTArray(const SkAutoSTArray&) = delete;
- SkAutoSTArray& operator=(SkAutoSTArray&&) = delete;
- SkAutoSTArray& operator=(const SkAutoSTArray&) = delete;
- /** Initialize with no objects */
- SkAutoSTArray() {
- fArray = nullptr;
- fCount = 0;
- }
- /** Allocate count number of T elements
- */
- SkAutoSTArray(int count) {
- fArray = nullptr;
- fCount = 0;
- this->reset(count);
- }
- ~SkAutoSTArray() {
- this->reset(0);
- }
- /** Destroys previous objects in the array and default constructs count number of objects */
- void reset(int count) {
- T* start = fArray;
- T* iter = start + fCount;
- while (iter > start) {
- (--iter)->~T();
- }
- SkASSERT(count >= 0);
- if (fCount != count) {
- if (fCount > kCount) {
- // 'fArray' was allocated last time so free it now
- SkASSERT((T*) fStorage != fArray);
- sk_free(fArray);
- }
- if (count > kCount) {
- fArray = (T*) sk_malloc_throw(count, sizeof(T));
- } else if (count > 0) {
- fArray = (T*) fStorage;
- } else {
- fArray = nullptr;
- }
- fCount = count;
- }
- iter = fArray;
- T* stop = fArray + count;
- while (iter < stop) {
- new (iter++) T;
- }
- }
- /** Return the number of T elements in the array
- */
- int count() const { return fCount; }
- /** Return the array of T elements. Will be NULL if count == 0
- */
- T* get() const { return fArray; }
- T* begin() { return fArray; }
- const T* begin() const { return fArray; }
- T* end() { return fArray + fCount; }
- const T* end() const { return fArray + fCount; }
- /** Return the nth element in the array
- */
- T& operator[](int index) const {
- SkASSERT(index < fCount);
- return fArray[index];
- }
- private:
- #if defined(SK_BUILD_FOR_GOOGLE3)
- // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions
- // have multiple large stack allocations.
- static const int kMaxBytes = 4 * 1024;
- static const int kCount = kCountRequested * sizeof(T) > kMaxBytes
- ? kMaxBytes / sizeof(T)
- : kCountRequested;
- #else
- static const int kCount = kCountRequested;
- #endif
- int fCount;
- T* fArray;
- // since we come right after fArray, fStorage should be properly aligned
- char fStorage[kCount * sizeof(T)];
- };
- /** Manages an array of T elements, freeing the array in the destructor.
- * Does NOT call any constructors/destructors on T (T must be POD).
- */
- template <typename T> class SkAutoTMalloc {
- public:
- /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */
- explicit SkAutoTMalloc(T* ptr = nullptr) : fPtr(ptr) {}
- /** Allocates space for 'count' Ts. */
- explicit SkAutoTMalloc(size_t count)
- : fPtr(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr) {}
- SkAutoTMalloc(SkAutoTMalloc&&) = default;
- SkAutoTMalloc& operator=(SkAutoTMalloc&&) = default;
- /** Resize the memory area pointed to by the current ptr preserving contents. */
- void realloc(size_t count) {
- fPtr.reset(count ? (T*)sk_realloc_throw(fPtr.release(), count * sizeof(T)) : nullptr);
- }
- /** Resize the memory area pointed to by the current ptr without preserving contents. */
- T* reset(size_t count = 0) {
- fPtr.reset(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr);
- return this->get();
- }
- T* get() const { return fPtr.get(); }
- operator T*() { return fPtr.get(); }
- operator const T*() const { return fPtr.get(); }
- T& operator[](int index) { return fPtr.get()[index]; }
- const T& operator[](int index) const { return fPtr.get()[index]; }
- /**
- * Transfer ownership of the ptr to the caller, setting the internal
- * pointer to NULL. Note that this differs from get(), which also returns
- * the pointer, but it does not transfer ownership.
- */
- T* release() { return fPtr.release(); }
- private:
- std::unique_ptr<T, SkFunctionWrapper<void, void, sk_free>> fPtr;
- };
- template <size_t kCountRequested, typename T> class SkAutoSTMalloc {
- public:
- SkAutoSTMalloc() : fPtr(fTStorage) {}
- SkAutoSTMalloc(size_t count) {
- if (count > kCount) {
- fPtr = (T*)sk_malloc_throw(count, sizeof(T));
- } else if (count) {
- fPtr = fTStorage;
- } else {
- fPtr = nullptr;
- }
- }
- SkAutoSTMalloc(SkAutoSTMalloc&&) = delete;
- SkAutoSTMalloc(const SkAutoSTMalloc&) = delete;
- SkAutoSTMalloc& operator=(SkAutoSTMalloc&&) = delete;
- SkAutoSTMalloc& operator=(const SkAutoSTMalloc&) = delete;
- ~SkAutoSTMalloc() {
- if (fPtr != fTStorage) {
- sk_free(fPtr);
- }
- }
- // doesn't preserve contents
- T* reset(size_t count) {
- if (fPtr != fTStorage) {
- sk_free(fPtr);
- }
- if (count > kCount) {
- fPtr = (T*)sk_malloc_throw(count, sizeof(T));
- } else if (count) {
- fPtr = fTStorage;
- } else {
- fPtr = nullptr;
- }
- return fPtr;
- }
- T* get() const { return fPtr; }
- operator T*() {
- return fPtr;
- }
- operator const T*() const {
- return fPtr;
- }
- T& operator[](int index) {
- return fPtr[index];
- }
- const T& operator[](int index) const {
- return fPtr[index];
- }
- // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent
- void realloc(size_t count) {
- if (count > kCount) {
- if (fPtr == fTStorage) {
- fPtr = (T*)sk_malloc_throw(count, sizeof(T));
- memcpy(fPtr, fTStorage, kCount * sizeof(T));
- } else {
- fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
- }
- } else if (count) {
- if (fPtr != fTStorage) {
- fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
- }
- } else {
- this->reset(0);
- }
- }
- private:
- // Since we use uint32_t storage, we might be able to get more elements for free.
- static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
- #if defined(SK_BUILD_FOR_GOOGLE3)
- // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions
- // have multiple large stack allocations.
- static const size_t kMaxBytes = 4 * 1024;
- static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
- ? kMaxBytes / sizeof(T)
- : kCountWithPadding;
- #else
- static const size_t kCount = kCountWithPadding;
- #endif
- T* fPtr;
- union {
- uint32_t fStorage32[SkAlign4(kCount*sizeof(T)) >> 2];
- T fTStorage[1]; // do NOT want to invoke T::T()
- };
- };
- //////////////////////////////////////////////////////////////////////////////////////////////////
- /**
- * Pass the object and the storage that was offered during SkInPlaceNewCheck, and this will
- * safely destroy (and free if it was dynamically allocated) the object.
- */
- template <typename T> void SkInPlaceDeleteCheck(T* obj, void* storage) {
- if (storage == obj) {
- obj->~T();
- } else {
- delete obj;
- }
- }
- /**
- * Allocates T, using storage if it is large enough, and allocating on the heap (via new) if
- * storage is not large enough.
- *
- * obj = SkInPlaceNewCheck<Type>(storage, size);
- * ...
- * SkInPlaceDeleteCheck(obj, storage);
- */
- template<typename T, typename... Args>
- T* SkInPlaceNewCheck(void* storage, size_t size, Args&&... args) {
- return (sizeof(T) <= size) ? new (storage) T(std::forward<Args>(args)...)
- : new T(std::forward<Args>(args)...);
- }
- /**
- * Reserves memory that is aligned on double and pointer boundaries.
- * Hopefully this is sufficient for all practical purposes.
- */
- template <size_t N> class SkAlignedSStorage {
- public:
- SkAlignedSStorage() {}
- SkAlignedSStorage(SkAlignedSStorage&&) = delete;
- SkAlignedSStorage(const SkAlignedSStorage&) = delete;
- SkAlignedSStorage& operator=(SkAlignedSStorage&&) = delete;
- SkAlignedSStorage& operator=(const SkAlignedSStorage&) = delete;
- size_t size() const { return N; }
- void* get() { return fData; }
- const void* get() const { return fData; }
- private:
- union {
- void* fPtr;
- double fDouble;
- char fData[N];
- };
- };
- /**
- * Reserves memory that is aligned on double and pointer boundaries.
- * Hopefully this is sufficient for all practical purposes. Otherwise,
- * we have to do some arcane trickery to determine alignment of non-POD
- * types. Lifetime of the memory is the lifetime of the object.
- */
- template <int N, typename T> class SkAlignedSTStorage {
- public:
- SkAlignedSTStorage() {}
- SkAlignedSTStorage(SkAlignedSTStorage&&) = delete;
- SkAlignedSTStorage(const SkAlignedSTStorage&) = delete;
- SkAlignedSTStorage& operator=(SkAlignedSTStorage&&) = delete;
- SkAlignedSTStorage& operator=(const SkAlignedSTStorage&) = delete;
- /**
- * Returns void* because this object does not initialize the
- * memory. Use placement new for types that require a cons.
- */
- void* get() { return fStorage.get(); }
- const void* get() const { return fStorage.get(); }
- private:
- SkAlignedSStorage<sizeof(T)*N> fStorage;
- };
- using SkAutoFree = std::unique_ptr<void, SkFunctionWrapper<void, void, sk_free>>;
- template<typename C, std::size_t... Is>
- constexpr auto SkMakeArrayFromIndexSequence(C c, skstd::index_sequence<Is...>)
- -> std::array<skstd::result_of_t<C(std::size_t)>, sizeof...(Is)> {
- return {{ c(Is)... }};
- }
- template<size_t N, typename C> constexpr auto SkMakeArray(C c)
- -> std::array<skstd::result_of_t<C(std::size_t)>, N> {
- return SkMakeArrayFromIndexSequence(c, skstd::make_index_sequence<N>{});
- }
- #endif
|