Skip to main content

haagenti_core/
stats.rs

1//! Statistics and metrics for compression operations.
2
3use crate::types::{Algorithm, CompressionRatio};
4
5/// Statistics from a compression/decompression operation.
6#[derive(Debug, Clone, Default)]
7pub struct CompressionStats {
8    /// Algorithm used.
9    pub algorithm: Option<Algorithm>,
10
11    /// Original (uncompressed) size in bytes.
12    pub original_size: usize,
13
14    /// Compressed size in bytes.
15    pub compressed_size: usize,
16
17    /// Time taken in microseconds.
18    pub time_us: u64,
19
20    /// Number of blocks processed.
21    pub blocks_processed: usize,
22
23    /// Peak memory usage in bytes.
24    pub peak_memory: Option<usize>,
25
26    /// Whether dictionary was used.
27    pub dictionary_used: bool,
28
29    /// Whether SIMD was used.
30    pub simd_used: bool,
31
32    /// Checksum (if computed).
33    pub checksum: Option<u64>,
34}
35
36impl CompressionStats {
37    /// Create new empty stats.
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    /// Create stats from a completed operation.
43    pub fn from_operation(
44        algorithm: Algorithm,
45        original_size: usize,
46        compressed_size: usize,
47        time_us: u64,
48    ) -> Self {
49        CompressionStats {
50            algorithm: Some(algorithm),
51            original_size,
52            compressed_size,
53            time_us,
54            blocks_processed: 1,
55            ..Default::default()
56        }
57    }
58
59    /// Get compression ratio.
60    pub fn ratio(&self) -> CompressionRatio {
61        CompressionRatio::new(self.original_size, self.compressed_size)
62    }
63
64    /// Get throughput in bytes per second.
65    pub fn throughput_bps(&self) -> f64 {
66        if self.time_us == 0 {
67            return 0.0;
68        }
69        self.original_size as f64 * 1_000_000.0 / self.time_us as f64
70    }
71
72    /// Get throughput in MB/s.
73    pub fn throughput_mbs(&self) -> f64 {
74        self.throughput_bps() / 1_000_000.0
75    }
76
77    /// Get space savings as percentage.
78    pub fn savings_percent(&self) -> f64 {
79        self.ratio().savings_percent()
80    }
81
82    /// Merge stats from multiple operations.
83    pub fn merge(&mut self, other: &CompressionStats) {
84        self.original_size += other.original_size;
85        self.compressed_size += other.compressed_size;
86        self.time_us += other.time_us;
87        self.blocks_processed += other.blocks_processed;
88
89        // Peak memory is max of both
90        match (&self.peak_memory, &other.peak_memory) {
91            (Some(a), Some(b)) => self.peak_memory = Some((*a).max(*b)),
92            (None, Some(b)) => self.peak_memory = Some(*b),
93            _ => {}
94        }
95    }
96}
97
98/// Metrics collector for aggregate statistics.
99#[derive(Debug, Clone, Default)]
100pub struct Metrics {
101    /// Total operations performed.
102    pub total_operations: u64,
103
104    /// Total bytes compressed.
105    pub total_bytes_in: u64,
106
107    /// Total bytes produced.
108    pub total_bytes_out: u64,
109
110    /// Total time spent in microseconds.
111    pub total_time_us: u64,
112
113    /// Number of errors encountered.
114    pub error_count: u64,
115}
116
117impl Metrics {
118    /// Create new metrics collector.
119    pub fn new() -> Self {
120        Self::default()
121    }
122
123    /// Record a completed operation.
124    pub fn record(&mut self, stats: &CompressionStats) {
125        self.total_operations += 1;
126        self.total_bytes_in += stats.original_size as u64;
127        self.total_bytes_out += stats.compressed_size as u64;
128        self.total_time_us += stats.time_us;
129    }
130
131    /// Record an error.
132    pub fn record_error(&mut self) {
133        self.error_count += 1;
134    }
135
136    /// Get average compression ratio.
137    pub fn average_ratio(&self) -> f64 {
138        if self.total_bytes_out == 0 {
139            return 1.0;
140        }
141        self.total_bytes_in as f64 / self.total_bytes_out as f64
142    }
143
144    /// Get average throughput in MB/s.
145    pub fn average_throughput_mbs(&self) -> f64 {
146        if self.total_time_us == 0 {
147            return 0.0;
148        }
149        self.total_bytes_in as f64 / self.total_time_us as f64
150    }
151
152    /// Get error rate (0.0 to 1.0).
153    pub fn error_rate(&self) -> f64 {
154        if self.total_operations == 0 {
155            return 0.0;
156        }
157        self.error_count as f64 / self.total_operations as f64
158    }
159
160    /// Reset all metrics.
161    pub fn reset(&mut self) {
162        *self = Self::default();
163    }
164
165    /// Get metrics summary as string.
166    pub fn summary(&self) -> String {
167        format!(
168            "Operations: {}, Bytes: {} -> {} (ratio: {:.2}x), Throughput: {:.1} MB/s, Errors: {}",
169            self.total_operations,
170            self.total_bytes_in,
171            self.total_bytes_out,
172            self.average_ratio(),
173            self.average_throughput_mbs(),
174            self.error_count,
175        )
176    }
177}