bonfida_utils/wrapped_pod.rs
1pub trait WrappedPod<'a>: Sized {
2 fn export(&self, buffer: &mut Vec<u8>);
3 fn size(&self) -> usize;
4 fn from_bytes(buffer: &'a [u8]) -> Self;
5 #[allow(unused_variables)]
6 fn try_from_bytes(buffer: &'a [u8]) -> Result<Self, std::io::Error> {
7 // Default implementation for backward comp
8 Err(std::io::Error::new(
9 std::io::ErrorKind::Unsupported,
10 "Not implemented yet",
11 ))
12 }
13}
14
15pub trait WrappedPodMut<'a>: Sized {
16 fn export(&self, buffer: &mut Vec<u8>);
17 fn size(&self) -> usize;
18 fn from_bytes(buffer: &'a mut [u8]) -> Self;
19 #[allow(unused_variables)]
20 fn try_from_bytes(buffer: &'a mut [u8]) -> Result<Self, std::io::Error> {
21 // Default implementation for backward comp
22 Err(std::io::Error::new(
23 std::io::ErrorKind::Unsupported,
24 "Not implemented yet",
25 ))
26 }
27}
28
29#[cfg(test)]
30pub mod tests {
31
32 use super::{WrappedPod, WrappedPodMut};
33 use bonfida_macros::{WrappedPod, WrappedPodMut};
34 use std::mem::{size_of, size_of_val};
35
36 #[derive(WrappedPodMut, PartialEq, Debug)]
37 pub struct TestStructMut<'a> {
38 pub a: &'a mut u64,
39 pub b: &'a mut [u32],
40 pub c: &'a mut [u128],
41 }
42
43 #[derive(WrappedPod, PartialEq, Debug)]
44 pub struct TestStruct<'a> {
45 pub a: &'a u64,
46 pub b: &'a [u32],
47 pub c: &'a [u128],
48 }
49
50 #[derive(WrappedPodMut, PartialEq, Debug)]
51 pub struct TestStructMutStr<'a> {
52 pub a: &'a mut u64,
53 pub b: &'a mut str,
54 pub c: &'a mut str,
55 }
56
57 #[derive(WrappedPod, PartialEq, Debug)]
58 pub struct TestStructStr<'a> {
59 pub a: &'a u64,
60 pub b: &'a str,
61 pub c: &'a str,
62 }
63
64 // #[derive(bonfida_macros_old::WrappedPodMut, PartialEq, Debug)]
65 // pub struct CompatTestStructMutOld<'a> {
66 // pub a: &'a mut u64,
67 // pub b: &'a mut [u32],
68 // }
69
70 // #[derive(bonfida_macros_old::WrappedPod, PartialEq, Debug)]
71 // pub struct CompatTestStructOld<'a> {
72 // pub a: &'a u64,
73 // pub b: &'a [u32],
74 // }
75
76 #[derive(WrappedPodMut, PartialEq, Debug)]
77 pub struct CompatTestStructMutNew<'a> {
78 pub a: &'a mut u64,
79 pub b: &'a mut [u32],
80 }
81
82 #[derive(WrappedPod, PartialEq, Debug)]
83 pub struct CompatTestStructNew<'a> {
84 pub a: &'a u64,
85 pub b: &'a [u32],
86 }
87
88 // #[derive(bonfida_macros_old::WrappedPodMut, PartialEq, Debug)]
89 // pub struct CompatTestStructMutOldStr<'a> {
90 // pub a: &'a mut u64,
91 // pub b: &'a mut str,
92 // }
93
94 // #[derive(bonfida_macros_old::WrappedPod, PartialEq, Debug)]
95 // pub struct CompatTestStructOldStr<'a> {
96 // pub a: &'a u64,
97 // pub b: &'a str,
98 // }
99
100 #[derive(WrappedPodMut, PartialEq, Debug)]
101 pub struct CompatTestStructMutNewStr<'a> {
102 pub a: &'a mut u64,
103 pub b: &'a mut str,
104 }
105
106 #[derive(WrappedPod, PartialEq, Debug)]
107 pub struct CompatTestStructNewStr<'a> {
108 pub a: &'a u64,
109 pub b: &'a str,
110 }
111
112 #[test]
113 pub fn test_mut() {
114 let a = &mut rand::random();
115 let b = &mut rand::random::<[u32; 4]>();
116 let c = &mut rand::random::<[u128; 7]>();
117 let o = TestStructMut { a, b, c };
118 let mut buf = Vec::with_capacity(o.size());
119 o.export(&mut buf);
120 assert_eq!(buf.len(), o.size());
121 assert_eq!(
122 buf.len(),
123 size_of::<u64>() + 8 + size_of_val(o.b) + size_of_val(o.c)
124 );
125 let o2 = TestStructMut::from_bytes(&mut buf);
126 assert_eq!(o2, o);
127
128 let o2_try = TestStructMut::try_from_bytes(&mut buf).unwrap();
129 assert_eq!(o2_try, o);
130 }
131
132 #[test]
133 pub fn test() {
134 let a = &rand::random();
135 let b = &rand::random::<[u32; 4]>();
136 let c = &rand::random::<[u128; 7]>();
137 let o = TestStruct { a, b, c };
138 let mut buf = Vec::with_capacity(o.size());
139 o.export(&mut buf);
140 assert_eq!(buf.len(), o.size());
141 assert_eq!(
142 buf.len(),
143 size_of::<u64>() + 8 + size_of_val(o.b) + size_of_val(o.c)
144 );
145 let o2 = TestStruct::from_bytes(&buf);
146 assert_eq!(o2, o);
147
148 let o2_try = TestStruct::try_from_bytes(&buf).unwrap();
149 assert_eq!(o2_try, o);
150 }
151
152 #[test]
153 pub fn test_mut_2() {
154 let a = &mut rand::random();
155 let b = &mut (0..10).map(|_| rand::random::<char>()).collect::<String>();
156 let c = &mut (0..15).map(|_| rand::random::<char>()).collect::<String>();
157 let o = TestStructMutStr { a, b, c };
158 let mut buf = Vec::with_capacity(o.size());
159 o.export(&mut buf);
160 assert_eq!(buf.len(), o.size());
161
162 assert_eq!(buf.len(), size_of::<u64>() + 8 + o.b.len() + o.c.len());
163 let o2 = TestStructMutStr::from_bytes(&mut buf);
164 assert_eq!(o2, o);
165
166 let o2_try = TestStructMutStr::try_from_bytes(&mut buf).unwrap();
167 assert_eq!(o2_try, o);
168 }
169
170 #[test]
171 pub fn test_2() {
172 let a = &rand::random();
173 let b = &(0..10).map(|_| rand::random::<char>()).collect::<String>();
174 let c = &(0..15).map(|_| rand::random::<char>()).collect::<String>();
175 let o = TestStructStr { a, b, c };
176 let mut buf = Vec::with_capacity(o.size());
177 o.export(&mut buf);
178 assert_eq!(buf.len(), o.size());
179 assert_eq!(buf.len(), size_of::<u64>() + 8 + o.b.len() + o.c.len());
180 let o2 = TestStructStr::from_bytes(&buf);
181 assert_eq!(o2, o);
182 let o2_try = TestStructStr::try_from_bytes(&buf).unwrap();
183 assert_eq!(o2_try, o);
184 }
185
186 // #[test]
187 // pub fn test_back_compat() {
188 // let a = &rand::random();
189 // let b = &rand::random::<[u32; 4]>();
190 // let o_old_reference = CompatTestStructOld { a, b };
191 // let o_new_reference = CompatTestStructNew { a, b };
192 // let mut buf_new = Vec::with_capacity(o_new_reference.size());
193 // o_new_reference.export(&mut buf_new);
194 // let o_old = CompatTestStructOld::from_bytes(&buf_new);
195 // assert_eq!(o_old, o_old_reference);
196
197 // let mut buf_old = Vec::with_capacity(o_old.size());
198 // o_old_reference.export(&mut buf_old);
199 // let o_new = CompatTestStructNew::from_bytes(&buf_old);
200 // assert_eq!(o_new, o_new_reference);
201
202 // let o_try = CompatTestStructNew::try_from_bytes(&buf_old).unwrap();
203 // assert_eq!(o_try, o_new_reference);
204 // }
205
206 // #[test]
207 // pub fn test_back_compat_mut() {
208 // let a: &mut u64 = &mut rand::random();
209 // let b = &mut rand::random::<[u32; 4]>();
210 // let a_clone = &mut a.clone();
211 // let b_clone = &mut b.clone();
212 // let o_old_reference = CompatTestStructMutOld {
213 // a: a_clone,
214 // b: b_clone,
215 // };
216 // let o_new_reference = CompatTestStructMutNew { a, b };
217 // let mut buf_new = Vec::with_capacity(o_new_reference.size());
218 // o_new_reference.export(&mut buf_new);
219 // let o_old = CompatTestStructMutOld::from_bytes(&mut buf_new);
220 // assert_eq!(o_old, o_old_reference);
221
222 // let mut buf_old = Vec::with_capacity(o_old.size());
223 // o_old_reference.export(&mut buf_old);
224 // {
225 // let o_new = CompatTestStructMutNew::from_bytes(&mut buf_old);
226 // assert_eq!(o_new, o_new_reference);
227 // }
228
229 // let o_try = CompatTestStructMutNew::try_from_bytes(&mut buf_old).unwrap();
230 // assert_eq!(o_try, o_new_reference)
231 // }
232
233 // #[test]
234 // pub fn test_back_compat_str() {
235 // let a = &rand::random();
236 // let b = &(0..10).map(|_| rand::random::<char>()).collect::<String>();
237 // let o_old_reference = CompatTestStructOldStr { a, b };
238 // let o_new_reference = CompatTestStructNewStr { a, b };
239 // let mut buf_new = Vec::with_capacity(o_new_reference.size());
240 // o_new_reference.export(&mut buf_new);
241 // let o_old = CompatTestStructOldStr::from_bytes(&buf_new);
242 // assert_eq!(o_old, o_old_reference);
243
244 // let mut buf_old = Vec::with_capacity(o_old.size());
245 // o_old_reference.export(&mut buf_old);
246 // let o_new = CompatTestStructNewStr::from_bytes(&buf_old);
247 // assert_eq!(o_new, o_new_reference);
248
249 // let o_try = CompatTestStructNewStr::try_from_bytes(&buf_old).unwrap();
250 // assert_eq!(o_try, o_new_reference);
251 // }
252
253 // #[test]
254 // pub fn test_back_compat_mut_str() {
255 // let a: &mut u64 = &mut rand::random();
256 // let b = &mut (0..10).map(|_| rand::random::<char>()).collect::<String>();
257 // let a_clone = &mut a.clone();
258 // let b_clone = &mut b.clone();
259 // let o_old_reference = CompatTestStructMutOldStr {
260 // a: a_clone,
261 // b: b_clone,
262 // };
263 // let o_new_reference = CompatTestStructMutNewStr { a, b };
264 // let mut buf_new = Vec::with_capacity(o_new_reference.size());
265 // o_new_reference.export(&mut buf_new);
266 // let o_old = CompatTestStructMutOldStr::from_bytes(&mut buf_new);
267 // assert_eq!(o_old, o_old_reference);
268
269 // let mut buf_old = Vec::with_capacity(o_old.size());
270 // o_old_reference.export(&mut buf_old);
271 // {
272 // let o_new = CompatTestStructMutNewStr::from_bytes(&mut buf_old);
273 // assert_eq!(o_new, o_new_reference);
274 // }
275
276 // let o_try = CompatTestStructMutNewStr::try_from_bytes(&mut buf_old).unwrap();
277 // assert_eq!(o_try, o_new_reference);
278 // }
279
280 #[test]
281 pub fn test_try_from_bytes_success() {
282 // TestStruct (immutable)
283 let a = &rand::random();
284 let b = &rand::random::<[u32; 4]>();
285 let c = &rand::random::<[u128; 7]>();
286 let o = TestStruct { a, b, c };
287 let mut buf = Vec::with_capacity(o.size());
288 o.export(&mut buf);
289
290 // Should succeed
291 let o2 = TestStruct::try_from_bytes(&buf).unwrap();
292 assert_eq!(o2, o);
293
294 // TestStructMut (mutable)
295 let a_mut = &mut rand::random();
296 let b_mut = &mut rand::random::<[u32; 4]>();
297 let c_mut = &mut rand::random::<[u128; 7]>();
298 let o_mut = TestStructMut {
299 a: a_mut,
300 b: b_mut,
301 c: c_mut,
302 };
303 let mut buf_mut = Vec::with_capacity(o_mut.size());
304 o_mut.export(&mut buf_mut);
305
306 // Should succeed
307 let o2_mut = TestStructMut::try_from_bytes(&mut buf_mut).unwrap();
308 assert_eq!(o2_mut, o_mut);
309
310 // TestStructStr
311 let a_str = &rand::random();
312 let b_str = &(0..10).map(|_| rand::random::<char>()).collect::<String>();
313 let c_str = &(0..15).map(|_| rand::random::<char>()).collect::<String>();
314 let o_str = TestStructStr {
315 a: a_str,
316 b: b_str,
317 c: c_str,
318 };
319 let mut buf_str = Vec::with_capacity(o_str.size());
320 o_str.export(&mut buf_str);
321
322 // Should succeed
323 let o2_str = TestStructStr::try_from_bytes(&buf_str).unwrap();
324 assert_eq!(o2_str, o_str);
325
326 // TestStructMutStr
327 let a_mut_str = &mut rand::random();
328 let b_mut_str = &mut (0..10).map(|_| rand::random::<char>()).collect::<String>();
329 let c_mut_str = &mut (0..15).map(|_| rand::random::<char>()).collect::<String>();
330 let o_mut_str = TestStructMutStr {
331 a: a_mut_str,
332 b: b_mut_str,
333 c: c_mut_str,
334 };
335 let mut buf_mut_str = Vec::with_capacity(o_mut_str.size());
336 o_mut_str.export(&mut buf_mut_str);
337
338 // Should succeed
339 let o2_mut_str = TestStructMutStr::try_from_bytes(&mut buf_mut_str).unwrap();
340 assert_eq!(o2_mut_str, o_mut_str);
341 }
342
343 #[test]
344 pub fn test_try_from_bytes_truncated_buffer() {
345 let a = &rand::random();
346 let b = &rand::random::<[u32; 4]>();
347 let c = &rand::random::<[u128; 7]>();
348 let o = TestStruct { a, b, c };
349 let mut buf = Vec::with_capacity(o.size());
350 o.export(&mut buf);
351
352 // Truncate the buffer intentionally
353 let truncated_len = buf.len() - 5;
354 buf.truncate(truncated_len);
355
356 // Now try_from_bytes should fail due to unexpected EOF
357 let res = TestStruct::try_from_bytes(&buf);
358 assert!(
359 res.is_err(),
360 "try_from_bytes should fail with truncated buffer"
361 );
362 }
363
364 #[test]
365 pub fn test_try_from_bytes_invalid_cast() {
366 // We'll test an invalid scenario. For instance, let's take a structure with a slice
367 // and corrupt the length field so it tries to read beyond the buffer length.
368
369 let a = &rand::random::<u64>();
370 let b = &rand::random::<[u32; 2]>();
371 let c = &rand::random::<[u128; 1]>();
372 let o = TestStruct { a, b, c };
373 let mut buf = Vec::with_capacity(o.size());
374 o.export(&mut buf);
375
376 // The buffer for TestStruct is structured as:
377 // a (8 bytes) + length for b (8 bytes) + contents of b + length for c (8 bytes) + contents of c
378 // Let's corrupt the length of b in the buffer to something large, causing an error.
379
380 // at offset 8 we have the length of b slice in bytes (which should be 4 * sizeof(u32) = 16 bytes)
381 // Let's set it to something huge.
382 if buf.len() > 16 {
383 buf[8..16].copy_from_slice(&(10_000u64.to_le_bytes())); // Set a huge length for b
384 }
385
386 // Now try_from_bytes should fail due to buffer too short
387 let res = TestStruct::try_from_bytes(&buf);
388 assert!(
389 res.is_err(),
390 "try_from_bytes should fail with invalid length"
391 );
392 let err = res.err().unwrap();
393
394 assert_eq!(
395 err.kind(),
396 std::io::ErrorKind::UnexpectedEof,
397 "Expected UnexpectedEof error due to invalid length"
398 );
399 }
400
401 #[test]
402 pub fn test_try_from_bytes_invalid_utf8() {
403 // Test invalid UTF-8 scenario.
404 // We'll use a TestStructStr, export it, and then corrupt the string content to invalid UTF-8.
405
406 let a = &rand::random::<u64>();
407 let b = &"HelloWorld".to_string(); // 10 bytes
408 let c = &"AnotherTest".to_string(); // 11 bytes
409 let o = TestStructStr { a, b, c };
410 let mut buf = Vec::with_capacity(o.size());
411 o.export(&mut buf);
412
413 // Corrupt one byte in the b string region to invalid UTF-8.
414 // The structure is: a (8 bytes), length of b (8 bytes), b bytes (10 bytes), length of c (8 bytes), c bytes (11 bytes)
415 // After the first 8 bytes (a), the next 8 are length of b, then 10 bytes of b. Let's corrupt one byte in the b region.
416
417 let b_start = 8 + 8; // after a and length of b
418 // Replace one byte in 'HelloWorld' (ASCII) with 0xFF (invalid in UTF-8)
419 if buf.len() > b_start {
420 buf[b_start] = 0xFF;
421 }
422
423 // Now try_from_bytes should fail due to invalid UTF-8
424 let res = TestStructStr::try_from_bytes(&buf);
425 assert!(
426 res.is_err(),
427 "try_from_bytes should fail with invalid UTF-8"
428 );
429 let err = res.err().unwrap();
430 assert_eq!(
431 err.kind(),
432 std::io::ErrorKind::InvalidData,
433 "Expected InvalidData error due to invalid UTF-8"
434 );
435 }
436
437 #[test]
438 pub fn test_try_from_bytes_empty_buffer() {
439 // try_from_bytes should fail if the buffer is empty or too small for even the first field
440 let buf: Vec<u8> = Vec::new();
441
442 let res = TestStruct::try_from_bytes(&buf);
443 assert!(res.is_err(), "try_from_bytes should fail with empty buffer");
444 let err = res.err().unwrap();
445 assert_eq!(
446 err.kind(),
447 std::io::ErrorKind::UnexpectedEof,
448 "Expected UnexpectedEof for empty buffer"
449 );
450 }
451
452 #[test]
453 pub fn test_try_from_bytes_same_as_from_bytes_on_valid_data() {
454 // We want to ensure that if the data is valid, try_from_bytes and from_bytes behave identically.
455 let a = &rand::random::<u64>();
456 let b = &rand::random::<[u32; 4]>();
457 let c = &rand::random::<[u128; 7]>();
458 let o = TestStruct { a, b, c };
459 let mut buf = Vec::with_capacity(o.size());
460 o.export(&mut buf);
461
462 let o_from = TestStruct::from_bytes(&buf);
463 let o_try = TestStruct::try_from_bytes(&buf).unwrap();
464 assert_eq!(
465 o_from, o_try,
466 "from_bytes and try_from_bytes differ on valid data"
467 );
468
469 let a_mut = &mut rand::random::<u64>();
470 let b_mut = &mut rand::random::<[u32; 4]>();
471 let c_mut = &mut rand::random::<[u128; 7]>();
472 let o_mut = TestStructMut {
473 a: a_mut,
474 b: b_mut,
475 c: c_mut,
476 };
477 let mut buf_mut = Vec::with_capacity(o_mut.size());
478 o_mut.export(&mut buf_mut);
479
480 let mut buf_mut_clone = buf_mut.clone();
481 let o_from_mut = TestStructMut::from_bytes(&mut buf_mut_clone);
482 let o_try_mut = TestStructMut::try_from_bytes(&mut buf_mut).unwrap();
483 assert_eq!(
484 o_from_mut, o_try_mut,
485 "from_bytes and try_from_bytes differ on valid data (mutable)"
486 );
487 }
488
489 // #[test]
490 // pub fn test_try_from_bytes_back_compat() {
491 // // Ensure that try_from_bytes can work with old data as well, mirroring from_bytes tests.
492 // let a = &rand::random();
493 // let b = &rand::random::<[u32; 4]>();
494 // let o_old_reference = CompatTestStructOld { a, b };
495 // let o_new_reference = CompatTestStructNew { a, b };
496 // let mut buf_new = Vec::with_capacity(o_new_reference.size());
497 // o_new_reference.export(&mut buf_new);
498
499 // // Old struct doesn't implement try_from_bytes (uses default which returns NotImplemented)
500 // // So we only test the new struct on the old buffer
501 // let o_old = CompatTestStructOld::try_from_bytes(&buf_new);
502 // // The old struct is derived using old macros without try_from_bytes implementation
503 // // It should return Err(Unsupported) by default
504 // assert!(o_old.is_err());
505 // let err = o_old.err().unwrap();
506 // assert_eq!(
507 // err.kind(),
508 // std::io::ErrorKind::Unsupported,
509 // "Old struct without try_from_bytes should return Unsupported"
510 // );
511
512 // // Now test the new struct on old-structured buffer
513 // // We'll export old reference using the old macro struct and parse with new macro struct.
514 // let mut buf_old = Vec::with_capacity(o_old_reference.size());
515 // o_old_reference.export(&mut buf_old);
516
517 // // The new struct should parse the old buffer correctly.
518 // let o_new = CompatTestStructNew::try_from_bytes(&buf_old)
519 // .expect("try_from_bytes should succeed for new struct on old buffer");
520 // assert_eq!(o_new, o_new_reference);
521 // }
522
523 // #[test]
524 // pub fn test_try_from_bytes_back_compat_str() {
525 // // Same logic for str
526 // let a = &rand::random();
527 // let b = &(0..10).map(|_| rand::random::<char>()).collect::<String>();
528 // let o_old_reference = CompatTestStructOldStr { a, b };
529 // let o_new_reference = CompatTestStructNewStr { a, b };
530 // let mut buf_new = Vec::with_capacity(o_new_reference.size());
531 // o_new_reference.export(&mut buf_new);
532
533 // let o_old = CompatTestStructOldStr::try_from_bytes(&buf_new);
534 // // Old struct uses old macros without try_from_bytes implementation
535 // assert!(o_old.is_err());
536 // let err = o_old.err().unwrap();
537 // assert_eq!(err.kind(), std::io::ErrorKind::Unsupported);
538
539 // let mut buf_old = Vec::with_capacity(o_old_reference.size());
540 // o_old_reference.export(&mut buf_old);
541
542 // // The new struct should parse the old buffer correctly using try_from_bytes
543 // let o_new = CompatTestStructNewStr::try_from_bytes(&buf_old).unwrap();
544 // assert_eq!(o_new, o_new_reference);
545 // }
546}