1use crate::{
2 alloc::string::ToString,
3 encoder::{align_up, read_u32_aligned, write_u32_aligned},
4 error::{CodecError, DecodingError},
5};
6use byteorder::ByteOrder;
7use bytes::{Buf, Bytes, BytesMut};
8use core::mem;
9
10pub fn write_bytes<B, const ALIGN: usize, const SOL_MODE: bool>(
42 buf: &mut BytesMut,
43 offset: usize,
44 data: &[u8],
45 elements: u32, ) -> usize
47where
48 B: ByteOrder,
49{
50 if SOL_MODE {
51 write_bytes_solidity::<B, ALIGN>(buf, offset, data, elements)
52 } else {
53 write_bytes_wasm::<B, ALIGN>(buf, offset, data)
54 }
55}
56
57pub fn write_bytes_solidity<B: ByteOrder, const ALIGN: usize>(
59 buf: &mut BytesMut,
60 offset: usize,
61 data: &[u8],
62 elements: u32, ) -> usize {
64 if buf.len() < offset {
67 buf.resize(offset, 0);
68 }
69 let data_offset = buf.len();
70
71 write_u32_aligned::<B, ALIGN>(buf, data_offset, elements);
73
74 buf.extend_from_slice(data);
76
77 buf.len() - data_offset
79}
80
81pub fn write_bytes_wasm<B: ByteOrder, const ALIGN: usize>(
83 buf: &mut BytesMut,
84 offset: usize,
85 data: &[u8],
86) -> usize {
87 let aligned_elem_size = align_up::<ALIGN>(mem::size_of::<u32>());
88 let aligned_header_size = aligned_elem_size * 2;
89
90 if buf.len() < offset + aligned_header_size {
92 buf.resize(offset + aligned_header_size, 0);
93 }
94
95 let data_offset = buf.len();
97
98 write_u32_aligned::<B, ALIGN>(buf, offset, data_offset as u32);
100 write_u32_aligned::<B, ALIGN>(buf, offset + aligned_elem_size, data.len() as u32);
101
102 buf.extend_from_slice(data);
104
105 buf.len() - data_offset
106}
107
108pub fn read_bytes<B: ByteOrder, const ALIGN: usize, const SOL_MODE: bool>(
109 buf: &impl Buf,
110 offset: usize,
111) -> Result<Bytes, CodecError> {
112 let (data_offset, data_len) = read_bytes_header::<B, ALIGN, SOL_MODE>(buf, offset)?;
113
114 let data = if SOL_MODE {
115 buf.chunk()[data_offset + 32..data_offset + 32 + data_len].to_vec()
116 } else {
117 buf.chunk()[data_offset..data_offset + data_len].to_vec()
118 };
119
120 Ok(Bytes::from(data))
121}
122
123pub fn read_bytes_header<B: ByteOrder, const ALIGN: usize, const SOL_MODE: bool>(
126 buf: &impl Buf,
127 offset: usize,
128) -> Result<(usize, usize), CodecError> {
129 match SOL_MODE {
130 true => read_bytes_header_solidity::<B, ALIGN>(buf, offset),
131 false => read_bytes_header_wasm::<B, ALIGN>(buf, offset),
132 }
133}
134
135pub fn read_bytes_header_wasm<B: ByteOrder, const ALIGN: usize>(
136 buffer: &impl Buf,
137 offset: usize,
138) -> Result<(usize, usize), CodecError> {
139 let aligned_elem_size = align_up::<ALIGN>(mem::size_of::<u32>());
140
141 if buffer.remaining() < offset + aligned_elem_size * 2 {
142 return Err(CodecError::Decoding(DecodingError::BufferTooSmall {
143 expected: offset + aligned_elem_size * 2,
144 found: buffer.remaining(),
145 msg: "buffer too small to read bytes header".to_string(),
146 }));
147 }
148
149 let data_offset = read_u32_aligned::<B, ALIGN>(buffer, offset)? as usize;
150
151 let data_len = read_u32_aligned::<B, ALIGN>(buffer, offset + aligned_elem_size)? as usize;
152
153 Ok((data_offset, data_len))
154}
155
156pub fn read_bytes_header_solidity<B: ByteOrder, const ALIGN: usize>(
192 buf: &impl Buf,
193 offset: usize,
194) -> Result<(usize, usize), CodecError> {
195 let data_offset = read_u32_aligned::<B, ALIGN>(buf, offset)? as usize;
196
197 let data_len = read_u32_aligned::<B, ALIGN>(buf, data_offset)? as usize;
198
199 Ok((data_offset, data_len))
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use crate::encoder::{CompactABI, SolidityABI};
206 use alloy_sol_types::{sol_data, SolType};
207 use byteorder::{BigEndian, LE};
208
209 #[test]
210 fn test_write_bytes_sol() {
211 let mut buf = BytesMut::new();
212
213 let bytes: &[u8] = &[1, 2, 3, 4, 5];
215 let written = write_bytes_solidity::<BigEndian, 32>(&mut buf, 0, bytes, bytes.len() as u32);
216 assert_eq!(written, 37); let expected = [
218 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
219 0, 0, 5, 1, 2, 3, 4, 5,
220 ];
221
222 assert_eq!(buf.to_vec(), expected);
223 let mut buf = BytesMut::new();
224
225 let offset = buf.len();
226
227 let vec_u32 = [0u8, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30];
230
231 let written = write_bytes_solidity::<BigEndian, 32>(&mut buf, offset, &vec_u32, 3);
232 assert_eq!(written, 44); let expected = [
235 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
236 0, 0, 3, 0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30,
237 ];
238 assert_eq!(buf.to_vec(), expected);
239 }
240
241 #[test]
242 fn test_read_bytes_header_solidity_simple() {
243 let original = alloy_primitives::Bytes::from(vec![1, 2, 3, 4, 5]);
244
245 let mut buf = BytesMut::new();
246 SolidityABI::encode(&original, &mut buf, 0).unwrap();
247
248 let encoded = buf.freeze();
249
250 let encoded_alloy = &sol_data::Bytes::abi_encode(&original)[..];
251
252 println!("alloy encoded: {:?}", encoded_alloy);
253 println!("encoded: {:?}", hex::encode(&encoded));
254 let (offset, size) = read_bytes_header::<BigEndian, 32, true>(&encoded, 0).unwrap();
255
256 println!("Offset: {}, Size: {}", offset, size);
257
258 assert_eq!(offset, 32);
259 assert_eq!(size, 5);
260 }
261
262 #[test]
263 fn test_read_bytes_header_solidity_complex() {
264 let original: Vec<Vec<u32>> = vec![vec![1, 2, 3], vec![4, 5]];
265
266 let mut buf = BytesMut::new();
267 SolidityABI::encode(&original, &mut buf, 0).unwrap();
268
269 let encoded = buf.freeze();
270 println!("encoded: {:?}", hex::encode(&encoded));
271
272 let chunk = &encoded.chunk()[64..];
273
274 let (offset, size) = read_bytes_header_solidity::<BigEndian, 32>(&chunk, 0).unwrap();
276 assert_eq!(offset, 64);
277 assert_eq!(size, 3);
278
279 let (offset, size) = read_bytes_header_solidity::<BigEndian, 32>(&chunk, 32).unwrap();
281 assert_eq!(offset, 192);
282 assert_eq!(size, 2);
283 }
284
285 #[test]
286 fn test_read_bytes_header_wasm() {
287 let original = alloy_primitives::Bytes::from(vec![1, 2, 3, 4, 5]);
288
289 let mut buf = BytesMut::new();
290 CompactABI::encode(&original, &mut buf, 0).unwrap();
291
292 let encoded = buf.freeze();
293 println!("encoded: {:?}", hex::encode(&encoded));
294
295 let (offset, size) = read_bytes_header::<LE, 4, false>(&encoded, 0).unwrap();
296
297 println!("Offset: {}, Size: {}", offset, size);
298
299 assert_eq!(offset, 8);
300 assert_eq!(size, 5);
301 }
302}