1use std::ptr::NonNull;
2use std::any::TypeId;
3use std::marker::PhantomData;
4
5pub struct Allocal {
8 data: NonNull<u8>,
10
11 cap: usize,
13
14 locations: Vec<LocationInner>,
17}
18
19#[derive(Clone)]
21pub struct LocationInner {
22 offset: usize,
24 size: usize,
26 drop_fn: Option<fn(*mut u8)>,
28}
29
30pub struct Location<T> {
31 offset: usize,
32 _m: PhantomData<*const T>,
33}
34
35impl<T> Clone for Location<T> {
36 fn clone(&self) -> Self {
37 Self {
38 offset: self.offset,
39 _m: PhantomData,
40 }
41 }
42}
43
44impl<T> Copy for Location<T> {}
45
46impl<T: 'static, U: 'static> PartialEq<Location<U>> for Location<T> {
47 #[inline(always)]
48 fn eq(&self, other: &Location<U>) -> bool {
49 TypeId::of::<T>() == TypeId::of::<U>() && self.offset == other.offset
50 }
51}
52
53impl Drop for Allocal {
54 fn drop(&mut self) {
55 use std::alloc::{dealloc, Layout};
56
57 self.clear();
59
60 let layout = unsafe { Layout::from_size_align_unchecked(self.cap, 1) };
62 unsafe { dealloc(self.data.as_ptr(), layout) };
63 }
64}
65
66impl Allocal {
67 pub fn with_capacity(capacity: usize) -> Self {
72 use std::alloc::{alloc, Layout};
73
74 let layout = unsafe { Layout::from_size_align_unchecked(capacity, 1) };
75 let data = unsafe { alloc(layout) };
76
77 Self {
78 data: NonNull::new(data).unwrap(),
79 cap: capacity,
80 locations: Vec::new(),
81 }
82 }
83
84 pub fn clear(&mut self) {
86 for loc in self.locations.drain(..) {
87 if let Some(drop_fn) = loc.drop_fn {
88 let ptr = unsafe { self.data.as_ptr().add(loc.offset) };
90 drop_fn(ptr);
91 }
92 }
93 }
94
95 pub fn reserve_items(&mut self, count: usize) {
100 self.locations.reserve(count);
101 }
102
103 #[inline]
105 pub fn get_ptr<T>(&self, location: Location<T>) -> Option<*const T> {
106 self.locations.binary_search_by_key(&location.offset, |loc| loc.offset).ok()?;
107
108 unsafe {
109 Some(self.data.as_ptr().add(location.offset).cast())
110 }
111 }
112
113 #[inline]
115 pub fn get_mut_ptr<T>(&mut self, location: Location<T>) -> Option<*mut T> {
116 self.locations.binary_search_by_key(&location.offset, |loc| loc.offset).ok()?;
117
118 unsafe {
119 Some(self.data.as_ptr().add(location.offset).cast())
120 }
121 }
122
123 #[inline(always)]
125 pub fn try_get<T>(&self, location: Location<T>) -> Option<&T> {
126 unsafe {
127 Some(&*self.get_ptr(location)?)
128 }
129 }
130
131 #[inline(always)]
133 pub fn try_get_mut<T>(&mut self, location: Location<T>) -> Option<&mut T> {
134 unsafe {
135 Some(&mut *self.get_mut_ptr(location)?)
136 }
137 }
138
139 #[inline(always)]
141 pub fn get<T>(&self, location: Location<T>) -> &T {
142 self.try_get(location).unwrap()
143 }
144
145 #[inline(always)]
147 pub fn get_mut<T>(&mut self, location: Location<T>) -> &mut T {
148 self.try_get_mut(location).unwrap()
149 }
150
151 pub fn allocate<T: 'static>(&mut self, item: T) -> Result<Location<T>, ()> {
154 use std::mem::{align_of, size_of, needs_drop};
155
156 let size = size_of::<T>();
157
158 if size == 0 {
160 let location = LocationInner {
161 size,
162 offset: usize::MAX,
163 drop_fn: None,
164 };
165
166 self.locations.push(location);
167
168 return Ok(Location {
171 offset: usize::MAX,
172 _m: PhantomData,
173 });
174 }
175
176 let align = align_of::<T>();
177
178 let mut offset = self.data.as_ptr() as usize % align;
180
181 let loc = if self.locations.is_empty() {
182 if offset + size > self.cap {
185 return Err(());
187 }
188
189 0usize
190 } else {
191 let mut loc = 0usize;
192
193 loop {
194 while offset < self.locations[loc].offset + self.locations[loc].size {
195 offset += align;
196 }
197
198 loc += 1;
201
202 if loc >= self.locations.len() {
203 if offset + size > self.cap {
207 return Err(());
209 }
210
211 break;
212 }
213
214 if offset + size < self.locations[loc].offset {
215 break;
217 }
218 }
219
220 loc
221 };
222
223 let location = LocationInner {
225 offset,
226 size,
227 drop_fn: if needs_drop::<T>() {
228 Some(|ptr: *mut u8| unsafe { std::ptr::drop_in_place(ptr as *mut T) })
229 } else { None },
230 };
231
232 self.locations.insert(loc, location.clone());
233
234 unsafe {
237 let ptr: *mut T = self.data.as_ptr().add(offset).cast();
238 ptr.write(item);
239 }
240
241 Ok(Location {
242 offset,
243 _m: PhantomData,
244 })
245 }
246
247 pub fn deallocate<T>(&mut self, location: Location<T>) -> T {
252 let idx = match self.locations.binary_search_by_key(&location.offset, |loc| loc.offset) {
253 Ok(val) => val,
254 Err(_) => panic!("Invalid location"),
255 };
256
257 let location = self.locations.remove(idx);
258
259 unsafe {
261 let ptr: *mut T = self.data.as_ptr().add(location.offset).cast();
262 ptr.read()
263 }
264 }
265}
266
267#[cfg(test)]
268#[test]
269fn create() {
270 let mut allocal = Allocal::with_capacity(100);
271 let mut locations = Vec::new();
272
273 for i in 0u8..100 {
274 locations.push(allocal.allocate(i).unwrap());
275 }
276
277 assert!(allocal.allocate(0u8).is_err());
278
279 for (i, loc) in locations.clone().into_iter().enumerate() {
280 assert_eq!(*allocal.get(loc), i as u8);
281 }
282
283 for (i, loc) in locations.clone().into_iter().enumerate().rev() {
284 assert_eq!(allocal.deallocate(loc), i as u8);
285 }
286
287 for loc in locations.into_iter() {
288 assert_eq!(allocal.try_get(loc), None);
289 }
290}
291
292#[cfg(test)]
293#[test]
294fn alignement() {
295 use std::mem::align_of;
296
297 for _ in 0..100 {
298 let mut allocal = Allocal::with_capacity(30);
299 let loc = allocal.allocate(0u128).unwrap();
300
301 let ptr = allocal.get_ptr(loc).unwrap() as usize;
302 assert_eq!(ptr % align_of::<u128>(), 0);
303 }
304}
305
306#[cfg(test)]
307#[test]
308fn drop() {
309 use std::rc::Rc;
310 use std::cell::Cell;
311
312 let drop_count = Rc::new(Cell::new(0u8));
313 struct DropCheck(Rc<Cell<u8>>);
314 impl Drop for DropCheck {
315 fn drop(&mut self) {
316 self.0.set(self.0.get() + 1);
317 }
318 }
319
320 {
321 let mut allocal = Allocal::with_capacity(1024);
322
323 for _ in 0..20 {
324 allocal.allocate(DropCheck(Rc::clone(&drop_count))).unwrap();
325 }
326 }
327
328 assert_eq!(drop_count.get(), 20);
329}