move arena into internal header, add mana gtest
This commit is contained in:
parent
2087933fac
commit
46efb1cff7
4 changed files with 196 additions and 84 deletions
|
|
@ -9,3 +9,13 @@ target_compile_features(mesytec-mnode PRIVATE cxx_std_17)
|
||||||
target_compile_options(mesytec-mnode PRIVATE ${MVLC_NNG_MNODE_WARN_FLAGS})
|
target_compile_options(mesytec-mnode PRIVATE ${MVLC_NNG_MNODE_WARN_FLAGS})
|
||||||
|
|
||||||
add_subdirectory(tools)
|
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)
|
||||||
|
add_test(NAME name COMMAND $<TARGET_FILE:test_${name}>)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
add_mnode_gtest(mana)
|
||||||
|
endif()
|
||||||
|
|
|
||||||
116
src/internal/mana_arena.h
Normal file
116
src/internal/mana_arena.h
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#ifndef F20CF38F_7327_4608_8307_6AE058041CD5
|
||||||
|
#define F20CF38F_7327_4608_8307_6AE058041CD5
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <list>
|
||||||
|
#include <mesytec-mnode/mnode_math.h>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
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 <typename T> T *push_t(size_t count = 1)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivial<T>::value, "T must be a trivial type");
|
||||||
|
return reinterpret_cast<T *>(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<const char *>(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<size_t>(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<size_t>(0u),
|
||||||
|
accu) - pad_waste_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Arena(const Arena &) = delete;
|
||||||
|
Arena &operator=(const Arena &) = delete;
|
||||||
|
|
||||||
|
struct Segment
|
||||||
|
{
|
||||||
|
std::vector<u8> 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<Segment> segments_;
|
||||||
|
Segment *segment_ = nullptr;
|
||||||
|
size_t allocations_ = 0;
|
||||||
|
size_t pad_waste_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace mesytec::mnode::mana
|
||||||
|
|
||||||
|
#endif /* F20CF38F_7327_4608_8307_6AE058041CD5 */
|
||||||
36
src/mana.test.cc
Normal file
36
src/mana.test.cc
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <mesytec-mnode/mnode_cpp_types.h>
|
||||||
|
#include "internal/mana_arena.h"
|
||||||
|
|
||||||
|
using namespace mesytec::mnode;
|
||||||
|
|
||||||
|
TEST(mnode_mana, Arena)
|
||||||
|
{
|
||||||
|
mana::Arena arena;
|
||||||
|
|
||||||
|
ASSERT_EQ(arena.allocations(), 0);
|
||||||
|
ASSERT_EQ(arena.pad_waste(), 0);
|
||||||
|
ASSERT_EQ(arena.segment_count(), 0);
|
||||||
|
ASSERT_EQ(arena.capacity(), 0);
|
||||||
|
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));
|
||||||
|
|
||||||
|
arena.reset();
|
||||||
|
|
||||||
|
ASSERT_EQ(arena.allocations(), 0);
|
||||||
|
ASSERT_EQ(arena.pad_waste(), 0);
|
||||||
|
ASSERT_GE(arena.segment_count(), 1);
|
||||||
|
ASSERT_GE(arena.capacity(), 10 * (mana::Arena::default_pad + 2));
|
||||||
|
ASSERT_EQ(arena.used(), 0);
|
||||||
|
}
|
||||||
|
|
@ -34,92 +34,17 @@
|
||||||
#include <cmrc/cmrc.hpp> // mnode::resources
|
#include <cmrc/cmrc.hpp> // mnode::resources
|
||||||
#include <mesytec-mvlc/mesytec-mvlc.h>
|
#include <mesytec-mvlc/mesytec-mvlc.h>
|
||||||
#include <mesytec-mnode/mnode_cpp_types.h>
|
#include <mesytec-mnode/mnode_cpp_types.h>
|
||||||
|
#include <mesytec-mnode/mnode_math.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include "mana_replay_api.h"
|
#include "mana_replay_api.h"
|
||||||
|
#include "internal/mana_arena.h"
|
||||||
|
|
||||||
CMRC_DECLARE(mnode::resources);
|
CMRC_DECLARE(mnode::resources);
|
||||||
|
|
||||||
using namespace mesytec;
|
using namespace mesytec;
|
||||||
using namespace mesytec::mnode;
|
using namespace mesytec::mnode;
|
||||||
|
|
||||||
namespace arena
|
|
||||||
{
|
|
||||||
|
|
||||||
// source: https://blog.xoria.org/rounding-up/
|
|
||||||
inline int64_t round_up(int64_t n, int64_t p)
|
|
||||||
{
|
|
||||||
int64_t mask = p - 1;
|
|
||||||
return (n + mask) & ~mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Arena
|
|
||||||
{
|
|
||||||
Arena() = default;
|
|
||||||
~Arena() = default;
|
|
||||||
Arena(const Arena &) = delete;
|
|
||||||
Arena &operator=(const Arena &) = delete;
|
|
||||||
Arena(Arena &&) = delete;
|
|
||||||
Arena &operator=(Arena &&) = delete;
|
|
||||||
struct Segment
|
|
||||||
{
|
|
||||||
std::vector<u8> data;
|
|
||||||
size_t used;
|
|
||||||
|
|
||||||
explicit Segment(size_t size)
|
|
||||||
: data(size)
|
|
||||||
, used(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
inline size_t free() const { return data.size() - used; }
|
|
||||||
inline u8 *cur() { return data.data() + used; }
|
|
||||||
};
|
|
||||||
|
|
||||||
std::list<Segment> segments;
|
|
||||||
size_t current = 0;
|
|
||||||
|
|
||||||
inline Segment *current_segment()
|
|
||||||
{
|
|
||||||
if (current < segments.size())
|
|
||||||
{
|
|
||||||
auto it = segments.begin();
|
|
||||||
std::advance(it, current);
|
|
||||||
return &(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline u8 *push_size(size_t required)
|
|
||||||
{
|
|
||||||
required = round_up(required, 16);
|
|
||||||
|
|
||||||
if (auto segment = current_segment(); !segment || segment->free() < required)
|
|
||||||
{
|
|
||||||
// could waste segments if required > default size of 1u << 20 but that's fine for now
|
|
||||||
segments.emplace_back(
|
|
||||||
Arena::Segment(std::max(required, static_cast<size_t>(1u << 20))));
|
|
||||||
current = segments.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto segment = current_segment();
|
|
||||||
assert(segment);
|
|
||||||
assert(segment->free() >= required);
|
|
||||||
u8 *result = segment->cur();
|
|
||||||
std::memset(result, 0, required);
|
|
||||||
segment->used += required;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T> T *push_t(size_t count = 1)
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivial<T>::value, "T must be a trivial type");
|
|
||||||
return reinterpret_cast<T *>(push_size(sizeof(T) * count));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}; // namespace arena
|
|
||||||
|
|
||||||
struct ListfileContext
|
struct ListfileContext
|
||||||
{
|
{
|
||||||
std::unique_ptr<mvlc::listfile::ZipReader> zipReader;
|
std::unique_ptr<mvlc::listfile::ZipReader> zipReader;
|
||||||
|
|
@ -167,7 +92,7 @@ std::optional<ListfileContext> make_listfile_context(const std::string &filename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::move(ctx);
|
return ctx;
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
|
|
@ -208,23 +133,46 @@ std::optional<ParserContext> make_parser_context(const mvlc::CrateConfig &crateC
|
||||||
|
|
||||||
struct AnalysisContext
|
struct AnalysisContext
|
||||||
{
|
{
|
||||||
|
std::unique_ptr<mana::Arena> arena;
|
||||||
|
run_descriptor_t *runDescriptor = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::optional<AnalysisContext> make_mana(const mvlc::CrateConfig &crateConfig,
|
||||||
|
const nlohmann::json &jModuleDataSources)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::map<std::string, nlohmann::json> moduleInfoByType;
|
||||||
|
for (const auto &mod_: jModuleDataSources["modules"])
|
||||||
|
moduleInfoByType[mod_["type_name"]] = mod_;
|
||||||
|
|
||||||
|
AnalysisContext ctx;
|
||||||
|
ctx.arena = std::make_unique<mana::Arena>();
|
||||||
|
auto arena = ctx.arena.get();
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
std::cerr << fmt::format("Error: {}\n", e.what());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void event_data_callback(AnalysisContext *ctx, int crateIndex, int eventIndex,
|
void event_data_callback(AnalysisContext *ctx, int crateIndex, int eventIndex,
|
||||||
const mvlc::readout_parser::ModuleData *moduleDataList,
|
const mvlc::readout_parser::ModuleData *moduleDataList,
|
||||||
unsigned moduleCount)
|
unsigned moduleCount)
|
||||||
{
|
{
|
||||||
assert(moduleDataList);
|
assert(moduleDataList);
|
||||||
// std::cout << fmt::format("ReadoutEvent: ctx={}, crateId={}, eventIndex={}, moduleCount={}\n",
|
std::cout << fmt::format("ReadoutEvent: ctx={}, crateId={}, eventIndex={}, moduleCount={}\n",
|
||||||
// fmt::ptr(ctx), crateIndex, eventIndex, moduleCount);
|
fmt::ptr(ctx), crateIndex, eventIndex, moduleCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void system_event_callback(AnalysisContext *ctx, int crateIndex, const u32 *header, u32 size)
|
void system_event_callback(AnalysisContext *ctx, int crateIndex, const u32 *header, u32 size)
|
||||||
{
|
{
|
||||||
assert(header);
|
assert(header);
|
||||||
// std::cout << fmt::format("SystemEvent: ctx={}, crateId={}, size={}\n", fmt::ptr(ctx),
|
std::cout << fmt::format("SystemEvent: ctx={}, crateId={}, size={}\n", fmt::ptr(ctx),
|
||||||
// crateIndex,
|
crateIndex, size);
|
||||||
// size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t process_one_buffer(size_t bufferNumber, ListfileContext &listfileContext,
|
size_t process_one_buffer(size_t bufferNumber, ListfileContext &listfileContext,
|
||||||
|
|
@ -263,11 +211,13 @@ int main(int argc, char *argv[])
|
||||||
if (!listfileContext)
|
if (!listfileContext)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
std::optional<AnalysisContext> analysisContext = AnalysisContext();
|
auto analysisContext = make_mana(listfileContext->crateConfig, jModuleDataSources);
|
||||||
|
|
||||||
if (!analysisContext)
|
if (!analysisContext)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
return 42;
|
||||||
|
|
||||||
auto event_data = [](void *ctx, int crateIndex, int eventIndex,
|
auto event_data = [](void *ctx, int crateIndex, int eventIndex,
|
||||||
const mvlc::readout_parser::ModuleData *moduleDataList,
|
const mvlc::readout_parser::ModuleData *moduleDataList,
|
||||||
unsigned moduleCount)
|
unsigned moduleCount)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue