1 | /* |
2 | * Copyright 2016 Google Inc. |
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 SkAutoMalloc_DEFINED |
9 | #define SkAutoMalloc_DEFINED |
10 | |
11 | #include "include/core/SkTypes.h" |
12 | #include "include/private/SkMacros.h" |
13 | #include "include/private/SkMalloc.h" |
14 | #include "include/private/SkNoncopyable.h" |
15 | |
16 | #include <memory> |
17 | |
18 | /** |
19 | * Manage an allocated block of heap memory. This object is the sole manager of |
20 | * the lifetime of the block, so the caller must not call sk_free() or delete |
21 | * on the block, unless release() was called. |
22 | */ |
23 | class SkAutoMalloc : SkNoncopyable { |
24 | public: |
25 | explicit SkAutoMalloc(size_t size = 0) |
26 | : fPtr(size ? sk_malloc_throw(size) : nullptr), fSize(size) {} |
27 | |
28 | /** |
29 | * Passed to reset to specify what happens if the requested size is smaller |
30 | * than the current size (and the current block was dynamically allocated). |
31 | */ |
32 | enum OnShrink { |
33 | /** |
34 | * If the requested size is smaller than the current size, and the |
35 | * current block is dynamically allocated, free the old block and |
36 | * malloc a new block of the smaller size. |
37 | */ |
38 | kAlloc_OnShrink, |
39 | |
40 | /** |
41 | * If the requested size is smaller than the current size, and the |
42 | * current block is dynamically allocated, just return the old |
43 | * block. |
44 | */ |
45 | kReuse_OnShrink |
46 | }; |
47 | |
48 | /** |
49 | * Reallocates the block to a new size. The ptr may or may not change. |
50 | */ |
51 | void* reset(size_t size = 0, OnShrink shrink = kAlloc_OnShrink) { |
52 | if (size != fSize && (size > fSize || kReuse_OnShrink != shrink)) { |
53 | fPtr.reset(size ? sk_malloc_throw(size) : nullptr); |
54 | fSize = size; |
55 | } |
56 | return fPtr.get(); |
57 | } |
58 | |
59 | /** |
60 | * Return the allocated block. |
61 | */ |
62 | void* get() { return fPtr.get(); } |
63 | const void* get() const { return fPtr.get(); } |
64 | |
65 | /** Transfer ownership of the current ptr to the caller, setting the |
66 | internal reference to null. Note the caller is reponsible for calling |
67 | sk_free on the returned address. |
68 | */ |
69 | void* release() { |
70 | fSize = 0; |
71 | return fPtr.release(); |
72 | } |
73 | |
74 | private: |
75 | struct WrapFree { |
76 | void operator()(void* p) { sk_free(p); } |
77 | }; |
78 | std::unique_ptr<void, WrapFree> fPtr; |
79 | size_t fSize; // can be larger than the requested size (see kReuse) |
80 | }; |
81 | |
82 | /** |
83 | * Manage an allocated block of memory. If the requested size is <= kSizeRequested (or slightly |
84 | * more), then the allocation will come from the stack rather than the heap. This object is the |
85 | * sole manager of the lifetime of the block, so the caller must not call sk_free() or delete on |
86 | * the block. |
87 | */ |
88 | template <size_t kSizeRequested> class SkAutoSMalloc : SkNoncopyable { |
89 | public: |
90 | /** |
91 | * Creates initially empty storage. get() returns a ptr, but it is to a zero-byte allocation. |
92 | * Must call reset(size) to return an allocated block. |
93 | */ |
94 | SkAutoSMalloc() { |
95 | fPtr = fStorage; |
96 | fSize = kSize; |
97 | } |
98 | |
99 | /** |
100 | * Allocate a block of the specified size. If size <= kSizeRequested (or slightly more), then |
101 | * the allocation will come from the stack, otherwise it will be dynamically allocated. |
102 | */ |
103 | explicit SkAutoSMalloc(size_t size) { |
104 | fPtr = fStorage; |
105 | fSize = kSize; |
106 | this->reset(size); |
107 | } |
108 | |
109 | /** |
110 | * Free the allocated block (if any). If the block was small enough to have been allocated on |
111 | * the stack, then this does nothing. |
112 | */ |
113 | ~SkAutoSMalloc() { |
114 | if (fPtr != (void*)fStorage) { |
115 | sk_free(fPtr); |
116 | } |
117 | } |
118 | |
119 | /** |
120 | * Return the allocated block. May return non-null even if the block is of zero size. Since |
121 | * this may be on the stack or dynamically allocated, the caller must not call sk_free() on it, |
122 | * but must rely on SkAutoSMalloc to manage it. |
123 | */ |
124 | void* get() const { return fPtr; } |
125 | |
126 | /** |
127 | * Return a new block of the requested size, freeing (as necessary) any previously allocated |
128 | * block. As with the constructor, if size <= kSizeRequested (or slightly more) then the return |
129 | * block may be allocated locally, rather than from the heap. |
130 | */ |
131 | void* reset(size_t size, |
132 | SkAutoMalloc::OnShrink shrink = SkAutoMalloc::kAlloc_OnShrink, |
133 | bool* didChangeAlloc = nullptr) { |
134 | size = (size < kSize) ? kSize : size; |
135 | bool alloc = size != fSize && (SkAutoMalloc::kAlloc_OnShrink == shrink || size > fSize); |
136 | if (didChangeAlloc) { |
137 | *didChangeAlloc = alloc; |
138 | } |
139 | if (alloc) { |
140 | if (fPtr != (void*)fStorage) { |
141 | sk_free(fPtr); |
142 | } |
143 | |
144 | if (size == kSize) { |
145 | SkASSERT(fPtr != fStorage); // otherwise we lied when setting didChangeAlloc. |
146 | fPtr = fStorage; |
147 | } else { |
148 | fPtr = sk_malloc_throw(size); |
149 | } |
150 | |
151 | fSize = size; |
152 | } |
153 | SkASSERT(fSize >= size && fSize >= kSize); |
154 | SkASSERT((fPtr == fStorage) || fSize > kSize); |
155 | return fPtr; |
156 | } |
157 | |
158 | private: |
159 | // Align up to 32 bits. |
160 | static const size_t kSizeAlign4 = SkAlign4(kSizeRequested); |
161 | #if defined(SK_BUILD_FOR_GOOGLE3) |
162 | // Stack frame size is limited for SK_BUILD_FOR_GOOGLE3. 4k is less than the actual max, but some functions |
163 | // have multiple large stack allocations. |
164 | static const size_t kMaxBytes = 4 * 1024; |
165 | static const size_t kSize = kSizeRequested > kMaxBytes ? kMaxBytes : kSizeAlign4; |
166 | #else |
167 | static const size_t kSize = kSizeAlign4; |
168 | #endif |
169 | |
170 | void* fPtr; |
171 | size_t fSize; // can be larger than the requested size (see kReuse) |
172 | uint32_t fStorage[kSize >> 2]; |
173 | }; |
174 | // Can't guard the constructor because it's a template class. |
175 | |
176 | #endif |
177 | |