Extend relocation packing to cover arm64.
[chromium-blink-merge.git] / courgette / memory_allocator.h
blob5386dad0d7d9ead4b26bd66d083eefb9f2ceb0d1
1 // Copyright (c) 2011 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.
5 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_
6 #define COURGETTE_MEMORY_ALLOCATOR_H_
8 #include <memory>
10 #include "base/basictypes.h"
11 #include "base/files/file.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
15 #ifndef NDEBUG
17 // A helper class to track down call sites that are not handling error cases.
18 template<class T>
19 class CheckReturnValue {
20 public:
21 // Not marked explicit on purpose.
22 CheckReturnValue(T value) : value_(value), checked_(false) { // NOLINT
24 CheckReturnValue(const CheckReturnValue& other)
25 : value_(other.value_), checked_(other.checked_) {
26 other.checked_ = true;
29 CheckReturnValue& operator=(const CheckReturnValue& other) {
30 if (this != &other) {
31 DCHECK(checked_);
32 value_ = other.value_;
33 checked_ = other.checked_;
34 other.checked_ = true;
38 ~CheckReturnValue() {
39 DCHECK(checked_);
42 operator const T&() const {
43 checked_ = true;
44 return value_;
47 private:
48 T value_;
49 mutable bool checked_;
51 typedef CheckReturnValue<bool> CheckBool;
52 #else
53 typedef bool CheckBool;
54 #endif
56 namespace courgette {
58 #if defined(OS_WIN)
60 // Manages a read/write virtual mapping of a physical file.
61 class FileMapping {
62 public:
63 FileMapping();
64 ~FileMapping();
66 // Map a file from beginning to |size|.
67 bool Create(HANDLE file, size_t size);
68 void Close();
70 // Returns true iff a mapping has been created.
71 bool valid() const;
73 // Returns a writable pointer to the beginning of the memory mapped file.
74 // If Create has not been called successfully, return value is NULL.
75 void* view() const;
77 protected:
78 bool InitializeView(size_t size);
80 HANDLE mapping_;
81 void* view_;
84 // Manages a temporary file and a memory mapping of the temporary file.
85 // The memory that this class manages holds a pointer back to the TempMapping
86 // object itself, so that given a memory pointer allocated by this class,
87 // you can get a pointer to the TempMapping instance that owns that memory.
88 class TempMapping {
89 public:
90 TempMapping();
91 ~TempMapping();
93 // Creates a temporary file of size |size| and maps it into the current
94 // process's address space.
95 bool Initialize(size_t size);
97 // Returns a writable pointer to the reserved memory.
98 void* memory() const;
100 // Returns true if the mapping is valid and memory is available.
101 bool valid() const;
103 // Returns a pointer to the TempMapping instance that allocated the |mem|
104 // block of memory. It's the callers responsibility to make sure that
105 // the memory block was allocated by the TempMapping class.
106 static TempMapping* GetMappingFromPtr(void* mem);
108 protected:
109 base::File file_;
110 FileMapping mapping_;
113 // A memory allocator class that allocates memory either from the heap or via a
114 // temporary file. The interface is STL inspired but the class does not throw
115 // STL exceptions on allocation failure. Instead it returns NULL.
116 // A file allocation will be made if either the requested memory size exceeds
117 // |kMaxHeapAllocationSize| or if a heap allocation fails.
118 // Allocating the memory as a mapping of a temporary file solves the problem
119 // that there might not be enough physical memory and pagefile to support the
120 // allocation. This can happen because these resources are too small, or
121 // already committed to other processes. Provided there is enough disk, the
122 // temporary file acts like a pagefile that other processes can't access.
123 template<class T>
124 class MemoryAllocator {
125 public:
126 typedef T value_type;
127 typedef value_type* pointer;
128 typedef value_type& reference;
129 typedef const value_type* const_pointer;
130 typedef const value_type& const_reference;
131 typedef size_t size_type;
132 typedef ptrdiff_t difference_type;
134 // Each allocation is tagged with a single byte so that we know how to
135 // deallocate it.
136 enum AllocationType {
137 HEAP_ALLOCATION,
138 FILE_ALLOCATION,
141 // 5MB is the maximum heap allocation size that we'll attempt.
142 // When applying a patch for Chrome 10.X we found that at this
143 // threshold there were 17 allocations higher than this threshold
144 // (largest at 136MB) 10 allocations just below the threshold and 6362
145 // smaller allocations.
146 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5;
148 template<class OtherT>
149 struct rebind {
150 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
151 typedef MemoryAllocator<OtherT> other;
154 MemoryAllocator() _THROW0() {
157 // We can't use an explicit constructor here, as dictated by our style guide.
158 // The implementation of basic_string in Visual Studio 2010 prevents this.
159 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { // NOLINT
162 template<class OtherT>
163 MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { // NOLINT
166 ~MemoryAllocator() {
169 void deallocate(pointer ptr, size_type size) {
170 uint8* mem = reinterpret_cast<uint8*>(ptr);
171 mem -= sizeof(T);
172 if (mem[0] == HEAP_ALLOCATION) {
173 delete [] mem;
174 } else {
175 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]);
176 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem);
177 delete mapping;
181 pointer allocate(size_type count) {
182 // We use the first byte of each allocation to mark the allocation type.
183 // However, so that the allocation is properly aligned, we allocate an
184 // extra element and then use the first byte of the first element
185 // to mark the allocation type.
186 count++;
188 if (count > max_size())
189 return NULL;
191 size_type bytes = count * sizeof(T);
192 uint8* mem = NULL;
194 // First see if we can do this allocation on the heap.
195 if (count < kMaxHeapAllocationSize)
196 mem = new(std::nothrow) uint8[bytes];
197 if (mem != NULL) {
198 mem[0] = static_cast<uint8>(HEAP_ALLOCATION);
199 } else {
200 // If either the heap allocation failed or the request exceeds the
201 // max heap allocation threshold, we back the allocation with a temp file.
202 TempMapping* mapping = new(std::nothrow) TempMapping();
203 if (mapping && mapping->Initialize(bytes)) {
204 mem = reinterpret_cast<uint8*>(mapping->memory());
205 mem[0] = static_cast<uint8>(FILE_ALLOCATION);
208 return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL;
211 pointer allocate(size_type count, const void* hint) {
212 return allocate(count);
215 void construct(pointer ptr, const T& value) {
216 ::new(ptr) T(value);
219 void destroy(pointer ptr) {
220 ptr->~T();
223 size_type max_size() const _THROW0() {
224 size_type count = static_cast<size_type>(-1) / sizeof(T);
225 return (0 < count ? count : 1);
229 #else // OS_WIN
231 // On Mac, Linux, we use a bare bones implementation that only does
232 // heap allocations.
233 template<class T>
234 class MemoryAllocator {
235 public:
236 typedef T value_type;
237 typedef value_type* pointer;
238 typedef value_type& reference;
239 typedef const value_type* const_pointer;
240 typedef const value_type& const_reference;
241 typedef size_t size_type;
242 typedef ptrdiff_t difference_type;
244 template<class OtherT>
245 struct rebind {
246 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
247 typedef MemoryAllocator<OtherT> other;
250 MemoryAllocator() {
253 explicit MemoryAllocator(const MemoryAllocator<T>& other) {
256 template<class OtherT>
257 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) {
260 ~MemoryAllocator() {
263 void deallocate(pointer ptr, size_type size) {
264 delete [] ptr;
267 pointer allocate(size_type count) {
268 if (count > max_size())
269 return NULL;
270 return reinterpret_cast<pointer>(
271 new(std::nothrow) uint8[count * sizeof(T)]);
274 pointer allocate(size_type count, const void* hint) {
275 return allocate(count);
278 void construct(pointer ptr, const T& value) {
279 ::new(ptr) T(value);
282 void destroy(pointer ptr) {
283 ptr->~T();
286 size_type max_size() const {
287 size_type count = static_cast<size_type>(-1) / sizeof(T);
288 return (0 < count ? count : 1);
292 #endif // OS_WIN
294 // Manages a growable buffer. The buffer allocation is done by the
295 // MemoryAllocator class. This class will not throw exceptions so call sites
296 // must be prepared to handle memory allocation failures.
297 // The interface is STL inspired to avoid having to make too many changes
298 // to code that previously was using STL.
299 template<typename T, class Allocator = MemoryAllocator<T> >
300 class NoThrowBuffer {
301 public:
302 typedef T value_type;
303 static const size_t kAllocationFailure = 0xffffffff;
304 static const size_t kStartSize = sizeof(T) > 0x100 ? 1 : 0x100 / sizeof(T);
306 NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) {
309 ~NoThrowBuffer() {
310 clear();
313 void clear() {
314 if (buffer_) {
315 alloc_.deallocate(buffer_, alloc_size_);
316 buffer_ = NULL;
317 size_ = 0;
318 alloc_size_ = 0;
322 bool empty() const {
323 return size_ == 0;
326 CheckBool reserve(size_t size) WARN_UNUSED_RESULT {
327 if (failed())
328 return false;
330 if (size <= alloc_size_)
331 return true;
333 if (size < kStartSize)
334 size = kStartSize;
336 T* new_buffer = alloc_.allocate(size);
337 if (!new_buffer) {
338 clear();
339 alloc_size_ = kAllocationFailure;
340 } else {
341 if (buffer_) {
342 memcpy(new_buffer, buffer_, size_ * sizeof(T));
343 alloc_.deallocate(buffer_, alloc_size_);
345 buffer_ = new_buffer;
346 alloc_size_ = size;
349 return !failed();
352 CheckBool append(const T* data, size_t size) WARN_UNUSED_RESULT {
353 if (failed())
354 return false;
356 if (size > alloc_.max_size() - size_)
357 return false;
359 if (!size)
360 return true;
362 if ((alloc_size_ - size_) < size) {
363 const size_t max_size = alloc_.max_size();
364 size_t new_size = alloc_size_ ? alloc_size_ : kStartSize;
365 while (new_size < size_ + size) {
366 if (new_size < max_size - new_size) {
367 new_size *= 2;
368 } else {
369 new_size = max_size;
372 if (!reserve(new_size))
373 return false;
376 memcpy(buffer_ + size_, data, size * sizeof(T));
377 size_ += size;
379 return true;
382 CheckBool resize(size_t size, const T& init_value) WARN_UNUSED_RESULT {
383 if (size > size_) {
384 if (!reserve(size))
385 return false;
386 for (size_t i = size_; i < size; ++i)
387 buffer_[i] = init_value;
388 } else if (size < size_) {
389 // TODO(tommi): Should we allocate a new, smaller buffer?
390 // It might be faster for us to simply change the size.
393 size_ = size;
395 return true;
398 CheckBool push_back(const T& item) WARN_UNUSED_RESULT {
399 return append(&item, 1);
402 const T& back() const {
403 return buffer_[size_ - 1];
406 T& back() {
407 return buffer_[size_ - 1];
410 const T* begin() const {
411 if (!size_)
412 return NULL;
413 return &buffer_[0];
416 T* begin() {
417 if (!size_)
418 return NULL;
419 return &buffer_[0];
422 const T* end() const {
423 if (!size_)
424 return NULL;
425 return &buffer_[size_ - 1];
428 T* end() {
429 if (!size_)
430 return NULL;
431 return &buffer_[size_ - 1];
434 const T& operator[](size_t index) const {
435 DCHECK(index < size_);
436 return buffer_[index];
439 T& operator[](size_t index) {
440 DCHECK(index < size_);
441 return buffer_[index];
444 size_t size() const {
445 return size_;
448 T* data() const {
449 return buffer_;
452 // Returns true if an allocation failure has ever occurred for this object.
453 bool failed() const {
454 return alloc_size_ == kAllocationFailure;
457 protected:
458 T* buffer_;
459 size_t size_; // how much of the buffer we're using.
460 size_t alloc_size_; // how much space we have allocated.
461 Allocator alloc_;
464 } // namespace courgette
466 #endif // COURGETTE_MEMORY_ALLOCATOR_H_