Program Listing for File arena.hpp¶
↰ Return to documentation for file (foxglove/include/foxglove/arena.hpp
)
#pragma once
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <new>
#include <type_traits>
#include <vector>
namespace foxglove {
class Arena {
public:
static constexpr std::size_t Size = 128 * 1024; // 128 KB
Arena()
: offset_(0) {}
template<
typename T, typename S, typename Fn,
typename = std::enable_if_t<std::is_pod_v<T> && std::is_invocable_v<Fn, T&, const S&, Arena&>>>
T* map(const std::vector<S>& src, Fn&& map_fn) {
const size_t elements = src.size();
T* result = (elements > 0) ? alloc<T>(elements) : nullptr;
T* current = result;
// Convert the elements from S to T, placing them in the result array
for (auto it = src.begin(); it != src.end(); ++it) {
map_fn(*current++, *it, *this);
}
return result;
}
template<
typename T, typename S, typename Fn,
typename = std::enable_if_t<std::is_pod_v<T> && std::is_invocable_v<Fn, T&, const S&, Arena&>>>
T* map_one(const S& src, Fn&& map_fn) {
T* result = alloc<T>(1);
map_fn(*result, src, *this);
return result;
}
template<typename T>
T* alloc(size_t elements) {
assert(elements > 0);
const size_t bytes_needed = elements * sizeof(T);
const size_t alignment = alignof(T);
// Calculate space available in the buffer
size_t space_left = available();
void* buffer_ptr = &buffer_[offset_];
// Align the pointer within the buffer
void* aligned_ptr = std::align(alignment, bytes_needed, buffer_ptr, space_left);
// Check if we have enough space
if (aligned_ptr == nullptr) {
// We don't use aligned_alloc because it fails on some platforms for larger alignments
size_t size_with_alignment = alignment + bytes_needed;
auto ptr = ::malloc(size_with_alignment);
aligned_ptr = std::align(alignment, bytes_needed, ptr, size_with_alignment);
if (aligned_ptr == nullptr) {
throw std::bad_alloc();
}
overflow_.emplace_back(static_cast<char*>(aligned_ptr));
return reinterpret_cast<T*>(aligned_ptr);
}
// Calculate the new offset
offset_ = Size - space_left + bytes_needed;
return reinterpret_cast<T*>(aligned_ptr);
}
size_t used() const {
return offset_;
}
size_t available() const {
return Size - offset_;
}
private:
struct Deleter {
void operator()(char* ptr) const {
free(ptr);
}
};
std::array<uint8_t, Size> buffer_;
std::size_t offset_;
std::vector<std::unique_ptr<char, Deleter>> overflow_;
};
} // namespace foxglove