osom_lib_arrays/fixed_array/
fixed_array.rs1#![allow(clippy::cast_possible_truncation)]
2
3use core::{
4 borrow::{Borrow, BorrowMut},
5 hash::Hash,
6 marker::PhantomData,
7 mem::{ManuallyDrop, MaybeUninit},
8 ops::{Index, IndexMut},
9};
10
11use osom_lib_primitives::length::Length;
12use osom_lib_reprc::traits::ReprC;
13
14use crate::{
15 errors::{ArrayError, ArrayIsEmptyError},
16 traits::{ImmutableArray, MutableArray},
17};
18
19#[repr(C)]
24#[must_use]
25pub struct FixedArray<const TSIZE: usize, T: Sized> {
26 length: Length,
27 inner: MaybeUninit<[T; TSIZE]>,
28 _phantom: PhantomData<T>,
29}
30
31unsafe impl<const TSIZE: usize, T: ReprC + Sized> ReprC for FixedArray<TSIZE, T> {
32 const CHECK: () = const {
33 let () = T::CHECK;
34 let () = <PhantomData<T> as ReprC>::CHECK;
35 let () = <Length as ReprC>::CHECK;
36 let () = <MaybeUninit<[T; TSIZE]> as ReprC>::CHECK;
37 };
38}
39
40impl<const TSIZE: usize, T: Sized> FixedArray<TSIZE, T> {
41 pub const fn new() -> Self {
48 const {
49 assert!(
50 TSIZE <= Length::MAX_LENGTH.as_usize(),
51 "TSIZE cannot exceed Length::MAX_LENGTH"
52 );
53 assert!(TSIZE > 0, "TSIZE cannot be 0");
54 }
55
56 Self {
57 length: Length::ZERO,
58 inner: MaybeUninit::uninit(),
59 _phantom: PhantomData,
60 }
61 }
62
63 #[inline(always)]
65 pub const fn length(&self) -> Length {
66 self.length
67 }
68
69 #[inline(always)]
70 pub const fn is_empty(&self) -> bool {
71 self.length.as_u32() == 0
72 }
73
74 #[inline(always)]
77 pub const fn capacity(&self) -> Length {
78 unsafe { Length::new_unchecked(TSIZE as u32) }
79 }
80
81 #[inline(always)]
83 pub const fn as_slice_const(&self) -> &[T] {
84 const {
88 assert!(
89 size_of::<[T; TSIZE]>() == size_of::<ManuallyDrop<[T; TSIZE]>>(),
90 "T and ManuallyDrop<[T; TSIZE]> must have the same size"
91 );
92 assert!(
93 align_of::<[T; TSIZE]>() == align_of::<ManuallyDrop<[T; TSIZE]>>(),
94 "T and ManuallyDrop<[T; TSIZE]> must have the same alignment"
95 );
96 }
97 let ptr = (&raw const self.inner).cast();
98 unsafe { core::slice::from_raw_parts(ptr, self.length.as_usize()) }
99 }
100
101 #[inline(always)]
103 pub const fn as_slice_mut_const(&mut self) -> &mut [T] {
104 const {
108 assert!(
109 size_of::<[T; TSIZE]>() == size_of::<ManuallyDrop<[T; TSIZE]>>(),
110 "T and ManuallyDrop<[T; TSIZE]> must have the same size"
111 );
112 assert!(
113 align_of::<[T; TSIZE]>() == align_of::<ManuallyDrop<[T; TSIZE]>>(),
114 "T and ManuallyDrop<[T; TSIZE]> must have the same alignment"
115 );
116 }
117 let ptr = (&raw mut self.inner).cast();
118 unsafe { core::slice::from_raw_parts_mut(ptr, self.length.as_usize()) }
119 }
120}
121
122impl<const TSIZE: usize, T: Sized> Default for FixedArray<TSIZE, T> {
123 fn default() -> Self {
124 Self::new()
125 }
126}
127
128impl<const TSIZE: usize, T: Sized> Index<Length> for FixedArray<TSIZE, T> {
129 type Output = T;
130
131 fn index(&self, index: Length) -> &T {
132 &self.as_slice_const()[index.as_usize()]
133 }
134}
135
136impl<const TSIZE: usize, T: Sized> IndexMut<Length> for FixedArray<TSIZE, T> {
137 fn index_mut(&mut self, index: Length) -> &mut T {
138 &mut self.as_slice_mut_const()[index.as_usize()]
139 }
140}
141
142impl<const TSIZE: usize, T: Sized> ImmutableArray<T> for FixedArray<TSIZE, T> {
143 #[inline(always)]
144 fn length(&self) -> Length {
145 self.length()
146 }
147
148 #[inline(always)]
149 fn capacity(&self) -> Length {
150 self.capacity()
151 }
152
153 #[inline(always)]
154 fn as_slice(&self) -> &[T] {
155 self.as_slice_const()
156 }
157}
158
159impl<const TSIZE: usize, T: Sized> MutableArray<T> for FixedArray<TSIZE, T> {
160 fn try_push_array<const TARRSIZE: usize>(&mut self, arr: [T; TARRSIZE]) -> Result<(), ArrayError> {
161 let len = self.length.as_usize();
162 if len + TARRSIZE > TSIZE {
163 return Err(ArrayError::LengthLimitExceeded);
164 }
165
166 unsafe {
167 let mut dst = self.inner.as_mut_ptr().cast::<T>().add(len);
168 let mut src = arr.as_ptr();
169 let end = src.add(TARRSIZE);
170 while src < end {
171 dst.write(src.read());
172 dst = dst.add(1);
173 src = src.add(1);
174 }
175 core::mem::forget(arr);
176 self.length = Length::new_unchecked((len + TARRSIZE) as u32);
177 }
178 Ok(())
179 }
180
181 fn try_push_slice(&mut self, slice: &[T]) -> Result<(), ArrayError>
182 where
183 T: Clone,
184 {
185 let len = self.length.as_usize();
186 let tsize = slice.len();
187 if len + tsize > TSIZE {
188 return Err(ArrayError::LengthLimitExceeded);
189 }
190
191 unsafe {
192 let mut dst = self.inner.as_mut_ptr().cast::<T>().add(len);
193 for item in slice {
194 dst.write(item.clone());
195 dst = dst.add(1);
196 }
197 self.length = Length::new_unchecked((len + tsize) as u32);
198 }
199 Ok(())
200 }
201
202 fn try_pop(&mut self) -> Result<T, ArrayIsEmptyError> {
203 if self.length == Length::ZERO {
204 return Err(ArrayIsEmptyError);
205 }
206
207 let len = self.length.as_u32();
208 let item = unsafe {
209 self.length = Length::new_unchecked(len - 1);
210 self.inner.as_ptr().cast::<T>().add((len - 1) as usize).read()
211 };
212 Ok(item)
213 }
214
215 #[inline(always)]
216 fn as_slice_mut(&mut self) -> &mut [T] {
217 self.as_slice_mut_const()
218 }
219}
220
221impl<const TSIZE: usize, T: Sized + Clone> Clone for FixedArray<TSIZE, T> {
222 fn clone(&self) -> Self {
223 let mut new_instance = Self::new();
224 new_instance
225 .try_push_slice(self.as_slice_const())
226 .expect("Failed to clone fixed array");
227 new_instance
228 }
229}
230
231impl<const TSIZE: usize, T: Sized + PartialEq, Rhs: AsRef<[T]>> PartialEq<Rhs> for FixedArray<TSIZE, T> {
232 fn eq(&self, other: &Rhs) -> bool {
233 self.as_slice_const() == other.as_ref()
234 }
235}
236
237impl<const TSIZE: usize, T: Sized + Eq> Eq for FixedArray<TSIZE, T> {}
238
239impl<const TSIZE: usize, T: Sized + Hash> Hash for FixedArray<TSIZE, T> {
240 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
241 self.as_slice_const().hash(state);
242 }
243}
244
245impl<const TSIZE: usize, T: Sized> AsRef<[T]> for FixedArray<TSIZE, T> {
246 fn as_ref(&self) -> &[T] {
247 self.as_slice_const()
248 }
249}
250
251impl<const TSIZE: usize, T: Sized> AsMut<[T]> for FixedArray<TSIZE, T> {
252 fn as_mut(&mut self) -> &mut [T] {
253 self.as_slice_mut_const()
254 }
255}
256
257impl<const TSIZE: usize, T: Sized> Borrow<[T]> for FixedArray<TSIZE, T> {
258 fn borrow(&self) -> &[T] {
259 self.as_slice_const()
260 }
261}
262
263impl<const TSIZE: usize, T: Sized> BorrowMut<[T]> for FixedArray<TSIZE, T> {
264 fn borrow_mut(&mut self) -> &mut [T] {
265 self.as_slice_mut_const()
266 }
267}
268
269impl<const TSIZE: usize, T: Sized> Drop for FixedArray<TSIZE, T> {
270 fn drop(&mut self) {
271 if !core::mem::needs_drop::<T>() {
272 return;
273 }
274
275 unsafe {
276 let mut start = (&raw mut self.inner).cast::<T>();
277 let end = start.add(self.length.as_usize());
278 while start < end {
279 core::ptr::drop_in_place(start);
280 start = start.add(1);
281 }
282 }
283 }
284}