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