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 kSize = static_cast<std::size_t>(8) * 1024; // 8 KB
Arena() = default;
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&>>>
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
T* map(const std::vector<S>& src, Fn&& map_fn) {
const size_t elements = src.size();
if (elements == 0) {
return nullptr;
}
T* result = alloc<T>(elements);
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* mapOne(const S& src, Fn&& map_fn) {
T* result = alloc<T>(1);
std::forward<Fn>(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();
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
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;
// NOLINTBEGIN(cppcoreguidelines-no-malloc,hicpp-no-malloc,cppcoreguidelines-owning-memory)
auto* ptr = ::malloc(size_with_alignment);
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc)
aligned_ptr = std::align(alignment, bytes_needed, ptr, size_with_alignment);
if (aligned_ptr == nullptr) {
#ifndef __wasm32__
throw std::bad_alloc();
#else
std::terminate();
#endif
}
overflow_.emplace_back(static_cast<char*>(ptr));
// NOLINTEND(cppcoreguidelines-no-malloc,hicpp-no-malloc,cppcoreguidelines-owning-memory)
return reinterpret_cast<T*>(aligned_ptr);
}
// Calculate the new offset
offset_ = kSize - space_left + bytes_needed;
return reinterpret_cast<T*>(aligned_ptr);
}
[[nodiscard]] size_t used() const {
return offset_;
}
[[nodiscard]] size_t available() const {
return kSize - offset_;
}
private:
struct Deleter {
void operator()(char* ptr) const {
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc,cppcoreguidelines-owning-memory)
free(ptr);
}
};
std::array<uint8_t, kSize> buffer_{};
std::size_t offset_ = 0;
std::vector<std::unique_ptr<char, Deleter>> overflow_;
};
} // namespace foxglove