const CACHE_SIZE: usize = 1024;
#[derive(Debug, Clone, Copy)]
pub struct MegamorphicEntry {
pub key: u64,
pub field_idx: u16,
pub field_type_tag: u16,
pub valid: bool,
}
impl Default for MegamorphicEntry {
fn default() -> Self {
Self {
key: 0,
field_idx: 0,
field_type_tag: 0,
valid: false,
}
}
}
pub struct MegamorphicCache {
entries: Box<[MegamorphicEntry; CACHE_SIZE]>,
}
impl MegamorphicCache {
pub fn new() -> Self {
Self {
entries: Box::new([MegamorphicEntry::default(); CACHE_SIZE]),
}
}
pub fn hash_key(schema_id: u64, field_name: &str) -> u64 {
let mut hash: u64 = 0xcbf29ce484222325 ^ schema_id;
for byte in field_name.bytes() {
hash ^= byte as u64;
hash = hash.wrapping_mul(0x100000001b3);
}
hash
}
pub fn probe(&self, key: u64) -> Option<(u16, u16)> {
let idx = (key as usize) % CACHE_SIZE;
let entry = &self.entries[idx];
if entry.valid && entry.key == key {
Some((entry.field_idx, entry.field_type_tag))
} else {
None
}
}
pub fn insert(&mut self, key: u64, field_idx: u16, field_type_tag: u16) {
let idx = (key as usize) % CACHE_SIZE;
self.entries[idx] = MegamorphicEntry {
key,
field_idx,
field_type_tag,
valid: true,
};
}
pub fn invalidate_all(&mut self) {
for entry in self.entries.iter_mut() {
entry.valid = false;
}
}
pub fn hit_rate(&self) -> f64 {
let valid_count = self.entries.iter().filter(|e| e.valid).count();
valid_count as f64 / CACHE_SIZE as f64
}
}
impl Default for MegamorphicCache {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_cache_empty() {
let cache = MegamorphicCache::new();
assert_eq!(cache.hit_rate(), 0.0);
assert_eq!(cache.probe(12345), None);
}
#[test]
fn test_insert_and_probe_hit() {
let mut cache = MegamorphicCache::new();
let key = MegamorphicCache::hash_key(42, "name");
cache.insert(key, 3, 7);
let result = cache.probe(key);
assert_eq!(result, Some((3, 7)));
}
#[test]
fn test_probe_miss() {
let mut cache = MegamorphicCache::new();
let key1 = MegamorphicCache::hash_key(42, "name");
let key2 = MegamorphicCache::hash_key(42, "age");
cache.insert(key1, 3, 7);
assert_eq!(cache.probe(key2), None);
}
#[test]
fn test_hash_key_consistency() {
let k1 = MegamorphicCache::hash_key(100, "field_a");
let k2 = MegamorphicCache::hash_key(100, "field_a");
assert_eq!(k1, k2);
let k3 = MegamorphicCache::hash_key(100, "field_b");
assert_ne!(k1, k3);
let k4 = MegamorphicCache::hash_key(200, "field_a");
assert_ne!(k1, k4);
}
#[test]
fn test_invalidate_all() {
let mut cache = MegamorphicCache::new();
for i in 0..10u64 {
let key = MegamorphicCache::hash_key(i, "x");
cache.insert(key, i as u16, 0);
}
assert!(cache.hit_rate() > 0.0);
cache.invalidate_all();
assert_eq!(cache.hit_rate(), 0.0);
let key = MegamorphicCache::hash_key(0, "x");
assert_eq!(cache.probe(key), None);
}
#[test]
fn test_collision_overwrites() {
let mut cache = MegamorphicCache::new();
let key1 = 100u64;
let key2 = key1 + CACHE_SIZE as u64;
cache.insert(key1, 1, 10);
assert_eq!(cache.probe(key1), Some((1, 10)));
cache.insert(key2, 2, 20);
assert_eq!(cache.probe(key2), Some((2, 20)));
assert_eq!(cache.probe(key1), None);
}
}