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_
10 #include "base/basictypes.h"
11 #include "base/file_path.h"
12 #include "base/logging.h"
13 #include "base/platform_file.h"
17 // A helper class to track down call sites that are not handling error cases.
19 class CheckReturnValue
{
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
) {
32 value_
= other
.value_
;
33 checked_
= other
.checked_
;
34 other
.checked_
= true;
42 operator const T
&() const {
49 mutable bool checked_
;
51 typedef CheckReturnValue
<bool> CheckBool
;
53 typedef bool CheckBool
;
60 // Manages a temporary file. The file is created in the %TEMP% folder and
61 // is deleted when the file handle is closed.
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.
71 bool SetSize(size_t size
);
73 // Returns true iff the temp file is currently open.
76 // Returns the handle of the temporary file or INVALID_HANDLE_VALUE if
77 // a temp file has not been created.
78 base::PlatformFile
handle() const;
81 base::PlatformFile file_
;
84 // Manages a read/write virtual mapping of a physical file.
90 // Map a file from beginning to |size|.
91 bool Create(HANDLE file
, size_t size
);
94 // Returns true iff a mapping has been created.
97 // Returns a writable pointer to the beginning of the memory mapped file.
98 // If Create has not been called successfully, return value is NULL.
102 bool InitializeView(size_t size
);
108 // Manages a temporary file and a memory mapping of the temporary file.
109 // The memory that this class manages holds a pointer back to the TempMapping
110 // object itself, so that given a memory pointer allocated by this class,
111 // you can get a pointer to the TempMapping instance that owns that memory.
117 // Creates a temporary file of size |size| and maps it into the current
118 // process's address space.
119 bool Initialize(size_t size
);
121 // Returns a writable pointer to the reserved memory.
122 void* memory() const;
124 // Returns true if the mapping is valid and memory is available.
127 // Returns a pointer to the TempMapping instance that allocated the |mem|
128 // block of memory. It's the callers responsibility to make sure that
129 // the memory block was allocated by the TempMapping class.
130 static TempMapping
* GetMappingFromPtr(void* mem
);
134 FileMapping mapping_
;
137 // A memory allocator class that allocates memory either from the heap or via a
138 // temporary file. The interface is STL inspired but the class does not throw
139 // STL exceptions on allocation failure. Instead it returns NULL.
140 // A file allocation will be made if either the requested memory size exceeds
141 // |kMaxHeapAllocationSize| or if a heap allocation fails.
142 // Allocating the memory as a mapping of a temporary file solves the problem
143 // that there might not be enough physical memory and pagefile to support the
144 // allocation. This can happen because these resources are too small, or
145 // already committed to other processes. Provided there is enough disk, the
146 // temporary file acts like a pagefile that other processes can't access.
148 class MemoryAllocator
{
150 typedef T value_type
;
151 typedef value_type
* pointer
;
152 typedef value_type
& reference
;
153 typedef const value_type
* const_pointer
;
154 typedef const value_type
& const_reference
;
155 typedef size_t size_type
;
156 typedef ptrdiff_t difference_type
;
158 // Each allocation is tagged with a single byte so that we know how to
160 enum AllocationType
{
165 // 5MB is the maximum heap allocation size that we'll attempt.
166 // When applying a patch for Chrome 10.X we found that at this
167 // threshold there were 17 allocations higher than this threshold
168 // (largest at 136MB) 10 allocations just below the threshold and 6362
169 // smaller allocations.
170 static const size_t kMaxHeapAllocationSize
= 1024 * 1024 * 5;
172 template<class OtherT
>
174 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
175 typedef MemoryAllocator
<OtherT
> other
;
178 MemoryAllocator() _THROW0() {
181 // We can't use an explicit constructor here, as dictated by our style guide.
182 // The implementation of basic_string in Visual Studio 2010 prevents this.
183 MemoryAllocator(const MemoryAllocator
<T
>& other
) _THROW0() { // NOLINT
186 template<class OtherT
>
187 MemoryAllocator(const MemoryAllocator
<OtherT
>& other
) _THROW0() { // NOLINT
193 void deallocate(pointer ptr
, size_type size
) {
194 uint8
* mem
= reinterpret_cast<uint8
*>(ptr
);
196 if (mem
[0] == HEAP_ALLOCATION
) {
199 DCHECK_EQ(static_cast<uint8
>(FILE_ALLOCATION
), mem
[0]);
200 TempMapping
* mapping
= TempMapping::GetMappingFromPtr(mem
);
205 pointer
allocate(size_type count
) {
206 // We use the first byte of each allocation to mark the allocation type.
207 // However, so that the allocation is properly aligned, we allocate an
208 // extra element and then use the first byte of the first element
209 // to mark the allocation type.
212 if (count
> max_size())
215 size_type bytes
= count
* sizeof(T
);
218 // First see if we can do this allocation on the heap.
219 if (count
< kMaxHeapAllocationSize
)
220 mem
= new(std::nothrow
) uint8
[bytes
];
222 mem
[0] = static_cast<uint8
>(HEAP_ALLOCATION
);
224 // If either the heap allocation failed or the request exceeds the
225 // max heap allocation threshold, we back the allocation with a temp file.
226 TempMapping
* mapping
= new(std::nothrow
) TempMapping();
227 if (mapping
&& mapping
->Initialize(bytes
)) {
228 mem
= reinterpret_cast<uint8
*>(mapping
->memory());
229 mem
[0] = static_cast<uint8
>(FILE_ALLOCATION
);
232 return mem
? reinterpret_cast<pointer
>(mem
+ sizeof(T
)) : NULL
;
235 pointer
allocate(size_type count
, const void* hint
) {
236 return allocate(count
);
239 void construct(pointer ptr
, const T
& value
) {
243 void destroy(pointer ptr
) {
247 size_type
max_size() const _THROW0() {
248 size_type count
= static_cast<size_type
>(-1) / sizeof(T
);
249 return (0 < count
? count
: 1);
255 // On Mac, Linux, we use a bare bones implementation that only does
258 class MemoryAllocator
{
260 typedef T value_type
;
261 typedef value_type
* pointer
;
262 typedef value_type
& reference
;
263 typedef const value_type
* const_pointer
;
264 typedef const value_type
& const_reference
;
265 typedef size_t size_type
;
266 typedef ptrdiff_t difference_type
;
268 template<class OtherT
>
270 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
271 typedef MemoryAllocator
<OtherT
> other
;
277 explicit MemoryAllocator(const MemoryAllocator
<T
>& other
) {
280 template<class OtherT
>
281 explicit MemoryAllocator(const MemoryAllocator
<OtherT
>& other
) {
287 void deallocate(pointer ptr
, size_type size
) {
291 pointer
allocate(size_type count
) {
292 if (count
> max_size())
294 return reinterpret_cast<pointer
>(
295 new(std::nothrow
) uint8
[count
* sizeof(T
)]);
298 pointer
allocate(size_type count
, const void* hint
) {
299 return allocate(count
);
302 void construct(pointer ptr
, const T
& value
) {
306 void destroy(pointer ptr
) {
310 size_type
max_size() const {
311 size_type count
= static_cast<size_type
>(-1) / sizeof(T
);
312 return (0 < count
? count
: 1);
318 // Manages a growable buffer. The buffer allocation is done by the
319 // MemoryAllocator class. This class will not throw exceptions so call sites
320 // must be prepared to handle memory allocation failures.
321 // The interface is STL inspired to avoid having to make too many changes
322 // to code that previously was using STL.
323 template<typename T
, class Allocator
= MemoryAllocator
<T
> >
324 class NoThrowBuffer
{
326 typedef T value_type
;
327 static const size_t kAllocationFailure
= 0xffffffff;
328 static const size_t kStartSize
= sizeof(T
) > 0x100 ? 1 : 0x100 / sizeof(T
);
330 NoThrowBuffer() : buffer_(NULL
), size_(0), alloc_size_(0) {
339 alloc_
.deallocate(buffer_
, alloc_size_
);
350 CheckBool
reserve(size_t size
) WARN_UNUSED_RESULT
{
354 if (size
<= alloc_size_
)
357 if (size
< kStartSize
)
360 T
* new_buffer
= alloc_
.allocate(size
);
363 alloc_size_
= kAllocationFailure
;
366 memcpy(new_buffer
, buffer_
, size_
* sizeof(T
));
367 alloc_
.deallocate(buffer_
, alloc_size_
);
369 buffer_
= new_buffer
;
376 CheckBool
append(const T
* data
, size_t size
) WARN_UNUSED_RESULT
{
380 if (size
> alloc_
.max_size() - size_
)
386 if ((alloc_size_
- size_
) < size
) {
387 const size_t max_size
= alloc_
.max_size();
388 size_t new_size
= alloc_size_
? alloc_size_
: kStartSize
;
389 while (new_size
< size_
+ size
) {
390 if (new_size
< max_size
- new_size
) {
396 if (!reserve(new_size
))
400 memcpy(buffer_
+ size_
, data
, size
* sizeof(T
));
406 CheckBool
resize(size_t size
, const T
& init_value
) WARN_UNUSED_RESULT
{
410 for (size_t i
= size_
; i
< size
; ++i
)
411 buffer_
[i
] = init_value
;
412 } else if (size
< size_
) {
413 // TODO(tommi): Should we allocate a new, smaller buffer?
414 // It might be faster for us to simply change the size.
422 CheckBool
push_back(const T
& item
) WARN_UNUSED_RESULT
{
423 return append(&item
, 1);
426 const T
& back() const {
427 return buffer_
[size_
- 1];
431 return buffer_
[size_
- 1];
434 const T
* begin() const {
446 const T
* end() const {
449 return &buffer_
[size_
- 1];
455 return &buffer_
[size_
- 1];
458 const T
& operator[](size_t index
) const {
459 DCHECK(index
< size_
);
460 return buffer_
[index
];
463 T
& operator[](size_t index
) {
464 DCHECK(index
< size_
);
465 return buffer_
[index
];
468 size_t size() const {
476 // Returns true if an allocation failure has ever occurred for this object.
477 bool failed() const {
478 return alloc_size_
== kAllocationFailure
;
483 size_t size_
; // how much of the buffer we're using.
484 size_t alloc_size_
; // how much space we have allocated.
488 } // namespace courgette
490 #endif // COURGETTE_MEMORY_ALLOCATOR_H_