pub const MAGIC: u32 = 0x5344_4543;
pub const VERSION: u16 = 2;
pub const HEADER_SIZE: usize = 4 + 2 + 2 + 8 + 4 + 4 + 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct PacketFlags(u16);
impl PacketFlags {
pub const FULL_SNAPSHOT: u16 = 1 << 0;
pub const DELTA_SNAPSHOT: u16 = 1 << 1;
pub const SESSION_INIT: u16 = 1 << 2;
const RESERVED_MASK: u16 = !0b111;
#[must_use]
pub const fn from_raw(raw: u16) -> Self {
Self(raw)
}
#[must_use]
pub const fn raw(self) -> u16 {
self.0
}
#[must_use]
pub const fn is_full_snapshot(self) -> bool {
self.0 & Self::FULL_SNAPSHOT != 0
}
#[must_use]
pub const fn is_delta_snapshot(self) -> bool {
self.0 & Self::DELTA_SNAPSHOT != 0
}
#[must_use]
pub const fn is_valid_v0(self) -> bool {
let has_full = self.is_full_snapshot();
let has_delta = self.is_delta_snapshot();
let has_reserved = self.0 & Self::RESERVED_MASK != 0;
has_full ^ has_delta && !has_reserved
}
#[must_use]
pub const fn is_session_init(self) -> bool {
self.0 & Self::SESSION_INIT != 0
}
#[must_use]
pub const fn is_valid_v2(self) -> bool {
let has_full = self.is_full_snapshot();
let has_delta = self.is_delta_snapshot();
let has_session = self.is_session_init();
let has_reserved = self.0 & Self::RESERVED_MASK != 0;
if has_reserved {
return false;
}
if has_session {
return !has_full && !has_delta;
}
has_full ^ has_delta
}
#[must_use]
pub const fn full_snapshot() -> Self {
Self(Self::FULL_SNAPSHOT)
}
#[must_use]
pub const fn delta_snapshot() -> Self {
Self(Self::DELTA_SNAPSHOT)
}
#[must_use]
pub const fn session_init() -> Self {
Self(Self::SESSION_INIT)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PacketHeader {
pub version: u16,
pub flags: PacketFlags,
pub schema_hash: u64,
pub tick: u32,
pub baseline_tick: u32,
pub payload_len: u32,
}
impl PacketHeader {
#[must_use]
pub const fn full_snapshot(schema_hash: u64, tick: u32, payload_len: u32) -> Self {
Self {
version: VERSION,
flags: PacketFlags::full_snapshot(),
schema_hash,
tick,
baseline_tick: 0,
payload_len,
}
}
#[must_use]
pub const fn delta_snapshot(
schema_hash: u64,
tick: u32,
baseline_tick: u32,
payload_len: u32,
) -> Self {
Self {
version: VERSION,
flags: PacketFlags::delta_snapshot(),
schema_hash,
tick,
baseline_tick,
payload_len,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn magic_is_sdec_ascii() {
assert_eq!(MAGIC, 0x5344_4543);
let bytes = MAGIC.to_be_bytes();
assert_eq!(&bytes, b"SDEC");
}
#[test]
fn version_is_two() {
assert_eq!(VERSION, 2);
}
#[test]
fn header_size_is_correct() {
assert_eq!(HEADER_SIZE, 28);
}
#[test]
fn flags_full_snapshot() {
let flags = PacketFlags::full_snapshot();
assert!(flags.is_full_snapshot());
assert!(!flags.is_delta_snapshot());
assert_eq!(flags.raw(), 0b01);
}
#[test]
fn flags_delta_snapshot() {
let flags = PacketFlags::delta_snapshot();
assert!(!flags.is_full_snapshot());
assert!(flags.is_delta_snapshot());
assert_eq!(flags.raw(), 0b10);
}
#[test]
fn flags_from_raw_roundtrip() {
let flags = PacketFlags::from_raw(0b01);
assert_eq!(flags.raw(), 0b01);
assert!(flags.is_full_snapshot());
}
#[test]
fn flags_validity_full() {
assert!(PacketFlags::full_snapshot().is_valid_v0());
}
#[test]
fn flags_validity_delta() {
assert!(PacketFlags::delta_snapshot().is_valid_v0());
}
#[test]
fn flags_invalid_neither_set() {
assert!(!PacketFlags::from_raw(0).is_valid_v0());
}
#[test]
fn flags_invalid_both_set() {
assert!(!PacketFlags::from_raw(0b11).is_valid_v2());
}
#[test]
fn flags_invalid_reserved_bits() {
assert!(!PacketFlags::from_raw(0b1001).is_valid_v2());
assert!(!PacketFlags::from_raw(0xFF01).is_valid_v2());
}
#[test]
fn flags_default() {
let flags = PacketFlags::default();
assert_eq!(flags.raw(), 0);
assert!(!flags.is_valid_v2()); }
#[test]
fn flags_equality() {
assert_eq!(PacketFlags::full_snapshot(), PacketFlags::from_raw(0b01));
assert_ne!(PacketFlags::full_snapshot(), PacketFlags::delta_snapshot());
}
#[test]
fn flags_clone_copy() {
let flags = PacketFlags::full_snapshot();
let copied = flags; assert_eq!(flags, copied);
}
#[test]
fn header_full_snapshot() {
let header = PacketHeader::full_snapshot(0x1234_5678_9ABC_DEF0, 100, 512);
assert_eq!(header.version, VERSION);
assert!(header.flags.is_full_snapshot());
assert!(!header.flags.is_delta_snapshot());
assert_eq!(header.schema_hash, 0x1234_5678_9ABC_DEF0);
assert_eq!(header.tick, 100);
assert_eq!(header.baseline_tick, 0);
assert_eq!(header.payload_len, 512);
}
#[test]
fn header_delta_snapshot() {
let header = PacketHeader::delta_snapshot(0xABCD, 100, 95, 256);
assert_eq!(header.version, VERSION);
assert!(header.flags.is_delta_snapshot());
assert!(!header.flags.is_full_snapshot());
assert_eq!(header.schema_hash, 0xABCD);
assert_eq!(header.tick, 100);
assert_eq!(header.baseline_tick, 95);
assert_eq!(header.payload_len, 256);
}
#[test]
fn header_equality() {
let h1 = PacketHeader::full_snapshot(0x1234, 100, 512);
let h2 = PacketHeader::full_snapshot(0x1234, 100, 512);
let h3 = PacketHeader::full_snapshot(0x1234, 101, 512);
assert_eq!(h1, h2);
assert_ne!(h1, h3);
}
#[test]
fn header_clone_copy() {
let header = PacketHeader::full_snapshot(0x1234, 100, 512);
let copied = header; assert_eq!(header, copied);
}
#[test]
fn header_debug() {
let header = PacketHeader::full_snapshot(0x1234, 100, 512);
let debug = format!("{header:?}");
assert!(debug.contains("PacketHeader"));
assert!(debug.contains("100")); }
#[test]
fn header_const_constructible() {
const HEADER: PacketHeader = PacketHeader::full_snapshot(0, 0, 0);
assert_eq!(HEADER.tick, 0);
}
}