1use core::mem::MaybeUninit;
8use core::ops::{Deref, DerefMut};
9use core::pin::Pin;
10use kalloc::{AllocError, Allocator, Box, DefaultAllocator};
11use pin_init::Init;
12
13#[repr(C)]
23pub struct InlineArray<T, const N: usize, A: Allocator = DefaultAllocator> {
24 count: usize,
27
28 ptr: *mut T,
34
35 inline_storage: [MaybeUninit<T>; N],
39
40 allocator: A,
44}
45
46zr::static_assert!(core::mem::size_of::<InlineArray<u32, 4>>() == 32);
47zr::static_assert!(core::mem::align_of::<InlineArray<u32, 4>>() == 8);
48
49impl<T, const N: usize, A: Allocator> InlineArray<T, N, A> {
50 pub fn init_in_with<F>(count: usize, allocator: A, mut f: F) -> impl Init<Self, AllocError>
55 where
56 F: FnMut() -> T,
57 {
58 let is_inline = count <= N;
59 let inline_count = if is_inline { count } else { 0 };
61 pin_init::init!(Self {
64 ptr: if is_inline {
65 core::ptr::null_mut()
66 } else {
67 let mut heap_data = Box::try_new_uninit_slice_in(count, allocator.clone())?;
68 for item in heap_data.iter_mut() {
69 item.write(f());
70 }
71 let heap_data = unsafe { heap_data.assume_init() };
73 Box::into_raw_with_allocator(heap_data).0 as *mut T
74 },
75 inline_storage <- pin_init::init_array_from_fn(|i| {
76 if i < inline_count { MaybeUninit::new(f()) } else { MaybeUninit::uninit() }
77 }),
78 allocator,
79 count,
80 }? AllocError)
81 }
82
83 pub fn init_in(count: usize, allocator: A) -> impl Init<Self, AllocError>
86 where
87 T: Default,
88 {
89 Self::init_in_with(count, allocator, T::default)
90 }
91
92 pub fn try_new_in_with<F>(count: usize, allocator: A, f: F) -> Result<Self, AllocError>
95 where
96 F: FnMut() -> T,
97 {
98 let mut slot = MaybeUninit::<Self>::uninit();
99 let init = Self::init_in_with(count, allocator, f);
100 unsafe {
103 init.__init(slot.as_mut_ptr())?;
104 Ok(slot.assume_init())
105 }
106 }
107
108 pub fn try_new_in(count: usize, allocator: A) -> Result<Self, AllocError>
111 where
112 T: Default,
113 {
114 Self::try_new_in_with(count, allocator, T::default)
115 }
116
117 const fn is_inline(&self) -> bool {
119 self.count <= N
120 }
121
122 pub const fn len(&self) -> usize {
124 self.count
125 }
126
127 pub const fn is_empty(&self) -> bool {
129 self.count == 0
130 }
131
132 pub fn as_slice(&self) -> &[T] {
134 if self.is_inline() {
135 unsafe { self.inline_storage[..self.count].assume_init_ref() }
137 } else {
138 unsafe { core::slice::from_raw_parts(self.ptr, self.count) }
140 }
141 }
142
143 pub fn as_mut_slice(&mut self) -> &mut [T] {
145 if self.is_inline() {
146 unsafe { self.inline_storage[..self.count].assume_init_mut() }
148 } else {
149 unsafe { core::slice::from_raw_parts_mut(self.ptr, self.count) }
151 }
152 }
153
154 pub fn as_mut_slice_pinned(self: Pin<&mut Self>) -> &mut [T] {
156 unsafe { self.get_unchecked_mut().as_mut_slice() }
159 }
160}
161
162impl<T, const N: usize> InlineArray<T, N, DefaultAllocator> {
163 pub fn init(count: usize) -> impl Init<Self, AllocError>
166 where
167 T: Default,
168 {
169 Self::init_in(count, DefaultAllocator)
170 }
171
172 pub fn init_with<F>(count: usize, f: F) -> impl Init<Self, AllocError>
175 where
176 F: FnMut() -> T,
177 {
178 Self::init_in_with(count, DefaultAllocator, f)
179 }
180
181 pub fn try_new(count: usize) -> Result<Self, AllocError>
184 where
185 T: Default,
186 {
187 Self::try_new_in(count, DefaultAllocator)
188 }
189
190 pub fn try_new_with<F>(count: usize, f: F) -> Result<Self, AllocError>
193 where
194 F: FnMut() -> T,
195 {
196 Self::try_new_in_with(count, DefaultAllocator, f)
197 }
198}
199
200impl<T, const N: usize, A: Allocator> Deref for InlineArray<T, N, A> {
201 type Target = [T];
202 fn deref(&self) -> &Self::Target {
203 self.as_slice()
204 }
205}
206
207impl<T, const N: usize, A: Allocator> DerefMut for InlineArray<T, N, A> {
208 fn deref_mut(&mut self) -> &mut Self::Target {
209 self.as_mut_slice()
210 }
211}
212
213impl<T, const N: usize, A: Allocator> Drop for InlineArray<T, N, A> {
214 fn drop(&mut self) {
215 if self.is_inline() {
216 unsafe {
217 self.inline_storage[..self.count].assume_init_drop();
218 }
219 } else {
220 unsafe {
224 let _ = Box::from_raw_in(
225 core::ptr::slice_from_raw_parts_mut(self.ptr, self.count),
226 self.allocator.clone(),
227 );
228 }
229 }
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236 use core::cell::Cell;
237 use core::ptr::NonNull;
238
239 #[derive(Debug, PartialEq, Eq)]
240 struct TestState {
241 live_obj_count: Cell<usize>,
242 ctor_count: Cell<usize>,
243 dtor_count: Cell<usize>,
244 alloc_count: Cell<usize>,
245 fail_threshold: Cell<usize>,
246 }
247
248 impl Default for TestState {
249 fn default() -> Self {
250 Self {
251 live_obj_count: Cell::new(0),
252 ctor_count: Cell::new(0),
253 dtor_count: Cell::new(0),
254 alloc_count: Cell::new(0),
255 fail_threshold: Cell::new(usize::MAX),
256 }
257 }
258 }
259
260 #[derive(Clone)]
261 struct TestAllocator<'a> {
262 state: &'a TestState,
263 }
264
265 impl<'a> Allocator for TestAllocator<'a> {
266 fn allocate(&self, layout: core::alloc::Layout) -> Result<NonNull<[u8]>, AllocError> {
267 let current = self.state.alloc_count.get();
268 self.state.alloc_count.set(current + 1);
269 if current >= self.state.fail_threshold.get() {
270 return Err(AllocError);
271 }
272 DefaultAllocator::default().allocate(layout)
273 }
274
275 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: core::alloc::Layout) {
276 unsafe { DefaultAllocator::default().deallocate(ptr, layout) }
277 }
278
279 unsafe fn grow(
280 &self,
281 ptr: NonNull<u8>,
282 old_layout: core::alloc::Layout,
283 new_layout: core::alloc::Layout,
284 ) -> Result<NonNull<[u8]>, AllocError> {
285 let current = self.state.alloc_count.get();
286 self.state.alloc_count.set(current + 1);
287 if current >= self.state.fail_threshold.get() {
288 return Err(AllocError);
289 }
290 unsafe { DefaultAllocator::default().grow(ptr, old_layout, new_layout) }
291 }
292
293 unsafe fn shrink(
294 &self,
295 ptr: NonNull<u8>,
296 old_layout: core::alloc::Layout,
297 new_layout: core::alloc::Layout,
298 ) -> Result<NonNull<[u8]>, AllocError> {
299 let current = self.state.alloc_count.get();
300 self.state.alloc_count.set(current + 1);
301 if current >= self.state.fail_threshold.get() {
302 return Err(AllocError);
303 }
304 unsafe { DefaultAllocator::default().shrink(ptr, old_layout, new_layout) }
305 }
306
307 fn allocate_zeroed(
308 &self,
309 layout: core::alloc::Layout,
310 ) -> Result<NonNull<[u8]>, AllocError> {
311 let current = self.state.alloc_count.get();
312 self.state.alloc_count.set(current + 1);
313 if current >= self.state.fail_threshold.get() {
314 return Err(AllocError);
315 }
316 DefaultAllocator::default().allocate_zeroed(layout)
317 }
318 }
319
320 #[derive(Debug)]
321 struct TestObject<'a> {
322 state: &'a TestState,
323 }
324
325 impl<'a> TestObject<'a> {
326 fn new(state: &'a TestState) -> Self {
327 state.live_obj_count.set(state.live_obj_count.get() + 1);
328 state.ctor_count.set(state.ctor_count.get() + 1);
329 TestObject { state }
330 }
331 }
332
333 impl<'a> Drop for TestObject<'a> {
334 fn drop(&mut self) {
335 self.state.live_obj_count.set(self.state.live_obj_count.get() - 1);
336 self.state.dtor_count.set(self.state.dtor_count.get() + 1);
337 }
338 }
339
340 #[test]
341 fn test_inline() {
342 let state = TestState::default();
343
344 for sz in 0..=3 {
345 state.ctor_count.set(0);
346 state.dtor_count.set(0);
347 {
348 let ia =
349 InlineArray::<TestObject<'_>, 3>::try_new_with(sz, || TestObject::new(&state))
350 .unwrap();
351 assert_eq!(ia.len(), sz);
352 }
353 assert_eq!(state.ctor_count.get(), sz);
354 assert_eq!(state.dtor_count.get(), sz);
355 }
356 }
357
358 #[test]
359 fn test_non_inline() {
360 let state = TestState::default();
361
362 let test_sizes = [4, 5, 6, 10, 100];
363
364 for &sz in &test_sizes {
365 state.ctor_count.set(0);
366 state.dtor_count.set(0);
367 {
368 let ia =
369 InlineArray::<TestObject<'_>, 3>::try_new_with(sz, || TestObject::new(&state))
370 .unwrap();
371 assert_eq!(ia.len(), sz);
372 }
373 assert_eq!(state.ctor_count.get(), sz);
374 assert_eq!(state.dtor_count.get(), sz);
375 }
376 }
377
378 #[test]
379 fn test_allocation_failure() {
380 let state = TestState::default();
381 state.fail_threshold.set(0); let ia = InlineArray::<u32, 3, TestAllocator<'_>>::try_new_in(
385 3,
386 TestAllocator { state: &state },
387 );
388 assert!(ia.is_ok());
389
390 let ia = InlineArray::<u32, 3, TestAllocator<'_>>::try_new_in(
392 4,
393 TestAllocator { state: &state },
394 );
395 assert!(ia.is_err());
396 }
397
398 #[test]
399 fn test_pinned_init() {
400 let state = TestState::default();
401
402 for sz in 0..=3 {
403 state.ctor_count.set(0);
404 state.dtor_count.set(0);
405 {
406 pin_init::stack_try_pin_init!(
407 let ia = InlineArray::<TestObject<'_>, 3>::init_with(sz, || {
408 TestObject::new(&state)
409 })
410 );
411 let ia = ia.unwrap();
412 assert_eq!(ia.len(), sz);
413 }
414 assert_eq!(state.ctor_count.get(), sz);
415 assert_eq!(state.dtor_count.get(), sz);
416 }
417 }
418}