use alloc::{
collections::{BTreeMap, BTreeSet, LinkedList, VecDeque},
string::String,
vec::Vec,
};
#[cfg(feature = "std")]
use std::{
collections::{HashMap, HashSet},
hash::BuildHasher,
io::{Read, Write},
};
#[cfg(feature = "std")]
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use crate::{
frame::{encode_header, validate_header, HEADER_LEN},
Config, Endian, Error, IntEncoding, Result, TrailingBytes,
};
pub const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
const FRAME_MAGIC: [u8; 4] = *b"RBFP";
pub const fn hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
let mut index = 0;
while index < bytes.len() {
hash ^= bytes[index] as u64;
hash = hash.wrapping_mul(FNV_PRIME);
index += 1;
}
hash
}
pub const fn hash_u64(hash: u64, value: u64) -> u64 {
hash_bytes(hash, &value.to_le_bytes())
}
const fn tagged(tag: &str) -> u64 {
hash_bytes(FNV_OFFSET, tag.as_bytes())
}
pub trait Fingerprint {
const TYPE_FINGERPRINT: u64;
fn fingerprint(config: Config) -> u64 {
config_fingerprint(Self::TYPE_FINGERPRINT, config)
}
}
pub(crate) const fn config_fingerprint(mut hash: u64, config: Config) -> u64 {
hash = hash_u64(hash, 0x7275_7374_6269_6e01);
hash = hash_u64(
hash,
match config.endian {
Endian::Little => 1,
Endian::Big => 2,
Endian::Native if cfg!(target_endian = "little") => 3,
Endian::Native => 4,
},
);
hash = hash_u64(
hash,
match config.integers {
IntEncoding::Fixed => 1,
IntEncoding::Variable => 2,
},
);
hash = hash_u64(
hash,
match config.trailing {
TrailingBytes::Allow => 1,
TrailingBytes::Reject => 2,
},
);
hash = hash_u64(hash, option_value(config.limit));
hash_u64(hash, option_value(config.collection_limit))
}
const fn option_value(value: Option<u64>) -> u64 {
match value {
Some(value) => value ^ (1 << 63),
None => 0,
}
}
macro_rules! primitive_fingerprints {
($($ty:ty => $name:literal),+ $(,)?) => {$(
impl Fingerprint for $ty {
const TYPE_FINGERPRINT: u64 = tagged($name);
}
)+};
}
primitive_fingerprints! {
() => "unit", bool => "bool", char => "char", str => "str", String => "String",
i8 => "i8", i16 => "i16", i32 => "i32", i64 => "i64", i128 => "i128",
u8 => "u8", u16 => "u16", u32 => "u32", u64 => "u64", u128 => "u128",
f32 => "f32", f64 => "f64"
}
impl<T: Fingerprint + ?Sized> Fingerprint for &T {
const TYPE_FINGERPRINT: u64 = hash_u64(tagged("ref"), T::TYPE_FINGERPRINT);
}
macro_rules! unary_fingerprint {
($container:ident, $tag:literal) => {
impl<T: Fingerprint> Fingerprint for $container<T> {
const TYPE_FINGERPRINT: u64 = hash_u64(tagged($tag), T::TYPE_FINGERPRINT);
}
};
}
unary_fingerprint!(Option, "Option");
unary_fingerprint!(Vec, "Vec");
unary_fingerprint!(VecDeque, "VecDeque");
unary_fingerprint!(LinkedList, "LinkedList");
unary_fingerprint!(BTreeSet, "BTreeSet");
impl<T: Fingerprint, const N: usize> Fingerprint for [T; N] {
const TYPE_FINGERPRINT: u64 =
hash_u64(hash_u64(tagged("array"), T::TYPE_FINGERPRINT), N as u64);
}
impl<K: Fingerprint, V: Fingerprint> Fingerprint for BTreeMap<K, V> {
const TYPE_FINGERPRINT: u64 = hash_u64(
hash_u64(tagged("BTreeMap"), K::TYPE_FINGERPRINT),
V::TYPE_FINGERPRINT,
);
}
#[cfg(feature = "std")]
impl<T: Fingerprint, S: BuildHasher> Fingerprint for HashSet<T, S> {
const TYPE_FINGERPRINT: u64 = hash_u64(tagged("HashSet"), T::TYPE_FINGERPRINT);
}
#[cfg(feature = "std")]
impl<K: Fingerprint, V: Fingerprint, S: BuildHasher> Fingerprint for HashMap<K, V, S> {
const TYPE_FINGERPRINT: u64 = hash_u64(
hash_u64(tagged("HashMap"), K::TYPE_FINGERPRINT),
V::TYPE_FINGERPRINT,
);
}
macro_rules! tuple_fingerprint {
($($name:ident),+) => {
impl<$($name: Fingerprint),+> Fingerprint for ($($name,)+) {
const TYPE_FINGERPRINT: u64 = {
let mut hash = tagged("tuple");
$(hash = hash_u64(hash, $name::TYPE_FINGERPRINT);)+
hash
};
}
};
}
tuple_fingerprint!(A);
tuple_fingerprint!(A, B);
tuple_fingerprint!(A, B, C);
tuple_fingerprint!(A, B, C, D);
tuple_fingerprint!(A, B, C, D, E);
tuple_fingerprint!(A, B, C, D, E, F);
tuple_fingerprint!(A, B, C, D, E, F, G);
tuple_fingerprint!(A, B, C, D, E, F, G, H);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FingerprintedConfig {
config: Config,
}
impl FingerprintedConfig {
pub(crate) const fn new(config: Config) -> Self {
Self { config }
}
pub const fn payload_config(self) -> Config {
self.config
}
pub fn fingerprint<T: Fingerprint + ?Sized>(self) -> u64 {
T::fingerprint(self.config)
}
pub fn serialize<T: Serialize + Fingerprint + ?Sized>(self, value: &T) -> Result<Vec<u8>> {
let payload = self.config.serialize(value)?;
let mut output = Vec::with_capacity(HEADER_LEN.saturating_add(payload.len()));
output.extend_from_slice(&encode_header(FRAME_MAGIC, T::fingerprint(self.config)));
output.extend_from_slice(&payload);
Ok(output)
}
#[cfg(feature = "std")]
pub fn serialize_into<W: Write, T: Serialize + Fingerprint + ?Sized>(
self,
mut writer: W,
value: &T,
) -> Result<()> {
writer.write_all(&encode_header(FRAME_MAGIC, T::fingerprint(self.config)))?;
self.config.serialize_into(writer, value)
}
pub fn serialize_into_slice<T: Serialize + Fingerprint + ?Sized>(
self,
output: &mut [u8],
value: &T,
) -> Result<usize> {
if output.len() < HEADER_LEN {
let payload = self.config.serialized_size(value)?;
let payload =
usize::try_from(payload).map_err(|_| Error::IntegerOverflow { target: "usize" })?;
return Err(Error::BufferTooSmall {
required: HEADER_LEN.saturating_add(payload),
available: output.len(),
});
}
output[..HEADER_LEN]
.copy_from_slice(&encode_header(FRAME_MAGIC, T::fingerprint(self.config)));
match self
.config
.serialize_into_slice(&mut output[HEADER_LEN..], value)
{
Ok(written) => Ok(HEADER_LEN + written),
Err(Error::BufferTooSmall {
required,
available: _,
}) => Err(Error::BufferTooSmall {
required: HEADER_LEN.saturating_add(required),
available: output.len(),
}),
Err(error) => Err(error),
}
}
pub fn deserialize<'de, T: Deserialize<'de> + Fingerprint>(
self,
input: &'de [u8],
) -> Result<T> {
let payload = validate_header(FRAME_MAGIC, input, T::fingerprint(self.config))?;
self.config.deserialize(payload)
}
#[cfg(feature = "std")]
pub fn deserialize_from<R: Read, T: DeserializeOwned + Fingerprint>(
self,
mut reader: R,
) -> Result<T> {
let mut frame_header = [0; HEADER_LEN];
reader.read_exact(&mut frame_header)?;
validate_header(FRAME_MAGIC, &frame_header, T::fingerprint(self.config))?;
self.config.deserialize_from(reader)
}
pub fn serialized_size<T: Serialize + Fingerprint + ?Sized>(self, value: &T) -> Result<u64> {
self.config
.serialized_size(value)?
.checked_add(HEADER_LEN as u64)
.ok_or(Error::SizeLimit { limit: u64::MAX })
}
}