use crate::services::memory_manager::{
global_memory_manager, MemoryManager, PoolType, PooledBuffer,
};
use anyhow::Result;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use tracing::{debug, trace};
pub struct MemoryVec<T> {
data: Vec<T>,
pool_type: PoolType,
memory_manager: Arc<MemoryManager>,
}
impl<T> MemoryVec<T> {
pub fn new(pool_type: PoolType) -> Result<Self> {
let memory_manager = global_memory_manager()?;
Ok(Self {
data: Vec::new(),
pool_type,
memory_manager,
})
}
pub fn with_capacity(pool_type: PoolType, capacity: usize) -> Result<Self> {
let memory_manager = global_memory_manager()?;
Ok(Self {
data: Vec::with_capacity(capacity),
pool_type,
memory_manager,
})
}
pub fn push(&mut self, item: T) -> Result<()> {
let old_capacity = self.data.capacity();
self.data.push(item);
let new_capacity = self.data.capacity();
if new_capacity > old_capacity {
let growth = (new_capacity - old_capacity) * std::mem::size_of::<T>();
trace!(
"MemoryVec grew by {} bytes for pool {:?}",
growth,
self.pool_type
);
}
Ok(())
}
pub fn reserve(&mut self, additional: usize) -> Result<()> {
let old_capacity = self.data.capacity();
self.data.reserve(additional);
let new_capacity = self.data.capacity();
if new_capacity > old_capacity {
let growth = (new_capacity - old_capacity) * std::mem::size_of::<T>();
trace!(
"MemoryVec reserved {} bytes for pool {:?}",
growth,
self.pool_type
);
}
Ok(())
}
#[must_use]
pub fn memory_usage(&self) -> usize {
self.data.capacity() * std::mem::size_of::<T>()
}
pub fn process_with_memory_awareness<F, R>(&self, f: F) -> Result<R>
where
F: FnOnce(&[T]) -> R,
{
let stats = self.memory_manager.stats();
if stats.allocation_pressure > 0.9 {
debug!(
"High memory pressure detected: {:.1}%",
stats.allocation_pressure * 100.0
);
self.memory_manager.cleanup()?;
}
Ok(f(&self.data))
}
}
impl<T> Deref for MemoryVec<T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T> DerefMut for MemoryVec<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
pub struct MemoryString {
content: Arc<str>,
memory_manager: Arc<MemoryManager>,
}
impl MemoryString {
pub fn new(content: &str) -> Result<Self> {
let memory_manager = global_memory_manager()?;
let interned = memory_manager.intern_string(content)?;
Ok(Self {
content: interned,
memory_manager,
})
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.content
}
#[must_use]
pub fn shares_memory_with(&self, other: &MemoryString) -> bool {
Arc::ptr_eq(&self.content, &other.content)
}
#[must_use]
pub fn memory_stats(&self) -> super::memory_manager::MemoryStats {
self.memory_manager.stats()
}
}
impl Deref for MemoryString {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.content
}
}
impl std::fmt::Display for MemoryString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.content)
}
}
impl std::fmt::Debug for MemoryString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MemoryString({})", self.content)
}
}
pub trait MemoryAware {
fn memory_usage(&self) -> usize;
fn cleanup_memory(&mut self) -> Result<usize>;
fn configure_memory(&mut self, max_memory: usize) -> Result<()>;
fn memory_pressure(&self) -> f64 {
0.0 }
}
pub struct AstBufferPool {
memory_manager: Arc<MemoryManager>,
pool_type: PoolType,
}
impl AstBufferPool {
pub fn new(pool_type: PoolType) -> Result<Self> {
let memory_manager = global_memory_manager()?;
Ok(Self {
memory_manager,
pool_type,
})
}
pub fn get_buffer(&self, min_size: usize) -> Result<PooledBuffer> {
self.memory_manager
.allocate_buffer(self.pool_type, min_size)
}
pub fn get_buffer_for_content(&self, content: &str) -> Result<PooledBuffer> {
let size = content.len() * 2; self.memory_manager.allocate_buffer(self.pool_type, size)
}
}
pub struct InternedStringSet {
strings: std::collections::HashSet<Arc<str>>,
memory_manager: Arc<MemoryManager>,
}
impl InternedStringSet {
pub fn new() -> Result<Self> {
let memory_manager = global_memory_manager()?;
Ok(Self {
strings: std::collections::HashSet::new(),
memory_manager,
})
}
pub fn insert(&mut self, s: &str) -> Result<bool> {
let interned = self.memory_manager.intern_string(s)?;
Ok(self.strings.insert(interned))
}
#[must_use]
pub fn contains(&self, s: &str) -> bool {
if let Ok(interned) = self.memory_manager.intern_string(s) {
self.strings.contains(&interned)
} else {
false
}
}
pub fn iter(&self) -> impl Iterator<Item = &str> {
self.strings.iter().map(std::convert::AsRef::as_ref)
}
#[must_use]
pub fn memory_usage(&self) -> usize {
self.strings.len() * std::mem::size_of::<Arc<str>>()
}
}
pub struct MemoryAwareCache<K, V> {
cache: std::collections::HashMap<K, V>,
memory_manager: Arc<MemoryManager>,
pool_type: PoolType,
max_items: usize,
}
impl<K, V> MemoryAwareCache<K, V>
where
K: std::hash::Hash + Eq + Clone,
V: Clone,
{
pub fn new(pool_type: PoolType, max_items: usize) -> Result<Self> {
let memory_manager = global_memory_manager()?;
Ok(Self {
cache: std::collections::HashMap::new(),
memory_manager,
pool_type,
max_items,
})
}
pub fn insert(&mut self, key: K, value: V) -> Result<Option<V>> {
let stats = self.memory_manager.stats();
if stats.allocation_pressure > 0.85 && self.cache.len() >= self.max_items {
let keys_to_remove: Vec<_> = self
.cache
.keys()
.take(self.cache.len() / 4)
.cloned()
.collect();
for key in keys_to_remove {
self.cache.remove(&key);
}
debug!("Evicted cache entries due to memory pressure");
}
Ok(self.cache.insert(key, value))
}
pub fn get(&self, key: &K) -> Option<&V> {
self.cache.get(key)
}
#[must_use]
pub fn stats(&self) -> CacheStats {
CacheStats {
item_count: self.cache.len(),
max_items: self.max_items,
estimated_memory: self.cache.len()
* (std::mem::size_of::<K>() + std::mem::size_of::<V>()),
}
}
#[must_use]
pub fn pool_type(&self) -> PoolType {
self.pool_type
}
}
#[derive(Debug, Clone)]
pub struct CacheStats {
pub item_count: usize,
pub max_items: usize,
pub estimated_memory: usize,
}
pub mod utils {
use super::{Result, MemoryVec, PoolType, global_memory_manager};
pub fn create_identifier_vec() -> Result<MemoryVec<String>> {
MemoryVec::new(PoolType::StringIntern)
}
pub fn create_content_vec() -> Result<MemoryVec<u8>> {
MemoryVec::new(PoolType::FileContent)
}
pub fn create_ast_vec<T>() -> Result<MemoryVec<T>> {
MemoryVec::new(PoolType::AstParsing)
}
pub fn estimate_collection_memory<T>(collection: &[T]) -> usize {
std::mem::size_of_val(collection)
}
pub fn should_cleanup_memory() -> Result<bool> {
let manager = global_memory_manager()?;
let stats = manager.stats();
Ok(stats.allocation_pressure > 0.8)
}
pub fn cleanup_if_needed() -> Result<usize> {
if should_cleanup_memory()? {
let manager = global_memory_manager()?;
manager.cleanup()
} else {
Ok(0)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::services::memory_manager::init_global_memory_manager;
fn setup_memory_manager() -> Result<()> {
init_global_memory_manager()
}
#[test]
fn test_memory_vec() -> Result<()> {
setup_memory_manager()?;
let mut vec = MemoryVec::new(PoolType::AstParsing)?;
vec.push("test1".to_string())?;
vec.push("test2".to_string())?;
assert_eq!(vec.len(), 2);
assert!(vec.memory_usage() > 0);
Ok(())
}
#[test]
fn test_memory_string() -> Result<()> {
setup_memory_manager()?;
let str1 = MemoryString::new("test")?;
let str2 = MemoryString::new("test")?;
assert_eq!(str1.as_str(), "test");
assert!(str1.shares_memory_with(&str2));
Ok(())
}
#[test]
fn test_ast_buffer_pool() -> Result<()> {
setup_memory_manager()?;
let pool = AstBufferPool::new(PoolType::AstParsing)?;
let buffer = pool.get_buffer(1024)?;
assert!(buffer.capacity() >= 1024);
Ok(())
}
#[test]
fn test_interned_string_set() -> Result<()> {
setup_memory_manager()?;
let mut set = InternedStringSet::new()?;
assert!(set.insert("test1")?);
assert!(!set.insert("test1")?); assert!(set.insert("test2")?);
assert_eq!(set.strings.len(), 2);
Ok(())
}
#[test]
fn test_memory_aware_cache() -> Result<()> {
setup_memory_manager()?;
let mut cache = MemoryAwareCache::new(PoolType::AnalysisCache, 100)?;
cache.insert("key1", "value1")?;
cache.insert("key2", "value2")?;
assert_eq!(cache.get(&"key1"), Some(&"value1"));
let stats = cache.stats();
assert_eq!(stats.item_count, 2);
assert_eq!(stats.max_items, 100);
Ok(())
}
#[test]
fn test_utils() -> Result<()> {
setup_memory_manager()?;
let _id_vec = utils::create_identifier_vec()?;
let _content_vec = utils::create_content_vec()?;
let _ast_vec: MemoryVec<i32> = utils::create_ast_vec()?;
let test_data = vec![1, 2, 3, 4, 5];
let memory_usage = utils::estimate_collection_memory(&test_data);
assert_eq!(memory_usage, 5 * std::mem::size_of::<i32>());
Ok(())
}
}
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}