From 6d93197f86e56403f7ca5479be505cc388abaa7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20L=C3=BCke?= Date: Wed, 25 Dec 2024 03:17:53 +0100 Subject: [PATCH] mana::Arena: fixes and segment limit and tests --- src/CMakeLists.txt | 4 +- src/internal/mana_arena.h | 33 +++++++++++----- src/mana.test.cc | 79 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 103 insertions(+), 13 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cd6b8bc..3a2f614 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -set(MVLC_NNG_MNODE_WARN_FLAGS -Wall -Wextra -Wpedantic) +set(MVLC_NNG_MNODE_WARN_FLAGS -Wall -Wextra -Wpedantic -Werror=return-type) add_library(mesytec-mnode mnode_nng.cc mnode_nng_async.cc mnode_nng_proto.cc) target_include_directories(mesytec-mnode @@ -13,7 +13,7 @@ add_subdirectory(tools) if (MNODE_BUILD_TESTS) function (add_mnode_gtest name) add_executable(test_${name} ${name}.test.cc) - target_link_libraries(test_${name} PRIVATE mesytec-mnode GTest::gtest_main) + target_link_libraries(test_${name} PRIVATE mesytec-mnode GTest::gtest_main spdlog) add_test(NAME name COMMAND $) endfunction() diff --git a/src/internal/mana_arena.h b/src/internal/mana_arena.h index 7de87ee..097d3f5 100644 --- a/src/internal/mana_arena.h +++ b/src/internal/mana_arena.h @@ -14,6 +14,7 @@ class Arena public: static constexpr size_t initial_segment_size = 1u << 20; static constexpr size_t default_pad = 8; + static constexpr size_t default_max_segments = 1; Arena() = default; ~Arena() = default; @@ -32,19 +33,27 @@ class Arena 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))); + if (segment_count() < max_segments()) + { + // 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))); + } + else + { + throw std::bad_alloc(); + } } assert(segment_ && segment_->free() >= required); - std::memset(segment_->cur(), 0, required); + auto result = segment_->cur(); + std::memset(result, 0, required); segment_->used += required; ++allocations_; pad_waste_ += pad_waste; - return segment_->cur(); + return result; } template T *push_t(size_t count = 1) @@ -53,11 +62,11 @@ class Arena return reinterpret_cast(push_size(sizeof(T) * count)); } - const char *push_cstr(const std::string &str) + 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); + return reinterpret_cast(mem); } void reset() @@ -69,6 +78,7 @@ class Arena pad_waste_ = 0; } + size_t max_segments() const { return max_segments_; } size_t allocations() const { return allocations_; } size_t pad_waste() const { return pad_waste_; } size_t segment_count() const { return segments_.size(); } @@ -83,9 +93,13 @@ class Arena { 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_; + accu) - + pad_waste_; } + const u8 *cur_begin() const { return segment_ ? segment_->data.data() : nullptr; } + const u8 *cur_end() const { return segment_ ? segment_->cur() : nullptr; } + private: Arena(const Arena &) = delete; Arena &operator=(const Arena &) = delete; @@ -105,6 +119,7 @@ class Arena void reset() { used = 0; } }; + size_t max_segments_ = default_max_segments; std::list segments_; Segment *segment_ = nullptr; size_t allocations_ = 0; diff --git a/src/mana.test.cc b/src/mana.test.cc index 477ef0f..3d67cd6 100644 --- a/src/mana.test.cc +++ b/src/mana.test.cc @@ -1,10 +1,13 @@ #include #include #include "internal/mana_arena.h" +#include +#include "tools/mana_replay_api.h" +#include "tools/mana_lib.hpp" using namespace mesytec::mnode; -TEST(mnode_mana, Arena) +TEST(mnode_mana_arena, push_reset_push) { mana::Arena arena; @@ -14,7 +17,7 @@ TEST(mnode_mana, Arena) ASSERT_EQ(arena.capacity(), 0); ASSERT_EQ(arena.used(), 0); - for (size_t i=0; i<10; ++i) + for (size_t i = 0; i < 10; ++i) { auto mem = arena.push_size(mana::Arena::default_pad + 2); ASSERT_NE(mem, nullptr); @@ -33,4 +36,76 @@ TEST(mnode_mana, Arena) ASSERT_GE(arena.segment_count(), 1); ASSERT_GE(arena.capacity(), 10 * (mana::Arena::default_pad + 2)); ASSERT_EQ(arena.used(), 0); + + for (size_t i = 0; i < 10; ++i) + { + auto mem = arena.push_size(mana::Arena::default_pad + 2); + ASSERT_NE(mem, nullptr); + } + + ASSERT_EQ(arena.allocations(), 10); + ASSERT_GE(arena.pad_waste(), 0); + ASSERT_GE(arena.segment_count(), 1); + ASSERT_GE(arena.capacity(), 10 * (mana::Arena::default_pad + 2)); + ASSERT_EQ(arena.used(), 10 * (mana::Arena::default_pad + 2)); +} + +TEST(mnode_mana_offset_ptr, basic) +{ + using namespace mana; + + struct OffsetPtrTest + { + u32 a; + mana_offset_ptr_t ptr0; // &a + mana_offset_ptr_t ptr1; // &b + u32 b; + }; + + mana::Arena arena; + + auto t = arena.push_t(); + t->a = 42; + t->b = 43; + + set(t->ptr0, &t->a); + set(t->ptr1, &t->b); + + ASSERT_EQ(get(t->ptr0), &t->a); + ASSERT_EQ(get(t->ptr1), &t->b); + ASSERT_EQ(get(t->ptr0), nullptr); + ASSERT_EQ(get(t->ptr1), nullptr); +} + +TEST(mnode_mana_offset_ptr, strings) +{ + using namespace mana; + + struct OffsetPtrTest + { + mana_offset_ptr_t ptr0; + mana_offset_ptr_t ptr1; + }; + + mana::Arena arena; + + auto t = arena.push_t(); + + auto s0 = arena.push_cstr("hello"); + auto s1 = arena.push_cstr("world"); + + spdlog::info("&t->ptr0={}, &t->ptr1={}", fmt::ptr(&t->ptr0), fmt::ptr(&t->ptr1)); + spdlog::info("s0={} @ {}, s1={} @ {}", s0, fmt::ptr(s0), s1, fmt::ptr(s1)); + + set(t->ptr0, s0); + set(t->ptr1, s1); + + ASSERT_EQ(get(t->ptr0), std::string("hello")); + ASSERT_EQ(get(t->ptr1), std::string("world")); + ASSERT_EQ(get(t->ptr0), nullptr); + ASSERT_EQ(get(t->ptr1), nullptr); + + set(t->ptr0, nullptr); + ASSERT_EQ(get(t->ptr0), nullptr); + ASSERT_EQ(get(t->ptr1), std::string("world")); }