ntex_bytes/buf/uninit_slice.rs
1use std::ops::{
2 Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
3};
4use std::{fmt, mem::MaybeUninit, ptr};
5
6/// Uninitialized byte slice.
7///
8/// Returned by `BufMut::chunk_mut()`, the referenced byte slice may be
9/// uninitialized. The wrapper provides safe access without introducing
10/// undefined behavior.
11///
12/// The safety invariants of this wrapper are:
13///
14/// 1. Reading from an `UninitSlice` is undefined behavior.
15/// 2. Writing uninitialized bytes to an `UninitSlice` is undefined behavior.
16///
17/// The difference between `&mut UninitSlice` and `&mut [MaybeUninit<u8>]` is
18/// that it is possible in safe code to write uninitialized bytes to an
19/// `&mut [MaybeUninit<u8>]`, which this type prohibits.
20#[repr(transparent)]
21pub struct UninitSlice([MaybeUninit<u8>]);
22
23impl UninitSlice {
24 /// Create a `&mut UninitSlice` from a pointer and a length.
25 ///
26 /// # Safety
27 ///
28 /// The caller must ensure that `ptr` references a valid memory region owned
29 /// by the caller representing a byte slice for the duration of `'a`.
30 ///
31 /// # Examples
32 ///
33 /// ```
34 /// use ntex_bytes::buf::UninitSlice;
35 ///
36 /// let bytes = b"hello world".to_vec();
37 /// let ptr = bytes.as_ptr() as *mut _;
38 /// let len = bytes.len();
39 ///
40 /// let slice = unsafe { UninitSlice::from_raw_parts_mut(ptr, len) };
41 /// ```
42 #[inline]
43 pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut UninitSlice {
44 UninitSlice::from_uninit_mut(core::slice::from_raw_parts_mut(ptr.cast(), len))
45 }
46
47 #[inline]
48 fn from_uninit(slice: &[MaybeUninit<u8>]) -> &UninitSlice {
49 // SAFETY: `UninitSlice` is a transparent wrapper of `[MaybeUninit<u8>]`, with the same
50 // lifetime.
51 unsafe { &*(ptr::from_ref(slice) as *const UninitSlice) }
52 }
53
54 #[inline]
55 fn from_uninit_mut(slice: &mut [MaybeUninit<u8>]) -> &mut UninitSlice {
56 // SAFETY: `UninitSlice` is a transparent wrapper of `[MaybeUninit<u8>]`, preserving
57 // exclusive access.
58 unsafe { &mut *(ptr::from_mut(slice) as *mut UninitSlice) }
59 }
60
61 /// Write a single byte at the specified offset.
62 ///
63 /// # Panics
64 ///
65 /// The function panics if `index` is out of bounds.
66 ///
67 /// # Examples
68 ///
69 /// ```
70 /// use ntex_bytes::buf::UninitSlice;
71 ///
72 /// let mut data = [b'f', b'o', b'o'];
73 /// let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) };
74 ///
75 /// slice.write_byte(0, b'b');
76 ///
77 /// assert_eq!(b"boo", &data[..]);
78 /// ```
79 #[inline]
80 pub fn write_byte(&mut self, index: usize, byte: u8) {
81 assert!(index < self.len());
82
83 // SAFETY: `index < len` was checked above, so the pointer addresses a writable byte.
84 unsafe { self[index..].as_mut_ptr().write(byte) }
85 }
86
87 /// Copies bytes from `src` into `self`.
88 ///
89 /// The length of `src` must be the same as `self`.
90 ///
91 /// # Panics
92 ///
93 /// The function panics if `src` has a different length than `self`.
94 ///
95 /// # Examples
96 ///
97 /// ```
98 /// use ntex_bytes::buf::UninitSlice;
99 ///
100 /// let mut data = [b'f', b'o', b'o'];
101 /// let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) };
102 ///
103 /// slice.copy_from_slice(b"bar");
104 ///
105 /// assert_eq!(b"bar", &data[..]);
106 /// ```
107 #[inline]
108 pub fn copy_from_slice(&mut self, src: &[u8]) {
109 use core::ptr;
110
111 assert_eq!(self.len(), src.len());
112
113 // SAFETY: The lengths match, and the exclusive destination slice cannot overlap `src`.
114 unsafe {
115 ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
116 }
117 }
118
119 /// Return a raw pointer to the slice's buffer.
120 ///
121 /// # Pointer usage
122 ///
123 /// The caller **must not** read from the referenced memory and **must not**
124 /// write **uninitialized** bytes to the slice either.
125 ///
126 /// # Examples
127 ///
128 /// ```
129 /// use ntex_bytes::BufMut;
130 ///
131 /// let mut data = [0, 1, 2];
132 /// let mut slice = &mut data[..];
133 /// let ptr = BufMut::chunk_mut(&mut slice).as_mut_ptr();
134 /// ```
135 #[inline]
136 pub fn as_mut_ptr(&mut self) -> *mut u8 {
137 self.0.as_mut_ptr().cast()
138 }
139
140 /// Returns the number of bytes in the slice.
141 ///
142 /// # Examples
143 ///
144 /// ```
145 /// use ntex_bytes::BufMut;
146 ///
147 /// let mut data = [0, 1, 2];
148 /// let mut slice = &mut data[..];
149 /// let len = BufMut::chunk_mut(&mut slice).len();
150 ///
151 /// assert_eq!(len, 3);
152 /// ```
153 #[inline]
154 #[allow(clippy::len_without_is_empty)]
155 pub fn len(&self) -> usize {
156 self.0.len()
157 }
158
159 /// Returns the slice as a mutable slice of [`MaybeUninit<u8>`].
160 ///
161 /// # Safety
162 ///
163 /// The memory may already be initialized, for example when the slice was
164 /// returned by the [`BufMut`](crate::BufMut) implementation for
165 /// `&mut [u8]`. The caller **must not** write uninitialized bytes to the
166 /// returned slice.
167 ///
168 /// # Examples
169 ///
170 /// ```
171 /// use ntex_bytes::BufMut;
172 ///
173 /// let mut data = [0, 1, 2];
174 /// let mut slice = &mut data[..];
175 /// let uninit = unsafe { BufMut::chunk_mut(&mut slice).as_uninit_slice_mut() };
176 /// uninit[0].write(b'a');
177 ///
178 /// assert_eq!(data[0], b'a');
179 /// ```
180 #[inline]
181 pub unsafe fn as_uninit_slice_mut(&mut self) -> &mut [MaybeUninit<u8>] {
182 &mut self.0
183 }
184}
185
186impl fmt::Debug for UninitSlice {
187 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
188 fmt.debug_struct("UninitSlice[...]").finish()
189 }
190}
191
192macro_rules! impl_index {
193 ($($t:ty),*) => {
194 $(
195 impl Index<$t> for UninitSlice {
196 type Output = UninitSlice;
197
198 #[inline]
199 fn index(&self, index: $t) -> &UninitSlice {
200 UninitSlice::from_uninit(&self.0[index])
201 }
202 }
203
204 impl IndexMut<$t> for UninitSlice {
205 #[inline]
206 fn index_mut(&mut self, index: $t) -> &mut UninitSlice {
207 UninitSlice::from_uninit_mut(&mut self.0[index])
208 }
209 }
210 )*
211 };
212}
213
214impl_index!(
215 Range<usize>,
216 RangeFrom<usize>,
217 RangeFull,
218 RangeInclusive<usize>,
219 RangeTo<usize>,
220 RangeToInclusive<usize>
221);
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 /// STATEMENT: `UninitSlice` supports standard range indexing and writes through uninitialized-slice views.
228 ///
229 /// HOW:
230 /// - Wrap an eight-byte array, check six range lengths, and write `a` and `b` through `as_uninit_slice_mut`.
231 /// - Verify the underlying array begins with `ab`.
232 #[test]
233 fn index_and_uninit_access() {
234 let mut data = [0u8; 8];
235 // SAFETY: The pointer and length cover the live array, exclusively borrowed until the last
236 // slice use.
237 let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), data.len()) };
238
239 assert_eq!(slice[..].len(), 8);
240 assert_eq!(slice[2..].len(), 6);
241 assert_eq!(slice[..3].len(), 3);
242 assert_eq!(slice[..=3].len(), 4);
243 assert_eq!(slice[1..3].len(), 2);
244 assert_eq!(slice[1..=3].len(), 3);
245
246 // SAFETY: Only initialized bytes are written, preserving initialization of the underlying
247 // array.
248 unsafe {
249 slice.as_uninit_slice_mut()[0].write(b'a');
250 slice.as_uninit_slice_mut()[1].write(b'b');
251 }
252 assert_eq!(&data[..2], b"ab");
253 }
254}