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