1use core::str;
13
14use yo_common::{Code, Error, Result};
15use yo_shape::Shape;
16
17pub trait Encode: Shape {
23 fn encode<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R;
30}
31
32pub trait Decode: Encode + Sized {
38 type Ref<'a>;
40
41 fn decode(bytes: &[u8]) -> Result<Self>;
48
49 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 #[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}