use alloc::string::String;
pub const FONT_EXTENSIONS: &[&str] = &["ttf", "otf", "ttc", "woff", "woff2", "dfont"];
pub const CONTENT_DEDUP_SAMPLE_BYTES: usize = 4096;
pub fn content_dedup_hash_u64(bytes: &[u8]) -> u64 {
let len = bytes.len();
let head_len = len.min(CONTENT_DEDUP_SAMPLE_BYTES);
let tail_len = (len - head_len).min(CONTENT_DEDUP_SAMPLE_BYTES);
let tail_start = len - tail_len;
let mut seed_buf = [0u8; 8];
seed_buf.copy_from_slice(&(len as u64).to_le_bytes());
let seed = content_hash_u64(&seed_buf);
let head = content_hash_u64(&bytes[..head_len]);
let tail = content_hash_u64(&bytes[tail_start..tail_start + tail_len]);
const K: u64 = 0x9E3779B97F4A7C15;
let mut h = seed;
h ^= head;
h = h.wrapping_mul(K);
h ^= tail;
h = h.wrapping_mul(K);
h ^= h >> 33;
h
}
pub fn content_hash_u64(bytes: &[u8]) -> u64 {
const K: u64 = 0x9E3779B97F4A7C15;
let mut h: u64 = K ^ (bytes.len() as u64);
let chunks = bytes.chunks_exact(8);
let remainder = chunks.remainder();
for chunk in chunks {
let mut arr = [0u8; 8];
arr.copy_from_slice(chunk);
let v = u64::from_le_bytes(arr);
h = h.wrapping_add(v).wrapping_mul(K);
h ^= h >> 33;
}
let mut tail: u64 = 0;
for (i, b) in remainder.iter().enumerate() {
tail |= (*b as u64) << (i * 8);
}
h = h.wrapping_add(tail).wrapping_mul(K);
h ^= h >> 33;
h = h.wrapping_mul(K);
h ^= h >> 33;
h
}
pub fn normalize_family_name(name: &str) -> String {
name.chars()
.filter(|c| c.is_alphanumeric())
.map(|c| c.to_ascii_lowercase())
.collect()
}
#[cfg(feature = "std")]
pub fn is_font_file(path: &std::path::Path) -> bool {
path.extension()
.and_then(|e| e.to_str())
.map(|ext| {
let lower = ext.to_lowercase();
FONT_EXTENSIONS.contains(&lower.as_str())
})
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn font_extensions_covers_common_formats() {
for ext in &["ttf", "otf", "ttc", "woff", "woff2"] {
assert!(FONT_EXTENSIONS.contains(ext), "missing extension: {}", ext);
}
}
#[cfg(feature = "std")]
#[test]
fn is_font_file_recognizes_fonts() {
use std::path::Path;
assert!(is_font_file(Path::new("Arial.ttf")));
assert!(is_font_file(Path::new("NotoSans.otf")));
assert!(is_font_file(Path::new("Font.TTC"))); assert!(is_font_file(Path::new("web.woff2")));
}
#[cfg(feature = "std")]
#[test]
fn is_font_file_rejects_non_fonts() {
use std::path::Path;
assert!(!is_font_file(Path::new("readme.txt")));
assert!(!is_font_file(Path::new("image.png")));
assert!(!is_font_file(Path::new("no_extension")));
}
}