use core::fmt;
pub const DEFAULT_SYMBOL_SIZE: usize = 1280;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ObjectId {
high: u64,
low: u64,
}
impl ObjectId {
#[inline]
#[must_use]
pub const fn new(high: u64, low: u64) -> Self {
Self { high, low }
}
#[inline]
#[must_use]
pub const fn from_u128(value: u128) -> Self {
Self {
high: (value >> 64) as u64,
low: value as u64,
}
}
#[inline]
#[must_use]
pub const fn as_u128(self) -> u128 {
((self.high as u128) << 64) | (self.low as u128)
}
#[inline]
#[must_use]
pub const fn high(self) -> u64 {
self.high
}
#[inline]
#[must_use]
pub const fn low(self) -> u64 {
self.low
}
#[must_use]
pub fn new_random(rng: &mut crate::util::DetRng) -> Self {
Self {
high: rng.next_u64(),
low: rng.next_u64(),
}
}
#[doc(hidden)]
#[inline]
#[must_use]
pub const fn new_for_test(value: u64) -> Self {
Self {
high: 0,
low: value,
}
}
pub const NIL: Self = Self { high: 0, low: 0 };
}
impl fmt::Debug for ObjectId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ObjectId({:016x}{:016x})", self.high, self.low)
}
}
impl fmt::Display for ObjectId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Obj-{:08x}", (self.high >> 32) as u32)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SymbolId {
object_id: ObjectId,
sbn: u8,
esi: u32,
}
impl SymbolId {
#[inline]
#[must_use]
pub const fn new(object_id: ObjectId, sbn: u8, esi: u32) -> Self {
Self {
object_id,
sbn,
esi,
}
}
#[inline]
#[must_use]
pub const fn object_id(self) -> ObjectId {
self.object_id
}
#[inline]
#[must_use]
pub const fn sbn(self) -> u8 {
self.sbn
}
#[inline]
#[must_use]
pub const fn esi(self) -> u32 {
self.esi
}
#[inline]
#[must_use]
pub const fn is_source(self, source_count: u32) -> bool {
self.esi < source_count
}
#[inline]
#[must_use]
pub const fn is_repair(self, source_count: u32) -> bool {
self.esi >= source_count
}
#[doc(hidden)]
#[inline]
#[must_use]
pub const fn new_for_test(object_value: u64, sbn: u8, esi: u32) -> Self {
Self {
object_id: ObjectId::new_for_test(object_value),
sbn,
esi,
}
}
}
impl fmt::Debug for SymbolId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SymbolId({}, sbn={}, esi={})",
self.object_id, self.sbn, self.esi
)
}
}
impl fmt::Display for SymbolId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}:{}", self.object_id, self.sbn, self.esi)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SymbolKind {
Source,
Repair,
}
impl SymbolKind {
#[inline]
#[must_use]
pub const fn is_source(self) -> bool {
matches!(self, Self::Source)
}
#[inline]
#[must_use]
pub const fn is_repair(self) -> bool {
matches!(self, Self::Repair)
}
}
impl fmt::Display for SymbolKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Source => write!(f, "source"),
Self::Repair => write!(f, "repair"),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Symbol {
id: SymbolId,
kind: SymbolKind,
data: Vec<u8>,
}
impl Symbol {
#[inline]
#[must_use]
pub fn new(id: SymbolId, data: Vec<u8>, kind: SymbolKind) -> Self {
Self { id, kind, data }
}
#[inline]
#[must_use]
pub fn from_slice(id: SymbolId, data: &[u8], kind: SymbolKind) -> Self {
Self {
id,
kind,
data: data.to_vec(),
}
}
#[inline]
#[must_use]
pub fn empty(id: SymbolId, size: usize, kind: SymbolKind) -> Self {
Self {
id,
kind,
data: vec![0u8; size],
}
}
#[inline]
#[must_use]
pub const fn id(&self) -> SymbolId {
self.id
}
#[inline]
#[must_use]
pub const fn kind(&self) -> SymbolKind {
self.kind
}
#[must_use]
#[inline]
pub fn data(&self) -> &[u8] {
&self.data
}
#[inline]
#[must_use]
pub fn data_mut(&mut self) -> &mut [u8] {
&mut self.data
}
#[inline]
#[must_use]
pub fn into_data(self) -> Vec<u8> {
self.data
}
#[must_use]
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[must_use]
#[inline]
pub const fn object_id(&self) -> ObjectId {
self.id.object_id()
}
#[inline]
#[must_use]
pub const fn sbn(&self) -> u8 {
self.id.sbn()
}
#[inline]
#[must_use]
pub const fn esi(&self) -> u32 {
self.id.esi()
}
#[doc(hidden)]
#[inline]
#[must_use]
pub fn new_for_test(object_value: u64, sbn: u8, esi: u32, data: &[u8]) -> Self {
Self::new_source_for_test(object_value, sbn, esi, data)
}
#[doc(hidden)]
#[inline]
#[must_use]
pub fn new_source_for_test(object_value: u64, sbn: u8, esi: u32, data: &[u8]) -> Self {
Self {
id: SymbolId::new_for_test(object_value, sbn, esi),
kind: SymbolKind::Source,
data: data.to_vec(),
}
}
#[doc(hidden)]
#[must_use]
#[inline]
pub fn new_repair_for_test(object_value: u64, sbn: u8, esi: u32, data: &[u8]) -> Self {
Self {
id: SymbolId::new_for_test(object_value, sbn, esi),
kind: SymbolKind::Repair,
data: data.to_vec(),
}
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Symbol")
.field("id", &self.id)
.field("kind", &self.kind)
.field("data_len", &self.data.len())
.finish()
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Symbol({}, {}, {} bytes)",
self.id,
self.kind,
self.data.len()
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ObjectParams {
pub object_id: ObjectId,
pub object_size: u64,
pub symbol_size: u16,
pub source_blocks: u16,
pub symbols_per_block: u16,
}
impl ObjectParams {
#[must_use]
#[inline]
pub const fn new(
object_id: ObjectId,
object_size: u64,
symbol_size: u16,
source_blocks: u16,
symbols_per_block: u16,
) -> Self {
Self {
object_id,
object_size,
symbol_size,
source_blocks,
symbols_per_block,
}
}
#[must_use]
#[inline]
pub const fn min_symbols_for_decode(&self) -> u32 {
self.total_source_symbols()
}
#[must_use]
pub const fn total_source_symbols(&self) -> u32 {
if self.symbol_size == 0 || self.object_size == 0 {
return 0;
}
let sym_size = self.symbol_size as u64;
let total = self.object_size.div_ceil(sym_size);
if total > u32::MAX as u64 {
u32::MAX
} else {
total as u32
}
}
#[doc(hidden)]
#[must_use]
#[inline]
pub const fn new_for_test(object_value: u64, size: u64) -> Self {
let symbol_size = DEFAULT_SYMBOL_SIZE as u64;
let symbols_per_block = if size == 0 {
0
} else {
(size - 1) / symbol_size + 1
};
Self {
object_id: ObjectId::new_for_test(object_value),
object_size: size,
symbol_size: DEFAULT_SYMBOL_SIZE as u16,
source_blocks: 1,
symbols_per_block: symbols_per_block as u16,
}
}
}
impl fmt::Display for ObjectParams {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ObjectParams({}, {} bytes, {} symbols/block)",
self.object_id, self.object_size, self.symbols_per_block
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn object_id_conversions() {
let id = ObjectId::new(0x1234_5678_9abc_def0, 0xfed_cba9_8765_4321);
assert_eq!(id.high(), 0x1234_5678_9abc_def0);
assert_eq!(id.low(), 0xfed_cba9_8765_4321);
let from_u128 = ObjectId::from_u128(id.as_u128());
assert_eq!(id, from_u128);
}
#[test]
fn object_id_nil() {
let nil = ObjectId::NIL;
assert_eq!(nil.high(), 0);
assert_eq!(nil.low(), 0);
assert_eq!(nil.as_u128(), 0);
}
#[test]
fn object_id_test_constructor() {
let id = ObjectId::new_for_test(42);
assert_eq!(id.high(), 0);
assert_eq!(id.low(), 42);
}
#[test]
fn symbol_id_creation() {
let object_id = ObjectId::new_for_test(1);
let symbol_id = SymbolId::new(object_id, 0, 5);
assert_eq!(symbol_id.object_id(), object_id);
assert_eq!(symbol_id.sbn(), 0);
assert_eq!(symbol_id.esi(), 5);
}
#[test]
fn symbol_id_source_vs_repair() {
let symbol_id = SymbolId::new_for_test(1, 0, 5);
assert!(symbol_id.is_source(10));
assert!(!symbol_id.is_repair(10));
assert!(!symbol_id.is_source(5));
assert!(symbol_id.is_repair(5));
}
#[test]
fn symbol_creation_and_data() {
let id = SymbolId::new_for_test(1, 0, 0);
let data = vec![1, 2, 3, 4, 5];
let symbol = Symbol::new(id, data.clone(), SymbolKind::Source);
assert_eq!(symbol.id(), id);
assert_eq!(symbol.kind(), SymbolKind::Source);
assert_eq!(symbol.data(), &data[..]);
assert_eq!(symbol.len(), 5);
assert!(!symbol.is_empty());
}
#[test]
fn symbol_from_slice() {
let id = SymbolId::new_for_test(1, 0, 0);
let data = [10, 20, 30];
let symbol = Symbol::from_slice(id, &data, SymbolKind::Repair);
assert_eq!(symbol.data(), &data[..]);
assert_eq!(symbol.kind(), SymbolKind::Repair);
}
#[test]
fn symbol_empty() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::empty(id, 100, SymbolKind::Source);
assert_eq!(symbol.len(), 100);
assert!(symbol.data().iter().all(|&b| b == 0));
}
#[test]
fn symbol_into_data() {
let id = SymbolId::new_for_test(1, 0, 0);
let original_data = vec![1, 2, 3];
let symbol = Symbol::new(id, original_data.clone(), SymbolKind::Source);
let recovered = symbol.into_data();
assert_eq!(recovered, original_data);
}
#[test]
fn symbol_kind_checks() {
assert!(SymbolKind::Source.is_source());
assert!(!SymbolKind::Source.is_repair());
assert!(!SymbolKind::Repair.is_source());
assert!(SymbolKind::Repair.is_repair());
}
#[test]
fn object_params_calculations() {
let params = ObjectParams::new(
ObjectId::new_for_test(1),
10000, 1280, 1, 8, );
assert_eq!(params.min_symbols_for_decode(), 8);
assert_eq!(params.total_source_symbols(), 8);
}
#[test]
fn object_params_multi_block() {
let params = ObjectParams::new(
ObjectId::new_for_test(1),
327_680, 1280,
4, 64, );
assert_eq!(params.min_symbols_for_decode(), 256);
assert_eq!(params.total_source_symbols(), 256);
}
#[test]
fn object_params_can_represent_full_256_block_contract() {
let params = ObjectParams::new(
ObjectId::new_for_test(1),
327_680, 1280,
256,
1,
);
assert_eq!(params.source_blocks, 256);
assert_eq!(params.min_symbols_for_decode(), 256);
assert_eq!(params.total_source_symbols(), 256);
}
#[test]
fn object_params_partial_last_block_does_not_overcount_total_symbols() {
let params = ObjectParams::new(
ObjectId::new_for_test(1),
326_400, 1280,
4,
64,
);
assert_eq!(params.min_symbols_for_decode(), 255);
assert_eq!(params.total_source_symbols(), 255);
}
#[test]
fn display_formatting() {
let object_id = ObjectId::new(0x1234_5678_0000_0000, 0);
assert!(format!("{object_id}").contains("Obj-"));
let symbol_id = SymbolId::new(object_id, 1, 42);
let display = format!("{symbol_id}");
assert!(display.contains(":1:42"));
let symbol = Symbol::new_for_test(1, 0, 0, &[1, 2, 3]);
let display = format!("{symbol}");
assert!(display.contains("3 bytes"));
}
#[test]
fn object_id_ord() {
let a = ObjectId::new(0, 1);
let b = ObjectId::new(0, 2);
let c = ObjectId::new(1, 0);
assert!(a < b);
assert!(b < c);
}
#[test]
fn object_id_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(ObjectId::new_for_test(1));
set.insert(ObjectId::new_for_test(2));
set.insert(ObjectId::new_for_test(1));
assert_eq!(set.len(), 2);
}
#[test]
fn symbol_id_ord_hash() {
use std::collections::HashSet;
let a = SymbolId::new_for_test(1, 0, 0);
let b = SymbolId::new_for_test(1, 0, 1);
assert!(a < b);
let mut set = HashSet::new();
set.insert(a);
set.insert(b);
set.insert(a);
assert_eq!(set.len(), 2);
}
#[test]
fn symbol_kind_clone_copy_hash_display() {
use std::collections::HashSet;
let src = SymbolKind::Source;
let rep = SymbolKind::Repair;
let cloned = src; assert_eq!(cloned, src);
assert_eq!(format!("{src}"), "source");
assert_eq!(format!("{rep}"), "repair");
let mut set = HashSet::new();
set.insert(src);
set.insert(rep);
assert_eq!(set.len(), 2);
}
#[test]
fn symbol_clone_hash() {
use std::collections::HashSet;
let sym = Symbol::new_for_test(1, 0, 0, &[1, 2, 3]);
let cloned = sym.clone();
assert_eq!(sym, cloned);
let mut set = HashSet::new();
set.insert(sym);
set.insert(cloned);
assert_eq!(set.len(), 1);
}
#[test]
fn symbol_data_mut() {
let id = SymbolId::new_for_test(1, 0, 0);
let mut symbol = Symbol::new(id, vec![1, 2, 3], SymbolKind::Source);
symbol.data_mut()[0] = 99;
assert_eq!(symbol.data()[0], 99);
}
#[test]
fn symbol_empty_is_empty() {
let id = SymbolId::new_for_test(1, 0, 0);
let symbol = Symbol::empty(id, 0, SymbolKind::Source);
assert!(symbol.is_empty());
assert_eq!(symbol.len(), 0);
}
#[test]
fn symbol_convenience_accessors() {
let sym = Symbol::new_for_test(42, 3, 7, &[10, 20]);
assert_eq!(sym.object_id(), ObjectId::new_for_test(42));
assert_eq!(sym.sbn(), 3);
assert_eq!(sym.esi(), 7);
}
#[test]
fn symbol_test_constructor_defaults_to_source() {
let sym = Symbol::new_for_test(42, 3, 7, &[10, 20]);
assert_eq!(sym.kind(), SymbolKind::Source);
}
#[test]
fn symbol_repair_test_constructor_preserves_repair_kind() {
let sym = Symbol::new_repair_for_test(42, 3, 7, &[10, 20]);
assert_eq!(sym.kind(), SymbolKind::Repair);
}
#[test]
fn object_params_clone_copy_display() {
let params = ObjectParams::new_for_test(1, 5000);
let cloned = params;
let copied = params; assert_eq!(cloned, copied);
let display = format!("{params}");
assert!(display.contains("ObjectParams"));
assert!(display.contains("5000"));
}
#[test]
fn debug_formatting() {
let object_id = ObjectId::new_for_test(42);
let debug = format!("{object_id:?}");
assert!(debug.contains("ObjectId"));
let symbol_id = SymbolId::new_for_test(1, 2, 3);
let debug = format!("{symbol_id:?}");
assert!(debug.contains("SymbolId"));
assert!(debug.contains("sbn=2"));
assert!(debug.contains("esi=3"));
}
}