• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2006 The Android Open Source Project
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #ifndef SkTemplates_DEFINED
11 #define SkTemplates_DEFINED
12 
13 #include "SkMath.h"
14 #include "SkMalloc.h"
15 #include "SkTLogic.h"
16 #include "SkTypes.h"
17 #include <limits.h>
18 #include <memory>
19 #include <new>
20 
21 /** \file SkTemplates.h
22 
23     This file contains light-weight template classes for type-safe and exception-safe
24     resource management.
25 */
26 
27 /**
28  *  Marks a local variable as known to be unused (to avoid warnings).
29  *  Note that this does *not* prevent the local variable from being optimized away.
30  */
sk_ignore_unused_variable(const T &)31 template<typename T> inline void sk_ignore_unused_variable(const T&) { }
32 
33 /**
34  *  Returns a pointer to a D which comes immediately after S[count].
35  */
36 template <typename D, typename S> static D* SkTAfter(S* ptr, size_t count = 1) {
37     return reinterpret_cast<D*>(ptr + count);
38 }
39 
40 /**
41  *  Returns a pointer to a D which comes byteOffset bytes after S.
42  */
SkTAddOffset(S * ptr,size_t byteOffset)43 template <typename D, typename S> static D* SkTAddOffset(S* ptr, size_t byteOffset) {
44     // The intermediate char* has the same cv-ness as D as this produces better error messages.
45     // This relies on the fact that reinterpret_cast can add constness, but cannot remove it.
46     return reinterpret_cast<D*>(reinterpret_cast<sknonstd::same_cv_t<char, D>*>(ptr) + byteOffset);
47 }
48 
49 template <typename R, typename T, R (*P)(T*)> struct SkFunctionWrapper {
operatorSkFunctionWrapper50     R operator()(T* t) { return P(t); }
51 };
52 
53 /** \class SkAutoTCallVProc
54 
55     Call a function when this goes out of scope. The template uses two
56     parameters, the object, and a function that is to be called in the destructor.
57     If release() is called, the object reference is set to null. If the object
58     reference is null when the destructor is called, we do not call the
59     function.
60 */
61 template <typename T, void (*P)(T*)> class SkAutoTCallVProc
62     : public std::unique_ptr<T, SkFunctionWrapper<void, T, P>> {
63 public:
SkAutoTCallVProc(T * obj)64     SkAutoTCallVProc(T* obj): std::unique_ptr<T, SkFunctionWrapper<void, T, P>>(obj) {}
65 
66     operator T*() const { return this->get(); }
67 };
68 
69 /** Allocate an array of T elements, and free the array in the destructor
70  */
71 template <typename T> class SkAutoTArray  {
72 public:
SkAutoTArray()73     SkAutoTArray() {}
74     /** Allocate count number of T elements
75      */
SkAutoTArray(int count)76     explicit SkAutoTArray(int count) {
77         SkASSERT(count >= 0);
78         if (count) {
79             fArray.reset(new T[count]);
80         }
81         SkDEBUGCODE(fCount = count;)
82     }
83 
SkAutoTArray(SkAutoTArray && other)84     SkAutoTArray(SkAutoTArray&& other) : fArray(std::move(other.fArray)) {
85         SkDEBUGCODE(fCount = other.fCount; other.fCount = 0;)
86     }
87     SkAutoTArray& operator=(SkAutoTArray&& other) {
88         if (this != &other) {
89             fArray = std::move(other.fArray);
90             SkDEBUGCODE(fCount = other.fCount; other.fCount = 0;)
91         }
92         return *this;
93     }
94 
95     /** Reallocates given a new count. Reallocation occurs even if new count equals old count.
96      */
reset(int count)97     void reset(int count) { *this = SkAutoTArray(count);  }
98 
99     /** Return the array of T elements. Will be NULL if count == 0
100      */
get()101     T* get() const { return fArray.get(); }
102 
103     /** Return the nth element in the array
104      */
105     T&  operator[](int index) const {
106         SkASSERT((unsigned)index < (unsigned)fCount);
107         return fArray[index];
108     }
109 
110 private:
111     std::unique_ptr<T[]> fArray;
112     SkDEBUGCODE(int fCount = 0;)
113 };
114 
115 /** Wraps SkAutoTArray, with room for kCountRequested elements preallocated.
116  */
117 template <int kCountRequested, typename T> class SkAutoSTArray {
118 public:
119     SkAutoSTArray(SkAutoSTArray&&) = delete;
120     SkAutoSTArray(const SkAutoSTArray&) = delete;
121     SkAutoSTArray& operator=(SkAutoSTArray&&) = delete;
122     SkAutoSTArray& operator=(const SkAutoSTArray&) = delete;
123 
124     /** Initialize with no objects */
SkAutoSTArray()125     SkAutoSTArray() {
126         fArray = nullptr;
127         fCount = 0;
128     }
129 
130     /** Allocate count number of T elements
131      */
SkAutoSTArray(int count)132     SkAutoSTArray(int count) {
133         fArray = nullptr;
134         fCount = 0;
135         this->reset(count);
136     }
137 
~SkAutoSTArray()138     ~SkAutoSTArray() {
139         this->reset(0);
140     }
141 
142     /** Destroys previous objects in the array and default constructs count number of objects */
reset(int count)143     void reset(int count) {
144         T* start = fArray;
145         T* iter = start + fCount;
146         while (iter > start) {
147             (--iter)->~T();
148         }
149 
150         SkASSERT(count >= 0);
151         if (fCount != count) {
152             if (fCount > kCount) {
153                 // 'fArray' was allocated last time so free it now
154                 SkASSERT((T*) fStorage != fArray);
155                 sk_free(fArray);
156             }
157 
158             if (count > kCount) {
159                 fArray = (T*) sk_malloc_throw(count, sizeof(T));
160             } else if (count > 0) {
161                 fArray = (T*) fStorage;
162             } else {
163                 fArray = nullptr;
164             }
165 
166             fCount = count;
167         }
168 
169         iter = fArray;
170         T* stop = fArray + count;
171         while (iter < stop) {
172             new (iter++) T;
173         }
174     }
175 
176     /** Return the number of T elements in the array
177      */
count()178     int count() const { return fCount; }
179 
180     /** Return the array of T elements. Will be NULL if count == 0
181      */
get()182     T* get() const { return fArray; }
183 
begin()184     T* begin() { return fArray; }
185 
begin()186     const T* begin() const { return fArray; }
187 
end()188     T* end() { return fArray + fCount; }
189 
end()190     const T* end() const { return fArray + fCount; }
191 
192     /** Return the nth element in the array
193      */
194     T&  operator[](int index) const {
195         SkASSERT(index < fCount);
196         return fArray[index];
197     }
198 
199 private:
200 #if defined(SK_BUILD_FOR_GOOGLE3)
201     // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions
202     // have multiple large stack allocations.
203     static const int kMaxBytes = 4 * 1024;
204     static const int kCount = kCountRequested * sizeof(T) > kMaxBytes
205         ? kMaxBytes / sizeof(T)
206         : kCountRequested;
207 #else
208     static const int kCount = kCountRequested;
209 #endif
210 
211     int     fCount;
212     T*      fArray;
213     // since we come right after fArray, fStorage should be properly aligned
214     char    fStorage[kCount * sizeof(T)];
215 };
216 
217 /** Manages an array of T elements, freeing the array in the destructor.
218  *  Does NOT call any constructors/destructors on T (T must be POD).
219  */
220 template <typename T> class SkAutoTMalloc  {
221 public:
222     /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */
fPtr(ptr)223     explicit SkAutoTMalloc(T* ptr = nullptr) : fPtr(ptr) {}
224 
225     /** Allocates space for 'count' Ts. */
SkAutoTMalloc(size_t count)226     explicit SkAutoTMalloc(size_t count)
227         : fPtr(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr) {}
228 
229     SkAutoTMalloc(SkAutoTMalloc&&) = default;
230     SkAutoTMalloc& operator=(SkAutoTMalloc&&) = default;
231 
232     /** Resize the memory area pointed to by the current ptr preserving contents. */
realloc(size_t count)233     void realloc(size_t count) {
234         fPtr.reset(count ? (T*)sk_realloc_throw(fPtr.release(), count * sizeof(T)) : nullptr);
235     }
236 
237     /** Resize the memory area pointed to by the current ptr without preserving contents. */
238     T* reset(size_t count = 0) {
239         fPtr.reset(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr);
240         return this->get();
241     }
242 
get()243     T* get() const { return fPtr.get(); }
244 
245     operator T*() { return fPtr.get(); }
246 
247     operator const T*() const { return fPtr.get(); }
248 
249     T& operator[](int index) { return fPtr.get()[index]; }
250 
251     const T& operator[](int index) const { return fPtr.get()[index]; }
252 
253     /**
254      *  Transfer ownership of the ptr to the caller, setting the internal
255      *  pointer to NULL. Note that this differs from get(), which also returns
256      *  the pointer, but it does not transfer ownership.
257      */
release()258     T* release() { return fPtr.release(); }
259 
260 private:
261     std::unique_ptr<T, SkFunctionWrapper<void, void, sk_free>> fPtr;
262 };
263 
264 template <size_t kCountRequested, typename T> class SkAutoSTMalloc {
265 public:
SkAutoSTMalloc()266     SkAutoSTMalloc() : fPtr(fTStorage) {}
267 
SkAutoSTMalloc(size_t count)268     SkAutoSTMalloc(size_t count) {
269         if (count > kCount) {
270             fPtr = (T*)sk_malloc_throw(count, sizeof(T));
271         } else if (count) {
272             fPtr = fTStorage;
273         } else {
274             fPtr = nullptr;
275         }
276     }
277 
278     SkAutoSTMalloc(SkAutoSTMalloc&&) = delete;
279     SkAutoSTMalloc(const SkAutoSTMalloc&) = delete;
280     SkAutoSTMalloc& operator=(SkAutoSTMalloc&&) = delete;
281     SkAutoSTMalloc& operator=(const SkAutoSTMalloc&) = delete;
282 
~SkAutoSTMalloc()283     ~SkAutoSTMalloc() {
284         if (fPtr != fTStorage) {
285             sk_free(fPtr);
286         }
287     }
288 
289     // doesn't preserve contents
reset(size_t count)290     T* reset(size_t count) {
291         if (fPtr != fTStorage) {
292             sk_free(fPtr);
293         }
294         if (count > kCount) {
295             fPtr = (T*)sk_malloc_throw(count, sizeof(T));
296         } else if (count) {
297             fPtr = fTStorage;
298         } else {
299             fPtr = nullptr;
300         }
301         return fPtr;
302     }
303 
get()304     T* get() const { return fPtr; }
305 
306     operator T*() {
307         return fPtr;
308     }
309 
310     operator const T*() const {
311         return fPtr;
312     }
313 
314     T& operator[](int index) {
315         return fPtr[index];
316     }
317 
318     const T& operator[](int index) const {
319         return fPtr[index];
320     }
321 
322     // Reallocs the array, can be used to shrink the allocation.  Makes no attempt to be intelligent
realloc(size_t count)323     void realloc(size_t count) {
324         if (count > kCount) {
325             if (fPtr == fTStorage) {
326                 fPtr = (T*)sk_malloc_throw(count, sizeof(T));
327                 memcpy(fPtr, fTStorage, kCount * sizeof(T));
328             } else {
329                 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
330             }
331         } else if (count) {
332             if (fPtr != fTStorage) {
333                 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
334             }
335         } else {
336             this->reset(0);
337         }
338     }
339 
340 private:
341     // Since we use uint32_t storage, we might be able to get more elements for free.
342     static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
343 #if defined(SK_BUILD_FOR_GOOGLE3)
344     // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions
345     // have multiple large stack allocations.
346     static const size_t kMaxBytes = 4 * 1024;
347     static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
348         ? kMaxBytes / sizeof(T)
349         : kCountWithPadding;
350 #else
351     static const size_t kCount = kCountWithPadding;
352 #endif
353 
354     T*          fPtr;
355     union {
356         uint32_t    fStorage32[SkAlign4(kCount*sizeof(T)) >> 2];
357         T           fTStorage[1];   // do NOT want to invoke T::T()
358     };
359 };
360 
361 //////////////////////////////////////////////////////////////////////////////////////////////////
362 
363 /**
364  *  Pass the object and the storage that was offered during SkInPlaceNewCheck, and this will
365  *  safely destroy (and free if it was dynamically allocated) the object.
366  */
SkInPlaceDeleteCheck(T * obj,void * storage)367 template <typename T> void SkInPlaceDeleteCheck(T* obj, void* storage) {
368     if (storage == obj) {
369         obj->~T();
370     } else {
371         delete obj;
372     }
373 }
374 
375 /**
376  *  Allocates T, using storage if it is large enough, and allocating on the heap (via new) if
377  *  storage is not large enough.
378  *
379  *      obj = SkInPlaceNewCheck<Type>(storage, size);
380  *      ...
381  *      SkInPlaceDeleteCheck(obj, storage);
382  */
383 template<typename T, typename... Args>
SkInPlaceNewCheck(void * storage,size_t size,Args &&...args)384 T* SkInPlaceNewCheck(void* storage, size_t size, Args&&... args) {
385     return (sizeof(T) <= size) ? new (storage) T(std::forward<Args>(args)...)
386                                : new T(std::forward<Args>(args)...);
387 }
388 /**
389  * Reserves memory that is aligned on double and pointer boundaries.
390  * Hopefully this is sufficient for all practical purposes.
391  */
392 template <size_t N> class SkAlignedSStorage {
393 public:
SkAlignedSStorage()394     SkAlignedSStorage() {}
395     SkAlignedSStorage(SkAlignedSStorage&&) = delete;
396     SkAlignedSStorage(const SkAlignedSStorage&) = delete;
397     SkAlignedSStorage& operator=(SkAlignedSStorage&&) = delete;
398     SkAlignedSStorage& operator=(const SkAlignedSStorage&) = delete;
399 
size()400     size_t size() const { return N; }
get()401     void* get() { return fData; }
get()402     const void* get() const { return fData; }
403 
404 private:
405     union {
406         void*   fPtr;
407         double  fDouble;
408         char    fData[N];
409     };
410 };
411 
412 /**
413  * Reserves memory that is aligned on double and pointer boundaries.
414  * Hopefully this is sufficient for all practical purposes. Otherwise,
415  * we have to do some arcane trickery to determine alignment of non-POD
416  * types. Lifetime of the memory is the lifetime of the object.
417  */
418 template <int N, typename T> class SkAlignedSTStorage {
419 public:
SkAlignedSTStorage()420     SkAlignedSTStorage() {}
421     SkAlignedSTStorage(SkAlignedSTStorage&&) = delete;
422     SkAlignedSTStorage(const SkAlignedSTStorage&) = delete;
423     SkAlignedSTStorage& operator=(SkAlignedSTStorage&&) = delete;
424     SkAlignedSTStorage& operator=(const SkAlignedSTStorage&) = delete;
425 
426     /**
427      * Returns void* because this object does not initialize the
428      * memory. Use placement new for types that require a cons.
429      */
get()430     void* get() { return fStorage.get(); }
get()431     const void* get() const { return fStorage.get(); }
432 private:
433     SkAlignedSStorage<sizeof(T)*N> fStorage;
434 };
435 
436 using SkAutoFree = std::unique_ptr<void, SkFunctionWrapper<void, void, sk_free>>;
437 
438 template<typename C, std::size_t... Is>
439 constexpr auto SkMakeArrayFromIndexSequence(C c, skstd::index_sequence<Is...>)
440 -> std::array<skstd::result_of_t<C(std::size_t)>, sizeof...(Is)> {
441     return {{ c(Is)... }};
442 }
443 
444 template<size_t N, typename C> constexpr auto SkMakeArray(C c)
445 -> std::array<skstd::result_of_t<C(std::size_t)>, N> {
446     return SkMakeArrayFromIndexSequence(c, skstd::make_index_sequence<N>{});
447 }
448 
449 #endif
450