use core::cmp::Ordering;
use core::mem;
use alloc::boxed::Box;
use alloc::vec;
use nt_string::u16strle::U16StrLe;
use zerocopy::byteorder::LittleEndian;
use zerocopy::{IntoBytes, U16};
use crate::attribute::NtfsAttributeType;
use crate::error::{NtfsError, Result};
use crate::file::KnownNtfsFileRecordNumber;
use crate::io::{Read, Seek};
use crate::ntfs::Ntfs;
use crate::traits::NtfsReadSeek;
const UPCASE_CHARACTER_COUNT: usize = 65536;
const UPCASE_TABLE_SIZE: u64 = (UPCASE_CHARACTER_COUNT * mem::size_of::<u16>()) as u64;
#[derive(Clone, Debug)]
pub(crate) struct UpcaseTable {
uppercase_characters: Box<[U16<LittleEndian>; UPCASE_CHARACTER_COUNT]>,
}
impl UpcaseTable {
pub(crate) fn read<T>(ntfs: &Ntfs, fs: &mut T) -> Result<Self>
where
T: Read + Seek,
{
let upcase_file = ntfs.file(fs, KnownNtfsFileRecordNumber::UpCase as u64)?;
let data_item = upcase_file
.data(fs, "")
.ok_or(NtfsError::AttributeNotFound {
position: upcase_file.position(),
ty: NtfsAttributeType::Data,
})??;
let data_attribute = data_item.to_attribute()?;
if data_attribute.value_length() != UPCASE_TABLE_SIZE {
return Err(NtfsError::InvalidUpcaseTableSize {
expected: UPCASE_TABLE_SIZE,
actual: data_attribute.value_length(),
});
}
let mut data_value = data_attribute.value(fs)?;
let uppercase_characters: Box<[U16<LittleEndian>; UPCASE_CHARACTER_COUNT]> =
vec![U16::new(0); UPCASE_CHARACTER_COUNT]
.into_boxed_slice()
.try_into()
.expect("the UpCase table has a fixed validated length");
let mut uppercase_characters = uppercase_characters;
data_value.read_exact(fs, uppercase_characters.as_mut_bytes())?;
Ok(Self {
uppercase_characters,
})
}
pub(crate) fn u16_to_uppercase(&self, character: u16) -> u16 {
self.uppercase_characters[character as usize].get()
}
}
pub trait UpcaseOrd<Rhs> {
fn upcase_cmp(&self, ntfs: &Ntfs, other: &Rhs) -> Ordering;
}
impl<'a, 'b> UpcaseOrd<U16StrLe<'a>> for U16StrLe<'b> {
fn upcase_cmp(&self, ntfs: &Ntfs, other: &U16StrLe<'a>) -> Ordering {
upcase_cmp_iter(self.u16_iter(), other.u16_iter(), ntfs)
}
}
impl<'a> UpcaseOrd<&str> for U16StrLe<'a> {
fn upcase_cmp(&self, ntfs: &Ntfs, other: &&str) -> Ordering {
upcase_cmp_iter(self.u16_iter(), other.encode_utf16(), ntfs)
}
}
impl<'a> UpcaseOrd<U16StrLe<'a>> for &str {
fn upcase_cmp(&self, ntfs: &Ntfs, other: &U16StrLe<'a>) -> Ordering {
upcase_cmp_iter(self.encode_utf16(), other.u16_iter(), ntfs)
}
}
fn upcase_cmp_iter<TI, OI>(mut this_iter: TI, mut other_iter: OI, ntfs: &Ntfs) -> Ordering
where
TI: Iterator<Item = u16>,
OI: Iterator<Item = u16>,
{
let upcase_table = ntfs.upcase_table();
loop {
match (this_iter.next(), other_iter.next()) {
(Some(this_code_unit), Some(other_code_unit)) => {
let this_upper = upcase_table.u16_to_uppercase(this_code_unit);
let other_upper = upcase_table.u16_to_uppercase(other_code_unit);
if this_upper != other_upper {
return this_upper.cmp(&other_upper);
}
}
(Some(_), None) => {
return Ordering::Greater;
}
(None, Some(_)) => {
return Ordering::Less;
}
(None, None) => {
return Ordering::Equal;
}
}
}
}
pub(crate) fn uppercase_cmp_u16str(
uppercase: &[u16],
other: &U16StrLe<'_>,
ntfs: &Ntfs,
) -> Ordering {
let upcase_table = ntfs.upcase_table();
let mut uppercase_iter = uppercase.iter().copied();
let mut other_iter = other.u16_iter();
loop {
match (uppercase_iter.next(), other_iter.next()) {
(Some(this_upper), Some(other_code_unit)) => {
let other_upper = upcase_table.u16_to_uppercase(other_code_unit);
if this_upper != other_upper {
return this_upper.cmp(&other_upper);
}
}
(Some(_), None) => return Ordering::Greater,
(None, Some(_)) => return Ordering::Less,
(None, None) => return Ordering::Equal,
}
}
}