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 "SkTypes.h"
14#include <new>
15
16/** \file SkTemplates.h
17
18    This file contains light-weight template classes for type-safe and exception-safe
19    resource management.
20*/
21
22/**
23 *  SkTIsConst<T>::value is true if the type T is const.
24 *  The type T is constrained not to be an array or reference type.
25 */
26template <typename T> struct SkTIsConst {
27    static T* t;
28    static uint16_t test(const volatile void*);
29    static uint32_t test(volatile void *);
30    static const bool value = (sizeof(uint16_t) == sizeof(test(t)));
31};
32
33///@{
34/** SkTConstType<T, CONST>::type will be 'const T' if CONST is true, 'T' otherwise. */
35template <typename T, bool CONST> struct SkTConstType {
36    typedef T type;
37};
38template <typename T> struct SkTConstType<T, true> {
39    typedef const T type;
40};
41///@}
42
43/** \class SkAutoTCallVProc
44
45    Call a function when this goes out of scope. The template uses two
46    parameters, the object, and a function that is to be called in the destructor.
47    If detach() is called, the object reference is set to null. If the object
48    reference is null when the destructor is called, we do not call the
49    function.
50*/
51template <typename T, void (*P)(T*)> class SkAutoTCallVProc : SkNoncopyable {
52public:
53    SkAutoTCallVProc(T* obj): fObj(obj) {}
54    ~SkAutoTCallVProc() { if (fObj) P(fObj); }
55    T* detach() { T* obj = fObj; fObj = NULL; return obj; }
56private:
57    T* fObj;
58};
59
60/** \class SkAutoTCallIProc
61
62Call a function when this goes out of scope. The template uses two
63parameters, the object, and a function that is to be called in the destructor.
64If detach() is called, the object reference is set to null. If the object
65reference is null when the destructor is called, we do not call the
66function.
67*/
68template <typename T, int (*P)(T*)> class SkAutoTCallIProc : SkNoncopyable {
69public:
70    SkAutoTCallIProc(T* obj): fObj(obj) {}
71    ~SkAutoTCallIProc() { if (fObj) P(fObj); }
72    T* detach() { T* obj = fObj; fObj = NULL; return obj; }
73private:
74    T* fObj;
75};
76
77// See also SkTScopedPtr.
78template <typename T> class SkAutoTDelete : SkNoncopyable {
79public:
80    SkAutoTDelete(T* obj) : fObj(obj) {}
81    ~SkAutoTDelete() { delete fObj; }
82
83    T* get() const { return fObj; }
84    T& operator*() const { SkASSERT(fObj); return *fObj; }
85    T* operator->() const { SkASSERT(fObj); return fObj; }
86
87    /**
88     *  Delete the owned object, setting the internal pointer to NULL.
89     */
90    void free() {
91        delete fObj;
92        fObj = NULL;
93    }
94
95    /**
96     *  Transfer ownership of the object to the caller, setting the internal
97     *  pointer to NULL. Note that this differs from get(), which also returns
98     *  the pointer, but it does not transfer ownership.
99     */
100    T* detach() {
101        T* obj = fObj;
102        fObj = NULL;
103        return obj;
104    }
105
106private:
107    T*  fObj;
108};
109
110template <typename T> class SkAutoTDeleteArray : SkNoncopyable {
111public:
112    SkAutoTDeleteArray(T array[]) : fArray(array) {}
113    ~SkAutoTDeleteArray() { SkDELETE_ARRAY(fArray); }
114
115    T*      get() const { return fArray; }
116    void    free() { SkDELETE_ARRAY(fArray); fArray = NULL; }
117    T*      detach() { T* array = fArray; fArray = NULL; return array; }
118
119private:
120    T*  fArray;
121};
122
123/** Allocate an array of T elements, and free the array in the destructor
124 */
125template <typename T> class SkAutoTArray : SkNoncopyable {
126public:
127    SkAutoTArray() {
128        fArray = NULL;
129        SkDEBUGCODE(fCount = 0;)
130    }
131    /** Allocate count number of T elements
132     */
133    explicit SkAutoTArray(int count) {
134        SkASSERT(count >= 0);
135        fArray = NULL;
136        if (count) {
137            fArray = new T[count];
138        }
139        SkDEBUGCODE(fCount = count;)
140    }
141
142    /** Reallocates given a new count. Reallocation occurs even if new count equals old count.
143     */
144    void reset(int count) {
145        delete[] fArray;
146        SkASSERT(count >= 0);
147        fArray = NULL;
148        if (count) {
149            fArray = new T[count];
150        }
151        SkDEBUGCODE(fCount = count;)
152    }
153
154    ~SkAutoTArray() {
155        delete[] fArray;
156    }
157
158    /** Return the array of T elements. Will be NULL if count == 0
159     */
160    T* get() const { return fArray; }
161
162    /** Return the nth element in the array
163     */
164    T&  operator[](int index) const {
165        SkASSERT((unsigned)index < (unsigned)fCount);
166        return fArray[index];
167    }
168
169private:
170    T*  fArray;
171    SkDEBUGCODE(int fCount;)
172};
173
174/** Wraps SkAutoTArray, with room for up to N elements preallocated
175 */
176template <size_t N, typename T> class SkAutoSTArray : SkNoncopyable {
177public:
178    /** Allocate count number of T elements
179     */
180    SkAutoSTArray(size_t count) {
181        if (count > N) {
182            fArray = new T[count];
183        } else if (count) {
184            fArray = new (fStorage) T[count];
185        } else {
186            fArray = NULL;
187        }
188        fCount = count;
189    }
190
191    ~SkAutoSTArray() {
192        if (fCount > N) {
193            delete[] fArray;
194        } else {
195            T* start = fArray;
196            T* iter = start + fCount;
197            while (iter > start) {
198                (--iter)->~T();
199            }
200        }
201    }
202
203    /** Return the number of T elements in the array
204     */
205    size_t count() const { return fCount; }
206
207    /** Return the array of T elements. Will be NULL if count == 0
208     */
209    T* get() const { return fArray; }
210
211    /** Return the nth element in the array
212     */
213    T&  operator[](int index) const {
214        SkASSERT((unsigned)index < fCount);
215        return fArray[index];
216    }
217
218private:
219    size_t  fCount;
220    T*      fArray;
221    // since we come right after fArray, fStorage should be properly aligned
222    char    fStorage[N * sizeof(T)];
223};
224
225/** Allocate a temp array on the stack/heap.
226    Does NOT call any constructors/destructors on T (i.e. T must be POD)
227*/
228template <typename T> class SkAutoTMalloc : SkNoncopyable {
229public:
230    SkAutoTMalloc(size_t count) {
231        fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
232    }
233
234    ~SkAutoTMalloc() {
235        sk_free(fPtr);
236    }
237
238    // doesn't preserve contents
239    void reset (size_t count) {
240        sk_free(fPtr);
241        fPtr = fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
242    }
243
244    T* get() const { return fPtr; }
245
246    operator T*() {
247        return fPtr;
248    }
249
250    operator const T*() const {
251        return fPtr;
252    }
253
254    T& operator[](int index) {
255        return fPtr[index];
256    }
257
258    const T& operator[](int index) const {
259        return fPtr[index];
260    }
261
262private:
263    T*  fPtr;
264};
265
266template <size_t N, typename T> class SK_API SkAutoSTMalloc : SkNoncopyable {
267public:
268    SkAutoSTMalloc(size_t count) {
269        if (count <= N) {
270            fPtr = fTStorage;
271        } else {
272            fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
273        }
274    }
275
276    ~SkAutoSTMalloc() {
277        if (fPtr != fTStorage) {
278            sk_free(fPtr);
279        }
280    }
281
282    // doesn't preserve contents
283    void reset(size_t count) {
284        if (fPtr != fTStorage) {
285            sk_free(fPtr);
286        }
287        if (count <= N) {
288            fPtr = fTStorage;
289        } else {
290            fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP);
291        }
292    }
293
294    T* get() const { return fPtr; }
295
296    operator T*() {
297        return fPtr;
298    }
299
300    operator const T*() const {
301        return fPtr;
302    }
303
304    T& operator[](int index) {
305        return fPtr[index];
306    }
307
308    const T& operator[](int index) const {
309        return fPtr[index];
310    }
311
312private:
313    T*          fPtr;
314    union {
315        uint32_t    fStorage32[(N*sizeof(T) + 3) >> 2];
316        T           fTStorage[1];   // do NOT want to invoke T::T()
317    };
318};
319
320/**
321 * Reserves memory that is aligned on double and pointer boundaries.
322 * Hopefully this is sufficient for all practical purposes.
323 */
324template <size_t N> class SkAlignedSStorage : SkNoncopyable {
325public:
326    void* get() { return fData; }
327private:
328    union {
329        void*   fPtr;
330        double  fDouble;
331        char    fData[N];
332    };
333};
334
335/**
336 * Reserves memory that is aligned on double and pointer boundaries.
337 * Hopefully this is sufficient for all practical purposes. Otherwise,
338 * we have to do some arcane trickery to determine alignment of non-POD
339 * types. Lifetime of the memory is the lifetime of the object.
340 */
341template <int N, typename T> class SkAlignedSTStorage : SkNoncopyable {
342public:
343    /**
344     * Returns void* because this object does not initialize the
345     * memory. Use placement new for types that require a cons.
346     */
347    void* get() { return fStorage.get(); }
348private:
349    SkAlignedSStorage<sizeof(T)*N> fStorage;
350};
351
352#endif
353