Skip to main content

just_engine/runner/ds/
heap.rs

1//! Heap management for the JavaScript runtime.
2//!
3//! This module provides heap allocation tracking with optional memory limits.
4
5use crate::runner::ds::error::JErrorType;
6
7/// Configuration for the heap manager.
8#[derive(Debug, Clone)]
9pub struct HeapConfig {
10    /// Maximum heap size in bytes. None means unlimited.
11    pub max_bytes: Option<usize>,
12}
13
14impl HeapConfig {
15    /// Create a new heap configuration with no memory limit.
16    pub fn unlimited() -> Self {
17        HeapConfig { max_bytes: None }
18    }
19
20    /// Create a new heap configuration with a memory limit.
21    pub fn with_limit(max_bytes: usize) -> Self {
22        HeapConfig {
23            max_bytes: Some(max_bytes),
24        }
25    }
26}
27
28impl Default for HeapConfig {
29    fn default() -> Self {
30        Self::unlimited()
31    }
32}
33
34/// Heap manager for tracking memory allocations.
35#[derive(Debug)]
36pub struct Heap {
37    config: HeapConfig,
38    allocated_bytes: usize,
39}
40
41impl Heap {
42    /// Create a new heap with the given configuration.
43    pub fn new(config: HeapConfig) -> Self {
44        Heap {
45            config,
46            allocated_bytes: 0,
47        }
48    }
49
50    /// Allocate memory on the heap.
51    ///
52    /// Returns an error if the allocation would exceed the memory limit.
53    pub fn allocate(&mut self, bytes: usize) -> Result<(), JErrorType> {
54        if let Some(max_bytes) = self.config.max_bytes {
55            if self.allocated_bytes + bytes > max_bytes {
56                return Err(JErrorType::RangeError("Out of memory".to_string()));
57            }
58        }
59        self.allocated_bytes += bytes;
60        Ok(())
61    }
62
63    /// Deallocate memory from the heap.
64    pub fn deallocate(&mut self, bytes: usize) {
65        self.allocated_bytes = self.allocated_bytes.saturating_sub(bytes);
66    }
67
68    /// Get the current allocated bytes.
69    pub fn get_allocated(&self) -> usize {
70        self.allocated_bytes
71    }
72
73    /// Get the maximum allowed bytes, if any.
74    pub fn get_max_bytes(&self) -> Option<usize> {
75        self.config.max_bytes
76    }
77
78    /// Check if allocation of the given size would succeed.
79    pub fn can_allocate(&self, bytes: usize) -> bool {
80        if let Some(max_bytes) = self.config.max_bytes {
81            self.allocated_bytes + bytes <= max_bytes
82        } else {
83            true
84        }
85    }
86
87    /// Get the remaining available bytes, if limited.
88    pub fn available_bytes(&self) -> Option<usize> {
89        self.config
90            .max_bytes
91            .map(|max| max.saturating_sub(self.allocated_bytes))
92    }
93}
94
95impl Default for Heap {
96    fn default() -> Self {
97        Self::new(HeapConfig::default())
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_heap_unlimited() {
107        let mut heap = Heap::new(HeapConfig::unlimited());
108        assert!(heap.allocate(1000).is_ok());
109        assert!(heap.allocate(1000000).is_ok());
110        assert_eq!(heap.get_allocated(), 1001000);
111    }
112
113    #[test]
114    fn test_heap_limited() {
115        let mut heap = Heap::new(HeapConfig::with_limit(1000));
116        assert!(heap.allocate(500).is_ok());
117        assert!(heap.allocate(400).is_ok());
118        assert_eq!(heap.get_allocated(), 900);
119
120        // This should fail
121        let result = heap.allocate(200);
122        assert!(result.is_err());
123        if let Err(JErrorType::RangeError(msg)) = result {
124            assert_eq!(msg, "Out of memory");
125        }
126    }
127
128    #[test]
129    fn test_heap_deallocate() {
130        let mut heap = Heap::new(HeapConfig::with_limit(1000));
131        heap.allocate(500).unwrap();
132        heap.deallocate(300);
133        assert_eq!(heap.get_allocated(), 200);
134
135        // Now we should be able to allocate more
136        assert!(heap.allocate(700).is_ok());
137    }
138
139    #[test]
140    fn test_heap_can_allocate() {
141        let heap = Heap::new(HeapConfig::with_limit(1000));
142        assert!(heap.can_allocate(500));
143        assert!(heap.can_allocate(1000));
144        assert!(!heap.can_allocate(1001));
145    }
146
147    #[test]
148    fn test_heap_available_bytes() {
149        let mut heap = Heap::new(HeapConfig::with_limit(1000));
150        assert_eq!(heap.available_bytes(), Some(1000));
151        heap.allocate(300).unwrap();
152        assert_eq!(heap.available_bytes(), Some(700));
153
154        let unlimited = Heap::new(HeapConfig::unlimited());
155        assert_eq!(unlimited.available_bytes(), None);
156    }
157}