1use std::{char, fmt::Display, str};
19
20use serde::{
21 Serialize,
22 de::{
23 self, DeserializeOwned, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess,
24 SeqAccess, VariantAccess, Visitor,
25 },
26 ser::{
27 self, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
28 SerializeTupleStruct, SerializeTupleVariant,
29 },
30};
31use thiserror::Error;
32
33const MAGIC: [u8; 4] = *b"NESC";
34const VERSION: u8 = 1;
35const HEADER_LEN: usize = MAGIC.len() + 1;
36const MAX_COLLECTION_COUNT: usize = 1_048_576;
37
38pub fn encode_to_vec<T: Serialize>(value: &T) -> Result<Vec<u8>, CodecError> {
48 let mut encoder = Encoder { out: Vec::new() };
49 encoder.out.extend_from_slice(&MAGIC);
50 encoder.out.push(VERSION);
51 value.serialize(&mut encoder)?;
52 Ok(encoder.out)
53}
54
55pub fn decode_from_slice<T: DeserializeOwned>(bytes: &[u8]) -> Result<T, CodecError> {
65 let mut decoder = Decoder {
66 input: bytes,
67 pos: 0,
68 };
69 decoder.read_header()?;
70 let value = T::deserialize(&mut decoder)?;
71 if decoder.pos != decoder.input.len() {
72 return Err(CodecError::TrailingBytes(decoder.input.len() - decoder.pos));
73 }
74 Ok(value)
75}
76
77#[derive(Debug, Error)]
79#[non_exhaustive]
80pub enum CodecError {
81 #[error("unexpected end of codec input: needed {needed} more byte(s)")]
83 UnexpectedEof {
84 needed: usize,
86 },
87 #[error("bad codec magic: input is not a nautilus event-store codec frame")]
89 BadMagic,
90 #[error("unsupported codec version: {0}")]
92 UnsupportedVersion(u8),
93 #[error("encoded length {claimed} exceeds remaining input {remaining}")]
95 LengthOverflow {
96 claimed: u64,
103 remaining: usize,
105 },
106 #[error("encoded collection count {claimed} exceeds maximum {max}")]
108 CountOverflow {
109 claimed: u64,
111 max: usize,
113 },
114 #[error("invalid utf-8 in encoded string")]
116 InvalidUtf8,
117 #[error("invalid bool discriminant: {0:#04x}")]
119 InvalidBool(u8),
120 #[error("invalid option discriminant: {0:#04x}")]
122 InvalidOption(u8),
123 #[error("invalid char scalar value: {0:#010x}")]
125 InvalidChar(u32),
126 #[error("unknown enum variant index: {0}")]
128 UnknownVariant(u32),
129 #[error("sequence length was not provided by the serializer")]
131 MissingLen,
132 #[error("{0} unconsumed trailing byte(s) after decode")]
134 TrailingBytes(usize),
135 #[error("self-describing deserialization is not supported by this format")]
137 SelfDescribing,
138 #[error("{0}")]
140 Message(String),
141}
142
143impl ser::Error for CodecError {
144 fn custom<T: Display>(msg: T) -> Self {
145 Self::Message(msg.to_string())
146 }
147}
148
149impl de::Error for CodecError {
150 fn custom<T: Display>(msg: T) -> Self {
151 Self::Message(msg.to_string())
152 }
153}
154
155#[derive(Debug)]
156struct Encoder {
157 out: Vec<u8>,
158}
159
160impl Encoder {
161 fn write_len(&mut self, len: usize) {
162 self.out.extend_from_slice(&(len as u64).to_le_bytes());
163 }
164
165 fn write_count(&mut self, count: usize) -> Result<(), CodecError> {
166 if count > MAX_COLLECTION_COUNT {
167 return Err(CodecError::CountOverflow {
168 claimed: count as u64,
169 max: MAX_COLLECTION_COUNT,
170 });
171 }
172
173 self.write_len(count);
174 Ok(())
175 }
176
177 fn write_variant(&mut self, variant_index: u32) {
178 self.out.extend_from_slice(&variant_index.to_le_bytes());
179 }
180}
181
182impl ser::Serializer for &mut Encoder {
183 type Ok = ();
184 type Error = CodecError;
185 type SerializeSeq = Self;
186 type SerializeTuple = Self;
187 type SerializeTupleStruct = Self;
188 type SerializeTupleVariant = Self;
189 type SerializeMap = Self;
190 type SerializeStruct = Self;
191 type SerializeStructVariant = Self;
192
193 fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
194 self.out.push(u8::from(v));
195 Ok(())
196 }
197
198 fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
199 self.out.extend_from_slice(&v.to_le_bytes());
200 Ok(())
201 }
202
203 fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
204 self.out.extend_from_slice(&v.to_le_bytes());
205 Ok(())
206 }
207
208 fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
209 self.out.extend_from_slice(&v.to_le_bytes());
210 Ok(())
211 }
212
213 fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
214 self.out.extend_from_slice(&v.to_le_bytes());
215 Ok(())
216 }
217
218 fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
219 self.out.push(v);
220 Ok(())
221 }
222
223 fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
224 self.out.extend_from_slice(&v.to_le_bytes());
225 Ok(())
226 }
227
228 fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
229 self.out.extend_from_slice(&v.to_le_bytes());
230 Ok(())
231 }
232
233 fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
234 self.out.extend_from_slice(&v.to_le_bytes());
235 Ok(())
236 }
237
238 fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
239 self.out.extend_from_slice(&v.to_le_bytes());
240 Ok(())
241 }
242
243 fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
244 self.out.extend_from_slice(&v.to_le_bytes());
245 Ok(())
246 }
247
248 fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
249 self.out.extend_from_slice(&(v as u32).to_le_bytes());
250 Ok(())
251 }
252
253 fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
254 self.write_len(v.len());
255 self.out.extend_from_slice(v.as_bytes());
256 Ok(())
257 }
258
259 fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
260 self.write_len(v.len());
261 self.out.extend_from_slice(v);
262 Ok(())
263 }
264
265 fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
266 self.out.push(0);
267 Ok(())
268 }
269
270 fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
271 where
272 T: ?Sized + Serialize,
273 {
274 self.out.push(1);
275 value.serialize(self)
276 }
277
278 fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
279 Ok(())
280 }
281
282 fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
283 Ok(())
284 }
285
286 fn serialize_unit_variant(
287 self,
288 _name: &'static str,
289 variant_index: u32,
290 _variant: &'static str,
291 ) -> Result<Self::Ok, Self::Error> {
292 self.write_variant(variant_index);
293 Ok(())
294 }
295
296 fn serialize_newtype_struct<T>(
297 self,
298 _name: &'static str,
299 value: &T,
300 ) -> Result<Self::Ok, Self::Error>
301 where
302 T: ?Sized + Serialize,
303 {
304 value.serialize(self)
305 }
306
307 fn serialize_newtype_variant<T>(
308 self,
309 _name: &'static str,
310 variant_index: u32,
311 _variant: &'static str,
312 value: &T,
313 ) -> Result<Self::Ok, Self::Error>
314 where
315 T: ?Sized + Serialize,
316 {
317 self.write_variant(variant_index);
318 value.serialize(self)
319 }
320
321 fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
322 let len = len.ok_or(CodecError::MissingLen)?;
323 self.write_count(len)?;
324 Ok(self)
325 }
326
327 fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
328 Ok(self)
329 }
330
331 fn serialize_tuple_struct(
332 self,
333 _name: &'static str,
334 _len: usize,
335 ) -> Result<Self::SerializeTupleStruct, Self::Error> {
336 Ok(self)
337 }
338
339 fn serialize_tuple_variant(
340 self,
341 _name: &'static str,
342 variant_index: u32,
343 _variant: &'static str,
344 _len: usize,
345 ) -> Result<Self::SerializeTupleVariant, Self::Error> {
346 self.write_variant(variant_index);
347 Ok(self)
348 }
349
350 fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
351 let len = len.ok_or(CodecError::MissingLen)?;
352 self.write_count(len)?;
353 Ok(self)
354 }
355
356 fn serialize_struct(
357 self,
358 _name: &'static str,
359 _len: usize,
360 ) -> Result<Self::SerializeStruct, Self::Error> {
361 Ok(self)
362 }
363
364 fn serialize_struct_variant(
365 self,
366 _name: &'static str,
367 variant_index: u32,
368 _variant: &'static str,
369 _len: usize,
370 ) -> Result<Self::SerializeStructVariant, Self::Error> {
371 self.write_variant(variant_index);
372 Ok(self)
373 }
374
375 fn is_human_readable(&self) -> bool {
376 false
377 }
378}
379
380impl SerializeSeq for &mut Encoder {
381 type Ok = ();
382 type Error = CodecError;
383
384 fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
385 where
386 T: ?Sized + Serialize,
387 {
388 value.serialize(&mut **self)
389 }
390
391 fn end(self) -> Result<Self::Ok, Self::Error> {
392 Ok(())
393 }
394}
395
396impl SerializeTuple for &mut Encoder {
397 type Ok = ();
398 type Error = CodecError;
399
400 fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
401 where
402 T: ?Sized + Serialize,
403 {
404 value.serialize(&mut **self)
405 }
406
407 fn end(self) -> Result<Self::Ok, Self::Error> {
408 Ok(())
409 }
410}
411
412impl SerializeTupleStruct for &mut Encoder {
413 type Ok = ();
414 type Error = CodecError;
415
416 fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
417 where
418 T: ?Sized + Serialize,
419 {
420 value.serialize(&mut **self)
421 }
422
423 fn end(self) -> Result<Self::Ok, Self::Error> {
424 Ok(())
425 }
426}
427
428impl SerializeTupleVariant for &mut Encoder {
429 type Ok = ();
430 type Error = CodecError;
431
432 fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
433 where
434 T: ?Sized + Serialize,
435 {
436 value.serialize(&mut **self)
437 }
438
439 fn end(self) -> Result<Self::Ok, Self::Error> {
440 Ok(())
441 }
442}
443
444impl SerializeMap for &mut Encoder {
445 type Ok = ();
446 type Error = CodecError;
447
448 fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
449 where
450 T: ?Sized + Serialize,
451 {
452 key.serialize(&mut **self)
453 }
454
455 fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
456 where
457 T: ?Sized + Serialize,
458 {
459 value.serialize(&mut **self)
460 }
461
462 fn end(self) -> Result<Self::Ok, Self::Error> {
463 Ok(())
464 }
465}
466
467impl SerializeStruct for &mut Encoder {
468 type Ok = ();
469 type Error = CodecError;
470
471 fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<(), Self::Error>
472 where
473 T: ?Sized + Serialize,
474 {
475 value.serialize(&mut **self)
476 }
477
478 fn end(self) -> Result<Self::Ok, Self::Error> {
479 Ok(())
480 }
481}
482
483impl SerializeStructVariant for &mut Encoder {
484 type Ok = ();
485 type Error = CodecError;
486
487 fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<(), Self::Error>
488 where
489 T: ?Sized + Serialize,
490 {
491 value.serialize(&mut **self)
492 }
493
494 fn end(self) -> Result<Self::Ok, Self::Error> {
495 Ok(())
496 }
497}
498
499#[derive(Debug)]
500struct Decoder<'de> {
501 input: &'de [u8],
502 pos: usize,
503}
504
505impl<'de> Decoder<'de> {
506 fn read_header(&mut self) -> Result<(), CodecError> {
507 if self.input.len() < HEADER_LEN {
508 return Err(CodecError::UnexpectedEof { needed: HEADER_LEN });
509 }
510
511 let magic = self.take(MAGIC.len())?;
512 if magic != MAGIC {
513 return Err(CodecError::BadMagic);
514 }
515
516 let version = self.take(1)?[0];
517 if version != VERSION {
518 return Err(CodecError::UnsupportedVersion(version));
519 }
520
521 Ok(())
522 }
523
524 fn remaining(&self) -> usize {
525 self.input.len() - self.pos
526 }
527
528 fn take(&mut self, n: usize) -> Result<&'de [u8], CodecError> {
529 let end = self
530 .pos
531 .checked_add(n)
532 .ok_or(CodecError::UnexpectedEof { needed: n })?;
533 let slice = self
534 .input
535 .get(self.pos..end)
536 .ok_or(CodecError::UnexpectedEof { needed: n })?;
537 self.pos = end;
538 Ok(slice)
539 }
540
541 fn read_byte_len(&mut self) -> Result<usize, CodecError> {
542 let raw = self.read_u64()?;
543 let remaining = self.remaining();
544 match usize::try_from(raw) {
548 Ok(claimed) if claimed <= remaining => Ok(claimed),
549 _ => Err(CodecError::LengthOverflow {
550 claimed: raw,
551 remaining,
552 }),
553 }
554 }
555
556 fn read_count(&mut self) -> Result<usize, CodecError> {
557 let raw = self.read_u64()?;
558 match usize::try_from(raw) {
559 Ok(count) if count <= MAX_COLLECTION_COUNT => Ok(count),
560 _ => Err(CodecError::CountOverflow {
561 claimed: raw,
562 max: MAX_COLLECTION_COUNT,
563 }),
564 }
565 }
566
567 fn read_u16(&mut self) -> Result<u16, CodecError> {
568 let bytes: [u8; 2] = self
569 .take(2)?
570 .try_into()
571 .expect("take returned the exact requested width");
572 Ok(u16::from_le_bytes(bytes))
573 }
574
575 fn read_u32(&mut self) -> Result<u32, CodecError> {
576 let bytes: [u8; 4] = self
577 .take(4)?
578 .try_into()
579 .expect("take returned the exact requested width");
580 Ok(u32::from_le_bytes(bytes))
581 }
582
583 fn read_u64(&mut self) -> Result<u64, CodecError> {
584 let bytes: [u8; 8] = self
585 .take(8)?
586 .try_into()
587 .expect("take returned the exact requested width");
588 Ok(u64::from_le_bytes(bytes))
589 }
590}
591
592macro_rules! deserialize_integer {
593 ($method:ident, $visit:ident, $ty:ty, $width:literal) => {
594 fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
595 where
596 V: Visitor<'de>,
597 {
598 let bytes: [u8; $width] = self
599 .take($width)?
600 .try_into()
601 .expect("take returned the exact requested width");
602 visitor.$visit(<$ty>::from_le_bytes(bytes))
603 }
604 };
605}
606
607impl<'de> de::Deserializer<'de> for &mut Decoder<'de> {
608 type Error = CodecError;
609
610 fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
611 where
612 V: Visitor<'de>,
613 {
614 Err(CodecError::SelfDescribing)
615 }
616
617 fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
618 where
619 V: Visitor<'de>,
620 {
621 match self.take(1)?[0] {
622 0 => visitor.visit_bool(false),
623 1 => visitor.visit_bool(true),
624 other => Err(CodecError::InvalidBool(other)),
625 }
626 }
627
628 fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
629 where
630 V: Visitor<'de>,
631 {
632 let bytes: [u8; 1] = self
633 .take(1)?
634 .try_into()
635 .expect("take returned the exact requested width");
636 visitor.visit_i8(i8::from_le_bytes(bytes))
637 }
638
639 deserialize_integer!(deserialize_i16, visit_i16, i16, 2);
640 deserialize_integer!(deserialize_i32, visit_i32, i32, 4);
641 deserialize_integer!(deserialize_i64, visit_i64, i64, 8);
642
643 fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
644 where
645 V: Visitor<'de>,
646 {
647 visitor.visit_u8(self.take(1)?[0])
648 }
649
650 fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
651 where
652 V: Visitor<'de>,
653 {
654 visitor.visit_u16(self.read_u16()?)
655 }
656
657 fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
658 where
659 V: Visitor<'de>,
660 {
661 visitor.visit_u32(self.read_u32()?)
662 }
663
664 fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
665 where
666 V: Visitor<'de>,
667 {
668 visitor.visit_u64(self.read_u64()?)
669 }
670
671 fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
672 where
673 V: Visitor<'de>,
674 {
675 let bytes: [u8; 4] = self
676 .take(4)?
677 .try_into()
678 .expect("take returned the exact requested width");
679 visitor.visit_f32(f32::from_le_bytes(bytes))
680 }
681
682 fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
683 where
684 V: Visitor<'de>,
685 {
686 let bytes: [u8; 8] = self
687 .take(8)?
688 .try_into()
689 .expect("take returned the exact requested width");
690 visitor.visit_f64(f64::from_le_bytes(bytes))
691 }
692
693 fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
694 where
695 V: Visitor<'de>,
696 {
697 let raw = self.read_u32()?;
698 let value = char::from_u32(raw).ok_or(CodecError::InvalidChar(raw))?;
699 visitor.visit_char(value)
700 }
701
702 fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
703 where
704 V: Visitor<'de>,
705 {
706 let len = self.read_byte_len()?;
707 let bytes = self.take(len)?;
708 let value = str::from_utf8(bytes).map_err(|_| CodecError::InvalidUtf8)?;
709 visitor.visit_borrowed_str(value)
710 }
711
712 fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
713 where
714 V: Visitor<'de>,
715 {
716 self.deserialize_str(visitor)
717 }
718
719 fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
720 where
721 V: Visitor<'de>,
722 {
723 let len = self.read_byte_len()?;
724 let bytes = self.take(len)?;
725 visitor.visit_borrowed_bytes(bytes)
726 }
727
728 fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
729 where
730 V: Visitor<'de>,
731 {
732 self.deserialize_bytes(visitor)
733 }
734
735 fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
736 where
737 V: Visitor<'de>,
738 {
739 match self.take(1)?[0] {
740 0 => visitor.visit_none(),
741 1 => visitor.visit_some(self),
742 other => Err(CodecError::InvalidOption(other)),
743 }
744 }
745
746 fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
747 where
748 V: Visitor<'de>,
749 {
750 visitor.visit_unit()
751 }
752
753 fn deserialize_unit_struct<V>(
754 self,
755 _name: &'static str,
756 visitor: V,
757 ) -> Result<V::Value, Self::Error>
758 where
759 V: Visitor<'de>,
760 {
761 visitor.visit_unit()
762 }
763
764 fn deserialize_newtype_struct<V>(
765 self,
766 _name: &'static str,
767 visitor: V,
768 ) -> Result<V::Value, Self::Error>
769 where
770 V: Visitor<'de>,
771 {
772 visitor.visit_newtype_struct(self)
773 }
774
775 fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
776 where
777 V: Visitor<'de>,
778 {
779 let remaining = self.read_count()?;
780 visitor.visit_seq(SeqReader {
781 dec: self,
782 remaining,
783 })
784 }
785
786 fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
787 where
788 V: Visitor<'de>,
789 {
790 visitor.visit_seq(SeqReader {
791 dec: self,
792 remaining: len,
793 })
794 }
795
796 fn deserialize_tuple_struct<V>(
797 self,
798 _name: &'static str,
799 len: usize,
800 visitor: V,
801 ) -> Result<V::Value, Self::Error>
802 where
803 V: Visitor<'de>,
804 {
805 self.deserialize_tuple(len, visitor)
806 }
807
808 fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
809 where
810 V: Visitor<'de>,
811 {
812 let remaining = self.read_count()?;
813 visitor.visit_map(MapReader {
814 dec: self,
815 remaining,
816 })
817 }
818
819 fn deserialize_struct<V>(
820 self,
821 _name: &'static str,
822 fields: &'static [&'static str],
823 visitor: V,
824 ) -> Result<V::Value, Self::Error>
825 where
826 V: Visitor<'de>,
827 {
828 visitor.visit_seq(SeqReader {
829 dec: self,
830 remaining: fields.len(),
831 })
832 }
833
834 fn deserialize_enum<V>(
835 self,
836 _name: &'static str,
837 variants: &'static [&'static str],
838 visitor: V,
839 ) -> Result<V::Value, Self::Error>
840 where
841 V: Visitor<'de>,
842 {
843 let index = self.read_u32()?;
844 if index as usize >= variants.len() {
845 return Err(CodecError::UnknownVariant(index));
846 }
847 visitor.visit_enum(EnumReader { dec: self, index })
848 }
849
850 fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
851 where
852 V: Visitor<'de>,
853 {
854 self.deserialize_u32(visitor)
855 }
856
857 fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
858 where
859 V: Visitor<'de>,
860 {
861 Err(CodecError::SelfDescribing)
862 }
863
864 fn is_human_readable(&self) -> bool {
865 false
866 }
867}
868
869#[derive(Debug)]
870struct SeqReader<'a, 'de> {
871 dec: &'a mut Decoder<'de>,
872 remaining: usize,
873}
874
875impl<'de> SeqAccess<'de> for SeqReader<'_, 'de> {
876 type Error = CodecError;
877
878 fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
879 where
880 T: DeserializeSeed<'de>,
881 {
882 if self.remaining == 0 {
883 return Ok(None);
884 }
885
886 self.remaining -= 1;
887 seed.deserialize(&mut *self.dec).map(Some)
888 }
889
890 fn size_hint(&self) -> Option<usize> {
891 Some(self.remaining)
892 }
893}
894
895#[derive(Debug)]
896struct MapReader<'a, 'de> {
897 dec: &'a mut Decoder<'de>,
898 remaining: usize,
899}
900
901impl<'de> MapAccess<'de> for MapReader<'_, 'de> {
902 type Error = CodecError;
903
904 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
905 where
906 K: DeserializeSeed<'de>,
907 {
908 if self.remaining == 0 {
909 return Ok(None);
910 }
911
912 self.remaining -= 1;
913 seed.deserialize(&mut *self.dec).map(Some)
914 }
915
916 fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
917 where
918 V: DeserializeSeed<'de>,
919 {
920 seed.deserialize(&mut *self.dec)
921 }
922
923 fn size_hint(&self) -> Option<usize> {
924 Some(self.remaining)
925 }
926}
927
928#[derive(Debug)]
929struct EnumReader<'a, 'de> {
930 dec: &'a mut Decoder<'de>,
931 index: u32,
932}
933
934impl<'de> EnumAccess<'de> for EnumReader<'_, 'de> {
935 type Error = CodecError;
936 type Variant = Self;
937
938 fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
939 where
940 V: DeserializeSeed<'de>,
941 {
942 let value = seed.deserialize(self.index.into_deserializer())?;
943 Ok((value, self))
944 }
945}
946
947impl<'de> VariantAccess<'de> for EnumReader<'_, 'de> {
948 type Error = CodecError;
949
950 fn unit_variant(self) -> Result<(), Self::Error> {
951 Ok(())
952 }
953
954 fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
955 where
956 T: DeserializeSeed<'de>,
957 {
958 seed.deserialize(self.dec)
959 }
960
961 fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
962 where
963 V: Visitor<'de>,
964 {
965 visitor.visit_seq(SeqReader {
966 dec: self.dec,
967 remaining: len,
968 })
969 }
970
971 fn struct_variant<V>(
972 self,
973 fields: &'static [&'static str],
974 visitor: V,
975 ) -> Result<V::Value, Self::Error>
976 where
977 V: Visitor<'de>,
978 {
979 visitor.visit_seq(SeqReader {
980 dec: self.dec,
981 remaining: fields.len(),
982 })
983 }
984}
985
986#[cfg(test)]
987mod tests {
988 use std::array;
989
990 use bytes::Bytes;
991 use indexmap::IndexMap;
992 use nautilus_core::{UUID4, UnixNanos};
993 use nautilus_system::RegisteredComponents;
994 use proptest::{prelude::*, test_runner::Config as ProptestConfig};
995 use rstest::rstest;
996 use serde::{Deserialize, Serializer};
997 use ustr::Ustr;
998
999 use super::*;
1000 use crate::{
1001 EventStoreEntry, Headers, RunManifest, RunStatus, SnapshotAnchor,
1002 hash::{EntryHash, compute_entry_hash},
1003 markers::{
1004 DataClass, DataCursorSnapshot, HiFiMarker, MarkerGap, MarkerGapReason, StreamCursor,
1005 StreamDictEntry,
1006 },
1007 };
1008
1009 #[derive(Debug, Serialize, Deserialize)]
1010 struct ScalarProbe {
1011 b: bool,
1012 i8s: [i8; 3],
1013 i16s: [i16; 3],
1014 i32s: [i32; 3],
1015 i64s: [i64; 3],
1016 u16: u16,
1017 f32s: [f32; 3],
1018 f64s: [f64; 3],
1019 ch: char,
1020 }
1021
1022 impl PartialEq for ScalarProbe {
1023 fn eq(&self, other: &Self) -> bool {
1024 self.b == other.b
1025 && self.i8s == other.i8s
1026 && self.i16s == other.i16s
1027 && self.i32s == other.i32s
1028 && self.i64s == other.i64s
1029 && self.u16 == other.u16
1030 && self
1031 .f32s
1032 .iter()
1033 .zip(other.f32s)
1034 .all(|(left, right)| left.to_bits() == right.to_bits())
1035 && self
1036 .f64s
1037 .iter()
1038 .zip(other.f64s)
1039 .all(|(left, right)| left.to_bits() == right.to_bits())
1040 && self.ch == other.ch
1041 }
1042 }
1043
1044 #[derive(Debug, PartialEq, Serialize, Deserialize)]
1045 struct BoolProbe {
1046 b: bool,
1047 }
1048
1049 struct TooManySeq;
1050
1051 impl Serialize for TooManySeq {
1052 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1053 where
1054 S: Serializer,
1055 {
1056 let seq = serializer.serialize_seq(Some(MAX_COLLECTION_COUNT + 1))?;
1057 SerializeSeq::end(seq)
1058 }
1059 }
1060
1061 fn roundtrip<T>(value: &T) -> T
1062 where
1063 T: Serialize + DeserializeOwned,
1064 {
1065 decode_from_slice(&encode_to_vec(value).expect("encode")).expect("decode")
1066 }
1067
1068 fn headers_populated() -> Headers {
1069 Headers {
1070 correlation_id: Some(UUID4::from_bytes([1; 16])),
1071 causation_id: Some(UUID4::from_bytes([2; 16])),
1072 }
1073 }
1074
1075 fn entry(headers: Headers) -> EventStoreEntry {
1076 let topic = "exec.command".into();
1077 let payload_type = Ustr::from("SubmitOrder");
1078 let payload = Bytes::from_static(b"\x01\x02\x03\x04");
1079 let seq = 42;
1080 let ts_init = UnixNanos::from(1_700_000_000_000_000_000);
1081 let ts_publish = UnixNanos::from(1_700_000_000_000_000_001);
1082 let entry_hash = compute_entry_hash(
1083 seq,
1084 ts_init,
1085 ts_publish,
1086 "exec.command",
1087 payload_type.as_str(),
1088 &payload,
1089 &headers,
1090 );
1091
1092 EventStoreEntry::new(
1093 entry_hash,
1094 seq,
1095 headers,
1096 topic,
1097 payload_type,
1098 payload,
1099 ts_init,
1100 ts_publish,
1101 )
1102 }
1103
1104 fn registered_components() -> RegisteredComponents {
1105 let mut components = RegisteredComponents::default();
1106 components
1107 .actors
1108 .insert("actor-1".to_string(), "hash-a".to_string());
1109 components
1110 .strategies
1111 .insert("strategy-1".to_string(), "hash-s".to_string());
1112 components
1113 .algorithms
1114 .insert("algo-1".to_string(), "hash-g".to_string());
1115 components.subscriptions.push("data.quotes".to_string());
1116 components.endpoints.push("exec.command".to_string());
1117 components
1118 }
1119
1120 fn running_manifest() -> RunManifest {
1121 RunManifest {
1122 run_id: "1700000000-abcd1234".to_string(),
1123 parent_run_id: None,
1124 instance_id: "trader-001".to_string(),
1125 binary_hash: "deadbeef".to_string(),
1126 schema_version: 1,
1127 crate_versions: "feedface".to_string(),
1128 feature_flags: Vec::new(),
1129 adapter_versions: IndexMap::new(),
1130 config_hash: "cafebabe".to_string(),
1131 registered_components: RegisteredComponents::default(),
1132 seed: None,
1133 start_ts_init: UnixNanos::from(10),
1134 end_ts_init: None,
1135 high_watermark: 0,
1136 status: RunStatus::Running,
1137 }
1138 }
1139
1140 fn sealed_manifest() -> RunManifest {
1141 let mut adapter_versions = IndexMap::new();
1142 adapter_versions.insert("binance".to_string(), "1.2.3".to_string());
1143 adapter_versions.insert("okx".to_string(), "2.3.4".to_string());
1144
1145 RunManifest {
1146 run_id: "1700000010-cafe1234".to_string(),
1147 parent_run_id: Some("1700000000-abcd1234".to_string()),
1148 instance_id: "trader-001".to_string(),
1149 binary_hash: "deadbeef".to_string(),
1150 schema_version: 2,
1151 crate_versions: "feedface".to_string(),
1152 feature_flags: vec!["live".to_string(), "persistence".to_string()],
1153 adapter_versions,
1154 config_hash: "cafebabe".to_string(),
1155 registered_components: registered_components(),
1156 seed: Some(7),
1157 start_ts_init: UnixNanos::from(10),
1158 end_ts_init: Some(UnixNanos::from(20)),
1159 high_watermark: 99,
1160 status: RunStatus::Ended,
1161 }
1162 }
1163
1164 fn snapshot_with_cursors() -> DataCursorSnapshot {
1165 DataCursorSnapshot {
1166 marker_seq: 7,
1167 event_seq_before: 42,
1168 ts_init: UnixNanos::from(100),
1169 advanced: vec![
1170 StreamCursor {
1171 slot: 1,
1172 ts_init_hi: UnixNanos::from(101),
1173 count: 10,
1174 },
1175 StreamCursor {
1176 slot: 2,
1177 ts_init_hi: UnixNanos::from(102),
1178 count: 11,
1179 },
1180 ],
1181 }
1182 }
1183
1184 fn hifi_marker() -> HiFiMarker {
1185 HiFiMarker {
1186 marker_seq: 1,
1187 event_seq_before: 42,
1188 slot: 3,
1189 ts_event: UnixNanos::from(1000),
1190 ts_init: UnixNanos::from(1001),
1191 same_ts_ordinal: 2,
1192 record_fingerprint: array::from_fn(|idx| {
1193 u8::try_from(idx).expect("fingerprint index is in 0..32")
1194 }),
1195 }
1196 }
1197
1198 #[rstest]
1199 #[case::empty(Headers::empty())]
1200 #[case::populated(headers_populated())]
1201 fn roundtrip_event_store_entry(#[case] headers: Headers) {
1202 let decoded = roundtrip(&entry(headers));
1203
1204 assert_eq!(decoded.recompute_hash(), decoded.entry_hash);
1205 }
1206
1207 #[rstest]
1208 #[case::running(running_manifest())]
1209 #[case::sealed(sealed_manifest())]
1210 fn roundtrip_run_manifest(#[case] manifest: RunManifest) {
1211 assert_eq!(roundtrip(&manifest), manifest);
1212 }
1213
1214 #[rstest]
1215 fn roundtrip_registered_components() {
1216 let components = registered_components();
1217
1218 assert_eq!(roundtrip(&components), components);
1219 }
1220
1221 #[rstest]
1222 fn roundtrip_snapshot_anchor() {
1223 let anchor = SnapshotAnchor::new(10, "cache://run/10", "blake3:abcd");
1224
1225 assert_eq!(roundtrip(&anchor), anchor);
1226 }
1227
1228 #[rstest]
1229 #[case::advanced(snapshot_with_cursors())]
1230 #[case::empty(DataCursorSnapshot {
1231 marker_seq: 8,
1232 event_seq_before: 43,
1233 ts_init: UnixNanos::from(101),
1234 advanced: Vec::new(),
1235 })]
1236 fn roundtrip_data_cursor_snapshot(#[case] snapshot: DataCursorSnapshot) {
1237 assert_eq!(roundtrip(&snapshot), snapshot);
1238 }
1239
1240 #[rstest]
1241 fn roundtrip_hifi_marker() {
1242 let marker = hifi_marker();
1243
1244 assert_eq!(roundtrip(&marker), marker);
1245 }
1246
1247 #[rstest]
1248 #[case(MarkerGapReason::Overflow)]
1249 #[case(MarkerGapReason::WriterClosed)]
1250 fn roundtrip_marker_gap(#[case] reason: MarkerGapReason) {
1251 let gap = MarkerGap {
1252 from_marker_seq: 1,
1253 to_marker_seq: 2,
1254 reason,
1255 };
1256
1257 assert_eq!(roundtrip(&gap), gap);
1258 }
1259
1260 #[rstest]
1261 fn roundtrip_stream_dict_entry() {
1262 let entry = StreamDictEntry {
1263 slot: 3,
1264 data_cls: DataClass::Quote,
1265 identifier: "ETHUSDT.BINANCE".to_string(),
1266 };
1267
1268 assert_eq!(roundtrip(&entry), entry);
1269 }
1270
1271 #[rstest]
1272 #[case(RunStatus::Running)]
1273 #[case(RunStatus::Ended)]
1274 #[case(RunStatus::CrashedRecovered)]
1275 #[case(RunStatus::Quarantined)]
1276 fn roundtrip_run_status_all_variants(#[case] status: RunStatus) {
1277 assert_eq!(roundtrip(&status), status);
1278 }
1279
1280 #[rstest]
1281 #[case(DataClass::BookDeltas)]
1282 #[case(DataClass::BookDepth10)]
1283 #[case(DataClass::Quote)]
1284 #[case(DataClass::Trade)]
1285 #[case(DataClass::Bar)]
1286 fn roundtrip_data_class_all_variants(#[case] data_class: DataClass) {
1287 assert_eq!(roundtrip(&data_class), data_class);
1288 }
1289
1290 #[rstest]
1291 #[case(MarkerGapReason::Overflow)]
1292 #[case(MarkerGapReason::WriterClosed)]
1293 fn roundtrip_marker_gap_reason_all_variants(#[case] reason: MarkerGapReason) {
1294 assert_eq!(roundtrip(&reason), reason);
1295 }
1296
1297 #[rstest]
1298 #[case::false_value(false)]
1299 #[case::true_value(true)]
1300 fn roundtrip_scalars(#[case] value: bool) {
1301 let probe = ScalarProbe {
1302 b: value,
1303 i8s: [i8::MIN, -1, i8::MAX],
1304 i16s: [i16::MIN, -2, i16::MAX],
1305 i32s: [i32::MIN, -3, i32::MAX],
1306 i64s: [i64::MIN, -4, i64::MAX],
1307 u16: u16::MAX,
1308 f32s: [0.0, 1.25, f32::NAN],
1309 f64s: [0.0, f64::INFINITY, f64::NAN],
1310 ch: '∞',
1311 };
1312
1313 assert_eq!(roundtrip(&probe), probe);
1314 }
1315
1316 #[rstest]
1317 fn roundtrip_zero_width_sequence() {
1318 let value = vec![(); 10];
1319
1320 assert_eq!(roundtrip(&value), value);
1321 }
1322
1323 #[rstest]
1324 fn encode_is_deterministic() {
1325 let entry = entry(headers_populated());
1326 let manifest = sealed_manifest();
1327
1328 assert_eq!(
1329 encode_to_vec(&entry).unwrap(),
1330 encode_to_vec(&entry).unwrap()
1331 );
1332 assert_eq!(
1333 encode_to_vec(&manifest).unwrap(),
1334 encode_to_vec(&manifest).unwrap()
1335 );
1336 }
1337
1338 #[rstest]
1339 fn header_present_and_correct() {
1340 let bytes = encode_to_vec(&RunStatus::Running).unwrap();
1341
1342 assert_eq!(&bytes[..4], b"NESC");
1343 assert_eq!(bytes[4], 1);
1344 }
1345
1346 #[rstest]
1347 fn header_bad_magic_rejected() {
1348 let mut bytes = encode_to_vec(&entry(Headers::empty())).unwrap();
1349 bytes[0] ^= 0xFF;
1350
1351 assert!(matches!(
1352 decode_from_slice::<EventStoreEntry>(&bytes),
1353 Err(CodecError::BadMagic)
1354 ));
1355 }
1356
1357 #[rstest]
1358 fn header_unsupported_version_rejected() {
1359 let mut bytes = encode_to_vec(&entry(Headers::empty())).unwrap();
1360 bytes[4] = 2;
1361
1362 assert!(matches!(
1363 decode_from_slice::<EventStoreEntry>(&bytes),
1364 Err(CodecError::UnsupportedVersion(2))
1365 ));
1366 }
1367
1368 #[rstest]
1369 fn header_truncated_rejected() {
1370 let bytes = encode_to_vec(&entry(Headers::empty())).unwrap();
1371
1372 assert!(matches!(
1373 decode_from_slice::<EventStoreEntry>(&bytes[..3]),
1374 Err(CodecError::UnexpectedEof { .. })
1375 ));
1376 }
1377
1378 #[rstest]
1379 fn decodes_a_bincode_blob_as_bad_magic() {
1380 const OLD_BINCODE_STRING: &[u8] = b"\x0aold-format";
1383
1384 assert!(matches!(
1385 decode_from_slice::<String>(OLD_BINCODE_STRING),
1386 Err(CodecError::BadMagic)
1387 ));
1388 }
1389
1390 #[rstest]
1391 fn truncated_body_rejected() {
1392 let bytes = encode_to_vec(&entry(Headers::empty())).unwrap();
1393
1394 assert!(decode_from_slice::<EventStoreEntry>(&bytes[..bytes.len() - 1]).is_err());
1395 }
1396
1397 #[rstest]
1398 fn forged_length_prefix_rejected() {
1399 let mut bytes = encode_to_vec(&"ok".to_string()).unwrap();
1400 bytes[HEADER_LEN..HEADER_LEN + 8].copy_from_slice(&u64::MAX.to_le_bytes());
1401
1402 assert!(matches!(
1403 decode_from_slice::<String>(&bytes),
1404 Err(CodecError::LengthOverflow { .. })
1405 ));
1406 }
1407
1408 #[rstest]
1409 fn forged_sequence_count_rejected_on_eof() {
1410 let mut bytes = encode_to_vec(&vec![1_u8, 2, 3]).unwrap();
1411 bytes[HEADER_LEN..HEADER_LEN + 8].copy_from_slice(&99_u64.to_le_bytes());
1412
1413 assert!(matches!(
1414 decode_from_slice::<Vec<u8>>(&bytes),
1415 Err(CodecError::UnexpectedEof { .. })
1416 ));
1417 }
1418
1419 #[rstest]
1420 fn collection_count_overflow_rejected() {
1421 let mut bytes = encode_to_vec(&vec![1_u8, 2, 3]).unwrap();
1422 let too_many = (MAX_COLLECTION_COUNT as u64) + 1;
1423 bytes[HEADER_LEN..HEADER_LEN + 8].copy_from_slice(&too_many.to_le_bytes());
1424
1425 assert!(matches!(
1426 decode_from_slice::<Vec<u8>>(&bytes),
1427 Err(CodecError::CountOverflow {
1428 claimed,
1429 max: MAX_COLLECTION_COUNT
1430 }) if claimed == too_many
1431 ));
1432 }
1433
1434 #[rstest]
1435 fn collection_count_overflow_rejected_on_encode() {
1436 assert!(matches!(
1437 encode_to_vec(&TooManySeq),
1438 Err(CodecError::CountOverflow {
1439 claimed,
1440 max: MAX_COLLECTION_COUNT
1441 }) if claimed == (MAX_COLLECTION_COUNT as u64) + 1
1442 ));
1443 }
1444
1445 #[rstest]
1446 fn bad_bool_discriminant_rejected() {
1447 let mut bytes = encode_to_vec(&BoolProbe { b: false }).unwrap();
1448 bytes[HEADER_LEN] = 2;
1449
1450 assert!(matches!(
1451 decode_from_slice::<BoolProbe>(&bytes),
1452 Err(CodecError::InvalidBool(2))
1453 ));
1454 }
1455
1456 #[rstest]
1457 fn bad_option_discriminant_rejected() {
1458 let mut bytes = encode_to_vec(&Headers::empty()).unwrap();
1459 bytes[HEADER_LEN] = 2;
1460
1461 assert!(matches!(
1462 decode_from_slice::<Headers>(&bytes),
1463 Err(CodecError::InvalidOption(2))
1464 ));
1465 }
1466
1467 #[rstest]
1468 fn invalid_utf8_rejected() {
1469 let mut bytes = encode_to_vec(&"ok".to_string()).unwrap();
1470 bytes[HEADER_LEN + 8] = 0xFF;
1471
1472 assert!(matches!(
1473 decode_from_slice::<String>(&bytes),
1474 Err(CodecError::InvalidUtf8)
1475 ));
1476 }
1477
1478 #[rstest]
1479 fn unknown_enum_variant_rejected() {
1480 let mut bytes = encode_to_vec(&RunStatus::Running).unwrap();
1481 bytes[HEADER_LEN..HEADER_LEN + 4].copy_from_slice(&99_u32.to_le_bytes());
1482
1483 assert!(matches!(
1484 decode_from_slice::<RunStatus>(&bytes),
1485 Err(CodecError::UnknownVariant(99))
1486 ));
1487 }
1488
1489 #[rstest]
1490 fn trailing_bytes_rejected() {
1491 let mut bytes = encode_to_vec(&RunStatus::Running).unwrap();
1492 bytes.push(0xFF);
1493
1494 assert!(matches!(
1495 decode_from_slice::<RunStatus>(&bytes),
1496 Err(CodecError::TrailingBytes(1))
1497 ));
1498 }
1499
1500 #[rstest]
1501 fn rejects_self_describing() {
1502 #[allow(dead_code)]
1503 #[derive(Debug, Deserialize)]
1504 #[serde(untagged)]
1505 enum Probe {
1506 A(u64),
1507 B(String),
1508 }
1509
1510 let bytes = encode_to_vec(&1_u64).unwrap();
1511 let err = decode_from_slice::<Probe>(&bytes).expect_err("untagged enum must reject any");
1512 assert!(matches!(err, CodecError::SelfDescribing));
1513 }
1514
1515 #[rstest]
1516 fn serialize_without_known_len_is_rejected() {
1517 let mut encoder = Encoder { out: Vec::new() };
1518 assert!(matches!(
1519 ser::Serializer::serialize_seq(&mut encoder, None),
1520 Err(CodecError::MissingLen)
1521 ));
1522
1523 let mut encoder = Encoder { out: Vec::new() };
1524 assert!(matches!(
1525 ser::Serializer::serialize_map(&mut encoder, None),
1526 Err(CodecError::MissingLen)
1527 ));
1528 }
1529
1530 struct U32Visitor;
1531
1532 impl Visitor<'_> for U32Visitor {
1533 type Value = u32;
1534
1535 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1536 formatter.write_str("a little-endian u32 identifier")
1537 }
1538
1539 fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
1540 where
1541 E: de::Error,
1542 {
1543 Ok(value)
1544 }
1545 }
1546
1547 #[rstest]
1548 fn deserialize_identifier_reads_u32() {
1549 let body = 7_u32.to_le_bytes();
1554 let mut decoder = Decoder {
1555 input: &body,
1556 pos: 0,
1557 };
1558
1559 let value = de::Deserializer::deserialize_identifier(&mut decoder, U32Visitor)
1560 .expect("identifier decodes as a u32");
1561
1562 assert_eq!(value, 7);
1563 assert_eq!(decoder.pos, body.len());
1564 }
1565
1566 #[rstest]
1567 fn deserialize_ignored_any_rejected() {
1568 let mut decoder = Decoder { input: &[], pos: 0 };
1574
1575 let err = de::IgnoredAny::deserialize(&mut decoder)
1576 .expect_err("ignored_any must reject as self-describing");
1577
1578 assert!(matches!(err, CodecError::SelfDescribing));
1579 }
1580
1581 #[rstest]
1582 fn entry_hash_newtype_roundtrips() {
1583 let hash = EntryHash(array::from_fn(|idx| {
1584 u8::try_from(idx).expect("hash index is in 0..32")
1585 }));
1586
1587 assert_eq!(roundtrip(&hash), hash);
1588 }
1589
1590 proptest! {
1591 #![proptest_config(ProptestConfig { cases: 64, ..ProptestConfig::default() })]
1592
1593 #[rstest]
1594 fn prop_roundtrip_data_cursor_snapshot(
1595 marker_seq in any::<u64>(),
1596 event_seq_before in any::<u64>(),
1597 ts_init in any::<u64>(),
1598 cursors in proptest::collection::vec((any::<u32>(), any::<u64>(), any::<u64>()), 0..8),
1599 ) {
1600 let snap = DataCursorSnapshot {
1601 marker_seq,
1602 event_seq_before,
1603 ts_init: UnixNanos::from(ts_init),
1604 advanced: cursors
1605 .into_iter()
1606 .map(|(slot, hi, count)| StreamCursor {
1607 slot,
1608 ts_init_hi: UnixNanos::from(hi),
1609 count,
1610 })
1611 .collect(),
1612 };
1613
1614 let bytes = encode_to_vec(&snap).expect("encode");
1615 let decoded: DataCursorSnapshot = decode_from_slice(&bytes).expect("decode");
1616 prop_assert_eq!(snap, decoded);
1617 }
1618
1619 #[rstest]
1620 fn prop_roundtrip_hifi_marker(
1621 marker_seq in any::<u64>(),
1622 event_seq_before in any::<u64>(),
1623 slot in any::<u32>(),
1624 ts_event in any::<u64>(),
1625 ts_init in any::<u64>(),
1626 same_ts_ordinal in any::<u32>(),
1627 fingerprint in proptest::array::uniform32(any::<u8>()),
1628 ) {
1629 let marker = HiFiMarker {
1630 marker_seq,
1631 event_seq_before,
1632 slot,
1633 ts_event: UnixNanos::from(ts_event),
1634 ts_init: UnixNanos::from(ts_init),
1635 same_ts_ordinal,
1636 record_fingerprint: fingerprint,
1637 };
1638
1639 let bytes = encode_to_vec(&marker).expect("encode");
1640 let decoded: HiFiMarker = decode_from_slice(&bytes).expect("decode");
1641 prop_assert_eq!(marker, decoded);
1642 }
1643 }
1644}