Skip to main content

fluentbase_codec/
tuple.rs

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                    // Solidity mode
122                    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                    // WASM mode
166                    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}