use std::cell::RefCell;
use std::num::NonZeroUsize;
const TL_CACHE_CAP: usize = 32;
thread_local! {
static TL_FONT_CACHE: RefCell<lru::LruCache<u64, fontdue::Font>> = RefCell::new(
lru::LruCache::new(NonZeroUsize::new(TL_CACHE_CAP).expect("TL_CACHE_CAP is non-zero")),
);
}
fn font_data_key(data: &[u8]) -> u64 {
const OFFSET_BASIS: u64 = 0xcbf29ce484222325;
const PRIME: u64 = 0x00000100000001b3;
let sample = &data[..data.len().min(64)];
let mut h: u64 = OFFSET_BASIS;
for &b in sample {
h ^= b as u64;
h = h.wrapping_mul(PRIME);
}
h
}
pub fn get_or_parse_fontdue(face_data: &[u8]) -> Option<fontdue::Font> {
if face_data.is_empty() {
return None;
}
let key = font_data_key(face_data);
TL_FONT_CACHE.with(|cache| {
let mut c = cache.borrow_mut();
if !c.contains(&key) {
let font =
fontdue::Font::from_bytes(face_data, fontdue::FontSettings::default()).ok()?;
c.put(key, font);
}
c.get(&key).cloned()
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_data_returns_none() {
assert!(get_or_parse_fontdue(&[]).is_none());
}
#[test]
fn invalid_data_returns_none() {
assert!(get_or_parse_fontdue(b"not a font file at all xxxx").is_none());
}
#[test]
fn font_data_key_stable() {
let data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let k1 = font_data_key(data);
let k2 = font_data_key(data);
assert_eq!(k1, k2);
}
#[test]
fn font_data_key_differs_for_different_data() {
let k1 = font_data_key(b"AAAAAAAAAA");
let k2 = font_data_key(b"BBBBBBBBBB");
assert_ne!(k1, k2);
}
}