1use crate::{
2 alloc::string::ToString,
3 encoder::{align_up, read_u32_aligned, write_u32_aligned, Encoder},
4 error::{CodecError, DecodingError},
5};
6use byteorder::ByteOrder;
7use bytes::{Buf, BytesMut};
8
9impl<B: ByteOrder, const ALIGN: usize, const SOL_MODE: bool, const IS_STATIC: bool>
10 Encoder<B, ALIGN, SOL_MODE, IS_STATIC> for ()
11{
12 const HEADER_SIZE: usize = 0;
13 const IS_DYNAMIC: bool = false;
14
15 fn encode(&self, _buf: &mut BytesMut, _offset: usize) -> Result<(), CodecError> {
16 Ok(())
17 }
18
19 fn decode(_buf: &impl Buf, _offset: usize) -> Result<Self, CodecError> {
20 Ok(())
21 }
22
23 fn partial_decode(_buf: &impl Buf, _offset: usize) -> Result<(usize, usize), CodecError> {
24 Ok((0, 0))
25 }
26}
27
28impl<T, B: ByteOrder, const ALIGN: usize, const SOL_MODE: bool, const IS_STATIC: bool>
29 Encoder<B, ALIGN, SOL_MODE, IS_STATIC> for (T,)
30where
31 T: Encoder<B, ALIGN, SOL_MODE, IS_STATIC>,
32{
33 const HEADER_SIZE: usize = align_up::<ALIGN>(T::HEADER_SIZE);
34 const IS_DYNAMIC: bool = T::IS_DYNAMIC;
35
36 fn encode(&self, buf: &mut BytesMut, offset: usize) -> Result<(), CodecError> {
37 let mut current_offset = offset;
38 let header_el_size = if SOL_MODE {
39 align_up::<ALIGN>(32)
40 } else {
41 align_up::<ALIGN>(4)
42 };
43 if Self::IS_DYNAMIC {
44 let buf_len = buf.len();
45 let dynamic_offset = if buf_len == 0 {
46 header_el_size
47 } else {
48 buf_len
49 };
50 write_u32_aligned::<B, ALIGN>(buf, current_offset, dynamic_offset as u32);
51 current_offset += header_el_size;
52
53 let aligned_header_size = align_up::<ALIGN>(T::HEADER_SIZE);
54 if buf_len < current_offset + aligned_header_size {
55 buf.resize(current_offset + aligned_header_size, 0);
56 }
57 let mut tmp = buf.split_off(current_offset);
58
59 self.0.encode(&mut tmp, 0)?;
60 buf.unsplit(tmp);
61 } else {
62 self.0.encode(buf, current_offset)?;
63 }
64
65 Ok(())
66 }
67
68 fn decode(buf: &impl Buf, offset: usize) -> Result<Self, CodecError> {
69 let chunk = if Self::IS_DYNAMIC {
70 let dynamic_offset = read_u32_aligned::<B, ALIGN>(&buf.chunk(), offset)? as usize;
71 &buf.chunk()[dynamic_offset..]
72 } else {
73 &buf.chunk()[offset..]
74 };
75
76 Ok((T::decode(&chunk, 0)?,))
77 }
78
79 fn partial_decode(buf: &impl Buf, offset: usize) -> Result<(usize, usize), CodecError> {
80 T::partial_decode(buf, offset)
81 }
82}
83const WORD_SIZE_SOL: usize = 32;
84const WORD_SIZE_DEFAULT: usize = 4;
85
86const fn is_power_of_two(n: usize) -> bool {
87 n != 0 && (n & (n - 1)) == 0
88}
89
90macro_rules! impl_encoder_for_tuple {
91 ($($T:ident),+; $($idx:tt),+; $is_solidity:expr) => {
92 #[allow(unused_assignments)]
93 impl<B: ByteOrder, const ALIGN: usize, const IS_STATIC: bool, $($T,)+>
94 Encoder<B, ALIGN, $is_solidity, IS_STATIC> for ($($T,)+)
95 where
96 $($T: Encoder<B, ALIGN, $is_solidity, IS_STATIC>,)+
97 {
98 const HEADER_SIZE: usize = {
99 let mut size = 0;
100 $(
101 size = align_up::<ALIGN>(size);
102 size += $T::HEADER_SIZE;
103 )+
104 align_up::<ALIGN>(size)
105 };
106
107 const IS_DYNAMIC: bool = {
108 let mut is_dynamic = false;
109 $(
110 is_dynamic |= $T::IS_DYNAMIC;
111 )+
112 is_dynamic
113 };
114
115
116 fn encode(&self, buf: &mut BytesMut, offset: usize) -> Result<(), CodecError> {
117 assert!(is_power_of_two(ALIGN), "ALIGN must be a power of two");
118
119
120 if $is_solidity {
121 let aligned_offset = align_up::<ALIGN>(offset);
123 let is_dynamic = Self::IS_DYNAMIC;
124
125 let aligned_header_size = {
126 let mut size = 0;
127 $(
128 size += if $T::IS_DYNAMIC {
129 align_up::<ALIGN>(4)
130 } else {
131 align_up::<ALIGN>($T::HEADER_SIZE)
132 };
133 )+
134 size
135 };
136
137 let mut tail = if is_dynamic {
138 let buf_len = buf.len();
139 let offset = if buf_len == 0 { align_up::<ALIGN>(4) } else { buf_len };
140 write_u32_aligned::<B, ALIGN>(buf, aligned_offset, offset as u32);
141 if buf.len() < aligned_header_size + offset {
142 buf.resize(aligned_header_size + offset, 0);
143 }
144 buf.split_off(offset)
145 } else {
146 if buf.len() < aligned_offset + aligned_header_size {
147 buf.resize(aligned_offset + aligned_header_size, 0);
148 }
149 buf.split_off(aligned_offset)
150 };
151
152 let mut tail_offset = 0;
153 $(
154 if $T::IS_DYNAMIC {
155 self.$idx.encode(&mut tail, tail_offset)?;
156 tail_offset += align_up::<ALIGN>(4);
157 } else {
158 self.$idx.encode(&mut tail, tail_offset)?;
159 tail_offset += align_up::<ALIGN>($T::HEADER_SIZE);
160 }
161 )+
162
163 buf.unsplit(tail);
164 } else {
165 let mut current_offset = offset;
167 let header_el_size = align_up::<ALIGN>(4);
168
169 if Self::IS_DYNAMIC {
170 let buf_len = buf.len();
171 let dynamic_offset = if buf_len == 0 {
172 header_el_size
173 } else {
174 buf_len
175 };
176 write_u32_aligned::<B, ALIGN>(buf, current_offset, dynamic_offset as u32);
177 current_offset += header_el_size;
178
179 let aligned_header_size = {
180 let mut size = 0;
181 $(
182 size += align_up::<ALIGN>($T::HEADER_SIZE);
183 )+
184 size
185 };
186
187 if buf_len < current_offset + aligned_header_size {
188 buf.resize(current_offset + aligned_header_size, 0);
189 }
190 let mut tmp = buf.split_off(current_offset);
191
192 let mut current_tmp_offset = 0;
193 $(
194 self.$idx.encode(&mut tmp, current_tmp_offset)?;
195 current_tmp_offset += align_up::<ALIGN>($T::HEADER_SIZE);
196 )+
197
198 buf.unsplit(tmp);
199 } else {
200 let mut current_field_offset = current_offset;
201 $(
202 self.$idx.encode(buf, current_field_offset)?;
203 current_field_offset += align_up::<ALIGN>($T::HEADER_SIZE);
204 )+
205 }
206 }
207
208 Ok(())
209 }
210
211 fn decode(buf: &impl Buf, offset: usize) -> Result<Self, CodecError> {
212 if buf.remaining() < offset {
213 return Err(CodecError::Decoding(DecodingError::BufferTooSmall {
214 expected: offset,
215 found: buf.remaining(),
216 msg: "buf too small to take offset".to_string(),
217 }));
218 }
219
220 let word_size = if $is_solidity { WORD_SIZE_SOL } else { WORD_SIZE_DEFAULT };
221
222 let tmp = if Self::IS_DYNAMIC {
223 let dynamic_offset = read_u32_aligned::<B, ALIGN>(&buf.chunk(), offset)? as usize;
224 if buf.remaining() < dynamic_offset {
225 return Err(CodecError::Decoding(DecodingError::BufferTooSmall {
226 expected: dynamic_offset,
227 found: buf.remaining(),
228 msg: "buf too small to take dynamic offset".to_string(),
229 }));
230 }
231 &buf.chunk()[dynamic_offset..]
232 } else {
233 &buf.chunk()[offset..]
234 };
235
236 let mut _current_offset = 0;
237
238 Ok(($(
239 {
240 let value = $T::decode(&tmp, _current_offset)?;
241 _current_offset += if $T::IS_DYNAMIC && $is_solidity {
242 word_size
243 } else {
244 align_up::<ALIGN>($T::HEADER_SIZE)
245 };
246 value
247 },
248 )+))
249 }
250
251 fn partial_decode(_buf: &impl Buf, _offset: usize) -> Result<(usize, usize), CodecError> {
252 Ok((0,0))
253 }
254 }
255 };
256}
257
258impl_encoder_for_tuple!(T1, T2; 0, 1; true);
259impl_encoder_for_tuple!(T1, T2; 0, 1; false);
260impl_encoder_for_tuple!(T1, T2, T3; 0, 1, 2; true);
261impl_encoder_for_tuple!(T1, T2, T3; 0, 1, 2; false);
262impl_encoder_for_tuple!(T1, T2, T3, T4; 0, 1, 2, 3; true);
263impl_encoder_for_tuple!(T1, T2, T3, T4; 0, 1, 2, 3; false);
264impl_encoder_for_tuple!(T1, T2, T3, T4, T5; 0, 1, 2, 3, 4; true);
265impl_encoder_for_tuple!(T1, T2, T3, T4, T5; 0, 1, 2, 3, 4; false);
266impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6; 0, 1, 2, 3, 4, 5; true);
267impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6; 0, 1, 2, 3, 4, 5; false);
268impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7; 0, 1, 2, 3, 4, 5, 6; true);
269impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7; 0, 1, 2, 3, 4, 5, 6; false);
270impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8; 0, 1, 2, 3, 4, 5, 6, 7; true);
271impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8; 0, 1, 2, 3, 4, 5, 6, 7; false);
272impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9; 0, 1, 2, 3, 4, 5, 6, 7, 8; true);
273impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9; 0, 1, 2, 3, 4, 5, 6, 7, 8; false);
274impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9; true);
275impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9; false);
276impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; true);
277impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; false);
278impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11; true);
279impl_encoder_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11; false);
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use crate::CompactABI;
285 use alloy_primitives::{address, Address, U256};
286 use bytes::BytesMut;
287
288 #[test]
289 fn test_empty_tuple() {
290 let t = ();
291 let mut buf = BytesMut::new();
292
293 CompactABI::encode(&t, &mut buf, 0).unwrap();
294 let encoded = buf.freeze();
295 assert_eq!(hex::encode(&encoded), "");
296 let decoded: () = CompactABI::decode(&encoded, 0).unwrap();
297 assert_eq!(decoded, ());
298 }
299
300 #[test]
301 fn test_single_element_tuple() {
302 let original: (u32,) = (100u32,);
303 let mut buf = BytesMut::new();
304 CompactABI::encode(&original, &mut buf, 0).unwrap();
305
306 let encoded = buf.freeze();
307 assert_eq!(hex::encode(&encoded), "64000000");
308
309 let decoded: (u32,) = CompactABI::decode(&encoded, 0).unwrap();
310 assert_eq!(decoded, original);
311 }
312
313 #[test]
314 fn test_simple_tuple() {
315 type Tuple = (u32, u16);
316 let original: Tuple = (100u32, 20u16);
317 let mut buf = BytesMut::new();
318 CompactABI::encode(&original, &mut buf, 0).unwrap();
319
320 let encoded = buf.freeze();
321 println!("{:?}", encoded);
322 assert_eq!(hex::encode(&encoded), "6400000014000000");
323
324 let decoded: Tuple = CompactABI::decode(&encoded, 0).unwrap();
325 assert_eq!(decoded, original);
326 }
327
328 #[test]
329 fn test_big_tuple() {
330 type Tuple = (u32, u16, u8, u64, u32, u16, u8, u64);
331 let original: Tuple = (100u32, 20u16, 30u8, 40u64, 50u32, 60u16, 70u8, 80u64);
332 let mut buf = BytesMut::new();
333 CompactABI::encode(&original, &mut buf, 0).unwrap();
334
335 let encoded = buf.freeze();
336 println!("{:?}", hex::encode(&encoded));
337 assert_eq!(
338 hex::encode(&encoded),
339 "64000000140000001e0000002800000000000000320000003c000000460000005000000000000000"
340 );
341
342 let decoded: Tuple = CompactABI::decode(&encoded, 0).unwrap();
343 assert_eq!(decoded, original);
344 }
345
346 #[test]
347 fn test_complex_tuple_fluent() {
348 let msg = "Hello World".to_string();
349 let contract_address = address!("f91c20c0cafbfdc150adff51bbfc5808edde7cb5");
350 let value = U256::from(0);
351 let gas_limit = 21_000;
352
353 type TestTuple = (Address, U256, u64, String);
354 let original: TestTuple = (contract_address, value, gas_limit, msg);
355
356 let mut buf = BytesMut::new();
357 CompactABI::encode(&original, &mut buf, 0).unwrap();
358
359 let encoded = buf.freeze();
360 println!("Encoded: {}", hex::encode(&encoded));
361 let expected_encoded = "04000000f91c20c0cafbfdc150adff51bbfc5808edde7cb500000000000000000000000000000000000000000000000000000000000000000852000000000000440000000b00000048656c6c6f20576f726c6400";
362
363 assert_eq!(hex::encode(&encoded), expected_encoded);
364 let decoded: TestTuple = CompactABI::decode(&encoded, 0).unwrap();
365 assert_eq!(decoded, original);
366 }
367}