#ifndef F20CF38F_7327_4608_8307_6AE058041CD5 #define F20CF38F_7327_4608_8307_6AE058041CD5 #include #include #include #include namespace mesytec::mnode::mana { class Arena { public: static constexpr size_t initial_segment_size = 1u << 20; static constexpr size_t default_pad = 8; Arena() = default; ~Arena() = default; Arena(Arena &&other); Arena &operator=(Arena &&other); // pushes at least the required amount of memory onto the arena. // 'required' is rounded up to the nearest multiple of 'default_pad' to make // subsequent allocations aligned. u8 *push_size(size_t required) { size_t padded = round_up(required, default_pad); size_t pad_waste = padded - required; required = padded; if (!segment_ || segment_->free() < required) { // could waste complete segments if required > default size of 1u << 20 but that's fine // for now segment_ = &segments_.emplace_back(Arena::Segment(std::max(required, initial_segment_size))); } assert(segment_ && segment_->free() >= required); std::memset(segment_->cur(), 0, required); segment_->used += required; ++allocations_; pad_waste_ += pad_waste; return segment_->cur(); } template T *push_t(size_t count = 1) { static_assert(std::is_trivial::value, "T must be a trivial type"); return reinterpret_cast(push_size(sizeof(T) * count)); } const char *push_cstr(const std::string &str) { auto mem = push_size(str.size() + 1); std::memcpy(mem, str.c_str(), str.size()); return reinterpret_cast(mem); } void reset() { std::for_each(std::begin(segments_), std::end(segments_), [](Segment &seg) { seg.reset(); }); segment_ = segments_.empty() ? nullptr : &segments_.front(); allocations_ = 0; pad_waste_ = 0; } size_t allocations() const { return allocations_; } size_t pad_waste() const { return pad_waste_; } size_t segment_count() const { return segments_.size(); } size_t capacity() const { auto accu = [](size_t sum, const Segment &seg) { return sum + seg.data.size(); }; return std::accumulate(std::begin(segments_), std::end(segments_), static_cast(0u), accu); } size_t used() const { auto accu = [](size_t sum, const Segment &seg) { return sum + seg.used; }; return std::accumulate(std::begin(segments_), std::end(segments_), static_cast(0u), accu) - pad_waste_; } private: Arena(const Arena &) = delete; Arena &operator=(const Arena &) = delete; struct Segment { std::vector data; size_t used; explicit Segment(size_t size) : data(size) , used(0) { } size_t free() const { return data.size() - used; } u8 *cur() { return data.data() + used; } void reset() { used = 0; } }; std::list segments_; Segment *segment_ = nullptr; size_t allocations_ = 0; size_t pad_waste_ = 0; }; } // namespace mesytec::mnode::mana #endif /* F20CF38F_7327_4608_8307_6AE058041CD5 */