foyer_common/
code.rs

1// Copyright 2025 foyer Project Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::hash::{BuildHasher, BuildHasherDefault, Hash};
16
17use twox_hash::XxHash64;
18
19/// Key trait for the in-memory cache.
20pub trait Key: Send + Sync + 'static + Hash + Eq {}
21/// Value trait for the in-memory cache.
22pub trait Value: Send + Sync + 'static {}
23
24impl<T: Send + Sync + 'static + std::hash::Hash + Eq> Key for T {}
25impl<T: Send + Sync + 'static> Value for T {}
26
27/// Hash builder trait.
28pub trait HashBuilder: BuildHasher + Send + Sync + 'static {}
29impl<T> HashBuilder for T where T: BuildHasher + Send + Sync + 'static {}
30
31/// The default hasher for foyer.
32///
33/// It is guaranteed that the hash results of the same key are the same across different runs.
34pub type DefaultHasher = BuildHasherDefault<XxHash64>;
35
36/// Code error.
37#[derive(Debug, thiserror::Error)]
38pub enum CodeError {
39    /// The buffer size is not enough to hold the serialized data.
40    #[error("exceed size limit")]
41    SizeLimit,
42    /// Other std io error.
43    #[error("io error: {0}")]
44    Io(std::io::Error),
45    #[cfg(feature = "serde")]
46    /// Other bincode error.
47    #[error("bincode error: {0}")]
48    Bincode(bincode::Error),
49    /// Unrecognized.
50    #[error("unrecognized data: {0:?}")]
51    Unrecognized(Vec<u8>),
52    /// Other error.
53    #[error("other error: {0}")]
54    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
55}
56
57/// Code Result.
58pub type CodeResult<T> = std::result::Result<T, CodeError>;
59
60impl From<std::io::Error> for CodeError {
61    fn from(err: std::io::Error) -> Self {
62        match err.kind() {
63            std::io::ErrorKind::WriteZero => Self::SizeLimit,
64            _ => Self::Io(err),
65        }
66    }
67}
68
69#[cfg(feature = "serde")]
70impl From<bincode::Error> for CodeError {
71    fn from(err: bincode::Error) -> Self {
72        match *err {
73            bincode::ErrorKind::SizeLimit => Self::SizeLimit,
74            bincode::ErrorKind::Io(e) => e.into(),
75            _ => Self::Bincode(err),
76        }
77    }
78}
79
80/// Key trait for the disk cache.
81pub trait StorageKey: Key + Code {}
82impl<T> StorageKey for T where T: Key + Code {}
83
84/// Value trait for the disk cache.
85pub trait StorageValue: Value + 'static + Code {}
86impl<T> StorageValue for T where T: Value + Code {}
87
88/// Encode/decode trait for key and value.
89///
90/// [`Code`] is required while working with foyer hybrid cache.
91///
92/// Some general types has already implemented [`Code`] by foyer, but the user needs to implement it for complex types.
93///
94/// Or, the user can enable `serde` feature for foyer.
95/// Then all types that implements `serde::Serialize` and `serde::de::DeserializeOwned` will be automatically
96/// implemented for [`Code`].
97pub trait Code {
98    /// Encode the object into a writer.
99    fn encode(&self, writer: &mut impl std::io::Write) -> std::result::Result<(), CodeError>;
100
101    /// Decode the object from a reader.
102    fn decode(reader: &mut impl std::io::Read) -> std::result::Result<Self, CodeError>
103    where
104        Self: Sized;
105
106    /// Estimated serialized size of the object.
107    ///
108    /// The estimated serialized size is used by selector between different disk cache engines.
109    fn estimated_size(&self) -> usize;
110}
111
112#[cfg(feature = "serde")]
113impl<T> Code for T
114where
115    T: serde::Serialize + serde::de::DeserializeOwned,
116{
117    fn encode(&self, writer: &mut impl std::io::Write) -> std::result::Result<(), CodeError> {
118        bincode::serialize_into(writer, self).map_err(CodeError::from)
119    }
120
121    fn decode(reader: &mut impl std::io::Read) -> std::result::Result<Self, CodeError> {
122        bincode::deserialize_from(reader).map_err(CodeError::from)
123    }
124
125    fn estimated_size(&self) -> usize {
126        bincode::serialized_size(self).unwrap() as usize
127    }
128}
129
130macro_rules! impl_serde_for_numeric_types {
131    ($($t:ty),*) => {
132        $(
133            #[cfg(not(feature = "serde"))]
134            impl Code for $t {
135                fn encode(&self, writer: &mut impl std::io::Write) -> std::result::Result<(), CodeError> {
136                    writer.write_all(&self.to_le_bytes()).map_err(CodeError::from)
137                }
138
139                fn decode(reader: &mut impl std::io::Read) -> std::result::Result<Self, CodeError> {
140                    let mut buf = [0u8; std::mem::size_of::<$t>()];
141                    reader.read_exact(&mut buf).map_err(CodeError::from)?;
142                    Ok(<$t>::from_le_bytes(buf))
143                }
144
145                fn estimated_size(&self) -> usize {
146                    std::mem::size_of::<$t>()
147                }
148            }
149        )*
150    };
151}
152
153macro_rules! for_all_numeric_types {
154    ($macro:ident) => {
155        $macro! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64}
156    };
157}
158
159for_all_numeric_types! { impl_serde_for_numeric_types }
160
161#[cfg(not(feature = "serde"))]
162impl Code for bool {
163    fn encode(&self, writer: &mut impl std::io::Write) -> std::result::Result<(), CodeError> {
164        writer
165            .write_all(if *self { &[1u8] } else { &[0u8] })
166            .map_err(CodeError::from)
167    }
168
169    fn decode(reader: &mut impl std::io::Read) -> std::result::Result<Self, CodeError>
170    where
171        Self: Sized,
172    {
173        let mut buf = [0u8; 1];
174        reader.read_exact(&mut buf).map_err(CodeError::from)?;
175        match buf[0] {
176            0 => Ok(false),
177            1 => Ok(true),
178            _ => Err(CodeError::Unrecognized(buf.to_vec())),
179        }
180    }
181
182    fn estimated_size(&self) -> usize {
183        1
184    }
185}
186
187#[cfg(not(feature = "serde"))]
188impl Code for Vec<u8> {
189    fn encode(&self, writer: &mut impl std::io::Write) -> std::result::Result<(), CodeError> {
190        self.len().encode(writer)?;
191        writer.write_all(self).map_err(CodeError::from)
192    }
193
194    #[expect(clippy::uninit_vec)]
195    fn decode(reader: &mut impl std::io::Read) -> std::result::Result<Self, CodeError>
196    where
197        Self: Sized,
198    {
199        let len = usize::decode(reader)?;
200        let mut v = Vec::with_capacity(len);
201        unsafe {
202            v.set_len(len);
203        }
204        reader.read_exact(&mut v).map_err(CodeError::from)?;
205        Ok(v)
206    }
207
208    fn estimated_size(&self) -> usize {
209        std::mem::size_of::<usize>() + self.len()
210    }
211}
212
213#[cfg(not(feature = "serde"))]
214impl Code for String {
215    fn encode(&self, writer: &mut impl std::io::Write) -> std::result::Result<(), CodeError> {
216        self.len().encode(writer)?;
217        writer.write_all(self.as_bytes()).map_err(CodeError::from)
218    }
219
220    #[expect(clippy::uninit_vec)]
221    fn decode(reader: &mut impl std::io::Read) -> std::result::Result<Self, CodeError>
222    where
223        Self: Sized,
224    {
225        let len = usize::decode(reader)?;
226        let mut v = Vec::with_capacity(len);
227        unsafe {
228            v.set_len(len);
229        }
230        reader.read_exact(&mut v).map_err(CodeError::from)?;
231        String::from_utf8(v).map_err(|e| CodeError::Unrecognized(e.into_bytes()))
232    }
233
234    fn estimated_size(&self) -> usize {
235        std::mem::size_of::<usize>() + self.len()
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242
243    #[cfg(feature = "serde")]
244    mod serde {
245        use super::*;
246
247        #[test]
248        fn test_encode_overflow() {
249            let mut buf = [0u8; 4];
250            assert!(matches! {1u64.encode(&mut buf.as_mut()), Err(CodeError::SizeLimit)});
251        }
252    }
253
254    #[cfg(not(feature = "serde"))]
255    mod non_serde {
256        use super::*;
257
258        #[test]
259        fn test_encode_overflow() {
260            let mut buf = [0u8; 4];
261            assert!(matches! {1u64.encode(&mut buf.as_mut()), Err(CodeError::SizeLimit)});
262        }
263
264        macro_rules! impl_serde_test_for_numeric_types {
265            ($($t:ty),*) => {
266                paste::paste! {
267                    $(
268                        #[test]
269                        fn [<test_ $t _serde>]() {
270                            for a in [0 as $t, <$t>::MIN, <$t>::MAX] {
271                                let mut buf = vec![0xffu8; a.estimated_size()];
272                                a.encode(&mut buf.as_mut_slice()).unwrap();
273                                let b = <$t>::decode(&mut buf.as_slice()).unwrap();
274                                assert_eq!(a, b);
275                            }
276                        }
277                    )*
278                }
279            };
280        }
281
282        for_all_numeric_types! { impl_serde_test_for_numeric_types }
283
284        #[test]
285        fn test_bool_serde() {
286            let a = true;
287            let mut buf = vec![0xffu8; a.estimated_size()];
288            a.encode(&mut buf.as_mut_slice()).unwrap();
289            let b = bool::decode(&mut buf.as_slice()).unwrap();
290            assert_eq!(a, b);
291        }
292
293        #[test]
294        fn test_vec_u8_serde() {
295            let mut a = vec![0u8; 42];
296            rand::fill(&mut a[..]);
297            let mut buf = vec![0xffu8; a.estimated_size()];
298            a.encode(&mut buf.as_mut_slice()).unwrap();
299            let b = Vec::<u8>::decode(&mut buf.as_slice()).unwrap();
300            assert_eq!(a, b);
301        }
302
303        #[test]
304        fn test_string_serde() {
305            let a = "hello world".to_string();
306            let mut buf = vec![0xffu8; a.estimated_size()];
307            a.encode(&mut buf.as_mut_slice()).unwrap();
308            let b = String::decode(&mut buf.as_slice()).unwrap();
309            assert_eq!(a, b);
310        }
311    }
312}