#![allow(non_snake_case)]
use dashmap::DashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use crate::cache::{RiCache, RiCachedValue, RiCacheStats};
use crate::core::RiResult;
struct AtomicCacheStats {
hits: AtomicU64,
misses: AtomicU64,
entries: AtomicUsize,
memory_usage_bytes: AtomicUsize,
eviction_count: AtomicU64,
}
impl AtomicCacheStats {
fn new() -> Self {
Self {
hits: AtomicU64::new(0),
misses: AtomicU64::new(0),
entries: AtomicUsize::new(0),
memory_usage_bytes: AtomicUsize::new(0),
eviction_count: AtomicU64::new(0),
}
}
fn increment_hits(&self) {
self.hits.fetch_add(1, Ordering::Relaxed);
}
fn increment_misses(&self) {
self.misses.fetch_add(1, Ordering::Relaxed);
}
#[allow(dead_code)]
fn increment_evictions(&self) {
self.eviction_count.fetch_add(1, Ordering::Relaxed);
}
fn to_cache_stats(&self) -> RiCacheStats {
let hits = self.hits.load(Ordering::Relaxed);
let misses = self.misses.load(Ordering::Relaxed);
let total = hits + misses;
let avg_hit_rate = if total > 0 {
hits as f64 / total as f64
} else {
0.0
};
RiCacheStats {
hits,
misses,
entries: self.entries.load(Ordering::Relaxed),
memory_usage_bytes: self.memory_usage_bytes.load(Ordering::Relaxed),
avg_hit_rate,
hit_count: hits,
miss_count: misses,
eviction_count: self.eviction_count.load(Ordering::Relaxed),
}
}
}
pub struct RiMemoryCache {
store: Arc<DashMap<String, RiCachedValue>>,
stats: Arc<AtomicCacheStats>,
}
const MAX_KEY_LENGTH: usize = 1024;
const MAX_VALUE_LENGTH: usize = 10 * 1024 * 1024;
const MAX_ENTRIES: usize = 100_000;
impl Default for RiMemoryCache {
fn default() -> Self {
Self::new()
}
}
impl RiMemoryCache {
pub fn new() -> Self {
RiMemoryCache {
store: Arc::new(DashMap::new()),
stats: Arc::new(AtomicCacheStats::new()),
}
}
fn validate_key(key: &str) -> crate::core::RiResult<()> {
if key.is_empty() {
return Err(crate::core::RiError::Other(
"Cache key cannot be empty".to_string()
));
}
if key.len() > MAX_KEY_LENGTH {
return Err(crate::core::RiError::Other(format!(
"Cache key too long: {} bytes (max {} bytes)",
key.len(), MAX_KEY_LENGTH
)));
}
for c in key.chars() {
if c.is_control() || c == '\0' {
return Err(crate::core::RiError::Other(
"Cache key contains invalid characters (control characters or null bytes)".to_string()
));
}
}
Ok(())
}
fn validate_value(value: &str) -> crate::core::RiResult<()> {
if value.len() > MAX_VALUE_LENGTH {
return Err(crate::core::RiError::Other(format!(
"Cache value too large: {} bytes (max {} bytes)",
value.len(), MAX_VALUE_LENGTH
)));
}
Ok(())
}
fn check_entry_limit(&self) -> crate::core::RiResult<()> {
let current_entries = self.store.len();
if current_entries >= MAX_ENTRIES {
return Err(crate::core::RiError::Other(format!(
"Cache entry limit reached: {} entries (max {} entries)",
current_entries, MAX_ENTRIES
)));
}
Ok(())
}
}
#[async_trait::async_trait]
impl RiCache for RiMemoryCache {
async fn get(&self, key: &str) -> RiResult<Option<String>> {
match self.store.get(key) {
Some(entry) => {
let value = entry.clone();
if value.is_expired() {
drop(entry);
self.store.remove(key);
self.stats.increment_misses();
Ok(None)
} else {
self.stats.increment_hits();
Ok(Some(value.value))
}
}
None => {
self.stats.increment_misses();
Ok(None)
}
}
}
async fn set(&self, key: &str, value: &str, ttl_seconds: Option<u64>) -> crate::core::RiResult<()> {
Self::validate_key(key)?;
Self::validate_value(value)?;
self.check_entry_limit()?;
let cached_value = RiCachedValue::new(value.to_string(), ttl_seconds);
self.store.insert(key.to_string(), cached_value);
Ok(())
}
async fn delete(&self, key: &str) -> crate::core::RiResult<bool> {
Ok(self.store.remove(key).is_some())
}
async fn exists(&self, key: &str) -> bool {
if let Some(entry) = self.store.get(key) {
if entry.is_expired() {
drop(entry);
self.store.remove(key);
false
} else {
true
}
} else {
false
}
}
async fn clear(&self) -> crate::core::RiResult<()> {
self.store.clear();
Ok(())
}
async fn stats(&self) -> RiCacheStats {
let mut stats = self.stats.to_cache_stats();
stats.entries = self.store.len();
stats
}
async fn cleanup_expired(&self) -> crate::core::RiResult<usize> {
let mut cleaned = 0;
let keys: Vec<String> = self.store.iter().map(|entry| entry.key().clone()).collect();
for key in keys {
if let Some(entry) = self.store.get(&key) {
if entry.is_expired() {
drop(entry);
self.store.remove(&key);
cleaned += 1;
}
}
}
Ok(cleaned)
}
async fn keys(&self) -> crate::core::RiResult<Vec<String>> {
let keys: Vec<String> = self.store.iter().map(|entry| entry.key().clone()).collect();
Ok(keys)
}
}