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}