static_bytes/
bytes_slice.rs

1use super::error::CapacityExceeded;
2use bytes::buf::UninitSlice;
3use bytes::BufMut;
4use core::mem;
5use core::mem::MaybeUninit;
6
7/// Non panic wrapper for `&mut [u8]` that implement [`BufMut`](bytes::buf::BufMut) trait.
8///
9/// # Example
10/// ### work with uninitialized memory `&mut [MaybeUninit<u8>]`
11/// ```rust
12/// use bytes::buf::BufMut;
13/// use static_bytes::SafeBytesSlice;
14/// use core::mem::MaybeUninit;
15/// //are you sure that's random?
16/// fn fill_with_random(buf: &mut dyn BufMut, amount: usize) {
17///     for _ in 0..amount {
18///         buf.put_u8(9);
19///     }
20/// }
21/// // This is optimized way of working with slices
22/// // This is safe. See way in rust doc:
23/// // https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element
24/// let mut fixed_storage: [MaybeUninit<u8>; 128] = unsafe {
25///        MaybeUninit::uninit().assume_init()
26///    };
27/// let mut slice = SafeBytesSlice::from(&mut fixed_storage[..]);
28/// // your function that accept `&mut dyn BufMut`
29/// fill_with_random(&mut slice, 32);
30/// let output = slice.try_into_bytes().unwrap();
31/// assert_eq!(output.len(), 32);
32/// assert_eq!(output[31], 9);
33/// ```
34///
35/// ### work with standard slice `&mut [u8]`
36/// ```rust
37/// use bytes::buf::BufMut;
38/// use static_bytes::SafeBytesSlice;
39/// # //are you sure that's random?
40/// # fn fill_with_random(buf: &mut dyn BufMut, amount: usize) {
41/// #    for _ in 0..amount {
42/// #        buf.put_u8(9);
43/// #    }
44/// # }
45///
46/// let mut fixed_storage = [0u8; 64];
47/// let mut slice = SafeBytesSlice::from(&mut fixed_storage[..]);
48/// // your function that accept `&mut dyn BufMut`
49/// // see function impl in `&mut [MaybeUninit<u8>]` example
50/// fill_with_random(&mut slice, 32);
51/// let output = slice.try_into_bytes().unwrap();
52/// assert_eq!(output.len(), 32);
53/// assert_eq!(output[31], 9);
54/// ```
55///
56/// Is `fill_with_random()` random?
57/// ![](https://starecat.com/content/wp-content/uploads/tour-of-accounting-over-here-we-have-our-random-number-generator-nine-nine-are-you-sure-thats-random-thats-the-problem-with-randomness-you-can-never-be-sure-gilbert-comic.jpg)
58pub struct SafeBytesSlice<'a> {
59    slice: &'a mut [MaybeUninit<u8>],
60    bytes_written: usize,
61    // capacity exceeded. User tried put more bytes than available in slice
62    cap_exceeded: bool,
63}
64
65impl<'a> From<&'a mut [u8]> for SafeBytesSlice<'a> {
66    fn from(slice: &'a mut [u8]) -> Self {
67        let maybe_uninit_slice =
68            unsafe { &mut *(&mut *slice as *mut [u8] as *mut [mem::MaybeUninit<u8>]) };
69
70        Self::from(maybe_uninit_slice)
71    }
72}
73
74impl<'a> From<&'a mut [MaybeUninit<u8>]> for SafeBytesSlice<'a> {
75    fn from(slice: &'a mut [MaybeUninit<u8>]) -> Self {
76        Self {
77            slice,
78            bytes_written: 0,
79            cap_exceeded: false,
80        }
81    }
82}
83
84impl<'a> SafeBytesSlice<'a> {
85    /// Returns the number of bytes wrote into inner slice.
86    ///
87    /// Use [`BufMut::remaining_mut()`](bytes::buf::BufMut::remaining_mut) to check the number of bytes that
88    /// can be written from the current position until the end of the buffer is reached.
89    ///
90    /// # Example
91    ///
92    /// This is example of BAD usage. Showing why this method is private and probably should never
93    /// be public.
94    ///
95    /// [Tracking Issue 1](https://github.com/xoac/static-bytes/issues/1)
96    ///
97    /// ```compile_fail
98    /// use static_bytes::SafeBytesSlice;
99    /// use bytes::buf::BufMut;
100    /// # fn fill_with_random(buf: &mut dyn BufMut, amount: usize) {
101    /// #    for _ in 0..amount {
102    /// #        buf.put_u8(0xFF);
103    /// #    }
104    /// # }
105    /// let mut static_data = [0u8; 32];
106    /// let mut safe_slice = SafeBytesSlice::from(&mut static_data[..]);
107    ///
108    /// fill_with_random(&mut safe_slice, 33);
109    /// assert_eq!(safe_slice.is_exceed(), true);
110    /// let output_len = safe_slice.bytes_written();
111    /// // ⚠️ This  allow use  output_len in improper way!
112    /// drop(safe_slice);
113    ///
114    /// // output is incorrect contains 32 bytes but should 33.
115    /// let _output = &static_data[..output_len];
116    /// ```
117    #[allow(dead_code)]
118    fn bytes_written(&self) -> usize {
119        debug_assert_eq!(self.cap_exceeded, false);
120        self.bytes_written
121    }
122
123    /// Returns true if the inner slice contains 0 bytes.
124    #[inline]
125    #[allow(dead_code)]
126    fn is_empty(&self) -> bool {
127        self.bytes_written() == 0
128    }
129
130    /// Returns filled bytes (`&[u8]`) or error if capacity exceeded.
131    pub fn try_into_bytes(self) -> Result<&'a [u8], CapacityExceeded> {
132        if self.is_exceed() {
133            Err(CapacityExceeded {})
134        } else {
135            // TODO in the future there will be function for this
136            // https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#method.slice_get_ref
137            //
138            // Safty this is safe because memory is properly initialized up to self.bytes_written
139            // MaybeUninit<T> is #[repr(transparent)]
140            Ok(unsafe {
141                &*(&self.slice[..self.bytes_written] as *const [core::mem::MaybeUninit<u8>]
142                    as *const [u8])
143            })
144        }
145    }
146
147    /// Returns true if capacity was exceeded.
148    ///
149    /// `SafeBytesSlice` is not usable anymore - there is no access to inner bytes because they are
150    /// in improper state.
151    pub fn is_exceed(&self) -> bool {
152        self.cap_exceeded
153    }
154}
155
156// Implement required methods
157unsafe impl<'a> BufMut for SafeBytesSlice<'a> {
158    fn remaining_mut(&self) -> usize {
159        debug_assert!(self.bytes_written <= self.slice.len());
160        self.slice.len() - self.bytes_written
161    }
162
163    unsafe fn advance_mut(&mut self, cnt: usize) {
164        let new_bytes_written = self.bytes_written + cnt;
165        if new_bytes_written > self.slice.len() {
166            self.bytes_written = self.slice.len(); // make `remaining_mut()` return 0
167            self.cap_exceeded = true;
168        } else {
169            self.bytes_written = new_bytes_written;
170        }
171    }
172
173    fn chunk_mut(&mut self) -> &mut UninitSlice {
174        let bytes = &mut self.slice[self.bytes_written..];
175        let len = bytes.len();
176        let ptr = bytes.as_mut_ptr() as *mut _;
177        unsafe { UninitSlice::from_raw_parts_mut(ptr, len) }
178    }
179
180    fn put_slice(&mut self, src: &[u8]) {
181        use core::ptr;
182        // check if we have enough data to put slice. If no set flag instead of panic!
183        let src_len = src.len();
184        if self.remaining_mut() < src_len {
185            self.bytes_written = self.slice.len(); // make `remaining_mut()` return 0
186            self.cap_exceeded = true;
187            return;
188        }
189
190        // enough inner capacity to execute safe copy
191        unsafe {
192            let dst = self.chunk_mut();
193            ptr::copy_nonoverlapping(src[..].as_ptr(), dst.as_mut_ptr() as *mut u8, src_len);
194            self.advance_mut(src_len);
195        }
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202
203    fn fill_with_random(buf: &mut dyn BufMut, amount: usize) {
204        for _ in 0..amount {
205            buf.put_u8(0xFF);
206        }
207    }
208
209    #[test]
210    fn usefullness() {
211        // standard way
212        let mut data = [0u8; 32];
213        let mut slice = &mut data[..];
214        let slice_len = slice.len();
215        fill_with_random(&mut slice, 27);
216        // how many data was wrote (?)
217        let n = slice_len - slice.len();
218        assert_eq!(n, 27);
219        let _wrote_data = &data[..n];
220
221        // new way
222        let mut raw = [0u8; 32];
223        let mut slice = SafeBytesSlice::from(&mut raw[..]);
224        fill_with_random(&mut slice, 27);
225        // slice already contains bytes_written();
226        let _wrote_data = match slice.try_into_bytes() {
227            Ok(bytes) => bytes,
228            Err(_err) => unimplemented!(),
229        };
230    }
231
232    #[test]
233    fn naive_test() {
234        let mut static_data = [0u8; 32];
235        let mut safe_slice = SafeBytesSlice::from(&mut static_data[..]);
236
237        fill_with_random(&mut safe_slice, 32);
238        assert_eq!(safe_slice.is_exceed(), false);
239
240        for v in safe_slice
241            .try_into_bytes()
242            .expect("not expected capacity")
243            .iter()
244        {
245            assert_eq!(*v, 0xFF);
246        }
247
248        // reuse data
249        let mut safe_slice = SafeBytesSlice::from(&mut static_data[..]);
250        fill_with_random(&mut safe_slice, 33);
251        assert_eq!(safe_slice.is_exceed(), true);
252    }
253}