use parking_lot::RwLock;
use rustc_hash::FxHashMap;
use std::sync::Arc;
pub const CUSTOM_GLYPH_FONT_ID: usize = usize::MAX;
pub const CUSTOM_GLYPH_FONT_ID_U32: u32 = u32::MAX;
#[inline]
pub fn pack_atlas_glyph_id(codepoint: u32, version: u32) -> u32 {
((version & 0x7FF) << 21) | (codepoint & 0x1F_FFFF)
}
pub const GLOSSARY_CAPACITY: usize = 1024;
#[inline]
pub fn is_pua(cp: u32) -> bool {
(0xE000..=0xF8FF).contains(&cp)
|| (0xF_0000..=0xF_FFFD).contains(&cp)
|| (0x10_0000..=0x10_FFFD).contains(&cp)
}
#[derive(Debug, Clone)]
pub enum StoredPayload {
Glyf {
glyf: Vec<u8>,
},
ColrV0 {
glyphs: Vec<Vec<u8>>,
colr: Vec<u8>,
cpal: Vec<u8>,
},
ColrV1 {
glyphs: Vec<Vec<u8>>,
colr: Vec<u8>,
cpal: Vec<u8>,
},
}
impl StoredPayload {
pub fn glyf(bytes: Vec<u8>) -> Self {
StoredPayload::Glyf { glyf: bytes }
}
}
#[derive(Debug, Clone)]
pub struct RegisteredGlyph {
pub payload: Arc<StoredPayload>,
pub upm: u16,
pub index: u16,
pub insertion_id: u64,
pub version: u32,
}
#[derive(Debug)]
struct Inner {
by_cp: FxHashMap<u32, RegisteredGlyph>,
indexed: [Option<u32>; GLOSSARY_CAPACITY],
next_insertion: u64,
next_version: u32,
}
impl Default for Inner {
fn default() -> Self {
Self {
by_cp: FxHashMap::default(),
indexed: [None; GLOSSARY_CAPACITY],
next_insertion: 0,
next_version: 0,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct GlyphRegistry {
inner: Arc<RwLock<Inner>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegisterRejection {
OutOfNamespace,
}
impl GlyphRegistry {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn ptr_eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
pub fn register(
&self,
cp: u32,
payload: StoredPayload,
upm: u16,
) -> Result<Option<u32>, RegisterRejection> {
if !is_pua(cp) {
return Err(RegisterRejection::OutOfNamespace);
}
let payload = Arc::new(payload);
let mut inner = self.inner.write();
let version = inner.next_version;
inner.next_version = inner.next_version.wrapping_add(1);
if let Some(existing) = inner.by_cp.get_mut(&cp) {
existing.payload = payload;
existing.upm = upm;
existing.version = version;
return Ok(None);
}
let (slot_index, evicted) = match inner.indexed.iter().position(|s| s.is_none()) {
Some(i) => (i as u16, None),
None => {
let (evict_cp, evict_entry) = inner
.by_cp
.iter()
.min_by_key(|(_, v)| v.insertion_id)
.map(|(cp, v)| (*cp, v.clone()))
.expect("capacity full but by_cp empty");
let freed_index = evict_entry.index;
inner.by_cp.remove(&evict_cp);
inner.indexed[freed_index as usize] = None;
(freed_index, Some(evict_cp))
}
};
let id = inner.next_insertion;
inner.next_insertion = inner.next_insertion.wrapping_add(1);
inner.indexed[slot_index as usize] = Some(cp);
inner.by_cp.insert(
cp,
RegisteredGlyph {
payload,
upm,
index: slot_index,
insertion_id: id,
version,
},
);
Ok(evicted)
}
pub fn clear_one(&self, cp: u32) {
let mut inner = self.inner.write();
if let Some(entry) = inner.by_cp.remove(&cp) {
inner.indexed[entry.index as usize] = None;
}
}
pub fn clear_all(&self) {
let mut inner = self.inner.write();
inner.by_cp.clear();
inner.indexed = [None; GLOSSARY_CAPACITY];
}
pub fn cp_for_index(&self, index: u16) -> Option<u32> {
self.inner
.read()
.indexed
.get(index as usize)
.copied()
.flatten()
}
pub fn get(&self, cp: u32) -> Option<RegisteredGlyph> {
self.inner.read().by_cp.get(&cp).cloned()
}
pub fn contains(&self, cp: u32) -> bool {
self.inner.read().by_cp.contains_key(&cp)
}
pub fn len(&self) -> usize {
self.inner.read().by_cp.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pua_check_matches_spec() {
assert!(is_pua(0xE000));
assert!(is_pua(0xE0A0));
assert!(is_pua(0xF8FF));
assert!(is_pua(0xF_0000));
assert!(is_pua(0x10_0000));
assert!(!is_pua(0x61)); assert!(!is_pua(0x1F600)); }
#[test]
fn pack_atlas_glyph_id_is_unique_per_codepoint_and_version() {
assert_ne!(
pack_atlas_glyph_id(0xE0A0, 0),
pack_atlas_glyph_id(0xE0A1, 0)
);
assert_ne!(
pack_atlas_glyph_id(0xE0A0, 0),
pack_atlas_glyph_id(0xE0A0, 1)
);
assert_eq!(
pack_atlas_glyph_id(0xE0A0, 0),
pack_atlas_glyph_id(0xE0A0, 0x800)
);
}
#[test]
fn register_overwrite_bumps_version_for_atlas_busting() {
let r = GlyphRegistry::new();
r.register(0xE0A0, glyf(vec![0xAA]), 1000).unwrap();
let v_first = r.get(0xE0A0).unwrap().version;
r.register(0xE0A0, glyf(vec![0xBB]), 1000).unwrap();
let v_second = r.get(0xE0A0).unwrap().version;
assert_ne!(
v_first, v_second,
"overwrite must produce a new version so atlas re-rasterises"
);
}
#[test]
fn clear_then_reregister_yields_new_version() {
let r = GlyphRegistry::new();
r.register(0xE0A0, glyf(vec![0xAA]), 1000).unwrap();
let v_first = r.get(0xE0A0).unwrap().version;
r.clear_one(0xE0A0);
r.register(0xE0A0, glyf(vec![0xBB]), 1000).unwrap();
let v_second = r.get(0xE0A0).unwrap().version;
assert_ne!(
v_first, v_second,
"register-after-clear must produce a new version"
);
}
fn glyf(bytes: Vec<u8>) -> StoredPayload {
StoredPayload::glyf(bytes)
}
fn assert_glyf_bytes(p: &StoredPayload, expected: &[u8]) {
match p {
StoredPayload::Glyf { glyf } => assert_eq!(glyf, expected),
other => panic!("expected Glyf, got {:?}", other),
}
}
#[test]
fn register_and_lookup() {
let r = GlyphRegistry::new();
assert_eq!(r.register(0xE0A0, glyf(vec![1, 2, 3]), 1000).unwrap(), None);
let g = r.get(0xE0A0).unwrap();
assert_glyf_bytes(&g.payload, &[1, 2, 3]);
assert_eq!(g.upm, 1000);
assert!(r.contains(0xE0A0));
assert_eq!(r.len(), 1);
}
#[test]
fn register_rejects_non_pua() {
let r = GlyphRegistry::new();
assert_eq!(
r.register(0x61, glyf(vec![1]), 1000),
Err(RegisterRejection::OutOfNamespace)
);
assert!(r.is_empty());
}
#[test]
fn register_overwrites_preserving_insertion_order_and_index() {
let r = GlyphRegistry::new();
r.register(0xE0A0, glyf(vec![1]), 1000).unwrap();
r.register(0xE0A1, glyf(vec![2]), 1000).unwrap();
let idx_before = r.get(0xE0A0).unwrap().index;
r.register(0xE0A0, glyf(vec![9]), 2048).unwrap();
let a = r.get(0xE0A0).unwrap();
let b = r.get(0xE0A1).unwrap();
assert_glyf_bytes(&a.payload, &[9]);
assert_eq!(a.upm, 2048);
assert_eq!(a.index, idx_before);
assert!(a.insertion_id < b.insertion_id);
}
#[test]
fn distinct_registrations_get_distinct_indices() {
let r = GlyphRegistry::new();
r.register(0xE0A0, glyf(vec![1]), 1000).unwrap();
r.register(0xE0A1, glyf(vec![2]), 1000).unwrap();
r.register(0xE0A2, glyf(vec![3]), 1000).unwrap();
let i0 = r.get(0xE0A0).unwrap().index;
let i1 = r.get(0xE0A1).unwrap().index;
let i2 = r.get(0xE0A2).unwrap().index;
assert_ne!(i0, i1);
assert_ne!(i1, i2);
assert_ne!(i0, i2);
assert_eq!(r.cp_for_index(i0), Some(0xE0A0));
assert_eq!(r.cp_for_index(i1), Some(0xE0A1));
}
#[test]
fn cleared_slot_is_reused_by_next_registration() {
let r = GlyphRegistry::new();
r.register(0xE0A0, glyf(vec![1]), 1000).unwrap();
let old_index = r.get(0xE0A0).unwrap().index;
r.clear_one(0xE0A0);
assert_eq!(r.cp_for_index(old_index), None);
r.register(0xE0A1, glyf(vec![2]), 1000).unwrap();
assert_eq!(r.get(0xE0A1).unwrap().index, old_index);
}
#[test]
fn clear_one_removes_registration() {
let r = GlyphRegistry::new();
r.register(0xE0A0, glyf(vec![1]), 1000).unwrap();
r.register(0xE0A1, glyf(vec![2]), 1000).unwrap();
r.clear_one(0xE0A0);
assert!(!r.contains(0xE0A0));
assert!(r.contains(0xE0A1));
}
#[test]
fn clear_one_unknown_is_noop() {
let r = GlyphRegistry::new();
r.clear_one(0xE0A0);
assert!(r.is_empty());
}
#[test]
fn clear_all_drops_everything() {
let r = GlyphRegistry::new();
r.register(0xE0A0, glyf(vec![1]), 1000).unwrap();
r.register(0xE0A1, glyf(vec![2]), 1000).unwrap();
r.clear_all();
assert!(r.is_empty());
}
#[test]
fn fifo_eviction_on_capacity() {
let r = GlyphRegistry::new();
for i in 0..GLOSSARY_CAPACITY as u32 {
r.register(0xE000 + i, glyf(vec![i as u8]), 1000).unwrap();
}
assert_eq!(r.len(), GLOSSARY_CAPACITY);
let evicted = r.register(0xE500, glyf(vec![0xFF]), 1000).unwrap();
assert_eq!(evicted, Some(0xE000));
assert!(!r.contains(0xE000));
assert!(r.contains(0xE500));
assert_eq!(r.len(), GLOSSARY_CAPACITY);
}
#[test]
fn overwrite_at_capacity_does_not_evict() {
let r = GlyphRegistry::new();
for i in 0..GLOSSARY_CAPACITY as u32 {
r.register(0xE000 + i, glyf(vec![i as u8]), 1000).unwrap();
}
let evicted = r.register(0xE000, glyf(vec![0xAB]), 1000).unwrap();
assert_eq!(evicted, None);
assert_eq!(r.len(), GLOSSARY_CAPACITY);
assert_glyf_bytes(&r.get(0xE000).unwrap().payload, &[0xAB]);
}
#[test]
fn registry_is_arc_shareable() {
let r1 = GlyphRegistry::new();
let r2 = r1.clone();
r1.register(0xE0A0, glyf(vec![1]), 1000).unwrap();
assert!(r2.contains(0xE0A0));
}
#[test]
fn register_stores_colrv0_payload() {
let r = GlyphRegistry::new();
let colr = vec![0xC0, 0x00, 0x01];
let cpal = vec![0xCA, 0xFE];
r.register(
0xE0A0,
StoredPayload::ColrV0 {
glyphs: vec![vec![0xA], vec![0xB, 0xC]],
colr: colr.clone(),
cpal: cpal.clone(),
},
1024,
)
.unwrap();
match &*r.get(0xE0A0).unwrap().payload {
StoredPayload::ColrV0 {
glyphs,
colr: c,
cpal: p,
} => {
assert_eq!(glyphs.len(), 2);
assert_eq!(c, &colr);
assert_eq!(p, &cpal);
}
other => panic!("expected ColrV0, got {:?}", other),
}
}
#[test]
fn register_stores_colrv1_payload() {
let r = GlyphRegistry::new();
r.register(
0x100000,
StoredPayload::ColrV1 {
glyphs: vec![vec![0xDE, 0xAD]],
colr: vec![0x01; 12],
cpal: vec![],
},
2048,
)
.unwrap();
assert!(matches!(
&*r.get(0x100000).unwrap().payload,
StoredPayload::ColrV1 { .. }
));
}
#[test]
fn overwrite_replaces_payload_across_formats() {
let r = GlyphRegistry::new();
r.register(0xE0A0, glyf(vec![1, 2, 3]), 1000).unwrap();
let idx = r.get(0xE0A0).unwrap().index;
r.register(
0xE0A0,
StoredPayload::ColrV0 {
glyphs: vec![vec![0]],
colr: vec![0x00; 4],
cpal: vec![],
},
1000,
)
.unwrap();
let g = r.get(0xE0A0).unwrap();
assert_eq!(g.index, idx);
assert!(matches!(&*g.payload, StoredPayload::ColrV0 { .. }));
}
}