ecksport_codec/
traits.rs

1//! Core codec trait and trait impls provided for ergonomics.
2//!
3//! This is not the most efficient impl in general, but it's efficient enough
4//! and significantly improves the ergonomics of using the codec system.
5
6use crate::errors::CodecError;
7use crate::util;
8
9/// Trait defined to messages over the wire.
10pub trait RpcCodec: Sized {
11    fn from_slice(buf: &[u8]) -> Result<Self, CodecError>;
12    fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError>;
13}
14
15impl RpcCodec for () {
16    fn from_slice(_buf: &[u8]) -> Result<Self, CodecError> {
17        Ok(())
18    }
19
20    fn fill_buf(&self, _buf: &mut Vec<u8>) -> Result<(), CodecError> {
21        Ok(())
22    }
23}
24
25/// Direct memcpying codec for transmitting bytebufs efficiently.  This encodes
26/// the vec directly and gobbles the entire input.
27impl RpcCodec for Vec<u8> {
28    fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
29        Ok(buf.to_vec())
30    }
31
32    fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
33        buf.extend_from_slice(&self);
34        Ok(())
35    }
36}
37
38macro_rules! decl_tuple_codec {
39    ( $($vident:ident: $tident:ident),* ) => {
40        impl<$($tident: RpcCodec),*> RpcCodec for ($($tident),*) {
41            fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
42                let mut cur = util::Cursor::new(buf);
43
44                $(let $vident = cur.take_len_tagged_inst::<$tident>()?;)*
45
46                if !cur.is_at_end() {
47                    return Err(CodecError::LeftoverBytes(
48                        cur.remaining_bytes(),
49                        cur.inner().len(),
50                    ));
51                }
52
53
54                Ok(($($vident),*))
55            }
56
57            fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
58                let ($($vident),*) = self;
59
60                $(util::write_len_tagged_inst($vident, buf)?;)*
61
62                Ok(())
63            }
64        }
65    }
66}
67
68decl_tuple_codec!(t1: T1, t2: T2);
69decl_tuple_codec!(t1: T1, t2: T2, t3: T3);
70decl_tuple_codec!(t1: T1, t2: T2, t3: T3, t4: T4);
71
72// FIXME figure out a better way to do this
73/*impl<T: RpcCodec> RpcCodec for Vec<T> {
74    fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
75        let mut cur = Cursor::new(buf);
76
77        let mut list = Vec::new();
78        let cnt = cur.take_u32()?;
79        for _ in 0..cnt {
80            let ent = cur.take_len_tagged_inst::<T>()?;
81            list.push(ent);
82        }
83
84        if !cur.is_at_end() {
85            return Err(CodecError::LeftoverBytes(
86                cur.remaining_bytes(),
87                cur.buf.len(),
88            ));
89        }
90
91        Ok(list)
92    }
93
94    fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
95        let cnt = buf.len();
96        if cnt > u32::MAX as usize {
97            return Err(CodecError::ContainerTooLarge(cnt));
98        }
99
100        write_u32(cnt as u32, buf);
101        for e in self {
102            write_len_tagged_inst(e, buf)?;
103        }
104
105        Ok(())
106    }
107}*/
108
109/// Similar implementation to `Vec<u8>` but with UTF-8 check.
110impl RpcCodec for String {
111    fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
112        Ok(std::str::from_utf8(buf)
113            .map_err(|_| CodecError::NonUtf8String)?
114            .to_owned())
115    }
116
117    fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
118        buf.extend_from_slice(self.as_bytes());
119        Ok(())
120    }
121}
122
123macro_rules! decl_int_codec {
124    ( $ity:ty ) => {
125        impl RpcCodec for $ity {
126            fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
127                const NBYTES: usize = (<$ity>::BITS / 8) as usize;
128                let mut cur = util::Cursor::new(buf);
129                let ibuf = cur.take_arr::<NBYTES>()?;
130                Ok(<$ity>::from_be_bytes(ibuf))
131            }
132
133            fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
134                buf.extend_from_slice(&self.to_be_bytes());
135                Ok(())
136            }
137        }
138    };
139}
140
141decl_int_codec!(i8);
142decl_int_codec!(i16);
143decl_int_codec!(i32);
144decl_int_codec!(i64);
145decl_int_codec!(u16);
146decl_int_codec!(u32);
147decl_int_codec!(u64);
148
149macro_rules! decl_intvec_codec {
150    ( $ity:ty ) => {
151        impl RpcCodec for Vec<$ity> {
152            fn from_slice(buf: &[u8]) -> Result<Self, CodecError> {
153                const NBYTES: usize = (<$ity>::BITS / 8) as usize;
154
155                // Check to see that it matches what we expect.
156                let leftover = buf.len() % NBYTES;
157                if leftover > 0 {
158                    return Err(CodecError::LeftoverBytes(leftover, buf.len()));
159                }
160
161                let mut cur = util::Cursor::new(buf);
162
163                let cnt = buf.len() / NBYTES;
164                let mut list = Vec::new();
165
166                for _ in 0..cnt {
167                    let ibuf = cur.take_arr::<NBYTES>()?;
168                    list.push(<$ity>::from_be_bytes(ibuf));
169                }
170
171                Ok(list)
172            }
173
174            fn fill_buf(&self, buf: &mut Vec<u8>) -> Result<(), CodecError> {
175                for i in self {
176                    buf.extend_from_slice(&i.to_be_bytes());
177                }
178                Ok(())
179            }
180        }
181    };
182}
183
184decl_intvec_codec!(i8);
185decl_intvec_codec!(i16);
186decl_intvec_codec!(i32);
187decl_intvec_codec!(i64);
188decl_intvec_codec!(u16);
189decl_intvec_codec!(u32);
190decl_intvec_codec!(u64);
191
192#[cfg(test)]
193mod tests {
194    use crate::*;
195
196    #[test]
197    fn test_tuple_3() {
198        type Tup = (String, String, Vec<u8>);
199        let val: Tup = ("foo".to_string(), "bar".to_string(), vec![2, 5, 7, 10, 12]);
200
201        let buf = encode_to_vec(&val).expect("test: encode");
202        eprintln!("val {val:?}, buf {buf:?}");
203        let dec = <Tup as RpcCodec>::from_slice(&buf).expect("test: decode");
204
205        assert_eq!(dec, val);
206    }
207
208    #[test]
209    fn test_int_arr() {
210        let v: Vec<i16> = vec![5, 10, 15, -1, -2];
211        let buf = encode_to_vec(&v).expect("test: encode");
212        assert_eq!(buf.as_slice(), &[0, 5, 0, 10, 0, 15, 255, 255, 255, 254]);
213        let res = <Vec<i16> as RpcCodec>::from_slice(&buf).expect("test: decode");
214        assert_eq!(res, v);
215    }
216
217    #[test]
218    fn test_int_arr_fail() {
219        let raw_buf = &[0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3];
220        let res = <Vec<u64> as RpcCodec>::from_slice(raw_buf);
221        eprintln!("result {res:?}");
222        if !matches!(res, Err(CodecError::LeftoverBytes(_, _))) {
223            panic!("not expected result");
224        }
225    }
226
227    #[test]
228    fn test_string_invalid() {
229        // This is invalid UTF-8 because the high bit is set indicating there's
230        // a subsequent byte but there isn't one.
231        let buf = vec![0xff];
232        let res = <String as RpcCodec>::from_slice(&buf);
233        eprintln!("result {res:?}");
234        if !matches!(res, Err(CodecError::NonUtf8String)) {
235            panic!("not expected result");
236        }
237    }
238}