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