use crate::SparseVec;
use std::fmt;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use std::time::Instant;
#[derive(Clone)]
pub struct VersionedChunk {
pub vector: Arc<SparseVec>,
pub version: u64,
pub created_at: Instant,
pub modified_at: Instant,
pub ref_count: Arc<AtomicU32>,
pub original_size: usize,
pub content_hash: [u8; 8],
}
impl VersionedChunk {
pub fn new(vector: SparseVec, original_size: usize, content_hash: [u8; 8]) -> Self {
let now = Instant::now();
Self {
vector: Arc::new(vector),
version: 0,
created_at: now,
modified_at: now,
ref_count: Arc::new(AtomicU32::new(1)),
original_size,
content_hash,
}
}
pub fn update(&self, new_vector: SparseVec, new_hash: [u8; 8]) -> Self {
Self {
vector: Arc::new(new_vector),
version: self.version + 1,
created_at: self.created_at,
modified_at: Instant::now(),
ref_count: Arc::new(AtomicU32::new(1)),
original_size: self.original_size,
content_hash: new_hash,
}
}
pub fn inc_ref(&self) {
self.ref_count.fetch_add(1, Ordering::AcqRel);
}
pub fn dec_ref(&self) -> u32 {
self.ref_count
.fetch_sub(1, Ordering::AcqRel)
.saturating_sub(1)
}
pub fn ref_count(&self) -> u32 {
self.ref_count.load(Ordering::Acquire)
}
pub fn is_unreferenced(&self) -> bool {
self.ref_count() == 0
}
pub fn age(&self) -> std::time::Duration {
Instant::now().duration_since(self.created_at)
}
pub fn time_since_modification(&self) -> std::time::Duration {
Instant::now().duration_since(self.modified_at)
}
}
impl fmt::Debug for VersionedChunk {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("VersionedChunk")
.field("version", &self.version)
.field("original_size", &self.original_size)
.field("ref_count", &self.ref_count())
.field("content_hash", &format!("{:02x?}", &self.content_hash))
.field("age_ms", &self.age().as_millis())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chunk_creation() {
let vec = SparseVec::new();
let chunk = VersionedChunk::new(vec, 4096, [1, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(chunk.version, 0);
assert_eq!(chunk.original_size, 4096);
assert_eq!(chunk.ref_count(), 1);
assert!(!chunk.is_unreferenced());
}
#[test]
fn test_chunk_update() {
let vec1 = SparseVec::new();
let chunk1 = VersionedChunk::new(vec1, 4096, [1, 2, 3, 4, 5, 6, 7, 8]);
let vec2 = SparseVec::new();
let chunk2 = chunk1.update(vec2, [9, 10, 11, 12, 13, 14, 15, 16]);
assert_eq!(chunk2.version, 1);
assert_eq!(chunk2.created_at, chunk1.created_at);
assert!(chunk2.modified_at >= chunk1.modified_at);
}
#[test]
fn test_reference_counting() {
let vec = SparseVec::new();
let chunk = VersionedChunk::new(vec, 4096, [0; 8]);
assert_eq!(chunk.ref_count(), 1);
chunk.inc_ref();
assert_eq!(chunk.ref_count(), 2);
chunk.inc_ref();
assert_eq!(chunk.ref_count(), 3);
let count = chunk.dec_ref();
assert_eq!(count, 2);
let count = chunk.dec_ref();
assert_eq!(count, 1);
let count = chunk.dec_ref();
assert_eq!(count, 0);
assert!(chunk.is_unreferenced());
}
}