arbos_types/
rlp.rs

1use alloy_rlp::{Decodable, EMPTY_LIST_CODE, EMPTY_STRING_CODE, Encodable, bytes::BufMut};
2
3/// Optional field, which encodes `None` as empty list (`0xC0`).
4///
5/// `T` must never be encodable to `0xC0` since it will resolve to `None`.
6#[derive(Debug, Clone, PartialEq, Eq, Default)]
7pub struct NilList<T>(pub Option<T>);
8
9impl<T: Encodable> Encodable for NilList<T> {
10    fn encode(&self, out: &mut dyn BufMut) {
11        match &self.0 {
12            None => out.put_u8(EMPTY_LIST_CODE), // 0xC0
13            Some(v) => v.encode(out),
14        }
15    }
16
17    fn length(&self) -> usize {
18        match &self.0 {
19            None => 1,
20            Some(v) => v.length(),
21        }
22    }
23}
24
25impl<T: Decodable> Decodable for NilList<T> {
26    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
27        if buf.first() == Some(&EMPTY_LIST_CODE) {
28            *buf = &buf[1..];
29            return Ok(Self(None));
30        }
31        Ok(Self(Some(T::decode(buf)?)))
32    }
33}
34
35/// Optional field, which encodes `None` as empty string (`0x80`).
36///
37/// `T` must never be encodable to `0x80` since it will resolve to `None`.
38#[derive(Debug, Clone, PartialEq, Eq, Default)]
39pub struct NilString<T>(pub Option<T>);
40
41impl<T: Encodable> Encodable for NilString<T> {
42    fn encode(&self, out: &mut dyn BufMut) {
43        match &self.0 {
44            None => out.put_u8(EMPTY_STRING_CODE), // 0x80
45            Some(v) => v.encode(out),
46        }
47    }
48
49    fn length(&self) -> usize {
50        match &self.0 {
51            None => 1,
52            Some(v) => v.length(),
53        }
54    }
55}
56
57impl<T: Decodable> Decodable for NilString<T> {
58    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
59        if buf.first() == Some(&EMPTY_STRING_CODE) {
60            *buf = &buf[1..];
61            return Ok(Self(None));
62        }
63        Ok(Self(Some(T::decode(buf)?)))
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use alloy_primitives::B256;
70    use alloy_rlp::{Decodable, Encodable};
71
72    use super::*;
73
74    fn enc<T: Encodable>(v: &T) -> Vec<u8> {
75        let mut b = Vec::new();
76        v.encode(&mut b);
77        b
78    }
79
80    #[test]
81    fn nil_list_none_is_empty_list_code() {
82        let none: NilList<B256> = NilList(None);
83        assert_eq!(enc(&none), [EMPTY_LIST_CODE]);
84        assert_eq!(NilList::<B256>::decode(&mut &enc(&none)[..]).unwrap(), none);
85    }
86
87    #[test]
88    fn nil_list_some_roundtrips() {
89        let some = NilList(Some(B256::repeat_byte(0xAB)));
90        let bytes = enc(&some);
91        // 32-byte string: 0xa0 header followed by the raw hash.
92        assert_eq!(bytes[0], 0xa0);
93        assert_eq!(&bytes[1..], B256::repeat_byte(0xAB).as_slice());
94        assert_eq!(NilList::<B256>::decode(&mut &bytes[..]).unwrap(), some);
95    }
96
97    #[test]
98    fn nil_string_none_is_empty_string_code() {
99        let none: NilString<B256> = NilString(None);
100        assert_eq!(enc(&none), [EMPTY_STRING_CODE]);
101        assert_eq!(
102            NilString::<B256>::decode(&mut &enc(&none)[..]).unwrap(),
103            none
104        );
105    }
106
107    #[test]
108    fn nil_string_some_roundtrips() {
109        let some = NilString(Some(B256::repeat_byte(0x11)));
110        assert_eq!(
111            NilString::<B256>::decode(&mut &enc(&some)[..]).unwrap(),
112            some
113        );
114    }
115}