xref: /aosp_15_r20/external/skia/include/private/base/SkTemplates.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/private/base/SkAlign.h"
12 #include "include/private/base/SkAssert.h"
13 #include "include/private/base/SkDebug.h"
14 #include "include/private/base/SkMalloc.h"
15 #include "include/private/base/SkTLogic.h"
16 #include "include/private/base/SkTo.h"
17 
18 #include <algorithm>
19 #include <array>
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 #include <memory>
24 #include <type_traits>
25 #include <utility>
26 
27 
28 /** \file SkTemplates.h
29 
30     This file contains light-weight template classes for type-safe and exception-safe
31     resource management.
32 */
33 
34 /**
35  *  Marks a local variable as known to be unused (to avoid warnings).
36  *  Note that this does *not* prevent the local variable from being optimized away.
37  */
sk_ignore_unused_variable(const T &)38 template<typename T> inline void sk_ignore_unused_variable(const T&) { }
39 
40 /**
41  * This is a general purpose absolute-value function.
42  * See SkAbs32 in (SkSafe32.h) for a 32-bit int specific version that asserts.
43  */
SkTAbs(T value)44 template <typename T> static inline T SkTAbs(T value) {
45     if (value < 0) {
46         value = -value;
47     }
48     return value;
49 }
50 
51 /**
52  *  Returns a pointer to a D which comes immediately after S[count].
53  */
54 template <typename D, typename S> inline D* SkTAfter(S* ptr, size_t count = 1) {
55     return reinterpret_cast<D*>(ptr + count);
56 }
57 
58 /**
59  *  Returns a pointer to a D which comes byteOffset bytes after S.
60  */
SkTAddOffset(S * ptr,ptrdiff_t byteOffset)61 template <typename D, typename S> inline D* SkTAddOffset(S* ptr, ptrdiff_t byteOffset) {
62     // The intermediate char* has the same cv-ness as D as this produces better error messages.
63     // This relies on the fact that reinterpret_cast can add constness, but cannot remove it.
64     return reinterpret_cast<D*>(reinterpret_cast<sknonstd::same_cv_t<char, D>*>(ptr) + byteOffset);
65 }
66 
67 template <typename T, T* P> struct SkOverloadedFunctionObject {
68     template <typename... Args>
69     auto operator()(Args&&... args) const -> decltype(P(std::forward<Args>(args)...)) {
70         return P(std::forward<Args>(args)...);
71     }
72 };
73 
74 template <auto F> using SkFunctionObject =
75     SkOverloadedFunctionObject<std::remove_pointer_t<decltype(F)>, F>;
76 
77 /** \class SkAutoTCallVProc
78 
79     Call a function when this goes out of scope. The template uses two
80     parameters, the object, and a function that is to be called in the destructor.
81     If release() is called, the object reference is set to null. If the object
82     reference is null when the destructor is called, we do not call the
83     function.
84 */
85 template <typename T, void (*P)(T*)> class SkAutoTCallVProc
86     : public std::unique_ptr<T, SkFunctionObject<P>> {
87     using inherited = std::unique_ptr<T, SkFunctionObject<P>>;
88 public:
89     using inherited::inherited;
90     SkAutoTCallVProc(const SkAutoTCallVProc&) = delete;
SkAutoTCallVProc(SkAutoTCallVProc && that)91     SkAutoTCallVProc(SkAutoTCallVProc&& that) : inherited(std::move(that)) {}
92 
93     operator T*() const { return this->get(); }
94 };
95 
96 
97 namespace skia_private {
98 /** Allocate an array of T elements on the heap. Once this goes out of scope, the
99  *  elements will be cleaned up "auto"matically.
100  */
101 template <typename T> class AutoTArray  {
102 public:
AutoTArray()103     AutoTArray() {}
104     // Allocate size number of T elements
AutoTArray(size_t size)105     explicit AutoTArray(size_t size) {
106         fSize = check_size_bytes_too_big<T>(size);
107         fData.reset(size > 0 ? new T[size] : nullptr);
108     }
109 
110     // TODO: remove when all uses are gone.
AutoTArray(int size)111     explicit AutoTArray(int size) : AutoTArray(SkToSizeT(size)) {}
112 
AutoTArray(AutoTArray && other)113     AutoTArray(AutoTArray&& other) : fData(std::move(other.fData)) {
114         fSize = std::exchange(other.fSize, 0);
115     }
116     AutoTArray& operator=(AutoTArray&& other) {
117         if (this != &other) {
118             fData = std::move(other.fData);
119             fSize = std::exchange(other.fSize, 0);
120         }
121         return *this;
122     }
123 
124     // Reallocates given a new count. Reallocation occurs even if new count equals old count.
125     void reset(size_t count = 0) {
126         *this = AutoTArray(count);
127     }
128 
get()129     T* get() const { return fData.get(); }
130 
131     T&  operator[](size_t index) const {
132         return fData[sk_collection_check_bounds(index, fSize)];
133     }
134 
data()135     const T* data() const { return fData.get(); }
data()136     T* data() { return fData.get(); }
137 
size()138     size_t size() const { return fSize; }
empty()139     bool empty() const { return fSize == 0; }
size_bytes()140     size_t size_bytes() const { return sizeof(T) * fSize; }
141 
begin()142     T* begin() {
143         return fData;
144     }
begin()145     const T* begin() const {
146         return fData;
147     }
148 
149     // It's safe to use fItemArray + fSize because if fItemArray is nullptr then adding 0 is
150     // valid and returns nullptr. See [expr.add] in the C++ standard.
end()151     T* end() {
152         if (fData == nullptr) {
153             SkASSERT(fSize == 0);
154         }
155         return fData + fSize;
156     }
end()157     const T* end() const {
158         if (fData == nullptr) {
159             SkASSERT(fSize == 0);
160         }
161         return fData + fSize;
162     }
163 
164 private:
165     std::unique_ptr<T[]> fData;
166     size_t fSize = 0;
167 };
168 
169 /** Like AutoTArray with room for kCountRequested elements preallocated on
170  *  the Stack. If count exceeds the space of the preallocation, the elements
171  *  will live on the heap. Once this goes out of scope, the elements will be
172  *  cleaned up "auto"matically.
173  */
174 template <int kCountRequested, typename T> class AutoSTArray {
175 public:
176     AutoSTArray(AutoSTArray&&) = delete;
177     AutoSTArray(const AutoSTArray&) = delete;
178     AutoSTArray& operator=(AutoSTArray&&) = delete;
179     AutoSTArray& operator=(const AutoSTArray&) = delete;
180 
181     /** Initialize with no objects */
AutoSTArray()182     AutoSTArray() {
183         fArray = nullptr;
184         fCount = 0;
185     }
186 
187     /** Allocate count number of T elements
188      */
AutoSTArray(int count)189     AutoSTArray(int count) {
190         fArray = nullptr;
191         fCount = 0;
192         this->reset(count);
193     }
194 
~AutoSTArray()195     ~AutoSTArray() {
196         this->reset(0);
197     }
198 
199     /** Destroys previous objects in the array and default constructs count number of objects */
reset(int count)200     void reset(int count) {
201         T* start = fArray;
202         T* iter = start + fCount;
203         while (iter > start) {
204             (--iter)->~T();
205         }
206 
207         SkASSERT(count >= 0);
208         if (fCount != count) {
209             if (fCount > kCount) {
210                 // 'fArray' was allocated last time so free it now
211                 SkASSERT((T*) fStorage != fArray);
212                 sk_free(fArray);
213             }
214 
215             if (count > kCount) {
216                 fArray = (T*) sk_malloc_throw(count, sizeof(T));
217             } else if (count > 0) {
218                 fArray = (T*) fStorage;
219             } else {
220                 fArray = nullptr;
221             }
222 
223             fCount = count;
224         }
225 
226         iter = fArray;
227         T* stop = fArray + count;
228         while (iter < stop) {
229             new (iter++) T;
230         }
231     }
232 
233     /** Return the number of T elements in the array
234      */
count()235     int count() const { return fCount; }
236 
237     /** Return the array of T elements. Will be NULL if count == 0
238      */
get()239     T* get() const { return fArray; }
240 
begin()241     T* begin() { return fArray; }
242 
begin()243     const T* begin() const { return fArray; }
244 
end()245     T* end() { return fArray + fCount; }
246 
end()247     const T* end() const { return fArray + fCount; }
248 
249     /** Return the nth element in the array
250      */
251     T&  operator[](int index) const {
252         return fArray[sk_collection_check_bounds(index, fCount)];
253     }
254 
255     /** Aliases matching other types, like std::vector. */
data()256     const T* data() const { return fArray; }
data()257     T* data() { return fArray; }
size()258     size_t size() const { return fCount; }
259 
260 private:
261 #if defined(SK_BUILD_FOR_GOOGLE3)
262     // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max,
263     // but some functions have multiple large stack allocations.
264     static constexpr int kMaxBytes = 4 * 1024;
265     static constexpr int kMinCount = kCountRequested * sizeof(T) > kMaxBytes
266         ? kMaxBytes / sizeof(T)
267         : kCountRequested;
268 #else
269     static constexpr int kMinCount = kCountRequested;
270 #endif
271 
272     // Because we are also storing an int, there is a tiny bit of padding that
273     // the C++ compiler adds after fStorage if sizeof(T) <= alignof(T*).
274     // Thus, we can expand how many elements are stored on the stack to make use of this
275     // (e.g. 1 extra element for 4 byte T if kCountRequested was even).
276     static_assert(alignof(int) <= alignof(T*) || alignof(int) <= alignof(T));
277     static constexpr int kCount =
278             SkAlignTo(kMinCount*sizeof(T) + sizeof(int), std::max(alignof(T*), alignof(T))) / sizeof(T);
279 
280     T* fArray;
281     alignas(T) std::byte fStorage[kCount * sizeof(T)];
282     int fCount;
283 };
284 
285 /** Manages an array of T elements, freeing the array in the destructor.
286  *  Does NOT call any constructors/destructors on T (T must be POD).
287  */
288 template <typename T,
289           typename = std::enable_if_t<std::is_trivially_default_constructible<T>::value &&
290                                       std::is_trivially_destructible<T>::value>>
291 class AutoTMalloc  {
292 public:
293     /** Takes ownership of the ptr. The ptr must be a value which can be passed to sk_free. */
fPtr(ptr)294     explicit AutoTMalloc(T* ptr = nullptr) : fPtr(ptr) {}
295 
296     /** Allocates space for 'count' Ts. */
AutoTMalloc(size_t count)297     explicit AutoTMalloc(size_t count)
298         : fPtr(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr) {}
299 
300     AutoTMalloc(AutoTMalloc&&) = default;
301     AutoTMalloc& operator=(AutoTMalloc&&) = default;
302 
303     /** Resize the memory area pointed to by the current ptr preserving contents. */
realloc(size_t count)304     void realloc(size_t count) {
305         fPtr.reset(count ? (T*)sk_realloc_throw(fPtr.release(), count * sizeof(T)) : nullptr);
306     }
307 
308     /** Resize the memory area pointed to by the current ptr without preserving contents. */
309     T* reset(size_t count = 0) {
310         fPtr.reset(count ? (T*)sk_malloc_throw(count, sizeof(T)) : nullptr);
311         return this->get();
312     }
313 
get()314     T* get() const { return fPtr.get(); }
315 
316     operator T*() { return fPtr.get(); }
317 
318     operator const T*() const { return fPtr.get(); }
319 
320     T& operator[](int index) { return fPtr.get()[index]; }
321 
322     const T& operator[](int index) const { return fPtr.get()[index]; }
323 
324     /** Aliases matching other types, like std::vector. */
data()325     const T* data() const { return fPtr.get(); }
data()326     T* data() { return fPtr.get(); }
327 
328     /**
329      *  Transfer ownership of the ptr to the caller, setting the internal
330      *  pointer to NULL. Note that this differs from get(), which also returns
331      *  the pointer, but it does not transfer ownership.
332      */
release()333     T* release() { return fPtr.release(); }
334 
335 private:
336     std::unique_ptr<T, SkOverloadedFunctionObject<void(void*), sk_free>> fPtr;
337 };
338 
339 template <size_t kCountRequested,
340           typename T,
341           typename = std::enable_if_t<std::is_trivially_default_constructible<T>::value &&
342                                       std::is_trivially_destructible<T>::value>>
343 class AutoSTMalloc {
344 public:
AutoSTMalloc()345     AutoSTMalloc() : fPtr(fTStorage) {}
346 
AutoSTMalloc(size_t count)347     AutoSTMalloc(size_t count) {
348         if (count > kCount) {
349             fPtr = (T*)sk_malloc_throw(count, sizeof(T));
350         } else if (count) {
351             fPtr = fTStorage;
352         } else {
353             fPtr = nullptr;
354         }
355     }
356 
357     AutoSTMalloc(AutoSTMalloc&&) = delete;
358     AutoSTMalloc(const AutoSTMalloc&) = delete;
359     AutoSTMalloc& operator=(AutoSTMalloc&&) = delete;
360     AutoSTMalloc& operator=(const AutoSTMalloc&) = delete;
361 
~AutoSTMalloc()362     ~AutoSTMalloc() {
363         if (fPtr != fTStorage) {
364             sk_free(fPtr);
365         }
366     }
367 
368     // doesn't preserve contents
reset(size_t count)369     T* reset(size_t count) {
370         if (fPtr != fTStorage) {
371             sk_free(fPtr);
372         }
373         if (count > kCount) {
374             fPtr = (T*)sk_malloc_throw(count, sizeof(T));
375         } else if (count) {
376             fPtr = fTStorage;
377         } else {
378             fPtr = nullptr;
379         }
380         return fPtr;
381     }
382 
get()383     T* get() const { return fPtr; }
384 
385     operator T*() {
386         return fPtr;
387     }
388 
389     operator const T*() const {
390         return fPtr;
391     }
392 
393     T& operator[](int index) {
394         return fPtr[index];
395     }
396 
397     const T& operator[](int index) const {
398         return fPtr[index];
399     }
400 
401     /** Aliases matching other types, like std::vector. */
data()402     const T* data() const { return fPtr; }
data()403     T* data() { return fPtr; }
404 
405     // Reallocs the array, can be used to shrink the allocation.  Makes no attempt to be intelligent
realloc(size_t count)406     void realloc(size_t count) {
407         if (count > kCount) {
408             if (fPtr == fTStorage) {
409                 fPtr = (T*)sk_malloc_throw(count, sizeof(T));
410                 memcpy((void*)fPtr, fTStorage, kCount * sizeof(T));
411             } else {
412                 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
413             }
414         } else if (count) {
415             if (fPtr != fTStorage) {
416                 fPtr = (T*)sk_realloc_throw(fPtr, count, sizeof(T));
417             }
418         } else {
419             this->reset(0);
420         }
421     }
422 
423 private:
424     // Since we use uint32_t storage, we might be able to get more elements for free.
425     static constexpr size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T);
426 #if defined(SK_BUILD_FOR_GOOGLE3)
427     // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions
428     // have multiple large stack allocations.
429     static constexpr size_t kMaxBytes = 4 * 1024;
430     static constexpr size_t kCount = kCountRequested * sizeof(T) > kMaxBytes
431         ? kMaxBytes / sizeof(T)
432         : kCountWithPadding;
433 #else
434     static constexpr size_t kCount = kCountWithPadding;
435 #endif
436 
437     T*          fPtr;
438     union {
439         uint32_t    fStorage32[SkAlign4(kCount*sizeof(T)) >> 2];
440         T           fTStorage[1];   // do NOT want to invoke T::T()
441     };
442 };
443 
444 using UniqueVoidPtr = std::unique_ptr<void, SkOverloadedFunctionObject<void(void*), sk_free>>;
445 
446 }  // namespace skia_private
447 
448 template<typename C, std::size_t... Is>
449 constexpr auto SkMakeArrayFromIndexSequence(C c, std::index_sequence<Is...> is)
450 -> std::array<decltype(c(std::declval<typename decltype(is)::value_type>())), sizeof...(Is)> {
451     return {{ c(Is)... }};
452 }
453 
454 template<size_t N, typename C> constexpr auto SkMakeArray(C c)
455 -> std::array<decltype(c(std::declval<typename std::index_sequence<N>::value_type>())), N> {
456     return SkMakeArrayFromIndexSequence(c, std::make_index_sequence<N>{});
457 }
458 
459 #endif
460