open_fastrlp/
encode.rs

1use crate::types::*;
2use arrayvec::ArrayVec;
3use auto_impl::auto_impl;
4use bytes::{BufMut, Bytes, BytesMut};
5use core::borrow::Borrow;
6
7fn zeroless_view(v: &impl AsRef<[u8]>) -> &[u8] {
8    let v = v.as_ref();
9    &v[v.iter().take_while(|&&b| b == 0).count()..]
10}
11
12impl Header {
13    /// Encodes the header into the `out` buffer.
14    pub fn encode(&self, out: &mut dyn BufMut) {
15        if self.payload_length < 56 {
16            let code = if self.list { EMPTY_LIST_CODE } else { EMPTY_STRING_CODE };
17            out.put_u8(code + self.payload_length as u8);
18        } else {
19            let len_be = self.payload_length.to_be_bytes();
20            let len_be = zeroless_view(&len_be);
21            let code = if self.list { 0xF7 } else { 0xB7 };
22            out.put_u8(code + len_be.len() as u8);
23            out.put_slice(len_be);
24        }
25    }
26
27    /// Returns the length of the encoded header
28    pub fn length(&self) -> usize {
29        let mut out = BytesMut::new();
30        self.encode(&mut out);
31        out.len()
32    }
33}
34
35pub const fn length_of_length(payload_length: usize) -> usize {
36    if payload_length < 56 {
37        1
38    } else {
39        1 + 8 - payload_length.leading_zeros() as usize / 8
40    }
41}
42
43#[doc(hidden)]
44pub const fn const_add(a: usize, b: usize) -> usize {
45    a + b
46}
47
48#[doc(hidden)]
49pub unsafe trait MaxEncodedLen<const LEN: usize>: Encodable {}
50
51#[doc(hidden)]
52pub unsafe trait MaxEncodedLenAssoc: Encodable {
53    const LEN: usize;
54}
55
56/// Use this to define length of an encoded entity
57///
58/// # Safety
59/// Invalid value can cause the encoder to crash.
60#[macro_export]
61macro_rules! impl_max_encoded_len {
62    ($t:ty, $len:block) => {
63        unsafe impl MaxEncodedLen<{ $len }> for $t {}
64        unsafe impl MaxEncodedLenAssoc for $t {
65            const LEN: usize = $len;
66        }
67    };
68}
69
70#[auto_impl(&)]
71#[cfg_attr(feature = "alloc", auto_impl(Box, Arc))]
72pub trait Encodable {
73    fn encode(&self, out: &mut dyn BufMut);
74    fn length(&self) -> usize {
75        let mut out = BytesMut::new();
76        self.encode(&mut out);
77        out.len()
78    }
79}
80
81impl<'a> Encodable for &'a [u8] {
82    fn length(&self) -> usize {
83        let mut len = self.len();
84        if self.len() != 1 || self[0] >= EMPTY_STRING_CODE {
85            len += length_of_length(self.len());
86        }
87        len
88    }
89
90    fn encode(&self, out: &mut dyn BufMut) {
91        if self.len() != 1 || self[0] >= EMPTY_STRING_CODE {
92            Header { list: false, payload_length: self.len() }.encode(out);
93        }
94        out.put_slice(self);
95    }
96}
97
98impl<const LEN: usize> Encodable for [u8; LEN] {
99    fn length(&self) -> usize {
100        (self as &[u8]).length()
101    }
102
103    fn encode(&self, out: &mut dyn BufMut) {
104        (self as &[u8]).encode(out)
105    }
106}
107
108unsafe impl<const LEN: usize> MaxEncodedLenAssoc for [u8; LEN] {
109    const LEN: usize = LEN + length_of_length(LEN);
110}
111
112macro_rules! encodable_uint {
113    ($t:ty) => {
114        #[allow(clippy::cmp_owned)]
115        impl Encodable for $t {
116            fn length(&self) -> usize {
117                if *self < <$t>::from(EMPTY_STRING_CODE) {
118                    1
119                } else {
120                    1 + (<$t>::BITS as usize / 8) - (self.leading_zeros() as usize / 8)
121                }
122            }
123
124            fn encode(&self, out: &mut dyn BufMut) {
125                if *self == 0 {
126                    out.put_u8(EMPTY_STRING_CODE);
127                } else if *self < <$t>::from(EMPTY_STRING_CODE) {
128                    out.put_u8(u8::try_from(*self).unwrap());
129                } else {
130                    let be = self.to_be_bytes();
131                    let be = zeroless_view(&be);
132                    out.put_u8(EMPTY_STRING_CODE + be.len() as u8);
133                    out.put_slice(be);
134                }
135            }
136        }
137    };
138}
139
140macro_rules! max_encoded_len_uint {
141    ($t:ty) => {
142        impl_max_encoded_len!($t, {
143            length_of_length(<$t>::MAX.to_be_bytes().len()) + <$t>::MAX.to_be_bytes().len()
144        });
145    };
146}
147
148encodable_uint!(usize);
149max_encoded_len_uint!(usize);
150
151encodable_uint!(u8);
152max_encoded_len_uint!(u8);
153
154encodable_uint!(u16);
155max_encoded_len_uint!(u16);
156
157encodable_uint!(u32);
158max_encoded_len_uint!(u32);
159
160encodable_uint!(u64);
161max_encoded_len_uint!(u64);
162
163encodable_uint!(u128);
164max_encoded_len_uint!(u128);
165
166impl Encodable for bool {
167    fn length(&self) -> usize {
168        (*self as u8).length()
169    }
170
171    fn encode(&self, out: &mut dyn BufMut) {
172        (*self as u8).encode(out)
173    }
174}
175
176impl_max_encoded_len!(bool, { <u8 as MaxEncodedLenAssoc>::LEN });
177
178#[cfg(feature = "ethnum")]
179mod ethnum_support {
180    use super::*;
181
182    encodable_uint!(ethnum::U256);
183    impl_max_encoded_len!(ethnum::U256, { length_of_length(32) + 32 });
184}
185
186#[cfg(feature = "ethereum-types")]
187mod ethereum_types_support {
188    use super::*;
189    use ethereum_types::*;
190
191    macro_rules! fixed_hash_impl {
192        ($t:ty) => {
193            impl Encodable for $t {
194                fn length(&self) -> usize {
195                    self.0.length()
196                }
197
198                fn encode(&self, out: &mut dyn bytes::BufMut) {
199                    self.0.encode(out)
200                }
201            }
202            impl_max_encoded_len!($t, { length_of_length(<$t>::len_bytes()) + <$t>::len_bytes() });
203        };
204    }
205
206    fixed_hash_impl!(H64);
207    fixed_hash_impl!(H128);
208    fixed_hash_impl!(H160);
209    fixed_hash_impl!(H256);
210    fixed_hash_impl!(H512);
211    fixed_hash_impl!(H520);
212    fixed_hash_impl!(Bloom);
213
214    macro_rules! fixed_uint_impl {
215        ($t:ty, $n_bytes:tt) => {
216            impl Encodable for $t {
217                fn length(&self) -> usize {
218                    if *self < <$t>::from(EMPTY_STRING_CODE) {
219                        1
220                    } else {
221                        1 + $n_bytes - (self.leading_zeros() as usize / 8)
222                    }
223                }
224
225                fn encode(&self, out: &mut dyn bytes::BufMut) {
226                    let mut temp_arr = [0u8; $n_bytes];
227                    self.to_big_endian(&mut temp_arr[..]);
228                    // cut the leading zeros after converting to big endian
229                    let sliced = &temp_arr[(self.leading_zeros() / 8) as usize..];
230                    sliced.encode(out);
231                }
232            }
233        };
234    }
235
236    fixed_uint_impl!(U64, 8);
237    fixed_uint_impl!(U128, 16);
238    fixed_uint_impl!(U256, 32);
239    fixed_uint_impl!(U512, 64);
240}
241
242macro_rules! slice_impl {
243    ($t:ty) => {
244        impl $crate::Encodable for $t {
245            fn length(&self) -> usize {
246                (&self[..]).length()
247            }
248
249            fn encode(&self, out: &mut dyn bytes::BufMut) {
250                (&self[..]).encode(out)
251            }
252        }
253    };
254}
255
256#[cfg(feature = "alloc")]
257mod alloc_support {
258    use super::*;
259
260    extern crate alloc;
261
262    impl<T> Encodable for ::alloc::vec::Vec<T>
263    where
264        T: Encodable,
265    {
266        fn length(&self) -> usize {
267            list_length(self)
268        }
269
270        fn encode(&self, out: &mut dyn BufMut) {
271            encode_list(self, out)
272        }
273    }
274
275    impl Encodable for ::alloc::string::String {
276        fn encode(&self, out: &mut dyn BufMut) {
277            self.as_bytes().encode(out);
278        }
279        fn length(&self) -> usize {
280            self.as_bytes().length()
281        }
282    }
283}
284slice_impl!(Bytes);
285slice_impl!(BytesMut);
286
287fn rlp_list_header<E, K>(v: &[K]) -> Header
288where
289    E: Encodable + ?Sized,
290    K: Borrow<E>,
291{
292    let mut h = Header { list: true, payload_length: 0 };
293    for x in v {
294        h.payload_length += x.borrow().length();
295    }
296    h
297}
298
299pub fn list_length<E, K>(v: &[K]) -> usize
300where
301    E: Encodable,
302    K: Borrow<E>,
303{
304    let payload_length = rlp_list_header(v).payload_length;
305    length_of_length(payload_length) + payload_length
306}
307
308pub fn encode_list<E, K>(v: &[K], out: &mut dyn BufMut)
309where
310    E: Encodable + ?Sized,
311    K: Borrow<E>,
312{
313    let h = rlp_list_header(v);
314    h.encode(out);
315    for x in v {
316        x.borrow().encode(out);
317    }
318}
319
320pub fn encode_fixed_size<E: MaxEncodedLen<LEN>, const LEN: usize>(v: &E) -> ArrayVec<u8, LEN> {
321    let mut out = ArrayVec::from([0_u8; LEN]);
322
323    let mut s = out.as_mut_slice();
324
325    v.encode(&mut s);
326
327    let final_len = LEN - s.len();
328    out.truncate(final_len);
329
330    out
331}
332
333#[cfg(test)]
334mod tests {
335    extern crate alloc;
336
337    use super::*;
338    use alloc::vec;
339    use bytes::BytesMut;
340    use hex_literal::hex;
341
342    fn encoded<T: Encodable>(t: T) -> BytesMut {
343        let mut out = BytesMut::new();
344        t.encode(&mut out);
345        out
346    }
347
348    fn encoded_list<T: Encodable + Clone>(t: &[T]) -> BytesMut {
349        let mut out1 = BytesMut::new();
350        encode_list(t, &mut out1);
351
352        let v = t.to_vec();
353        assert_eq!(out1.len(), v.length());
354
355        let mut out2 = BytesMut::new();
356        v.encode(&mut out2);
357        assert_eq!(out1, out2);
358
359        out1
360    }
361
362    #[test]
363    fn rlp_strings() {
364        assert_eq!(encoded(hex!(""))[..], hex!("80")[..]);
365        assert_eq!(encoded(hex!("7B"))[..], hex!("7b")[..]);
366        assert_eq!(encoded(hex!("80"))[..], hex!("8180")[..]);
367        assert_eq!(encoded(hex!("ABBA"))[..], hex!("82abba")[..]);
368    }
369
370    fn u8_fixtures() -> impl IntoIterator<Item = (u8, &'static [u8])> {
371        vec![
372            (0, &hex!("80")[..]),
373            (1, &hex!("01")[..]),
374            (0x7F, &hex!("7F")[..]),
375            (0x80, &hex!("8180")[..]),
376        ]
377    }
378
379    fn c<T, U: From<T>>(
380        it: impl IntoIterator<Item = (T, &'static [u8])>,
381    ) -> impl Iterator<Item = (U, &'static [u8])> {
382        it.into_iter().map(|(k, v)| (k.into(), v))
383    }
384
385    fn u16_fixtures() -> impl IntoIterator<Item = (u16, &'static [u8])> {
386        c(u8_fixtures()).chain(vec![(0x400, &hex!("820400")[..])])
387    }
388
389    fn u32_fixtures() -> impl IntoIterator<Item = (u32, &'static [u8])> {
390        c(u16_fixtures())
391            .chain(vec![(0xFFCCB5, &hex!("83ffccb5")[..]), (0xFFCCB5DD, &hex!("84ffccb5dd")[..])])
392    }
393
394    fn u64_fixtures() -> impl IntoIterator<Item = (u64, &'static [u8])> {
395        c(u32_fixtures()).chain(vec![
396            (0xFFCCB5DDFF, &hex!("85ffccb5ddff")[..]),
397            (0xFFCCB5DDFFEE, &hex!("86ffccb5ddffee")[..]),
398            (0xFFCCB5DDFFEE14, &hex!("87ffccb5ddffee14")[..]),
399            (0xFFCCB5DDFFEE1483, &hex!("88ffccb5ddffee1483")[..]),
400        ])
401    }
402
403    fn u128_fixtures() -> impl IntoIterator<Item = (u128, &'static [u8])> {
404        c(u64_fixtures()).chain(vec![(
405            0x10203E405060708090A0B0C0D0E0F2,
406            &hex!("8f10203e405060708090a0b0c0d0e0f2")[..],
407        )])
408    }
409
410    #[cfg(feature = "ethnum")]
411    fn u256_fixtures() -> impl IntoIterator<Item = (ethnum::U256, &'static [u8])> {
412        c(u128_fixtures()).chain(vec![(
413            ethnum::U256::from_str_radix(
414                "0100020003000400050006000700080009000A0B4B000C000D000E01",
415                16,
416            )
417            .unwrap(),
418            &hex!("9c0100020003000400050006000700080009000a0b4b000c000d000e01")[..],
419        )])
420    }
421
422    #[cfg(feature = "ethereum-types")]
423    fn eth_u64_fixtures() -> impl IntoIterator<Item = (ethereum_types::U64, &'static [u8])> {
424        c(u64_fixtures()).chain(vec![
425            (
426                ethereum_types::U64::from_str_radix("FFCCB5DDFF", 16).unwrap(),
427                &hex!("85ffccb5ddff")[..],
428            ),
429            (
430                ethereum_types::U64::from_str_radix("FFCCB5DDFFEE", 16).unwrap(),
431                &hex!("86ffccb5ddffee")[..],
432            ),
433            (
434                ethereum_types::U64::from_str_radix("FFCCB5DDFFEE14", 16).unwrap(),
435                &hex!("87ffccb5ddffee14")[..],
436            ),
437            (
438                ethereum_types::U64::from_str_radix("FFCCB5DDFFEE1483", 16).unwrap(),
439                &hex!("88ffccb5ddffee1483")[..],
440            ),
441        ])
442    }
443
444    #[cfg(feature = "ethereum-types")]
445    fn eth_u128_fixtures() -> impl IntoIterator<Item = (ethereum_types::U128, &'static [u8])> {
446        c(u128_fixtures()).chain(vec![(
447            ethereum_types::U128::from_str_radix("10203E405060708090A0B0C0D0E0F2", 16).unwrap(),
448            &hex!("8f10203e405060708090a0b0c0d0e0f2")[..],
449        )])
450    }
451
452    #[cfg(feature = "ethereum-types")]
453    fn eth_u256_fixtures() -> impl IntoIterator<Item = (ethereum_types::U256, &'static [u8])> {
454        c(u128_fixtures()).chain(vec![(
455            ethereum_types::U256::from_str_radix(
456                "0100020003000400050006000700080009000A0B4B000C000D000E01",
457                16,
458            )
459            .unwrap(),
460            &hex!("9c0100020003000400050006000700080009000a0b4b000c000d000e01")[..],
461        )])
462    }
463
464    #[cfg(feature = "ethereum-types")]
465    fn eth_u512_fixtures() -> impl IntoIterator<Item = (ethereum_types::U512, &'static [u8])> {
466        c(eth_u256_fixtures()).chain(vec![(
467            ethereum_types::U512::from_str_radix(
468                "0100020003000400050006000700080009000A0B4B000C000D000E010100020003000400050006000700080009000A0B4B000C000D000E01",
469                16,
470            )
471            .unwrap(),
472            &hex!("b8380100020003000400050006000700080009000A0B4B000C000D000E010100020003000400050006000700080009000A0B4B000C000D000E01")[..],
473        )])
474    }
475
476    macro_rules! uint_rlp_test {
477        ($fixtures:expr) => {
478            for (input, output) in $fixtures {
479                assert_eq!(encoded(input), output);
480            }
481        };
482    }
483
484    #[test]
485    fn rlp_uints() {
486        uint_rlp_test!(u8_fixtures());
487        uint_rlp_test!(u16_fixtures());
488        uint_rlp_test!(u32_fixtures());
489        uint_rlp_test!(u64_fixtures());
490        uint_rlp_test!(u128_fixtures());
491        #[cfg(feature = "ethnum")]
492        uint_rlp_test!(u256_fixtures());
493    }
494
495    #[cfg(feature = "ethereum-types")]
496    #[test]
497    fn rlp_eth_uints() {
498        uint_rlp_test!(eth_u64_fixtures());
499        uint_rlp_test!(eth_u128_fixtures());
500        uint_rlp_test!(eth_u256_fixtures());
501        uint_rlp_test!(eth_u512_fixtures());
502    }
503
504    #[test]
505    fn rlp_list() {
506        assert_eq!(encoded_list::<u64>(&[]), &hex!("c0")[..]);
507        assert_eq!(encoded_list(&[0xFFCCB5_u64, 0xFFC0B5_u64]), &hex!("c883ffccb583ffc0b5")[..]);
508    }
509}