#[derive(Debug, Clone, Default)]
pub struct Upcase {
table: Vec<u16>,
}
impl Upcase {
pub fn ascii() -> Self {
let mut table = vec![0u16; 0x80];
for (i, slot) in table.iter_mut().enumerate() {
let c = i as u8;
*slot = if c.is_ascii_lowercase() {
(c - b'a' + b'A') as u16
} else {
c as u16
};
}
Self { table }
}
pub fn decode(bytes: &[u8], data_length: u64) -> crate::Result<Self> {
let data_length = data_length as usize;
if data_length > bytes.len() {
return Err(crate::Error::InvalidImage(format!(
"exfat: upcase table DataLength {data_length} exceeds cluster bytes {}",
bytes.len()
)));
}
if data_length % 2 != 0 {
return Err(crate::Error::InvalidImage(
"exfat: upcase table DataLength is not a multiple of 2".into(),
));
}
let slice = &bytes[..data_length];
let mut table: Vec<u16> = Vec::new();
let mut i = 0usize;
while i + 2 <= slice.len() {
let v = u16::from_le_bytes(slice[i..i + 2].try_into().unwrap());
i += 2;
if v == 0xFFFF {
if i + 2 > slice.len() {
return Err(crate::Error::InvalidImage(
"exfat: upcase table truncated after 0xFFFF escape".into(),
));
}
let count = u16::from_le_bytes(slice[i..i + 2].try_into().unwrap()) as usize;
i += 2;
let base = table.len();
if base + count > 0x11_0000 {
return Err(crate::Error::InvalidImage(
"exfat: upcase table exceeds 0x110000 entries".into(),
));
}
for k in 0..count {
table.push((base + k) as u16);
}
} else {
table.push(v);
}
}
Ok(Self { table })
}
pub fn up(&self, ch: u16) -> u16 {
self.table.get(ch as usize).copied().unwrap_or(ch)
}
pub fn up_slice(&self, units: &[u16]) -> Vec<u16> {
units.iter().map(|&u| self.up(u)).collect()
}
pub fn up_str(&self, s: &str) -> Vec<u16> {
let units: Vec<u16> = s.encode_utf16().collect();
self.up_slice(&units)
}
pub fn eq_ignore_case(&self, a: &[u16], b: &[u16]) -> bool {
if a.len() != b.len() {
return false;
}
a.iter()
.zip(b.iter())
.all(|(&x, &y)| self.up(x) == self.up(y))
}
pub fn len(&self) -> usize {
self.table.len()
}
pub fn is_empty(&self) -> bool {
self.table.is_empty()
}
}
pub fn table_checksum(bytes: &[u8]) -> u32 {
let mut sum: u32 = 0;
for &b in bytes {
sum = sum.rotate_right(1).wrapping_add(b as u32);
}
sum
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ascii_upcase_basic() {
let uc = Upcase::ascii();
assert_eq!(uc.up(b'a' as u16), b'A' as u16);
assert_eq!(uc.up(b'z' as u16), b'Z' as u16);
assert_eq!(uc.up(b'A' as u16), b'A' as u16);
assert_eq!(uc.up(b'0' as u16), b'0' as u16);
assert_eq!(uc.up(0x1234), 0x1234);
}
#[test]
fn ascii_upcase_str() {
let uc = Upcase::ascii();
let folded = uc.up_str("Hello.txt");
let expected: Vec<u16> = "HELLO.TXT".encode_utf16().collect();
assert_eq!(folded, expected);
}
#[test]
fn ascii_eq_ignore_case() {
let uc = Upcase::ascii();
let a: Vec<u16> = "ReadMe".encode_utf16().collect();
let b: Vec<u16> = "README".encode_utf16().collect();
assert!(uc.eq_ignore_case(&a, &b));
let c: Vec<u16> = "README!".encode_utf16().collect();
assert!(!uc.eq_ignore_case(&a, &c));
}
#[test]
fn decode_simple_uncompressed() {
let mut bytes = Vec::new();
for v in [b'A' as u16, b'B' as u16, b'C' as u16] {
bytes.extend_from_slice(&v.to_le_bytes());
}
let uc = Upcase::decode(&bytes, bytes.len() as u64).unwrap();
assert_eq!(uc.len(), 3);
assert_eq!(uc.up(0), b'A' as u16);
assert_eq!(uc.up(1), b'B' as u16);
assert_eq!(uc.up(2), b'C' as u16);
}
#[test]
fn decode_with_identity_run() {
let mut bytes = Vec::new();
bytes.extend_from_slice(&(b'A' as u16).to_le_bytes());
bytes.extend_from_slice(&(b'B' as u16).to_le_bytes());
bytes.extend_from_slice(&0xFFFFu16.to_le_bytes());
bytes.extend_from_slice(&5u16.to_le_bytes());
bytes.extend_from_slice(&0xABCDu16.to_le_bytes());
let uc = Upcase::decode(&bytes, bytes.len() as u64).unwrap();
assert_eq!(uc.len(), 8);
assert_eq!(uc.up(0), b'A' as u16);
assert_eq!(uc.up(1), b'B' as u16);
assert_eq!(uc.up(2), 2);
assert_eq!(uc.up(6), 6);
assert_eq!(uc.up(7), 0xABCD);
}
#[test]
fn checksum_known() {
assert_eq!(table_checksum(&[]), 0);
assert_eq!(table_checksum(&[0x01]), 1);
assert_eq!(table_checksum(&[0x01, 0x02]), 0x8000_0002);
}
}