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/// 
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}