Home | History | Annotate | Download | only in courgette
      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.
      4 
      5 #ifndef COURGETTE_MEMORY_ALLOCATOR_H_
      6 #define COURGETTE_MEMORY_ALLOCATOR_H_
      7 
      8 #include <memory>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/files/file_path.h"
     12 #include "base/logging.h"
     13 #include "base/platform_file.h"
     14 
     15 #ifndef NDEBUG
     16 
     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
     23   }
     24   CheckReturnValue(const CheckReturnValue& other)
     25       : value_(other.value_), checked_(other.checked_) {
     26     other.checked_ = true;
     27   }
     28 
     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;
     35     }
     36   }
     37 
     38   ~CheckReturnValue() {
     39     DCHECK(checked_);
     40   }
     41 
     42   operator const T&() const {
     43     checked_ = true;
     44     return value_;
     45   }
     46 
     47  private:
     48   T value_;
     49   mutable bool checked_;
     50 };
     51 typedef CheckReturnValue<bool> CheckBool;
     52 #else
     53 typedef bool CheckBool;
     54 #endif
     55 
     56 namespace courgette {
     57 
     58 #if defined(OS_WIN)
     59 
     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.
     64 class TempFile {
     65  public:
     66   TempFile();
     67   ~TempFile();
     68 
     69   bool Create();
     70   void Close();
     71   bool SetSize(size_t size);
     72 
     73   // Returns true iff the temp file is currently open.
     74   bool valid() const;
     75 
     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;
     79 
     80  protected:
     81   base::PlatformFile file_;
     82 };
     83 
     84 // Manages a read/write virtual mapping of a physical file.
     85 class FileMapping {
     86  public:
     87   FileMapping();
     88   ~FileMapping();
     89 
     90   // Map a file from beginning to |size|.
     91   bool Create(HANDLE file, size_t size);
     92   void Close();
     93 
     94   // Returns true iff a mapping has been created.
     95   bool valid() const;
     96 
     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.
     99   void* view() const;
    100 
    101  protected:
    102   bool InitializeView(size_t size);
    103 
    104   HANDLE mapping_;
    105   void* view_;
    106 };
    107 
    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.
    112 class TempMapping {
    113  public:
    114   TempMapping();
    115   ~TempMapping();
    116 
    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);
    120 
    121   // Returns a writable pointer to the reserved memory.
    122   void* memory() const;
    123 
    124   // Returns true if the mapping is valid and memory is available.
    125   bool valid() const;
    126 
    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);
    131 
    132  protected:
    133   TempFile file_;
    134   FileMapping mapping_;
    135 };
    136 
    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.
    147 template<class T>
    148 class MemoryAllocator {
    149  public:
    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;
    157 
    158   // Each allocation is tagged with a single byte so that we know how to
    159   // deallocate it.
    160   enum AllocationType {
    161     HEAP_ALLOCATION,
    162     FILE_ALLOCATION,
    163   };
    164 
    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;
    171 
    172   template<class OtherT>
    173   struct rebind {
    174     // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
    175     typedef MemoryAllocator<OtherT> other;
    176   };
    177 
    178   MemoryAllocator() _THROW0() {
    179   }
    180 
    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
    184   }
    185 
    186   template<class OtherT>
    187   MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() {  // NOLINT
    188   }
    189 
    190   ~MemoryAllocator() {
    191   }
    192 
    193   void deallocate(pointer ptr, size_type size) {
    194     uint8* mem = reinterpret_cast<uint8*>(ptr);
    195     mem -= sizeof(T);
    196     if (mem[0] == HEAP_ALLOCATION) {
    197       delete [] mem;
    198     } else {
    199       DCHECK_EQ(static_cast<uint8>(FILE_ALLOCATION), mem[0]);
    200       TempMapping* mapping = TempMapping::GetMappingFromPtr(mem);
    201       delete mapping;
    202     }
    203   }
    204 
    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.
    210     count++;
    211 
    212     if (count > max_size())
    213       return NULL;
    214 
    215     size_type bytes = count * sizeof(T);
    216     uint8* mem = NULL;
    217 
    218     // First see if we can do this allocation on the heap.
    219     if (count < kMaxHeapAllocationSize)
    220       mem = new(std::nothrow) uint8[bytes];
    221     if (mem != NULL) {
    222       mem[0] = static_cast<uint8>(HEAP_ALLOCATION);
    223     } else {
    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);
    230       }
    231     }
    232     return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL;
    233   }
    234 
    235   pointer allocate(size_type count, const void* hint) {
    236     return allocate(count);
    237   }
    238 
    239   void construct(pointer ptr, const T& value) {
    240     ::new(ptr) T(value);
    241   }
    242 
    243   void destroy(pointer ptr) {
    244     ptr->~T();
    245   }
    246 
    247   size_type max_size() const _THROW0() {
    248     size_type count = static_cast<size_type>(-1) / sizeof(T);
    249     return (0 < count ? count : 1);
    250   }
    251 };
    252 
    253 #else  // OS_WIN
    254 
    255 // On Mac, Linux, we use a bare bones implementation that only does
    256 // heap allocations.
    257 template<class T>
    258 class MemoryAllocator {
    259  public:
    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;
    267 
    268   template<class OtherT>
    269   struct rebind {
    270     // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT>
    271     typedef MemoryAllocator<OtherT> other;
    272   };
    273 
    274   MemoryAllocator() {
    275   }
    276 
    277   explicit MemoryAllocator(const MemoryAllocator<T>& other) {
    278   }
    279 
    280   template<class OtherT>
    281   explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) {
    282   }
    283 
    284   ~MemoryAllocator() {
    285   }
    286 
    287   void deallocate(pointer ptr, size_type size) {
    288     delete [] ptr;
    289   }
    290 
    291   pointer allocate(size_type count) {
    292     if (count > max_size())
    293       return NULL;
    294     return reinterpret_cast<pointer>(
    295         new(std::nothrow) uint8[count * sizeof(T)]);
    296   }
    297 
    298   pointer allocate(size_type count, const void* hint) {
    299     return allocate(count);
    300   }
    301 
    302   void construct(pointer ptr, const T& value) {
    303     ::new(ptr) T(value);
    304   }
    305 
    306   void destroy(pointer ptr) {
    307     ptr->~T();
    308   }
    309 
    310   size_type max_size() const {
    311     size_type count = static_cast<size_type>(-1) / sizeof(T);
    312     return (0 < count ? count : 1);
    313   }
    314 };
    315 
    316 #endif  // OS_WIN
    317 
    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 {
    325  public:
    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);
    329 
    330   NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) {
    331   }
    332 
    333   ~NoThrowBuffer() {
    334     clear();
    335   }
    336 
    337   void clear() {
    338     if (buffer_) {
    339       alloc_.deallocate(buffer_, alloc_size_);
    340       buffer_ = NULL;
    341       size_ = 0;
    342       alloc_size_ = 0;
    343     }
    344   }
    345 
    346   bool empty() const {
    347     return size_ == 0;
    348   }
    349 
    350   CheckBool reserve(size_t size) WARN_UNUSED_RESULT {
    351     if (failed())
    352       return false;
    353 
    354     if (size <= alloc_size_)
    355       return true;
    356 
    357     if (size < kStartSize)
    358       size = kStartSize;
    359 
    360     T* new_buffer = alloc_.allocate(size);
    361     if (!new_buffer) {
    362       clear();
    363       alloc_size_ = kAllocationFailure;
    364     } else {
    365       if (buffer_) {
    366         memcpy(new_buffer, buffer_, size_ * sizeof(T));
    367         alloc_.deallocate(buffer_, alloc_size_);
    368       }
    369       buffer_ = new_buffer;
    370       alloc_size_ = size;
    371     }
    372 
    373     return !failed();
    374   }
    375 
    376   CheckBool append(const T* data, size_t size) WARN_UNUSED_RESULT {
    377     if (failed())
    378       return false;
    379 
    380     if (size > alloc_.max_size() - size_)
    381       return false;
    382 
    383     if (!size)
    384       return true;
    385 
    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) {
    391           new_size *= 2;
    392         } else {
    393           new_size = max_size;
    394         }
    395       }
    396       if (!reserve(new_size))
    397         return false;
    398     }
    399 
    400     memcpy(buffer_ + size_, data, size * sizeof(T));
    401     size_ += size;
    402 
    403     return true;
    404   }
    405 
    406   CheckBool resize(size_t size, const T& init_value) WARN_UNUSED_RESULT {
    407     if (size > size_) {
    408       if (!reserve(size))
    409         return false;
    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.
    415     }
    416 
    417     size_ = size;
    418 
    419     return true;
    420   }
    421 
    422   CheckBool push_back(const T& item) WARN_UNUSED_RESULT {
    423     return append(&item, 1);
    424   }
    425 
    426   const T& back() const {
    427     return buffer_[size_ - 1];
    428   }
    429 
    430   T& back() {
    431     return buffer_[size_ - 1];
    432   }
    433 
    434   const T* begin() const {
    435     if (!size_)
    436       return NULL;
    437     return &buffer_[0];
    438   }
    439 
    440   T* begin() {
    441     if (!size_)
    442       return NULL;
    443     return &buffer_[0];
    444   }
    445 
    446   const T* end() const {
    447     if (!size_)
    448       return NULL;
    449     return &buffer_[size_ - 1];
    450   }
    451 
    452   T* end() {
    453     if (!size_)
    454       return NULL;
    455     return &buffer_[size_ - 1];
    456   }
    457 
    458   const T& operator[](size_t index) const {
    459     DCHECK(index < size_);
    460     return buffer_[index];
    461   }
    462 
    463   T& operator[](size_t index) {
    464     DCHECK(index < size_);
    465     return buffer_[index];
    466   }
    467 
    468   size_t size() const {
    469     return size_;
    470   }
    471 
    472   T* data() const {
    473     return buffer_;
    474   }
    475 
    476   // Returns true if an allocation failure has ever occurred for this object.
    477   bool failed() const {
    478     return alloc_size_ == kAllocationFailure;
    479   }
    480 
    481  protected:
    482   T* buffer_;
    483   size_t size_;  // how much of the buffer we're using.
    484   size_t alloc_size_;  // how much space we have allocated.
    485   Allocator alloc_;
    486 };
    487 
    488 }  // namespace courgette
    489 
    490 #endif  // COURGETTE_MEMORY_ALLOCATOR_H_
    491