stable_hash/
utils.rs

1use crate::prelude::*;
2use crate::verification::*;
3
4/// Treat some &[u8] as a sequence of bytes, rather than a sequence of numbers.
5/// Using this can result in a significant performance gain but does not support
6/// the backward compatible change to different int types as numbers do by default
7pub struct AsBytes<'a>(pub &'a [u8]);
8
9impl StableHash for AsBytes<'_> {
10    fn stable_hash<H: StableHasher>(&self, field_address: H::Addr, state: &mut H) {
11        profile_method!(stable_hash);
12
13        if !self.0.is_empty() {
14            state.write(field_address, self.0)
15        }
16    }
17}
18
19fn trim_zeros(bytes: &[u8]) -> &[u8] {
20    profile_fn!(trim_zeros);
21
22    let mut end = bytes.len();
23    while end != 0 && bytes[end - 1] == 0 {
24        end -= 1;
25    }
26    &bytes[0..end]
27}
28
29/// Canonical way to write an integer of any size.
30///
31/// Backward compatibility:
32/// * The value +0 never writes bytes to the stream.
33/// * Integers of any size (u8..u24..u128...uN) are written in a canonical form, and can be written in any order.
34pub struct AsInt<'a> {
35    pub is_negative: bool,
36    pub little_endian: &'a [u8],
37}
38
39impl StableHash for AsInt<'_> {
40    fn stable_hash<H: StableHasher>(&self, field_address: H::Addr, state: &mut H) {
41        profile_method!(stable_hash);
42
43        // Having the negative sign be a child makes it possible to change the schema
44        // from u32 to i64 in a backward compatible way.
45        // This is also allowing for negative 0, like float, which is not used by
46        // any standard impl but may be used by some types.
47        if self.is_negative {
48            state.write(field_address.child(0), &[]);
49        }
50        let canon = trim_zeros(self.little_endian);
51        if !canon.is_empty() {
52            state.write(field_address, canon);
53        }
54    }
55}
56
57pub(crate) fn generic_stable_hash<T: StableHash, H: StableHasher>(value: &T) -> H::Out {
58    let mut hasher = H::new();
59    value.stable_hash(FieldAddress::root(), &mut hasher);
60    hasher.finish()
61}
62
63// TODO: Create unit tests where this should fail
64pub fn check_for_child_errors<T: StableHash>(value: &T) -> Result<(), (ChildErr, Vec<PathItem>)> {
65    profile_fn!(check_for_child_errors);
66    generic_stable_hash::<T, crate::verification::ChildChecker>(value)
67}