1use std::ptr::NonNull;
9use std::sync::{Arc, Mutex};
10use std::time::Instant;
11
12pub struct ArenaAllocator {
14 base_ptr: NonNull<u8>,
16 total_size: usize,
18 current_offset: usize,
20 high_water_mark: usize,
22 alignment: usize,
24 config: ArenaConfig,
26 allocations: Vec<AllocationRecord>,
28 stats: ArenaStats,
30 checkpoints: Vec<ArenaCheckpoint>,
32}
33
34#[derive(Debug, Clone)]
36pub struct AllocationRecord {
37 pub ptr: NonNull<u8>,
39 pub size: usize,
41 pub offset: usize,
43 pub allocated_at: Instant,
45 pub id: u64,
47 pub tag: Option<String>,
49}
50
51#[derive(Debug, Clone)]
53pub struct ArenaCheckpoint {
54 pub offset: usize,
56 pub allocation_count: usize,
58 pub created_at: Instant,
60 pub name: Option<String>,
62}
63
64#[derive(Debug, Clone)]
66pub struct ArenaConfig {
67 pub alignment: usize,
69 pub enable_tracking: bool,
71 pub enable_debug: bool,
73 pub enable_checkpoints: bool,
75 pub enable_stats: bool,
77 pub growth_strategy: GrowthStrategy,
79 pub initial_tracking_capacity: usize,
81}
82
83impl Default for ArenaConfig {
84 fn default() -> Self {
85 Self {
86 alignment: 8,
87 enable_tracking: false,
88 enable_debug: false,
89 enable_checkpoints: true,
90 enable_stats: true,
91 growth_strategy: GrowthStrategy::Fixed,
92 initial_tracking_capacity: 1024,
93 }
94 }
95}
96
97#[derive(Debug, Clone)]
106pub enum GrowthStrategy {
107 Fixed,
109 Double,
111 Linear(usize),
113 Custom(fn(usize) -> usize),
115}
116
117#[derive(Debug, Clone, Default)]
119pub struct ArenaStats {
120 pub total_allocations: u64,
122 pub total_bytes_allocated: u64,
124 pub current_bytes_allocated: usize,
126 pub peak_bytes_allocated: usize,
128 pub reset_count: u64,
130 pub checkpoint_count: u64,
132 pub rollback_count: u64,
134 pub average_allocation_size: f64,
136 pub allocation_rate: f64,
138 pub utilization_ratio: f64,
140 pub first_allocation_time: Option<Instant>,
142 pub last_allocation_time: Option<Instant>,
144 pub bytes_wasted_to_alignment: u64,
148}
149
150impl ArenaStats {
151 pub fn record_allocation(&mut self, size: usize) {
152 let now = Instant::now();
153
154 self.total_allocations += 1;
155 self.total_bytes_allocated += size as u64;
156 self.current_bytes_allocated += size;
157
158 if self.current_bytes_allocated > self.peak_bytes_allocated {
159 self.peak_bytes_allocated = self.current_bytes_allocated;
160 }
161
162 self.average_allocation_size =
164 self.total_bytes_allocated as f64 / self.total_allocations as f64;
165
166 if let Some(first_time) = self.first_allocation_time {
168 let elapsed = now.duration_since(first_time).as_secs_f64();
169 if elapsed > 0.0 {
170 self.allocation_rate = self.total_allocations as f64 / elapsed;
171 }
172 } else {
173 self.first_allocation_time = Some(now);
174 }
175
176 self.last_allocation_time = Some(now);
177 }
178
179 pub fn record_reset(&mut self) {
180 self.reset_count += 1;
181 self.current_bytes_allocated = 0;
182 }
183
184 pub fn record_checkpoint(&mut self) {
185 self.checkpoint_count += 1;
186 }
187
188 pub fn record_rollback(&mut self, bytes_freed: usize) {
189 self.rollback_count += 1;
190 self.current_bytes_allocated = self.current_bytes_allocated.saturating_sub(bytes_freed);
191 }
192
193 pub fn update_utilization(&mut self, total_size: usize) {
194 if total_size > 0 {
195 self.utilization_ratio = self.current_bytes_allocated as f64 / total_size as f64;
196 }
197 }
198
199 pub fn record_padding(&mut self, padding: usize) {
201 self.bytes_wasted_to_alignment += padding as u64;
202 }
203}
204
205impl ArenaAllocator {
206 pub fn new(
208 base_ptr: NonNull<u8>,
209 size: usize,
210 config: ArenaConfig,
211 ) -> Result<Self, ArenaError> {
212 if size == 0 {
213 return Err(ArenaError::InvalidSize(
214 "Arena size cannot be zero".to_string(),
215 ));
216 }
217
218 if !config.alignment.is_power_of_two() {
219 return Err(ArenaError::InvalidAlignment(format!(
220 "Alignment {} is not a power of two",
221 config.alignment
222 )));
223 }
224
225 let allocations = if config.enable_tracking {
226 Vec::with_capacity(config.initial_tracking_capacity)
227 } else {
228 Vec::new()
229 };
230
231 Ok(Self {
232 base_ptr,
233 total_size: size,
234 current_offset: 0,
235 high_water_mark: 0,
236 alignment: config.alignment,
237 allocations,
238 stats: ArenaStats::default(),
239 checkpoints: Vec::new(),
240 config,
241 })
242 }
243
244 pub fn allocate(&mut self, size: usize) -> Result<NonNull<u8>, ArenaError> {
246 if size == 0 {
247 return Err(ArenaError::InvalidSize(
248 "Cannot allocate zero bytes".to_string(),
249 ));
250 }
251
252 let aligned_size = (size + self.alignment - 1) & !(self.alignment - 1);
254
255 if self.current_offset + aligned_size > self.total_size {
257 return Err(ArenaError::OutOfMemory(format!(
258 "Not enough space: need {}, have {}",
259 aligned_size,
260 self.total_size - self.current_offset
261 )));
262 }
263
264 let ptr =
266 unsafe { NonNull::new_unchecked(self.base_ptr.as_ptr().add(self.current_offset)) };
267
268 self.current_offset += aligned_size;
270 if self.current_offset > self.high_water_mark {
271 self.high_water_mark = self.current_offset;
272 }
273
274 if self.config.enable_tracking {
276 let record = AllocationRecord {
277 ptr,
278 size: aligned_size,
279 offset: self.current_offset - aligned_size,
280 allocated_at: Instant::now(),
281 id: self.stats.total_allocations,
282 tag: None,
283 };
284 self.allocations.push(record);
285 }
286
287 if self.config.enable_stats {
289 self.stats.record_allocation(aligned_size);
290 self.stats.update_utilization(self.total_size);
291 }
292
293 Ok(ptr)
294 }
295
296 pub fn allocate_tagged(&mut self, size: usize, tag: String) -> Result<NonNull<u8>, ArenaError> {
298 let ptr = self.allocate(size)?;
299
300 if self.config.enable_tracking && !self.allocations.is_empty() {
301 let last_idx = self.allocations.len() - 1;
302 self.allocations[last_idx].tag = Some(tag);
303 }
304
305 Ok(ptr)
306 }
307
308 pub fn allocate_aligned(
310 &mut self,
311 size: usize,
312 alignment: usize,
313 ) -> Result<NonNull<u8>, ArenaError> {
314 if !alignment.is_power_of_two() {
315 return Err(ArenaError::InvalidAlignment(format!(
316 "Alignment {} is not a power of two",
317 alignment
318 )));
319 }
320
321 let aligned_offset = (self.current_offset + alignment - 1) & !(alignment - 1);
323 let padding = aligned_offset - self.current_offset;
324
325 if aligned_offset + size > self.total_size {
327 return Err(ArenaError::OutOfMemory(format!(
328 "Not enough space for aligned allocation: need {}, have {}",
329 aligned_offset + size - self.current_offset,
330 self.total_size - self.current_offset
331 )));
332 }
333
334 self.current_offset = aligned_offset;
336 if self.config.enable_stats && padding > 0 {
337 self.stats.record_padding(padding);
338 }
339
340 self.allocate(size)
342 }
343
344 pub fn reset(&mut self) {
346 self.current_offset = 0;
347
348 if self.config.enable_tracking {
349 self.allocations.clear();
350 }
351
352 if self.config.enable_stats {
353 self.stats.record_reset();
354 self.stats.update_utilization(self.total_size);
355 }
356
357 self.checkpoints.clear();
358 }
359
360 pub fn checkpoint(&mut self) -> Result<CheckpointHandle, ArenaError> {
362 if !self.config.enable_checkpoints {
363 return Err(ArenaError::CheckpointsDisabled);
364 }
365
366 let checkpoint = ArenaCheckpoint {
367 offset: self.current_offset,
368 allocation_count: self.allocations.len(),
369 created_at: Instant::now(),
370 name: None,
371 };
372
373 self.checkpoints.push(checkpoint);
374
375 if self.config.enable_stats {
376 self.stats.record_checkpoint();
377 }
378
379 Ok(CheckpointHandle {
380 index: self.checkpoints.len() - 1,
381 offset: self.current_offset,
382 })
383 }
384
385 pub fn checkpoint_named(&mut self, name: String) -> Result<CheckpointHandle, ArenaError> {
387 if !self.config.enable_checkpoints {
388 return Err(ArenaError::CheckpointsDisabled);
389 }
390
391 let checkpoint = ArenaCheckpoint {
392 offset: self.current_offset,
393 allocation_count: self.allocations.len(),
394 created_at: Instant::now(),
395 name: Some(name),
396 };
397
398 self.checkpoints.push(checkpoint);
399
400 if self.config.enable_stats {
401 self.stats.record_checkpoint();
402 }
403
404 Ok(CheckpointHandle {
405 index: self.checkpoints.len() - 1,
406 offset: self.current_offset,
407 })
408 }
409
410 pub fn rollback(&mut self, handle: CheckpointHandle) -> Result<(), ArenaError> {
412 if !self.config.enable_checkpoints {
413 return Err(ArenaError::CheckpointsDisabled);
414 }
415
416 if handle.index >= self.checkpoints.len() {
417 return Err(ArenaError::InvalidCheckpoint(
418 "Checkpoint index out of range".to_string(),
419 ));
420 }
421
422 let checkpoint = &self.checkpoints[handle.index];
423
424 if checkpoint.offset != handle.offset {
436 return Err(ArenaError::InvalidCheckpoint(
437 "stale checkpoint handle: the checkpoint at this index was replaced since the handle was created".to_string(),
438 ));
439 }
440
441 let bytes_freed = self.current_offset - checkpoint.offset;
442
443 self.current_offset = checkpoint.offset;
445
446 if self.config.enable_tracking {
447 self.allocations.truncate(checkpoint.allocation_count);
448 }
449
450 self.checkpoints.truncate(handle.index);
452
453 if self.config.enable_stats {
454 self.stats.record_rollback(bytes_freed);
455 self.stats.update_utilization(self.total_size);
456 }
457
458 Ok(())
459 }
460
461 pub fn get_usage(&self) -> ArenaUsage {
463 ArenaUsage {
464 total_size: self.total_size,
465 used_size: self.current_offset,
466 free_size: self.total_size - self.current_offset,
467 high_water_mark: self.high_water_mark,
468 allocation_count: self.allocations.len(),
469 checkpoint_count: self.checkpoints.len(),
470 utilization_ratio: self.current_offset as f64 / self.total_size as f64,
471 }
472 }
473
474 pub fn get_stats(&self) -> &ArenaStats {
476 &self.stats
477 }
478
479 pub fn get_allocations(&self) -> &[AllocationRecord] {
481 &self.allocations
482 }
483
484 pub fn get_checkpoints(&self) -> &[ArenaCheckpoint] {
486 &self.checkpoints
487 }
488
489 pub fn contains_pointer(&self, ptr: NonNull<u8>) -> bool {
491 let ptr_addr = ptr.as_ptr() as usize;
492 let base_addr = self.base_ptr.as_ptr() as usize;
493
494 ptr_addr >= base_addr && ptr_addr < base_addr + self.current_offset
495 }
496
497 pub fn get_allocation_info(&self, ptr: NonNull<u8>) -> Option<&AllocationRecord> {
499 if !self.config.enable_tracking {
500 return None;
501 }
502
503 self.allocations.iter().find(|record| record.ptr == ptr)
504 }
505
506 pub fn validate(&self) -> Result<(), ArenaError> {
508 if self.current_offset > self.total_size {
509 return Err(ArenaError::CorruptedArena(format!(
510 "Current offset {} exceeds total size {}",
511 self.current_offset, self.total_size
512 )));
513 }
514
515 if self.high_water_mark > self.total_size {
516 return Err(ArenaError::CorruptedArena(format!(
517 "High water mark {} exceeds total size {}",
518 self.high_water_mark, self.total_size
519 )));
520 }
521
522 if self.high_water_mark < self.current_offset {
523 return Err(ArenaError::CorruptedArena(format!(
524 "High water mark {} is less than current offset {}",
525 self.high_water_mark, self.current_offset
526 )));
527 }
528
529 if self.config.enable_tracking {
531 let mut total_tracked_size = 0;
532
533 for (i, record) in self.allocations.iter().enumerate() {
534 if !self.contains_pointer(record.ptr) {
536 return Err(ArenaError::CorruptedArena(format!(
537 "Allocation {} has pointer outside arena bounds",
538 i
539 )));
540 }
541
542 total_tracked_size += record.size;
543 }
544
545 if total_tracked_size > self.current_offset {
547 return Err(ArenaError::CorruptedArena(format!(
548 "Tracked size {} exceeds current offset {}",
549 total_tracked_size, self.current_offset
550 )));
551 }
552 }
553
554 Ok(())
555 }
556
557 pub fn get_memory_layout(&self) -> MemoryLayout {
559 let mut layout = MemoryLayout {
560 base_address: self.base_ptr.as_ptr() as usize,
561 total_size: self.total_size,
562 used_size: self.current_offset,
563 regions: Vec::new(),
564 };
565
566 if self.config.enable_tracking {
567 for record in &self.allocations {
568 layout.regions.push(MemoryRegion {
569 offset: record.offset,
570 size: record.size,
571 allocated_at: record.allocated_at,
572 tag: record.tag.clone(),
573 });
574 }
575 }
576
577 layout
578 }
579}
580
581unsafe impl Send for ArenaAllocator {}
587unsafe impl Sync for ArenaAllocator {}
588
589#[derive(Debug, Clone)]
591pub struct CheckpointHandle {
592 index: usize,
593 offset: usize,
594}
595
596#[derive(Debug, Clone)]
598pub struct ArenaUsage {
599 pub total_size: usize,
600 pub used_size: usize,
601 pub free_size: usize,
602 pub high_water_mark: usize,
603 pub allocation_count: usize,
604 pub checkpoint_count: usize,
605 pub utilization_ratio: f64,
606}
607
608#[derive(Debug, Clone)]
610pub struct MemoryLayout {
611 pub base_address: usize,
612 pub total_size: usize,
613 pub used_size: usize,
614 pub regions: Vec<MemoryRegion>,
615}
616
617#[derive(Debug, Clone)]
619pub struct MemoryRegion {
620 pub offset: usize,
621 pub size: usize,
622 pub allocated_at: Instant,
623 pub tag: Option<String>,
624}
625
626pub struct RingArena {
628 arena: ArenaAllocator,
629 read_offset: usize,
631 live_allocations: usize,
633 ring_config: RingConfig,
635}
636
637#[derive(Debug, Clone)]
639pub struct RingConfig {
640 pub overwrite_protection: bool,
642 pub overwrite_callback: Option<fn(*mut u8, usize)>,
644 pub enable_stats: bool,
646}
647
648impl Default for RingConfig {
649 fn default() -> Self {
650 Self {
651 overwrite_protection: true,
652 overwrite_callback: None,
653 enable_stats: true,
654 }
655 }
656}
657
658impl RingArena {
659 pub fn new(
660 base_ptr: NonNull<u8>,
661 size: usize,
662 ring_config: RingConfig,
663 ) -> Result<Self, ArenaError> {
664 let arena_config = ArenaConfig {
665 enable_tracking: ring_config.enable_stats,
666 enable_checkpoints: false,
667 ..ArenaConfig::default()
668 };
669
670 let arena = ArenaAllocator::new(base_ptr, size, arena_config)?;
671
672 Ok(Self {
673 arena,
674 read_offset: 0,
675 live_allocations: 0,
676 ring_config,
677 })
678 }
679
680 pub fn allocate(&mut self, size: usize) -> Result<NonNull<u8>, ArenaError> {
682 if self.ring_config.overwrite_protection {
684 let aligned_size = (size + self.arena.alignment - 1) & !(self.arena.alignment - 1);
685
686 if self.arena.current_offset + aligned_size > self.arena.total_size {
687 if self.read_offset > 0 && aligned_size > self.read_offset {
689 return Err(ArenaError::RingBufferFull(
690 "Ring buffer full, would overwrite live data".to_string(),
691 ));
692 }
693
694 self.arena.current_offset = 0;
696 } else if self.read_offset > self.arena.current_offset {
697 if self.arena.current_offset + aligned_size > self.read_offset {
699 return Err(ArenaError::RingBufferFull(
700 "Ring buffer full, would overwrite live data".to_string(),
701 ));
702 }
703 }
704 }
705
706 let ptr = self.arena.allocate(size)?;
707 self.live_allocations += 1;
708
709 Ok(ptr)
710 }
711
712 pub fn consume(&mut self, size: usize) -> Result<(), ArenaError> {
714 let aligned_size = (size + self.arena.alignment - 1) & !(self.arena.alignment - 1);
715
716 if self.read_offset + aligned_size > self.arena.total_size {
717 self.read_offset = aligned_size - (self.arena.total_size - self.read_offset);
719 } else {
720 self.read_offset += aligned_size;
721 }
722
723 self.live_allocations = self.live_allocations.saturating_sub(1);
724
725 Ok(())
726 }
727
728 pub fn reset(&mut self) {
730 self.arena.reset();
731 self.read_offset = 0;
732 self.live_allocations = 0;
733 }
734
735 pub fn get_ring_usage(&self) -> RingUsage {
737 let total_size = self.arena.total_size;
738 let write_offset = self.arena.current_offset;
739
740 let used_size = if write_offset >= self.read_offset {
741 write_offset - self.read_offset
742 } else {
743 total_size - self.read_offset + write_offset
744 };
745
746 RingUsage {
747 total_size,
748 used_size,
749 free_size: total_size - used_size,
750 read_offset: self.read_offset,
751 write_offset,
752 live_allocations: self.live_allocations,
753 }
754 }
755}
756
757#[derive(Debug, Clone)]
759pub struct RingUsage {
760 pub total_size: usize,
761 pub used_size: usize,
762 pub free_size: usize,
763 pub read_offset: usize,
764 pub write_offset: usize,
765 pub live_allocations: usize,
766}
767
768pub struct GrowingArena {
770 current_arena: ArenaAllocator,
772 previous_arenas: Vec<ArenaAllocator>,
774 growth_strategy: GrowthStrategy,
776 external_allocator: Option<Box<dyn ExternalAllocator>>,
778}
779
780pub trait ExternalAllocator {
782 fn allocate(&mut self, size: usize) -> Result<NonNull<u8>, ArenaError>;
783 fn deallocate(&mut self, ptr: NonNull<u8>, size: usize);
784}
785
786impl GrowingArena {
787 pub fn new(
788 base_ptr: NonNull<u8>,
789 initial_size: usize,
790 growth_strategy: GrowthStrategy,
791 ) -> Result<Self, ArenaError> {
792 let config = ArenaConfig::default();
793 let arena = ArenaAllocator::new(base_ptr, initial_size, config)?;
794
795 Ok(Self {
796 current_arena: arena,
797 previous_arenas: Vec::new(),
798 growth_strategy,
799 external_allocator: None,
800 })
801 }
802
803 pub fn with_external_allocator(mut self, allocator: Box<dyn ExternalAllocator>) -> Self {
804 self.external_allocator = Some(allocator);
805 self
806 }
807
808 pub fn allocate(&mut self, size: usize) -> Result<NonNull<u8>, ArenaError> {
810 match self.current_arena.allocate(size) {
812 Ok(ptr) => Ok(ptr),
813 Err(ArenaError::OutOfMemory(_)) => {
814 self.grow(size)?;
816 self.current_arena.allocate(size)
817 }
818 Err(e) => Err(e),
819 }
820 }
821
822 fn grow(&mut self, min_additional_size: usize) -> Result<(), ArenaError> {
823 if self.external_allocator.is_none() {
824 return Err(ArenaError::CannotGrow(
825 "No external allocator configured".to_string(),
826 ));
827 }
828
829 let current_size = self.current_arena.total_size;
830 let new_size = match &self.growth_strategy {
831 GrowthStrategy::Fixed => {
832 return Err(ArenaError::CannotGrow("Fixed size arena".to_string()))
833 }
834 GrowthStrategy::Double => current_size * 2,
835 GrowthStrategy::Linear(increment) => current_size + increment,
836 GrowthStrategy::Custom(func) => func(current_size),
837 };
838
839 let actual_new_size = new_size.max(min_additional_size);
840
841 let new_ptr = self
842 .external_allocator
843 .as_mut()
844 .ok_or_else(|| ArenaError::CannotGrow("No external allocator configured".to_string()))?
845 .allocate(actual_new_size)?;
846
847 let old_arena = std::mem::replace(
849 &mut self.current_arena,
850 ArenaAllocator::new(new_ptr, actual_new_size, ArenaConfig::default())?,
851 );
852
853 self.previous_arenas.push(old_arena);
854
855 Ok(())
856 }
857
858 pub fn contains_pointer(&self, ptr: NonNull<u8>) -> bool {
860 if self.current_arena.contains_pointer(ptr) {
861 return true;
862 }
863
864 self.previous_arenas
865 .iter()
866 .any(|arena| arena.contains_pointer(ptr))
867 }
868
869 pub fn get_total_usage(&self) -> GrowingArenaUsage {
871 let mut total_size = self.current_arena.total_size;
872 let mut used_size = self.current_arena.current_offset;
873 let mut allocation_count = self.current_arena.allocations.len();
874
875 for arena in &self.previous_arenas {
876 total_size += arena.total_size;
877 used_size += arena.current_offset;
878 allocation_count += arena.allocations.len();
879 }
880
881 GrowingArenaUsage {
882 total_size,
883 used_size,
884 free_size: total_size - used_size,
885 arena_count: 1 + self.previous_arenas.len(),
886 allocation_count,
887 current_arena_size: self.current_arena.total_size,
888 utilization_ratio: used_size as f64 / total_size as f64,
889 }
890 }
891}
892
893#[derive(Debug, Clone)]
895pub struct GrowingArenaUsage {
896 pub total_size: usize,
897 pub used_size: usize,
898 pub free_size: usize,
899 pub arena_count: usize,
900 pub allocation_count: usize,
901 pub current_arena_size: usize,
902 pub utilization_ratio: f64,
903}
904
905#[derive(Debug, Clone)]
907pub enum ArenaError {
908 InvalidSize(String),
909 InvalidAlignment(String),
910 OutOfMemory(String),
911 CheckpointsDisabled,
912 InvalidCheckpoint(String),
913 CorruptedArena(String),
914 RingBufferFull(String),
915 CannotGrow(String),
916}
917
918impl std::fmt::Display for ArenaError {
919 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
920 match self {
921 ArenaError::InvalidSize(msg) => write!(f, "Invalid size: {}", msg),
922 ArenaError::InvalidAlignment(msg) => write!(f, "Invalid alignment: {}", msg),
923 ArenaError::OutOfMemory(msg) => write!(f, "Out of memory: {}", msg),
924 ArenaError::CheckpointsDisabled => write!(f, "Checkpoints are disabled"),
925 ArenaError::InvalidCheckpoint(msg) => write!(f, "Invalid checkpoint: {}", msg),
926 ArenaError::CorruptedArena(msg) => write!(f, "Corrupted arena: {}", msg),
927 ArenaError::RingBufferFull(msg) => write!(f, "Ring buffer full: {}", msg),
928 ArenaError::CannotGrow(msg) => write!(f, "Cannot grow: {}", msg),
929 }
930 }
931}
932
933impl std::error::Error for ArenaError {}
934
935pub struct ThreadSafeArena {
937 arena: Arc<Mutex<ArenaAllocator>>,
938}
939
940impl ThreadSafeArena {
941 pub fn new(
942 base_ptr: NonNull<u8>,
943 size: usize,
944 config: ArenaConfig,
945 ) -> Result<Self, ArenaError> {
946 let arena = ArenaAllocator::new(base_ptr, size, config)?;
947 Ok(Self {
948 arena: Arc::new(Mutex::new(arena)),
949 })
950 }
951
952 pub fn allocate(&self, size: usize) -> Result<NonNull<u8>, ArenaError> {
953 let mut arena = self.arena.lock().unwrap_or_else(|e| e.into_inner());
954 arena.allocate(size)
955 }
956
957 pub fn reset(&self) {
958 let mut arena = self.arena.lock().unwrap_or_else(|e| e.into_inner());
959 arena.reset();
960 }
961
962 pub fn checkpoint(&self) -> Result<CheckpointHandle, ArenaError> {
963 let mut arena = self.arena.lock().unwrap_or_else(|e| e.into_inner());
964 arena.checkpoint()
965 }
966
967 pub fn rollback(&self, handle: CheckpointHandle) -> Result<(), ArenaError> {
968 let mut arena = self.arena.lock().unwrap_or_else(|e| e.into_inner());
969 arena.rollback(handle)
970 }
971
972 pub fn get_usage(&self) -> ArenaUsage {
973 let arena = self.arena.lock().unwrap_or_else(|e| e.into_inner());
974 arena.get_usage()
975 }
976
977 pub fn get_stats(&self) -> ArenaStats {
978 let arena = self.arena.lock().unwrap_or_else(|e| e.into_inner());
979 arena.get_stats().clone()
980 }
981}
982
983#[cfg(test)]
984mod tests {
985 use super::*;
986
987 #[test]
988 fn test_arena_creation() {
989 let size = 4096;
990 let memory = vec![0u8; size];
991 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
992
993 let config = ArenaConfig::default();
994 let arena = ArenaAllocator::new(ptr, size, config);
995 assert!(arena.is_ok());
996 }
997
998 #[test]
999 fn test_basic_allocation() {
1000 let size = 4096;
1001 let memory = vec![0u8; size];
1002 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1003
1004 let config = ArenaConfig::default();
1005 let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1006
1007 let alloc1 = arena.allocate(100);
1008 assert!(alloc1.is_ok());
1009
1010 let alloc2 = arena.allocate(200);
1011 assert!(alloc2.is_ok());
1012
1013 let usage = arena.get_usage();
1014 assert!(usage.used_size > 0);
1015 assert!(usage.allocation_count == 2 || !arena.config.enable_tracking);
1016 }
1017
1018 #[test]
1019 fn test_alignment() {
1020 let size = 4096;
1021 let memory = vec![0u8; size];
1022 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1023
1024 let config = ArenaConfig {
1025 alignment: 16,
1026 ..ArenaConfig::default()
1027 };
1028 let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1029
1030 let alloc_ptr = arena.allocate(10).expect("unwrap failed");
1031 assert_eq!(alloc_ptr.as_ptr() as usize % 16, 0);
1032 }
1033
1034 #[test]
1035 fn test_allocate_aligned_records_padding_in_stats() {
1036 let size = 4096;
1037 let memory = vec![0u8; size];
1038 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1039
1040 let config = ArenaConfig {
1041 alignment: 1,
1042 ..ArenaConfig::default()
1043 };
1044 let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1045 assert_eq!(arena.get_stats().bytes_wasted_to_alignment, 0);
1046
1047 arena.allocate(3).expect("unwrap failed");
1050
1051 arena
1053 .allocate_aligned(10, 64)
1054 .expect("aligned allocation should succeed");
1055 assert_eq!(arena.get_stats().bytes_wasted_to_alignment, 61);
1056
1057 let before = arena.get_stats().bytes_wasted_to_alignment;
1061 arena
1062 .allocate_aligned(4, 1)
1063 .expect("aligned allocation should succeed");
1064 assert_eq!(arena.get_stats().bytes_wasted_to_alignment, before);
1065 }
1066
1067 #[test]
1068 fn test_checkpoints() {
1069 let size = 4096;
1070 let memory = vec![0u8; size];
1071 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1072
1073 let config = ArenaConfig {
1074 enable_checkpoints: true,
1075 enable_tracking: true,
1076 ..ArenaConfig::default()
1077 };
1078 let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1079
1080 arena.allocate(100).expect("unwrap failed");
1081 let checkpoint = arena.checkpoint().expect("unwrap failed");
1082 arena.allocate(200).expect("unwrap failed");
1083
1084 let usage_before = arena.get_usage();
1085 arena.rollback(checkpoint).expect("unwrap failed");
1086 let usage_after = arena.get_usage();
1087
1088 assert!(usage_after.used_size < usage_before.used_size);
1089 }
1090
1091 #[test]
1092 fn test_rollback_rejects_stale_handle_after_index_reuse() {
1093 let size = 4096;
1094 let memory = vec![0u8; size];
1095 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1096
1097 let config = ArenaConfig {
1098 enable_checkpoints: true,
1099 enable_tracking: true,
1100 ..ArenaConfig::default()
1101 };
1102 let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1103
1104 arena.allocate(100).expect("unwrap failed");
1105 let handle_a = arena.checkpoint().expect("unwrap failed");
1106
1107 arena.rollback(handle_a.clone()).expect("unwrap failed");
1110
1111 arena.allocate(50).expect("unwrap failed");
1114 arena.checkpoint().expect("unwrap failed");
1115
1116 let result = arena.rollback(handle_a);
1120 assert!(matches!(result, Err(ArenaError::InvalidCheckpoint(_))));
1121 }
1122
1123 #[test]
1124 fn test_reset() {
1125 let size = 4096;
1126 let memory = vec![0u8; size];
1127 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1128
1129 let config = ArenaConfig::default();
1130 let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1131
1132 arena.allocate(100).expect("unwrap failed");
1133 arena.allocate(200).expect("unwrap failed");
1134
1135 let usage_before = arena.get_usage();
1136 assert!(usage_before.used_size > 0);
1137
1138 arena.reset();
1139 let usage_after = arena.get_usage();
1140 assert_eq!(usage_after.used_size, 0);
1141 }
1142
1143 #[test]
1144 fn test_ring_arena() {
1145 let size = 1024;
1146 let memory = vec![0u8; size];
1147 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1148
1149 let config = RingConfig::default();
1150 let mut ring = RingArena::new(ptr, size, config).expect("unwrap failed");
1151
1152 let alloc1 = ring.allocate(100);
1153 assert!(alloc1.is_ok());
1154
1155 ring.consume(100).expect("unwrap failed");
1156
1157 let alloc2 = ring.allocate(100);
1158 assert!(alloc2.is_ok());
1159 }
1160
1161 #[test]
1162 fn test_thread_safe_arena() {
1163 let size = 4096;
1164 let memory = vec![0u8; size];
1165 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1166
1167 let config = ArenaConfig::default();
1168 let arena = ThreadSafeArena::new(ptr, size, config).expect("unwrap failed");
1169
1170 let alloc_result = arena.allocate(100);
1171 assert!(alloc_result.is_ok());
1172
1173 let usage = arena.get_usage();
1174 assert!(usage.used_size > 0);
1175 }
1176
1177 #[test]
1178 fn test_arena_validation() {
1179 let size = 4096;
1180 let memory = vec![0u8; size];
1181 let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1182
1183 let config = ArenaConfig {
1184 enable_tracking: true,
1185 ..ArenaConfig::default()
1186 };
1187 let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1188
1189 arena.allocate(100).expect("unwrap failed");
1190 arena.allocate(200).expect("unwrap failed");
1191
1192 let validation_result = arena.validate();
1193 assert!(validation_result.is_ok());
1194 }
1195}