1pub const MAGIC: u32 = 0x5344_4543; pub const VERSION: u16 = 2;
10
11pub const HEADER_SIZE: usize = 4 + 2 + 2 + 8 + 4 + 4 + 4;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
16pub struct PacketFlags(u16);
17
18impl PacketFlags {
19 pub const FULL_SNAPSHOT: u16 = 1 << 0;
21
22 pub const DELTA_SNAPSHOT: u16 = 1 << 1;
24
25 pub const SESSION_INIT: u16 = 1 << 2;
27
28 const RESERVED_MASK: u16 = !0b111;
30
31 #[must_use]
33 pub const fn from_raw(raw: u16) -> Self {
34 Self(raw)
35 }
36
37 #[must_use]
39 pub const fn raw(self) -> u16 {
40 self.0
41 }
42
43 #[must_use]
45 pub const fn is_full_snapshot(self) -> bool {
46 self.0 & Self::FULL_SNAPSHOT != 0
47 }
48
49 #[must_use]
51 pub const fn is_delta_snapshot(self) -> bool {
52 self.0 & Self::DELTA_SNAPSHOT != 0
53 }
54
55 #[must_use]
60 pub const fn is_valid_v0(self) -> bool {
61 let has_full = self.is_full_snapshot();
62 let has_delta = self.is_delta_snapshot();
63 let has_reserved = self.0 & Self::RESERVED_MASK != 0;
64
65 has_full ^ has_delta && !has_reserved
66 }
67
68 #[must_use]
70 pub const fn is_session_init(self) -> bool {
71 self.0 & Self::SESSION_INIT != 0
72 }
73
74 #[must_use]
81 pub const fn is_valid_v2(self) -> bool {
82 let has_full = self.is_full_snapshot();
83 let has_delta = self.is_delta_snapshot();
84 let has_session = self.is_session_init();
85 let has_reserved = self.0 & Self::RESERVED_MASK != 0;
86 if has_reserved {
87 return false;
88 }
89 if has_session {
90 return !has_full && !has_delta;
91 }
92 has_full ^ has_delta
93 }
94
95 #[must_use]
97 pub const fn full_snapshot() -> Self {
98 Self(Self::FULL_SNAPSHOT)
99 }
100
101 #[must_use]
103 pub const fn delta_snapshot() -> Self {
104 Self(Self::DELTA_SNAPSHOT)
105 }
106
107 #[must_use]
109 pub const fn session_init() -> Self {
110 Self(Self::SESSION_INIT)
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub struct PacketHeader {
123 pub version: u16,
125 pub flags: PacketFlags,
127 pub schema_hash: u64,
129 pub tick: u32,
131 pub baseline_tick: u32,
133 pub payload_len: u32,
135}
136
137impl PacketHeader {
138 #[must_use]
140 pub const fn full_snapshot(schema_hash: u64, tick: u32, payload_len: u32) -> Self {
141 Self {
142 version: VERSION,
143 flags: PacketFlags::full_snapshot(),
144 schema_hash,
145 tick,
146 baseline_tick: 0,
147 payload_len,
148 }
149 }
150
151 #[must_use]
153 pub const fn delta_snapshot(
154 schema_hash: u64,
155 tick: u32,
156 baseline_tick: u32,
157 payload_len: u32,
158 ) -> Self {
159 Self {
160 version: VERSION,
161 flags: PacketFlags::delta_snapshot(),
162 schema_hash,
163 tick,
164 baseline_tick,
165 payload_len,
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
176 fn magic_is_sdec_ascii() {
177 assert_eq!(MAGIC, 0x5344_4543);
179 let bytes = MAGIC.to_be_bytes();
180 assert_eq!(&bytes, b"SDEC");
181 }
182
183 #[test]
184 fn version_is_two() {
185 assert_eq!(VERSION, 2);
186 }
187
188 #[test]
189 fn header_size_is_correct() {
190 assert_eq!(HEADER_SIZE, 28);
192 }
193
194 #[test]
196 fn flags_full_snapshot() {
197 let flags = PacketFlags::full_snapshot();
198 assert!(flags.is_full_snapshot());
199 assert!(!flags.is_delta_snapshot());
200 assert_eq!(flags.raw(), 0b01);
201 }
202
203 #[test]
204 fn flags_delta_snapshot() {
205 let flags = PacketFlags::delta_snapshot();
206 assert!(!flags.is_full_snapshot());
207 assert!(flags.is_delta_snapshot());
208 assert_eq!(flags.raw(), 0b10);
209 }
210
211 #[test]
212 fn flags_from_raw_roundtrip() {
213 let flags = PacketFlags::from_raw(0b01);
214 assert_eq!(flags.raw(), 0b01);
215 assert!(flags.is_full_snapshot());
216 }
217
218 #[test]
219 fn flags_validity_full() {
220 assert!(PacketFlags::full_snapshot().is_valid_v0());
221 }
222
223 #[test]
224 fn flags_validity_delta() {
225 assert!(PacketFlags::delta_snapshot().is_valid_v0());
226 }
227
228 #[test]
229 fn flags_invalid_neither_set() {
230 assert!(!PacketFlags::from_raw(0).is_valid_v0());
231 }
232
233 #[test]
234 fn flags_invalid_both_set() {
235 assert!(!PacketFlags::from_raw(0b11).is_valid_v2());
236 }
237
238 #[test]
239 fn flags_invalid_reserved_bits() {
240 assert!(!PacketFlags::from_raw(0b1001).is_valid_v2());
242 assert!(!PacketFlags::from_raw(0xFF01).is_valid_v2());
244 }
245
246 #[test]
247 fn flags_default() {
248 let flags = PacketFlags::default();
249 assert_eq!(flags.raw(), 0);
250 assert!(!flags.is_valid_v2()); }
252
253 #[test]
254 fn flags_equality() {
255 assert_eq!(PacketFlags::full_snapshot(), PacketFlags::from_raw(0b01));
256 assert_ne!(PacketFlags::full_snapshot(), PacketFlags::delta_snapshot());
257 }
258
259 #[test]
260 fn flags_clone_copy() {
261 let flags = PacketFlags::full_snapshot();
262 let copied = flags; assert_eq!(flags, copied);
264 }
265
266 #[test]
268 fn header_full_snapshot() {
269 let header = PacketHeader::full_snapshot(0x1234_5678_9ABC_DEF0, 100, 512);
270
271 assert_eq!(header.version, VERSION);
272 assert!(header.flags.is_full_snapshot());
273 assert!(!header.flags.is_delta_snapshot());
274 assert_eq!(header.schema_hash, 0x1234_5678_9ABC_DEF0);
275 assert_eq!(header.tick, 100);
276 assert_eq!(header.baseline_tick, 0);
277 assert_eq!(header.payload_len, 512);
278 }
279
280 #[test]
281 fn header_delta_snapshot() {
282 let header = PacketHeader::delta_snapshot(0xABCD, 100, 95, 256);
283
284 assert_eq!(header.version, VERSION);
285 assert!(header.flags.is_delta_snapshot());
286 assert!(!header.flags.is_full_snapshot());
287 assert_eq!(header.schema_hash, 0xABCD);
288 assert_eq!(header.tick, 100);
289 assert_eq!(header.baseline_tick, 95);
290 assert_eq!(header.payload_len, 256);
291 }
292
293 #[test]
294 fn header_equality() {
295 let h1 = PacketHeader::full_snapshot(0x1234, 100, 512);
296 let h2 = PacketHeader::full_snapshot(0x1234, 100, 512);
297 let h3 = PacketHeader::full_snapshot(0x1234, 101, 512);
298
299 assert_eq!(h1, h2);
300 assert_ne!(h1, h3);
301 }
302
303 #[test]
304 fn header_clone_copy() {
305 let header = PacketHeader::full_snapshot(0x1234, 100, 512);
306 let copied = header; assert_eq!(header, copied);
308 }
309
310 #[test]
311 fn header_debug() {
312 let header = PacketHeader::full_snapshot(0x1234, 100, 512);
313 let debug = format!("{header:?}");
314 assert!(debug.contains("PacketHeader"));
315 assert!(debug.contains("100")); }
317
318 #[test]
319 fn header_const_constructible() {
320 const HEADER: PacketHeader = PacketHeader::full_snapshot(0, 0, 0);
321 assert_eq!(HEADER.tick, 0);
322 }
323}