static BLOB: &[u8] = include_bytes!("../tables/cmaps.bin");
#[cfg(test)]
const MAGIC: u32 = 0x504D_4331;
const ENTRY_LEN: usize = 20;
const DIR_LEN: usize = 8;
pub(crate) const REGISTRY_COUNT: usize = 4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Entry {
pub(crate) name_off: u32,
pub(crate) word_off: u32,
pub(crate) dword_off: u32,
pub(crate) word_count: u16,
pub(crate) dword_count: u16,
pub(crate) is_range: bool,
pub(crate) use_offset: i8,
}
fn u16_at(bytes: &[u8], at: usize) -> Option<u16> {
let end = at.checked_add(2)?;
Some(u16::from_le_bytes([*bytes.get(at)?, *bytes.get(end - 1)?]))
}
fn u32_at(bytes: &[u8], at: usize) -> Option<u32> {
let s = bytes.get(at..at.checked_add(4)?)?;
Some(u32::from_le_bytes([
*s.first()?,
*s.get(1)?,
*s.get(2)?,
*s.get(3)?,
]))
}
fn header_u32(slot: usize) -> Option<u32> {
u32_at(BLOB, 8usize.checked_add(slot.checked_mul(4)?)?)
}
#[cfg(test)]
pub(crate) fn header_is_valid() -> bool {
u32_at(BLOB, 0) == Some(MAGIC)
&& u16_at(BLOB, 4) == Some(1)
&& u16_at(BLOB, 6) == Some(REGISTRY_COUNT as u16)
&& header_u32(4) == Some(BLOB.len() as u32)
}
#[cfg(test)]
pub(crate) fn total_len() -> usize {
BLOB.len()
}
fn index_off() -> Option<usize> {
header_u32(0).map(|v| v as usize)
}
fn words_off() -> Option<usize> {
header_u32(1).map(|v| v as usize)
}
fn dwords_off() -> Option<usize> {
header_u32(2).map(|v| v as usize)
}
fn cid2uni_off() -> Option<usize> {
header_u32(3).map(|v| v as usize)
}
fn names_off() -> Option<usize> {
header_u32(5).map(|v| v as usize)
}
pub(crate) fn entry_count(reg: usize) -> usize {
let at = match index_off() {
Some(v) => v + reg * DIR_LEN,
None => return 0,
};
if reg >= REGISTRY_COUNT {
return 0;
}
u16_at(BLOB, at + 4).unwrap_or(0) as usize
}
pub(crate) fn entry(reg: usize, i: usize) -> Option<Entry> {
if reg >= REGISTRY_COUNT || i >= entry_count(reg) {
return None;
}
let dir = index_off()?.checked_add(reg.checked_mul(DIR_LEN)?)?;
let base = u32_at(BLOB, dir)? as usize;
let at = base.checked_add(i.checked_mul(ENTRY_LEN)?)?;
Some(Entry {
name_off: u32_at(BLOB, at)?,
word_off: u32_at(BLOB, at.checked_add(4)?)?,
dword_off: u32_at(BLOB, at.checked_add(8)?)?,
word_count: u16_at(BLOB, at.checked_add(12)?)?,
dword_count: u16_at(BLOB, at.checked_add(14)?)?,
is_range: *BLOB.get(at.checked_add(16)?)? != 0,
use_offset: (*BLOB.get(at.checked_add(17)?)?).cast_signed(),
})
}
pub(crate) fn name(entry: &Entry) -> Option<&'static [u8]> {
let at = names_off()?.checked_add(entry.name_off as usize)?;
let len = *BLOB.get(at)? as usize;
BLOB.get(at.checked_add(1)?..at.checked_add(1)?.checked_add(len)?)
}
fn word_bytes(entry: &Entry) -> Option<&'static [u8]> {
let stride = if entry.is_range { 6usize } else { 4 };
let len = usize::from(entry.word_count).checked_mul(stride)?;
let at = words_off()?.checked_add(entry.word_off as usize)?;
BLOB.get(at..at.checked_add(len)?)
}
pub(crate) fn word_record(entry: &Entry, i: usize) -> Option<(u16, u16, u16)> {
let bytes = word_bytes(entry)?;
if entry.is_range {
let at = i.checked_mul(6)?;
Some((
u16_at(bytes, at)?,
u16_at(bytes, at.checked_add(2)?)?,
u16_at(bytes, at.checked_add(4)?)?,
))
} else {
let at = i.checked_mul(4)?;
let code = u16_at(bytes, at)?;
Some((code, code, u16_at(bytes, at.checked_add(2)?)?))
}
}
pub(crate) fn dword_record(entry: &Entry, i: usize) -> Option<(u16, u16, u16, u16)> {
if entry.dword_off == u32::MAX {
return None;
}
let len = usize::from(entry.dword_count).checked_mul(8)?;
let base = dwords_off()?.checked_add(entry.dword_off as usize)?;
let bytes = BLOB.get(base..base.checked_add(len)?)?;
let at = i.checked_mul(8)?;
Some((
u16_at(bytes, at)?,
u16_at(bytes, at.checked_add(2)?)?,
u16_at(bytes, at.checked_add(4)?)?,
u16_at(bytes, at.checked_add(6)?)?,
))
}
pub(crate) fn cid2unicode_len(reg: usize) -> usize {
if reg >= REGISTRY_COUNT {
return 0;
}
let Some(at) = cid2uni_off() else { return 0 };
u32_at(BLOB, at + reg * DIR_LEN + 4).unwrap_or(0) as usize
}
pub(crate) fn cid2unicode(reg: usize, cid: u16) -> Option<u16> {
if reg >= REGISTRY_COUNT {
return None;
}
let dir = cid2uni_off()?.checked_add(reg.checked_mul(DIR_LEN)?)?;
let base = u32_at(BLOB, dir)? as usize;
let len = u32_at(BLOB, dir.checked_add(4)?)? as usize;
let i = usize::from(cid);
if i >= len {
return None;
}
u16_at(BLOB, base.checked_add(i.checked_mul(2)?)?)
}
#[cfg(test)]
pub(crate) fn word_arrays_identical(a: &Entry, b: &Entry) -> bool {
a.is_range == b.is_range && a.word_count == b.word_count && word_bytes(a) == word_bytes(b)
}
#[cfg(test)]
mod tests {
use super::{
Entry, REGISTRY_COUNT, cid2unicode, cid2unicode_len, entry, entry_count, header_is_valid,
name, total_len, word_arrays_identical, word_record,
};
const EXPECT: [(usize, usize); REGISTRY_COUNT] =
[(14, 30284), (14, 19088), (20, 15444), (11, 18352)];
fn find(reg: usize, want: &[u8]) -> Entry {
(0..entry_count(reg))
.filter_map(|i| entry(reg, i))
.find(|e| name(e) == Some(want))
.unwrap()
}
#[test]
fn header_matches_the_generator() {
assert!(header_is_valid());
assert_eq!(total_len(), 630_716);
}
#[test]
fn registry_shapes_match_the_tabulated_counts() {
for (reg, (entries, uni)) in EXPECT.iter().copied().enumerate() {
assert_eq!(entry_count(reg), entries, "registry {reg} entry count");
assert_eq!(cid2unicode_len(reg), uni, "registry {reg} CID2Unicode len");
assert_eq!(cid2unicode(reg, 0), Some(0xFFFD), "registry {reg} CID 0");
}
assert_eq!(entry_count(REGISTRY_COUNT), 0);
assert_eq!(entry(REGISTRY_COUNT, 0), None);
assert_eq!(cid2unicode(REGISTRY_COUNT, 0), None);
}
#[test]
fn every_chain_terminates() {
for reg in 0..REGISTRY_COUNT {
let n = entry_count(reg);
for start in 0..n {
let mut at = start;
let mut links = 0usize;
loop {
let e = entry(reg, at).expect("chain link in bounds");
assert!(e.word_count > 0 || e.is_range, "link has a word array");
links += 1;
assert!(
links <= 8,
"registry {reg} chain from {start} exceeds 8 links"
);
if e.use_offset == 0 {
break;
}
let next = isize::try_from(at).unwrap() + isize::from(e.use_offset);
let next = usize::try_from(next).expect("chain stays in bounds");
assert!(next < n, "chain leaves the table");
at = next;
}
}
}
}
#[test]
fn names_are_unique_within_a_registry() {
for reg in 0..REGISTRY_COUNT {
let mut seen: Vec<&[u8]> = (0..entry_count(reg))
.filter_map(|i| entry(reg, i))
.filter_map(|e| name(&e))
.collect();
assert_eq!(seen.len(), entry_count(reg));
seen.sort_unstable();
let before = seen.len();
seen.dedup();
assert_eq!(before, seen.len(), "registry {reg} has a duplicate name");
}
}
#[test]
fn utf16_rows_alias_their_ucs2_arrays() {
for (reg, a, b) in [
(0usize, &b"UniGB-UTF16-H"[..], &b"UniGB-UCS2-H"[..]),
(0, b"UniGB-UTF16-V", b"UniGB-UCS2-V"),
(1, b"UniCNS-UTF16-V", b"UniCNS-UCS2-V"),
(2, b"UniJIS-UTF16-H", b"UniJIS-UCS2-H"),
(2, b"UniJIS-UTF16-V", b"UniJIS-UCS2-V"),
(3, b"UniKS-UTF16-V", b"UniKS-UCS2-V"),
] {
let (x, y) = (find(reg, a), find(reg, b));
assert_eq!(x.word_off, y.word_off, "{a:?} and {b:?} word offsets");
assert!(word_arrays_identical(&x, &y), "{a:?} and {b:?} bytes");
}
}
#[test]
fn word_records_are_sorted_on_the_search_key() {
for reg in 0..REGISTRY_COUNT {
for i in 0..entry_count(reg) {
let e = entry(reg, i).unwrap();
let mut prev = None;
for r in 0..usize::from(e.word_count) {
let (low, high, _) = word_record(&e, r).expect("record in bounds");
assert!(
low <= high,
"registry {reg} entry {i} record {r} low > high"
);
if let Some(p) = prev {
assert!(p <= high, "registry {reg} entry {i} record {r} unsorted");
}
prev = Some(high);
}
assert_eq!(word_record(&e, usize::from(e.word_count)), None);
}
}
}
#[test]
fn dword_records_are_sorted_and_only_three_entries_have_them() {
let mut with_dwords = Vec::new();
for reg in 0..REGISTRY_COUNT {
for i in 0..entry_count(reg) {
let e = entry(reg, i).unwrap();
if e.dword_count == 0 {
assert_eq!(super::dword_record(&e, 0), None, "no dwords means no table");
continue;
}
with_dwords.push((name(&e).unwrap(), e.dword_count));
let mut prev = None;
for r in 0..usize::from(e.dword_count) {
let (hi, lo_low, lo_high, _) =
super::dword_record(&e, r).expect("dword record in bounds");
assert!(lo_low <= lo_high);
if let Some(p) = prev {
assert!(p <= (hi, lo_high), "dword record {r} unsorted");
}
prev = Some((hi, lo_high));
}
}
}
assert_eq!(
with_dwords,
vec![
(&b"GBK2K-H"[..], 1017),
(&b"CNS-EUC-H"[..], 238),
(&b"CNS-EUC-V"[..], 261),
]
);
}
#[test]
fn gb_euc_h_head_matches_the_source_array() {
let e = find(0, b"GB-EUC-H");
assert!(e.is_range);
assert_eq!(e.word_count, 90);
assert_eq!(word_record(&e, 0), Some((0x0020, 0x0020, 0x1E24)));
assert_eq!(word_record(&e, 1), Some((0x0021, 0x007E, 0x032E)));
assert_eq!(word_record(&e, 2), Some((0xA1A1, 0xA1FE, 0x0060)));
}
#[test]
fn single_tables_read_as_one_wide_ranges() {
let e = find(1, b"UniCNS-UTF16-H");
assert!(!e.is_range);
assert_eq!(e.word_count, 14557);
let (low, high, _) = word_record(&e, 0).unwrap();
assert_eq!(low, high);
}
#[test]
fn cid2unicode_is_bounded_by_the_table_length() {
let len = cid2unicode_len(2);
assert!(cid2unicode(2, u16::try_from(len - 1).unwrap()).is_some());
assert_eq!(cid2unicode(2, u16::try_from(len).unwrap()), None);
}
}