Skip to main content

reliakit_codec/
impls.rs

1//! Canonical implementations for Rust primitive and standard library types.
2
3use crate::format::{BOOL_FALSE, BOOL_TRUE, OPTION_NONE, OPTION_SOME, RESULT_ERR, RESULT_OK};
4use crate::{CanonicalDecode, CanonicalEncode, CodecError, DecodeSource, EncodeSink};
5
6macro_rules! impl_int {
7    ($ty:ty) => {
8        impl CanonicalEncode for $ty {
9            fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
10                writer.write_all(&self.to_le_bytes())
11            }
12        }
13
14        impl CanonicalDecode for $ty {
15            fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
16                let mut bytes = [0u8; core::mem::size_of::<$ty>()];
17                reader.read_exact(&mut bytes)?;
18                Ok(<$ty>::from_le_bytes(bytes))
19            }
20        }
21    };
22}
23
24impl_int!(u16);
25impl_int!(i16);
26impl_int!(u32);
27impl_int!(i32);
28impl_int!(u64);
29impl_int!(i64);
30impl_int!(u128);
31impl_int!(i128);
32
33impl CanonicalEncode for u8 {
34    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
35        writer.write_all(&[*self])
36    }
37}
38
39impl CanonicalDecode for u8 {
40    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
41        let mut byte = [0u8; 1];
42        reader.read_exact(&mut byte)?;
43        Ok(byte[0])
44    }
45}
46
47impl CanonicalEncode for i8 {
48    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
49        writer.write_all(&self.to_le_bytes())
50    }
51}
52
53impl CanonicalDecode for i8 {
54    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
55        let byte = u8::decode(reader)?;
56        Ok(Self::from_le_bytes([byte]))
57    }
58}
59
60impl CanonicalEncode for bool {
61    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
62        writer.write_all(&[if *self { BOOL_TRUE } else { BOOL_FALSE }])
63    }
64}
65
66impl CanonicalDecode for bool {
67    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
68        match u8::decode(reader)? {
69            BOOL_FALSE => Ok(false),
70            BOOL_TRUE => Ok(true),
71            _ => Err(CodecError::invalid_value(
72                "invalid bool byte: expected 0x00 or 0x01",
73            )),
74        }
75    }
76}
77
78impl CanonicalEncode for str {
79    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
80        let bytes = self.as_bytes();
81        let len = u32::try_from(bytes.len())
82            .map_err(|_| CodecError::length_overflow("string length exceeds u32::MAX bytes"))?;
83        len.encode(writer)?;
84        writer.write_all(bytes)
85    }
86}
87
88#[cfg(feature = "alloc")]
89impl CanonicalEncode for alloc::string::String {
90    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
91        self.as_str().encode(writer)
92    }
93}
94
95#[cfg(feature = "alloc")]
96impl CanonicalDecode for alloc::string::String {
97    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
98        let bytes = read_len_prefixed_bytes(reader, "string")?;
99        Self::from_utf8(bytes)
100            .map_err(|_| CodecError::invalid_value("invalid UTF-8 string payload"))
101    }
102}
103
104#[cfg(feature = "alloc")]
105impl<T: CanonicalEncode> CanonicalEncode for alloc::vec::Vec<T> {
106    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
107        let len = u32::try_from(self.len())
108            .map_err(|_| CodecError::length_overflow("vector length exceeds u32::MAX items"))?;
109        len.encode(writer)?;
110        for item in self {
111            item.encode(writer)?;
112        }
113        Ok(())
114    }
115}
116
117#[cfg(feature = "alloc")]
118impl<T: CanonicalDecode> CanonicalDecode for alloc::vec::Vec<T> {
119    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
120        let len = u32::decode(reader)?;
121        let len = usize::try_from(len)
122            .map_err(|_| CodecError::length_overflow("vector length does not fit usize"))?;
123        let mut items = alloc::vec::Vec::new();
124        for _ in 0..len {
125            items.push(T::decode(reader)?);
126        }
127        Ok(items)
128    }
129}
130
131#[cfg(feature = "alloc")]
132fn read_len_prefixed_bytes<R: DecodeSource + ?Sized>(
133    reader: &mut R,
134    context: &'static str,
135) -> Result<alloc::vec::Vec<u8>, CodecError> {
136    const CHUNK_SIZE: usize = 8 * 1024;
137
138    let len = u32::decode(reader)?;
139    let len = usize::try_from(len)
140        .map_err(|_| CodecError::length_overflow("byte length does not fit usize"))?;
141
142    if let Some(remaining) = reader.remaining_len() {
143        if len > remaining {
144            return Err(CodecError::unexpected_eof());
145        }
146    }
147
148    let mut bytes = alloc::vec::Vec::new();
149    let mut remaining = len;
150    while remaining != 0 {
151        let chunk_len = remaining.min(CHUNK_SIZE);
152        bytes.try_reserve_exact(chunk_len).map_err(|_| {
153            CodecError::length_overflow(match context {
154                "string" => "string length exceeds available memory",
155                _ => "byte length exceeds available memory",
156            })
157        })?;
158
159        let start = bytes.len();
160        bytes.resize(start + chunk_len, 0);
161        reader.read_exact(&mut bytes[start..])?;
162        remaining -= chunk_len;
163    }
164
165    Ok(bytes)
166}
167
168impl<T: CanonicalEncode> CanonicalEncode for Option<T> {
169    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
170        match self {
171            None => OPTION_NONE.encode(writer),
172            Some(value) => {
173                OPTION_SOME.encode(writer)?;
174                value.encode(writer)
175            }
176        }
177    }
178}
179
180impl<T: CanonicalDecode> CanonicalDecode for Option<T> {
181    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
182        match u8::decode(reader)? {
183            OPTION_NONE => Ok(None),
184            OPTION_SOME => T::decode(reader).map(Some),
185            _ => Err(CodecError::invalid_value(
186                "invalid Option tag: expected 0x00 for None or 0x01 for Some",
187            )),
188        }
189    }
190}
191
192impl<T: CanonicalEncode, E: CanonicalEncode> CanonicalEncode for Result<T, E> {
193    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
194        match self {
195            Ok(value) => {
196                RESULT_OK.encode(writer)?;
197                value.encode(writer)
198            }
199            Err(error) => {
200                RESULT_ERR.encode(writer)?;
201                error.encode(writer)
202            }
203        }
204    }
205}
206
207impl<T: CanonicalDecode, E: CanonicalDecode> CanonicalDecode for Result<T, E> {
208    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
209        match u8::decode(reader)? {
210            RESULT_OK => T::decode(reader).map(Ok),
211            RESULT_ERR => E::decode(reader).map(Err),
212            _ => Err(CodecError::invalid_value(
213                "invalid Result tag: expected 0x00 for Ok or 0x01 for Err",
214            )),
215        }
216    }
217}
218
219impl<T: CanonicalEncode, const N: usize> CanonicalEncode for [T; N] {
220    fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
221        for item in self {
222            item.encode(writer)?;
223        }
224        Ok(())
225    }
226}
227
228#[cfg(feature = "alloc")]
229impl<T: CanonicalDecode, const N: usize> CanonicalDecode for [T; N] {
230    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
231        let mut items = alloc::vec::Vec::with_capacity(N);
232        for _ in 0..N {
233            items.push(T::decode(reader)?);
234        }
235        match items.try_into() {
236            Ok(array) => Ok(array),
237            Err(_) => Err(CodecError::invalid_value("decoded array length mismatch")),
238        }
239    }
240}
241
242#[cfg(not(feature = "alloc"))]
243impl<const N: usize> CanonicalDecode for [u8; N] {
244    fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
245        let mut bytes = [0u8; N];
246        reader.read_exact(&mut bytes)?;
247        Ok(bytes)
248    }
249}
250
251macro_rules! impl_tuple {
252    ($($name:ident),+) => {
253        impl<$($name: CanonicalEncode),+> CanonicalEncode for ($($name,)+) {
254            #[allow(non_snake_case)]
255            fn encode<W: EncodeSink + ?Sized>(&self, writer: &mut W) -> Result<(), CodecError> {
256                let ($($name,)+) = self;
257                $($name.encode(writer)?;)+
258                Ok(())
259            }
260        }
261
262        impl<$($name: CanonicalDecode),+> CanonicalDecode for ($($name,)+) {
263            fn decode<R: DecodeSource + ?Sized>(reader: &mut R) -> Result<Self, CodecError> {
264                Ok(($($name::decode(reader)?,)+))
265            }
266        }
267    };
268}
269
270impl_tuple!(A);
271impl_tuple!(A, B);
272impl_tuple!(A, B, C);
273impl_tuple!(A, B, C, D);