department/statics/
multi.rs

1use core::alloc::Layout;
2#[cfg(feature = "unsize")]
3use core::marker::Unsize;
4use core::mem;
5use core::ptr::{NonNull, Pointee};
6
7use super::StorageCell;
8use crate::base::{ExactSizeStorage, MultiItemStorage, Storage, StorageSafe};
9use crate::error::{Result, StorageError};
10use crate::handles::{Handle, OffsetMetaHandle};
11use crate::statics::traits::StaticStorage;
12use crate::utils;
13
14/// Static multi-element storage implementation
15pub struct MultiStatic<S: 'static, const N: usize> {
16    used: [bool; N],
17    storage: &'static StorageCell<[S; N]>,
18}
19
20impl<S: 'static, const N: usize> StaticStorage<[S; N]> for MultiStatic<S, N> {
21    fn take_cell(storage: &'static StorageCell<[S; N]>) -> MultiStatic<S, N> {
22        MultiStatic {
23            used: [false; N],
24            storage,
25        }
26    }
27}
28
29// SAFETY: Internal locks and checks ensure memory safety
30unsafe impl<S, const N: usize> Storage for MultiStatic<S, N>
31where
32    S: StorageSafe,
33{
34    type Handle<T: ?Sized> = OffsetMetaHandle<T>;
35
36    unsafe fn get<T: ?Sized>(&self, handle: Self::Handle<T>) -> NonNull<T> {
37        // SAFETY: The inner Cell must be claimed as that's the only way to construct a SingleStatic
38        let store_ptr = unsafe { self.storage.as_ptr() };
39        // SAFETY: The storage pointer is guaranteed valid to dereference
40        let idx = unsafe { core::ptr::addr_of_mut!((*store_ptr.as_ptr())[handle.offset()]) };
41        let ptr: NonNull<()> = NonNull::new(idx).unwrap().cast();
42        NonNull::from_raw_parts(ptr, handle.metadata())
43    }
44
45    fn from_raw_parts<T: ?Sized + Pointee>(
46        handle: Self::Handle<()>,
47        meta: T::Metadata,
48    ) -> Self::Handle<T> {
49        <Self::Handle<T>>::from_raw_parts(handle, meta)
50    }
51
52    fn cast<T: ?Sized + Pointee, U>(handle: Self::Handle<T>) -> Self::Handle<U> {
53        handle.cast()
54    }
55
56    fn cast_unsized<T: ?Sized + Pointee, U: ?Sized + Pointee<Metadata = T::Metadata>>(
57        handle: Self::Handle<T>,
58    ) -> Self::Handle<U> {
59        handle.cast_unsized()
60    }
61
62    #[cfg(feature = "unsize")]
63    fn coerce<T: ?Sized + Pointee + Unsize<U>, U: ?Sized + Pointee>(
64        handle: Self::Handle<T>,
65    ) -> Self::Handle<U> {
66        handle.coerce()
67    }
68
69    fn allocate_single<T: ?Sized + Pointee>(
70        &mut self,
71        meta: T::Metadata,
72    ) -> Result<Self::Handle<T>> {
73        self.allocate(meta)
74    }
75
76    unsafe fn deallocate_single<T: ?Sized>(&mut self, handle: Self::Handle<T>) {
77        // SAFETY: Shares our safety requirements
78        unsafe { self.deallocate(handle) }
79    }
80
81    unsafe fn try_grow<T>(
82        &mut self,
83        handle: Self::Handle<[T]>,
84        capacity: usize,
85    ) -> Result<Self::Handle<[T]>> {
86        debug_assert!(capacity >= handle.metadata());
87        let new_layout = Layout::array::<T>(capacity).map_err(|_| StorageError::exceeds_max())?;
88
89        if self.will_fit::<[T]>(capacity) {
90            Ok(OffsetMetaHandle::from_offset_meta(
91                handle.offset(),
92                capacity,
93            ))
94        } else {
95            Err(StorageError::InsufficientSpace {
96                expected: new_layout.size(),
97                available: Some(self.max_range::<T>()),
98            })
99        }
100    }
101
102    unsafe fn try_shrink<T>(
103        &mut self,
104        handle: Self::Handle<[T]>,
105        capacity: usize,
106    ) -> Result<Self::Handle<[T]>> {
107        debug_assert!(capacity <= handle.metadata());
108        Ok(OffsetMetaHandle::from_offset_meta(
109            handle.offset(),
110            capacity,
111        ))
112    }
113}
114
115// SAFETY: Internal locks and checks ensure memory safety
116unsafe impl<S, const N: usize> MultiItemStorage for MultiStatic<S, N>
117where
118    S: StorageSafe,
119{
120    fn allocate<T: ?Sized + Pointee>(&mut self, meta: T::Metadata) -> Result<Self::Handle<T>> {
121        utils::validate_layout::<T, S>(meta)?;
122
123        let pos = self
124            .used
125            .iter()
126            .position(|i| !*i)
127            .ok_or(StorageError::NoSlots)?;
128
129        self.used[pos] = true;
130
131        Ok(OffsetMetaHandle::from_offset_meta(pos, meta))
132    }
133
134    unsafe fn deallocate<T: ?Sized + Pointee>(&mut self, handle: Self::Handle<T>) {
135        self.used[handle.offset()] = false;
136    }
137}
138
139impl<S, const N: usize> ExactSizeStorage for MultiStatic<S, N>
140where
141    S: StorageSafe,
142{
143    fn will_fit<T: ?Sized + Pointee>(&self, meta: T::Metadata) -> bool {
144        let layout = utils::layout_of::<T>(meta);
145        mem::size_of::<S>() >= layout.size()
146    }
147
148    fn max_range<T>(&self) -> usize {
149        let layout = Layout::new::<T>();
150        mem::size_of::<S>() / layout.size()
151    }
152}
153
154impl<S, const N: usize> Drop for MultiStatic<S, N> {
155    fn drop(&mut self) {
156        self.storage.release();
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use crate::backing::{Align8, Backing};
164    use crate::collections::LinkedList;
165
166    #[test]
167    fn test_linked_list() {
168        static FOO: StorageCell<[Backing<24, Align8>; 4]> = StorageCell::new([Backing::new(); 4]);
169
170        let mut list = LinkedList::<u8, MultiStatic<Backing<24, Align8>, 4>>::new_in(FOO.claim());
171        list.push(1);
172        list.push(2);
173
174        assert_eq!(list.get(0), Some(&1));
175        assert_eq!(list.get(1), Some(&2));
176        assert_eq!(list.get(3), None);
177    }
178}