From fffd27abb3b3e1cbe0a4236ee68be5bf6588a20c Mon Sep 17 00:00:00 2001 From: metamuffin Date: Mon, 13 Jan 2025 15:23:50 +0100 Subject: filesystem resource cache --- shared/src/store.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) (limited to 'shared/src/store.rs') diff --git a/shared/src/store.rs b/shared/src/store.rs index 673a40e..08e62be 100644 --- a/shared/src/store.rs +++ b/shared/src/store.rs @@ -16,21 +16,51 @@ */ use crate::{helper::ReadWrite, packets::Resource}; use anyhow::Result; +use log::info; use redb::{Database, TableDefinition}; -use std::{collections::HashMap, marker::PhantomData, path::Path, sync::Mutex}; +use std::{ + collections::HashMap, + env::var, + fs::{File, create_dir_all, rename}, + io::{Read, Write}, + marker::PhantomData, + path::{Path, PathBuf}, + sync::Mutex, +}; const T_ENTRIES: TableDefinition<[u8; 32], &[u8]> = TableDefinition::new("e"); pub enum ResourceStore { Redb(Database), + Filesystem(PathBuf), Memory(Mutex>>), } impl ResourceStore { - pub fn new_persistent(path: &Path) -> Result { + pub fn new_env() -> Result { + if var("WEARECHAT_RES_CACHE_USE_REDB").is_ok() { + ResourceStore::new_redb( + &xdg::BaseDirectories::with_prefix("wearechat")? + .place_cache_file("resources.db")?, + ) + } else { + ResourceStore::new_filesystem( + &xdg::BaseDirectories::with_prefix("wearechat")? + .create_cache_directory("resources")?, + ) + } + } + pub fn new_filesystem(path: &Path) -> Result { + info!("using filesystem resource cache in {path:?}"); + create_dir_all(path)?; + Ok(Self::Filesystem(path.to_owned())) + } + pub fn new_redb(path: &Path) -> Result { + info!("initializing redb resource cache..."); let db = Database::create(path)?; let txn = db.begin_write()?; txn.open_table(T_ENTRIES)?; txn.commit()?; + info!("done"); Ok(Self::Redb(db)) } pub fn new_memory() -> Self { @@ -55,6 +85,16 @@ impl ResourceStore { } } ResourceStore::Memory(map) => Ok(map.lock().unwrap().get(&key).map(|x| x.to_vec())), + ResourceStore::Filesystem(root) => { + let path = fs_cache_path(&root, key); + if path.exists() { + let mut buf = Vec::new(); + File::open(path)?.read_to_end(&mut buf)?; + Ok(Some(buf)) + } else { + Ok(None) + } + } } } pub fn set_raw(&self, value: &[u8]) -> Result { @@ -70,12 +110,19 @@ impl ResourceStore { ResourceStore::Memory(map) => { map.lock().unwrap().insert(key, value.to_vec()); } + ResourceStore::Filesystem(root) => { + let path = fs_cache_path(&root, key); + let path_temp = path.with_extension("part"); + File::create(&path_temp)?.write_all(value)?; + rename(path_temp, path)?; + } } Ok(key) } pub fn iter(&self, mut cb: impl FnMut(&[u8])) -> Result<()> { match self { ResourceStore::Redb(_database) => todo!(), + ResourceStore::Filesystem(_root) => todo!(), ResourceStore::Memory(mutex) => { mutex.lock().unwrap().values().for_each(|v| cb(v)); Ok(()) @@ -89,3 +136,13 @@ pub fn resource_hash(x: &[u8]) -> [u8; 32] { hasher.update(x); hasher.finalize().into() } + +fn fs_cache_path(path: &Path, res: Resource) -> PathBuf { + path.join(format!( + "{:08x}{:08x}{:08x}{:08x}", + u64::from_be_bytes(res.0[0..8].try_into().unwrap()), + u64::from_be_bytes(res.0[8..16].try_into().unwrap()), + u64::from_be_bytes(res.0[16..24].try_into().unwrap()), + u64::from_be_bytes(res.0[24..32].try_into().unwrap()), + )) +} -- cgit v1.2.3-70-g09d2