use std::collections::{HashMap, VecDeque};
use std::sync::{Arc, Mutex, OnceLock};
use crate::compression::dictionary::DictionaryHash;
pub(crate) const STORE_BUDGET: usize = 32 * 1024 * 1024;
pub(crate) fn matching_pattern<'a>(patterns: &'a [String], path: &str) -> Option<&'a str> {
patterns.iter().map(String::as_str).find(|p| glob(p, path))
}
pub(crate) fn glob(pattern: &str, path: &str) -> bool {
let mut parts = pattern.split('*');
let first = parts.next().unwrap_or("");
let Some(mut rest) = path.strip_prefix(first) else { return false };
let parts: Vec<&str> = parts.collect();
let Some((last, middle)) = parts.split_last() else { return rest.is_empty() };
for part in middle {
match rest.find(part) {
Some(at) => rest = &rest[at + part.len()..],
None => return false,
}
}
rest.len() >= last.len() && rest.ends_with(last)
}
pub(crate) fn use_as_dictionary(pattern: &str) -> String {
let mut value = String::with_capacity(pattern.len() + 9);
value.push_str("match=\"");
for c in pattern.chars() {
if c == '"' || c == '\\' {
value.push('\\');
}
value.push(c);
}
value.push('"');
value
}
#[derive(Default)]
struct Store {
by_hash: HashMap<DictionaryHash, Arc<[u8]>>,
order: VecDeque<DictionaryHash>,
bytes: usize,
}
fn store() -> &'static Mutex<Store> {
static STORE: OnceLock<Mutex<Store>> = OnceLock::new();
STORE.get_or_init(Mutex::default)
}
pub(crate) fn remember(hash: DictionaryHash, bytes: Arc<[u8]>) {
if bytes.len() > STORE_BUDGET {
return;
}
let mut store = store().lock().unwrap_or_else(|e| e.into_inner());
if store.by_hash.contains_key(&hash) {
return;
}
store.bytes += bytes.len();
store.by_hash.insert(hash, bytes);
store.order.push_back(hash);
while store.bytes > STORE_BUDGET {
let Some(oldest) = store.order.pop_front() else { break };
if let Some(gone) = store.by_hash.remove(&oldest) {
store.bytes -= gone.len();
}
}
}
pub(crate) fn get(hash: &DictionaryHash) -> Option<Arc<[u8]>> {
store().lock().unwrap_or_else(|e| e.into_inner()).by_hash.get(hash).cloned()
}
#[cfg(test)]
pub(crate) fn kept() -> usize {
store().lock().unwrap_or_else(|e| e.into_inner()).by_hash.len()
}