1use std::alloc::{alloc, dealloc, Layout};
43use std::marker::PhantomData;
44use std::mem::MaybeUninit;
45use std::ptr::NonNull;
46use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering};
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[repr(u8)]
51pub enum SizeClass {
52 Size64 = 0,
54 Size128 = 1,
56 Size256 = 2,
58 Size512 = 3,
60 Size1024 = 4,
62}
63
64impl SizeClass {
65 #[inline]
67 pub const fn size_bytes(&self) -> usize {
68 match self {
69 Self::Size64 => 64,
70 Self::Size128 => 128,
71 Self::Size256 => 256,
72 Self::Size512 => 512,
73 Self::Size1024 => 1024,
74 }
75 }
76
77 pub fn for_size(size: usize) -> Option<Self> {
79 match size {
80 0..=64 => Some(Self::Size64),
81 65..=128 => Some(Self::Size128),
82 129..=256 => Some(Self::Size256),
83 257..=512 => Some(Self::Size512),
84 513..=1024 => Some(Self::Size1024),
85 _ => None,
86 }
87 }
88
89 pub const fn all() -> [Self; 5] {
91 [Self::Size64, Self::Size128, Self::Size256, Self::Size512, Self::Size1024]
92 }
93}
94
95#[derive(Clone, Copy, Debug)]
103pub struct GenerationalHandle {
104 packed: u64,
106}
107
108impl GenerationalHandle {
109 #[inline]
111 pub const fn new(slot: u32, generation: u32) -> Self {
112 Self {
113 packed: ((generation as u64) << 32) | (slot as u64),
114 }
115 }
116
117 #[inline]
119 pub const fn slot(&self) -> u32 {
120 self.packed as u32
121 }
122
123 #[inline]
125 pub const fn generation(&self) -> u32 {
126 (self.packed >> 32) as u32
127 }
128
129 #[inline]
131 pub const fn from_raw(packed: u64) -> Self {
132 Self { packed }
133 }
134
135 #[inline]
137 pub const fn to_raw(&self) -> u64 {
138 self.packed
139 }
140
141 #[inline]
143 pub const fn invalid() -> Self {
144 Self { packed: u64::MAX }
145 }
146
147 #[inline]
149 pub const fn is_valid(&self) -> bool {
150 self.packed != u64::MAX
151 }
152}
153
154#[repr(C)]
160struct SlotHeader {
161 generation: AtomicU32,
163 next_free: AtomicU32,
165 flags: AtomicU32,
167 _reserved: u32,
169}
170
171impl SlotHeader {
172 const FLAG_ALLOCATED: u32 = 1 << 0;
173
174 fn new() -> Self {
175 Self {
176 generation: AtomicU32::new(0),
177 next_free: AtomicU32::new(u32::MAX),
178 flags: AtomicU32::new(0),
179 _reserved: 0,
180 }
181 }
182
183 #[inline]
184 fn is_allocated(&self) -> bool {
185 self.flags.load(Ordering::Acquire) & Self::FLAG_ALLOCATED != 0
186 }
187
188 #[inline]
189 fn set_allocated(&self, allocated: bool) {
190 if allocated {
191 self.flags.fetch_or(Self::FLAG_ALLOCATED, Ordering::Release);
192 } else {
193 self.flags.fetch_and(!Self::FLAG_ALLOCATED, Ordering::Release);
194 }
195 }
196
197 #[inline]
198 fn increment_generation(&self) -> u32 {
199 self.generation.fetch_add(1, Ordering::Release) + 1
200 }
201}
202
203struct Slab {
209 data: NonNull<u8>,
211 slot_size: usize,
213 slot_count: usize,
215 layout: Layout,
217 free_head: AtomicU32,
219 allocated_count: AtomicUsize,
221}
222
223impl Slab {
224 fn new(size_class: SizeClass, slot_count: usize) -> Option<Self> {
226 let user_size = size_class.size_bytes();
227 let header_size = std::mem::size_of::<SlotHeader>();
228 let slot_size = header_size + user_size;
229
230 let slot_size = (slot_size + 15) & !15; let total_size = slot_size * slot_count;
234 let layout = Layout::from_size_align(total_size, 64).ok()?; let ptr = unsafe { alloc(layout) };
237 let data = NonNull::new(ptr)?;
238
239 unsafe {
241 for i in 0..slot_count {
242 let slot_ptr = data.as_ptr().add(i * slot_size);
243 let header = &mut *(slot_ptr as *mut SlotHeader);
244 *header = SlotHeader::new();
245
246 if i < slot_count - 1 {
248 header.next_free.store((i + 1) as u32, Ordering::Relaxed);
249 } else {
250 header.next_free.store(u32::MAX, Ordering::Relaxed);
251 }
252 }
253 }
254
255 Some(Self {
256 data,
257 slot_size,
258 slot_count,
259 layout,
260 free_head: AtomicU32::new(0),
261 allocated_count: AtomicUsize::new(0),
262 })
263 }
264
265 fn allocate(&self) -> Option<GenerationalHandle> {
267 loop {
268 let head = self.free_head.load(Ordering::Acquire);
269
270 if head == u32::MAX {
271 return None; }
273
274 let header = self.get_header(head as usize)?;
275 let next = header.next_free.load(Ordering::Acquire);
276
277 match self.free_head.compare_exchange_weak(
278 head,
279 next,
280 Ordering::AcqRel,
281 Ordering::Acquire,
282 ) {
283 Ok(_) => {
284 let generation = header.increment_generation();
286 header.set_allocated(true);
287 self.allocated_count.fetch_add(1, Ordering::Relaxed);
288 return Some(GenerationalHandle::new(head, generation));
289 }
290 Err(_) => continue, }
292 }
293 }
294
295 fn free(&self, handle: GenerationalHandle) -> bool {
297 let slot = handle.slot() as usize;
298
299 if slot >= self.slot_count {
300 return false;
301 }
302
303 let header = match self.get_header(slot) {
304 Some(h) => h,
305 None => return false,
306 };
307
308 if header.generation.load(Ordering::Acquire) != handle.generation() {
310 return false; }
312
313 if !header.is_allocated() {
315 return false; }
317
318 header.set_allocated(false);
319
320 loop {
322 let head = self.free_head.load(Ordering::Acquire);
323 header.next_free.store(head, Ordering::Release);
324
325 match self.free_head.compare_exchange_weak(
326 head,
327 slot as u32,
328 Ordering::AcqRel,
329 Ordering::Acquire,
330 ) {
331 Ok(_) => {
332 self.allocated_count.fetch_sub(1, Ordering::Relaxed);
333 return true;
334 }
335 Err(_) => continue,
336 }
337 }
338 }
339
340 fn get_ptr(&self, handle: GenerationalHandle) -> Option<NonNull<u8>> {
342 let slot = handle.slot() as usize;
343
344 if slot >= self.slot_count {
345 return None;
346 }
347
348 let header = self.get_header(slot)?;
349
350 if header.generation.load(Ordering::Acquire) != handle.generation() {
352 return None;
353 }
354
355 if !header.is_allocated() {
357 return None;
358 }
359
360 let header_size = std::mem::size_of::<SlotHeader>();
361 let slot_ptr = unsafe { self.data.as_ptr().add(slot * self.slot_size) };
362 let user_ptr = unsafe { slot_ptr.add(header_size) };
363
364 NonNull::new(user_ptr)
365 }
366
367 fn get_header(&self, slot: usize) -> Option<&SlotHeader> {
369 if slot >= self.slot_count {
370 return None;
371 }
372
373 let slot_ptr = unsafe { self.data.as_ptr().add(slot * self.slot_size) };
374 Some(unsafe { &*(slot_ptr as *const SlotHeader) })
375 }
376
377 fn stats(&self) -> SlabStats {
379 SlabStats {
380 slot_count: self.slot_count,
381 allocated_count: self.allocated_count.load(Ordering::Relaxed),
382 slot_size: self.slot_size,
383 }
384 }
385}
386
387impl Drop for Slab {
388 fn drop(&mut self) {
389 unsafe {
390 dealloc(self.data.as_ptr(), self.layout);
391 }
392 }
393}
394
395unsafe impl Send for Slab {}
397unsafe impl Sync for Slab {}
398
399#[derive(Debug, Clone)]
401pub struct SlabStats {
402 pub slot_count: usize,
404 pub allocated_count: usize,
406 pub slot_size: usize,
408}
409
410#[derive(Clone)]
416pub struct SlabAllocatorConfig {
417 pub initial_slots: [usize; 5],
419 pub max_slabs: usize,
421}
422
423impl Default for SlabAllocatorConfig {
424 fn default() -> Self {
425 Self {
426 initial_slots: [1024, 512, 256, 128, 64], max_slabs: 64,
428 }
429 }
430}
431
432pub struct SlabAllocator {
434 slabs: [parking_lot::RwLock<Vec<Slab>>; 5],
436 config: SlabAllocatorConfig,
438 total_allocations: AtomicU64,
440 total_frees: AtomicU64,
442}
443
444impl SlabAllocator {
445 pub fn new() -> Self {
447 Self::with_config(SlabAllocatorConfig::default())
448 }
449
450 pub fn with_config(config: SlabAllocatorConfig) -> Self {
452 let slabs = std::array::from_fn(|i| {
453 let size_class = SizeClass::all()[i];
454 let initial_slots = config.initial_slots[i];
455 let slab = Slab::new(size_class, initial_slots)
456 .expect("Failed to create initial slab");
457 parking_lot::RwLock::new(vec![slab])
458 });
459
460 Self {
461 slabs,
462 config,
463 total_allocations: AtomicU64::new(0),
464 total_frees: AtomicU64::new(0),
465 }
466 }
467
468 pub fn allocate(&self, size: usize) -> Option<(GenerationalHandle, NonNull<u8>)> {
470 let size_class = SizeClass::for_size(size)?;
471 self.allocate_from_class(size_class)
472 }
473
474 pub fn allocate_from_class(&self, size_class: SizeClass) -> Option<(GenerationalHandle, NonNull<u8>)> {
476 let class_idx = size_class as usize;
477
478 {
480 let slabs = self.slabs[class_idx].read();
481 for (slab_idx, slab) in slabs.iter().enumerate() {
482 if let Some(handle) = slab.allocate() {
483 let ptr = slab.get_ptr(handle)?;
484 let full_handle = GenerationalHandle::new(
486 ((slab_idx as u32) << 24) | handle.slot(),
487 handle.generation(),
488 );
489 self.total_allocations.fetch_add(1, Ordering::Relaxed);
490 return Some((full_handle, ptr));
491 }
492 }
493 }
494
495 let mut slabs = self.slabs[class_idx].write();
497
498 for (slab_idx, slab) in slabs.iter().enumerate() {
500 if let Some(handle) = slab.allocate() {
501 let ptr = slab.get_ptr(handle)?;
502 let full_handle = GenerationalHandle::new(
503 ((slab_idx as u32) << 24) | handle.slot(),
504 handle.generation(),
505 );
506 self.total_allocations.fetch_add(1, Ordering::Relaxed);
507 return Some((full_handle, ptr));
508 }
509 }
510
511 if slabs.len() >= self.config.max_slabs {
513 return None; }
515
516 let new_slab = Slab::new(size_class, self.config.initial_slots[class_idx])?;
517 let handle = new_slab.allocate()?;
518 let ptr = new_slab.get_ptr(handle)?;
519
520 let slab_idx = slabs.len();
521 slabs.push(new_slab);
522
523 let full_handle = GenerationalHandle::new(
524 ((slab_idx as u32) << 24) | handle.slot(),
525 handle.generation(),
526 );
527 self.total_allocations.fetch_add(1, Ordering::Relaxed);
528 Some((full_handle, ptr))
529 }
530
531 pub fn free(&self, size_class: SizeClass, handle: GenerationalHandle) -> bool {
533 let class_idx = size_class as usize;
534 let slab_idx = (handle.slot() >> 24) as usize;
535 let slot = handle.slot() & 0x00FFFFFF;
536 let local_handle = GenerationalHandle::new(slot, handle.generation());
537
538 let slabs = self.slabs[class_idx].read();
539
540 if slab_idx >= slabs.len() {
541 return false;
542 }
543
544 let result = slabs[slab_idx].free(local_handle);
545 if result {
546 self.total_frees.fetch_add(1, Ordering::Relaxed);
547 }
548 result
549 }
550
551 pub fn get_ptr(&self, size_class: SizeClass, handle: GenerationalHandle) -> Option<NonNull<u8>> {
553 let class_idx = size_class as usize;
554 let slab_idx = (handle.slot() >> 24) as usize;
555 let slot = handle.slot() & 0x00FFFFFF;
556 let local_handle = GenerationalHandle::new(slot, handle.generation());
557
558 let slabs = self.slabs[class_idx].read();
559
560 if slab_idx >= slabs.len() {
561 return None;
562 }
563
564 slabs[slab_idx].get_ptr(local_handle)
565 }
566
567 pub fn stats(&self) -> AllocatorStats {
569 let mut class_stats = Vec::new();
570
571 for (i, size_class) in SizeClass::all().iter().enumerate() {
572 let slabs = self.slabs[i].read();
573 let slab_stats: Vec<_> = slabs.iter().map(|s| s.stats()).collect();
574 class_stats.push(SizeClassStats {
575 size_class: *size_class,
576 slab_count: slab_stats.len(),
577 total_slots: slab_stats.iter().map(|s| s.slot_count).sum(),
578 allocated_slots: slab_stats.iter().map(|s| s.allocated_count).sum(),
579 });
580 }
581
582 AllocatorStats {
583 total_allocations: self.total_allocations.load(Ordering::Relaxed),
584 total_frees: self.total_frees.load(Ordering::Relaxed),
585 class_stats,
586 }
587 }
588}
589
590impl Default for SlabAllocator {
591 fn default() -> Self {
592 Self::new()
593 }
594}
595
596#[derive(Debug, Clone)]
598pub struct SizeClassStats {
599 pub size_class: SizeClass,
601 pub slab_count: usize,
603 pub total_slots: usize,
605 pub allocated_slots: usize,
607}
608
609#[derive(Debug, Clone)]
611pub struct AllocatorStats {
612 pub total_allocations: u64,
614 pub total_frees: u64,
616 pub class_stats: Vec<SizeClassStats>,
618}
619
620pub struct TypedSlabAllocator<T> {
626 allocator: SlabAllocator,
628 size_class: SizeClass,
630 _marker: PhantomData<T>,
632}
633
634impl<T: Sized> TypedSlabAllocator<T> {
635 pub fn new() -> Option<Self> {
637 let size = std::mem::size_of::<T>();
638 let size_class = SizeClass::for_size(size)?;
639
640 Some(Self {
641 allocator: SlabAllocator::new(),
642 size_class,
643 _marker: PhantomData,
644 })
645 }
646
647 pub fn allocate(&self, value: T) -> Option<(GenerationalHandle, NonNull<T>)> {
649 let (handle, ptr) = self.allocator.allocate_from_class(self.size_class)?;
650
651 unsafe {
653 std::ptr::write(ptr.as_ptr() as *mut T, value);
654 }
655
656 Some((handle, ptr.cast()))
657 }
658
659 pub fn allocate_uninit(&self) -> Option<(GenerationalHandle, NonNull<MaybeUninit<T>>)> {
661 let (handle, ptr) = self.allocator.allocate_from_class(self.size_class)?;
662 Some((handle, ptr.cast()))
663 }
664
665 pub fn free(&self, handle: GenerationalHandle) -> bool {
667 if let Some(ptr) = self.allocator.get_ptr(self.size_class, handle) {
669 unsafe {
670 std::ptr::drop_in_place(ptr.as_ptr() as *mut T);
671 }
672 }
673
674 self.allocator.free(self.size_class, handle)
675 }
676
677 pub fn get(&self, handle: GenerationalHandle) -> Option<&T> {
679 let ptr = self.allocator.get_ptr(self.size_class, handle)?;
680 Some(unsafe { &*(ptr.as_ptr() as *const T) })
681 }
682
683 pub fn get_mut(&self, handle: GenerationalHandle) -> Option<&mut T> {
685 let ptr = self.allocator.get_ptr(self.size_class, handle)?;
686 Some(unsafe { &mut *(ptr.as_ptr() as *mut T) })
687 }
688}
689
690impl<T: Sized> Default for TypedSlabAllocator<T> {
691 fn default() -> Self {
692 Self::new().expect("Type too large for slab allocation")
693 }
694}
695
696#[cfg(test)]
697mod tests {
698 use super::*;
699 use std::sync::Arc;
700 use std::thread;
701
702 #[test]
703 fn test_generational_handle() {
704 let handle = GenerationalHandle::new(42, 7);
705 assert_eq!(handle.slot(), 42);
706 assert_eq!(handle.generation(), 7);
707
708 let raw = handle.to_raw();
709 let handle2 = GenerationalHandle::from_raw(raw);
710 assert_eq!(handle2.slot(), 42);
711 assert_eq!(handle2.generation(), 7);
712 }
713
714 #[test]
715 fn test_size_class() {
716 assert_eq!(SizeClass::for_size(32), Some(SizeClass::Size64));
717 assert_eq!(SizeClass::for_size(64), Some(SizeClass::Size64));
718 assert_eq!(SizeClass::for_size(65), Some(SizeClass::Size128));
719 assert_eq!(SizeClass::for_size(512), Some(SizeClass::Size512));
720 assert_eq!(SizeClass::for_size(1025), None);
721 }
722
723 #[test]
724 fn test_slab_basic() {
725 let slab = Slab::new(SizeClass::Size64, 10).unwrap();
726
727 let h1 = slab.allocate().unwrap();
728 let h2 = slab.allocate().unwrap();
729 let h3 = slab.allocate().unwrap();
730
731 assert_ne!(h1.slot(), h2.slot());
732 assert_ne!(h2.slot(), h3.slot());
733
734 assert!(slab.free(h2));
735
736 let h4 = slab.allocate().unwrap();
737 assert_eq!(h4.slot(), h2.slot()); assert_ne!(h4.generation(), h2.generation()); }
740
741 #[test]
742 fn test_slab_generation() {
743 let slab = Slab::new(SizeClass::Size64, 10).unwrap();
744
745 let h1 = slab.allocate().unwrap();
746 assert!(slab.free(h1));
747
748 let ptr = slab.get_ptr(h1);
750 assert!(ptr.is_none());
751
752 assert!(!slab.free(h1));
754 }
755
756 #[test]
757 fn test_allocator_basic() {
758 let allocator = SlabAllocator::new();
759
760 let (h1, p1) = allocator.allocate(32).unwrap();
761 let (h2, p2) = allocator.allocate(100).unwrap();
762 let (h3, p3) = allocator.allocate(300).unwrap();
763
764 assert!(p1.as_ptr() != p2.as_ptr());
765 assert!(p2.as_ptr() != p3.as_ptr());
766
767 assert!(allocator.free(SizeClass::Size64, h1));
768 assert!(allocator.free(SizeClass::Size128, h2));
769 assert!(allocator.free(SizeClass::Size512, h3));
770 }
771
772 #[test]
773 fn test_allocator_concurrent() {
774 let allocator = Arc::new(SlabAllocator::new());
775 let mut handles = vec![];
776
777 for _ in 0..4 {
778 let allocator = allocator.clone();
779 handles.push(thread::spawn(move || {
780 let mut local_handles = Vec::new();
781 for _ in 0..1000 {
782 let (handle, _ptr) = allocator.allocate(64).unwrap();
783 local_handles.push(handle);
784 }
785
786 for handle in local_handles.drain(..500) {
788 allocator.free(SizeClass::Size64, handle);
789 }
790
791 local_handles
792 }));
793 }
794
795 let mut all_handles = Vec::new();
796 for handle in handles {
797 all_handles.extend(handle.join().unwrap());
798 }
799
800 let stats = allocator.stats();
801 assert_eq!(stats.total_allocations, 4000);
802 assert_eq!(stats.total_frees, 2000);
803 }
804
805 #[test]
806 fn test_typed_allocator() {
807 #[derive(Debug, PartialEq)]
808 struct TestNode {
809 value: i32,
810 next: Option<u64>,
811 }
812
813 let allocator = TypedSlabAllocator::<TestNode>::new().unwrap();
814
815 let (h1, _) = allocator.allocate(TestNode { value: 42, next: None }).unwrap();
816 let (h2, _) = allocator.allocate(TestNode { value: 100, next: Some(h1.to_raw()) }).unwrap();
817
818 let node1 = allocator.get(h1).unwrap();
819 assert_eq!(node1.value, 42);
820
821 let node2 = allocator.get(h2).unwrap();
822 assert_eq!(node2.value, 100);
823 assert_eq!(node2.next, Some(h1.to_raw()));
824
825 assert!(allocator.free(h1));
826 assert!(allocator.free(h2));
827 }
828
829 #[test]
830 fn test_stats() {
831 let allocator = SlabAllocator::new();
832
833 for _ in 0..50 {
834 allocator.allocate(32).unwrap();
835 }
836 for _ in 0..30 {
837 allocator.allocate(200).unwrap();
838 }
839
840 let stats = allocator.stats();
841 assert_eq!(stats.total_allocations, 80);
842
843 let size64_stats = stats.class_stats.iter()
845 .find(|s| s.size_class == SizeClass::Size64)
846 .unwrap();
847 assert_eq!(size64_stats.allocated_slots, 50);
848 }
849}