1use core::fmt;
34
35pub const DEFAULT_SYMBOL_SIZE: usize = 1280;
37
38#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43#[cfg_attr(feature = "test-internals", derive(serde::Serialize))]
44pub struct ObjectId {
45 high: u64,
47 low: u64,
49}
50
51impl ObjectId {
52 #[inline]
54 #[must_use]
55 pub const fn new(high: u64, low: u64) -> Self {
56 Self { high, low }
57 }
58
59 #[inline]
61 #[must_use]
62 pub const fn from_u128(value: u128) -> Self {
63 Self {
64 high: (value >> 64) as u64,
65 low: value as u64,
66 }
67 }
68
69 #[inline]
71 #[must_use]
72 pub const fn as_u128(self) -> u128 {
73 ((self.high as u128) << 64) | (self.low as u128)
74 }
75
76 #[inline]
78 #[must_use]
79 pub const fn high(self) -> u64 {
80 self.high
81 }
82
83 #[inline]
85 #[must_use]
86 pub const fn low(self) -> u64 {
87 self.low
88 }
89
90 #[must_use]
94 pub fn new_random(rng: &mut crate::util::DetRng) -> Self {
95 Self {
96 high: rng.next_u64(),
97 low: rng.next_u64(),
98 }
99 }
100
101 #[doc(hidden)]
103 #[inline]
104 #[must_use]
105 pub const fn new_for_test(value: u64) -> Self {
106 Self {
107 high: 0,
108 low: value,
109 }
110 }
111
112 pub const NIL: Self = Self { high: 0, low: 0 };
114}
115
116impl fmt::Debug for ObjectId {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(f, "ObjectId({:016x}{:016x})", self.high, self.low)
119 }
120}
121
122impl fmt::Display for ObjectId {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(f, "Obj-{:08x}", (self.high >> 32) as u32)
126 }
127}
128
129#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
140pub struct SymbolId {
141 object_id: ObjectId,
143 sbn: u8,
145 esi: u32,
147}
148
149impl SymbolId {
150 #[inline]
152 #[must_use]
153 pub const fn new(object_id: ObjectId, sbn: u8, esi: u32) -> Self {
154 Self {
155 object_id,
156 sbn,
157 esi,
158 }
159 }
160
161 #[inline]
163 #[must_use]
164 pub const fn object_id(self) -> ObjectId {
165 self.object_id
166 }
167
168 #[inline]
170 #[must_use]
171 pub const fn sbn(self) -> u8 {
172 self.sbn
173 }
174
175 #[inline]
177 #[must_use]
178 pub const fn esi(self) -> u32 {
179 self.esi
180 }
181
182 #[inline]
184 #[must_use]
185 pub const fn is_source(self, source_count: u32) -> bool {
186 self.esi < source_count
187 }
188
189 #[inline]
191 #[must_use]
192 pub const fn is_repair(self, source_count: u32) -> bool {
193 self.esi >= source_count
194 }
195
196 #[doc(hidden)]
198 #[inline]
199 #[must_use]
200 pub const fn new_for_test(object_value: u64, sbn: u8, esi: u32) -> Self {
201 Self {
202 object_id: ObjectId::new_for_test(object_value),
203 sbn,
204 esi,
205 }
206 }
207}
208
209impl fmt::Debug for SymbolId {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(
212 f,
213 "SymbolId({}, sbn={}, esi={})",
214 self.object_id, self.sbn, self.esi
215 )
216 }
217}
218
219impl fmt::Display for SymbolId {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 write!(f, "{}:{}:{}", self.object_id, self.sbn, self.esi)
222 }
223}
224
225#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
227pub enum SymbolKind {
228 Source,
230 Repair,
232}
233
234impl SymbolKind {
235 #[inline]
237 #[must_use]
238 pub const fn is_source(self) -> bool {
239 matches!(self, Self::Source)
240 }
241
242 #[inline]
244 #[must_use]
245 pub const fn is_repair(self) -> bool {
246 matches!(self, Self::Repair)
247 }
248}
249
250impl fmt::Display for SymbolKind {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 match self {
253 Self::Source => write!(f, "source"),
254 Self::Repair => write!(f, "repair"),
255 }
256 }
257}
258
259#[derive(Clone, PartialEq, Eq, Hash)]
270pub struct Symbol {
271 id: SymbolId,
273 kind: SymbolKind,
275 data: Vec<u8>,
277}
278
279impl Symbol {
280 #[inline]
288 #[must_use]
289 pub fn new(id: SymbolId, data: Vec<u8>, kind: SymbolKind) -> Self {
290 Self { id, kind, data }
291 }
292
293 #[inline]
295 #[must_use]
296 pub fn from_slice(id: SymbolId, data: &[u8], kind: SymbolKind) -> Self {
297 Self {
298 id,
299 kind,
300 data: data.to_vec(),
301 }
302 }
303
304 #[inline]
306 #[must_use]
307 pub fn empty(id: SymbolId, size: usize, kind: SymbolKind) -> Self {
308 Self {
309 id,
310 kind,
311 data: vec![0u8; size],
312 }
313 }
314
315 #[inline]
317 #[must_use]
318 pub const fn id(&self) -> SymbolId {
319 self.id
320 }
321
322 #[inline]
324 #[must_use]
325 pub const fn kind(&self) -> SymbolKind {
326 self.kind
327 }
328
329 #[must_use]
331 #[inline]
332 pub fn data(&self) -> &[u8] {
333 &self.data
334 }
335
336 #[inline]
338 #[must_use]
339 pub fn data_mut(&mut self) -> &mut [u8] {
340 &mut self.data
341 }
342
343 #[inline]
345 #[must_use]
346 pub fn into_data(self) -> Vec<u8> {
347 self.data
348 }
349
350 #[must_use]
352 #[inline]
353 pub fn len(&self) -> usize {
354 self.data.len()
355 }
356
357 #[must_use]
359 #[inline]
360 pub fn is_empty(&self) -> bool {
361 self.data.is_empty()
362 }
363
364 #[must_use]
366 #[inline]
367 pub const fn object_id(&self) -> ObjectId {
368 self.id.object_id()
369 }
370
371 #[inline]
373 #[must_use]
374 pub const fn sbn(&self) -> u8 {
375 self.id.sbn()
376 }
377
378 #[inline]
380 #[must_use]
381 pub const fn esi(&self) -> u32 {
382 self.id.esi()
383 }
384
385 #[doc(hidden)]
392 #[inline]
393 #[must_use]
394 pub fn new_for_test(object_value: u64, sbn: u8, esi: u32, data: &[u8]) -> Self {
395 Self::new_source_for_test(object_value, sbn, esi, data)
396 }
397
398 #[doc(hidden)]
400 #[inline]
401 #[must_use]
402 pub fn new_source_for_test(object_value: u64, sbn: u8, esi: u32, data: &[u8]) -> Self {
403 Self {
404 id: SymbolId::new_for_test(object_value, sbn, esi),
405 kind: SymbolKind::Source,
406 data: data.to_vec(),
407 }
408 }
409
410 #[doc(hidden)]
412 #[must_use]
413 #[inline]
414 pub fn new_repair_for_test(object_value: u64, sbn: u8, esi: u32, data: &[u8]) -> Self {
415 Self {
416 id: SymbolId::new_for_test(object_value, sbn, esi),
417 kind: SymbolKind::Repair,
418 data: data.to_vec(),
419 }
420 }
421}
422
423impl fmt::Debug for Symbol {
424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
425 f.debug_struct("Symbol")
426 .field("id", &self.id)
427 .field("kind", &self.kind)
428 .field("data_len", &self.data.len())
429 .finish()
430 }
431}
432
433impl fmt::Display for Symbol {
434 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
435 write!(
436 f,
437 "Symbol({}, {}, {} bytes)",
438 self.id,
439 self.kind,
440 self.data.len()
441 )
442 }
443}
444
445#[derive(Clone, Copy, Debug, PartialEq, Eq)]
450pub struct ObjectParams {
451 pub object_id: ObjectId,
453 pub object_size: u64,
455 pub symbol_size: u16,
457 pub source_blocks: u16,
459 pub symbols_per_block: u16,
461}
462
463impl ObjectParams {
464 #[must_use]
466 #[inline]
467 pub const fn new(
468 object_id: ObjectId,
469 object_size: u64,
470 symbol_size: u16,
471 source_blocks: u16,
472 symbols_per_block: u16,
473 ) -> Self {
474 Self {
475 object_id,
476 object_size,
477 symbol_size,
478 source_blocks,
479 symbols_per_block,
480 }
481 }
482
483 #[must_use]
489 #[inline]
490 pub const fn min_symbols_for_decode(&self) -> u32 {
491 self.total_source_symbols()
492 }
493
494 #[must_use]
496 pub const fn total_source_symbols(&self) -> u32 {
497 if self.symbol_size == 0 || self.object_size == 0 {
498 return 0;
499 }
500
501 let sym_size = self.symbol_size as u64;
502 let total = self.object_size.div_ceil(sym_size);
503 if total > u32::MAX as u64 {
504 u32::MAX
505 } else {
506 total as u32
507 }
508 }
509
510 #[doc(hidden)]
512 #[must_use]
513 #[inline]
514 pub const fn new_for_test(object_value: u64, size: u64) -> Self {
515 let symbol_size = DEFAULT_SYMBOL_SIZE as u64;
516 let symbols_per_block = if size == 0 {
517 0
518 } else {
519 (size - 1) / symbol_size + 1
520 };
521 Self {
522 object_id: ObjectId::new_for_test(object_value),
523 object_size: size,
524 symbol_size: DEFAULT_SYMBOL_SIZE as u16,
525 source_blocks: 1,
526 symbols_per_block: symbols_per_block as u16,
527 }
528 }
529}
530
531impl fmt::Display for ObjectParams {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 write!(
534 f,
535 "ObjectParams({}, {} bytes, {} symbols/block)",
536 self.object_id, self.object_size, self.symbols_per_block
537 )
538 }
539}
540
541#[cfg(test)]
542mod tests {
543 #![allow(
544 clippy::pedantic,
545 clippy::nursery,
546 clippy::expect_fun_call,
547 clippy::map_unwrap_or,
548 clippy::cast_possible_wrap,
549 clippy::future_not_send
550 )]
551 use super::*;
552
553 #[test]
554 fn object_id_conversions() {
555 let id = ObjectId::new(0x1234_5678_9abc_def0, 0xfed_cba9_8765_4321);
556 assert_eq!(id.high(), 0x1234_5678_9abc_def0);
557 assert_eq!(id.low(), 0xfed_cba9_8765_4321);
558
559 let from_u128 = ObjectId::from_u128(id.as_u128());
560 assert_eq!(id, from_u128);
561 }
562
563 #[test]
564 fn object_id_nil() {
565 let nil = ObjectId::NIL;
566 assert_eq!(nil.high(), 0);
567 assert_eq!(nil.low(), 0);
568 assert_eq!(nil.as_u128(), 0);
569 }
570
571 #[test]
572 fn object_id_test_constructor() {
573 let id = ObjectId::new_for_test(42);
574 assert_eq!(id.high(), 0);
575 assert_eq!(id.low(), 42);
576 }
577
578 #[test]
579 fn symbol_id_creation() {
580 let object_id = ObjectId::new_for_test(1);
581 let symbol_id = SymbolId::new(object_id, 0, 5);
582
583 assert_eq!(symbol_id.object_id(), object_id);
584 assert_eq!(symbol_id.sbn(), 0);
585 assert_eq!(symbol_id.esi(), 5);
586 }
587
588 #[test]
589 fn symbol_id_source_vs_repair() {
590 let symbol_id = SymbolId::new_for_test(1, 0, 5);
591
592 assert!(symbol_id.is_source(10));
594 assert!(!symbol_id.is_repair(10));
595
596 assert!(!symbol_id.is_source(5));
598 assert!(symbol_id.is_repair(5));
599 }
600
601 #[test]
602 fn symbol_creation_and_data() {
603 let id = SymbolId::new_for_test(1, 0, 0);
604 let data = vec![1, 2, 3, 4, 5];
605 let symbol = Symbol::new(id, data.clone(), SymbolKind::Source);
606
607 assert_eq!(symbol.id(), id);
608 assert_eq!(symbol.kind(), SymbolKind::Source);
609 assert_eq!(symbol.data(), &data[..]);
610 assert_eq!(symbol.len(), 5);
611 assert!(!symbol.is_empty());
612 }
613
614 #[test]
615 fn symbol_from_slice() {
616 let id = SymbolId::new_for_test(1, 0, 0);
617 let data = [10, 20, 30];
618 let symbol = Symbol::from_slice(id, &data, SymbolKind::Repair);
619
620 assert_eq!(symbol.data(), &data[..]);
621 assert_eq!(symbol.kind(), SymbolKind::Repair);
622 }
623
624 #[test]
625 fn symbol_empty() {
626 let id = SymbolId::new_for_test(1, 0, 0);
627 let symbol = Symbol::empty(id, 100, SymbolKind::Source);
628
629 assert_eq!(symbol.len(), 100);
630 assert!(symbol.data().iter().all(|&b| b == 0));
631 }
632
633 #[test]
634 fn symbol_into_data() {
635 let id = SymbolId::new_for_test(1, 0, 0);
636 let original_data = vec![1, 2, 3];
637 let symbol = Symbol::new(id, original_data.clone(), SymbolKind::Source);
638
639 let recovered = symbol.into_data();
640 assert_eq!(recovered, original_data);
641 }
642
643 #[test]
644 fn symbol_kind_checks() {
645 assert!(SymbolKind::Source.is_source());
646 assert!(!SymbolKind::Source.is_repair());
647 assert!(!SymbolKind::Repair.is_source());
648 assert!(SymbolKind::Repair.is_repair());
649 }
650
651 #[test]
652 fn object_params_calculations() {
653 let params = ObjectParams::new(
654 ObjectId::new_for_test(1),
655 10000, 1280, 1, 8, );
660
661 assert_eq!(params.min_symbols_for_decode(), 8);
662 assert_eq!(params.total_source_symbols(), 8);
663 }
664
665 #[test]
666 fn object_params_multi_block() {
667 let params = ObjectParams::new(
668 ObjectId::new_for_test(1),
669 327_680, 1280,
671 4, 64, );
674
675 assert_eq!(params.min_symbols_for_decode(), 256);
676 assert_eq!(params.total_source_symbols(), 256);
677 }
678
679 #[test]
680 fn object_params_can_represent_full_256_block_contract() {
681 let params = ObjectParams::new(
682 ObjectId::new_for_test(1),
683 327_680, 1280,
685 256,
686 1,
687 );
688
689 assert_eq!(params.source_blocks, 256);
690 assert_eq!(params.min_symbols_for_decode(), 256);
691 assert_eq!(params.total_source_symbols(), 256);
692 }
693
694 #[test]
695 fn object_params_partial_last_block_does_not_overcount_total_symbols() {
696 let params = ObjectParams::new(
697 ObjectId::new_for_test(1),
698 326_400, 1280,
700 4,
701 64,
702 );
703
704 assert_eq!(params.min_symbols_for_decode(), 255);
705 assert_eq!(params.total_source_symbols(), 255);
706 }
707
708 #[test]
709 fn display_formatting() {
710 let object_id = ObjectId::new(0x1234_5678_0000_0000, 0);
711 assert!(format!("{object_id}").contains("Obj-"));
712
713 let symbol_id = SymbolId::new(object_id, 1, 42);
714 let display = format!("{symbol_id}");
715 assert!(display.contains(":1:42"));
716
717 let symbol = Symbol::new_for_test(1, 0, 0, &[1, 2, 3]);
718 let display = format!("{symbol}");
719 assert!(display.contains("3 bytes"));
720 }
721
722 #[test]
727 fn object_id_ord() {
728 let a = ObjectId::new(0, 1);
729 let b = ObjectId::new(0, 2);
730 let c = ObjectId::new(1, 0);
731 assert!(a < b);
732 assert!(b < c);
733 }
734
735 #[test]
736 fn object_id_hash() {
737 use std::collections::HashSet;
738 let mut set = HashSet::new();
739 set.insert(ObjectId::new_for_test(1));
740 set.insert(ObjectId::new_for_test(2));
741 set.insert(ObjectId::new_for_test(1));
742 assert_eq!(set.len(), 2);
743 }
744
745 #[test]
746 fn symbol_id_ord_hash() {
747 use std::collections::HashSet;
748 let a = SymbolId::new_for_test(1, 0, 0);
749 let b = SymbolId::new_for_test(1, 0, 1);
750 assert!(a < b);
751 let mut set = HashSet::new();
752 set.insert(a);
753 set.insert(b);
754 set.insert(a);
755 assert_eq!(set.len(), 2);
756 }
757
758 #[test]
759 fn symbol_kind_clone_copy_hash_display() {
760 use std::collections::HashSet;
761 let src = SymbolKind::Source;
762 let rep = SymbolKind::Repair;
763 let cloned = src; assert_eq!(cloned, src);
765 assert_eq!(format!("{src}"), "source");
766 assert_eq!(format!("{rep}"), "repair");
767 let mut set = HashSet::new();
768 set.insert(src);
769 set.insert(rep);
770 assert_eq!(set.len(), 2);
771 }
772
773 #[test]
774 fn symbol_clone_hash() {
775 use std::collections::HashSet;
776 let sym = Symbol::new_for_test(1, 0, 0, &[1, 2, 3]);
777 let cloned = sym.clone();
778 assert_eq!(sym, cloned);
779 let mut set = HashSet::new();
780 set.insert(sym);
781 set.insert(cloned);
782 assert_eq!(set.len(), 1);
783 }
784
785 #[test]
786 fn symbol_data_mut() {
787 let id = SymbolId::new_for_test(1, 0, 0);
788 let mut symbol = Symbol::new(id, vec![1, 2, 3], SymbolKind::Source);
789 symbol.data_mut()[0] = 99;
790 assert_eq!(symbol.data()[0], 99);
791 }
792
793 #[test]
794 fn symbol_empty_is_empty() {
795 let id = SymbolId::new_for_test(1, 0, 0);
796 let symbol = Symbol::empty(id, 0, SymbolKind::Source);
797 assert!(symbol.is_empty());
798 assert_eq!(symbol.len(), 0);
799 }
800
801 #[test]
802 fn symbol_convenience_accessors() {
803 let sym = Symbol::new_for_test(42, 3, 7, &[10, 20]);
804 assert_eq!(sym.object_id(), ObjectId::new_for_test(42));
805 assert_eq!(sym.sbn(), 3);
806 assert_eq!(sym.esi(), 7);
807 }
808
809 #[test]
810 fn symbol_test_constructor_defaults_to_source() {
811 let sym = Symbol::new_for_test(42, 3, 7, &[10, 20]);
812 assert_eq!(sym.kind(), SymbolKind::Source);
813 }
814
815 #[test]
816 fn symbol_repair_test_constructor_preserves_repair_kind() {
817 let sym = Symbol::new_repair_for_test(42, 3, 7, &[10, 20]);
818 assert_eq!(sym.kind(), SymbolKind::Repair);
819 }
820
821 #[test]
822 fn object_params_clone_copy_display() {
823 let params = ObjectParams::new_for_test(1, 5000);
824 let cloned = params;
825 let copied = params; assert_eq!(cloned, copied);
827 let display = format!("{params}");
828 assert!(display.contains("ObjectParams"));
829 assert!(display.contains("5000"));
830 }
831
832 #[test]
833 fn debug_formatting() {
834 let object_id = ObjectId::new_for_test(42);
835 let debug = format!("{object_id:?}");
836 assert!(debug.contains("ObjectId"));
837
838 let symbol_id = SymbolId::new_for_test(1, 2, 3);
839 let debug = format!("{symbol_id:?}");
840 assert!(debug.contains("SymbolId"));
841 assert!(debug.contains("sbn=2"));
842 assert!(debug.contains("esi=3"));
843 }
844}