Skip to content

Instantly share code, notes, and snippets.

@FrancoisChabot
Last active February 10, 2020 22:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save FrancoisChabot/174bfd8916ed4fbb5181e502ca80373d to your computer and use it in GitHub Desktop.
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
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