OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef BASE_STACK_CONTAINER_H_ | |
6 #define BASE_STACK_CONTAINER_H_ | |
7 | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/basictypes.h" | |
12 #include "build/build_config.h" | |
13 #include "base/memory/aligned_memory.h" | |
14 | |
15 // This allocator can be used with STL containers to provide a stack buffer | |
16 // from which to allocate memory and overflows onto the heap. This stack buffer | |
17 // would be allocated on the stack and allows us to avoid heap operations in | |
18 // some situations. | |
19 // | |
20 // STL likes to make copies of allocators, so the allocator itself can't hold | |
21 // the data. Instead, we make the creator responsible for creating a | |
22 // StackAllocator::Source which contains the data. Copying the allocator | |
23 // merely copies the pointer to this shared source, so all allocators created | |
24 // based on our allocator will share the same stack buffer. | |
25 // | |
26 // This stack buffer implementation is very simple. The first allocation that | |
27 // fits in the stack buffer will use the stack buffer. Any subsequent | |
28 // allocations will not use the stack buffer, even if there is unused room. | |
29 // This makes it appropriate for array-like containers, but the caller should | |
30 // be sure to reserve() in the container up to the stack buffer size. Otherwise | |
31 // the container will allocate a small array which will "use up" the stack | |
32 // buffer. | |
33 template<typename T, size_t stack_capacity> | |
34 class StackAllocator : public std::allocator<T> { | |
35 public: | |
36 typedef typename std::allocator<T>::pointer pointer; | |
37 typedef typename std::allocator<T>::size_type size_type; | |
38 | |
39 // Backing store for the allocator. The container owner is responsible for | |
40 // maintaining this for as long as any containers using this allocator are | |
41 // live. | |
42 struct Source { | |
43 Source() : used_stack_buffer_(false) { | |
44 } | |
45 | |
46 // Casts the buffer in its right type. | |
47 T* stack_buffer() { return stack_buffer_.template data_as<T>(); } | |
48 const T* stack_buffer() const { | |
49 return stack_buffer_.template data_as<T>(); | |
50 } | |
51 | |
52 // The buffer itself. It is not of type T because we don't want the | |
53 // constructors and destructors to be automatically called. Define a POD | |
54 // buffer of the right size instead. | |
55 base::AlignedMemory<sizeof(T[stack_capacity]), ALIGNOF(T)> stack_buffer_; | |
56 #if defined(OS_ANDROID) | |
57 COMPILE_ASSERT(ALIGNOF(T) <= 16, crbug_115612); | |
58 #endif | |
59 | |
60 // Set when the stack buffer is used for an allocation. We do not track | |
61 // how much of the buffer is used, only that somebody is using it. | |
62 bool used_stack_buffer_; | |
63 }; | |
64 | |
65 // Used by containers when they want to refer to an allocator of type U. | |
66 template<typename U> | |
67 struct rebind { | |
68 typedef StackAllocator<U, stack_capacity> other; | |
69 }; | |
70 | |
71 // For the straight up copy c-tor, we can share storage. | |
72 StackAllocator(const StackAllocator<T, stack_capacity>& rhs) | |
73 : std::allocator<T>(), source_(rhs.source_) { | |
74 } | |
75 | |
76 // ISO C++ requires the following constructor to be defined, | |
77 // and std::vector in VC++2008SP1 Release fails with an error | |
78 // in the class _Container_base_aux_alloc_real (from <xutility>) | |
79 // if the constructor does not exist. | |
80 // For this constructor, we cannot share storage; there's | |
81 // no guarantee that the Source buffer of Ts is large enough | |
82 // for Us. | |
83 // TODO: If we were fancy pants, perhaps we could share storage | |
84 // iff sizeof(T) == sizeof(U). | |
85 template<typename U, size_t other_capacity> | |
86 StackAllocator(const StackAllocator<U, other_capacity>& other) | |
87 : source_(NULL) { | |
88 } | |
89 | |
90 explicit StackAllocator(Source* source) : source_(source) { | |
91 } | |
92 | |
93 // Actually do the allocation. Use the stack buffer if nobody has used it yet | |
94 // and the size requested fits. Otherwise, fall through to the standard | |
95 // allocator. | |
96 pointer allocate(size_type n, void* hint = 0) { | |
97 if (source_ != NULL && !source_->used_stack_buffer_ | |
98 && n <= stack_capacity) { | |
99 source_->used_stack_buffer_ = true; | |
100 return source_->stack_buffer(); | |
101 } else { | |
102 return std::allocator<T>::allocate(n, hint); | |
103 } | |
104 } | |
105 | |
106 // Free: when trying to free the stack buffer, just mark it as free. For | |
107 // non-stack-buffer pointers, just fall though to the standard allocator. | |
108 void deallocate(pointer p, size_type n) { | |
109 if (source_ != NULL && p == source_->stack_buffer()) | |
110 source_->used_stack_buffer_ = false; | |
111 else | |
112 std::allocator<T>::deallocate(p, n); | |
113 } | |
114 | |
115 private: | |
116 Source* source_; | |
117 }; | |
118 | |
119 // A wrapper around STL containers that maintains a stack-sized buffer that the | |
120 // initial capacity of the vector is based on. Growing the container beyond the | |
121 // stack capacity will transparently overflow onto the heap. The container must | |
122 // support reserve(). | |
123 // | |
124 // WATCH OUT: the ContainerType MUST use the proper StackAllocator for this | |
125 // type. This object is really intended to be used only internally. You'll want | |
126 // to use the wrappers below for different types. | |
127 template<typename TContainerType, int stack_capacity> | |
128 class StackContainer { | |
129 public: | |
130 typedef TContainerType ContainerType; | |
131 typedef typename ContainerType::value_type ContainedType; | |
132 typedef StackAllocator<ContainedType, stack_capacity> Allocator; | |
133 | |
134 // Allocator must be constructed before the container! | |
135 StackContainer() : allocator_(&stack_data_), container_(allocator_) { | |
136 // Make the container use the stack allocation by reserving our buffer size | |
137 // before doing anything else. | |
138 container_.reserve(stack_capacity); | |
139 } | |
140 | |
141 // Getters for the actual container. | |
142 // | |
143 // Danger: any copies of this made using the copy constructor must have | |
144 // shorter lifetimes than the source. The copy will share the same allocator | |
145 // and therefore the same stack buffer as the original. Use std::copy to | |
146 // copy into a "real" container for longer-lived objects. | |
147 ContainerType& container() { return container_; } | |
148 const ContainerType& container() const { return container_; } | |
149 | |
150 // Support operator-> to get to the container. This allows nicer syntax like: | |
151 // StackContainer<...> foo; | |
152 // std::sort(foo->begin(), foo->end()); | |
153 ContainerType* operator->() { return &container_; } | |
154 const ContainerType* operator->() const { return &container_; } | |
155 | |
156 #ifdef UNIT_TEST | |
157 // Retrieves the stack source so that that unit tests can verify that the | |
158 // buffer is being used properly. | |
159 const typename Allocator::Source& stack_data() const { | |
160 return stack_data_; | |
161 } | |
162 #endif | |
163 | |
164 protected: | |
165 typename Allocator::Source stack_data_; | |
166 Allocator allocator_; | |
167 ContainerType container_; | |
168 | |
169 DISALLOW_COPY_AND_ASSIGN(StackContainer); | |
170 }; | |
171 | |
172 // StackString | |
173 template<size_t stack_capacity> | |
174 class StackString : public StackContainer< | |
175 std::basic_string<char, | |
176 std::char_traits<char>, | |
177 StackAllocator<char, stack_capacity> >, | |
178 stack_capacity> { | |
179 public: | |
180 StackString() : StackContainer< | |
181 std::basic_string<char, | |
182 std::char_traits<char>, | |
183 StackAllocator<char, stack_capacity> >, | |
184 stack_capacity>() { | |
185 } | |
186 | |
187 private: | |
188 DISALLOW_COPY_AND_ASSIGN(StackString); | |
189 }; | |
190 | |
191 // StackWString | |
192 template<size_t stack_capacity> | |
193 class StackWString : public StackContainer< | |
194 std::basic_string<wchar_t, | |
195 std::char_traits<wchar_t>, | |
196 StackAllocator<wchar_t, stack_capacity> >, | |
197 stack_capacity> { | |
198 public: | |
199 StackWString() : StackContainer< | |
200 std::basic_string<wchar_t, | |
201 std::char_traits<wchar_t>, | |
202 StackAllocator<wchar_t, stack_capacity> >, | |
203 stack_capacity>() { | |
204 } | |
205 | |
206 private: | |
207 DISALLOW_COPY_AND_ASSIGN(StackWString); | |
208 }; | |
209 | |
210 // StackVector | |
211 // | |
212 // Example: | |
213 // StackVector<int, 16> foo; | |
214 // foo->push_back(22); // we have overloaded operator-> | |
215 // foo[0] = 10; // as well as operator[] | |
216 template<typename T, size_t stack_capacity> | |
217 class StackVector : public StackContainer< | |
218 std::vector<T, StackAllocator<T, stack_capacity> >, | |
219 stack_capacity> { | |
220 public: | |
221 StackVector() : StackContainer< | |
222 std::vector<T, StackAllocator<T, stack_capacity> >, | |
223 stack_capacity>() { | |
224 } | |
225 | |
226 // We need to put this in STL containers sometimes, which requires a copy | |
227 // constructor. We can't call the regular copy constructor because that will | |
228 // take the stack buffer from the original. Here, we create an empty object | |
229 // and make a stack buffer of its own. | |
230 StackVector(const StackVector<T, stack_capacity>& other) | |
231 : StackContainer< | |
232 std::vector<T, StackAllocator<T, stack_capacity> >, | |
233 stack_capacity>() { | |
234 this->container().assign(other->begin(), other->end()); | |
235 } | |
236 | |
237 StackVector<T, stack_capacity>& operator=( | |
238 const StackVector<T, stack_capacity>& other) { | |
239 this->container().assign(other->begin(), other->end()); | |
240 return *this; | |
241 } | |
242 | |
243 // Vectors are commonly indexed, which isn't very convenient even with | |
244 // operator-> (using "->at()" does exception stuff we don't want). | |
245 T& operator[](size_t i) { return this->container().operator[](i); } | |
246 const T& operator[](size_t i) const { | |
247 return this->container().operator[](i); | |
248 } | |
249 }; | |
250 | |
251 #endif // BASE_STACK_CONTAINER_H_ | |
OLD | NEW |