use super::*;
use blake2::{Blake2b, Digest};
use serde::{Deserialize, Serialize};
const KEY_SIZE: usize = 16;
pub type HashBinary = [u8; KEY_SIZE];
fn hex2str(hex: &[u8]) -> String {
use std::fmt::Write;
let mut s = String::with_capacity(KEY_SIZE * 2);
for c in hex {
write!(s, "{:02x}", c).unwrap(); }
s
}
pub fn str2hex(s: &str) -> Option<HashBinary> {
if s.len() != KEY_SIZE * 2 {
return None;
}
let mut output = [0; KEY_SIZE];
hex::decode_to_slice(s.as_bytes(), &mut output).ok()?;
Some(output)
}
pub trait CacheHashKey {
fn primary_bin(&self) -> HashBinary;
fn variance_bin(&self) -> Option<HashBinary>;
fn combined_bin(&self) -> HashBinary {
let key = self.primary_bin();
if let Some(v) = self.variance_bin() {
let mut hasher = Blake2b128::new();
hasher.update(key);
hasher.update(v);
hasher.finalize().into()
} else {
key
}
}
fn user_tag(&self) -> &str;
fn primary(&self) -> String {
hex2str(&self.primary_bin())
}
fn variance(&self) -> Option<String> {
self.variance_bin().as_ref().map(|b| hex2str(&b[..]))
}
fn combined(&self) -> String {
hex2str(&self.combined_bin())
}
}
#[derive(Debug, Clone)]
pub struct CacheKey {
namespace: String,
primary: String,
primary_bin_override: Option<HashBinary>,
variance: Option<HashBinary>,
pub user_tag: String,
}
impl CacheKey {
pub fn set_variance_key(&mut self, key: HashBinary) {
self.variance = Some(key)
}
pub fn get_variance_key(&self) -> Option<&HashBinary> {
self.variance.as_ref()
}
pub fn remove_variance_key(&mut self) {
self.variance = None
}
pub fn set_primary_bin_override(&mut self, key: HashBinary) {
self.primary_bin_override = Some(key)
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct CompactCacheKey {
pub primary: HashBinary,
pub variance: Option<Box<HashBinary>>,
pub user_tag: Box<str>, }
impl CacheHashKey for CompactCacheKey {
fn primary_bin(&self) -> HashBinary {
self.primary
}
fn variance_bin(&self) -> Option<HashBinary> {
self.variance.as_ref().map(|s| *s.as_ref())
}
fn user_tag(&self) -> &str {
&self.user_tag
}
}
pub(crate) type Blake2b128 = Blake2b<blake2::digest::consts::U16>;
pub fn hash_u8(key: &str) -> u8 {
let mut hasher = Blake2b128::new();
hasher.update(key);
let raw = hasher.finalize();
raw[0]
}
pub fn hash_key(key: &str) -> HashBinary {
let mut hasher = Blake2b128::new();
hasher.update(key);
let raw = hasher.finalize();
raw.into()
}
impl CacheKey {
fn primary_hasher(&self) -> Blake2b128 {
let mut hasher = Blake2b128::new();
hasher.update(&self.namespace);
hasher.update(&self.primary);
hasher
}
pub fn default(req_header: &ReqHeader) -> Self {
CacheKey {
namespace: "".into(),
primary: format!("{}", req_header.uri),
primary_bin_override: None,
variance: None,
user_tag: "".into(),
}
}
pub fn new<S1, S2, S3>(namespace: S1, primary: S2, user_tag: S3) -> Self
where
S1: Into<String>,
S2: Into<String>,
S3: Into<String>,
{
CacheKey {
namespace: namespace.into(),
primary: primary.into(),
primary_bin_override: None,
variance: None,
user_tag: user_tag.into(),
}
}
pub fn namespace(&self) -> &str {
&self.namespace
}
pub fn primary_key(&self) -> &str {
&self.primary
}
pub fn to_compact(&self) -> CompactCacheKey {
let primary = self.primary_bin();
CompactCacheKey {
primary,
variance: self.variance_bin().map(Box::new),
user_tag: self.user_tag.clone().into_boxed_str(),
}
}
}
impl CacheHashKey for CacheKey {
fn primary_bin(&self) -> HashBinary {
if let Some(primary_bin_override) = self.primary_bin_override {
primary_bin_override
} else {
self.primary_hasher().finalize().into()
}
}
fn variance_bin(&self) -> Option<HashBinary> {
self.variance
}
fn user_tag(&self) -> &str {
&self.user_tag
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_key_hash() {
let key = CacheKey {
namespace: "".into(),
primary: "aa".into(),
primary_bin_override: None,
variance: None,
user_tag: "1".into(),
};
let hash = key.primary();
assert_eq!(hash, "ac10f2aef117729f8dad056b3059eb7e");
assert!(key.variance().is_none());
assert_eq!(key.combined(), hash);
let compact = key.to_compact();
assert_eq!(compact.primary(), hash);
assert!(compact.variance().is_none());
assert_eq!(compact.combined(), hash);
}
#[test]
fn test_cache_key_hash_override() {
let mut key = CacheKey {
namespace: "".into(),
primary: "aa".into(),
primary_bin_override: str2hex("27c35e6e9373877f29e562464e46497e"),
variance: None,
user_tag: "1".into(),
};
let hash = key.primary();
assert_eq!(hash, "27c35e6e9373877f29e562464e46497e");
assert!(key.variance().is_none());
assert_eq!(key.combined(), hash);
let compact = key.to_compact();
assert_eq!(compact.primary(), hash);
assert!(compact.variance().is_none());
assert_eq!(compact.combined(), hash);
key.set_primary_bin_override(str2hex("004174d3e75a811a5b44c46b3856f3ee").unwrap());
let hash = key.primary();
assert_eq!(hash, "004174d3e75a811a5b44c46b3856f3ee");
assert!(key.variance().is_none());
assert_eq!(key.combined(), hash);
let compact = key.to_compact();
assert_eq!(compact.primary(), hash);
assert!(compact.variance().is_none());
assert_eq!(compact.combined(), hash);
}
#[test]
fn test_cache_key_vary_hash() {
let key = CacheKey {
namespace: "".into(),
primary: "aa".into(),
primary_bin_override: None,
variance: Some([0u8; 16]),
user_tag: "1".into(),
};
let hash = key.primary();
assert_eq!(hash, "ac10f2aef117729f8dad056b3059eb7e");
assert_eq!(key.variance().unwrap(), "00000000000000000000000000000000");
assert_eq!(key.combined(), "004174d3e75a811a5b44c46b3856f3ee");
let compact = key.to_compact();
assert_eq!(compact.primary(), "ac10f2aef117729f8dad056b3059eb7e");
assert_eq!(
compact.variance().unwrap(),
"00000000000000000000000000000000"
);
assert_eq!(compact.combined(), "004174d3e75a811a5b44c46b3856f3ee");
}
#[test]
fn test_cache_key_vary_hash_override() {
let key = CacheKey {
namespace: "".into(),
primary: "saaaad".into(),
primary_bin_override: str2hex("ac10f2aef117729f8dad056b3059eb7e"),
variance: Some([0u8; 16]),
user_tag: "1".into(),
};
let hash = key.primary();
assert_eq!(hash, "ac10f2aef117729f8dad056b3059eb7e");
assert_eq!(key.variance().unwrap(), "00000000000000000000000000000000");
assert_eq!(key.combined(), "004174d3e75a811a5b44c46b3856f3ee");
let compact = key.to_compact();
assert_eq!(compact.primary(), "ac10f2aef117729f8dad056b3059eb7e");
assert_eq!(
compact.variance().unwrap(),
"00000000000000000000000000000000"
);
assert_eq!(compact.combined(), "004174d3e75a811a5b44c46b3856f3ee");
}
#[test]
fn test_hex_str() {
let mut key = [0; KEY_SIZE];
for (i, v) in key.iter_mut().enumerate() {
*v = i as u8;
}
let hex_str = hex2str(&key);
let key2 = str2hex(&hex_str).unwrap();
for i in 0..KEY_SIZE {
assert_eq!(key[i], key2[i]);
}
}
}