Skip to main content

bincode_next/
fingerprint.rs

1//! Fingerprinting implementation for bincode.
2//!
3//! This module provides the [`Fingerprint`] trait and derive macro to enable
4//! schema verification during encoding and decoding. The fingerprint is a 64-bit
5//! hash that validates that the encoder and decoder are using the same schema
6//! and configuration.
7//!
8//! The fingerprint covers:
9//! - Type names
10//! - Struct field names, types, and their order
11//! - Enum variant names, field names, types, and their order
12//! - Relevant configuration settings (endianness, integer encoding, etc.)
13
14use crate::config::Config;
15#[cfg(feature = "alloc")]
16use alloc::borrow::ToOwned;
17
18/// Fingerprint trait for types that can be hashed for schema verification.
19///
20/// The fingerprint covers:
21/// - The type name
22/// - For structs: field names, types, and their order
23/// - For enums: variant names, field names, types, and their order
24/// - Relevant configuration settings (endianness, integer encoding, etc.)
25pub trait Fingerprint<C: Config> {
26    /// Unique 64-bit compile-time hash of the type's schema AND the Configuration used.
27    const SCHEMA_HASH: u64;
28}
29
30macro_rules! impl_fingerprint {
31    ($($t:ty => $name:expr),*) => {
32        $(
33            impl<C: Config> Fingerprint<C> for $t {
34                const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded($name, &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH));
35            }
36        )*
37    };
38}
39
40impl_fingerprint! {
41    u8 => b"u8",
42    u16 => b"u16",
43    u32 => b"u32",
44    u64 => b"u64",
45    u128 => b"u128",
46    usize => b"usize",
47    i8 => b"i8",
48    i16 => b"i16",
49    i32 => b"i32",
50    i64 => b"i64",
51    i128 => b"i128",
52    isize => b"isize",
53    bool => b"bool",
54    f32 => b"f32",
55    f64 => b"f64",
56    char => b"char",
57    str => b"str"
58}
59
60impl<C: Config, T: Fingerprint<C>, const N: usize> Fingerprint<C> for [T; N] {
61    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
62        &N.to_le_bytes(),
63        &rapidhash::v3::RapidSecrets::seed_cpp(T::SCHEMA_HASH),
64    );
65}
66
67#[cfg(feature = "alloc")]
68impl<C: Config, T: Fingerprint<C>> Fingerprint<C> for alloc::vec::Vec<T> {
69    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
70        b"Vec",
71        &rapidhash::v3::RapidSecrets::seed_cpp(T::SCHEMA_HASH),
72    );
73}
74
75#[cfg(feature = "alloc")]
76impl<C: Config> Fingerprint<C> for alloc::string::String {
77    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
78        b"str",
79        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
80    );
81}
82
83impl<C: Config> Fingerprint<C> for () {
84    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
85        b"()",
86        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
87    );
88}
89
90impl<C: Config, T1: Fingerprint<C>> Fingerprint<C> for (T1,) {
91    const SCHEMA_HASH: u64 = T1::SCHEMA_HASH;
92}
93
94impl<C: Config, T1: Fingerprint<C>, T2: Fingerprint<C>> Fingerprint<C> for (T1, T2) {
95    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
96        &T2::SCHEMA_HASH.to_le_bytes(),
97        &rapidhash::v3::RapidSecrets::seed_cpp(T1::SCHEMA_HASH),
98    );
99}
100
101impl<C: Config, T: Fingerprint<C>> Fingerprint<C> for Option<T> {
102    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
103        b"Option",
104        &rapidhash::v3::RapidSecrets::seed_cpp(T::SCHEMA_HASH),
105    );
106}
107
108impl<C: Config, T: Fingerprint<C>, E: Fingerprint<C>> Fingerprint<C> for Result<T, E> {
109    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
110        &E::SCHEMA_HASH.to_le_bytes(),
111        &rapidhash::v3::RapidSecrets::seed_cpp(rapidhash::v3::rapidhash_v3_seeded(
112            b"Result",
113            &rapidhash::v3::RapidSecrets::seed_cpp(T::SCHEMA_HASH),
114        )),
115    );
116}
117
118impl<C: Config, T: ?Sized> Fingerprint<C> for core::marker::PhantomData<T> {
119    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
120        b"PhantomData",
121        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
122    );
123}
124
125impl<C: Config, T: ?Sized + Fingerprint<C>> Fingerprint<C> for &T {
126    const SCHEMA_HASH: u64 = T::SCHEMA_HASH;
127}
128
129impl<C: Config, T: ?Sized + Fingerprint<C>> Fingerprint<C> for &mut T {
130    const SCHEMA_HASH: u64 = T::SCHEMA_HASH;
131}
132
133#[cfg(feature = "alloc")]
134impl<C: Config, T: ?Sized + Fingerprint<C>> Fingerprint<C> for alloc::boxed::Box<T> {
135    const SCHEMA_HASH: u64 = T::SCHEMA_HASH;
136}
137
138#[cfg(feature = "alloc")]
139impl<C: Config, T: Fingerprint<C> + ToOwned + ?Sized> Fingerprint<C> for alloc::borrow::Cow<'_, T> {
140    const SCHEMA_HASH: u64 = T::SCHEMA_HASH;
141}
142
143#[cfg(feature = "alloc")]
144impl<C: Config, T: ?Sized + Fingerprint<C>> Fingerprint<C> for alloc::rc::Rc<T> {
145    const SCHEMA_HASH: u64 = T::SCHEMA_HASH;
146}
147
148#[cfg(feature = "alloc")]
149impl<C: Config, T: ?Sized + Fingerprint<C>> Fingerprint<C> for alloc::sync::Arc<T> {
150    const SCHEMA_HASH: u64 = T::SCHEMA_HASH;
151}
152
153#[cfg(feature = "alloc")]
154impl<C: Config, T: Fingerprint<C>> Fingerprint<C> for alloc::collections::VecDeque<T> {
155    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
156        b"VecDeque",
157        &rapidhash::v3::RapidSecrets::seed_cpp(T::SCHEMA_HASH),
158    );
159}
160
161#[cfg(feature = "alloc")]
162impl<C: Config, T: Fingerprint<C>> Fingerprint<C> for alloc::collections::BinaryHeap<T> {
163    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
164        b"BinaryHeap",
165        &rapidhash::v3::RapidSecrets::seed_cpp(T::SCHEMA_HASH),
166    );
167}
168
169#[cfg(feature = "alloc")]
170impl<C: Config, K: Fingerprint<C>, V: Fingerprint<C>> Fingerprint<C>
171    for alloc::collections::BTreeMap<K, V>
172{
173    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
174        b"BTreeMap",
175        &rapidhash::v3::RapidSecrets::seed_cpp(rapidhash::v3::rapidhash_v3_seeded(
176            &V::SCHEMA_HASH.to_le_bytes(),
177            &rapidhash::v3::RapidSecrets::seed_cpp(K::SCHEMA_HASH),
178        )),
179    );
180}
181
182#[cfg(feature = "alloc")]
183impl<C: Config, T: Fingerprint<C>> Fingerprint<C> for alloc::collections::BTreeSet<T> {
184    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
185        b"BTreeSet",
186        &rapidhash::v3::RapidSecrets::seed_cpp(T::SCHEMA_HASH),
187    );
188}
189
190#[cfg(feature = "std")]
191impl<C: Config, K: Fingerprint<C>, V: Fingerprint<C>, S> Fingerprint<C>
192    for std::collections::HashMap<K, V, S>
193{
194    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
195        b"HashMap",
196        &rapidhash::v3::RapidSecrets::seed_cpp(rapidhash::v3::rapidhash_v3_seeded(
197            &V::SCHEMA_HASH.to_le_bytes(),
198            &rapidhash::v3::RapidSecrets::seed_cpp(K::SCHEMA_HASH),
199        )),
200    );
201}
202
203#[cfg(feature = "std")]
204impl<C: Config, T: Fingerprint<C>, S> Fingerprint<C> for std::collections::HashSet<T, S> {
205    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
206        b"HashSet",
207        &rapidhash::v3::RapidSecrets::seed_cpp(T::SCHEMA_HASH),
208    );
209}
210
211impl<C: Config> Fingerprint<C> for core::time::Duration {
212    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
213        b"Duration",
214        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
215    );
216}
217
218#[cfg(feature = "std")]
219impl<C: Config> Fingerprint<C> for std::time::SystemTime {
220    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
221        b"SystemTime",
222        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
223    );
224}
225
226#[cfg(feature = "std")]
227impl<C: Config> Fingerprint<C> for std::net::IpAddr {
228    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
229        b"IpAddr",
230        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
231    );
232}
233
234#[cfg(feature = "std")]
235impl<C: Config> Fingerprint<C> for std::net::Ipv4Addr {
236    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
237        b"Ipv4Addr",
238        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
239    );
240}
241
242#[cfg(feature = "std")]
243impl<C: Config> Fingerprint<C> for std::net::Ipv6Addr {
244    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
245        b"Ipv6Addr",
246        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
247    );
248}
249
250#[cfg(feature = "std")]
251impl<C: Config> Fingerprint<C> for std::net::SocketAddr {
252    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
253        b"SocketAddr",
254        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
255    );
256}
257
258#[cfg(feature = "std")]
259impl<C: Config> Fingerprint<C> for std::net::SocketAddrV4 {
260    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
261        b"SocketAddrV4",
262        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
263    );
264}
265
266#[cfg(feature = "std")]
267impl<C: Config> Fingerprint<C> for std::net::SocketAddrV6 {
268    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
269        b"SocketAddrV6",
270        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
271    );
272}
273
274#[cfg(feature = "std")]
275impl<C: Config> Fingerprint<C> for std::path::Path {
276    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
277        b"Path",
278        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
279    );
280}
281
282#[cfg(feature = "std")]
283impl<C: Config> Fingerprint<C> for std::path::PathBuf {
284    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
285        b"PathBuf",
286        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
287    );
288}
289
290#[cfg(feature = "std")]
291impl<C: Config> Fingerprint<C> for std::ffi::CStr {
292    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
293        b"CStr",
294        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
295    );
296}
297
298#[cfg(feature = "std")]
299impl<C: Config> Fingerprint<C> for std::ffi::CString {
300    const SCHEMA_HASH: u64 = rapidhash::v3::rapidhash_v3_seeded(
301        b"CString",
302        &rapidhash::v3::RapidSecrets::seed_cpp(C::CONFIG_HASH),
303    );
304}
305
306#[cfg(feature = "std")]
307impl<C: Config, T: Fingerprint<C>> Fingerprint<C> for std::sync::Mutex<T> {
308    const SCHEMA_HASH: u64 = T::SCHEMA_HASH;
309}
310
311#[cfg(feature = "std")]
312impl<C: Config, T: Fingerprint<C>> Fingerprint<C> for std::sync::RwLock<T> {
313    const SCHEMA_HASH: u64 = T::SCHEMA_HASH;
314}
315
316macro_rules! impl_tuple {
317    ($($t:ident),*) => {
318        impl<C: Config, $($t: Fingerprint<C>),*> Fingerprint<C> for ($($t,)*) {
319            const SCHEMA_HASH: u64 = {
320                let mut hash = C::CONFIG_HASH;
321                $(
322                    hash = rapidhash::v3::rapidhash_v3_seeded(&$t::SCHEMA_HASH.to_le_bytes(), &rapidhash::v3::RapidSecrets::seed_cpp(hash));
323                )*
324                hash
325            };
326        }
327    };
328}
329
330impl_tuple!(T1, T2, T3);
331impl_tuple!(T1, T2, T3, T4);
332impl_tuple!(T1, T2, T3, T4, T5);
333impl_tuple!(T1, T2, T3, T4, T5, T6);
334impl_tuple!(T1, T2, T3, T4, T5, T6, T7);
335impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);