head/slice.rs
1use core::{
2 mem::{self, MaybeUninit},
3 ptr, slice,
4};
5
6#[cfg(feature = "alloc")]
7use alloc::boxed::Box;
8
9/// A dynamically-sized view into a contiguous header and trailing sequence.
10#[repr(C)]
11#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
12pub struct HeaderSlice<H, T> {
13 header: H,
14 slice: [T],
15}
16
17#[repr(C)]
18struct HeaderSliceDummy<H, T> {
19 header: MaybeUninit<H>,
20 slice: MaybeUninit<[T; 0]>,
21}
22
23#[repr(C)]
24struct HeaderSliceHeader<H, T> {
25 header: H,
26 // Using `MaybeUninit` to avoid dropck for `T`.
27 slice: MaybeUninit<[T; 0]>,
28}
29
30impl<H, T> HeaderSliceHeader<H, T> {
31 #[inline]
32 fn as_header_slice(&self) -> &HeaderSlice<H, T> {
33 // SAFETY: `header` satisfies slice alignment requirement of `T`.
34 unsafe { HeaderSlice::from_header_unchecked(&self.header) }
35 }
36
37 #[inline]
38 fn as_header_slice_mut(&mut self) -> &mut HeaderSlice<H, T> {
39 // SAFETY: `header` satisfies slice alignment requirement of `T`.
40 unsafe { HeaderSlice::from_header_unchecked_mut(&mut self.header) }
41 }
42}
43
44/// Convenience functions for handling raw memory.
45#[allow(dead_code)]
46impl<H, T> HeaderSlice<H, T> {
47 /// Returns the alignment for header-slice allocations.
48 #[inline]
49 pub(crate) fn align() -> usize {
50 mem::align_of::<HeaderSliceDummy<H, T>>()
51 }
52
53 /// Returns the offset from the base address of a header-slice to the slice.
54 #[inline]
55 pub(crate) fn slice_offset() -> usize {
56 let dummy = HeaderSliceDummy::<H, T> {
57 header: MaybeUninit::uninit(),
58 slice: MaybeUninit::uninit(),
59 };
60
61 let base_addr = &dummy as *const _ as usize;
62 let slice_addr = &dummy.slice as *const _ as usize;
63
64 slice_addr - base_addr
65 }
66}
67
68// TODO: `From<Arc<H>>` for `Arc<HeaderSlice<H, H>>`
69// TODO: `From<Rc<H>>` for `Rc<HeaderSlice<H, H>>`
70
71// TODO: `Clone` for `Box<HeaderSlice<H, T>>`
72
73impl<'a, H> From<&'a H> for &'a HeaderSlice<H, H> {
74 #[inline]
75 fn from(header: &'a H) -> Self {
76 // SAFETY: `H` satisfies slice alignment requirement.
77 unsafe { HeaderSlice::from_header_unchecked(header) }
78 }
79}
80
81impl<'a, H> From<&'a mut H> for &'a mut HeaderSlice<H, H> {
82 #[inline]
83 fn from(header: &'a mut H) -> Self {
84 // SAFETY: `H` satisfies slice alignment requirement.
85 unsafe { HeaderSlice::from_header_unchecked_mut(header) }
86 }
87}
88
89#[cfg(feature = "alloc")]
90impl<H> From<Box<H>> for Box<HeaderSlice<H, H>> {
91 #[inline]
92 fn from(header: Box<H>) -> Self {
93 // SAFETY: `H` satisfies slice alignment requirement.
94 unsafe { HeaderSlice::from_boxed_header_unchecked(header) }
95 }
96}
97
98#[cfg(feature = "alloc")]
99impl<H> From<Box<HeaderSlice<H, H>>> for Box<[H]> {
100 #[inline]
101 fn from(hs: Box<HeaderSlice<H, H>>) -> Self {
102 hs.into_full_boxed_slice()
103 }
104}
105
106/// Returns `true` if `header` is aligned to the slice element type `T`.
107#[inline]
108fn is_header_slice_aligned<H, T>(header: *const H) -> bool {
109 header as usize % mem::align_of::<T>() == 0
110}
111
112impl<H, T> HeaderSlice<H, T> {
113 /// Returns the result of calling `f` on a shared header-slice starting with
114 /// `header`.
115 #[inline]
116 pub fn with_header<F, R>(header: H, f: F) -> R
117 where
118 F: for<'a> FnOnce(&'a Self) -> R,
119 {
120 let hs = HeaderSliceHeader::<H, T> {
121 header,
122 slice: MaybeUninit::uninit(),
123 };
124
125 f(hs.as_header_slice())
126 }
127
128 /// Returns the result of calling `f` on a mutable header-slice starting
129 /// with `header`.
130 #[inline]
131 pub fn with_header_mut<F, R>(header: H, f: F) -> R
132 where
133 F: for<'a> FnOnce(&'a mut Self) -> R,
134 {
135 let mut hs = HeaderSliceHeader::<H, T> {
136 header,
137 slice: MaybeUninit::uninit(),
138 };
139
140 f(hs.as_header_slice_mut())
141 }
142
143 /// Attempts to create a shared header-slice from just `header`.
144 ///
145 /// The address of `header` must be at least aligned to `T` because the
146 /// empty slice component must be properly aligned.
147 ///
148 /// If `T` has equal or greater alignment than `H`, unwrapping the returned
149 /// value is a no-op.
150 #[inline]
151 pub fn from_header(header: &H) -> Option<&Self> {
152 if is_header_slice_aligned::<H, T>(header) {
153 // SAFETY: `header` satisfies slice alignment requirement.
154 Some(unsafe { Self::from_header_unchecked(header) })
155 } else {
156 None
157 }
158 }
159
160 /// Attempts to create a mutable header-slice from just `header`.
161 ///
162 /// The address of `header` must be at least aligned to `T` because the
163 /// empty slice component must be properly aligned.
164 ///
165 /// If `T` has equal or greater alignment than `H`, unwrapping the returned
166 /// value is a no-op.
167 #[inline]
168 pub fn from_header_mut(header: &mut H) -> Option<&mut Self> {
169 if is_header_slice_aligned::<H, T>(header) {
170 // SAFETY: `header` satisfies slice alignment requirement.
171 Some(unsafe { Self::from_header_unchecked_mut(header) })
172 } else {
173 None
174 }
175 }
176
177 /// Attempts to create a boxed header-slice from just `header`.
178 ///
179 /// The address of `header` must be at least aligned to `T` because the
180 /// empty slice component must be properly aligned.
181 ///
182 /// If `T` has equal or greater alignment than `H`, unwrapping the returned
183 /// value is a no-op.
184 #[cfg(feature = "alloc")]
185 #[inline]
186 pub fn from_boxed_header(header: Box<H>) -> Result<Box<Self>, Box<H>> {
187 if is_header_slice_aligned::<H, T>(&*header) {
188 // SAFETY: `header` satisfies slice alignment requirement.
189 Ok(unsafe { Self::from_boxed_header_unchecked(header) })
190 } else {
191 Err(header)
192 }
193 }
194
195 /// Create a shared header-slice from just `header`, without checking its
196 /// alignment.
197 ///
198 /// # Safety
199 ///
200 /// The address of `header` must be at least aligned to `T` because the
201 /// empty slice component must be properly aligned.
202 #[inline]
203 pub unsafe fn from_header_unchecked(header: &H) -> &Self {
204 Self::from_raw_parts(header, 0)
205 }
206
207 /// Create a mutable header-slice from just `header`, without checking its
208 /// alignment.
209 ///
210 /// # Safety
211 ///
212 /// The address of `header` must be at least aligned to `T` because the
213 /// empty slice component must be properly aligned.
214 #[inline]
215 pub unsafe fn from_header_unchecked_mut(header: &mut H) -> &mut Self {
216 Self::from_raw_parts_mut(header, 0)
217 }
218
219 /// Create a boxed header-slice from just `header`, without checking its
220 /// alignment.
221 ///
222 /// # Safety
223 ///
224 /// The address of `header` must be at least aligned to `T` because the
225 /// empty slice component must be properly aligned.
226 #[cfg(feature = "alloc")]
227 #[inline]
228 pub unsafe fn from_boxed_header_unchecked(header: Box<H>) -> Box<Self> {
229 Self::boxed_from_raw_parts(Box::into_raw(header), 0)
230 }
231
232 /// Forms a shared header-slice from a pointer and a length.
233 ///
234 /// # Safety
235 ///
236 /// Behavior is undefined if any of the following conditions are violated:
237 ///
238 /// - `header` and any slice following it must be [valid].
239 ///
240 /// - `header` must be non-null and aligned to the greater alignment
241 /// between `H` and `T`.
242 ///
243 /// - If `len` is non-zero, the slice following `header` must be aligned
244 /// to `T`.
245 ///
246 /// - The entire memory range of spanning from the start of `header` to
247 /// the end of the trailing slice must be contained within a single
248 /// allocated object! Header-slices can never span across multiple
249 /// allocated objects.
250 ///
251 /// - The memory referenced by the returned header-slice must not be mutated
252 /// for the duration of lifetime `'a`, except inside an [`UnsafeCell`].
253 ///
254 /// - The total size of the resulting header-slice must be no larger than
255 /// `isize::MAX`.
256 ///
257 /// # Caveat
258 ///
259 /// The lifetime for the returned slice is inferred from its usage. To
260 /// prevent accidental misuse, it's suggested to tie the lifetime to
261 /// whichever source lifetime is safe in the context, such as by providing a
262 /// helper function taking the lifetime of a host value for the slice, or by
263 /// explicit annotation.
264 ///
265 /// [valid]: https://doc.rust-lang.org/std/ptr/index.html#safety
266 /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
267 #[inline]
268 pub unsafe fn from_raw_parts<'a>(header: *const H, len: usize) -> &'a Self {
269 // We never create `&[H]` because data past `header` may refer to
270 // invalid instances of `H`. So instead we strictly use a raw slice
271 // pointer.
272 &*(ptr::slice_from_raw_parts(header, len) as *const Self)
273 }
274
275 /// Forms a mutable header-slice from a pointer and a length.
276 ///
277 /// # Safety
278 ///
279 /// Behavior is undefined if any of the following conditions are violated:
280 ///
281 /// - `header` and any slice following it must be [valid].
282 ///
283 /// - `header` must be non-null and aligned to the greater alignment
284 /// between `H` and `T`.
285 ///
286 /// - If `len` is non-zero, the slice following `header` must be aligned
287 /// to `T`.
288 ///
289 /// - The entire memory range of spanning from the start of `header` to
290 /// the end of the trailing slice must be contained within a single
291 /// allocated object! Header-slices can never span across multiple
292 /// allocated objects.
293 ///
294 /// - The memory referenced by the returned header-slice must not be
295 /// accessed through any other pointer (not derived from the return value)
296 /// for the duration of lifetime `'a`. Both read and write accesses are
297 /// forbidden.
298 ///
299 /// - The total size of the resulting header-slice must be no larger than
300 /// `isize::MAX`.
301 ///
302 /// # Caveat
303 ///
304 /// The lifetime for the returned slice is inferred from its usage. To
305 /// prevent accidental misuse, it's suggested to tie the lifetime to
306 /// whichever source lifetime is safe in the context, such as by providing a
307 /// helper function taking the lifetime of a host value for the slice, or by
308 /// explicit annotation.
309 ///
310 /// [valid]: https://doc.rust-lang.org/std/ptr/index.html#safety
311 #[inline]
312 pub unsafe fn from_raw_parts_mut<'a>(header: *mut H, len: usize) -> &'a mut Self {
313 // We never create `&mut [H]` because data past `header` may refer to
314 // invalid instances of `H`. So instead we strictly use a raw slice
315 // pointer.
316 &mut *(ptr::slice_from_raw_parts_mut(header, len) as *mut Self)
317 }
318
319 /// Forms a boxed header-slice from a pointer and a length.
320 ///
321 /// # Safety
322 ///
323 /// `header` must point to a header-slice with a slice of `len` items that
324 /// has been allocated by the global allocator.
325 ///
326 /// Improper use can lead to:
327 ///
328 /// - A double-free if the function is called twice on the same raw pointer.
329 ///
330 /// - Mutable aliasing, which causes undefined behavior.
331 ///
332 /// [valid]: https://doc.rust-lang.org/std/ptr/index.html#safety
333 #[cfg(feature = "alloc")]
334 #[inline]
335 pub unsafe fn boxed_from_raw_parts(header: *mut H, len: usize) -> Box<Self> {
336 // We never create `&mut [H]` because data past `header` may refer to
337 // invalid instances of `H`. So instead we strictly use a raw slice
338 // pointer.
339 Box::from_raw(ptr::slice_from_raw_parts_mut(header, len) as *mut Self)
340 }
341
342 /// Returns a shared reference to the value preceding a slice of `T` in
343 /// memory.
344 #[inline]
345 pub fn as_header(&self) -> &H {
346 &self.header
347 }
348
349 /// Returns a mutable reference to the value preceding a slice of `T` in
350 /// memory.
351 #[inline]
352 pub fn as_header_mut(&mut self) -> &mut H {
353 &mut self.header
354 }
355
356 /// Returns a shared reference to the trailing contiguous sequence of
357 /// values.
358 #[inline]
359 pub fn as_slice(&self) -> &[T] {
360 &self.slice
361 }
362
363 /// Returns a mutable reference to the trailing contiguous sequence of
364 /// values.
365 #[inline]
366 pub fn as_slice_mut(&mut self) -> &mut [T] {
367 &mut self.slice
368 }
369
370 /// Returns shared references to the header and its proceeded slice of `T`.
371 #[inline]
372 pub fn as_header_and_slice(&self) -> (&H, &[T]) {
373 (&self.header, &self.slice)
374 }
375
376 /// Returns mutable references to the header and its proceeded slice of `T`.
377 #[inline]
378 pub fn as_header_and_slice_mut(&mut self) -> (&mut H, &mut [T]) {
379 (&mut self.header, &mut self.slice)
380 }
381}
382
383impl<H> HeaderSlice<H, H> {
384 /// Attempts to create a shared header-slice from `slice`, using the first
385 /// element as the header.
386 ///
387 /// Returns `None` if `slice` is empty.
388 #[inline]
389 pub fn from_full_slice(slice: &[H]) -> Option<&Self> {
390 if slice.is_empty() {
391 None
392 } else {
393 // SAFETY: `slice` has an element for a header.
394 Some(unsafe { Self::from_full_slice_unchecked(slice) })
395 }
396 }
397
398 /// Attempts to create a mutable header-slice from `slice`, using the first
399 /// element as the header.
400 ///
401 /// Returns `None` if `slice` is empty.
402 #[inline]
403 pub fn from_full_slice_mut(slice: &mut [H]) -> Option<&mut Self> {
404 if slice.is_empty() {
405 None
406 } else {
407 // SAFETY: `slice` has an element for a header.
408 Some(unsafe { Self::from_full_slice_unchecked_mut(slice) })
409 }
410 }
411
412 /// Attempts to create a boxed header-slice from `slice`, using the first
413 /// element as the header.
414 ///
415 /// Returns `None` if `slice` is empty.
416 #[cfg(feature = "alloc")]
417 #[inline]
418 pub fn from_full_boxed_slice(slice: Box<[H]>) -> Option<Box<Self>> {
419 if slice.is_empty() {
420 None
421 } else {
422 // SAFETY: `slice` has an element for a header.
423 Some(unsafe { Self::from_full_boxed_slice_unchecked(slice) })
424 }
425 }
426
427 /// Creates a shared header-slice from `slice`, using the first element as
428 /// the header without checking if it exists.
429 ///
430 /// # Safety
431 ///
432 /// `slice` must be non-empty.
433 #[inline]
434 pub unsafe fn from_full_slice_unchecked(slice: &[H]) -> &Self {
435 Self::from_raw_parts(slice.as_ptr(), slice.len().wrapping_sub(1))
436 }
437
438 /// Creates a mutable header-slice from `slice`, using the first element as
439 /// the header without checking if it exists.
440 ///
441 /// # Safety
442 ///
443 /// `slice` must be non-empty.
444 #[inline]
445 pub unsafe fn from_full_slice_unchecked_mut(slice: &mut [H]) -> &mut Self {
446 Self::from_raw_parts_mut(slice.as_mut_ptr(), slice.len().wrapping_sub(1))
447 }
448
449 /// Creates a boxed header-slice from `slice`, using the first element as
450 /// the header without checking if it exists.
451 ///
452 /// # Safety
453 ///
454 /// `slice` must be non-empty.
455 #[cfg(feature = "alloc")]
456 #[inline]
457 pub unsafe fn from_full_boxed_slice_unchecked(slice: Box<[H]>) -> Box<Self> {
458 let new_len = slice.len().wrapping_sub(1);
459 let header = Box::into_raw(slice) as *mut H;
460
461 Self::boxed_from_raw_parts(header, new_len)
462 }
463
464 /// Returns the full range of `self` as a single shared slice.
465 #[inline]
466 pub fn as_full_slice(&self) -> &[H] {
467 let data = self as *const Self as *const H;
468 let len = self.slice.len() + 1;
469
470 unsafe { slice::from_raw_parts(data, len) }
471 }
472
473 /// Returns the full range of `self` as a single mutable slice.
474 #[inline]
475 pub fn as_full_slice_mut(&mut self) -> &mut [H] {
476 let data = self as *mut Self as *mut H;
477 let len = self.slice.len() + 1;
478
479 unsafe { slice::from_raw_parts_mut(data, len) }
480 }
481
482 /// Returns the full range of `self` as a single boxed slice.
483 #[cfg(feature = "alloc")]
484 #[inline]
485 pub fn into_full_boxed_slice(self: Box<Self>) -> Box<[H]> {
486 let len = self.slice.len() + 1;
487 let data = Box::into_raw(self) as *mut H;
488
489 unsafe { Box::from_raw(slice::from_raw_parts_mut(data, len)) }
490 }
491}