1use core::marker::PhantomData;
2use core::mem::ManuallyDrop;
3use core::mem::MaybeUninit;
4use core::ptr::NonNull;
5use core::slice;
67use crate::alloc::alloc;
8use crate::alloc::dealloc;
9use crate::alloc::handle_alloc_error;
10use crate::index::Concrete;
11use crate::params::Params;
12use crate::params::ParamsExt;
1314/// A fixed-size array with cache-line-aligned allocation.
15#[repr(transparent)]
16pub(crate) struct Array<T, P>
17where
18P: Params + ?Sized,
19{
20 nonnull: NonNull<T>,
21 phantom: PhantomData<P>,
22}
2324impl<T, P> Array<T, P>
25where
26P: Params + ?Sized,
27{
28/// Creates an array where each element is produced by calling `init` with
29 /// that element’s index while walking forward through the array.
30#[inline]
31pub(crate) fn new<F>(init: F) -> Self
32where
33F: Fn(usize, &mut MaybeUninit<T>),
34 {
35let this: Array<MaybeUninit<T>, P> = Self::new_uninit();
3637for index in 0..P::LENGTH.as_usize() {
38// SAFETY:
39 // - `index` is strictly less than `P::LENGTH`.
40 // - The allocation performed by `new_uninit` reserves space for exactly
41 // `P::LENGTH` contiguous elements.
42 // - The pointer returned by `as_non_null` is properly aligned for `T`.
43 // - We have exclusive access to the allocation, so creating a unique
44 // mutable reference is sound.
45init(index, unsafe { this.as_non_null().add(index).as_mut() });
46 }
4748// SAFETY: The loop above initializes every element in the allocation
49 // exactly once, so all `P::LENGTH` elements are initialized.
50unsafe { this.assume_init() }
51 }
5253/// Constructs a new array with uninitialized contents.
54#[inline]
55pub(crate) fn new_uninit() -> Array<MaybeUninit<T>, P> {
56// SAFETY:
57 // - `P::LAYOUT` describes a non-zero-sized allocation.
58 // - Its size and alignment have been validated when constructing the
59 // associated `Params` implementation.
60let raw: *mut u8 = unsafe { alloc(P::LAYOUT) };
6162Array {
63 nonnull: match NonNull::new(raw.cast()) {
64Some(ptr) => ptr,
65None => handle_alloc_error(P::LAYOUT),
66 },
67 phantom: PhantomData,
68 }
69 }
7071/// Returns a `NonNull` pointer to the array's buffer.
72#[inline]
73pub(crate) const fn as_non_null(&self) -> NonNull<T> {
74self.nonnull
75 }
7677/// Returns a raw pointer to the array's buffer.
78#[cfg(test)]
79 #[inline]
80pub(crate) const fn as_ptr(&self) -> *const T {
81self.as_non_null().as_ptr()
82 }
8384/// Returns a raw mutable pointer to the array's buffer.
85#[inline]
86pub(crate) const fn as_mut_ptr(&self) -> *mut T {
87self.as_non_null().as_ptr()
88 }
8990/// Extracts a slice containing the entire array.
91#[cfg(test)]
92 #[inline]
93pub(crate) const fn as_slice(&self) -> &[T] {
94// SAFETY:
95 // - The allocation contains `P::LENGTH` contiguous elements of `T`.
96 // - For `Array<T, P>`, all elements are guaranteed to be initialized.
97 // - The pointer is valid for reads for the entire range.
98unsafe { slice::from_raw_parts(self.as_ptr(), P::LENGTH.as_usize()) }
99 }
100101/// Extracts a mutable slice of the entire array.
102#[inline]
103pub(crate) const fn as_mut_slice(&mut self) -> &mut [T] {
104// SAFETY:
105 // - The allocation contains `P::LENGTH` contiguous elements of `T`.
106 // - For `Array<T, P>`, all elements are guaranteed to be initialized.
107 // - `&mut self` guarantees unique access to the allocation.
108unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), P::LENGTH.as_usize()) }
109 }
110111/// Returns a reference to the element at `index`.
112#[inline]
113pub(crate) const fn get(&self, index: Concrete<P>) -> &T {
114// SAFETY: `Concrete<P>` ensures that the underlying index is strictly less
115 // than `P::LENGTH`, so it is within bounds.
116unsafe { self.get_unchecked(index.get()) }
117 }
118119/// Returns a reference to the element at `index`, without doing bounds
120 /// checking.
121 ///
122 /// # Safety
123 ///
124 /// `index` must be strictly less than `P::LENGTH`. Passing an out-of-bounds
125 /// index results in undefined behavior, even if the returned reference is not
126 /// used.
127#[inline]
128pub(crate) const unsafe fn get_unchecked(&self, index: usize) -> &T {
129if true {
if !(index < P::LENGTH.as_usize()) {
{
::core::panicking::panic_fmt(format_args!("Array::get_unchecked requires that the index is in bounds"));
}
};
};debug_assert!(
130 index < P::LENGTH.as_usize(),
131"Array::get_unchecked requires that the index is in bounds",
132 );
133134// SAFETY:
135 // - The caller guarantees `index < P::LENGTH`.
136 // - The allocation holds `P::LENGTH` contiguous elements.
137 // - The pointer is properly aligned and valid for reads.
138unsafe { self.as_non_null().add(index).as_ref() }
139 }
140}
141142impl<T, P> Array<MaybeUninit<T>, P>
143where
144P: Params + ?Sized,
145{
146/// Converts to `Array<T, P>`.
147 ///
148 /// # Safety
149 ///
150 /// The caller must guarantee that all elements in the allocation are fully
151 /// initialized. If any element is uninitialized, converting to `Array<T, P>`
152 /// results in immediate undefined behavior.
153#[inline]
154pub(crate) unsafe fn assume_init(self) -> Array<T, P> {
155// SAFETY:
156 // - The caller guarantees that all elements are initialized.
157 // - `Array<MaybeUninit<T>, P>` and `Array<T, P>` have identical layout.
158 // - `ManuallyDrop` prevents `self` from being dropped, so the allocation is
159 // not freed during the conversion.
160Array {
161 nonnull: ManuallyDrop::new(self).as_non_null().cast(),
162 phantom: PhantomData,
163 }
164 }
165}
166167impl<T, P> Dropfor Array<T, P>
168where
169P: Params + ?Sized,
170{
171fn drop(&mut self) {
172// SAFETY:
173 // - The allocation was created with `alloc(P::LAYOUT)` in `new_uninit`.
174 // - `P::LAYOUT` is the exact layout used for allocation.
175 // - `self.nonnull` still points to the original allocation.
176unsafe {
177dealloc(self.as_non_null().cast().as_ptr(), P::LAYOUT);
178 }
179 }
180}
181182// -----------------------------------------------------------------------------
183// Tests
184// -----------------------------------------------------------------------------
185186#[cfg_attr(coverage_nightly, coverage(off))]
187#[cfg(test)]
188mod tests {
189use crate::array::Array;
190use crate::index::Concrete;
191use crate::params::CACHE_LINE;
192use crate::params::Params;
193use crate::utils::each_capacity;
194195#[cfg_attr(loom, ignore = "loom does not run this test")]
196 #[test]
197fn alignment() {
198each_capacity!({
199let array: Array<usize, P> = Array::new(|_, slot| {
200 slot.write(0);
201 });
202203// TODO: ptr::is_aligned_to once stable
204assert_eq!(array.as_ptr().addr() & (CACHE_LINE - 1), 0);
205 });
206 }
207208#[cfg_attr(loom, ignore = "loom does not run this test")]
209 #[test]
210fn get() {
211each_capacity!({
212let array: Array<usize, P> = Array::new(|index, slot| {
213 slot.write(index);
214 });
215216for index in 0..P::LENGTH.as_usize() {
217assert_eq!(array.get(Concrete::<P>::new(index)), &index);
218 }
219 });
220 }
221222#[cfg_attr(loom, ignore = "loom does not run this test")]
223 #[test]
224fn as_slice() {
225each_capacity!({
226let array: Array<usize, P> = Array::new(|index, slot| {
227 slot.write(index);
228 });
229230assert_eq!(array.as_slice().len(), P::LENGTH.as_usize());
231232for (index, value) in array.as_slice().iter().enumerate() {
233assert_eq!(*value, index);
234 }
235 });
236 }
237238#[cfg_attr(loom, ignore = "loom does not run this test")]
239 #[test]
240fn as_mut_slice() {
241each_capacity!({
242let mut array: Array<usize, P> = Array::new(|index, slot| {
243 slot.write(index);
244 });
245246assert_eq!(array.as_mut_slice().len(), P::LENGTH.as_usize());
247248for (index, value) in array.as_mut_slice().iter_mut().enumerate() {
249assert_eq!(*value, index);
250*value += 1;
251 }
252253for (index, value) in array.as_slice().iter().enumerate() {
254assert_eq!(*value, index + 1);
255 }
256 });
257 }
258}