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