-
-
Save FrancoisChabot/174bfd8916ed4fbb5181e502ca80373d to your computer and use it in GitHub Desktop.
Updated version of arena allocator to address feedback in https://codereview.stackexchange.com/a/174953/128454
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<size_t BLOCK_SIZE, size_t BLOCK_ALIGN> | |
struct AlignedBlockAllocatorTraits { | |
using storage_t = std::aligned_storage<BLOCK_SIZE, BLOCK_ALIGN>; | |
union entry_t { | |
storage_t storage; | |
size_t next_free; | |
}; | |
}; | |
template<typename T> | |
struct BlockAllocatorTraits : public AlignedBlockAllocatorTraits<sizeof(T), alignof(T)> {}; | |
template<size_t BLOCK_SIZE, | |
size_t BLOCK_ALIGN, | |
typename ALLOC_T=std::allocator<typename AlignedBlockAllocatorTraits<BLOCK_SIZE, BLOCK_ALIGN>::entry_t>> | |
class BlockAllocator { | |
using storage_t = typename AlignedBlockAllocatorTraits<BLOCK_SIZE, BLOCK_ALIGN>::storage_t; | |
using entry_t = typename AlignedBlockAllocatorTraits<BLOCK_SIZE, BLOCK_ALIGN>::entry_t; | |
public: | |
BlockAllocator(size_t count, ALLOC_T const& alloc=ALLOC_T()) | |
: storage_(count, alloc) { } | |
BlockAllocator(BlockAllocator const&) = delete; | |
BlockAllocator& operator=(BlockAllocator const&) = delete; | |
BlockAllocator(BlockAllocator&& rhs) | |
: storage_(std::move(rhs.storage_)), | |
alloc_count_(rhs.alloc_count_), | |
free_stack_top_(rhs.free_stack_top_) { | |
rhs.alloc_count_ = 0; | |
rhs.free_stack_top_ = npos; | |
} | |
BlockAllocator& operator=(BlockAllocator&& rhs) { | |
storage_ = std::move(rhs.storage_); | |
alloc_count_ = rhs.alloc_count_; | |
free_stack_top_ = rhs.free_stack_top_; | |
rhs.alloc_count_ = 0; | |
rhs.free_stack_top_ = npos; | |
} | |
char* allocate() { | |
entry_t* selected_entry = nullptr; | |
if (free_stack_top_ != npos) { | |
// If the free list is not empty, pick from it. | |
assert(free_stack_top_ < storage_.size()); | |
selected_entry = &storage_[free_stack_top_]; | |
// Update the free list | |
free_stack_top_ = selected_entry->next_free; | |
assert(free_stack_top_ < storage_.size() || free_stack_top_ == npos); | |
} | |
else { | |
// Pick from the end of the currently allocated block, which is currently contiguous. | |
if (alloc_count_ == storage_.size()) { | |
throw std::bad_alloc(); | |
} | |
assert(alloc_count_ < storage_.size()); | |
selected_entry = &storage_[alloc_count_]; | |
} | |
selected_entry->storage = storage_t(); | |
++alloc_count_; | |
return reinterpret_cast<char*>(&selected_entry->storage); | |
} | |
void free(char* what) { | |
auto as_storage = reinterpret_cast<storage_t*>(what); | |
auto as_entry = reinterpret_cast<entry_t*>(as_storage); | |
auto index = getId_(as_entry); | |
// Insert the freed node on the free stack | |
storage_[index].next_free = free_stack_top_; | |
free_stack_top_ = index; | |
--alloc_count_; | |
} | |
size_t available() const { | |
return storage_.size() - alloc_count_; | |
} | |
size_t capacity() const { | |
return storage_.size(); | |
} | |
bool full() const { | |
return alloc_count_ == capacity(); | |
} | |
bool empty() const { | |
return alloc_count_ == 0; | |
} | |
private: | |
// returns the index of a given storage entry. | |
size_t getId_(entry_t* storage_entry) { | |
assert(storage_entry >= &storage_.front() && storage_entry <= &storage_.back()); | |
return storage_entry - &storage_[0]; | |
} | |
static const size_t npos = -1; | |
vector<entry_t, ALLOC_T> storage_; | |
size_t alloc_count_ = 0; | |
size_t free_stack_top_ = npos; | |
}; | |
template<typename T, typename ALLOC_T=std::allocator<typename BlockAllocatorTraits<T>::entry_t>> | |
class Arena { | |
BlockAllocator<sizeof(T), alignof(T), ALLOC_T> allocator_; | |
public: | |
Arena(size_t count, ALLOC_T const& alloc=ALLOC_T()) | |
: allocator_(count, alloc) {} | |
size_t capacity() const { | |
return allocator_.capacity(); | |
} | |
size_t available() const { | |
return allocator_.available(); | |
} | |
bool full() const { | |
return allocator_.full(); | |
} | |
bool empty() const { | |
return allocator_.empty(); | |
} | |
template<typename... ARGS> | |
T* create(ARGS &&... args) { | |
return new(allocator_.allocate()) T(std::forward<ARGS>(args)...); | |
} | |
void destroy(T* what) { | |
what->~T(); | |
allocator_.free(reinterpret_cast<char*>(what)); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment