use std::collections::HashMap;
use super::types::{DeltaMetric, MetricData, RetentionTier};
#[derive(Debug, Clone)]
pub struct ProfileSnapshot {
pub index: usize,
pub timestamp_ns: u64,
pub workload_fingerprint: String,
pub metrics: HashMap<String, MetricData>,
pub metadata: HashMap<String, String>,
pub checksum: u32,
}
impl ProfileSnapshot {
pub fn new(index: usize) -> Self {
Self {
index,
timestamp_ns: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0),
workload_fingerprint: String::new(),
metrics: HashMap::new(),
metadata: HashMap::new(),
checksum: 0,
}
}
pub fn add_metric(&mut self, metric: MetricData) {
self.metrics.insert(metric.name.clone(), metric);
}
pub fn get_metric(&self, name: &str) -> Option<&MetricData> {
self.metrics.get(name)
}
pub fn set_fingerprint(&mut self, fingerprint: impl Into<String>) {
self.workload_fingerprint = fingerprint.into();
}
pub fn size_bytes(&self) -> usize {
let metrics_size: usize = self.metrics.values().map(|m| m.size_bytes()).sum();
let metadata_size: usize = self.metadata.iter().map(|(k, v)| k.len() + v.len()).sum();
24 + self.workload_fingerprint.len() + metrics_size + metadata_size
}
pub fn compute_checksum(&mut self) {
let mut hash: u32 = 0;
hash = hash.wrapping_add(self.index as u32);
hash = hash.wrapping_mul(31);
hash = hash.wrapping_add((self.timestamp_ns & 0xFFFFFFFF) as u32);
let mut keys: Vec<_> = self.metrics.keys().collect();
keys.sort();
for name in keys {
if let Some(metric) = self.metrics.get(name) {
for c in name.bytes() {
hash = hash.wrapping_mul(31).wrapping_add(c as u32);
}
for &val in &metric.values {
let bits = val.to_bits();
hash = hash.wrapping_mul(31).wrapping_add((bits >> 32) as u32);
hash = hash
.wrapping_mul(31)
.wrapping_add((bits & 0xFFFFFFFF) as u32);
}
}
}
self.checksum = hash;
}
pub fn verify_checksum(&self) -> bool {
let mut copy = self.clone();
copy.compute_checksum();
copy.checksum == self.checksum
}
}
#[derive(Debug, Clone)]
pub struct DeltaSnapshot {
pub index: usize,
pub base_index: usize,
pub timestamp_ns: u64,
pub workload_fingerprint: String,
pub deltas: HashMap<String, DeltaMetric>,
pub new_metrics: HashMap<String, MetricData>,
pub removed_metrics: Vec<String>,
pub checksum: u32,
}
impl DeltaSnapshot {
pub fn from_diff(base: &ProfileSnapshot, current: &ProfileSnapshot) -> Self {
let mut deltas = HashMap::new();
let mut new_metrics = HashMap::new();
let mut removed_metrics = Vec::new();
for (name, metric) in ¤t.metrics {
if let Some(base_metric) = base.metrics.get(name) {
let delta = metric.delta_from(base_metric);
if delta.size_bytes() < metric.size_bytes() {
deltas.insert(name.clone(), delta);
} else {
new_metrics.insert(name.clone(), metric.clone());
}
} else {
new_metrics.insert(name.clone(), metric.clone());
}
}
for name in base.metrics.keys() {
if !current.metrics.contains_key(name) {
removed_metrics.push(name.clone());
}
}
Self {
index: current.index,
base_index: base.index,
timestamp_ns: current.timestamp_ns,
workload_fingerprint: current.workload_fingerprint.clone(),
deltas,
new_metrics,
removed_metrics,
checksum: 0,
}
}
pub fn size_bytes(&self) -> usize {
let delta_size: usize = self.deltas.values().map(|d| d.size_bytes()).sum();
let new_size: usize = self.new_metrics.values().map(|m| m.size_bytes()).sum();
let removed_size: usize = self.removed_metrics.iter().map(|s| s.len()).sum();
24 + self.workload_fingerprint.len() + delta_size + new_size + removed_size
}
pub fn apply_to(&self, base: &ProfileSnapshot) -> ProfileSnapshot {
let mut result = ProfileSnapshot::new(self.index);
result.timestamp_ns = self.timestamp_ns;
result.workload_fingerprint = self.workload_fingerprint.clone();
for (name, metric) in &base.metrics {
if self.removed_metrics.contains(name) {
continue;
}
if let Some(delta) = self.deltas.get(name) {
let reconstructed = metric.apply_delta(delta);
result.metrics.insert(name.clone(), reconstructed);
} else {
result.metrics.insert(name.clone(), metric.clone());
}
}
for (name, metric) in &self.new_metrics {
result.metrics.insert(name.clone(), metric.clone());
}
result.compute_checksum();
result
}
}
#[derive(Debug, Clone)]
pub struct SnapshotIndex {
pub index: usize,
pub timestamp_ns: u64,
pub fingerprint: String,
pub tier: RetentionTier,
pub offset: usize,
pub size_bytes: usize,
pub is_delta: bool,
pub base_index: Option<usize>,
}