use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use hashbrown::HashMap;
use crate::bytes::Bytes;
use crate::sync::{Arc, Lazy, Mutex};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Origin {
Local,
Imported,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Insertion {
Stored,
Conflict(Bytes),
Failed(String),
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct InsertSummary {
pub stored: usize,
pub conflict: usize,
pub failed: usize,
}
impl InsertSummary {
pub fn record(&mut self, insertion: &Insertion) {
match insertion {
Insertion::Stored => self.stored += 1,
Insertion::Conflict(_) => self.conflict += 1,
Insertion::Failed(_) => self.failed += 1,
}
}
}
pub trait Storage: Send + core::fmt::Debug {
fn get(&self, key: &[u8]) -> Option<Bytes>;
fn insert(&self, key: &[u8], value: Bytes, origin: Origin) -> Insertion;
fn replace(&self, key: &[u8], value: Bytes, origin: Origin) -> Insertion;
fn insert_many(
&self,
entries: &mut dyn Iterator<Item = (Bytes, Bytes)>,
origin: Origin,
) -> InsertSummary {
let mut summary = InsertSummary::default();
for (key, value) in entries {
summary.record(&self.insert(&key, value, origin));
}
summary
}
fn scan(&self, visit: &mut dyn FnMut(&[u8], &[u8]));
fn purge(&self);
fn purge_key(&self, key: &[u8]);
fn loading(&self) -> bool {
false
}
fn describe(&self) -> String;
}
static MEMORY: Lazy<Mutex<HashMap<String, Arc<Mutex<Entries>>>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
pub(crate) type Entries = HashMap<Vec<u8>, (Bytes, Origin)>;
pub(crate) mod entries {
use super::{Bytes, Entries, Insertion, Origin, replaces};
pub(crate) fn get(entries: &Entries, key: &[u8]) -> Option<Bytes> {
entries.get(key).map(|(value, _)| value.clone())
}
pub(crate) fn insert(
entries: &mut Entries,
key: &[u8],
value: Bytes,
origin: Origin,
) -> Insertion {
if let Some((existing, existing_origin)) = entries.get(key)
&& !replaces(origin, *existing_origin)
{
return Insertion::Conflict(existing.clone());
}
entries.insert(key.to_vec(), (value, origin));
Insertion::Stored
}
pub(crate) fn replace(
entries: &mut Entries,
key: &[u8],
value: Bytes,
origin: Origin,
) -> Insertion {
entries.insert(key.to_vec(), (value, origin));
Insertion::Stored
}
pub(crate) fn scan(entries: &Entries, visit: &mut dyn FnMut(&[u8], &[u8])) {
for (key, (value, _)) in entries.iter() {
visit(key, value);
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryStorage {
namespace: String,
entries: Arc<Mutex<Entries>>,
}
impl MemoryStorage {
pub fn new(namespace: &str) -> Self {
Self::with_key(namespace.to_string(), namespace)
}
pub(crate) fn in_environment(namespace: &str) -> Self {
let key = alloc::format!("{}\u{1f}{namespace}", crate::environment::scope());
Self::with_key(key, namespace)
}
fn with_key(key: String, namespace: &str) -> Self {
let mut memory = MEMORY.lock();
let entries = match memory.get(&key) {
Some(entries) => entries.clone(),
None => {
let entries = Arc::new(Mutex::new(HashMap::new()));
memory.insert(key, entries.clone());
entries
}
};
Self {
namespace: namespace.to_string(),
entries,
}
}
pub fn namespaces() -> Vec<NamespaceSummary> {
let prefix = alloc::format!("{}\u{1f}", crate::environment::scope());
let memory = MEMORY.lock();
memory
.iter()
.filter_map(|(key, entries)| {
let namespace = key.strip_prefix(&prefix)?;
let entries = entries.lock();
Some(NamespaceSummary {
namespace: namespace.to_string(),
entries: entries.len() as u64,
bytes: entries
.iter()
.map(|(key, (value, _))| (key.len() + value.len()) as u64)
.sum(),
})
})
.collect()
}
}
impl Storage for MemoryStorage {
fn get(&self, key: &[u8]) -> Option<Bytes> {
entries::get(&self.entries.lock(), key)
}
fn insert(&self, key: &[u8], value: Bytes, origin: Origin) -> Insertion {
entries::insert(&mut self.entries.lock(), key, value, origin)
}
fn replace(&self, key: &[u8], value: Bytes, origin: Origin) -> Insertion {
entries::replace(&mut self.entries.lock(), key, value, origin)
}
fn scan(&self, visit: &mut dyn FnMut(&[u8], &[u8])) {
entries::scan(&self.entries.lock(), visit)
}
fn purge(&self) {
self.entries.lock().clear();
}
fn purge_key(&self, key: &[u8]) {
self.entries.lock().remove(key);
}
fn describe(&self) -> String {
alloc::format!("memory ({})", self.namespace)
}
}
pub(crate) fn replaces(incoming: Origin, existing: Origin) -> bool {
matches!((incoming, existing), (Origin::Local, Origin::Imported))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NamespaceSummary {
pub namespace: String,
pub entries: u64,
pub bytes: u64,
}
pub fn open(namespace: &str) -> Box<dyn Storage> {
cfg_if::cfg_if! {
if #[cfg(native_cache)] {
super::open_database_storage(namespace)
} else if #[cfg(browser_cache)] {
super::browser::open_storage(namespace)
} else {
Box::new(MemoryStorage::in_environment(namespace))
}
}
}