Skip to main content

optirs_gpu/memory/allocation/
arena_allocator.rs

1// Arena allocator for GPU memory management
2//
3// This module implements arena (linear) allocators that allocate objects
4// sequentially from a contiguous block of memory. Arena allocators are
5// extremely fast for allocation and are ideal for temporary allocations
6// that can be freed all at once.
7
8#[allow(dead_code)]
9use std::collections::VecDeque;
10use std::marker::PhantomData;
11use std::ptr::NonNull;
12use std::sync::{Arc, Mutex};
13use std::time::{Duration, Instant};
14
15/// Main arena allocator implementation
16pub struct ArenaAllocator {
17    /// Base pointer of the arena
18    base_ptr: NonNull<u8>,
19    /// Total size of the arena
20    total_size: usize,
21    /// Current allocation offset
22    current_offset: usize,
23    /// High water mark (maximum offset reached)
24    high_water_mark: usize,
25    /// Memory alignment requirement
26    alignment: usize,
27    /// Arena configuration
28    config: ArenaConfig,
29    /// Allocation tracking (if enabled)
30    allocations: Vec<AllocationRecord>,
31    /// Statistics
32    stats: ArenaStats,
33    /// Checkpoints for nested scopes
34    checkpoints: Vec<ArenaCheckpoint>,
35}
36
37/// Arena allocation record
38#[derive(Debug, Clone)]
39pub struct AllocationRecord {
40    /// Pointer to the allocation
41    pub ptr: NonNull<u8>,
42    /// Size of the allocation
43    pub size: usize,
44    /// Offset from base
45    pub offset: usize,
46    /// Timestamp of allocation
47    pub allocated_at: Instant,
48    /// Allocation ID for debugging
49    pub id: u64,
50    /// Optional debug tag
51    pub tag: Option<String>,
52}
53
54/// Arena checkpoint for nested scopes
55#[derive(Debug, Clone)]
56pub struct ArenaCheckpoint {
57    /// Offset at checkpoint creation
58    pub offset: usize,
59    /// Number of allocations at checkpoint
60    pub allocation_count: usize,
61    /// Timestamp of checkpoint creation
62    pub created_at: Instant,
63    /// Optional checkpoint name
64    pub name: Option<String>,
65}
66
67/// Arena allocator configuration
68#[derive(Debug, Clone)]
69pub struct ArenaConfig {
70    /// Memory alignment (must be power of 2)
71    pub alignment: usize,
72    /// Enable allocation tracking
73    pub enable_tracking: bool,
74    /// Enable debug mode with extra checks
75    pub enable_debug: bool,
76    /// Enable checkpoint support
77    pub enable_checkpoints: bool,
78    /// Enable statistics collection
79    pub enable_stats: bool,
80    /// Growth strategy for resizable arenas
81    pub growth_strategy: GrowthStrategy,
82    /// Initial capacity for allocation tracking
83    pub initial_tracking_capacity: usize,
84}
85
86impl Default for ArenaConfig {
87    fn default() -> Self {
88        Self {
89            alignment: 8,
90            enable_tracking: false,
91            enable_debug: false,
92            enable_checkpoints: true,
93            enable_stats: true,
94            growth_strategy: GrowthStrategy::Fixed,
95            initial_tracking_capacity: 1024,
96        }
97    }
98}
99
100/// Growth strategy for resizable arenas
101#[derive(Debug, Clone, PartialEq)]
102pub enum GrowthStrategy {
103    /// Fixed size arena (no growth)
104    Fixed,
105    /// Double the size when full
106    Double,
107    /// Linear growth by fixed amount
108    Linear(usize),
109    /// Custom growth function
110    Custom(fn(usize) -> usize),
111}
112
113/// Arena allocator statistics
114#[derive(Debug, Clone, Default)]
115pub struct ArenaStats {
116    /// Total number of allocations
117    pub total_allocations: u64,
118    /// Total bytes allocated
119    pub total_bytes_allocated: u64,
120    /// Current bytes allocated
121    pub current_bytes_allocated: usize,
122    /// Peak bytes allocated
123    pub peak_bytes_allocated: usize,
124    /// Number of resets
125    pub reset_count: u64,
126    /// Number of checkpoint operations
127    pub checkpoint_count: u64,
128    /// Number of rollback operations
129    pub rollback_count: u64,
130    /// Average allocation size
131    pub average_allocation_size: f64,
132    /// Allocation rate (allocations per second)
133    pub allocation_rate: f64,
134    /// Memory utilization ratio
135    pub utilization_ratio: f64,
136    /// Time of first allocation
137    pub first_allocation_time: Option<Instant>,
138    /// Time of last allocation
139    pub last_allocation_time: Option<Instant>,
140}
141
142impl ArenaStats {
143    pub fn record_allocation(&mut self, size: usize) {
144        let now = Instant::now();
145
146        self.total_allocations += 1;
147        self.total_bytes_allocated += size as u64;
148        self.current_bytes_allocated += size;
149
150        if self.current_bytes_allocated > self.peak_bytes_allocated {
151            self.peak_bytes_allocated = self.current_bytes_allocated;
152        }
153
154        // Update average allocation size
155        self.average_allocation_size =
156            self.total_bytes_allocated as f64 / self.total_allocations as f64;
157
158        // Update allocation rate
159        if let Some(first_time) = self.first_allocation_time {
160            let elapsed = now.duration_since(first_time).as_secs_f64();
161            if elapsed > 0.0 {
162                self.allocation_rate = self.total_allocations as f64 / elapsed;
163            }
164        } else {
165            self.first_allocation_time = Some(now);
166        }
167
168        self.last_allocation_time = Some(now);
169    }
170
171    pub fn record_reset(&mut self) {
172        self.reset_count += 1;
173        self.current_bytes_allocated = 0;
174    }
175
176    pub fn record_checkpoint(&mut self) {
177        self.checkpoint_count += 1;
178    }
179
180    pub fn record_rollback(&mut self, bytes_freed: usize) {
181        self.rollback_count += 1;
182        self.current_bytes_allocated = self.current_bytes_allocated.saturating_sub(bytes_freed);
183    }
184
185    pub fn update_utilization(&mut self, total_size: usize) {
186        if total_size > 0 {
187            self.utilization_ratio = self.current_bytes_allocated as f64 / total_size as f64;
188        }
189    }
190}
191
192impl ArenaAllocator {
193    /// Create a new arena allocator
194    pub fn new(
195        base_ptr: NonNull<u8>,
196        size: usize,
197        config: ArenaConfig,
198    ) -> Result<Self, ArenaError> {
199        if size == 0 {
200            return Err(ArenaError::InvalidSize(
201                "Arena size cannot be zero".to_string(),
202            ));
203        }
204
205        if !config.alignment.is_power_of_two() {
206            return Err(ArenaError::InvalidAlignment(format!(
207                "Alignment {} is not a power of two",
208                config.alignment
209            )));
210        }
211
212        let allocations = if config.enable_tracking {
213            Vec::with_capacity(config.initial_tracking_capacity)
214        } else {
215            Vec::new()
216        };
217
218        Ok(Self {
219            base_ptr,
220            total_size: size,
221            current_offset: 0,
222            high_water_mark: 0,
223            alignment: config.alignment,
224            allocations,
225            stats: ArenaStats::default(),
226            checkpoints: Vec::new(),
227            config,
228        })
229    }
230
231    /// Allocate memory from the arena
232    pub fn allocate(&mut self, size: usize) -> Result<NonNull<u8>, ArenaError> {
233        if size == 0 {
234            return Err(ArenaError::InvalidSize(
235                "Cannot allocate zero bytes".to_string(),
236            ));
237        }
238
239        // Align the size
240        let aligned_size = (size + self.alignment - 1) & !(self.alignment - 1);
241
242        // Check if we have enough space
243        if self.current_offset + aligned_size > self.total_size {
244            return Err(ArenaError::OutOfMemory(format!(
245                "Not enough space: need {}, have {}",
246                aligned_size,
247                self.total_size - self.current_offset
248            )));
249        }
250
251        // Calculate the pointer
252        let ptr =
253            unsafe { NonNull::new_unchecked(self.base_ptr.as_ptr().add(self.current_offset)) };
254
255        // Update state
256        self.current_offset += aligned_size;
257        if self.current_offset > self.high_water_mark {
258            self.high_water_mark = self.current_offset;
259        }
260
261        // Record allocation
262        if self.config.enable_tracking {
263            let record = AllocationRecord {
264                ptr,
265                size: aligned_size,
266                offset: self.current_offset - aligned_size,
267                allocated_at: Instant::now(),
268                id: self.stats.total_allocations,
269                tag: None,
270            };
271            self.allocations.push(record);
272        }
273
274        // Update statistics
275        if self.config.enable_stats {
276            self.stats.record_allocation(aligned_size);
277            self.stats.update_utilization(self.total_size);
278        }
279
280        Ok(ptr)
281    }
282
283    /// Allocate memory with a debug tag
284    pub fn allocate_tagged(&mut self, size: usize, tag: String) -> Result<NonNull<u8>, ArenaError> {
285        let ptr = self.allocate(size)?;
286
287        if self.config.enable_tracking && !self.allocations.is_empty() {
288            let last_idx = self.allocations.len() - 1;
289            self.allocations[last_idx].tag = Some(tag);
290        }
291
292        Ok(ptr)
293    }
294
295    /// Allocate aligned memory
296    pub fn allocate_aligned(
297        &mut self,
298        size: usize,
299        alignment: usize,
300    ) -> Result<NonNull<u8>, ArenaError> {
301        if !alignment.is_power_of_two() {
302            return Err(ArenaError::InvalidAlignment(format!(
303                "Alignment {} is not a power of two",
304                alignment
305            )));
306        }
307
308        // Calculate aligned offset
309        let aligned_offset = (self.current_offset + alignment - 1) & !(alignment - 1);
310        let padding = aligned_offset - self.current_offset;
311
312        // Check if we have enough space including padding
313        if aligned_offset + size > self.total_size {
314            return Err(ArenaError::OutOfMemory(format!(
315                "Not enough space for aligned allocation: need {}, have {}",
316                aligned_offset + size - self.current_offset,
317                self.total_size - self.current_offset
318            )));
319        }
320
321        // Update offset to aligned position
322        self.current_offset = aligned_offset;
323
324        // Now allocate normally
325        self.allocate(size)
326    }
327
328    /// Reset the arena to empty state
329    pub fn reset(&mut self) {
330        self.current_offset = 0;
331
332        if self.config.enable_tracking {
333            self.allocations.clear();
334        }
335
336        if self.config.enable_stats {
337            self.stats.record_reset();
338            self.stats.update_utilization(self.total_size);
339        }
340
341        self.checkpoints.clear();
342    }
343
344    /// Create a checkpoint for later rollback
345    pub fn checkpoint(&mut self) -> Result<CheckpointHandle, ArenaError> {
346        if !self.config.enable_checkpoints {
347            return Err(ArenaError::CheckpointsDisabled);
348        }
349
350        let checkpoint = ArenaCheckpoint {
351            offset: self.current_offset,
352            allocation_count: self.allocations.len(),
353            created_at: Instant::now(),
354            name: None,
355        };
356
357        self.checkpoints.push(checkpoint);
358
359        if self.config.enable_stats {
360            self.stats.record_checkpoint();
361        }
362
363        Ok(CheckpointHandle {
364            index: self.checkpoints.len() - 1,
365            offset: self.current_offset,
366        })
367    }
368
369    /// Create a named checkpoint
370    pub fn checkpoint_named(&mut self, name: String) -> Result<CheckpointHandle, ArenaError> {
371        if !self.config.enable_checkpoints {
372            return Err(ArenaError::CheckpointsDisabled);
373        }
374
375        let checkpoint = ArenaCheckpoint {
376            offset: self.current_offset,
377            allocation_count: self.allocations.len(),
378            created_at: Instant::now(),
379            name: Some(name),
380        };
381
382        self.checkpoints.push(checkpoint);
383
384        if self.config.enable_stats {
385            self.stats.record_checkpoint();
386        }
387
388        Ok(CheckpointHandle {
389            index: self.checkpoints.len() - 1,
390            offset: self.current_offset,
391        })
392    }
393
394    /// Rollback to a checkpoint
395    pub fn rollback(&mut self, handle: CheckpointHandle) -> Result<(), ArenaError> {
396        if !self.config.enable_checkpoints {
397            return Err(ArenaError::CheckpointsDisabled);
398        }
399
400        if handle.index >= self.checkpoints.len() {
401            return Err(ArenaError::InvalidCheckpoint(
402                "Checkpoint index out of range".to_string(),
403            ));
404        }
405
406        let checkpoint = &self.checkpoints[handle.index];
407        let bytes_freed = self.current_offset - checkpoint.offset;
408
409        // Rollback state
410        self.current_offset = checkpoint.offset;
411
412        if self.config.enable_tracking {
413            self.allocations.truncate(checkpoint.allocation_count);
414        }
415
416        // Remove checkpoints created after this one
417        self.checkpoints.truncate(handle.index);
418
419        if self.config.enable_stats {
420            self.stats.record_rollback(bytes_freed);
421            self.stats.update_utilization(self.total_size);
422        }
423
424        Ok(())
425    }
426
427    /// Get current usage information
428    pub fn get_usage(&self) -> ArenaUsage {
429        ArenaUsage {
430            total_size: self.total_size,
431            used_size: self.current_offset,
432            free_size: self.total_size - self.current_offset,
433            high_water_mark: self.high_water_mark,
434            allocation_count: self.allocations.len(),
435            checkpoint_count: self.checkpoints.len(),
436            utilization_ratio: self.current_offset as f64 / self.total_size as f64,
437        }
438    }
439
440    /// Get statistics
441    pub fn get_stats(&self) -> &ArenaStats {
442        &self.stats
443    }
444
445    /// Get allocation records (if tracking enabled)
446    pub fn get_allocations(&self) -> &[AllocationRecord] {
447        &self.allocations
448    }
449
450    /// Get checkpoints
451    pub fn get_checkpoints(&self) -> &[ArenaCheckpoint] {
452        &self.checkpoints
453    }
454
455    /// Check if a pointer belongs to this arena
456    pub fn contains_pointer(&self, ptr: NonNull<u8>) -> bool {
457        let ptr_addr = ptr.as_ptr() as usize;
458        let base_addr = self.base_ptr.as_ptr() as usize;
459
460        ptr_addr >= base_addr && ptr_addr < base_addr + self.current_offset
461    }
462
463    /// Get allocation info for a pointer (if tracking enabled)
464    pub fn get_allocation_info(&self, ptr: NonNull<u8>) -> Option<&AllocationRecord> {
465        if !self.config.enable_tracking {
466            return None;
467        }
468
469        self.allocations.iter().find(|record| record.ptr == ptr)
470    }
471
472    /// Validate arena consistency
473    pub fn validate(&self) -> Result<(), ArenaError> {
474        if self.current_offset > self.total_size {
475            return Err(ArenaError::CorruptedArena(format!(
476                "Current offset {} exceeds total size {}",
477                self.current_offset, self.total_size
478            )));
479        }
480
481        if self.high_water_mark > self.total_size {
482            return Err(ArenaError::CorruptedArena(format!(
483                "High water mark {} exceeds total size {}",
484                self.high_water_mark, self.total_size
485            )));
486        }
487
488        if self.high_water_mark < self.current_offset {
489            return Err(ArenaError::CorruptedArena(format!(
490                "High water mark {} is less than current offset {}",
491                self.high_water_mark, self.current_offset
492            )));
493        }
494
495        // Validate tracking records if enabled
496        if self.config.enable_tracking {
497            let mut total_tracked_size = 0;
498
499            for (i, record) in self.allocations.iter().enumerate() {
500                // Check pointer is within arena bounds
501                if !self.contains_pointer(record.ptr) {
502                    return Err(ArenaError::CorruptedArena(format!(
503                        "Allocation {} has pointer outside arena bounds",
504                        i
505                    )));
506                }
507
508                total_tracked_size += record.size;
509            }
510
511            // Note: total_tracked_size might be less than current_offset due to alignment padding
512            if total_tracked_size > self.current_offset {
513                return Err(ArenaError::CorruptedArena(format!(
514                    "Tracked size {} exceeds current offset {}",
515                    total_tracked_size, self.current_offset
516                )));
517            }
518        }
519
520        Ok(())
521    }
522
523    /// Get memory layout information
524    pub fn get_memory_layout(&self) -> MemoryLayout {
525        let mut layout = MemoryLayout {
526            base_address: self.base_ptr.as_ptr() as usize,
527            total_size: self.total_size,
528            used_size: self.current_offset,
529            regions: Vec::new(),
530        };
531
532        if self.config.enable_tracking {
533            for record in &self.allocations {
534                layout.regions.push(MemoryRegion {
535                    offset: record.offset,
536                    size: record.size,
537                    allocated_at: record.allocated_at,
538                    tag: record.tag.clone(),
539                });
540            }
541        }
542
543        layout
544    }
545}
546
547// Safety: ArenaAllocator manages GPU memory pointers. While NonNull<u8> is not Send/Sync by default,
548// it's safe to share ArenaAllocator across threads when protected by Arc<Mutex<>> because:
549// 1. The pointers point to GPU memory managed by the GPU driver
550// 2. The Mutex provides exclusive access for all mutable operations
551// 3. No thread-local state is maintained
552unsafe impl Send for ArenaAllocator {}
553unsafe impl Sync for ArenaAllocator {}
554
555/// Checkpoint handle for rollback operations
556#[derive(Debug, Clone)]
557pub struct CheckpointHandle {
558    index: usize,
559    offset: usize,
560}
561
562/// Arena usage information
563#[derive(Debug, Clone)]
564pub struct ArenaUsage {
565    pub total_size: usize,
566    pub used_size: usize,
567    pub free_size: usize,
568    pub high_water_mark: usize,
569    pub allocation_count: usize,
570    pub checkpoint_count: usize,
571    pub utilization_ratio: f64,
572}
573
574/// Memory layout information
575#[derive(Debug, Clone)]
576pub struct MemoryLayout {
577    pub base_address: usize,
578    pub total_size: usize,
579    pub used_size: usize,
580    pub regions: Vec<MemoryRegion>,
581}
582
583/// Memory region within arena
584#[derive(Debug, Clone)]
585pub struct MemoryRegion {
586    pub offset: usize,
587    pub size: usize,
588    pub allocated_at: Instant,
589    pub tag: Option<String>,
590}
591
592/// Ring buffer arena allocator for circular allocation patterns
593pub struct RingArena {
594    arena: ArenaAllocator,
595    /// Read pointer for ring buffer
596    read_offset: usize,
597    /// Number of live allocations
598    live_allocations: usize,
599    /// Ring configuration
600    ring_config: RingConfig,
601}
602
603/// Ring arena configuration
604#[derive(Debug, Clone)]
605pub struct RingConfig {
606    /// Enable overwrite protection
607    pub overwrite_protection: bool,
608    /// Callback when data is overwritten
609    pub overwrite_callback: Option<fn(*mut u8, usize)>,
610    /// Enable statistics
611    pub enable_stats: bool,
612}
613
614impl Default for RingConfig {
615    fn default() -> Self {
616        Self {
617            overwrite_protection: true,
618            overwrite_callback: None,
619            enable_stats: true,
620        }
621    }
622}
623
624impl RingArena {
625    pub fn new(
626        base_ptr: NonNull<u8>,
627        size: usize,
628        ring_config: RingConfig,
629    ) -> Result<Self, ArenaError> {
630        let arena_config = ArenaConfig {
631            enable_tracking: ring_config.enable_stats,
632            enable_checkpoints: false,
633            ..ArenaConfig::default()
634        };
635
636        let arena = ArenaAllocator::new(base_ptr, size, arena_config)?;
637
638        Ok(Self {
639            arena,
640            read_offset: 0,
641            live_allocations: 0,
642            ring_config,
643        })
644    }
645
646    /// Allocate from ring buffer
647    pub fn allocate(&mut self, size: usize) -> Result<NonNull<u8>, ArenaError> {
648        // Check if allocation would wrap around and collide with live data
649        if self.ring_config.overwrite_protection {
650            let aligned_size = (size + self.arena.alignment - 1) & !(self.arena.alignment - 1);
651
652            if self.arena.current_offset + aligned_size > self.arena.total_size {
653                // Would wrap around
654                if self.read_offset > 0 && aligned_size > self.read_offset {
655                    return Err(ArenaError::RingBufferFull(
656                        "Ring buffer full, would overwrite live data".to_string(),
657                    ));
658                }
659
660                // Safe to wrap
661                self.arena.current_offset = 0;
662            } else if self.read_offset > self.arena.current_offset {
663                // Normal case, check collision
664                if self.arena.current_offset + aligned_size > self.read_offset {
665                    return Err(ArenaError::RingBufferFull(
666                        "Ring buffer full, would overwrite live data".to_string(),
667                    ));
668                }
669            }
670        }
671
672        let ptr = self.arena.allocate(size)?;
673        self.live_allocations += 1;
674
675        Ok(ptr)
676    }
677
678    /// Mark data as consumed (advance read pointer)
679    pub fn consume(&mut self, size: usize) -> Result<(), ArenaError> {
680        let aligned_size = (size + self.arena.alignment - 1) & !(self.arena.alignment - 1);
681
682        if self.read_offset + aligned_size > self.arena.total_size {
683            // Wrap around
684            self.read_offset = aligned_size - (self.arena.total_size - self.read_offset);
685        } else {
686            self.read_offset += aligned_size;
687        }
688
689        self.live_allocations = self.live_allocations.saturating_sub(1);
690
691        Ok(())
692    }
693
694    /// Reset ring buffer
695    pub fn reset(&mut self) {
696        self.arena.reset();
697        self.read_offset = 0;
698        self.live_allocations = 0;
699    }
700
701    /// Get ring buffer usage
702    pub fn get_ring_usage(&self) -> RingUsage {
703        let total_size = self.arena.total_size;
704        let write_offset = self.arena.current_offset;
705
706        let used_size = if write_offset >= self.read_offset {
707            write_offset - self.read_offset
708        } else {
709            total_size - self.read_offset + write_offset
710        };
711
712        RingUsage {
713            total_size,
714            used_size,
715            free_size: total_size - used_size,
716            read_offset: self.read_offset,
717            write_offset,
718            live_allocations: self.live_allocations,
719        }
720    }
721}
722
723/// Ring buffer usage information
724#[derive(Debug, Clone)]
725pub struct RingUsage {
726    pub total_size: usize,
727    pub used_size: usize,
728    pub free_size: usize,
729    pub read_offset: usize,
730    pub write_offset: usize,
731    pub live_allocations: usize,
732}
733
734/// Growing arena that can expand its capacity
735pub struct GrowingArena {
736    /// Current arena
737    current_arena: ArenaAllocator,
738    /// Previous arenas (for lookups)
739    previous_arenas: Vec<ArenaAllocator>,
740    /// Growth strategy
741    growth_strategy: GrowthStrategy,
742    /// External memory allocator for growth
743    external_allocator: Option<Box<dyn ExternalAllocator>>,
744}
745
746/// External allocator trait for growing arenas
747pub trait ExternalAllocator {
748    fn allocate(&mut self, size: usize) -> Result<NonNull<u8>, ArenaError>;
749    fn deallocate(&mut self, ptr: NonNull<u8>, size: usize);
750}
751
752impl GrowingArena {
753    pub fn new(
754        base_ptr: NonNull<u8>,
755        initial_size: usize,
756        growth_strategy: GrowthStrategy,
757    ) -> Result<Self, ArenaError> {
758        let config = ArenaConfig::default();
759        let arena = ArenaAllocator::new(base_ptr, initial_size, config)?;
760
761        Ok(Self {
762            current_arena: arena,
763            previous_arenas: Vec::new(),
764            growth_strategy,
765            external_allocator: None,
766        })
767    }
768
769    pub fn with_external_allocator(mut self, allocator: Box<dyn ExternalAllocator>) -> Self {
770        self.external_allocator = Some(allocator);
771        self
772    }
773
774    /// Allocate with automatic growth
775    pub fn allocate(&mut self, size: usize) -> Result<NonNull<u8>, ArenaError> {
776        // Try current arena first
777        match self.current_arena.allocate(size) {
778            Ok(ptr) => Ok(ptr),
779            Err(ArenaError::OutOfMemory(_)) => {
780                // Need to grow
781                self.grow(size)?;
782                self.current_arena.allocate(size)
783            }
784            Err(e) => Err(e),
785        }
786    }
787
788    fn grow(&mut self, min_additional_size: usize) -> Result<(), ArenaError> {
789        if self.external_allocator.is_none() {
790            return Err(ArenaError::CannotGrow(
791                "No external allocator configured".to_string(),
792            ));
793        }
794
795        let current_size = self.current_arena.total_size;
796        let new_size = match &self.growth_strategy {
797            GrowthStrategy::Fixed => {
798                return Err(ArenaError::CannotGrow("Fixed size arena".to_string()))
799            }
800            GrowthStrategy::Double => current_size * 2,
801            GrowthStrategy::Linear(increment) => current_size + increment,
802            GrowthStrategy::Custom(func) => func(current_size),
803        };
804
805        let actual_new_size = new_size.max(min_additional_size);
806
807        let new_ptr = self
808            .external_allocator
809            .as_mut()
810            .expect("unwrap failed")
811            .allocate(actual_new_size)?;
812
813        // Move current arena to previous arenas
814        let old_arena = std::mem::replace(
815            &mut self.current_arena,
816            ArenaAllocator::new(new_ptr, actual_new_size, ArenaConfig::default())?,
817        );
818
819        self.previous_arenas.push(old_arena);
820
821        Ok(())
822    }
823
824    /// Check if pointer belongs to any arena
825    pub fn contains_pointer(&self, ptr: NonNull<u8>) -> bool {
826        if self.current_arena.contains_pointer(ptr) {
827            return true;
828        }
829
830        self.previous_arenas
831            .iter()
832            .any(|arena| arena.contains_pointer(ptr))
833    }
834
835    /// Get total usage across all arenas
836    pub fn get_total_usage(&self) -> GrowingArenaUsage {
837        let mut total_size = self.current_arena.total_size;
838        let mut used_size = self.current_arena.current_offset;
839        let mut allocation_count = self.current_arena.allocations.len();
840
841        for arena in &self.previous_arenas {
842            total_size += arena.total_size;
843            used_size += arena.current_offset;
844            allocation_count += arena.allocations.len();
845        }
846
847        GrowingArenaUsage {
848            total_size,
849            used_size,
850            free_size: total_size - used_size,
851            arena_count: 1 + self.previous_arenas.len(),
852            allocation_count,
853            current_arena_size: self.current_arena.total_size,
854            utilization_ratio: used_size as f64 / total_size as f64,
855        }
856    }
857}
858
859/// Growing arena usage information
860#[derive(Debug, Clone)]
861pub struct GrowingArenaUsage {
862    pub total_size: usize,
863    pub used_size: usize,
864    pub free_size: usize,
865    pub arena_count: usize,
866    pub allocation_count: usize,
867    pub current_arena_size: usize,
868    pub utilization_ratio: f64,
869}
870
871/// Arena allocator errors
872#[derive(Debug, Clone)]
873pub enum ArenaError {
874    InvalidSize(String),
875    InvalidAlignment(String),
876    OutOfMemory(String),
877    CheckpointsDisabled,
878    InvalidCheckpoint(String),
879    CorruptedArena(String),
880    RingBufferFull(String),
881    CannotGrow(String),
882}
883
884impl std::fmt::Display for ArenaError {
885    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
886        match self {
887            ArenaError::InvalidSize(msg) => write!(f, "Invalid size: {}", msg),
888            ArenaError::InvalidAlignment(msg) => write!(f, "Invalid alignment: {}", msg),
889            ArenaError::OutOfMemory(msg) => write!(f, "Out of memory: {}", msg),
890            ArenaError::CheckpointsDisabled => write!(f, "Checkpoints are disabled"),
891            ArenaError::InvalidCheckpoint(msg) => write!(f, "Invalid checkpoint: {}", msg),
892            ArenaError::CorruptedArena(msg) => write!(f, "Corrupted arena: {}", msg),
893            ArenaError::RingBufferFull(msg) => write!(f, "Ring buffer full: {}", msg),
894            ArenaError::CannotGrow(msg) => write!(f, "Cannot grow: {}", msg),
895        }
896    }
897}
898
899impl std::error::Error for ArenaError {}
900
901/// Thread-safe arena allocator wrapper
902pub struct ThreadSafeArena {
903    arena: Arc<Mutex<ArenaAllocator>>,
904}
905
906impl ThreadSafeArena {
907    pub fn new(
908        base_ptr: NonNull<u8>,
909        size: usize,
910        config: ArenaConfig,
911    ) -> Result<Self, ArenaError> {
912        let arena = ArenaAllocator::new(base_ptr, size, config)?;
913        Ok(Self {
914            arena: Arc::new(Mutex::new(arena)),
915        })
916    }
917
918    pub fn allocate(&self, size: usize) -> Result<NonNull<u8>, ArenaError> {
919        let mut arena = self.arena.lock().expect("lock poisoned");
920        arena.allocate(size)
921    }
922
923    pub fn reset(&self) {
924        let mut arena = self.arena.lock().expect("lock poisoned");
925        arena.reset();
926    }
927
928    pub fn checkpoint(&self) -> Result<CheckpointHandle, ArenaError> {
929        let mut arena = self.arena.lock().expect("lock poisoned");
930        arena.checkpoint()
931    }
932
933    pub fn rollback(&self, handle: CheckpointHandle) -> Result<(), ArenaError> {
934        let mut arena = self.arena.lock().expect("lock poisoned");
935        arena.rollback(handle)
936    }
937
938    pub fn get_usage(&self) -> ArenaUsage {
939        let arena = self.arena.lock().expect("lock poisoned");
940        arena.get_usage()
941    }
942
943    pub fn get_stats(&self) -> ArenaStats {
944        let arena = self.arena.lock().expect("lock poisoned");
945        arena.get_stats().clone()
946    }
947}
948
949#[cfg(test)]
950mod tests {
951    use super::*;
952
953    #[test]
954    fn test_arena_creation() {
955        let size = 4096;
956        let memory = vec![0u8; size];
957        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
958
959        let config = ArenaConfig::default();
960        let arena = ArenaAllocator::new(ptr, size, config);
961        assert!(arena.is_ok());
962    }
963
964    #[test]
965    fn test_basic_allocation() {
966        let size = 4096;
967        let memory = vec![0u8; size];
968        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
969
970        let config = ArenaConfig::default();
971        let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
972
973        let alloc1 = arena.allocate(100);
974        assert!(alloc1.is_ok());
975
976        let alloc2 = arena.allocate(200);
977        assert!(alloc2.is_ok());
978
979        let usage = arena.get_usage();
980        assert!(usage.used_size > 0);
981        assert!(usage.allocation_count == 2 || !arena.config.enable_tracking);
982    }
983
984    #[test]
985    fn test_alignment() {
986        let size = 4096;
987        let memory = vec![0u8; size];
988        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
989
990        let config = ArenaConfig {
991            alignment: 16,
992            ..ArenaConfig::default()
993        };
994        let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
995
996        let alloc_ptr = arena.allocate(10).expect("unwrap failed");
997        assert_eq!(alloc_ptr.as_ptr() as usize % 16, 0);
998    }
999
1000    #[test]
1001    fn test_checkpoints() {
1002        let size = 4096;
1003        let memory = vec![0u8; size];
1004        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1005
1006        let config = ArenaConfig {
1007            enable_checkpoints: true,
1008            enable_tracking: true,
1009            ..ArenaConfig::default()
1010        };
1011        let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1012
1013        arena.allocate(100).expect("unwrap failed");
1014        let checkpoint = arena.checkpoint().expect("unwrap failed");
1015        arena.allocate(200).expect("unwrap failed");
1016
1017        let usage_before = arena.get_usage();
1018        arena.rollback(checkpoint).expect("unwrap failed");
1019        let usage_after = arena.get_usage();
1020
1021        assert!(usage_after.used_size < usage_before.used_size);
1022    }
1023
1024    #[test]
1025    fn test_reset() {
1026        let size = 4096;
1027        let memory = vec![0u8; size];
1028        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1029
1030        let config = ArenaConfig::default();
1031        let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1032
1033        arena.allocate(100).expect("unwrap failed");
1034        arena.allocate(200).expect("unwrap failed");
1035
1036        let usage_before = arena.get_usage();
1037        assert!(usage_before.used_size > 0);
1038
1039        arena.reset();
1040        let usage_after = arena.get_usage();
1041        assert_eq!(usage_after.used_size, 0);
1042    }
1043
1044    #[test]
1045    fn test_ring_arena() {
1046        let size = 1024;
1047        let memory = vec![0u8; size];
1048        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1049
1050        let config = RingConfig::default();
1051        let mut ring = RingArena::new(ptr, size, config).expect("unwrap failed");
1052
1053        let alloc1 = ring.allocate(100);
1054        assert!(alloc1.is_ok());
1055
1056        ring.consume(100).expect("unwrap failed");
1057
1058        let alloc2 = ring.allocate(100);
1059        assert!(alloc2.is_ok());
1060    }
1061
1062    #[test]
1063    fn test_thread_safe_arena() {
1064        let size = 4096;
1065        let memory = vec![0u8; size];
1066        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1067
1068        let config = ArenaConfig::default();
1069        let arena = ThreadSafeArena::new(ptr, size, config).expect("unwrap failed");
1070
1071        let alloc_result = arena.allocate(100);
1072        assert!(alloc_result.is_ok());
1073
1074        let usage = arena.get_usage();
1075        assert!(usage.used_size > 0);
1076    }
1077
1078    #[test]
1079    fn test_arena_validation() {
1080        let size = 4096;
1081        let memory = vec![0u8; size];
1082        let ptr = NonNull::new(memory.as_ptr() as *mut u8).expect("unwrap failed");
1083
1084        let config = ArenaConfig {
1085            enable_tracking: true,
1086            ..ArenaConfig::default()
1087        };
1088        let mut arena = ArenaAllocator::new(ptr, size, config).expect("unwrap failed");
1089
1090        arena.allocate(100).expect("unwrap failed");
1091        arena.allocate(200).expect("unwrap failed");
1092
1093        let validation_result = arena.validate();
1094        assert!(validation_result.is_ok());
1095    }
1096}