ps_buffer/methods/
from_ffi_init.rs

1use crate::{Buffer, BufferError};
2
3impl Buffer {
4    /// Creates a new `Buffer` with custom initialization.
5    ///
6    /// # Arguments
7    ///
8    /// * `bytes` - The number of bytes to allocate.
9    /// * `init_fn` - A closure that initializes the buffer and returns the number of bytes actually used.
10    ///
11    /// # Returns
12    ///
13    /// `Ok(Buffer)` with at least `bytes` allocated, where `init_fn` has been applied to initialize some portion of it,
14    /// or `Err(BufferError)` if allocation fails.
15    ///
16    /// # Errors
17    ///
18    /// - `AllocationError` is returned if allocation fails.
19    ///
20    /// # Panics
21    ///
22    /// - Panics if the number of initialized bytes > number of allocated bytes.
23    pub fn from_ffi_init<F>(bytes: usize, init_fn: F) -> Result<Self, BufferError>
24    where
25        F: FnOnce(&mut [u8]) -> usize,
26    {
27        // Allocate the buffer with the specified number of bytes
28        let mut buffer = Self::alloc_uninit(bytes)?;
29
30        // Invoke the initialization function
31        let initialized_bytes = init_fn(buffer.as_mut());
32
33        // Ensure that the number of bytes initialized does not exceed the allocated size
34        debug_assert!(
35            initialized_bytes <= bytes,
36            "Initialized bytes ({initialized_bytes}) cannot exceed allocated bytes ({bytes})."
37        );
38
39        // Truncate uninitialized bytes
40        buffer.truncate(initialized_bytes);
41
42        // Return resulting `Buffer`
43        Ok(buffer)
44    }
45}
46
47#[cfg(test)]
48#[allow(clippy::unwrap_used)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn test_from_ffi_init_full_buffer() {
54        let buffer = Buffer::from_ffi_init(10, |slice| {
55            for (i, byte) in slice.iter_mut().enumerate() {
56                *byte = u8::try_from(i).unwrap();
57            }
58            slice.len()
59        })
60        .unwrap();
61
62        // Check if the buffer is fully initialized
63        assert_eq!(buffer.len(), 10);
64        assert_eq!(&buffer[..], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
65    }
66
67    #[test]
68    fn test_from_ffi_init_partial_buffer() {
69        let buffer = Buffer::from_ffi_init(10, |slice| {
70            for (i, byte) in slice.iter_mut().enumerate() {
71                if i < 5 {
72                    *byte = u8::try_from(i).unwrap();
73                }
74            }
75            5 // Only 5 bytes are initialized
76        })
77        .unwrap();
78
79        // Check if the buffer is partially initialized
80        assert_eq!(buffer.len(), 5);
81        assert_eq!(&buffer[..], &[0, 1, 2, 3, 4]);
82    }
83
84    #[test]
85    fn test_from_ffi_init_zero_bytes() {
86        let buffer = Buffer::from_ffi_init(10, |_slice| {
87            0 // No bytes are initialized
88        })
89        .unwrap();
90
91        // Check if the buffer is empty
92        assert_eq!(buffer.len(), 0);
93        assert_eq!(buffer.capacity(), 10); // Capacity should remain 10
94    }
95
96    #[test]
97    fn test_from_ffi_init_allocation_failure() {
98        // Assuming Buffer::alloc_uninit might fail if asking for a very large buffer
99        let result = Buffer::from_ffi_init(usize::MAX, |_slice| 0);
100        assert!(result.is_err());
101    }
102
103    #[test]
104    #[should_panic(expected = "Initialized bytes (11) cannot exceed allocated bytes (10).")]
105    fn test_from_ffi_init_panic_on_over_initialization() {
106        // This test should panic due to the debug_assert
107        let _ = Buffer::from_ffi_init(10, |slice| {
108            for (i, byte) in slice.iter_mut().enumerate() {
109                *byte = u8::try_from(i).unwrap();
110            }
111            11 // Trying to initialize more bytes than allocated
112        })
113        .unwrap();
114    }
115}