use crate::lossless::constants::HASH_MUL;
use crate::lossless::prelude::*;
pub(crate) struct ColorCache {
entries: Vec<u32>,
bits: u32,
}
impl ColorCache {
pub(crate) fn new(bits: u32) -> Self {
debug_assert!((1..=crate::lossless::constants::MAX_CACHE_BITS).contains(&bits));
Self {
entries: alloc_zeroed(1usize << bits),
bits,
}
}
pub(crate) fn reset(&mut self, bits: u32) {
debug_assert!((1..=crate::lossless::constants::MAX_CACHE_BITS).contains(&bits));
debug_assert!(self.entries.len() >= 1usize << bits);
self.bits = bits;
self.entries[..1usize << bits]
.iter_mut()
.for_each(|e| *e = 0);
}
pub(crate) const fn index(argb: u32, bits: u32) -> usize {
(HASH_MUL.wrapping_mul(argb) >> (32 - bits)) as usize
}
pub(crate) fn insert(&mut self, argb: u32) {
let i = Self::index(argb, self.bits);
self.entries[i] = argb;
}
pub(crate) fn get(&self, index: usize) -> u32 {
self.entries.get(index).copied().unwrap_or(0)
}
}
fn alloc_zeroed(len: usize) -> Vec<u32> {
vec![0u32; len]
}
#[cfg(test)]
mod tests {
use super::ColorCache;
#[test]
fn insert_then_get_round_trips() {
let mut cache = ColorCache::new(11);
let argb = 0xAABB_CCDD;
cache.insert(argb);
assert_eq!(cache.get(ColorCache::index(argb, 11)), argb);
}
#[test]
fn index_is_deterministic() {
let argb = 0x1234_5678;
assert_eq!(ColorCache::index(argb, 10), ColorCache::index(argb, 10));
}
#[test]
fn index_matches_hash_formula() {
assert_eq!(ColorCache::index(1, 1), 0);
let argb = 0x00FF_00FFu32;
let expected = (0x1e35_a7bdu32.wrapping_mul(argb) >> (32 - 8)) as usize;
assert_eq!(ColorCache::index(argb, 8), expected);
}
#[test]
fn cache_size_follows_bits() {
let cache = ColorCache::new(4);
for v in 0..1000u32 {
let i = ColorCache::index(v.wrapping_mul(0x9E37), 4);
assert!(i < (1 << 4));
let _ = cache.get(i);
}
}
}