// Copyright 2026 Noverberrain.
// SPDX-License-Identifier: Apache-2.0
///|
/// Read-only decoded fields of a v4 common event header.
/// `event_size` includes the common header and any checksum trailer.
/// `next_position` is retained verbatim and must not replace the input cursor.
pub struct EventHeader {
file_offset : UInt64
timestamp : UInt32
event_type : Byte
server_id : UInt32
event_size : UInt32
next_position : UInt32
flags : UInt16
} derive(Debug, Eq)
///|
pub extend EventHeader with Eq::{equal, not_equal}
///|
pub extend EventHeader with @debug.Debug::{to_repr}
///|
/// Decode the first 19 bytes without consuming or validating an event payload.
/// `data` starts at the event header; `file_offset` is its physical file offset.
/// Unknown event type codes and zero next positions are preserved.
///
/// The default event-size limit is 16 MiB. A limit below 19 is invalid.
/// A caller may pass only the header, then read the declared payload separately.
/// Full-event availability, FORMAT_DESCRIPTION and checksums are separate steps.
pub fn EventHeader::parse(
data : BytesView,
file_offset~ : UInt64,
max_event_size~ : UInt32 = 16_777_216,
) -> EventHeader raise DecodeError {
guard max_event_size >= 19 else { raise InvalidEventLimit(max_event_size~) }
guard data.length() >= 19 else {
raise TruncatedInput(file_offset~, expected=19, actual=data.length())
}
let event_size = read_u32_le(data, 9)
guard event_size >= 19 else {
raise InvalidEventLength(file_offset~, event_size~)
}
guard event_size <= max_event_size else {
raise EventTooLarge(file_offset~, event_size~, max_event_size~)
}
{
file_offset,
timestamp: read_u32_le(data, 0),
event_type: data[4],
server_id: read_u32_le(data, 5),
event_size,
next_position: read_u32_le(data, 13),
flags: read_u16_le(data, 17),
}
}
///|
// Private helpers: the caller checks the complete 19-byte prefix before reads.
fn read_u32_le(data : BytesView, offset : Int32) -> UInt32 {
data[offset].to_uint() |
(data[offset + 1].to_uint() << 8) |
(data[offset + 2].to_uint() << 16) |
(data[offset + 3].to_uint() << 24)
}
///|
fn read_u16_le(data : BytesView, offset : Int32) -> UInt16 {
(data[offset].to_uint() | (data[offset + 1].to_uint() << 8)).to_uint16()
}