diff options
Diffstat (limited to 'cache/src/key.rs')
| -rw-r--r-- | cache/src/key.rs | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/cache/src/key.rs b/cache/src/key.rs new file mode 100644 index 0000000..d8ca510 --- /dev/null +++ b/cache/src/key.rs @@ -0,0 +1,83 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2025 metamuffin <metamuffin.org> +*/ +use crate::{CACHE_GENERATION_BUCKET_COUNT, CONF}; +use anyhow::bail; +use base64::{Engine, prelude::BASE64_URL_SAFE}; +use sha2::Sha256; +use std::{ + fmt::Display, + hash::{Hash, Hasher}, + str::FromStr, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CacheKey(pub [u8; 32]); + +impl CacheKey { + pub fn new(ty: CacheContentType, seed: impl Hash) -> Self { + use sha2::Digest; + struct ShaHasher(Sha256); + impl Hasher for ShaHasher { + fn finish(&self) -> u64 { + unreachable!() + } + fn write(&mut self, bytes: &[u8]) { + self.0.update(bytes); + } + } + let mut d = ShaHasher(sha2::Sha256::new()); + d.0.update(CONF.secret.as_bytes()); + seed.hash(&mut d); + let d = d.0.finalize(); + let mut key: [u8; 32] = d.as_slice().try_into().unwrap(); + key[0] = ty as u8; + Self(key) + } + pub fn new_json(seed: impl Hash) -> Self { + Self::new(CacheContentType::Json, seed) + } + pub fn new_image(seed: impl Hash) -> Self { + Self::new(CacheContentType::Image, seed) + } + pub fn content_type(&self) -> CacheContentType { + match self.0[0] { + 1 => CacheContentType::Image, + 2 => CacheContentType::Json, + _ => CacheContentType::Unknown, + } + } + pub(super) fn bucket(&self) -> usize { + (self.0[1] as usize + | ((self.0[2] as usize) << 8) + | ((self.0[3] as usize) << 16) + | ((self.0[4] as usize) << 24)) + % CACHE_GENERATION_BUCKET_COUNT + } +} + +impl Display for CacheKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&BASE64_URL_SAFE.encode(self.0)) + } +} +impl FromStr for CacheKey { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let mut out = [0; 32]; + let size = BASE64_URL_SAFE.decode_slice(s, &mut out)?; + if size != out.len() { + bail!("cache key parse invalid size") + } + Ok(Self(out)) + } +} + +#[repr(u8)] +pub enum CacheContentType { + Unknown = 0, + Image = 1, + Json = 2, +} |