amberjs 1.17.0

JavaScript/TypeScript runtime built with Rust and V8
Documentation


use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use std::time::SystemTime;
use std::hash::{Hash, Hasher, DefaultHasher};

/// 字节码缓存条目
#[derive(Debug, Clone)]
pub struct CacheEntry {
    /// 编译后的脚本
    pub script: Vec<u8>,
    /// 创建时间
    pub created_at: Instant,
    /// 最后访问时间
    pub last_accessed: Instant,
    /// 访问次数
    pub access_count: usize,
    /// 文件修改时间(如果是文件)
    #[allow(dead_code)]
    pub file_modified: Option<std::time::SystemTime>,
}
/// 字节码缓存管理器
/// 用于缓存V8脚本编译结果,避免重复编译相同代码
pub struct BytecodeCache {
    /// 缓存存储
    entries: Arc<Mutex<HashMap<String, CacheEntry>>>,
    /// 缓存配置
    config: CacheConfig,
    /// 统计信息
    stats: Arc<Mutex<CacheStats>>,
}
/// 缓存配置
#[derive(Debug, Clone)]
pub struct CacheConfig {
    /// 最大缓存条目数
    #[allow(dead_code)]
    pub max_entries: usize,
    /// 缓存条目最大生存时间(秒)
    #[allow(dead_code)]
    pub max_age: Duration,
    /// 最小访问次数(低于此次数的条目可能被清除)
    #[allow(dead_code)]
    pub min_access_count: usize,
    /// 缓存清理间隔(秒)
    #[allow(dead_code)]
    pub cleanup_interval: Duration,
}
impl Default for CacheConfig {
    fn default() -> Self {
        Self {
            max_entries: 1000,
            max_age: Duration::from_secs(3600), // 1小时
            min_access_count: 3,
            cleanup_interval: Duration::from_secs(300), // 5分钟
        }
    }
}
/// 缓存统计信息
#[derive(Debug, Clone, Default)]
pub struct CacheStats {
    #[allow(dead_code)]
    pub hits: usize,
    #[allow(dead_code)]
    pub misses: usize,
    #[allow(dead_code)]
    pub evictions: usize,
    #[allow(dead_code)]
    pub total_cached: usize,
}
#[allow(dead_code)]
impl BytecodeCache {
    /// 创建新的字节码缓存
    pub fn new(config: CacheConfig) -> Self {
        Self {
            entries: Arc::new(Mutex::new(HashMap::new())),
            config,
            stats: Arc::new(Mutex::new(CacheStats::default())),
        }
    }
    /// 计算代码的哈希值
    fn calculate_code_hash(code: &str) -> u64 {
        let mut hasher = DefaultHasher::new();
        code.hash(&mut hasher);
        hasher.finish()
    }
    /// 获取缓存的键
    fn get_cache_key(code: &str, source: Option<&str>) -> String {
        // 如果提供了源文件路径,使用路径作为键
        if let Some(source_path) = source {
            format!("file:{}", source_path)
        } else {
            // 否则使用代码哈希
            format!("hash:{:016x}", Self::calculate_code_hash(code))
        }
    }
    /// 获取缓存的脚本
    pub fn get(&self, code: &str, source: Option<&str>) -> Option<Vec<u8>> {
        let key: _ = Self::get_cache_key(code, source);
        let mut entries = self.entries.lock().unwrap();
        let mut stats = self.stats.lock().unwrap();
        if let Some(entry) = entries.get_mut(&key) {
            // 更新访问信息
            entry.last_accessed = Instant::now();
            entry.access_count += 1;
            stats.hits += 1;
            println!(
                "[CACHE HIT] Key: {}, Access count: {}",
                key, entry.access_count
            );
            Some(entry.script.clone())
        } else {
            stats.misses += 1;
            println!("[CACHE MISS] Key: {}", key);
            None
        }
    }
    /// 存储脚本到缓存
    pub fn put(&self, code: &str, source: Option<&str>, script: Vec<u8>) {
        let key: _ = Self::get_cache_key(code, source);
        let now: _ = Instant::now();
        let mut entries = self.entries.lock().unwrap();
        let mut stats = self.stats.lock().unwrap();
        // 插入新条目
        entries.insert(
            key.clone(),
            CacheEntry {
                script,
                created_at: now,
                last_accessed: now,
                access_count: 1,
                file_modified: None,
            },
        );
        stats.total_cached += 1;
        println!(
            "[CACHE STORE] Key: {}, Total cached: {}",
            key, stats.total_cached
        );
        // 检查是否需要清理
        if entries.len() > self.config.max_entries {
            self.evict_old_entries(&mut entries, &mut stats);
        }
    }
    /// 清除过期条目
    fn evict_old_entries(&self, entries: &mut HashMap<String, CacheEntry>, stats: &mut CacheStats) {
        let now: _ = Instant::now();
        let max_age: _ = self.config.max_age;
        let min_access: _ = self.config.min_access_count;
        // 收集要删除的键
        let keys_to_remove: Vec<String> = entries
            .iter()
            .filter_map(|(key, entry)| {
                let is_old: _ = now.duration_since(entry.created_at) > max_age;
                let is_rarely_used: _ = entry.access_count < min_access;
                if is_old || is_rarely_used {
                    Some(key.clone())
                } else {
                    None
                }
            })
            .collect();
        // 删除过期条目
        for key in keys_to_remove {
            entries.remove(&key);
            stats.evictions += 1;
            stats.total_cached = stats.total_cached.saturating_sub(1);
        }
        if stats.evictions > 0 {
            println!(
                "[CACHE CLEANUP] Evicted {} entries, {} remaining",
                stats.evictions,
                entries.len()
            );
        }
    }
    /// 获取缓存统计信息
    pub fn get_stats(&self) -> CacheStats {
        self.stats.lock().unwrap().clone()
    }
    /// 清理过期缓存
    pub fn cleanup(&self) {
        let mut entries = self.entries.lock().unwrap();
        let mut stats = self.stats.lock().unwrap();
        self.evict_old_entries(&mut entries, &mut stats);
    }
    /// 清空所有缓存
    pub fn clear(&self) {
        let mut entries = self.entries.lock().unwrap();
        let mut stats = self.stats.lock().unwrap();
        let count: _ = entries.len();
        entries.clear();
        stats.total_cached = 0;
        println!("[CACHE CLEAR] Cleared {} entries", count);
    }
    /// 获取缓存使用率
    pub fn usage_ratio(&self) -> f64 {
        let entries: _ = self.entries.lock().unwrap();
        entries.len() as f64 / self.config.max_entries as f64
    }
}
#[cfg(test)]
mod tests {
    #[test]
    fn test_cache_basic_operations() {
        let cache: _ = BytecodeCache::new(CacheConfig::default());
        // 测试存储和获取
        let script_data: _ = vec![1, 2, 3, 4, 5];
        cache.put("test code", None, script_data.clone());
        let retrieved: _ = cache.get("test code", None);
        assert_eq!(retrieved, Some(script_data));
        // 测试缓存统计
        let stats: _ = cache.get_stats();
        assert_eq!(stats.hits, 1);
        assert_eq!(stats.misses, 0);
    }
    #[test]
    fn test_cache_miss() {
        let cache: _ = BytecodeCache::new(CacheConfig::default());
        // 测试缓存未命中
        let retrieved: _ = cache.get("nonexistent code", None);
        assert_eq!(retrieved, None);
        let stats: _ = cache.get_stats();
        assert_eq!(stats.hits, 0);
        assert_eq!(stats.misses, 1);
    }
    #[test]
    fn test_cache_key_generation() {
        let _cache: _ = BytecodeCache::new(CacheConfig::default());
        // 测试相同代码生成相同哈希
        let key1: _ = BytecodeCache::get_cache_key("const x = 1;", None);
        let key2: _ = BytecodeCache::get_cache_key("const x = 1;", None);
        assert_eq!(key1, key2);
        // 测试不同代码生成不同哈希
        let key3: _ = BytecodeCache::get_cache_key("const x = 2;", None);
        assert_ne!(key1, key3);
        // 测试文件路径作为键
        let file_key: _ = BytecodeCache::get_cache_key("const x = 1;", Some("/path/to/file.js"));
        assert!(file_key.starts_with("file:"));
    }
}