Corium 1.1.0
High-Performance Zero-Heap C++20 MPSC Application Runtime
Loading...
Searching...
No Matches
EventJournal.hpp
Go to the documentation of this file.
1
7#pragma once
8
9#include <array>
10#include <cstddef>
11#include <cstdint>
12#include <cstring>
13#include <span>
14#include <type_traits>
15#include <utility>
16#include <variant>
17
21
22namespace corium::wire {
23
25inline constexpr uint32_t CORIUM_JOURNAL_MAGIC = 0x4a4f5552;
26
28inline constexpr uint32_t CORIUM_JOURNAL_VERSION = 1;
29
31template <typename EventVariant>
32[[nodiscard]] constexpr uint64_t computeVariantSchemaHash() noexcept {
33 constexpr size_t numTypes = std::variant_size_v<EventVariant>;
34 uint64_t hash = 0xcbf29ce484222325ULL; // FNV-1a 64-bit basis
35 hash ^= static_cast<uint64_t>(numTypes);
36 hash *= 0x100000001b3ULL;
37 return hash;
38}
39
40#pragma pack(push, 1)
41
46 uint64_t schemaHash{0};
47 uint32_t recordCount{0};
48 uint32_t reserved{0};
49};
50
53 uint64_t timestampUs{0};
54 uint32_t typeIndex{0};
55 uint32_t payloadLength{0};
56 uint16_t checksum{0};
57 uint8_t priority{static_cast<uint8_t>(EventPriority::Normal)};
58 uint8_t typeSignature{0};
59};
60
61#pragma pack(pop)
62
67template <typename EventVariant, size_t BufferCapacity = 4096>
69public:
70 constexpr EventJournalWriter() noexcept {
71 initHeader();
72 }
73
75 void reset() noexcept {
76 m_offset = 0;
77 m_recordCount = 0;
78 initHeader();
79 }
80
87 template <typename Event>
88 bool record(const Event& event, uint64_t timestampUs, EventPriority priority = EventPriority::Normal) noexcept {
89 static_assert(std::is_trivially_copyable_v<Event>, "Event must be trivially copyable for journal serialization.");
90 constexpr size_t typeIdx = corium::variant_index_v<Event, EventVariant>;
91 static_assert(typeIdx != static_cast<size_t>(-1), "Event type is not in the specified EventVariant.");
92
93 constexpr size_t recordSize = sizeof(JournalRecordHeader) + sizeof(Event);
94 if (m_offset + recordSize > BufferCapacity) {
95 return false;
96 }
97
98 JournalRecordHeader recHeader{};
99 recHeader.timestampUs = timestampUs;
100 recHeader.typeIndex = static_cast<uint32_t>(typeIdx);
101 recHeader.payloadLength = static_cast<uint32_t>(sizeof(Event));
102 recHeader.priority = static_cast<uint8_t>(priority);
103 recHeader.typeSignature = computeTypeSignature<Event>();
104 recHeader.checksum = calculateCrc16(std::span<const uint8_t>(
105 reinterpret_cast<const uint8_t*>(&event), sizeof(Event)));
106
107 // Write record header
108 std::memcpy(&m_buffer[m_offset], &recHeader, sizeof(JournalRecordHeader));
109 m_offset += sizeof(JournalRecordHeader);
110
111 // Write event payload
112 std::memcpy(&m_buffer[m_offset], &event, sizeof(Event));
113 m_offset += sizeof(Event);
114
115 m_recordCount++;
116 updateHeaderCount();
117 return true;
118 }
119
121 [[nodiscard]] size_t recordCount() const noexcept {
122 return m_recordCount;
123 }
124
126 [[nodiscard]] size_t bytesWritten() const noexcept {
127 return m_offset;
128 }
129
131 [[nodiscard]] std::span<const uint8_t> data() const noexcept {
132 return std::span<const uint8_t>(m_buffer.data(), m_offset);
133 }
134
135private:
136 void initHeader() noexcept {
137 JournalHeader hdr{};
139 hdr.version = CORIUM_JOURNAL_VERSION;
140 hdr.schemaHash = computeVariantSchemaHash<EventVariant>();
141 hdr.recordCount = 0;
142 hdr.reserved = 0;
143 std::memcpy(&m_buffer[0], &hdr, sizeof(JournalHeader));
144 m_offset = sizeof(JournalHeader);
145 }
146
147 void updateHeaderCount() noexcept {
148 auto* hdr = reinterpret_cast<JournalHeader*>(&m_buffer[0]);
149 hdr->recordCount = static_cast<uint32_t>(m_recordCount);
150 }
151
152 std::array<uint8_t, BufferCapacity> m_buffer{};
153 size_t m_offset{0};
154 size_t m_recordCount{0};
155};
156
160template <typename EventVariant>
162public:
164 explicit EventJournalReader(std::span<const uint8_t> journalData) noexcept
165 : m_data(journalData) {
166 validateAndParseHeader();
167 }
168
170 [[nodiscard]] bool isValid() const noexcept {
171 return m_valid;
172 }
173
175 [[nodiscard]] size_t totalRecords() const noexcept {
176 return m_valid ? m_header.recordCount : 0;
177 }
178
180 void rewind() noexcept {
181 m_cursor = sizeof(JournalHeader);
182 }
183
188 template <typename Sink>
189 size_t replayInto(Sink& sink) noexcept {
190 if (!m_valid) {
191 return 0;
192 }
193
194 rewind();
195 size_t replayed = 0;
196
197 while (m_cursor + sizeof(JournalRecordHeader) <= m_data.size()) {
198 JournalRecordHeader recHeader{};
199 std::memcpy(&recHeader, &m_data[m_cursor], sizeof(JournalRecordHeader));
200
201 size_t payloadStart = m_cursor + sizeof(JournalRecordHeader);
202 if (payloadStart + recHeader.payloadLength > m_data.size()) {
203 break; // Truncated record
204 }
205
206 // Verify CRC
207 uint16_t expectedCrc = calculateCrc16(std::span<const uint8_t>(
208 &m_data[payloadStart], recHeader.payloadLength));
209 if (recHeader.checksum != expectedCrc) {
210 break; // Corrupted record
211 }
212
213 // Deserialize and push
214 constexpr size_t numTypes = std::variant_size_v<EventVariant>;
215 if (recHeader.typeIndex < numTypes) {
216 bool pushed = deserializeIndex<Sink>(
217 recHeader,
218 &m_data[payloadStart],
219 sink,
220 std::make_index_sequence<numTypes>{}
221 );
222 if (pushed) {
223 replayed++;
224 }
225 }
226
227 m_cursor = payloadStart + recHeader.payloadLength;
228 }
229
230 return replayed;
231 }
232
233private:
234 void validateAndParseHeader() noexcept {
235 if (m_data.size() < sizeof(JournalHeader)) {
236 m_valid = false;
237 return;
238 }
239
240 std::memcpy(&m_header, m_data.data(), sizeof(JournalHeader));
241
242 if (m_header.magic != CORIUM_JOURNAL_MAGIC) {
243 m_valid = false;
244 return;
245 }
246 if (m_header.version != CORIUM_JOURNAL_VERSION) {
247 m_valid = false;
248 return;
249 }
250 if (m_header.schemaHash != computeVariantSchemaHash<EventVariant>()) {
251 m_valid = false;
252 return;
253 }
254
255 m_valid = true;
256 m_cursor = sizeof(JournalHeader);
257 }
258
259 template <typename Sink, size_t... Is>
260 bool deserializeIndex(
261 const JournalRecordHeader& recHeader,
262 const uint8_t* payload,
263 Sink& sink,
264 std::index_sequence<Is...>
265 ) noexcept {
266 bool handled = false;
267 (void)((recHeader.typeIndex == Is ? (handled = deserializeExact<Is, Sink>(recHeader, payload, sink), true) : false) || ...);
268 return handled;
269 }
270
271 template <size_t Index, typename Sink>
272 bool deserializeExact(
273 const JournalRecordHeader& recHeader,
274 const uint8_t* payload,
275 Sink& sink
276 ) noexcept {
277 using TargetEvent = std::variant_alternative_t<Index, EventVariant>;
278 if (recHeader.payloadLength != sizeof(TargetEvent)) {
279 return false;
280 }
281
282 if (recHeader.typeSignature != 0 && recHeader.typeSignature != computeTypeSignature<TargetEvent>()) {
283 return false;
284 }
285
286 TargetEvent evt{};
287 std::memcpy(&evt, payload, sizeof(TargetEvent));
288 auto prio = static_cast<EventPriority>(recHeader.priority);
289 sink.post(EventVariant{std::move(evt)}, prio);
290 return true;
291 }
292
293 std::span<const uint8_t> m_data;
294 JournalHeader m_header{};
295 size_t m_cursor{0};
296 bool m_valid{false};
297};
298
299} // namespace corium::wire
Bounded and multi-tier priority MPSC queueing policies.
Compile-time type index resolution for std::variant alternative types.
Binary packet framing with CRC-16 checksum and schema versioning.
Zero-heap event journal reader and deterministic player into Corium EventSinks.
Definition EventJournal.hpp:161
void rewind() noexcept
Rewind playback cursor to the first record.
Definition EventJournal.hpp:180
bool isValid() const noexcept
Returns true if the journal header is valid (magic, version, schema match).
Definition EventJournal.hpp:170
size_t totalRecords() const noexcept
Total number of records declared in the header.
Definition EventJournal.hpp:175
EventJournalReader(std::span< const uint8_t > journalData) noexcept
Construct reader over a byte span.
Definition EventJournal.hpp:164
size_t replayInto(Sink &sink) noexcept
Replay all valid records in the journal directly into an EventSink.
Definition EventJournal.hpp:189
Statically allocated binary event journal writer for zero-heap post-mortem logging and record playbac...
Definition EventJournal.hpp:68
void reset() noexcept
Reset journal to initial empty state.
Definition EventJournal.hpp:75
constexpr EventJournalWriter() noexcept
Definition EventJournal.hpp:70
std::span< const uint8_t > data() const noexcept
Read-only view of the serialized journal data.
Definition EventJournal.hpp:131
size_t bytesWritten() const noexcept
Total bytes written into buffer (header + all records).
Definition EventJournal.hpp:126
size_t recordCount() const noexcept
Number of records written.
Definition EventJournal.hpp:121
bool record(const Event &event, uint64_t timestampUs, EventPriority priority=EventPriority::Normal) noexcept
Record a concrete typed event into the journal.
Definition EventJournal.hpp:88
Definition EventJournal.hpp:22
constexpr uint64_t computeVariantSchemaHash() noexcept
Computes a deterministic 64-bit ABI hash for an EventVariant type.
Definition EventJournal.hpp:32
constexpr uint32_t CORIUM_JOURNAL_MAGIC
Magic identifier for Corium binary event journals ("JOUR" in hex: 0x4a4f5552).
Definition EventJournal.hpp:25
constexpr uint32_t CORIUM_JOURNAL_VERSION
Current schema version for Corium event journals.
Definition EventJournal.hpp:28
constexpr uint16_t calculateCrc16(std::span< const uint8_t > data) noexcept
Calculate CRC-16-CCITT checksum over a byte span without lookup tables.
Definition WirePacket.hpp:21
EventPriority
Event priority levels for multi-priority queue policies.
Definition QueuePolicies.hpp:32
DefaultEvents Event
Alias for DefaultEvents.
Definition Events.hpp:65
Header placed at the beginning of an event journal binary stream.
Definition EventJournal.hpp:43
uint32_t recordCount
Definition EventJournal.hpp:47
uint32_t version
Definition EventJournal.hpp:45
uint64_t schemaHash
Definition EventJournal.hpp:46
uint32_t magic
Definition EventJournal.hpp:44
uint32_t reserved
Definition EventJournal.hpp:48
Header preceding every serialized event record in the journal.
Definition EventJournal.hpp:52
uint8_t priority
Definition EventJournal.hpp:57
uint32_t typeIndex
Definition EventJournal.hpp:54
uint64_t timestampUs
Definition EventJournal.hpp:53
uint8_t typeSignature
Definition EventJournal.hpp:58
uint32_t payloadLength
Definition EventJournal.hpp:55
uint16_t checksum
Definition EventJournal.hpp:56