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/files/file.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.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 read/write virtual mapping of a physical file.
66 // Map a file from beginning to |size|.
67 bool Create(HANDLE file
, size_t size
);
70 // Returns true iff a mapping has been created.
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.
78 bool InitializeView(size_t size
);
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.
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.
100 // Returns true if the mapping is valid and memory is available.
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
);
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.
124 class MemoryAllocator
{
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
136 enum AllocationType
{
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
>
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
169 void deallocate(pointer ptr
, size_type size
) {
170 uint8
* mem
= reinterpret_cast<uint8
*>(ptr
);
172 if (mem
[0] == HEAP_ALLOCATION
) {
175 DCHECK_EQ(static_cast<uint8
>(FILE_ALLOCATION
), mem
[0]);
176 TempMapping
* mapping
= TempMapping::GetMappingFromPtr(mem
);
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.
188 if (count
> max_size())
191 size_type bytes
= count
* sizeof(T
);
194 // First see if we can do this allocation on the heap.
195 if (count
< kMaxHeapAllocationSize
)
196 mem
= new(std::nothrow
) uint8
[bytes
];
198 mem
[0] = static_cast<uint8
>(HEAP_ALLOCATION
);
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
) {
219 void destroy(pointer ptr
) {
223 size_type
max_size() const _THROW0() {
224 size_type count
= static_cast<size_type
>(-1) / sizeof(T
);
225 return (0 < count
? count
: 1);
231 // On Mac, Linux, we use a bare bones implementation that only does
234 class MemoryAllocator
{
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
>
246 // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
247 typedef MemoryAllocator
<OtherT
> other
;
253 explicit MemoryAllocator(const MemoryAllocator
<T
>& other
) {
256 template<class OtherT
>
257 explicit MemoryAllocator(const MemoryAllocator
<OtherT
>& other
) {
263 void deallocate(pointer ptr
, size_type size
) {
267 pointer
allocate(size_type count
) {
268 if (count
> max_size())
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
) {
282 void destroy(pointer ptr
) {
286 size_type
max_size() const {
287 size_type count
= static_cast<size_type
>(-1) / sizeof(T
);
288 return (0 < count
? count
: 1);
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
{
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) {
315 alloc_
.deallocate(buffer_
, alloc_size_
);
326 CheckBool
reserve(size_t size
) WARN_UNUSED_RESULT
{
330 if (size
<= alloc_size_
)
333 if (size
< kStartSize
)
336 // Use a size 1% higher than requested. In practice, this makes Courgette as
337 // much as 5x faster on typical Chrome update payloads as a lot of future
338 // reserve() calls will become no-ops instead of costly resizes that copy
339 // all the data. Note that doing this here instead of outside the function
340 // is more efficient, since it's after the no-op early return checks above.
342 T
* new_buffer
= alloc_
.allocate(size
);
345 alloc_size_
= kAllocationFailure
;
348 memcpy(new_buffer
, buffer_
, size_
* sizeof(T
));
349 alloc_
.deallocate(buffer_
, alloc_size_
);
351 buffer_
= new_buffer
;
358 CheckBool
append(const T
* data
, size_t size
) WARN_UNUSED_RESULT
{
362 if (size
> alloc_
.max_size() - size_
)
368 if ((alloc_size_
- size_
) < size
) {
369 const size_t max_size
= alloc_
.max_size();
370 size_t new_size
= alloc_size_
? alloc_size_
: kStartSize
;
371 while (new_size
< size_
+ size
) {
372 if (new_size
< max_size
- new_size
) {
378 if (!reserve(new_size
))
382 memcpy(buffer_
+ size_
, data
, size
* sizeof(T
));
388 CheckBool
resize(size_t size
, const T
& init_value
) WARN_UNUSED_RESULT
{
392 for (size_t i
= size_
; i
< size
; ++i
)
393 buffer_
[i
] = init_value
;
394 } else if (size
< size_
) {
395 // TODO(tommi): Should we allocate a new, smaller buffer?
396 // It might be faster for us to simply change the size.
404 CheckBool
push_back(const T
& item
) WARN_UNUSED_RESULT
{
405 return append(&item
, 1);
408 const T
& back() const {
409 return buffer_
[size_
- 1];
413 return buffer_
[size_
- 1];
416 const T
* begin() const {
428 const T
* end() const {
431 return &buffer_
[size_
- 1];
437 return &buffer_
[size_
- 1];
440 const T
& operator[](size_t index
) const {
441 DCHECK(index
< size_
);
442 return buffer_
[index
];
445 T
& operator[](size_t index
) {
446 DCHECK(index
< size_
);
447 return buffer_
[index
];
450 size_t size() const {
458 // Returns true if an allocation failure has ever occurred for this object.
459 bool failed() const {
460 return alloc_size_
== kAllocationFailure
;
465 size_t size_
; // how much of the buffer we're using.
466 size_t alloc_size_
; // how much space we have allocated.
470 } // namespace courgette
472 #endif // COURGETTE_MEMORY_ALLOCATOR_H_