Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
event_storage.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/core_pch.hpp>
4
8
9#include <bit>
10#include <cstddef>
11#include <cstring>
12#include <iterator>
13#include <ranges>
14#include <span>
15#include <vector>
16
17namespace helios::ecs::details {
18
19/**
20 * @brief Type-erased storage for events using a byte vector.
21 * @details Stores events as raw bytes and provides methods to write/read events with proper type safety through.
22 * Events are stored sequentially with their size information for proper deserialization.
23 * @note Not thread-safe.
24 */
26public:
27 /**
28 * @brief Constructs an EventStorage.
29 * @param event_size Size of each event in bytes
30 */
31 explicit EventStorage(size_t event_size) : event_size_(event_size) {};
32 EventStorage(const EventStorage&) = delete;
35
36 /**
37 * @brief Creates an EventStorage for a specific event type.
38 * @tparam T Event type
39 * @return EventStorage configured for the event type
40 */
43 return EventStorage(sizeof(T));
44 }
45
48
49 /**
50 * @brief Clears all stored events.
51 */
52 void Clear() noexcept { data_.clear(); }
53
54 /**
55 * @brief Reserves space for events.
56 * @param capacity_bytes Capacity to reserve in bytes
57 */
58 void Reserve(size_t capacity_bytes) { data_.reserve(capacity_bytes); }
59
60 /**
61 * @brief Writes a single event to storage.
62 * @warning Triggers assertion if event size doesn't match storage event size.
63 * @tparam T Event type
64 * @param event Event to store
65 */
66 template <EventTrait T>
67 void Write(const T& event);
68
69 /**
70 * @brief Writes multiple events to storage in bulk.
71 * @warning Triggers assertion if event size doesn't match storage event size.
72 * @tparam R Range of events
73 * @param events Range of events to store
74 */
75 template <std::ranges::sized_range R>
77 void WriteBulk(const R& events);
78
79 /**
80 * @brief Reads all events from storage.
81 * @warning Triggers assertion if event size doesn't match storage event size.
82 * Span gets invalidated if storage is modified.
83 * @tparam T Event type
84 * @return Span containing all events
85 */
86 template <EventTrait T>
87 [[nodiscard]] auto ReadAll() const noexcept -> std::span<const T>;
88
89 /**
90 * @brief Reads events of a specific type into a provided output iterator.
91 * @note Thread-safe for read operations.
92 * @warning Triggers assertion if event size doesn't match storage event size.
93 * @tparam T Event type
94 * @tparam OutIt Output iterator type
95 * @param out Output iterator to write events into
96 */
99 void ReadInto(OutIt out) const;
100
101 /**
102 * @brief Appends raw bytes from another EventStorage.
103 * @param data Pointer to raw byte data
104 */
105 void AppendRawBytes(std::span<const std::byte> data);
106
107 /**
108 * @brief Checks if storage is empty.
109 * @return True if no events are stored, false otherwise
110 */
111 [[nodiscard]] bool Empty() const noexcept { return data_.empty(); }
112
113 /**
114 * @brief Gets the size of stored data in bytes.
115 * @return Size in bytes
116 */
117 [[nodiscard]] size_t SizeBytes() const noexcept { return data_.size(); }
118
119 /**
120 * @brief Gets raw data (const).
121 * @return Pointer to raw byte data
122 */
123 [[nodiscard]] auto Data() const noexcept -> std::span<const std::byte> { return {data_.data(), data_.size()}; }
124
125 /**
126 * @brief Gets the size of each event in bytes.
127 * @return Event size in bytes
128 */
129 [[nodiscard]] size_t EventSize() const noexcept { return event_size_; }
130
131private:
132 size_t event_size_ = 0; ///< Size of each event in bytes
133 std::vector<std::byte> data_; ///< Events data stored in bytes
134};
135
136template <EventTrait T>
137inline void EventStorage::Write(const T& event) {
138 constexpr size_t event_size = sizeof(T);
139 HELIOS_ASSERT(event_size == event_size_, "Failed to write event: Event size misatch, expected '{}', got '{}'!",
140 event_size_, event_size);
141
142 const size_t old_size = data_.size();
143 data_.resize(old_size + event_size);
144 std::memcpy(data_.data() + old_size, &event, event_size);
145}
146
147template <std::ranges::sized_range R>
149inline void EventStorage::WriteBulk(const R& events) {
150 using EventType = std::ranges::range_value_t<R>;
151 constexpr size_t event_size = sizeof(EventType);
152 HELIOS_ASSERT(event_size == event_size_, "Failed to write events bulk: Event size misatch, expected '{}', got '{}'!",
153 event_size_, event_size);
154
155 const size_t old_size = data_.size();
156 const size_t total_bytes = events.size() * event_size;
157
158 data_.resize(old_size + total_bytes);
159
160 size_t offset = old_size;
161 for (const auto& event : events) {
162 // Write event data using memcpy (safe for trivially copyable types)
163 std::memcpy(data_.data() + offset, &event, event_size);
165 }
166}
167
168template <EventTrait T>
169inline auto EventStorage::ReadAll() const noexcept -> std::span<const T> {
170 constexpr size_t event_size = sizeof(T);
171 HELIOS_ASSERT(event_size == event_size_, "Failed to write event: Event size misatch, expected '{}', got '{}'!",
172 event_size_, event_size);
173
174 const auto* begin = std::bit_cast<const T*>(data_.data());
175 const auto* end = std::bit_cast<const T*>(data_.data() + data_.size());
176 return {begin, end};
177}
178
179template <EventTrait T, typename OutIt>
180 requires std::output_iterator<OutIt, T>
181inline void EventStorage::ReadInto(OutIt out) const {
182 constexpr size_t event_size = sizeof(T);
183 HELIOS_ASSERT(event_size == event_size_, "Failed to write event: Event size misatch, expected '{}', got '{}'!",
184 event_size_, event_size);
185
186 size_t offset = 0;
187 while (offset < data_.size()) {
188 // Read event data using memcpy (safe for trivially copyable types)
189 T event;
190 std::memcpy(&event, data_.data() + offset, event_size);
191 *out++ = std::move(event);
192
194 }
195}
196
197inline void EventStorage::AppendRawBytes(std::span<const std::byte> data) {
198 if (data.empty()) [[unlikely]] {
199 return;
200 }
201
202 const size_t old_size = data_.size();
203 data_.resize(old_size + data.size());
204 std::memcpy(data_.data() + old_size, data.data(), data.size());
205}
206
207} // namespace helios::ecs::details
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:140
Type-erased storage for events using a byte vector.
bool Empty() const noexcept
Checks if storage is empty.
static EventStorage FromEvent()
Creates an EventStorage for a specific event type.
void WriteBulk(const R &events)
Writes multiple events to storage in bulk.
size_t EventSize() const noexcept
Gets the size of each event in bytes.
EventStorage(size_t event_size)
Constructs an EventStorage.
void ReadInto(OutIt out) const
Reads events of a specific type into a provided output iterator.
auto Data() const noexcept -> std::span< const std::byte >
Gets raw data (const).
void Reserve(size_t capacity_bytes)
Reserves space for events.
EventStorage(EventStorage &&) noexcept=default
EventStorage(const EventStorage &)=delete
size_t SizeBytes() const noexcept
Gets the size of stored data in bytes.
EventStorage & operator=(EventStorage &&) noexcept=default
EventStorage & operator=(const EventStorage &)=delete
void AppendRawBytes(std::span< const std::byte > data)
Appends raw bytes from another EventStorage.
auto ReadAll() const noexcept -> std::span< const T >
Reads all events from storage.
void Clear() noexcept
Clears all stored events.
void Write(const T &event)
Writes a single event to storage.
Concept for valid event types.
Definition event.hpp:44
BasicQuery< World, Allocator, Components... > Query
Type alias for query with mutable world access.
Definition query.hpp:2481
STL namespace.