alloy_primitives/utils/
keccak_cache.rs1use super::{hint::unlikely, keccak256_impl as keccak256};
6use crate::{B256, KECCAK256_EMPTY};
7use std::mem::MaybeUninit;
8
9pub(super) const MAX_INPUT_LEN: usize =
11 128 - size_of::<B256>() - size_of::<u8>() - size_of::<usize>();
12
13const COUNT: usize = 1 << 17; static CACHE: fixed_cache::Cache<Key, B256, BuildHasher> =
15 fixed_cache::static_cache!(Key, B256, COUNT, BuildHasher::new());
16
17pub(super) fn compute(input: &[u8]) -> B256 {
18 if unlikely(input.is_empty() | (input.len() > MAX_INPUT_LEN)) {
19 return if input.is_empty() { KECCAK256_EMPTY } else { keccak256(input) };
20 }
21
22 CACHE.get_or_insert_with_ref(input, keccak256, |input| {
23 let mut data = [MaybeUninit::uninit(); MAX_INPUT_LEN];
24 unsafe {
25 std::ptr::copy_nonoverlapping(input.as_ptr(), data.as_mut_ptr().cast(), input.len())
26 };
27 Key { len: input.len() as u8, data }
28 })
29}
30
31type BuildHasher = std::hash::BuildHasherDefault<Hasher>;
32#[derive(Default)]
33struct Hasher(u64);
34
35impl std::hash::Hasher for Hasher {
36 #[inline]
37 fn finish(&self) -> u64 {
38 self.0
39 }
40
41 #[inline]
42 fn write(&mut self, bytes: &[u8]) {
43 unsafe { core::hint::assert_unchecked(bytes.len() <= MAX_INPUT_LEN) };
49 if bytes.len() <= 16 {
50 super::hint::cold_path();
51 }
52 self.0 = rapidhash::v3::rapidhash_v3_micro_inline::<false, false>(
53 bytes,
54 const { &rapidhash::v3::RapidSecrets::seed(0) },
55 );
56 }
57}
58
59#[derive(Clone, Copy)]
60struct Key {
61 len: u8,
62 data: [MaybeUninit<u8>; MAX_INPUT_LEN],
63}
64
65impl PartialEq for Key {
66 #[inline]
67 fn eq(&self, other: &Self) -> bool {
68 self.len == other.len && self.get() == other.get()
69 }
70}
71impl Eq for Key {}
72
73impl std::borrow::Borrow<[u8]> for Key {
74 #[inline]
75 fn borrow(&self) -> &[u8] {
76 self.get()
77 }
78}
79
80impl std::hash::Hash for Key {
81 #[inline]
82 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
83 state.write(self.get());
84 }
85}
86
87impl Key {
88 #[inline]
89 const fn get(&self) -> &[u8] {
90 unsafe { std::slice::from_raw_parts(self.data.as_ptr().cast(), self.len as usize) }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn test_sizes() {
100 assert_eq!(size_of::<Key>(), MAX_INPUT_LEN + 1);
101 assert_eq!(size_of::<fixed_cache::Bucket<(Key, B256)>>(), 128);
102 }
103}