1use crate::*;
25
26#[macro_export]
27macro_rules! four_byte_option_impl {
28 ($mod_name: ident, $type: ty) => {
29 #[allow(dead_code)]
30 mod $mod_name {
31 use super::*;
32
33 pub mod encode {
34 use super::*;
35 #[allow(unused_imports)]
36 use ssz::*;
37
38 pub fn is_ssz_fixed_len() -> bool {
39 false
40 }
41
42 pub fn ssz_fixed_len() -> usize {
43 BYTES_PER_LENGTH_OFFSET
44 }
45
46 pub fn ssz_bytes_len(opt: &Option<$type>) -> usize {
47 if let Some(some) = opt {
48 let len = if <$type as Encode>::is_ssz_fixed_len() {
49 <$type as Encode>::ssz_fixed_len()
50 } else {
51 <$type as Encode>::ssz_bytes_len(some)
52 };
53 len + BYTES_PER_LENGTH_OFFSET
54 } else {
55 BYTES_PER_LENGTH_OFFSET
56 }
57 }
58
59 pub fn ssz_append(opt: &Option<$type>, buf: &mut Vec<u8>) {
60 match opt {
61 None => buf.extend_from_slice(&legacy::encode_four_byte_union_selector(0)),
62 Some(t) => {
63 buf.extend_from_slice(&legacy::encode_four_byte_union_selector(1));
64 t.ssz_append(buf);
65 }
66 }
67 }
68
69 pub fn as_ssz_bytes(opt: &Option<$type>) -> Vec<u8> {
70 let mut buf = vec![];
71
72 ssz_append(opt, &mut buf);
73
74 buf
75 }
76 }
77
78 pub mod decode {
79 use super::*;
80 #[allow(unused_imports)]
81 use ssz::*;
82
83 pub fn is_ssz_fixed_len() -> bool {
84 false
85 }
86
87 pub fn ssz_fixed_len() -> usize {
88 BYTES_PER_LENGTH_OFFSET
89 }
90
91 pub fn from_ssz_bytes(bytes: &[u8]) -> Result<Option<$type>, DecodeError> {
92 if bytes.len() < BYTES_PER_LENGTH_OFFSET {
93 return Err(DecodeError::InvalidByteLength {
94 len: bytes.len(),
95 expected: BYTES_PER_LENGTH_OFFSET,
96 });
97 }
98
99 let (index_bytes, value_bytes) = bytes.split_at(BYTES_PER_LENGTH_OFFSET);
100
101 let index = legacy::read_four_byte_union_selector(index_bytes)?;
102 if index == 0 {
103 Ok(None)
104 } else if index == 1 {
105 Ok(Some(<$type as ssz::Decode>::from_ssz_bytes(value_bytes)?))
106 } else {
107 Err(DecodeError::BytesInvalid(format!(
108 "{} is not a valid union index for Option<T>",
109 index
110 )))
111 }
112 }
113 }
114 }
115 };
116}
117
118pub fn encode_four_byte_union_selector(selector: usize) -> [u8; BYTES_PER_LENGTH_OFFSET] {
119 encode_length(selector)
120}
121
122pub fn read_four_byte_union_selector(bytes: &[u8]) -> Result<usize, DecodeError> {
123 read_offset(bytes)
124}
125
126#[cfg(test)]
127mod test {
128 use super::*;
129 use crate as ssz;
130 use ssz_derive::{Decode, Encode};
131
132 type VecU16 = Vec<u16>;
133
134 four_byte_option_impl!(impl_u16, u16);
135 four_byte_option_impl!(impl_vec_u16, VecU16);
136
137 #[test]
138 fn ssz_encode_option_u16() {
139 let item = Some(65535_u16);
140 let bytes = vec![1, 0, 0, 0, 255, 255];
141 assert_eq!(impl_u16::encode::as_ssz_bytes(&item), bytes);
142 assert_eq!(impl_u16::decode::from_ssz_bytes(&bytes).unwrap(), item);
143
144 let item = None;
145 let bytes = vec![0, 0, 0, 0];
146 assert_eq!(impl_u16::encode::as_ssz_bytes(&item), bytes);
147 assert_eq!(impl_u16::decode::from_ssz_bytes(&bytes).unwrap(), None);
148 }
149
150 #[test]
151 fn ssz_encode_option_vec_u16() {
152 let item = Some(vec![0_u16, 1]);
153 let bytes = vec![1, 0, 0, 0, 0, 0, 1, 0];
154 assert_eq!(impl_vec_u16::encode::as_ssz_bytes(&item), bytes);
155 assert_eq!(impl_vec_u16::decode::from_ssz_bytes(&bytes).unwrap(), item);
156
157 let item = None;
158 let bytes = vec![0, 0, 0, 0];
159 assert_eq!(impl_vec_u16::encode::as_ssz_bytes(&item), bytes);
160 assert_eq!(impl_vec_u16::decode::from_ssz_bytes(&bytes).unwrap(), item);
161 }
162
163 fn round_trip<T: Encode + Decode + std::fmt::Debug + PartialEq>(items: Vec<T>) {
164 for item in items {
165 let encoded = &item.as_ssz_bytes();
166 assert_eq!(item.ssz_bytes_len(), encoded.len());
167 assert_eq!(T::from_ssz_bytes(encoded), Ok(item));
168 }
169 }
170
171 #[derive(Debug, PartialEq, Encode, Decode)]
172 struct TwoVariableLenOptions {
173 a: u16,
174 #[ssz(with = "impl_u16")]
175 b: Option<u16>,
176 #[ssz(with = "impl_vec_u16")]
177 c: Option<Vec<u16>>,
178 #[ssz(with = "impl_vec_u16")]
179 d: Option<Vec<u16>>,
180 }
181
182 #[test]
183 #[allow(clippy::zero_prefixed_literal)]
184 fn two_variable_len_options_encoding() {
185 let s = TwoVariableLenOptions {
186 a: 42,
187 b: None,
188 c: Some(vec![0]),
189 d: None,
190 };
191
192 let bytes = vec![
193 42, 00, 14, 00, 00, 00, 18, 00, 00, 00, 24, 00, 00, 00, 00, 00, 00, 00, 01, 00, 00, 00,
196 00, 00, 00, 00, 00, 00,
199 ];
200
201 assert_eq!(s.as_ssz_bytes(), bytes);
202 }
203
204 #[test]
205 fn two_variable_len_options_round_trip() {
206 let vec: Vec<TwoVariableLenOptions> = vec![
207 TwoVariableLenOptions {
208 a: 42,
209 b: Some(12),
210 c: Some(vec![0]),
211 d: Some(vec![1]),
212 },
213 TwoVariableLenOptions {
214 a: 42,
215 b: Some(12),
216 c: Some(vec![0]),
217 d: None,
218 },
219 TwoVariableLenOptions {
220 a: 42,
221 b: None,
222 c: Some(vec![0]),
223 d: None,
224 },
225 TwoVariableLenOptions {
226 a: 42,
227 b: None,
228 c: None,
229 d: None,
230 },
231 ];
232
233 round_trip(vec);
234 }
235
236 #[test]
237 fn tuple_u8_u16() {
238 let vec: Vec<(u8, u16)> = vec![
239 (0, 0),
240 (0, 1),
241 (1, 0),
242 (u8::max_value(), u16::max_value()),
243 (0, u16::max_value()),
244 (u8::max_value(), 0),
245 (42, 12301),
246 ];
247
248 round_trip(vec);
249 }
250
251 #[test]
252 fn tuple_vec_vec() {
253 let vec: Vec<(u64, Vec<u8>, Vec<Vec<u16>>)> = vec![
254 (0, vec![], vec![vec![]]),
255 (99, vec![101], vec![vec![], vec![]]),
256 (
257 42,
258 vec![12, 13, 14],
259 vec![vec![99, 98, 97, 96], vec![42, 44, 46, 48, 50]],
260 ),
261 ];
262
263 round_trip(vec);
264 }
265}