use core::hash::{Hash, Hasher};
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct CacheKey<T>(pub T);
impl<T> CacheKey<T> {
pub fn new(value: T) -> Self {
Self(value)
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T: BitEq> Eq for CacheKey<T> {}
impl<T: BitEq> PartialEq for CacheKey<T> {
fn eq(&self, other: &Self) -> bool {
self.0.bit_eq(&other.0)
}
}
impl<T: BitHash> Hash for CacheKey<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.bit_hash(state);
}
}
pub trait BitHash {
fn bit_hash<H: Hasher>(&self, state: &mut H);
}
impl BitHash for f32 {
fn bit_hash<H: Hasher>(&self, state: &mut H) {
self.to_bits().hash(state);
}
}
impl<T: BitHash, const N: usize> BitHash for [T; N] {
fn bit_hash<H: Hasher>(&self, state: &mut H) {
self[..].bit_hash(state);
}
}
impl<T: BitHash> BitHash for [T] {
fn bit_hash<H: Hasher>(&self, state: &mut H) {
state.write_usize(self.len());
for piece in self {
piece.bit_hash(state);
}
}
}
impl<T: BitHash> BitHash for &T {
fn bit_hash<H: Hasher>(&self, state: &mut H) {
T::bit_hash(*self, state);
}
}
pub trait BitEq {
fn bit_eq(&self, other: &Self) -> bool;
}
impl BitEq for f32 {
fn bit_eq(&self, other: &Self) -> bool {
self.to_bits() == other.to_bits()
}
}
impl<T: BitEq, const N: usize> BitEq for [T; N] {
fn bit_eq(&self, other: &Self) -> bool {
for i in 0..N {
if !self[i].bit_eq(&other[i]) {
return false;
}
}
true
}
}
impl<T: BitEq> BitEq for [T] {
fn bit_eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
for (a, b) in self.iter().zip(other) {
if !a.bit_eq(b) {
return false;
}
}
true
}
}
impl<T: BitEq> BitEq for &T {
fn bit_eq(&self, other: &Self) -> bool {
T::bit_eq(*self, *other)
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::CacheKey;
use crate::{parse_color, DynamicColor};
use std::collections::HashMap;
#[test]
fn bit_eq_hashmap() {
let mut map: HashMap<CacheKey<f32>, i32> = HashMap::new();
assert!(map.insert(CacheKey(0.0), 0).is_none());
assert!(map.insert(CacheKey(-0.0), -1).is_none());
assert!(map.insert(CacheKey(1.0), 1).is_none());
assert!(map.insert(CacheKey(0.5), 5).is_none());
assert_eq!(map.get(&CacheKey(1.0)).unwrap(), &1);
assert_eq!(map.get(&CacheKey(0.0)).unwrap(), &0);
assert_eq!(map.remove(&CacheKey(-0.0)).unwrap(), -1);
assert!(!map.contains_key(&CacheKey(-0.0)));
assert_eq!(map.get(&CacheKey(0.5)).unwrap(), &5);
}
#[test]
fn bit_eq_color_hashmap() {
let mut map: HashMap<CacheKey<DynamicColor>, i32> = HashMap::new();
let red = parse_color("red").unwrap();
let red2 = parse_color("red").unwrap();
let other = parse_color("oklab(0.4 0.2 0.6)").unwrap();
assert!(map.insert(CacheKey(red), 10).is_none());
assert_eq!(map.insert(CacheKey(red2), 5).unwrap(), 10);
assert!(map.insert(CacheKey(other), 15).is_none());
assert_eq!(map.get(&CacheKey(other)).unwrap(), &15);
}
}