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