use std::hash::Hash;
use crate::error::CacheError;
pub const MAX_KEY_SIZE: usize = 512;
pub const TENANT_SEPARATOR: u8 = 0x00;
pub fn frame_tenant_key(tenant: &str, key: &[u8], buf: &mut [u8]) -> Result<usize, CacheError> {
let tenant_bytes = tenant.as_bytes();
if tenant_bytes.contains(&TENANT_SEPARATOR) {
return Err(CacheError::ConfigurationError);
}
let needed = tenant_bytes
.len()
.checked_add(1)
.and_then(|n| n.checked_add(key.len()))
.ok_or(CacheError::ConfigurationError)?;
if needed > buf.len() {
return Err(CacheError::ConfigurationError);
}
let (prefix, rest) = buf.split_at_mut(tenant_bytes.len());
prefix.copy_from_slice(tenant_bytes);
rest[0] = TENANT_SEPARATOR;
rest[1..=key.len()].copy_from_slice(key);
Ok(needed)
}
#[derive(Debug, Clone, Copy)]
pub struct KeyRef<'a>(pub &'a [u8]);
impl PartialEq for KeyRef<'_> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for KeyRef<'_> {}
impl std::hash::Hash for KeyRef<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<'a> From<&'a [u8]> for KeyRef<'a> {
fn from(bytes: &'a [u8]) -> Self {
KeyRef(bytes)
}
}
impl<'a> From<&'a Vec<u8>> for KeyRef<'a> {
fn from(bytes: &'a Vec<u8>) -> Self {
KeyRef(bytes.as_slice())
}
}
pub trait Key: Hash + Eq + Clone + Send + Sync + std::fmt::Debug {
fn encode(&self, buf: &mut [u8]) -> Result<usize, CacheError>;
fn encoded_len(&self) -> usize;
}
impl Key for String {
fn encode(&self, buf: &mut [u8]) -> Result<usize, CacheError> {
let bytes = self.as_bytes();
if buf.len() < bytes.len() {
return Err(CacheError::ConfigurationError);
}
buf[..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
}
fn encoded_len(&self) -> usize {
self.as_bytes().len()
}
}
impl Key for &str {
fn encode(&self, buf: &mut [u8]) -> Result<usize, CacheError> {
let bytes = self.as_bytes();
if buf.len() < bytes.len() {
return Err(CacheError::ConfigurationError);
}
buf[..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
}
fn encoded_len(&self) -> usize {
self.as_bytes().len()
}
}
impl Key for Vec<u8> {
fn encode(&self, buf: &mut [u8]) -> Result<usize, CacheError> {
if buf.len() < self.len() {
return Err(CacheError::ConfigurationError);
}
buf[..self.len()].copy_from_slice(self);
Ok(self.len())
}
fn encoded_len(&self) -> usize {
self.len()
}
}
impl<K> Key for &K
where
K: Key,
{
fn encode(&self, buf: &mut [u8]) -> Result<usize, CacheError> {
(*self).encode(buf)
}
fn encoded_len(&self) -> usize {
(*self).encoded_len()
}
}
impl<T> Key for std::sync::Arc<T>
where
T: Key,
{
fn encode(&self, buf: &mut [u8]) -> Result<usize, CacheError> {
self.as_ref().encode(buf)
}
fn encoded_len(&self) -> usize {
self.as_ref().encoded_len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_frame_separates_tenants_that_would_otherwise_concatenate_alike() {
let mut a = [0u8; 64];
let mut b = [0u8; 64];
let la = frame_tenant_key("ab", b"c", &mut a).expect("frame");
let lb = frame_tenant_key("a", b"bc", &mut b).expect("frame");
assert_ne!(&a[..la], &b[..lb], "tenant framing is ambiguous");
}
#[test]
fn the_same_tenant_and_key_frame_identically() {
let mut a = [0u8; 64];
let mut b = [0u8; 64];
let la = frame_tenant_key("t", b"k", &mut a).expect("frame");
let lb = frame_tenant_key("t", b"k", &mut b).expect("frame");
assert_eq!(&a[..la], &b[..lb]);
}
#[test]
fn a_tenant_containing_the_separator_is_rejected() {
let mut buf = [0u8; 64];
assert_eq!(
frame_tenant_key("a\0b", b"k", &mut buf),
Err(CacheError::ConfigurationError)
);
}
#[test]
fn an_oversized_frame_is_rejected_rather_than_truncated() {
let mut buf = [0u8; 8];
assert_eq!(
frame_tenant_key("tenant", b"key", &mut buf),
Err(CacheError::ConfigurationError),
"a frame that does not fit must not be shortened, because a shortened \
frame is a different key"
);
}
#[test]
fn an_empty_tenant_still_frames_deterministically() {
let mut a = [0u8; 16];
let n = frame_tenant_key("", b"k", &mut a).expect("frame");
assert_eq!(&a[..n], &[0u8, b'k']);
}
}