use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::Instant;
#[derive(Debug, Default, Clone)]
pub struct PerformanceMetrics {
pub file_processing_time: u128,
pub highlighting_time: u128,
pub summarization_time: u128,
pub tokenization_time: u128,
pub total_bytes_processed: usize,
pub total_lines_processed: usize,
}
impl PerformanceMetrics {
pub fn new() -> Self {
Self::default()
}
pub fn add_processing_time(&mut self, duration_nanos: u128) {
self.file_processing_time += duration_nanos;
}
pub fn add_highlighting_time(&mut self, duration_nanos: u128) {
self.highlighting_time += duration_nanos;
}
pub fn add_summarization_time(&mut self, duration_nanos: u128) {
self.summarization_time += duration_nanos;
}
pub fn add_tokenization_time(&mut self, duration_nanos: u128) {
self.tokenization_time += duration_nanos;
}
pub fn record_bytes_processed(&mut self, bytes: usize) {
self.total_bytes_processed += bytes;
}
pub fn record_lines_processed(&mut self, lines: usize) {
self.total_lines_processed += lines;
}
pub fn total_time(&self) -> u128 {
self.file_processing_time
+ self.highlighting_time
+ self.summarization_time
+ self.tokenization_time
}
pub fn bytes_per_second(&self) -> f64 {
if self.total_time() == 0 {
0.0
} else {
(self.total_bytes_processed as f64 * 1_000_000_000.0) / self.total_time() as f64
}
}
pub fn lines_per_second(&self) -> f64 {
if self.total_time() == 0 {
0.0
} else {
(self.total_lines_processed as f64 * 1_000_000_000.0) / self.total_time() as f64
}
}
}
pub struct LanguageCache {
cache: RwLock<HashMap<String, String>>,
max_size: usize,
}
impl LanguageCache {
pub fn new(max_size: usize) -> Self {
Self {
cache: RwLock::new(HashMap::new()),
max_size,
}
}
pub fn get(&self, extension: &str) -> Option<String> {
self.cache.read().ok()?.get(extension).cloned()
}
pub fn insert(&self, extension: String, language: String) {
if let Ok(mut cache) = self.cache.write() {
if cache.len() >= self.max_size {
if let Some(first_key) = cache.keys().next().cloned() {
cache.remove(&first_key);
}
}
cache.insert(extension, language);
}
}
pub fn clear(&self) {
if let Ok(mut cache) = self.cache.write() {
cache.clear();
}
}
pub fn size(&self) -> usize {
self.cache.read().map(|c| c.len()).unwrap_or(0)
}
}
static LANGUAGE_CACHE: std::sync::OnceLock<LanguageCache> = std::sync::OnceLock::new();
pub fn language_cache() -> &'static LanguageCache {
LANGUAGE_CACHE.get_or_init(|| LanguageCache::new(1000))
}
pub struct Timer {
start: Instant,
label: String,
}
impl Timer {
pub fn new(label: impl Into<String>) -> Self {
Self {
start: Instant::now(),
label: label.into(),
}
}
pub fn elapsed_nanos(&self) -> u128 {
self.start.elapsed().as_nanos()
}
pub fn elapsed_micros(&self) -> u128 {
self.start.elapsed().as_micros()
}
pub fn elapsed_millis(&self) -> u128 {
self.start.elapsed().as_millis()
}
pub fn restart(&mut self) {
self.start = Instant::now();
}
}
impl Drop for Timer {
fn drop(&mut self) {
if cfg!(debug_assertions) {
eprintln!("Timer '{}': {}μs", self.label, self.elapsed_micros());
}
}
}
#[macro_export]
macro_rules! time_operation {
($label:expr, $operation:expr) => {{
let _timer = $crate::performance::Timer::new($label);
let result = $operation;
result
}};
}
pub struct StringPool {
pool: RwLock<HashMap<String, Arc<String>>>,
}
impl StringPool {
pub fn new() -> Self {
Self {
pool: RwLock::new(HashMap::new()),
}
}
pub fn get_or_insert(&self, s: &str) -> Arc<String> {
if let Ok(pool) = self.pool.read() {
if let Some(existing) = pool.get(s) {
return Arc::clone(existing);
}
}
if let Ok(mut pool) = self.pool.write() {
if let Some(existing) = pool.get(s) {
return Arc::clone(existing);
}
let arc_string = Arc::new(s.to_string());
pool.insert(s.to_string(), Arc::clone(&arc_string));
arc_string
} else {
Arc::new(s.to_string())
}
}
pub fn clear(&self) {
if let Ok(mut pool) = self.pool.write() {
pool.clear();
}
}
pub fn size(&self) -> usize {
self.pool.read().map(|p| p.len()).unwrap_or(0)
}
}
impl Default for StringPool {
fn default() -> Self {
Self::new()
}
}
static STRING_POOL: std::sync::OnceLock<StringPool> = std::sync::OnceLock::new();
pub fn string_pool() -> &'static StringPool {
STRING_POOL.get_or_init(StringPool::new)
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
use std::time::Duration;
#[test]
fn test_performance_metrics() {
let mut metrics = PerformanceMetrics::new();
metrics.add_processing_time(1_000_000); metrics.record_bytes_processed(1024);
metrics.record_lines_processed(50);
assert_eq!(metrics.total_time(), 1_000_000);
assert!(metrics.bytes_per_second() > 0.0);
assert!(metrics.lines_per_second() > 0.0);
}
#[test]
fn test_language_cache() {
let cache = LanguageCache::new(2);
cache.insert("rs".to_string(), "Rust".to_string());
assert_eq!(cache.get("rs"), Some("Rust".to_string()));
assert_eq!(cache.size(), 1);
cache.insert("js".to_string(), "JavaScript".to_string());
assert_eq!(cache.size(), 2);
cache.insert("py".to_string(), "Python".to_string());
assert_eq!(cache.size(), 2);
}
#[test]
fn test_timer() {
let mut timer = Timer::new("test");
thread::sleep(Duration::from_millis(1));
assert!(timer.elapsed_micros() >= 1000);
timer.restart();
let elapsed_after_restart = timer.elapsed_micros();
assert!(elapsed_after_restart < 1000); }
#[test]
fn test_string_pool() {
let pool = StringPool::new();
let s1 = pool.get_or_insert("hello");
let s2 = pool.get_or_insert("hello");
assert!(Arc::ptr_eq(&s1, &s2));
assert_eq!(pool.size(), 1);
let s3 = pool.get_or_insert("world");
assert_eq!(pool.size(), 2);
assert!(!Arc::ptr_eq(&s1, &s3));
}
#[test]
fn test_global_caches() {
let cache = language_cache();
cache.clear();
assert_eq!(cache.size(), 0);
let pool = string_pool();
pool.clear();
assert_eq!(pool.size(), 0);
}
}