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