1use std::alloc::{alloc, dealloc, Layout};
14use std::cell::RefCell;
15use std::ptr;
16
17pub const CACHE_LINE_SIZE: usize = 64;
19
20pub struct Arena {
25 chunks: RefCell<Vec<Chunk>>,
26 chunk_size: usize,
27}
28
29struct Chunk {
30 data: *mut u8,
31 capacity: usize,
32 used: usize,
33}
34
35impl Arena {
36 pub fn new(chunk_size: usize) -> Self {
38 Self {
39 chunks: RefCell::new(Vec::new()),
40 chunk_size,
41 }
42 }
43
44 pub fn with_default_chunk_size() -> Self {
46 Self::new(1024 * 1024) }
48
49 pub fn alloc_vec<T>(&self, count: usize) -> ArenaVec<T> {
51 let size = count * std::mem::size_of::<T>();
52 let align = std::mem::align_of::<T>();
53
54 let ptr = self.alloc_raw(size, align);
55
56 ArenaVec {
57 ptr: ptr as *mut T,
58 len: 0,
59 capacity: count,
60 _phantom: std::marker::PhantomData,
61 }
62 }
63
64 fn alloc_raw(&self, size: usize, align: usize) -> *mut u8 {
66 assert!(
68 align > 0 && align.is_power_of_two(),
69 "Alignment must be a power of 2"
70 );
71 assert!(size > 0, "Cannot allocate zero bytes");
72 assert!(size <= isize::MAX as usize, "Allocation size too large");
73
74 let mut chunks = self.chunks.borrow_mut();
75
76 if let Some(chunk) = chunks.last_mut() {
78 let current = chunk.used;
80 let aligned = (current + align - 1) & !(align - 1);
81
82 if aligned < current {
84 panic!("Alignment calculation overflow");
85 }
86
87 let needed = aligned
88 .checked_add(size)
89 .expect("Arena allocation size overflow");
90
91 if needed <= chunk.capacity {
92 chunk.used = needed;
93 return unsafe {
94 let ptr = chunk.data.add(aligned);
96 debug_assert!(ptr as usize >= chunk.data as usize, "Pointer underflow");
97 ptr
98 };
99 }
100 }
101
102 let chunk_size = self.chunk_size.max(size + align);
104 let layout = Layout::from_size_align(chunk_size, 64).unwrap();
105 let data = unsafe { alloc(layout) };
106
107 let aligned = align;
108 let chunk = Chunk {
109 data,
110 capacity: chunk_size,
111 used: aligned + size,
112 };
113
114 let ptr = unsafe { data.add(aligned) };
115 chunks.push(chunk);
116
117 ptr
118 }
119
120 pub fn reset(&self) {
122 let mut chunks = self.chunks.borrow_mut();
123 for chunk in chunks.iter_mut() {
124 chunk.used = 0;
125 }
126 }
127
128 pub fn allocated_bytes(&self) -> usize {
130 let chunks = self.chunks.borrow();
131 chunks.iter().map(|c| c.capacity).sum()
132 }
133
134 pub fn used_bytes(&self) -> usize {
136 let chunks = self.chunks.borrow();
137 chunks.iter().map(|c| c.used).sum()
138 }
139}
140
141impl Drop for Arena {
142 fn drop(&mut self) {
143 let chunks = self.chunks.borrow();
144 for chunk in chunks.iter() {
145 let layout = Layout::from_size_align(chunk.capacity, 64).unwrap();
146 unsafe {
147 dealloc(chunk.data, layout);
148 }
149 }
150 }
151}
152
153pub struct ArenaVec<T> {
155 ptr: *mut T,
156 len: usize,
157 capacity: usize,
158 _phantom: std::marker::PhantomData<T>,
159}
160
161impl<T> ArenaVec<T> {
162 pub fn push(&mut self, value: T) {
164 assert!(self.len < self.capacity, "ArenaVec capacity exceeded");
166 assert!(!self.ptr.is_null(), "ArenaVec pointer is null");
167
168 unsafe {
169 let offset_ptr = self.ptr.add(self.len);
171 debug_assert!(
172 offset_ptr as usize >= self.ptr as usize,
173 "Pointer arithmetic overflow"
174 );
175 ptr::write(offset_ptr, value);
176 }
177 self.len += 1;
178 }
179
180 pub fn len(&self) -> usize {
182 self.len
183 }
184
185 pub fn is_empty(&self) -> bool {
187 self.len == 0
188 }
189
190 pub fn capacity(&self) -> usize {
192 self.capacity
193 }
194
195 pub fn as_slice(&self) -> &[T] {
197 assert!(self.len <= self.capacity, "Length exceeds capacity");
199 assert!(!self.ptr.is_null(), "Cannot create slice from null pointer");
200
201 unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
202 }
203
204 pub fn as_mut_slice(&mut self) -> &mut [T] {
206 assert!(self.len <= self.capacity, "Length exceeds capacity");
208 assert!(!self.ptr.is_null(), "Cannot create slice from null pointer");
209
210 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }
211 }
212}
213
214impl<T> std::ops::Deref for ArenaVec<T> {
215 type Target = [T];
216
217 fn deref(&self) -> &[T] {
218 self.as_slice()
219 }
220}
221
222impl<T> std::ops::DerefMut for ArenaVec<T> {
223 fn deref_mut(&mut self) -> &mut [T] {
224 self.as_mut_slice()
225 }
226}
227
228thread_local! {
230 static THREAD_ARENA: RefCell<Arena> = RefCell::new(Arena::with_default_chunk_size());
231}
232
233#[repr(C, align(64))]
249pub struct CacheAlignedVec {
250 data: *mut f32,
251 len: usize,
252 capacity: usize,
253}
254
255impl CacheAlignedVec {
256 pub fn with_capacity(capacity: usize) -> Self {
263 Self::try_with_capacity(capacity).expect("Failed to allocate cache-aligned memory")
264 }
265
266 pub fn try_with_capacity(capacity: usize) -> Option<Self> {
270 if capacity == 0 {
272 return Some(Self {
273 data: std::ptr::null_mut(),
274 len: 0,
275 capacity: 0,
276 });
277 }
278
279 let layout =
281 Layout::from_size_align(capacity * std::mem::size_of::<f32>(), CACHE_LINE_SIZE).ok()?;
282
283 let data = unsafe { alloc(layout) as *mut f32 };
284
285 if data.is_null() {
287 return None;
288 }
289
290 Some(Self {
291 data,
292 len: 0,
293 capacity,
294 })
295 }
296
297 pub fn from_slice(slice: &[f32]) -> Self {
304 Self::try_from_slice(slice).expect("Failed to allocate cache-aligned memory for slice")
305 }
306
307 pub fn try_from_slice(slice: &[f32]) -> Option<Self> {
311 let mut vec = Self::try_with_capacity(slice.len())?;
312 if !slice.is_empty() {
313 unsafe {
314 ptr::copy_nonoverlapping(slice.as_ptr(), vec.data, slice.len());
315 }
316 }
317 vec.len = slice.len();
318 Some(vec)
319 }
320
321 pub fn push(&mut self, value: f32) {
327 assert!(
328 self.len < self.capacity,
329 "CacheAlignedVec capacity exceeded"
330 );
331 assert!(
332 !self.data.is_null(),
333 "Cannot push to zero-capacity CacheAlignedVec"
334 );
335 unsafe {
336 *self.data.add(self.len) = value;
337 }
338 self.len += 1;
339 }
340
341 #[inline]
343 pub fn len(&self) -> usize {
344 self.len
345 }
346
347 #[inline]
349 pub fn is_empty(&self) -> bool {
350 self.len == 0
351 }
352
353 #[inline]
355 pub fn capacity(&self) -> usize {
356 self.capacity
357 }
358
359 #[inline]
361 pub fn as_slice(&self) -> &[f32] {
362 if self.len == 0 {
363 return &[];
365 }
366 unsafe { std::slice::from_raw_parts(self.data, self.len) }
368 }
369
370 #[inline]
372 pub fn as_mut_slice(&mut self) -> &mut [f32] {
373 if self.len == 0 {
374 return &mut [];
376 }
377 unsafe { std::slice::from_raw_parts_mut(self.data, self.len) }
379 }
380
381 #[inline]
383 pub fn as_ptr(&self) -> *const f32 {
384 self.data
385 }
386
387 #[inline]
389 pub fn as_mut_ptr(&mut self) -> *mut f32 {
390 self.data
391 }
392
393 #[inline]
397 pub fn is_aligned(&self) -> bool {
398 if self.data.is_null() {
399 return self.capacity == 0;
401 }
402 (self.data as usize) % CACHE_LINE_SIZE == 0
403 }
404
405 pub fn clear(&mut self) {
407 self.len = 0;
408 }
409}
410
411impl Drop for CacheAlignedVec {
412 fn drop(&mut self) {
413 if !self.data.is_null() && self.capacity > 0 {
414 let layout = Layout::from_size_align(
415 self.capacity * std::mem::size_of::<f32>(),
416 CACHE_LINE_SIZE,
417 )
418 .expect("Invalid layout");
419
420 unsafe {
421 dealloc(self.data as *mut u8, layout);
422 }
423 }
424 }
425}
426
427impl std::ops::Deref for CacheAlignedVec {
428 type Target = [f32];
429
430 fn deref(&self) -> &[f32] {
431 self.as_slice()
432 }
433}
434
435impl std::ops::DerefMut for CacheAlignedVec {
436 fn deref_mut(&mut self) -> &mut [f32] {
437 self.as_mut_slice()
438 }
439}
440
441unsafe impl Send for CacheAlignedVec {}
443unsafe impl Sync for CacheAlignedVec {}
444
445pub struct BatchVectorAllocator {
450 data: *mut f32,
451 dimensions: usize,
452 capacity: usize,
453 count: usize,
454}
455
456impl BatchVectorAllocator {
457 pub fn new(dimensions: usize, initial_capacity: usize) -> Self {
464 Self::try_new(dimensions, initial_capacity)
465 .expect("Failed to allocate batch vector storage")
466 }
467
468 pub fn try_new(dimensions: usize, initial_capacity: usize) -> Option<Self> {
472 if dimensions == 0 || initial_capacity == 0 {
474 return Some(Self {
475 data: std::ptr::null_mut(),
476 dimensions,
477 capacity: initial_capacity,
478 count: 0,
479 });
480 }
481
482 let total_floats = dimensions * initial_capacity;
483
484 let layout =
485 Layout::from_size_align(total_floats * std::mem::size_of::<f32>(), CACHE_LINE_SIZE)
486 .ok()?;
487
488 let data = unsafe { alloc(layout) as *mut f32 };
489
490 if data.is_null() {
492 return None;
493 }
494
495 Some(Self {
496 data,
497 dimensions,
498 capacity: initial_capacity,
499 count: 0,
500 })
501 }
502
503 pub fn add(&mut self, vector: &[f32]) -> usize {
509 assert_eq!(vector.len(), self.dimensions, "Vector dimension mismatch");
510 assert!(self.count < self.capacity, "Batch allocator full");
511 assert!(
512 !self.data.is_null(),
513 "Cannot add to zero-capacity BatchVectorAllocator"
514 );
515
516 let offset = self.count * self.dimensions;
517 unsafe {
518 ptr::copy_nonoverlapping(vector.as_ptr(), self.data.add(offset), self.dimensions);
519 }
520
521 let index = self.count;
522 self.count += 1;
523 index
524 }
525
526 pub fn get(&self, index: usize) -> &[f32] {
528 assert!(index < self.count, "Index out of bounds");
529 let offset = index * self.dimensions;
530 unsafe { std::slice::from_raw_parts(self.data.add(offset), self.dimensions) }
531 }
532
533 pub fn get_mut(&mut self, index: usize) -> &mut [f32] {
535 assert!(index < self.count, "Index out of bounds");
536 let offset = index * self.dimensions;
537 unsafe { std::slice::from_raw_parts_mut(self.data.add(offset), self.dimensions) }
538 }
539
540 #[inline]
542 pub fn ptr_at(&self, index: usize) -> *const f32 {
543 assert!(index < self.count, "Index out of bounds");
544 let offset = index * self.dimensions;
545 unsafe { self.data.add(offset) }
546 }
547
548 #[inline]
550 pub fn len(&self) -> usize {
551 self.count
552 }
553
554 #[inline]
556 pub fn is_empty(&self) -> bool {
557 self.count == 0
558 }
559
560 #[inline]
562 pub fn dimensions(&self) -> usize {
563 self.dimensions
564 }
565
566 pub fn clear(&mut self) {
568 self.count = 0;
569 }
570}
571
572impl Drop for BatchVectorAllocator {
573 fn drop(&mut self) {
574 if !self.data.is_null() {
575 let layout = Layout::from_size_align(
576 self.dimensions * self.capacity * std::mem::size_of::<f32>(),
577 CACHE_LINE_SIZE,
578 )
579 .expect("Invalid layout");
580
581 unsafe {
582 dealloc(self.data as *mut u8, layout);
583 }
584 }
585 }
586}
587
588unsafe impl Send for BatchVectorAllocator {}
590unsafe impl Sync for BatchVectorAllocator {}
591
592#[cfg(test)]
593mod tests {
594 use super::*;
595
596 #[test]
597 fn test_arena_alloc() {
598 let arena = Arena::new(1024);
599
600 let mut vec1 = arena.alloc_vec::<f32>(10);
601 vec1.push(1.0);
602 vec1.push(2.0);
603 vec1.push(3.0);
604
605 assert_eq!(vec1.len(), 3);
606 assert_eq!(vec1[0], 1.0);
607 assert_eq!(vec1[1], 2.0);
608 assert_eq!(vec1[2], 3.0);
609 }
610
611 #[test]
612 fn test_arena_multiple_allocs() {
613 let arena = Arena::new(1024);
614
615 let vec1 = arena.alloc_vec::<u32>(100);
616 let vec2 = arena.alloc_vec::<u64>(50);
617 let vec3 = arena.alloc_vec::<f32>(200);
618
619 assert_eq!(vec1.capacity(), 100);
620 assert_eq!(vec2.capacity(), 50);
621 assert_eq!(vec3.capacity(), 200);
622 }
623
624 #[test]
625 fn test_arena_reset() {
626 let arena = Arena::new(1024);
627
628 {
629 let _vec1 = arena.alloc_vec::<f32>(100);
630 let _vec2 = arena.alloc_vec::<f32>(100);
631 }
632
633 let used_before = arena.used_bytes();
634 arena.reset();
635 let used_after = arena.used_bytes();
636
637 assert!(used_after < used_before);
638 }
639
640 #[test]
641 fn test_cache_aligned_vec() {
642 let mut vec = CacheAlignedVec::with_capacity(100);
643
644 assert!(vec.is_aligned(), "Vector should be cache-aligned");
646
647 for i in 0..50 {
649 vec.push(i as f32);
650 }
651 assert_eq!(vec.len(), 50);
652
653 let slice = vec.as_slice();
655 assert_eq!(slice[0], 0.0);
656 assert_eq!(slice[49], 49.0);
657 }
658
659 #[test]
660 fn test_cache_aligned_vec_from_slice() {
661 let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
662 let aligned = CacheAlignedVec::from_slice(&data);
663
664 assert!(aligned.is_aligned());
665 assert_eq!(aligned.len(), 5);
666 assert_eq!(aligned.as_slice(), &data[..]);
667 }
668
669 #[test]
670 fn test_batch_vector_allocator() {
671 let mut allocator = BatchVectorAllocator::new(4, 10);
672
673 let v1 = vec![1.0, 2.0, 3.0, 4.0];
674 let v2 = vec![5.0, 6.0, 7.0, 8.0];
675
676 let idx1 = allocator.add(&v1);
677 let idx2 = allocator.add(&v2);
678
679 assert_eq!(idx1, 0);
680 assert_eq!(idx2, 1);
681 assert_eq!(allocator.len(), 2);
682
683 assert_eq!(allocator.get(0), &v1[..]);
685 assert_eq!(allocator.get(1), &v2[..]);
686 }
687
688 #[test]
689 fn test_batch_allocator_clear() {
690 let mut allocator = BatchVectorAllocator::new(3, 5);
691
692 allocator.add(&[1.0, 2.0, 3.0]);
693 allocator.add(&[4.0, 5.0, 6.0]);
694
695 assert_eq!(allocator.len(), 2);
696
697 allocator.clear();
698 assert_eq!(allocator.len(), 0);
699
700 allocator.add(&[7.0, 8.0, 9.0]);
702 assert_eq!(allocator.len(), 1);
703 }
704}