use std::fmt;
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlockId(pub u32);
impl fmt::Display for BlockId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "B{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SeqId(pub u64);
impl fmt::Display for SeqId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "S{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum EvictionStrategy {
LRU,
LFU,
LongestFirst,
Priority { levels: usize },
StreamingLLM {
sink_tokens: usize,
window_tokens: usize,
},
}
impl Default for EvictionStrategy {
fn default() -> Self {
EvictionStrategy::LRU
}
}
impl fmt::Display for EvictionStrategy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EvictionStrategy::LRU => write!(f, "LRU"),
EvictionStrategy::LFU => write!(f, "LFU"),
EvictionStrategy::LongestFirst => write!(f, "LongestFirst"),
EvictionStrategy::Priority { levels } => write!(f, "Priority({})", levels),
EvictionStrategy::StreamingLLM {
sink_tokens,
window_tokens,
} => {
write!(
f,
"StreamingLLM(sink={}, window={})",
sink_tokens, window_tokens
)
}
}
}
}
#[derive(Debug)]
pub struct KvBlock {
pub id: BlockId,
pub num_tokens: usize,
pub ref_count: AtomicU32,
pub capacity: usize,
}
impl KvBlock {
pub fn new(id: BlockId, capacity: usize) -> Self {
Self {
id,
num_tokens: 0,
ref_count: AtomicU32::new(1),
capacity,
}
}
pub fn is_full(&self) -> bool {
self.num_tokens >= self.capacity
}
pub fn remaining(&self) -> usize {
self.capacity.saturating_sub(self.num_tokens)
}
pub fn refs(&self) -> u32 {
self.ref_count.load(Ordering::Acquire)
}
pub fn inc_ref(&self) {
self.ref_count.fetch_add(1, Ordering::AcqRel);
}
pub fn dec_ref(&self) -> bool {
self.ref_count.fetch_sub(1, Ordering::AcqRel) == 1
}
}
#[derive(Debug, Clone)]
pub struct SequenceInfo {
pub seq_id: SeqId,
pub num_tokens: usize,
pub block_ids: Vec<BlockId>,
pub last_access: Instant,
pub access_count: u64,
pub priority: u32,
}
impl SequenceInfo {
pub fn new(seq_id: SeqId) -> Self {
Self {
seq_id,
num_tokens: 0,
block_ids: Vec::new(),
last_access: Instant::now(),
access_count: 0,
priority: 0,
}
}
pub fn touch(&mut self) {
self.last_access = Instant::now();
self.access_count += 1;
}
pub fn num_blocks(&self) -> usize {
self.block_ids.len()
}
}
#[derive(Debug, Clone)]
pub enum PagedKvError {
OutOfMemory { requested: usize, available: usize },
SequenceNotFound(SeqId),
BlockNotFound(BlockId),
InvalidOperation(String),
}
impl fmt::Display for PagedKvError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PagedKvError::OutOfMemory {
requested,
available,
} => {
write!(
f,
"Out of memory: requested {} blocks, {} available",
requested, available
)
}
PagedKvError::SequenceNotFound(seq_id) => {
write!(f, "Sequence not found: {}", seq_id)
}
PagedKvError::BlockNotFound(block_id) => {
write!(f, "Block not found: {}", block_id)
}
PagedKvError::InvalidOperation(msg) => {
write!(f, "Invalid operation: {}", msg)
}
}
}
}
impl std::error::Error for PagedKvError {}
pub type PagedKvResult<T> = Result<T, PagedKvError>;
#[derive(Debug, Clone, Default)]
pub struct CacheStats {
pub total_allocations: u64,
pub total_frees: u64,
pub total_evictions: u64,
pub total_forks: u64,
pub peak_blocks_used: usize,
}