diff options
Diffstat (limited to 'courgette/memory_allocator.h')
-rw-r--r-- | courgette/memory_allocator.h | 281 |
1 files changed, 252 insertions, 29 deletions
diff --git a/courgette/memory_allocator.h b/courgette/memory_allocator.h index 4774b88..08defe4 100644 --- a/courgette/memory_allocator.h +++ b/courgette/memory_allocator.h @@ -66,9 +66,9 @@ class TempFile { TempFile(); ~TempFile(); - __declspec(noinline) void Create(); + bool Create(); void Close(); - __declspec(noinline) void SetSize(size_t size); + bool SetSize(size_t size); // Returns true iff the temp file is currently open. bool valid() const; @@ -77,15 +77,8 @@ class TempFile { // a temp file has not been created. base::PlatformFile handle() const; - // Returns the size of the temp file. If the temp file doesn't exist, - // the return value is 0. - size_t size() const; - protected: - __declspec(noinline) FilePath PrepareTempFile(); - base::PlatformFile file_; - size_t size_; }; // Manages a read/write virtual mapping of a physical file. @@ -95,7 +88,7 @@ class FileMapping { ~FileMapping(); // Map a file from beginning to |size|. - __declspec(noinline) void Create(HANDLE file, size_t size); + bool Create(HANDLE file, size_t size); void Close(); // Returns true iff a mapping has been created. @@ -106,7 +99,7 @@ class FileMapping { void* view() const; protected: - __declspec(noinline) void InitializeView(size_t size); + bool InitializeView(size_t size); HANDLE mapping_; void* view_; @@ -122,12 +115,15 @@ class TempMapping { ~TempMapping(); // Creates a temporary file of size |size| and maps it into the current - // process' address space. - __declspec(noinline) void Initialize(size_t size); + // process's address space. + bool Initialize(size_t size); // Returns a writable pointer to the reserved memory. void* memory() const; + // Returns true if the mapping is valid and memory is available. + bool valid() const; + // Returns a pointer to the TempMapping instance that allocated the |mem| // block of memory. It's the callers responsibility to make sure that // the memory block was allocated by the TempMapping class. @@ -138,10 +134,11 @@ class TempMapping { FileMapping mapping_; }; -// An STL compatible memory allocator class that allocates memory either -// from the heap or via a temporary file. A file allocation will be made -// if either the requested memory size exceeds |kMaxHeapAllocationSize| -// or if a heap allocation fails. +// A memory allocator class that allocates memory either from the heap or via a +// temporary file. The interface is STL inspired but the class does not throw +// STL exceptions on allocation failure. Instead it returns NULL. +// A file allocation will be made if either the requested memory size exceeds +// |kMaxHeapAllocationSize| or if a heap allocation fails. // Allocating the memory as a mapping of a temporary file solves the problem // that there might not be enough physical memory and pagefile to support the // allocation. This can happen because these resources are too small, or @@ -174,7 +171,7 @@ class MemoryAllocator { template<class OtherT> struct rebind { - // convert an MemoryAllocator<T> to a MemoryAllocator<OtherT> + // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT> typedef MemoryAllocator<OtherT> other; }; @@ -183,11 +180,11 @@ class MemoryAllocator { // We can't use an explicit constructor here, as dictated by our style guide. // The implementation of basic_string in Visual Studio 2010 prevents this. - MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { + MemoryAllocator(const MemoryAllocator<T>& other) _THROW0() { // NOLINT } template<class OtherT> - explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { + MemoryAllocator(const MemoryAllocator<OtherT>& other) _THROW0() { // NOLINT } ~MemoryAllocator() { @@ -213,7 +210,7 @@ class MemoryAllocator { count++; if (count > max_size()) - throw std::length_error("overflow"); + return NULL; size_type bytes = count * sizeof(T); uint8* mem = NULL; @@ -226,12 +223,13 @@ class MemoryAllocator { } else { // If either the heap allocation failed or the request exceeds the // max heap allocation threshold, we back the allocation with a temp file. - TempMapping* mapping = new TempMapping(); - mapping->Initialize(bytes); - mem = reinterpret_cast<uint8*>(mapping->memory()); - mem[0] = static_cast<uint8>(FILE_ALLOCATION); + TempMapping* mapping = new(std::nothrow) TempMapping(); + if (mapping && mapping->Initialize(bytes)) { + mem = reinterpret_cast<uint8*>(mapping->memory()); + mem[0] = static_cast<uint8>(FILE_ALLOCATION); + } } - return reinterpret_cast<pointer>(mem + sizeof(T)); + return mem ? reinterpret_cast<pointer>(mem + sizeof(T)) : NULL; } pointer allocate(size_type count, const void* hint) { @@ -246,7 +244,7 @@ class MemoryAllocator { ptr->~T(); } - size_t max_size() const _THROW0() { + size_type max_size() const _THROW0() { size_type count = static_cast<size_type>(-1) / sizeof(T); return (0 < count ? count : 1); } @@ -254,14 +252,239 @@ class MemoryAllocator { #else // OS_WIN -// On Mac, Linux, we just use the default STL allocator. +// On Mac, Linux, we use a bare bones implementation that only does +// heap allocations. template<class T> -class MemoryAllocator : public std::allocator<T> { +class MemoryAllocator { public: + typedef T value_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef const value_type* const_pointer; + typedef const value_type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + template<class OtherT> + struct rebind { + // convert a MemoryAllocator<T> to a MemoryAllocator<OtherT> + typedef MemoryAllocator<OtherT> other; + }; + + MemoryAllocator() { + } + + explicit MemoryAllocator(const MemoryAllocator<T>& other) { + } + + template<class OtherT> + explicit MemoryAllocator(const MemoryAllocator<OtherT>& other) { + } + + ~MemoryAllocator() { + } + + void deallocate(pointer ptr, size_type size) { + delete [] ptr; + } + + pointer allocate(size_type count) { + if (count > max_size()) + return NULL; + return reinterpret_cast<pointer>( + new(std::nothrow) uint8[count * sizeof(T)]); + } + + pointer allocate(size_type count, const void* hint) { + return allocate(count); + } + + void construct(pointer ptr, const T& value) { + ::new(ptr) T(value); + } + + void destroy(pointer ptr) { + ptr->~T(); + } + + size_type max_size() const { + size_type count = static_cast<size_type>(-1) / sizeof(T); + return (0 < count ? count : 1); + } }; #endif // OS_WIN +// Manages a growable buffer. The buffer allocation is done by the +// MemoryAllocator class. This class will not throw exceptions so call sites +// must be prepared to handle memory allocation failures. +// The interface is STL inspired to avoid having to make too many changes +// to code that previously was using STL. +template<typename T, class Allocator = MemoryAllocator<T> > +class NoThrowBuffer { + public: + typedef T value_type; + static const size_t kAllocationFailure = 0xffffffff; + static const size_t kStartSize = sizeof(T) > 0x100 ? 1 : 0x100 / sizeof(T); + + NoThrowBuffer() : buffer_(NULL), size_(0), alloc_size_(0) { + } + + ~NoThrowBuffer() { + clear(); + } + + void clear() { + if (buffer_) { + alloc_.deallocate(buffer_, alloc_size_); + buffer_ = NULL; + size_ = 0; + alloc_size_ = 0; + } + } + + bool empty() const { + return size_ == 0; + } + + CheckBool reserve(size_t size) WARN_UNUSED_RESULT { + if (failed()) + return false; + + if (size <= alloc_size_) + return true; + + if (size < kStartSize) + size = kStartSize; + + T* new_buffer = alloc_.allocate(size); + if (!new_buffer) { + clear(); + alloc_size_ = kAllocationFailure; + } else { + if (buffer_) { + memcpy(new_buffer, buffer_, size_ * sizeof(T)); + alloc_.deallocate(buffer_, alloc_size_); + } + buffer_ = new_buffer; + alloc_size_ = size; + } + + return !failed(); + } + + CheckBool append(const T* data, size_t size) WARN_UNUSED_RESULT { + if (failed()) + return false; + + if (size > alloc_.max_size() - size_) + return false; + + if (!size) + return true; + + if ((alloc_size_ - size_) < size) { + const size_t max_size = alloc_.max_size(); + size_t new_size = alloc_size_ ? alloc_size_ : kStartSize; + while (new_size < size_ + size) { + if (new_size < max_size - new_size) { + new_size *= 2; + } else { + new_size = max_size; + } + } + if (!reserve(new_size)) + return false; + } + + memcpy(buffer_ + size_, data, size * sizeof(T)); + size_ += size; + + return true; + } + + CheckBool resize(size_t size, const T& init_value) WARN_UNUSED_RESULT { + if (size > size_) { + if (!reserve(size)) + return false; + for (size_t i = size_; i < size; ++i) + buffer_[i] = init_value; + } else if (size < size_) { + // TODO(tommi): Should we allocate a new, smaller buffer? + // It might be faster for us to simply change the size. + } + + size_ = size; + + return true; + } + + CheckBool push_back(const T& item) WARN_UNUSED_RESULT { + return append(&item, 1); + } + + const T& back() const { + return buffer_[size_ - 1]; + } + + T& back() { + return buffer_[size_ - 1]; + } + + const T* begin() const { + if (!size_) + return NULL; + return &buffer_[0]; + } + + T* begin() { + if (!size_) + return NULL; + return &buffer_[0]; + } + + const T* end() const { + if (!size_) + return NULL; + return &buffer_[size_ - 1]; + } + + T* end() { + if (!size_) + return NULL; + return &buffer_[size_ - 1]; + } + + const T& operator[](size_t index) const { + DCHECK(index < size_); + return buffer_[index]; + } + + T& operator[](size_t index) { + DCHECK(index < size_); + return buffer_[index]; + } + + size_t size() const { + return size_; + } + + T* data() const { + return buffer_; + } + + // Returns true if an allocation failure has ever occurred for this object. + bool failed() const { + return alloc_size_ == kAllocationFailure; + } + + protected: + T* buffer_; + size_t size_; // how much of the buffer we're using. + size_t alloc_size_; // how much space we have allocated. + Allocator alloc_; +}; + } // namespace courgette #endif // COURGETTE_MEMORY_ALLOCATOR_H_ |