cocoon_tpm_utils_common/fixed_vec.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 SUSE LLC
3// Author: Nicolai Stange <nstange@suse.de>
4
5//! [`FixedVec`] -- non-resizeable heap allocations with efficient memory
6//! footprint.
7
8#![deny(unsafe_op_in_unsafe_fn)]
9
10extern crate alloc;
11
12#[cfg(feature = "zeroize")]
13use zeroize;
14
15use core::{cmp, convert, fmt, iter, marker, mem, ops, ptr, slice};
16
17#[cfg(doc)]
18use alloc::vec::Vec;
19
20/// Error type returned for [`FixedVec`] allocation failures.
21#[derive(Clone, Copy, Debug)]
22pub struct FixedVecMemoryAllocationFailure;
23
24/// Error type returned by [`FixedVec::new_from_fn()`].
25#[derive(Clone, Copy, Debug)]
26pub enum FixedVecNewFromFnError<E: Sized> {
27 /// Memory allocation failure.
28 MemoryAllocationFailure,
29 /// The provided initialization function returned the wrapped error.
30 FnError(E),
31}
32
33impl<E: Sized> From<FixedVecMemoryAllocationFailure> for FixedVecNewFromFnError<E> {
34 fn from(_value: FixedVecMemoryAllocationFailure) -> Self {
35 Self::MemoryAllocationFailure
36 }
37}
38
39impl From<FixedVecNewFromFnError<convert::Infallible>> for FixedVecMemoryAllocationFailure {
40 fn from(value: FixedVecNewFromFnError<convert::Infallible>) -> Self {
41 match value {
42 FixedVecNewFromFnError::MemoryAllocationFailure => FixedVecMemoryAllocationFailure,
43 FixedVecNewFromFnError::FnError(e) => match e {
44 // Infallible.
45 },
46 }
47 }
48}
49
50/// Number of least significant "tag" bits in a [`FixedVec`] heap allocation
51/// pointer value reserved for encoding the length, if possible.
52const PTR_TAG_BITS: u32 = 3;
53/// Mask for the least significant [`PTR_TAG_BITS`] bits.
54const PTR_TAG_MASK: usize = (1usize << PTR_TAG_BITS) - 1;
55
56/// Manage heap allocations for the case that the [`FixedVec`]'s length is not
57/// stored within the allocated memory.
58///
59/// The heap allocation's internal format differs slightly between the cases
60/// that the [`FixedVec`]'s length is encoded in its pointer's tag bits or
61/// within the allocated memory respectively.
62///
63/// `FixedVecDataWithoutLen` implements functionality related to handling the
64/// former. The heap allocation simply comprises the [`FixedVec`]'s sequence of
65/// `T` elements.
66///
67/// # See also:
68///
69/// * [`FixedVecDataWithLen`].
70struct FixedVecDataWithoutLen<T: Sized> {
71 _phantom: marker::PhantomData<fn() -> *const T>,
72}
73
74impl<T: Sized> FixedVecDataWithoutLen<T> {
75 /// Allocate memory suitable for storing a sequence of `T` elements of
76 /// specified length.
77 ///
78 /// On success, a pointer to the allocation is returned. Use
79 /// [`data_ptr()`](Self::data_ptr) to obtain a pointer to the sequence
80 /// of `T` elements.
81 ///
82 /// # Arguments:
83 ///
84 /// * `len` - Number of `T` elements to be stored in the [`FixedVec`].
85 ///
86 /// # Safety:
87 ///
88 /// * `T` must not be a ZST and `len` must not be zero.
89 /// * On success, it's guaranteed that `len * size_of::<T>()` will not
90 /// exceed [`isize::MAX`].
91 unsafe fn try_allocate(len: usize) -> Result<ptr::NonNull<u8>, FixedVecMemoryAllocationFailure> {
92 debug_assert!(mem::size_of::<T>() != 0);
93 debug_assert!(len != 0);
94 let size = len
95 .checked_mul(mem::size_of::<T>())
96 .ok_or(FixedVecMemoryAllocationFailure)?;
97 let align = (1usize << PTR_TAG_BITS).max(mem::align_of::<T>());
98 // Note: this fails if the aligned size exceeds isize::MAX.
99 let layout = alloc::alloc::Layout::from_size_align(size, align).map_err(|_| FixedVecMemoryAllocationFailure)?;
100 // SAFETY: T is not a ZST and len is not zero, hence the layout has non-zero
101 // size.
102 let allocation_ptr = unsafe { alloc::alloc::alloc(layout) };
103 if allocation_ptr.is_null() {
104 return Err(FixedVecMemoryAllocationFailure);
105 }
106 // SAFETY: it's been checked above that the pointer is non-null.
107 Ok(unsafe { ptr::NonNull::new_unchecked(allocation_ptr) })
108 }
109
110 /// Deallocate the memory.
111 ///
112 /// # Arguments:
113 ///
114 /// * `allocation_ptr` - Pointer to the memory previously obtained from
115 /// [`try_allocate()`](Self::try_allocate).
116 ///
117 /// # Safety:
118 ///
119 /// `allocation_ptr` must have been allocated by [`Self::try_allocate()`]
120 /// with matching `len` and not been deallocated already.
121 unsafe fn deallocate(allocation_ptr: ptr::NonNull<u8>, len: usize) {
122 // No overflow checks here, they've already been passed
123 // in Self::try_allocate().
124 let size = len * mem::size_of::<T>();
125 let align = (1usize << PTR_TAG_BITS).max(mem::align_of::<T>());
126 // SAFETY: this reconstructs the very same Layout already instantiated from
127 // Self::try_allocate().
128 let layout = unsafe { alloc::alloc::Layout::from_size_align_unchecked(size, align) };
129 // SAFETY: The reconstructed layout matches that used by Self::try_allocate()
130 // when allocating the memory referenced by allocation_ptr.
131 unsafe { alloc::alloc::dealloc(allocation_ptr.as_ptr(), layout) };
132 }
133
134 /// Obtain a pointer to the sequence of `T` elements within the heap
135 /// allocation.
136 ///
137 /// # Arguments:
138 ///
139 /// * `allocation_ptr` - Pointer to the memory previously obtained from
140 /// [`try_allocate()`](Self::try_allocate).
141 ///
142 /// # Safety:
143 ///
144 /// * `allocation_ptr` must have been allocated by [`Self::try_allocate()`]
145 /// and not been [deallocated](Self::deallocate) yet.
146 /// * The returned pointer points to the beginning of contiguously
147 /// allocated, properly aligned sequence of memory slots suitable for
148 /// storing a `T` each, of number as requested for the `len` argument
149 /// initially passed to [`Self::try_allocate()`]. The backing memory's
150 /// total size does not exceed `isize::MAX`, as per the prior
151 /// [`Self::try_allocate()`] having been successful.
152 unsafe fn data_ptr(allocation_ptr: ptr::NonNull<u8>) -> ptr::NonNull<T> {
153 let data_ptr = allocation_ptr.as_ptr() as *mut T;
154 // SAFETY: the pointer had been NonNull on input, and still is.
155 unsafe { ptr::NonNull::new_unchecked(data_ptr) }
156 }
157}
158
159/// Manage heap allocations for the case that the [`FixedVec`]'s length is
160/// stored within the allocated memory.
161///
162/// The heap allocation's internal format differs slightly between the cases
163/// that the [`FixedVec`]'s length is encoded in its pointer's tag bits or
164/// within the allocated memory respectively.
165///
166/// `FixedVecDataWithLen` implements functionality related to handling the
167/// latter. The [`FixedVec`]'s length is stored first, followed by its (aligned)
168/// sequence of `T` elements.
169///
170/// # See also:
171///
172/// * [`FixedVecDataWithoutLen`].
173struct FixedVecDataWithLen<T: Sized> {
174 _phantom: marker::PhantomData<fn() -> *const T>,
175}
176
177impl<T: Sized> FixedVecDataWithLen<T> {
178 /// Allocate memory suitable for storing a sequence of `T` elements of
179 /// specified length.
180 ///
181 /// On success, a pointer to the allocation is returned. Use
182 /// [`data_ptr()`](Self::data_ptr) to obtain a pointer to the sequence
183 /// of `T` elements.
184 ///
185 /// # Arguments:
186 ///
187 /// * `len` - Number of `T` elements to be stored in the [`FixedVec`].
188 ///
189 /// # Safety:
190 ///
191 /// * `T` must not be a ZST and `len` must not be zero.
192 /// * On success, it's guaranteed that `len * size_of::<T>()` will not
193 /// exceed [`isize::MAX`].
194 unsafe fn try_allocate(len: usize) -> Result<ptr::NonNull<u8>, FixedVecMemoryAllocationFailure> {
195 debug_assert!(mem::size_of::<T>() != 0);
196 debug_assert!(len != 0);
197 let size = len
198 .checked_mul(mem::size_of::<T>())
199 .and_then(|size| size.checked_add(Self::data_offset()))
200 .ok_or(FixedVecMemoryAllocationFailure)?;
201 let align = (1usize << PTR_TAG_BITS)
202 .max(mem::align_of::<usize>())
203 .max(mem::align_of::<T>());
204 // Note: this fails if the aligned size exceeds isize::MAX.
205 let layout = alloc::alloc::Layout::from_size_align(size, align).map_err(|_| FixedVecMemoryAllocationFailure)?;
206 // SAFETY: T is not a ZST and len is not zero, hence the layout has non-zero
207 // size.
208 let allocation_ptr = unsafe { alloc::alloc::alloc(layout) };
209 if allocation_ptr.is_null() {
210 return Err(FixedVecMemoryAllocationFailure);
211 }
212 // SAFETY: the memory has been layout such that there's a properly aligned usize
213 // at the head.
214 unsafe { (allocation_ptr as *mut usize).write(len) };
215 // SAFETY: it's been checked above that the pointer is non-null.
216 Ok(unsafe { ptr::NonNull::new_unchecked(allocation_ptr) })
217 }
218
219 /// Deallocate the memory.
220 ///
221 /// # Arguments:
222 ///
223 /// * `allocation_ptr` - Pointer to the memory previously obtained from
224 /// [`try_allocate()`](Self::try_allocate).
225 ///
226 /// # Safety:
227 ///
228 /// `allocation_ptr` must have been allocated by [`Self::try_allocate()`]
229 /// with matching `len` and not been deallocated already.
230 unsafe fn deallocate(allocation_ptr: ptr::NonNull<u8>, len: usize) {
231 // No overflow checks here, they've already been passed
232 // in Self::try_allocate().
233 let size = len * mem::size_of::<T>() + Self::data_offset();
234 let align = (1usize << PTR_TAG_BITS)
235 .max(mem::align_of::<usize>())
236 .max(mem::align_of::<T>());
237 // SAFETY: this reconstructs the very same Layout already instantiated from
238 // Self::try_allocate().
239 let layout = unsafe { alloc::alloc::Layout::from_size_align_unchecked(size, align) };
240 // SAFETY: The reconstructed layout matches that used by Self::try_allocate()
241 // when allocating the memory referenced by allocation_ptr.
242 unsafe { alloc::alloc::dealloc(allocation_ptr.as_ptr(), layout) };
243 }
244
245 /// Retrieve the [`FixedVec`] length stored within the heap allocation.
246 ///
247 /// # Arguments:
248 ///
249 /// * `allocation_ptr` - Pointer to the memory previously obtained from
250 /// [`try_allocate()`](Self::try_allocate).
251 ///
252 /// # Safety:
253 ///
254 /// `allocation_ptr` must have been allocated by [`Self::try_allocate()`]
255 /// and not been [deallocated](Self::deallocate) yet.
256 unsafe fn data_len(allocation_ptr: ptr::NonNull<u8>) -> usize {
257 let ptr_to_len = allocation_ptr.as_ptr() as *const usize;
258 // SAFETY: The allocation_ptr obtained from Self::try_allocate() points at an
259 // usize at the head and has been initialized with the length value
260 // there.
261 unsafe { *ptr_to_len }
262 }
263
264 /// Offset of the sequence of `T` elements within the heap allocation.
265 const fn data_offset() -> usize {
266 assert!(mem::align_of::<T>().is_power_of_two());
267 mem::size_of::<usize>() + (mem::size_of::<usize>().wrapping_neg() & (mem::align_of::<T>() - 1))
268 }
269
270 /// Obtain a pointer to the sequence of `T` elements within the heap
271 /// allocation.
272 ///
273 /// # Arguments:
274 ///
275 /// * `allocation_ptr` - Pointer to the memory previously obtained from
276 /// [`try_allocate()`](Self::try_allocate).
277 ///
278 /// # Safety:
279 ///
280 /// * `allocation_ptr` must have been allocated by [`Self::try_allocate()`]
281 /// and not been [deallocated](Self::deallocate) yet.
282 /// * The returned pointer points to the beginning of a contiguously
283 /// allocated, properly aligned sequence of memory slots suitable for
284 /// storing a `T` each, of number as requested for the `len` argument
285 /// initially passed to [`Self::try_allocate()`]. The backing memory's
286 /// total size does not exceed `isize::MAX`, as per the prior
287 /// [`Self::try_allocate()`] having been successful.
288 unsafe fn data_ptr(allocation_ptr: ptr::NonNull<u8>) -> ptr::NonNull<T> {
289 // SAFETY: stays in the bounds of the allocation, as per the layout construction
290 // in Self::try_allocate().
291 let data_ptr = (unsafe { allocation_ptr.as_ptr().add(Self::data_offset()) }) as *mut T;
292 // SAFETY: the pointer had been NonNull on input, and still is.
293 unsafe { ptr::NonNull::new_unchecked(data_ptr) }
294 }
295}
296
297/// Representation of [`FixedVec::tagged_ptr`].
298union FixedVecTaggedPtr {
299 /// Tagged pointer to the data for non-ZST element types.
300 ///
301 /// If a [null pointer](ptr::null_mut), then the [`FixedVec`] is empty and
302 /// no memory is allocated. Otherwise, `non_zst_tagged_ptr` is a tagged
303 /// pointer, storing the tag in the least significant [`PTR_TAG_BITS`]:
304 /// * If the least significant [`PTR_TAG_BITS`] are equal to
305 /// [`PTR_TAG_MASK`], i.e. the maximum possible encodable value, then the
306 /// pointer points to a memory in the [`FixedVecDataWithLen`] format, i.e.
307 /// the [`FixedVec`]'s length is stored in front of the data in the memory
308 /// allocation.
309 /// * Otherwise the [`FixedVec`]'s length is a power of two, and the tag
310 /// encodes the base-2 logarithm thereof, relative to the [`FixedVec`]'s
311 /// `BASE_LEN_LOG2` generic parameter.
312 non_zst_tagged_ptr: *mut u8,
313 /// The [`FixedVec`] length for ZST element types.
314 ///
315 /// For ZST element types, no memory is ever allocated and
316 /// the tagged pointer always stores the [`FixedVec`] length.
317 zst_data_len: usize,
318}
319
320/// Non-resizeable heap allocations with efficient memory footprint.
321///
322/// In order to support resizing operations, a standard Rust [`struct Vec`](Vec)
323/// is relatively large: it comprises a pointer to the heap allocation itself,
324/// alongsize two [`usize`]s for the [`Vec`]'s [length](Vec::len) and
325/// [capacity](Vec::capacity) each. In situations where there are many instances
326/// thereof, and resizing is generally not needed, such that as for IO buffers,
327/// this can incur quite some unnecessary overhead.
328///
329/// `FixedVec` provides a more memory efficient alternative. Resizing operations
330/// are not supported, and therefore there is no notion of a capacity.
331/// The `FixedVec` length `usize` is stored externally within the heap
332/// allocation, except for some special values of particular relevance to the
333/// intended use-cases:
334/// * Either if the length is zero,
335/// * or if it is a power of two, with a base-2 logarithm greater or equal to
336/// the `BASE_LEN_LOG2` generic parameter, and less than some fixed bound
337/// internal to the implementation, then it will get encoded into (the least
338/// significant bits of) the heap pointer value.
339///
340/// In either case, the memory occupied by a `struct FixedVec` is always that of
341/// a (thin) pointer, and by dimensioning the `BASE_LEN_LOG2` parameter properly
342/// in accordance with the expected usecase, even the `FixedVec`'s length usize
343/// often doesn't need to get accomodated for within the heap allocation.
344pub struct FixedVec<T: Sized, const BASE_LEN_LOG2: u32> {
345 /// The tagged pointer.
346 ///
347 /// If T is a ZST, then [`FixedVecTaggedPtr::zst_data_len`] is active.
348 /// Otherwise [`FixedVecTaggedPtr::non_zst_tagged_ptr`], i.e. a tagged
349 /// pointer to the heap allocation, if any, is active.
350 tagged_ptr: FixedVecTaggedPtr,
351 _phantom: marker::PhantomData<fn() -> *const T>,
352}
353
354impl<T: Sized, const BASE_LEN_LOG2: u32> FixedVec<T, BASE_LEN_LOG2> {
355 /// Instantitate a new empty [`FixedVec`].
356 pub const fn new_empty() -> Self {
357 if mem::size_of::<T>() == 0 {
358 Self {
359 tagged_ptr: FixedVecTaggedPtr {
360 // For ZST T, the tagged_ptr's zst_data_len union field is active.
361 zst_data_len: 0,
362 },
363 _phantom: marker::PhantomData,
364 }
365 } else {
366 Self {
367 tagged_ptr: FixedVecTaggedPtr {
368 // For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is active.
369 non_zst_tagged_ptr: ptr::null_mut(),
370 },
371 _phantom: marker::PhantomData,
372 }
373 }
374 }
375
376 /// Instantiate a [`FixedVec`] of specified length and initialize its
377 /// elements with values produced by a provided function.
378 ///
379 /// Instantiate a [`FixedVec`] of length `len`, and initialize its elements
380 /// with the values produced by invoking `f` with the respective element
381 /// index. `f` may return an error, in which case its propagated back by
382 /// means of an [`FixedVecNewFromFnError::FnError`]. Otherwise, on
383 /// success of `f`, the associated [`FixedVec`] element is initialized
384 /// with the returned value.
385 ///
386 /// # Arguments:
387 ///
388 /// * `len` - The number of elements the instantiated [`FixedVec`] shall
389 /// contain.
390 /// * `f` - The provided element initialization function.
391 pub fn new_from_fn<E: Sized, F: FnMut(usize) -> Result<T, E>>(
392 len: usize,
393 mut f: F,
394 ) -> Result<Self, FixedVecNewFromFnError<E>> {
395 let (v, data_ptr) = if mem::size_of::<T>() == 0 {
396 let v = Self {
397 tagged_ptr: FixedVecTaggedPtr {
398 // For ZST T, the tagged_ptr's zst_data_len union field is active.
399 zst_data_len: len,
400 },
401 _phantom: marker::PhantomData,
402 };
403 if len == 0 {
404 return Ok(v);
405 }
406 // Don't invoke Self::drop() until all elements have been initialized.
407 (mem::ManuallyDrop::new(v), ptr::NonNull::dangling())
408 } else if len == 0 {
409 let v = Self {
410 tagged_ptr: FixedVecTaggedPtr {
411 // For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is active.
412 non_zst_tagged_ptr: ptr::null_mut(),
413 },
414 _phantom: marker::PhantomData,
415 };
416 return Ok(v);
417 } else if BASE_LEN_LOG2 < usize::BITS
418 && len.is_power_of_two()
419 && len >> BASE_LEN_LOG2 != 0
420 && len >> BASE_LEN_LOG2 < (1usize << PTR_TAG_MASK)
421 {
422 // The len value qualifies for encoding in the tagged_ptr's tag.
423 let ptr_tag = (len.ilog2() - BASE_LEN_LOG2) as usize;
424 // SAFETY: T is not a ZST and len iz not zero.
425 let untagged_ptr = unsafe { FixedVecDataWithoutLen::<T>::try_allocate(len)? };
426 // SAFETY: untagged_ptr has just been obtained from
427 // FixedVecDataWithoutLen::try_allocate().
428 let data_ptr = unsafe { FixedVecDataWithoutLen::<T>::data_ptr(untagged_ptr) };
429 let v = Self {
430 tagged_ptr: FixedVecTaggedPtr {
431 // For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is active.
432 non_zst_tagged_ptr: untagged_ptr.as_ptr().map_addr(|untagged_ptr| untagged_ptr | ptr_tag),
433 },
434 _phantom: marker::PhantomData,
435 };
436 // Don't invoke Self::drop() until all elements have been initialized.
437 (mem::ManuallyDrop::new(v), data_ptr)
438 } else {
439 // The len value does not qualify for encoding in the tagged_ptr's tag. It must
440 // get stored at the allocated memory's head.
441 let ptr_tag = PTR_TAG_MASK;
442 // SAFETY: T is not a ZST and len iz not zero.
443 let untagged_ptr = unsafe { FixedVecDataWithLen::<T>::try_allocate(len)? };
444 // SAFETY: untagged_ptr has just been obtained from
445 // FixedVecDataWithLen::try_allocate().
446 let data_ptr = unsafe { FixedVecDataWithLen::data_ptr(untagged_ptr) };
447 let v = Self {
448 // For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is active.
449 tagged_ptr: FixedVecTaggedPtr {
450 non_zst_tagged_ptr: untagged_ptr.as_ptr().map_addr(|untagged_ptr| untagged_ptr | ptr_tag),
451 },
452 _phantom: marker::PhantomData,
453 };
454 // Don't invoke Self::drop() until all elements have been initialized.
455 (mem::ManuallyDrop::new(v), data_ptr)
456 };
457
458 let mut element_ptr = data_ptr;
459 for i in 0..len {
460 let value = match f(i) {
461 Ok(value) => value,
462 Err(e) => {
463 // Drop the elements initialized so far.
464 let mut element_ptr = data_ptr;
465 for _ in 0..i {
466 // SAFETY:
467 // - If T is a ZST, then data_ptr and hence element_ptr is ptr::dangling_mut(),
468 // hence properly aligned and non-null.
469 // - Otherwise data_ptr has been obtained from either
470 // FixedVecDataWithoutLen::data_ptr() or FixedVecDataWithLen::data_ptr() on an
471 // allocation obtained from a prior successful
472 // FixedVecDataWithoutLen::try_allocate() or
473 // FixedVecDataWithLen::try_allocate() invoked with len respectively. Thus,
474 // considering the loop bounds, element_ptr is valid for reads and writes, is
475 // aligned and points at an initialized T.
476 unsafe { element_ptr.drop_in_place() };
477 // SAFETY: Either T is a ZST, or element_ptr never moves past the end of the
478 // allocation, as per the loop bounds.
479 element_ptr = unsafe { element_ptr.add(1) };
480 }
481 // Deallocate again.
482 if mem::size_of::<T>() != 0 {
483 // SAFETY: For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is
484 // active.
485 let tagged_ptr = unsafe { v.tagged_ptr.non_zst_tagged_ptr };
486 let tag = tagged_ptr.addr() & PTR_TAG_MASK;
487 let untagged_ptr = tagged_ptr.map_addr(|tagged_ptr| tagged_ptr & !PTR_TAG_MASK);
488 // SAFETY: untagged_ptr is non-null as T is not a ZST and len is != 0 when here.
489 let untagged_ptr = unsafe { ptr::NonNull::new_unchecked(untagged_ptr) };
490 if tag != PTR_TAG_MASK {
491 // SAFETY: untagged_ptr has previously been obtained above from
492 // FixedVecDataWithoutLen::try_allocate() as per the tag value and not
493 // been deallocated since.
494 unsafe { FixedVecDataWithoutLen::<T>::deallocate(untagged_ptr, len) };
495 } else {
496 // SAFETY: untagged_ptr has previously been obtained above from
497 // FixedVecDataWithLen::try_allocate() as per the tag value and not been
498 // deallocated since.
499 unsafe { FixedVecDataWithLen::<T>::deallocate(untagged_ptr, len) };
500 }
501 }
502
503 return Err(FixedVecNewFromFnError::FnError(e));
504 }
505 };
506 // SAFETY:
507 // - If T is a ZST, then data_ptr and hence element_ptr is ptr::dangling_mut(),
508 // hence properly aligned and non-null.
509 // - Otherwise data_ptr has been obtained from either
510 // FixedVecDataWithoutLen::data_ptr() or FixedVecDataWithLen::data_ptr() on an
511 // allocation obtained from a prior successful
512 // FixedVecDataWithoutLen::try_allocate() or
513 // FixedVecDataWithLen::try_allocate() invoked with len respectively. Thus,
514 // considering the loop bounds, element_ptr is valid for reads and writes and
515 // is aligned.
516 unsafe { element_ptr.write(value) };
517 // SAFETY: Either T is a ZST, or element_ptr never moves past the end of the
518 // allocation, as per the loop bounds.
519 element_ptr = unsafe { element_ptr.add(1) };
520 }
521
522 Ok(mem::ManuallyDrop::into_inner(v))
523 }
524
525 /// Returns true if the [`FixedVec`] contains no elements.
526 pub fn is_empty(&self) -> bool {
527 if mem::size_of::<T>() == 0 {
528 // SAFETY: For ZST T, the tagged_ptr's zst_data_len union field is active.
529 (unsafe { self.tagged_ptr.zst_data_len }) == 0
530 } else {
531 // SAFETY: For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is
532 // active.
533 (unsafe { self.tagged_ptr.non_zst_tagged_ptr }).is_null()
534 }
535 }
536
537 /// Returns the number of elements in the [`FixedVec`].
538 pub fn len(&self) -> usize {
539 if mem::size_of::<T>() == 0 {
540 // SAFETY: For ZST T, the tagged_ptr's zst_data_len union field is active.
541 unsafe { self.tagged_ptr.zst_data_len }
542 } else {
543 // SAFETY: For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is
544 // active.
545 let tagged_ptr = unsafe { self.tagged_ptr.non_zst_tagged_ptr };
546 if tagged_ptr.is_null() {
547 0
548 } else {
549 let tag = tagged_ptr.addr() & PTR_TAG_MASK;
550 if tag != PTR_TAG_MASK {
551 // The tag encodes the length.
552 1usize << (BASE_LEN_LOG2 + tag as u32)
553 } else {
554 // The length is stored at the allocated memory's head.
555 let untagged_ptr = tagged_ptr.map_addr(|tagged_ptr| tagged_ptr & !PTR_TAG_MASK);
556 // SAFETY: If the FixedVec had been empty, then tagged_ptr would have been null
557 // in the branch condition above. As the FixedVec isn't empty, some memory must
558 // have been allocated for it, and untagged_ptr points at that.
559 let untagged_ptr = unsafe { ptr::NonNull::new_unchecked(untagged_ptr) };
560 // SAFETY: untagged_ptr has previously been obtained from
561 // FixedVecDataWithLen::try_allocate() as per the tag value and not deallocated
562 // yet because self is still alive.
563 unsafe { FixedVecDataWithLen::<T>::data_len(untagged_ptr) }
564 }
565 }
566 }
567 }
568
569 /// Extracts a slice containing the entire [`FixedVec`].
570 ///
571 /// Equivalent to `&s[..]`.
572 pub fn as_slice(&self) -> &[T] {
573 let (data_ptr, len) = if mem::size_of::<T>() == 0 {
574 // SAFETY: For ZST T, the tagged_ptr's zst_data_len union field is active.
575 let len = unsafe { self.tagged_ptr.zst_data_len };
576 (ptr::NonNull::dangling(), len)
577 } else {
578 // SAFETY: xFor non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is
579 // active.
580 let tagged_ptr = unsafe { self.tagged_ptr.non_zst_tagged_ptr };
581 if tagged_ptr.is_null() {
582 (ptr::NonNull::dangling(), 0)
583 } else {
584 let tag = tagged_ptr.addr() & PTR_TAG_MASK;
585 let untagged_ptr = tagged_ptr.map_addr(|tagged_ptr| tagged_ptr & !PTR_TAG_MASK);
586 // SAFETY: If the FixedVec had been empty, then tagged_ptr would have been null
587 // in the branch condition above. As the FixedVec isn't empty,
588 // some memory must have been allocated for it, and untagged_ptr
589 // points at that.
590 let untagged_ptr = unsafe { ptr::NonNull::new_unchecked(untagged_ptr) };
591 if tag != PTR_TAG_MASK {
592 // The tag encodes the length and the memory contains only the data elements.
593 let len = 1usize << (BASE_LEN_LOG2 + tag as u32);
594 // SAFETY: untagged_ptr has previously been obtained from
595 // FixedVecDataWithoutLen::try_allocate() as per the tag value and not been
596 // deallocated yet because self is still alive.
597 let data_ptr = unsafe { FixedVecDataWithoutLen::data_ptr(untagged_ptr) };
598 (data_ptr, len)
599 } else {
600 // SAFETY: untagged_ptr has previously been obtained from
601 // FixedVecDataWithLen::try_allocate() as per the tag value and not been
602 // deallocated yet because self is still alive.
603 let len = unsafe { FixedVecDataWithLen::<T>::data_len(untagged_ptr) };
604 // SAFETY: likewise.
605 let data_ptr = unsafe { FixedVecDataWithLen::data_ptr(untagged_ptr) };
606 (data_ptr, len)
607 }
608 }
609 };
610
611 // SAFETY:
612 // - If T is a ZST or the FixedVec is empty, the data_ptr is
613 // ptr::dangling_mut().
614 // - Otherwise it's been obtained from either FixedVecDataWithoutLen::data_ptr()
615 // or FixedVecDataWithLen::data_ptr() on an allocation obtained from a prior
616 // successful FixedVecDataWithoutLen::try_allocate() or
617 // FixedVecDataWithLen::try_allocate() invoked with len respectively,
618 // therefore points at the to the beginning of a contiguously allocated
619 // sequence of len properly aligned memory slots suitable for storing a `T`
620 // each, and the total memory size does not exceed `isize::MAX`. All Ts have
621 // been initialized.
622 // In either case, while the constructed slice is alive, it carries a borrow on
623 // self, hence is exclusive with any writes.
624 unsafe { slice::from_raw_parts(data_ptr.as_ptr(), len) }
625 }
626
627 /// Extracts a mutable slice of the entire vector.
628 ///
629 /// Equivalent to `&mut s[..]`.
630 pub fn as_mut_slice(&mut self) -> &mut [T] {
631 let (data_ptr, len) = if mem::size_of::<T>() == 0 {
632 // SAFETY: For ZST T, the tagged_ptr's zst_data_len union field is active.
633 let len = unsafe { self.tagged_ptr.zst_data_len };
634 (ptr::NonNull::dangling(), len)
635 } else {
636 // SAFETY: For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is
637 // active.
638 let tagged_ptr = unsafe { self.tagged_ptr.non_zst_tagged_ptr };
639 if tagged_ptr.is_null() {
640 (ptr::NonNull::dangling(), 0)
641 } else {
642 let tag = tagged_ptr.addr() & PTR_TAG_MASK;
643 let untagged_ptr = tagged_ptr.map_addr(|tagged_ptr| tagged_ptr & !PTR_TAG_MASK);
644 // SAFETY: If the FixedVec had been empty, then tagged_ptr would have been null
645 // in the branch condition above. As the FixedVec isn't empty,
646 // some memory must have been allocated for it, and untagged_ptr
647 // points at that.
648 let untagged_ptr = unsafe { ptr::NonNull::new_unchecked(untagged_ptr) };
649 if tag != PTR_TAG_MASK {
650 // The tag encodes the length and the memory contains only the data elements.
651 let len = 1usize << (BASE_LEN_LOG2 + tag as u32);
652 // SAFETY: untagged_ptr has previously been obtained from
653 // FixedVecDataWithoutLen::try_allocate() as per the tag value and not been
654 // deallocated yet because self is still alive.
655 let data_ptr = unsafe { FixedVecDataWithoutLen::data_ptr(untagged_ptr) };
656 (data_ptr, len)
657 } else {
658 // SAFETY: untagged_ptr has previously been obtained from
659 // FixedVecDataWithLen::try_allocate() as per the tag value and not been
660 // deallocated yet because self is still alive.
661 let len = unsafe { FixedVecDataWithLen::<T>::data_len(untagged_ptr) };
662 // SAFETY: likewise.
663 let data_ptr = unsafe { FixedVecDataWithLen::data_ptr(untagged_ptr) };
664 (data_ptr, len)
665 }
666 }
667 };
668
669 // SAFETY:
670 // - If T is a ZST or the FixedVec is empty, the data_ptr is
671 // ptr::dangling_mut().
672 // - Otherwise it's been obtained from either FixedVecDataWithoutLen::data_ptr()
673 // or FixedVecDataWithLen::data_ptr() on an allocation obtained from a prior
674 // successful FixedVecDataWithoutLen::try_allocate() or
675 // FixedVecDataWithLen::try_allocate() invoked with len respectively,
676 // therefore points at the to the beginning of a contiguously allocated
677 // sequence of len properly aligned memory slots suitable for storing a `T`
678 // each, and the total memory size does not exceed `isize::MAX`. All Ts have
679 // been initialized.
680 // In either case, while the constructed slice is alive, all accesses to the
681 // backing memory will be made exclusively through it, because it
682 // carries a borrow on self.
683 unsafe { slice::from_raw_parts_mut(data_ptr.as_ptr(), len) }
684 }
685}
686
687impl<T: Sized, const BASE_LEN_LOG2: u32> Drop for FixedVec<T, BASE_LEN_LOG2> {
688 fn drop(&mut self) {
689 if mem::size_of::<T>() == 0 {
690 // SAFETY: For ZST T, the tagged_ptr's zst_data_len union field is active.
691 let len = unsafe { self.tagged_ptr.zst_data_len };
692 let data_ptr = ptr::dangling_mut::<T>();
693 let mut element_ptr = data_ptr;
694 for _ in 0..len {
695 // SAFETY: T is a ZST, and element_ptr is ptr::dangling_mut(), so aligned and
696 // non-null.
697 unsafe { element_ptr.drop_in_place() };
698 // SAFETY: T is a ZST, so this is a nop.
699 element_ptr = unsafe { element_ptr.add(1) };
700 }
701 } else {
702 // SAFETY: For non-ZST T, the tagged_ptr's non_zst_tagged_ptr union field is
703 // active.
704 let tagged_ptr = unsafe { self.tagged_ptr.non_zst_tagged_ptr };
705 if !tagged_ptr.is_null() {
706 let tag = tagged_ptr.addr() & PTR_TAG_MASK;
707 let untagged_ptr = tagged_ptr.map_addr(|tagged_ptr| tagged_ptr & !PTR_TAG_MASK);
708 // SAFETY: If the FixedVec had been empty, then tagged_ptr would have been null
709 // in the branch condition above. As the FixedVec isn't empty,
710 // some memory must have been allocated for it, and untagged_ptr
711 // points at that.
712 let untagged_ptr = unsafe { ptr::NonNull::new_unchecked(untagged_ptr) };
713 let (data_ptr, len) = if tag != PTR_TAG_MASK {
714 // The tag encodes the length and the memory contains only the data elements.
715 let len = 1usize << (BASE_LEN_LOG2 + tag as u32);
716 // SAFETY: untagged_ptr has previously been obtained from
717 // FixedVecDataWithoutLen::try_allocate() as per the tag value and not been
718 // deallocated already because self is only about to get dropped now.
719 let data_ptr = unsafe { FixedVecDataWithoutLen::<T>::data_ptr(untagged_ptr) };
720 (data_ptr, len)
721 } else {
722 // SAFETY: untagged_ptr has previously been obtained from
723 // FixedVecDataWithLen::try_allocate() as per the tag value and not been
724 // deallocated already because self is only about to get dropped now.
725 let len = unsafe { FixedVecDataWithLen::<T>::data_len(untagged_ptr) };
726 // SAFETY: likewise.
727 let data_ptr = unsafe { FixedVecDataWithLen::<T>::data_ptr(untagged_ptr) };
728 (data_ptr, len)
729 };
730
731 let mut element_ptr = data_ptr;
732 for _ in 0..len {
733 // - SAFETY: data_ptr has been obtained from either
734 // FixedVecDataWithoutLen::data_ptr() or FixedVecDataWithLen::data_ptr() on an
735 // allocation obtained from a prior successful
736 // FixedVecDataWithoutLen::try_allocate() or
737 // FixedVecDataWithLen::try_allocate() invoked with len respectively. Thus,
738 // considering the loop bounds, element_ptr is valid for reads and writes, is
739 // aligned and points at an initialized T.
740 unsafe { element_ptr.drop_in_place() };
741 // SAFETY: element_ptr never moves past the end of the allocation,
742 // as per the loop bounds.
743 element_ptr = unsafe { element_ptr.add(1) };
744 }
745
746 if tag != PTR_TAG_MASK {
747 // SAFETY: untagged_ptr has previously been obtained from
748 // FixedVecDataWithoutLen::try_allocate() as per the tag value and not been
749 // deallocated already because self is only about to get dropped now.
750 unsafe { FixedVecDataWithoutLen::<T>::deallocate(untagged_ptr, len) };
751 } else {
752 // SAFETY: untagged_ptr has previously been obtained from
753 // FixedVecDataWithLen::try_allocate() as per the tag value and not been
754 // deallocated already because self is only about to get dropped now.
755 unsafe { FixedVecDataWithLen::<T>::deallocate(untagged_ptr, len) };
756 }
757 }
758 }
759 }
760}
761
762impl<T: Sized + Default, const BASE_LEN_LOG2: u32> FixedVec<T, BASE_LEN_LOG2> {
763 /// Instantiate a [`FixedVec`] of specified length and default-initialize
764 /// its elements.
765 ///
766 /// Instantiate a [`FixedVec`] of length `len`, and initialize all its
767 /// elements with `T::default()`.
768 ///
769 /// # Arguments:
770 ///
771 /// * `len` - The number of elements the instantiated [`FixedVec`] shall
772 /// contain.
773 pub fn new_with_default(len: usize) -> Result<Self, FixedVecMemoryAllocationFailure> {
774 Self::new_from_fn(len, |_| -> Result<T, convert::Infallible> { Ok(T::default()) })
775 .map_err(FixedVecMemoryAllocationFailure::from)
776 }
777}
778
779impl<T: Sized + Clone, const BASE_LEN_LOG2: u32> FixedVec<T, BASE_LEN_LOG2> {
780 /// Instantiate a [`FixedVec`] of specified length and initialize all its
781 /// elements with a given value.
782 ///
783 /// Instantiate a [`FixedVec`] of length `len`, and initialize its elements
784 /// with `value`.
785 ///
786 /// # Arguments:
787 ///
788 /// * `len` - The number of elements the instantiated [`FixedVec`] shall
789 /// contain.
790 /// * `value` - The element initialization value.
791 pub fn new_with_value(len: usize, value: T) -> Result<Self, FixedVecMemoryAllocationFailure> {
792 Self::new_from_fn(len, |_| -> Result<T, convert::Infallible> { Ok(value.clone()) })
793 .map_err(FixedVecMemoryAllocationFailure::from)
794 }
795}
796
797impl<T: Sized, const BASE_LEN_LOG2: u32> ops::Deref for FixedVec<T, BASE_LEN_LOG2> {
798 type Target = [T];
799 fn deref(&self) -> &Self::Target {
800 self.as_slice()
801 }
802}
803
804impl<T: Sized, const BASE_LEN_LOG2: u32> ops::DerefMut for FixedVec<T, BASE_LEN_LOG2> {
805 fn deref_mut(&mut self) -> &mut Self::Target {
806 self.as_mut_slice()
807 }
808}
809
810impl<T: Sized, const BASE_LEN_LOG2: u32> convert::AsRef<[T]> for FixedVec<T, BASE_LEN_LOG2> {
811 fn as_ref(&self) -> &[T] {
812 self
813 }
814}
815
816impl<T: Sized, const BASE_LEN_LOG2: u32> convert::AsMut<[T]> for FixedVec<T, BASE_LEN_LOG2> {
817 fn as_mut(&mut self) -> &mut [T] {
818 self
819 }
820}
821
822impl<T: Sized, I: slice::SliceIndex<[T]>, const BASE_LEN_LOG2: u32> ops::Index<I> for FixedVec<T, BASE_LEN_LOG2> {
823 type Output = I::Output;
824
825 fn index(&self, index: I) -> &Self::Output {
826 &ops::Deref::deref(self)[index]
827 }
828}
829
830impl<T: Sized, I: slice::SliceIndex<[T]>, const BASE_LEN_LOG2: u32> ops::IndexMut<I> for FixedVec<T, BASE_LEN_LOG2> {
831 fn index_mut(&mut self, index: I) -> &mut Self::Output {
832 &mut ops::DerefMut::deref_mut(self)[index]
833 }
834}
835
836impl<T: Sized, const BASE_LEN_LOG2: u32> Default for FixedVec<T, BASE_LEN_LOG2> {
837 fn default() -> Self {
838 Self::new_empty()
839 }
840}
841
842impl<T: Sized + Clone, const BASE_LEN_LOG2: u32> Clone for FixedVec<T, BASE_LEN_LOG2> {
843 fn clone(&self) -> Self {
844 Self::new_from_fn(self.len(), |i| -> Result<T, convert::Infallible> {
845 Ok((*self)[i].clone())
846 })
847 .unwrap()
848 }
849}
850
851impl<'a, T: Sized + Clone, const BASE_LEN_LOG2: u32> iter::IntoIterator for &'a FixedVec<T, BASE_LEN_LOG2> {
852 type Item = &'a T;
853 type IntoIter = slice::Iter<'a, T>;
854
855 fn into_iter(self) -> Self::IntoIter {
856 (**self).iter()
857 }
858}
859
860impl<'a, T: Sized + Clone, const BASE_LEN_LOG2: u32> iter::IntoIterator for &'a mut FixedVec<T, BASE_LEN_LOG2> {
861 type Item = &'a mut T;
862 type IntoIter = slice::IterMut<'a, T>;
863
864 fn into_iter(self) -> Self::IntoIter {
865 (**self).iter_mut()
866 }
867}
868
869impl<T: Sized + cmp::PartialEq, const BASE_LEN_LOG2: u32> cmp::PartialEq for FixedVec<T, BASE_LEN_LOG2> {
870 fn eq(&self, other: &Self) -> bool {
871 self.as_slice().eq(other.as_slice())
872 }
873}
874
875impl<T: Sized + cmp::Eq, const BASE_LEN_LOG2: u32> cmp::Eq for FixedVec<T, BASE_LEN_LOG2> {}
876
877// SAFETY: if T is Send, then so is the FixedVec.
878unsafe impl<T: Sized + marker::Send, const BASE_LEN_LOG2: u32> marker::Send for FixedVec<T, BASE_LEN_LOG2> {}
879
880// SAFETY: if T is Sync, then so is the FixedVec.
881unsafe impl<T: Sized + marker::Sync, const BASE_LEN_LOG2: u32> marker::Sync for FixedVec<T, BASE_LEN_LOG2> {}
882
883impl<T: Sized + fmt::Debug, const BASE_LEN_LOG2: u32> fmt::Debug for FixedVec<T, BASE_LEN_LOG2> {
884 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
885 fmt::Debug::fmt(&**self, f)
886 }
887}
888
889#[cfg(feature = "zeroize")]
890impl<T: Sized + zeroize::Zeroize, const BASE_LEN_LOG2: u32> zeroize::Zeroize for FixedVec<T, BASE_LEN_LOG2> {
891 fn zeroize(&mut self) {
892 for element in self.iter_mut() {
893 element.zeroize();
894 }
895 }
896}
897
898#[cfg(feature = "zeroize")]
899impl<T: Sized + zeroize::ZeroizeOnDrop, const BASE_LEN_LOG2: u32> zeroize::ZeroizeOnDrop
900 for FixedVec<T, BASE_LEN_LOG2>
901{
902}
903
904#[cfg(test)]
905fn test_one<T: Sized + Default + Clone + cmp::Eq + fmt::Debug, const BASE_LEN_LOG2: u32>(test_value: T) {
906 let mut v_empty = FixedVec::<T, BASE_LEN_LOG2>::new_empty();
907 assert!(v_empty.is_empty());
908 assert_eq!(v_empty.len(), 0);
909 assert_eq!(v_empty.iter().count(), 0);
910 assert_eq!(v_empty.iter_mut().count(), 0);
911
912 for len in [
913 0usize,
914 (1usize << BASE_LEN_LOG2) - 1,
915 (1usize << BASE_LEN_LOG2),
916 (1usize << BASE_LEN_LOG2) + 1,
917 1usize << (BASE_LEN_LOG2 + PTR_TAG_MASK as u32 - 1),
918 1usize << (BASE_LEN_LOG2 + PTR_TAG_MASK as u32),
919 1usize << (BASE_LEN_LOG2 + PTR_TAG_MASK as u32 + 1),
920 ] {
921 let mut v0 = FixedVec::<T, BASE_LEN_LOG2>::new_with_default(len).unwrap();
922 assert_eq!(v0.is_empty(), len == 0);
923 assert_eq!(v0.len(), len);
924 assert_eq!(v0.iter().count(), len);
925 assert_eq!(v0.iter_mut().count(), len);
926 for element in v0.iter() {
927 assert_eq!(*element, T::default());
928 }
929 for element in v0.iter_mut() {
930 *element = test_value.clone();
931 }
932
933 let mut v1 = FixedVec::<T, BASE_LEN_LOG2>::new_with_value(len, test_value.clone()).unwrap();
934 assert_eq!(v1.is_empty(), len == 0);
935 assert_eq!(v1.len(), len);
936 assert_eq!(v1.iter().count(), len);
937 assert_eq!(v1.iter_mut().count(), len);
938 for element in v1.iter() {
939 assert_eq!(*element, test_value.clone());
940 }
941
942 assert_eq!(v0.as_slice(), v1.as_slice());
943
944 assert!(
945 len == 0
946 || matches!(
947 FixedVec::<T, BASE_LEN_LOG2>::new_from_fn(len, |i| {
948 if i != len - 1 { Ok(T::default()) } else { Err(()) }
949 },),
950 Err(FixedVecNewFromFnError::FnError(()))
951 )
952 );
953 }
954}
955
956#[test]
957fn fixed_vec_u32_0() {
958 test_one::<u32, 0>(42);
959}
960
961#[test]
962fn fixed_vec_u32_2() {
963 test_one::<u32, 2>(42);
964}
965
966#[cfg(test)]
967use core::sync;
968
969#[cfg(test)]
970static TEST_ZST_INSTANCES: sync::atomic::AtomicUsize = sync::atomic::AtomicUsize::new(0);
971
972#[cfg(test)]
973#[derive(Debug, PartialEq, Eq)]
974struct TestZST;
975
976#[cfg(test)]
977impl Default for TestZST {
978 fn default() -> Self {
979 TEST_ZST_INSTANCES.fetch_add(1, sync::atomic::Ordering::Relaxed);
980 TestZST
981 }
982}
983
984#[cfg(test)]
985impl Clone for TestZST {
986 fn clone(&self) -> Self {
987 Self::default()
988 }
989}
990
991#[cfg(test)]
992impl Drop for TestZST {
993 fn drop(&mut self) {
994 TEST_ZST_INSTANCES.fetch_sub(1, sync::atomic::Ordering::Relaxed);
995 }
996}
997
998#[test]
999fn fixed_vec_zst() {
1000 fn _fixed_vec_zst() {
1001 test_one::<TestZST, 0>(TestZST::default());
1002 test_one::<TestZST, 2>(TestZST::default());
1003 }
1004 _fixed_vec_zst();
1005 assert_eq!(TEST_ZST_INSTANCES.load(sync::atomic::Ordering::Relaxed), 0);
1006}