mod ser;
pub use ser::{DumpError, to_dump};
use crate::dump::StateDump;
use crate::probe::DeterminismProbe;
use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HashAlgo {
#[default]
Xxh3,
#[cfg(feature = "blake3")]
Blake3,
}
impl HashAlgo {
pub fn id(self) -> u16 {
match self {
Self::Xxh3 => 1,
#[cfg(feature = "blake3")]
Self::Blake3 => 2,
}
}
pub fn hash(self, bytes: &[u8]) -> u64 {
match self {
Self::Xxh3 => xxhash_rust::xxh3::xxh3_64(bytes),
#[cfg(feature = "blake3")]
Self::Blake3 => {
let digest = blake3::hash(bytes);
let mut first = [0u8; 8];
first.copy_from_slice(&digest.as_bytes()[..8]);
u64::from_le_bytes(first)
}
}
}
}
pub fn format_id(label: &str) -> u64 {
let mut digest = crate::format::wire::Fnv1a::new();
digest.update(label.as_bytes());
digest.value()
}
pub fn to_bytes<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>, postcard::Error> {
postcard::to_allocvec(value)
}
pub struct SerdeProbe<'a, S: Serialize + ?Sized, L: Serialize + ?Sized = S> {
state: &'a S,
light: Option<&'a L>,
algo: HashAlgo,
}
impl<'a, S: Serialize + ?Sized> SerdeProbe<'a, S, S> {
pub fn new(state: &'a S) -> Self {
Self {
state,
light: None,
algo: HashAlgo::default(),
}
}
}
impl<'a, S: Serialize + ?Sized, L: Serialize + ?Sized> SerdeProbe<'a, S, L> {
pub fn with_light(state: &'a S, light: &'a L) -> Self {
Self {
state,
light: Some(light),
algo: HashAlgo::default(),
}
}
pub fn with_algo(mut self, algo: HashAlgo) -> Self {
self.algo = algo;
self
}
pub fn hash_algo_id(&self) -> u16 {
self.algo.id()
}
fn hash_of<T: Serialize + ?Sized>(&self, value: &T) -> u64 {
to_bytes(value)
.map(|bytes| self.algo.hash(&bytes))
.unwrap_or(0)
}
}
impl<S: Serialize + ?Sized, L: Serialize + ?Sized> DeterminismProbe for SerdeProbe<'_, S, L> {
fn light_hash(&self) -> u64 {
match self.light {
Some(light) => self.hash_of(light),
None => self.hash_of(self.state),
}
}
fn full_hash(&self) -> u64 {
self.hash_of(self.state)
}
fn state_dump(&self) -> StateDump {
match to_dump(self.state) {
Ok(dump) => dump,
Err(err) => {
let mut dump = StateDump::empty();
dump.insert("$error", err.to_string());
dump
}
}
}
}