Skip to main content

yo/
store.rs

1//! How a value becomes the bytes in a record, and back (`15` section 4).
2//!
3//! There is no serialization here in the sense the word usually has. A `u64` is
4//! eight bytes little endian, a string is its own bytes, and neither of them
5//! goes near a format that would have to be parsed. Nothing allocates on the
6//! way in, and the borrowed form on the way out does not allocate either.
7//!
8//! What is missing is the part `#[derive(Yo)]` writes: a struct's fields laid
9//! out in the order its shape declares. Until the derive lands, a collection
10//! holds the primitives, which is what `Map<K, V>` needs to be worth measuring.
11
12use core::str;
13
14use yo_common::{Code, Error, Result};
15use yo_shape::Shape;
16
17/// A type that can be written into a record.
18///
19/// Implemented for a borrowed form as well as an owned one, so that a lookup
20/// can take `&str` where the collection holds `String`, exactly as
21/// `HashMap<String, _>::get` does.
22pub trait Encode: Shape {
23    /// Hand the bytes of `self` to `f`.
24    ///
25    /// A callback rather than a returned `Vec` because a returned `Vec` is an
26    /// allocation, and this is the write path. A type whose bytes are already
27    /// contiguous passes them straight through, and a fixed width one builds
28    /// them on the stack.
29    fn encode<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R;
30}
31
32/// A type that can be read back out of a record.
33///
34/// [`Decode::Ref`] is the borrowed view: the form that reads out of the arena
35/// without copying, which is Y29 and where G6's point read budget lives. The
36/// owned form is one call away for the code that does not care.
37pub trait Decode: Encode + Sized {
38    /// The borrowed view of this type.
39    type Ref<'a>;
40
41    /// Read an owned value.
42    ///
43    /// # Errors
44    ///
45    /// [`Code::Corrupt`] when the bytes are not this type, which means the
46    /// collection holds something the shape said it did not.
47    fn decode(bytes: &[u8]) -> Result<Self>;
48
49    /// Read a borrowed view, which copies nothing.
50    ///
51    /// # Errors
52    ///
53    /// The same as [`Decode::decode`].
54    fn view(bytes: &[u8]) -> Result<Self::Ref<'_>>;
55}
56
57fn wrong_len(what: &str, want: usize, got: usize) -> Error {
58    Error::fmt(
59        Code::Corrupt,
60        format_args!("a {what} in this collection is {got} bytes and should be {want}"),
61    )
62}
63
64macro_rules! fixed {
65    ($($t:ty),* $(,)?) => {
66        $(
67            impl Encode for $t {
68                #[inline]
69                fn encode<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
70                    f(&self.to_le_bytes())
71                }
72            }
73
74            impl Decode for $t {
75                type Ref<'a> = $t;
76
77                #[inline]
78                fn decode(bytes: &[u8]) -> Result<$t> {
79                    let want = size_of::<$t>();
80                    let array = bytes
81                        .try_into()
82                        .map_err(|_| wrong_len(stringify!($t), want, bytes.len()))?;
83                    Ok(<$t>::from_le_bytes(array))
84                }
85
86                #[inline]
87                fn view(bytes: &[u8]) -> Result<$t> {
88                    <$t as Decode>::decode(bytes)
89                }
90            }
91        )*
92    };
93}
94
95fixed!(u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
96
97impl Encode for bool {
98    #[inline]
99    fn encode<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
100        f(&[u8::from(*self)])
101    }
102}
103
104impl Decode for bool {
105    type Ref<'a> = bool;
106
107    #[inline]
108    fn decode(bytes: &[u8]) -> Result<bool> {
109        match bytes {
110            [0] => Ok(false),
111            [1] => Ok(true),
112            [_] => Err(Error::new(
113                Code::Corrupt,
114                "a bool in this collection is neither 0 nor 1",
115            )),
116            other => Err(wrong_len("bool", 1, other.len())),
117        }
118    }
119
120    #[inline]
121    fn view(bytes: &[u8]) -> Result<bool> {
122        <bool as Decode>::decode(bytes)
123    }
124}
125
126impl Encode for str {
127    #[inline]
128    fn encode<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
129        f(self.as_bytes())
130    }
131}
132
133impl Encode for String {
134    #[inline]
135    fn encode<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
136        f(self.as_bytes())
137    }
138}
139
140impl Decode for String {
141    type Ref<'a> = &'a str;
142
143    fn decode(bytes: &[u8]) -> Result<String> {
144        <String as Decode>::view(bytes).map(ToOwned::to_owned)
145    }
146
147    #[inline]
148    fn view(bytes: &[u8]) -> Result<&str> {
149        str::from_utf8(bytes).map_err(|e| {
150            Error::fmt(
151                Code::Corrupt,
152                format_args!("a str in this collection is not UTF-8: {e}"),
153            )
154        })
155    }
156}
157
158impl Encode for [u8] {
159    #[inline]
160    fn encode<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
161        f(self)
162    }
163}
164
165impl Encode for Vec<u8> {
166    #[inline]
167    fn encode<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
168        f(self)
169    }
170}
171
172impl Decode for Vec<u8> {
173    type Ref<'a> = &'a [u8];
174
175    #[inline]
176    fn decode(bytes: &[u8]) -> Result<Vec<u8>> {
177        Ok(bytes.to_vec())
178    }
179
180    #[inline]
181    fn view(bytes: &[u8]) -> Result<&[u8]> {
182        Ok(bytes)
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    fn bytes_of(v: &(impl Encode + ?Sized)) -> Vec<u8> {
191        v.encode(<[u8]>::to_vec)
192    }
193
194    #[test]
195    fn fixed_widths_are_little_endian_and_their_own_size() {
196        assert_eq!(bytes_of(&1u32), vec![1, 0, 0, 0]);
197        assert_eq!(bytes_of(&-2i16), vec![0xfe, 0xff]);
198        assert_eq!(bytes_of(&1.5f64), 1.5f64.to_le_bytes().to_vec());
199        assert_eq!(bytes_of(&true), vec![1]);
200        assert_eq!(u64::decode(&bytes_of(&9u64)).unwrap(), 9);
201        assert_eq!(f32::decode(&bytes_of(&0.5f32)).unwrap(), 0.5);
202        assert!(bool::decode(&bytes_of(&false)).unwrap().eq(&false));
203    }
204
205    #[test]
206    fn text_and_bytes_pass_straight_through() {
207        assert_eq!(bytes_of("hello"), b"hello".to_vec());
208        assert_eq!(String::view(b"hello").unwrap(), "hello");
209        assert_eq!(Vec::<u8>::view(b"\x00\xff").unwrap(), b"\x00\xff");
210    }
211
212    /// The bytes in a record are the only thing that says what a value is, so
213    /// the wrong number of them is a corruption and says which type it was
214    /// expecting.
215    #[test]
216    fn the_wrong_number_of_bytes_is_corruption() {
217        let e = u64::decode(b"1234").expect_err("four bytes is not a u64");
218        assert_eq!(e.code(), Code::Corrupt);
219        assert_eq!(
220            e.message(),
221            "a u64 in this collection is 4 bytes and should be 8"
222        );
223
224        assert_eq!(
225            bool::decode(&[2]).expect_err("2 is not a bool").code(),
226            Code::Corrupt
227        );
228        assert_eq!(
229            bool::decode(&[0, 0])
230                .expect_err("two bytes is not a bool")
231                .code(),
232            Code::Corrupt
233        );
234        assert!(
235            String::decode(&[0xff, 0xfe])
236                .expect_err("that is not UTF-8")
237                .message()
238                .contains("not UTF-8")
239        );
240    }
241}