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