use core::fmt;
use core::hash::{Hash, Hasher};
use std::net::IpAddr;
const INLINE_CAP: usize = 23;
#[derive(Clone)]
enum Repr {
Inline { buf: [u8; INLINE_CAP], len: u8 },
Heap(Box<[u8]>),
}
#[derive(Clone)]
pub struct Key(Repr);
impl Key {
fn from_bytes(bytes: &[u8]) -> Self {
if bytes.len() <= INLINE_CAP {
let mut buf = [0u8; INLINE_CAP];
buf[..bytes.len()].copy_from_slice(bytes);
Self(Repr::Inline {
buf,
len: bytes.len() as u8,
})
} else {
Self(Repr::Heap(bytes.into()))
}
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
match &self.0 {
Repr::Inline { buf, len } => &buf[..*len as usize],
Repr::Heap(bytes) => bytes,
}
}
}
impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl Eq for Key {}
impl Hash for Key {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state);
}
}
impl fmt::Debug for Key {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Key").field(&self.as_bytes()).finish()
}
}
impl From<&str> for Key {
fn from(value: &str) -> Self {
Self::from_bytes(value.as_bytes())
}
}
impl From<String> for Key {
fn from(value: String) -> Self {
if value.len() <= INLINE_CAP {
Self::from_bytes(value.as_bytes())
} else {
Self(Repr::Heap(value.into_bytes().into_boxed_slice()))
}
}
}
impl From<&[u8]> for Key {
fn from(value: &[u8]) -> Self {
Self::from_bytes(value)
}
}
impl From<Vec<u8>> for Key {
fn from(value: Vec<u8>) -> Self {
if value.len() <= INLINE_CAP {
Self::from_bytes(&value)
} else {
Self(Repr::Heap(value.into_boxed_slice()))
}
}
}
impl From<u64> for Key {
fn from(value: u64) -> Self {
Self::from_bytes(&value.to_be_bytes())
}
}
impl From<IpAddr> for Key {
fn from(value: IpAddr) -> Self {
match value {
IpAddr::V4(addr) => Self::from_bytes(&addr.octets()),
IpAddr::V6(addr) => Self::from_bytes(&addr.octets()),
}
}
}
#[cfg(test)]
mod tests {
use super::{INLINE_CAP, Key, Repr};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
fn is_inline(key: &Key) -> bool {
matches!(key.0, Repr::Inline { .. })
}
#[test]
fn test_str_and_string_are_equal_for_same_bytes() {
let a: Key = "user:42".into();
let b: Key = String::from("user:42").into();
assert_eq!(a, b);
assert_eq!(a.as_bytes(), b"user:42");
}
#[test]
fn test_distinct_content_is_distinct() {
let a: Key = "a".into();
let b: Key = "b".into();
assert_ne!(a, b);
}
#[test]
fn test_byte_slice_and_vec_round_trip() {
let bytes: &[u8] = &[1, 2, 3, 4];
let a: Key = bytes.into();
let b: Key = vec![1u8, 2, 3, 4].into();
assert_eq!(a, b);
assert_eq!(a.as_bytes(), bytes);
}
#[test]
fn test_u64_uses_big_endian_bytes() {
let key: Key = 1u64.into();
assert_eq!(key.as_bytes(), &[0, 0, 0, 0, 0, 0, 0, 1]);
}
#[test]
fn test_ip_addresses_encode_their_octets() {
let v4: Key = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)).into();
assert_eq!(v4.as_bytes(), &[10, 0, 0, 1]);
let v6: Key = IpAddr::V6(Ipv6Addr::LOCALHOST).into();
assert_eq!(v6.as_bytes().len(), 16);
}
#[test]
fn test_small_keys_are_stored_inline() {
assert!(is_inline(&Key::from("user:42")));
assert!(is_inline(&Key::from(1u64)));
assert!(is_inline(&Key::from(IpAddr::V6(Ipv6Addr::LOCALHOST))));
let at_cap = vec![b'x'; INLINE_CAP];
assert!(is_inline(&Key::from(at_cap.as_slice())));
}
#[test]
fn test_large_keys_spill_to_heap_and_compare_equal() {
let long = vec![b'y'; INLINE_CAP + 1];
let from_slice: Key = long.as_slice().into();
let from_vec: Key = long.clone().into();
assert!(!is_inline(&from_slice));
assert_eq!(from_slice, from_vec);
assert_eq!(from_slice.as_bytes(), long.as_slice());
}
#[test]
fn test_inline_and_heap_hash_consistently_by_bytes() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn hash(key: &Key) -> u64 {
let mut h = DefaultHasher::new();
key.hash(&mut h);
h.finish()
}
let a: Key = "short".into();
let b: Key = String::from("short").into();
assert_eq!(hash(&a), hash(&b));
}
}