Chromium Code Reviews| Index: courgette/memory_allocator.h |
| =================================================================== |
| --- courgette/memory_allocator.h (revision 80344) |
| +++ courgette/memory_allocator.h (working copy) |
| @@ -66,9 +66,9 @@ |
| 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 @@ |
| // 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 @@ |
| ~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,8 @@ |
| void* view() const; |
| protected: |
| - __declspec(noinline) void InitializeView(size_t size); |
| + __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.
|
| + bool InitializeView(size_t size); |
| HANDLE mapping_; |
| void* view_; |
| @@ -123,11 +117,14 @@ |
| // Creates a temporary file of size |size| and maps it into the current |
| // 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.
|
| - __declspec(noinline) void Initialize(size_t size); |
| + 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 +135,12 @@ |
| 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. |
| +// 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!
|
| +// 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 +173,7 @@ |
| 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 +182,11 @@ |
| // 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 +212,7 @@ |
| count++; |
| if (count > max_size()) |
| - throw std::length_error("overflow"); |
| + return NULL; |
| size_type bytes = count * sizeof(T); |
| uint8* mem = NULL; |
| @@ -226,12 +225,13 @@ |
| } 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) { |
| @@ -254,14 +254,239 @@ |
| #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_t 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_ |