Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_ | 5 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_ |
| 6 #define COURGETTE_MEMORY_ALLOCATOR_H_ | 6 #define COURGETTE_MEMORY_ALLOCATOR_H_ |
| 7 | 7 |
| 8 #include <memory> | 8 #include <memory> |
| 9 | 9 |
| 10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 59 | 59 |
| 60 // Manages a temporary file. The file is created in the %TEMP% folder and | 60 // Manages a temporary file. The file is created in the %TEMP% folder and |
| 61 // is deleted when the file handle is closed. | 61 // is deleted when the file handle is closed. |
| 62 // NOTE: Since the file will be used as backing for a memory allocation, | 62 // NOTE: Since the file will be used as backing for a memory allocation, |
| 63 // it will never be so big that size_t cannot represent its size. | 63 // it will never be so big that size_t cannot represent its size. |
| 64 class TempFile { | 64 class TempFile { |
| 65 public: | 65 public: |
| 66 TempFile(); | 66 TempFile(); |
| 67 ~TempFile(); | 67 ~TempFile(); |
| 68 | 68 |
| 69 __declspec(noinline) void Create(); | 69 bool Create(); |
| 70 void Close(); | 70 void Close(); |
| 71 __declspec(noinline) void SetSize(size_t size); | 71 bool SetSize(size_t size); |
| 72 | 72 |
| 73 // Returns true iff the temp file is currently open. | 73 // Returns true iff the temp file is currently open. |
| 74 bool valid() const; | 74 bool valid() const; |
| 75 | 75 |
| 76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if | 76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if |
| 77 // a temp file has not been created. | 77 // a temp file has not been created. |
| 78 base::PlatformFile handle() const; | 78 base::PlatformFile handle() const; |
| 79 | 79 |
| 80 // Returns the size of the temp file. If the temp file doesn't exist, | |
| 81 // the return value is 0. | |
| 82 size_t size() const; | |
| 83 | |
| 84 protected: | 80 protected: |
| 85 __declspec(noinline) FilePath PrepareTempFile(); | |
| 86 | |
| 87 base::PlatformFile file_; | 81 base::PlatformFile file_; |
| 88 size_t size_; | |
| 89 }; | 82 }; |
| 90 | 83 |
| 91 // Manages a read/write virtual mapping of a physical file. | 84 // Manages a read/write virtual mapping of a physical file. |
| 92 class FileMapping { | 85 class FileMapping { |
| 93 public: | 86 public: |
| 94 FileMapping(); | 87 FileMapping(); |
| 95 ~FileMapping(); | 88 ~FileMapping(); |
| 96 | 89 |
| 97 // Map a file from beginning to |size|. | 90 // Map a file from beginning to |size|. |
| 98 __declspec(noinline) void Create(HANDLE file, size_t size); | 91 bool Create(HANDLE file, size_t size); |
| 99 void Close(); | 92 void Close(); |
| 100 | 93 |
| 101 // Returns true iff a mapping has been created. | 94 // Returns true iff a mapping has been created. |
| 102 bool valid() const; | 95 bool valid() const; |
| 103 | 96 |
| 104 // Returns a writable pointer to the beginning of the memory mapped file. | 97 // Returns a writable pointer to the beginning of the memory mapped file. |
| 105 // If Create has not been called successfully, return value is NULL. | 98 // If Create has not been called successfully, return value is NULL. |
| 106 void* view() const; | 99 void* view() const; |
| 107 | 100 |
| 108 protected: | 101 protected: |
| 109 __declspec(noinline) void InitializeView(size_t size); | 102 __declspec(noinline) |
|
grt (UTC plus 2)
2011/04/06 02:46:38
Should this __declspec go away, too?
tommi (sloooow) - chröme
2011/04/06 13:50:02
Done.
| |
| 103 bool InitializeView(size_t size); | |
| 110 | 104 |
| 111 HANDLE mapping_; | 105 HANDLE mapping_; |
| 112 void* view_; | 106 void* view_; |
| 113 }; | 107 }; |
| 114 | 108 |
| 115 // Manages a temporary file and a memory mapping of the temporary file. | 109 // Manages a temporary file and a memory mapping of the temporary file. |
| 116 // The memory that this class manages holds a pointer back to the TempMapping | 110 // The memory that this class manages holds a pointer back to the TempMapping |
| 117 // object itself, so that given a memory pointer allocated by this class, | 111 // object itself, so that given a memory pointer allocated by this class, |
| 118 // you can get a pointer to the TempMapping instance that owns that memory. | 112 // you can get a pointer to the TempMapping instance that owns that memory. |
| 119 class TempMapping { | 113 class TempMapping { |
| 120 public: | 114 public: |
| 121 TempMapping(); | 115 TempMapping(); |
| 122 ~TempMapping(); | 116 ~TempMapping(); |
| 123 | 117 |
| 124 // Creates a temporary file of size |size| and maps it into the current | 118 // Creates a temporary file of size |size| and maps it into the current |
| 125 // process' address space. | 119 // process' address space. |
|
grt (UTC plus 2)
2011/04/06 02:46:38
process's
tommi (sloooow) - chröme
2011/04/06 13:50:02
Done.
| |
| 126 __declspec(noinline) void Initialize(size_t size); | 120 bool Initialize(size_t size); |
| 127 | 121 |
| 128 // Returns a writable pointer to the reserved memory. | 122 // Returns a writable pointer to the reserved memory. |
| 129 void* memory() const; | 123 void* memory() const; |
| 130 | 124 |
| 125 // Returns true if the mapping is valid and memory is available. | |
| 126 bool valid() const; | |
| 127 | |
| 131 // Returns a pointer to the TempMapping instance that allocated the |mem| | 128 // Returns a pointer to the TempMapping instance that allocated the |mem| |
| 132 // block of memory. It's the callers responsibility to make sure that | 129 // block of memory. It's the callers responsibility to make sure that |
| 133 // the memory block was allocated by the TempMapping class. | 130 // the memory block was allocated by the TempMapping class. |
| 134 static TempMapping* GetMappingFromPtr(void* mem); | 131 static TempMapping* GetMappingFromPtr(void* mem); |
| 135 | 132 |
| 136 protected: | 133 protected: |
| 137 TempFile file_; | 134 TempFile file_; |
| 138 FileMapping mapping_; | 135 FileMapping mapping_; |
| 139 }; | 136 }; |
| 140 | 137 |
| 141 // An STL compatible memory allocator class that allocates memory either | 138 // A memory allocator class that allocates memory either from the heap or via a |
| 142 // from the heap or via a temporary file. A file allocation will be made | 139 // temporary file. The interface is STL inspired but the class does not throw |
| 143 // if either the requested memory size exceeds |kMaxHeapAllocationSize| | 140 // STL exceptions on allocation failure. Instead it returns NULL. |
| 144 // or if a heap allocation fails. | 141 // implementation that throws those exceptions. |
|
grt (UTC plus 2)
2011/04/06 02:46:38
should this line be removed?
tommi (sloooow) - chröme
2011/04/06 13:50:02
thanks!
| |
| 142 // A file allocation will be made if either the requested memory size exceeds | |
| 143 // |kMaxHeapAllocationSize| or if a heap allocation fails. | |
| 145 // Allocating the memory as a mapping of a temporary file solves the problem | 144 // Allocating the memory as a mapping of a temporary file solves the problem |
| 146 // that there might not be enough physical memory and pagefile to support the | 145 // that there might not be enough physical memory and pagefile to support the |
| 147 // allocation. This can happen because these resources are too small, or | 146 // allocation. This can happen because these resources are too small, or |
| 148 // already committed to other processes. Provided there is enough disk, the | 147 // already committed to other processes. Provided there is enough disk, the |
| 149 // temporary file acts like a pagefile that other processes can't access. | 148 // temporary file acts like a pagefile that other processes can't access. |
| 150 template<class T> | 149 template<class T> |
| 151 class MemoryAllocator { | 150 class MemoryAllocator { |
| 152 public: | 151 public: |
| 153 typedef T value_type; | 152 typedef T value_type; |
| 154 typedef value_type* pointer; | 153 typedef value_type* pointer; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 167 | 166 |
| 168 // 5MB is the maximum heap allocation size that we'll attempt. | 167 // 5MB is the maximum heap allocation size that we'll attempt. |
| 169 // When applying a patch for Chrome 10.X we found that at this | 168 // When applying a patch for Chrome 10.X we found that at this |
| 170 // threshold there were 17 allocations higher than this threshold | 169 // threshold there were 17 allocations higher than this threshold |
| 171 // (largest at 136MB) 10 allocations just below the threshold and 6362 | 170 // (largest at 136MB) 10 allocations just below the threshold and 6362 |
| 172 // smaller allocations. | 171 // smaller allocations. |
| 173 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5; | 172 static const size_t kMaxHeapAllocationSize = 1024 * 1024 * 5; |
| 174 | 173 |
| 175 template<class OtherT> | 174 template<class OtherT> |
| 176 struct rebind { | 175 struct rebind { |
| 177 // convert an MemoryAllocator<T> to a MemoryAllocator<OtherT> | 176 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT> |
| 178 typedef MemoryAllocator<OtherT> other; | 177 typedef MemoryAllocator<OtherT> other; |
| 179 }; | 178 }; |
| 180 | 179 |
| 181 MemoryAllocator() _THROW0() { | 180 MemoryAllocator() _THROW0() { |
| 182 } | 181 } |
| 183 | 182 |
| 184 // We can't use an explicit constructor here, as dictated by our style guide. | 183 // We can't use an explicit constructor here, as dictated by our style guide. |
| 185 // The implementation of basic_string in Visual Studio 2010 prevents this. | 184 // The implementation of basic_string in Visual Studio 2010 prevents this. |
| 186 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { | 185 MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { // NOLINT |
| 187 } | 186 } |
| 188 | 187 |
| 189 template<class OtherT> | 188 template<class OtherT> |
| 190 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { | 189 MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { // NOLINT |
| 191 } | 190 } |
| 192 | 191 |
| 193 ~MemoryAllocator() { | 192 ~MemoryAllocator() { |
| 194 } | 193 } |
| 195 | 194 |
| 196 void deallocate(pointer ptr, size_type size) { | 195 void deallocate(pointer ptr, size_type size) { |
| 197 uint8* mem = reinterpret_cast<uint8*>(ptr); | 196 uint8* mem = reinterpret_cast<uint8*>(ptr); |
| 198 mem -= sizeof(T); | 197 mem -= sizeof(T); |
| 199 if (mem[0] == HEAP_ALLOCATION) { | 198 if (mem[0] == HEAP_ALLOCATION) { |
| 200 delete [] mem; | 199 delete [] mem; |
| 201 } else { | 200 } else { |
| 202 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]); | 201 DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]); |
| 203 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem); | 202 TempMapping* mapping = TempMapping::GetMappingFromPtr(mem); |
| 204 delete mapping; | 203 delete mapping; |
| 205 } | 204 } |
| 206 } | 205 } |
| 207 | 206 |
| 208 pointer allocate(size_type count) { | 207 pointer allocate(size_type count) { |
| 209 // We use the first byte of each allocation to mark the allocation type. | 208 // We use the first byte of each allocation to mark the allocation type. |
| 210 // However, so that the allocation is properly aligned, we allocate an | 209 // However, so that the allocation is properly aligned, we allocate an |
| 211 // extra element and then use the first byte of the first element | 210 // extra element and then use the first byte of the first element |
| 212 // to mark the allocation type. | 211 // to mark the allocation type. |
| 213 count++; | 212 count++; |
| 214 | 213 |
| 215 if (count > max_size()) | 214 if (count > max_size()) |
| 216 throw std::length_error("overflow"); | 215 return NULL; |
| 217 | 216 |
| 218 size_type bytes = count * sizeof(T); | 217 size_type bytes = count * sizeof(T); |
| 219 uint8* mem = NULL; | 218 uint8* mem = NULL; |
| 220 | 219 |
| 221 // First see if we can do this allocation on the heap. | 220 // First see if we can do this allocation on the heap. |
| 222 if (count < kMaxHeapAllocationSize) | 221 if (count < kMaxHeapAllocationSize) |
| 223 mem = new(std::nothrow) uint8[bytes]; | 222 mem = new(std::nothrow) uint8[bytes]; |
| 224 if (mem != NULL) { | 223 if (mem != NULL) { |
| 225 mem[0] = static_cast<uint8>(HEAP_ALLOCATION); | 224 mem[0] = static_cast<uint8>(HEAP_ALLOCATION); |
| 226 } else { | 225 } else { |
| 227 // If either the heap allocation failed or the request exceeds the | 226 // If either the heap allocation failed or the request exceeds the |
| 228 // max heap allocation threshold, we back the allocation with a temp file. | 227 // max heap allocation threshold, we back the allocation with a temp file. |
| 229 TempMapping* mapping = new TempMapping(); | 228 TempMapping* mapping = new(std::nothrow) TempMapping(); |
| 230 mapping->Initialize(bytes); | 229 if (mapping && mapping->Initialize(bytes)) { |
| 231 mem = reinterpret_cast<uint8*>(mapping->memory()); | 230 mem = reinterpret_cast<uint8*>(mapping->memory()); |
| 232 mem[0] = static_cast<uint8>(FILE_ALLOCATION); | 231 mem[0] = static_cast<uint8>(FILE_ALLOCATION); |
| 232 } | |
| 233 } | 233 } |
| 234 return reinterpret_cast<pointer>(mem + sizeof(T)); | 234 return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL; |
| 235 } | 235 } |
| 236 | 236 |
| 237 pointer allocate(size_type count, const void* hint) { | 237 pointer allocate(size_type count, const void* hint) { |
| 238 return allocate(count); | 238 return allocate(count); |
| 239 } | 239 } |
| 240 | 240 |
| 241 void construct(pointer ptr, const T& value) { | 241 void construct(pointer ptr, const T& value) { |
| 242 ::new(ptr) T(value); | 242 ::new(ptr) T(value); |
| 243 } | 243 } |
| 244 | 244 |
| 245 void destroy(pointer ptr) { | 245 void destroy(pointer ptr) { |
| 246 ptr->~T(); | 246 ptr->~T(); |
| 247 } | 247 } |
| 248 | 248 |
| 249 size_t max_size() const _THROW0() { | 249 size_t max_size() const _THROW0() { |
| 250 size_type count = static_cast<size_type>(-1) / sizeof(T); | 250 size_type count = static_cast<size_type>(-1) / sizeof(T); |
| 251 return (0 < count ? count : 1); | 251 return (0 < count ? count : 1); |
| 252 } | 252 } |
| 253 }; | 253 }; |
| 254 | 254 |
| 255 #else // OS_WIN | 255 #else // OS_WIN |
| 256 | 256 |
| 257 // On Mac, Linux, we just use the default STL allocator. | 257 // On Mac, Linux, we use a bare bones implementation that only does |
| 258 // heap allocations. | |
| 258 template<class T> | 259 template<class T> |
| 259 class MemoryAllocator : public std::allocator<T> { | 260 class MemoryAllocator { |
| 260 public: | 261 public: |
| 262 typedef T value_type; | |
| 263 typedef value_type* pointer; | |
| 264 typedef value_type& reference; | |
| 265 typedef const value_type* const_pointer; | |
| 266 typedef const value_type& const_reference; | |
| 267 typedef size_t size_type; | |
| 268 typedef ptrdiff_t difference_type; | |
| 269 | |
| 270 template<class OtherT> | |
| 271 struct rebind { | |
| 272 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT> | |
| 273 typedef MemoryAllocator<OtherT> other; | |
| 274 }; | |
| 275 | |
| 276 MemoryAllocator() { | |
| 277 } | |
| 278 | |
| 279 explicit MemoryAllocator(const MemoryAllocator<T>& other) { | |
| 280 } | |
| 281 | |
| 282 template<class OtherT> | |
| 283 explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) { | |
| 284 } | |
| 285 | |
| 286 ~MemoryAllocator() { | |
| 287 } | |
| 288 | |
| 289 void deallocate(pointer ptr, size_type size) { | |
| 290 delete [] ptr; | |
| 291 } | |
| 292 | |
| 293 pointer allocate(size_type count) { | |
| 294 if (count > max_size()) | |
| 295 return NULL; | |
| 296 return reinterpret_cast<pointer>( | |
| 297 new(std::nothrow) uint8[count * sizeof(T)]); | |
| 298 } | |
| 299 | |
| 300 pointer allocate(size_type count, const void* hint) { | |
| 301 return allocate(count); | |
| 302 } | |
| 303 | |
| 304 void construct(pointer ptr, const T& value) { | |
| 305 ::new(ptr) T(value); | |
| 306 } | |
| 307 | |
| 308 void destroy(pointer ptr) { | |
| 309 ptr->~T(); | |
| 310 } | |
| 311 | |
| 312 size_t max_size() const () { | |
| 313 size_type count = static_cast<size_type>(-1) / sizeof(T); | |
| 314 return (0 < count ? count : 1); | |
| 315 } | |
| 261 }; | 316 }; |
| 262 | 317 |
| 263 #endif // OS_WIN | 318 #endif // OS_WIN |
| 264 | 319 |
| 320 // Manages a growable buffer. The buffer allocation is done by the | |
| 321 // MemoryAllocator class. This class will not throw exceptions so call sites | |
| 322 // must be prepared to handle memory allocation failures. | |
| 323 // The interface is STL inspired to avoid having to make too many changes | |
| 324 // to code that previously was using STL. | |
| 325 template<typename T, class Allocator = MemoryAllocator<T> > | |
| 326 class NoThrowBuffer { | |
| 327 public: | |
| 328 typedef T value_type; | |
| 329 static const size_t kAllocationFailure = 0xffffffff; | |
| 330 static const size_t kStartSize = sizeof(T) > 0x100 ? 1 : 0x100 / sizeof(T); | |
| 331 | |
| 332 NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) { | |
| 333 } | |
| 334 | |
| 335 ~NoThrowBuffer() { | |
| 336 clear(); | |
| 337 } | |
| 338 | |
| 339 void clear() { | |
| 340 if (buffer_) { | |
| 341 alloc_.deallocate(buffer_, alloc_size_); | |
| 342 buffer_ = NULL; | |
| 343 size_ = 0; | |
| 344 alloc_size_ = 0; | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 bool empty() const { | |
| 349 return size_ == 0; | |
| 350 } | |
| 351 | |
| 352 CheckBool reserve(size_t size) WARN_UNUSED_RESULT { | |
| 353 if (failed()) | |
| 354 return false; | |
| 355 | |
| 356 if (size <= alloc_size_) | |
| 357 return true; | |
| 358 | |
| 359 if (size < kStartSize) | |
| 360 size = kStartSize; | |
| 361 | |
| 362 T* new_buffer = alloc_.allocate(size); | |
| 363 if (!new_buffer) { | |
| 364 clear(); | |
| 365 alloc_size_ = kAllocationFailure; | |
| 366 } else { | |
| 367 if (buffer_) { | |
| 368 memcpy(new_buffer, buffer_, size_ * sizeof(T)); | |
| 369 alloc_.deallocate(buffer_, alloc_size_); | |
| 370 } | |
| 371 buffer_ = new_buffer; | |
| 372 alloc_size_ = size; | |
| 373 } | |
| 374 | |
| 375 return !failed(); | |
| 376 } | |
| 377 | |
| 378 CheckBool append(const T* data, size_t size) WARN_UNUSED_RESULT { | |
| 379 if (failed()) | |
| 380 return false; | |
| 381 | |
| 382 if (size > alloc_.max_size() - size_) | |
| 383 return false; | |
| 384 | |
| 385 if (!size) | |
| 386 return true; | |
| 387 | |
| 388 if ((alloc_size_ - size_) < size) { | |
| 389 const size_t max_size = alloc_.max_size(); | |
| 390 size_t new_size = alloc_size_ ? alloc_size_ : kStartSize; | |
| 391 while (new_size < size_ + size) { | |
| 392 if (new_size < max_size - new_size) { | |
| 393 new_size *= 2; | |
| 394 } else { | |
| 395 new_size = max_size; | |
| 396 } | |
| 397 } | |
| 398 if (!reserve(new_size)) | |
| 399 return false; | |
| 400 } | |
| 401 | |
| 402 memcpy(buffer_ + size_, data, size * sizeof(T)); | |
| 403 size_ += size; | |
| 404 | |
| 405 return true; | |
| 406 } | |
| 407 | |
| 408 CheckBool resize(size_t size, const T& init_value) WARN_UNUSED_RESULT { | |
| 409 if (size > size_) { | |
| 410 if (!reserve(size)) | |
| 411 return false; | |
| 412 for (size_t i = size_; i < size; ++i) | |
| 413 buffer_[i] = init_value; | |
| 414 } else if (size < size_) { | |
| 415 // TODO(tommi): Should we allocate a new, smaller buffer? | |
| 416 // It might be faster for us to simply change the size. | |
| 417 } | |
| 418 | |
| 419 size_ = size; | |
| 420 | |
| 421 return true; | |
| 422 } | |
| 423 | |
| 424 CheckBool push_back(const T& item) WARN_UNUSED_RESULT { | |
| 425 return append(&item, 1); | |
| 426 } | |
| 427 | |
| 428 const T& back() const { | |
| 429 return buffer_[size_ - 1]; | |
| 430 } | |
| 431 | |
| 432 T& back() { | |
| 433 return buffer_[size_ - 1]; | |
| 434 } | |
| 435 | |
| 436 const T* begin() const { | |
| 437 if (!size_) | |
| 438 return NULL; | |
| 439 return &buffer_[0]; | |
| 440 } | |
| 441 | |
| 442 T* begin() { | |
| 443 if (!size_) | |
| 444 return NULL; | |
| 445 return &buffer_[0]; | |
| 446 } | |
| 447 | |
| 448 const T* end() const { | |
| 449 if (!size_) | |
| 450 return NULL; | |
| 451 return &buffer_[size_ - 1]; | |
| 452 } | |
| 453 | |
| 454 T* end() { | |
| 455 if (!size_) | |
| 456 return NULL; | |
| 457 return &buffer_[size_ - 1]; | |
| 458 } | |
| 459 | |
| 460 const T& operator[](size_t index) const { | |
| 461 DCHECK(index < size_); | |
| 462 return buffer_[index]; | |
| 463 } | |
| 464 | |
| 465 T& operator[](size_t index) { | |
| 466 DCHECK(index < size_); | |
| 467 return buffer_[index]; | |
| 468 } | |
| 469 | |
| 470 size_t size() const { | |
| 471 return size_; | |
| 472 } | |
| 473 | |
| 474 T* data() const { | |
| 475 return buffer_; | |
| 476 } | |
| 477 | |
| 478 // Returns true if an allocation failure has ever occurred for this object. | |
| 479 bool failed() const { | |
| 480 return alloc_size_ == kAllocationFailure; | |
| 481 } | |
| 482 | |
| 483 protected: | |
| 484 T* buffer_; | |
| 485 size_t size_; // how much of the buffer we're using. | |
| 486 size_t alloc_size_; // how much space we have allocated. | |
| 487 Allocator alloc_; | |
| 488 }; | |
| 489 | |
| 265 } // namespace courgette | 490 } // namespace courgette |
| 266 | 491 |
| 267 #endif // COURGETTE_MEMORY_ALLOCATOR_H_ | 492 #endif // COURGETTE_MEMORY_ALLOCATOR_H_ |
| OLD | NEW |