1use crate::func::FunctionArgs;
2use crate::{alloc::string::ToString, error::CodecError};
3use byteorder::{ByteOrder, BE, LE};
4use bytes::{Buf, BytesMut};
5use core::marker::PhantomData;
6pub trait Encoder<B: ByteOrder, const ALIGN: usize, const SOL_MODE: bool, const IS_STATIC: bool>:
21 Sized
22{
23 const HEADER_SIZE: usize;
25 const IS_DYNAMIC: bool;
26
27 fn encode(&self, buf: &mut BytesMut, offset: usize) -> Result<(), CodecError>;
36
37 fn decode(buf: &impl Buf, offset: usize) -> Result<Self, CodecError>;
46
47 fn partial_decode(buf: &impl Buf, offset: usize) -> Result<(usize, usize), CodecError>;
56
57 fn size_hint(&self) -> usize {
62 align_up::<ALIGN>(Self::HEADER_SIZE)
63 }
64}
65
66macro_rules! define_encoder_mode {
67 ($name:ident, $byte_order:ty, $align:expr, $sol_mode:expr) => {
68 pub struct $name<T>(PhantomData<T>);
69
70 impl<T> $name<T>
71 where
72 T: Encoder<$byte_order, $align, $sol_mode, false>,
73 {
74 pub fn is_dynamic() -> bool {
75 <T as Encoder<$byte_order, $align, $sol_mode, false>>::IS_DYNAMIC
76 }
77
78 pub fn encode(value: &T, buf: &mut BytesMut, offset: usize) -> Result<(), CodecError> {
79 value.encode(buf, offset)
80 }
81
82 pub fn decode(buf: &impl Buf, offset: usize) -> Result<T, CodecError> {
83 T::decode(buf, offset)
84 }
85
86 pub fn partial_decode(
87 buf: &impl Buf,
88 offset: usize,
89 ) -> Result<(usize, usize), CodecError> {
90 T::partial_decode(buf, offset)
91 }
92
93 pub fn size_hint(value: &T) -> usize {
94 value.size_hint()
95 }
96 }
97 };
98 ($name:ident, $byte_order:ty, $align:expr, $sol_mode:expr, static_only) => {
99 pub struct $name<T>(PhantomData<T>);
100
101 impl<T> $name<T>
102 where
103 T: Encoder<$byte_order, $align, $sol_mode, true>,
104 {
105 pub fn is_dynamic() -> bool {
106 T::IS_DYNAMIC
107 }
108
109 pub fn encode(value: &T, buf: &mut BytesMut, offset: usize) -> Result<(), CodecError> {
110 value.encode(buf, offset)
111 }
112
113 pub fn decode(buf: &impl Buf, offset: usize) -> Result<T, CodecError> {
114 T::decode(buf, offset)
115 }
116
117 pub fn partial_decode(
118 buf: &impl Buf,
119 offset: usize,
120 ) -> Result<(usize, usize), CodecError> {
121 T::partial_decode(buf, offset)
122 }
123
124 pub fn size_hint(value: &T) -> usize {
125 value.size_hint()
126 }
127 }
128 };
129}
130
131define_encoder_mode!(SolidityABI, BE, 32, true);
132define_encoder_mode!(CompactABI, LE, 4, false);
133
134define_encoder_mode!(SolidityPackedABI, BE, 1, true, static_only);
136
137impl<T> SolidityABI<T> {
138 pub fn encode_function_args(value: &T, buf: &mut BytesMut) -> Result<(), CodecError>
139 where
140 T: FunctionArgs<BE, 32, true, false>,
141 {
142 value.encode_as_args(buf)
143 }
144
145 pub fn decode_function_args(buf: &impl Buf) -> Result<T, CodecError>
146 where
147 T: FunctionArgs<BE, 32, true, false>,
148 {
149 T::decode_as_args(buf)
150 }
151}
152
153impl<T> CompactABI<T> {
154 pub fn encode_function_args(value: &T, buf: &mut BytesMut) -> Result<(), CodecError>
155 where
156 T: FunctionArgs<LE, 4, false, false>,
157 {
158 value.encode_as_args(buf)
159 }
160
161 pub fn decode_function_args(buf: &impl Buf) -> Result<T, CodecError>
162 where
163 T: FunctionArgs<LE, 4, false, false>,
164 {
165 T::decode_as_args(buf)
166 }
167}
168pub trait SolidityEncoder: Encoder<BE, 32, true, false> {
169 const SOLIDITY_HEADER_SIZE: usize = <Self as Encoder<BE, 32, true, false>>::HEADER_SIZE;
170}
171
172impl<T> SolidityEncoder for T where T: Encoder<BE, 32, true, false> {}
173
174pub trait SolidityPackedEncoder: Encoder<BE, 1, true, true> {
175 const SOLIDITY_PACKED_HEADER_SIZE: usize = <Self as Encoder<BE, 1, true, true>>::HEADER_SIZE;
176}
177
178impl<T> SolidityPackedEncoder for T where T: Encoder<BE, 1, true, true> {}
179
180pub trait FluentEncoder: Encoder<LE, 4, false, false> {
181 const FLUENT_HEADER_SIZE: usize = <Self as Encoder<LE, 4, false, false>>::HEADER_SIZE;
182}
183
184impl<T> FluentEncoder for T where T: Encoder<LE, 4, false, false> {}
185
186pub fn is_big_endian<B: ByteOrder>() -> bool {
188 B::read_u16(&[0x12, 0x34]) == 0x1234
189}
190
191#[inline]
194pub const fn align_up<const ALIGN: usize>(offset: usize) -> usize {
195 (offset + ALIGN - 1) & !(ALIGN - 1)
196}
197
198pub fn is_dynamic<
200 T: Encoder<B, ALIGN, SOL_MODE, IS_STATIC>,
201 B: ByteOrder,
202 const ALIGN: usize,
203 const SOL_MODE: bool,
204 const IS_STATIC: bool,
205>() -> bool {
206 T::IS_DYNAMIC
207}
208
209pub fn write_u32_aligned<B: ByteOrder, const ALIGN: usize>(
210 buf: &mut BytesMut,
211 offset: usize,
212 value: u32,
213) {
214 let aligned_value_size = align_up::<ALIGN>(4);
215
216 ensure_buf_size(buf, offset + aligned_value_size);
217
218 if is_big_endian::<B>() {
219 let start = offset + aligned_value_size - 4;
221 B::write_u32(&mut buf[start..], value);
222 } else {
223 B::write_u32(&mut buf[offset..offset + 4], value);
225 }
226}
227
228pub fn read_u32_aligned<B: ByteOrder, const ALIGN: usize>(
229 buf: &impl Buf,
230 offset: usize,
231) -> Result<u32, CodecError> {
232 let aligned_value_size = align_up::<ALIGN>(4);
233
234 let end_offset = offset.checked_add(aligned_value_size).ok_or_else(|| {
236 CodecError::Decoding(crate::error::DecodingError::BufferOverflow {
237 msg: "Overflow occurred when calculating end offset while reading aligned u32"
238 .to_string(),
239 })
240 })?;
241
242 if buf.remaining() < end_offset {
243 return Err(CodecError::Decoding(
244 crate::error::DecodingError::BufferTooSmall {
245 expected: end_offset,
246 found: buf.remaining(),
247 msg: "Buffer underflow occurred while reading aligned u32".to_string(),
248 },
249 ));
250 }
251
252 if is_big_endian::<B>() {
253 Ok(B::read_u32(&buf.chunk()[end_offset - 4..end_offset]))
254 } else {
255 Ok(B::read_u32(&buf.chunk()[offset..offset + 4]))
256 }
257}
258
259pub(crate) fn get_aligned_slice<B: ByteOrder, const ALIGN: usize>(
262 buf: &mut BytesMut,
263 offset: usize,
264 value_size: usize,
265) -> &mut [u8] {
266 let aligned_offset = align_up::<ALIGN>(offset);
267 let word_size = align_up::<ALIGN>(ALIGN.max(value_size));
268
269 ensure_buf_size(buf, aligned_offset + word_size);
271
272 let write_offset = if is_big_endian::<B>() {
273 aligned_offset + word_size - value_size
275 } else {
276 aligned_offset
278 };
279
280 &mut buf[write_offset..write_offset + value_size]
281}
282
283pub(crate) fn get_aligned_indices<B: ByteOrder, const ALIGN: usize>(
284 offset: usize,
285 value_size: usize,
286) -> (usize, usize) {
287 let aligned_offset = align_up::<ALIGN>(offset);
288 let word_size = align_up::<ALIGN>(ALIGN.max(value_size));
289
290 let write_offset = if is_big_endian::<B>() {
291 aligned_offset + word_size - value_size
293 } else {
294 aligned_offset
296 };
297
298 (write_offset, write_offset + value_size)
299}
300
301pub fn ensure_buf_size(buf: &mut BytesMut, required_size: usize) {
303 if buf.len() < required_size {
304 buf.resize(required_size, 0);
305 }
306}