1use core::{
50 fmt::{Debug, Formatter},
51 marker::PhantomData,
52 mem::{size_of, MaybeUninit},
53};
54
55use bytemuck::Zeroable;
56
57#[derive(Clone, Copy)]
60struct SegmentHeader([u32; NUM_U32_PER_HEADER]);
61impl SegmentHeader {
62 const fn new() -> Self {
63 SegmentHeader([0u32; NUM_U32_PER_HEADER])
64 }
65
66 fn first_free_slot_idx(&self) -> Option<usize> {
67 for header in self.0 {
68 let clo = header.leading_ones();
69 if clo == 32 {
70 continue;
71 }
72 return Some(usize::try_from(clo).unwrap());
73 }
74 None
75 }
76
77 fn slot_to_idx(slot_idx: usize) -> (usize, usize) {
78 (slot_idx >> 5, slot_idx % 32)
79 }
80
81 fn set_slot(&mut self, slot_idx: usize) {
82 let (arr_idx, bit_idx) = Self::slot_to_idx(slot_idx);
83 self.0[arr_idx] |= 1 << (31 - bit_idx);
84 }
85
86 fn unset_slot(&mut self, slot_idx: usize) {
87 let (arr_idx, bit_idx) = Self::slot_to_idx(slot_idx);
88 self.0[arr_idx] &= !(1 << (31 - bit_idx));
89 }
90}
91
92unsafe impl Zeroable for SegmentHeader {}
93
94impl Debug for SegmentHeader {
95 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
96 f.write_str("SegmentHeader {")?;
97 for header in self.0 {
98 f.write_fmt(format_args!("{:08b}", (header >> 24) & 0xff))?;
99 f.write_str("_")?;
100 f.write_fmt(format_args!("{:08b}", (header >> 16) & 0xff))?;
101 f.write_str("_")?;
102 f.write_fmt(format_args!("{:08b}", (header >> 8) & 0xff))?;
103 f.write_str("_")?;
104 f.write_fmt(format_args!("{:08b}", (header >> 0) & 0xff))?;
105 }
106 f.write_str("}")?;
107 Ok(())
108 }
109}
110
111const NUM_U32_PER_HEADER: usize = 1;
112pub const NUM_SLOTS_PER_SEGMENT: usize = NUM_U32_PER_HEADER * size_of::<u32>() * 8;
113pub const SEGMENT_HEADER_SIZE: usize = NUM_U32_PER_HEADER * size_of::<u32>();
114
115pub trait Slot: Copy + Default {
116 fn get(&self) -> *const u8;
117 fn size() -> usize;
118}
119
120#[derive(Clone, Copy)]
121pub struct Segment<S: Slot> {
122 header: SegmentHeader,
123 slots: [S; NUM_SLOTS_PER_SEGMENT],
124}
125
126impl<S: Slot> Segment<S> {
127 fn new() -> Self {
128 Segment {
129 header: SegmentHeader::new(),
130 slots: [S::default(); NUM_SLOTS_PER_SEGMENT],
131 }
132 }
133
134 fn get_slot(&self, slot_idx: usize) -> *const u8 {
135 self.slots[slot_idx].get()
136 }
137}
138
139impl<S: Slot> Default for Segment<S> {
140 fn default() -> Self {
141 Self::new()
142 }
143}
144
145unsafe impl<S: Slot + Zeroable> Zeroable for Segment<S> {}
146
147impl<S: Slot> Debug for Segment<S> {
148 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
149 f.debug_struct("Segment")
150 .field("header", &self.header)
151 .finish()?;
152 Ok(())
153 }
154}
155
156macro_rules! align_type {
157 ($name:ident, $n:expr) => {
158 #[derive(Debug, Clone, Copy)]
159 #[repr(C, align($n))]
160 pub struct $name<const N: usize>([u8; N]);
161 impl<const N: usize> $name<N> {
162 pub const fn new() -> Self {
163 $name([0u8; N])
164 }
165 }
166
167 impl<const N: usize> Default for $name<N> {
168 fn default() -> Self {
169 Self::new()
170 }
171 }
172
173 impl<const N: usize> Slot for $name<N> {
174 fn get(&self) -> *const u8 {
175 self.0.as_ptr()
176 }
177
178 fn size() -> usize {
179 N
180 }
181 }
182
183 unsafe impl<const N: usize> Zeroable for $name<N> {}
184 };
185}
186
187align_type!(SlotWithAlign1, 1);
188align_type!(SlotWithAlign2, 2);
189align_type!(SlotWithAlign4, 4);
190align_type!(SlotWithAlign8, 8);
191align_type!(SlotWithAlign16, 16);
192align_type!(SlotWithAlign32, 32);
193align_type!(SlotWithAlign64, 64);
194align_type!(SlotWithAlign128, 128);
195align_type!(SlotWithAlign256, 256);
196align_type!(SlotWithAlign512, 512);
197
198#[derive(Debug, Clone, Copy)]
199pub struct BucketImpl<S: Slot, const N: usize> {
200 is_init: bool,
205 segments: MaybeUninit<[Segment<S>; N]>,
206}
207
208impl<S: Slot, const NUM_SEGMENTS: usize> BucketImpl<S, NUM_SEGMENTS> {
209 pub const fn new() -> Self {
210 Self {
211 is_init: false,
212 segments: MaybeUninit::uninit(),
213 }
214 }
216
217 pub fn ensure_init(&mut self) {
218 if self.is_init {
219 return;
220 }
221 unsafe {
222 core::ptr::write_bytes(self.segments.as_mut_ptr(), 0u8, 1);
223 }
224 self.is_init = true
225 }
226
227 fn get_segments(&self) -> &[Segment<S>; NUM_SEGMENTS] {
228 assert!(self.is_init);
229 unsafe { self.segments.assume_init_ref() }
230 }
231
232 fn get_segments_mut(&mut self) -> &mut [Segment<S>; NUM_SEGMENTS] {
233 assert!(self.is_init);
234 unsafe { self.segments.assume_init_mut() }
235 }
236
237 pub fn claim_first_available_slot(&mut self) -> Option<*const u8> {
238 for seg in self.get_segments_mut().iter_mut() {
239 let Some(slot_idx) = seg.header.first_free_slot_idx() else {continue};
240 seg.header.set_slot(slot_idx);
241 return Some(seg.get_slot(slot_idx));
242 }
243 None
244 }
245
246 fn global_to_local(&self, slot_idx: usize) -> (usize, usize) {
247 let seg_idx = slot_idx / NUM_SLOTS_PER_SEGMENT;
248 let slot_idx = slot_idx % NUM_SLOTS_PER_SEGMENT;
249 (seg_idx, slot_idx)
250 }
251
252 pub fn get_slot(&self, slot_idx: usize) -> *const u8 {
253 let (seg_idx, slot_idx) = self.global_to_local(slot_idx);
254 self.get_segments()[seg_idx].get_slot(slot_idx)
255 }
256
257 pub fn set_slot(&mut self, slot_idx: usize) {
258 let (seg_idx, slot_idx) = self.global_to_local(slot_idx);
259 self.get_segments_mut()[seg_idx].header.set_slot(slot_idx);
260 }
261
262 pub fn unset_slot(&mut self, slot_idx: usize) {
263 let (seg_idx, slot_idx) = self.global_to_local(slot_idx);
264 self.get_segments_mut()[seg_idx].header.unset_slot(slot_idx);
265 }
266
267 pub fn slot_idx_for_ptr(&self, ptr: *const u8) -> Option<usize> {
268 let seg_stride = size_of::<Segment<S>>();
269 let slot_stride = size_of::<S>();
270
271 let start = self.segments.as_ptr() as *const u8;
272 let offset = unsafe { ptr.offset_from(start) };
273 let offset = usize::try_from(offset).ok()?;
276 let seg_idx = offset / seg_stride;
277 let offset = offset % seg_stride;
278 let slot_idx = offset / slot_stride;
279 if seg_idx >= NUM_SEGMENTS || slot_idx > NUM_SLOTS_PER_SEGMENT {
280 return None;
281 }
282
283 Some(seg_idx * NUM_SLOTS_PER_SEGMENT + slot_idx)
284 }
285}
286
287unsafe impl<S: Slot, const N: usize> Zeroable for BucketImpl<S, N> {}
288
289impl<S: Slot, const N: usize> Default for BucketImpl<S, N> {
290 fn default() -> Self {
291 BucketImpl::<S, N>::new()
292 }
293}
294
295pub struct SlotSize<const N: usize>;
296pub struct NumSlots<const N: usize>;
297pub struct Align<const N: usize>;
298
299pub struct Bucket<S, N, A = Align<1>>(PhantomData<S>, PhantomData<N>, PhantomData<A>);
300
301#[cfg(test)]
302mod test {
303 use super::*;
304
305 use core::alloc::{GlobalAlloc, Layout};
306
307 use anyhow::Result;
308
309 use silly_alloc_macros::bucket_allocator;
310
311 #[bucket_allocator]
312 struct MyBucketAllocator {
313 vec2: Bucket<SlotSize<2>, NumSlots<32>, Align<2>>,
314 vec4: Bucket<SlotSize<4>, NumSlots<32>, Align<4>>,
315 vec8: Bucket<SlotSize<8>, NumSlots<32>, Align<8>>,
316 }
317
318 #[test]
319 fn next_in_bucket() -> Result<()> {
320 let b = MyBucketAllocator::new();
321 unsafe {
322 let ptr1 = b.alloc(Layout::from_size_align(2, 1)?);
323 let ptr2 = b.alloc(Layout::from_size_align(2, 1)?);
324 assert!(!ptr1.is_null());
325 assert!(!ptr2.is_null());
326 assert_eq!(ptr1.offset(2), ptr2);
327 }
328 Ok(())
329 }
330
331 #[test]
332 fn full_bucket() -> Result<()> {
333 #[bucket_allocator]
334 struct MyBucketAllocator {
335 vec2: Bucket<SlotSize<2>, NumSlots<32>, Align<2>>,
336 }
337 unsafe {
338 let b = MyBucketAllocator::new();
339 let l = Layout::from_size_align(2, 1)?;
340 for _ in 0..32 {
341 b.alloc(l);
342 }
343 let ptr = b.alloc(l);
344 assert!(ptr.is_null());
345 }
346 Ok(())
347 }
348
349 #[test]
350 fn reuse() -> Result<()> {
351 let b = MyBucketAllocator::new();
352 unsafe {
353 let layout = Layout::from_size_align(2, 1)?;
354 let ptr1 = b.alloc(layout.clone());
355 let ptr2 = b.alloc(layout.clone());
356 let ptr3 = b.alloc(layout.clone());
357 assert_eq!(ptr1.offset(2), ptr2);
358 assert_eq!(ptr2.offset(2), ptr3);
359 b.dealloc(ptr2, layout);
360 let ptr4 = b.alloc(layout.clone());
361 assert_eq!(ptr2, ptr4);
362 }
363 Ok(())
364 }
365
366 #[test]
367 fn bucket_overflow() -> Result<()> {
368 let b = MyBucketAllocator::new();
369 unsafe {
370 let layout = Layout::from_size_align(2, 1)?;
371 for _ in 0..32 {
373 b.alloc(layout.clone());
374 }
375 let ptr1 = b.alloc(Layout::from_size_align(4, 1)?);
376 let ptr2 = b.alloc(layout.clone());
377 assert_eq!(ptr1.offset(4), ptr2);
378 }
379 Ok(())
380 }
381
382 #[test]
383 fn alignment() -> Result<()> {
384 let mut b = MyBucketAllocator::new();
385 unsafe {
386 let layout = Layout::from_size_align(2, 8)?;
387 let ptr1 = b.alloc(layout);
388 assert!(ptr1 >= &mut b.2 as *mut _ as *mut u8);
390 }
391 Ok(())
392 }
393
394 #[test]
395 fn alignment_fail() -> Result<()> {
396 let b = MyBucketAllocator::new();
397 unsafe {
398 let layout = Layout::from_size_align(2, 32)?;
399 let ptr1 = b.alloc(layout);
400 assert!(ptr1.is_null());
401 }
402 Ok(())
403 }
404
405 #[test]
406 fn first_alloc_in_late_bucket() -> Result<()> {
407 unsafe {
408 let layouts = vec![
410 Layout::from_size_align(2, 1)?,
411 Layout::from_size_align(8, 1)?,
412 ];
413 for layout in layouts {
414 let b = MyBucketAllocator::new();
415 let _ptr1 = b.alloc(layout.clone());
416 let ptr2 = b.alloc(layout.clone());
417 let _ptr3 = b.alloc(layout.clone());
418 b.dealloc(ptr2, layout);
419 let ptr4 = b.alloc(layout.clone());
420 assert_eq!(ptr2, ptr4);
421 }
422 }
423 Ok(())
424 }
425
426 #[test]
427 fn small_buckets() -> Result<()> {
428 #[bucket_allocator]
429 struct MyBucketAllocator {
430 vec2: Bucket<SlotSize<2>, NumSlots<1>, Align<2>>,
431 }
432 unsafe {
433 let b = MyBucketAllocator::new();
434 let l = Layout::from_size_align(2, 2)?;
435 let ptr1 = b.alloc(l);
436 assert!(!ptr1.is_null());
437 }
438 Ok(())
439 }
440
441 #[test]
442 fn unsorted_buckets() -> Result<()> {
443 #[bucket_allocator(sort_buckets = true)]
444 struct MyBucketAllocator {
445 vec8: Bucket<SlotSize<8>, NumSlots<32>, Align<8>>,
446 vec2: Bucket<SlotSize<2>, NumSlots<32>, Align<8>>,
447 }
448
449 unsafe {
450 let b = MyBucketAllocator::new();
451 let ptr1 = b.alloc(Layout::from_size_align(2, 2)?);
452 let ptr2 = b.alloc(Layout::from_size_align(8, 8)?);
453 assert!(ptr1.offset_from(ptr2).abs() > 8);
455 }
456 Ok(())
457 }
458}