Skip to content

Instantly share code, notes, and snippets.

@sekia
Created May 7, 2021 12:33
Show Gist options
  • Save sekia/23b92ed7c7e975a650a305f955c5d243 to your computer and use it in GitHub Desktop.
Save sekia/23b92ed7c7e975a650a305f955c5d243 to your computer and use it in GitHub Desktop.
Slab allocator using C++ 20 <bit> library
#include <array>
#include <bitset>
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <new>
#include <utility>
#include <vector>
template<typename C, std::size_t N = 256>
class Arena final {
private:
class Deleter;
public:
using Ptr = std::unique_ptr<C, Deleter>;
static constexpr std::size_t Capacity() noexcept {
std::size_t mask = UsageBitMapSize - 1;
return (N & mask) == 0 ? N : (N & ~mask) + UsageBitMapSize;
}
Arena(): buffer_(reinterpret_cast<C *>(std::calloc(Capacity(), sizeof(C)))) {
if (buffer_ == nullptr) {
throw std::bad_alloc();
}
}
~Arena() noexcept {
std::free(buffer_);
}
template<typename ...Ts>
Ptr Create(Ts&& ...args) {
std::size_t i = FindFreeSlot();
if (i >= N) {
throw std::bad_alloc();
}
SetSlotUsage(i, true);
C *slot = &buffer_[i];
return Ptr(new (slot) C(std::forward<Ts>(args)...), Deleter(this));
}
private:
static constexpr std::size_t UsageBitMapSize = sizeof(unsigned long);
struct Deleter {
Deleter() = delete;
explicit Deleter(Arena *arena): arena_(arena) {}
Deleter(const Deleter&) = default;
Deleter(Deleter&&) = default;
void operator()(C *obj) {
arena_->ReleaseSlot(obj - arena_->buffer_);
}
Arena *arena_;
};
constexpr std::size_t FindFreeSlot() const noexcept {
std::size_t i = 0;
for (const auto& bitmap : alloted_) {
std::size_t free = std::countr_one(bitmap.to_ulong());
i += free;
if (free < bitmap.size()) {
break;
}
}
return i;
}
void ReleaseSlot(std::size_t i) {
SetSlotUsage(i, false);
C *target = &buffer_[i];
target->~C();
}
constexpr void SetSlotUsage(std::size_t i, bool value) noexcept {
alloted_[i / UsageBitMapSize].set(i % UsageBitMapSize, value);
}
std::array<
std::bitset<UsageBitMapSize>,
Capacity() / UsageBitMapSize> alloted_;
C *buffer_;
};
class C {
public:
C() = default;
C(int n): n_(n) {
printf("Created: %d\n", n_);
}
~C() {
printf("Demolished: %d\n", n_);
n_ = -1;
}
int GetN() const { return n_; }
private:
int n_ = 0;
};
int main() {
Arena<C> arena;
std::vector<Arena<C>::Ptr> ptrs;
for (int i = 0; i < arena.Capacity(); ++i) {
ptrs.push_back(arena.Create(i));
}
ptrs[42].reset();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment