use alloc::{string::String, vec, vec::Vec};
use super::{
bisync,
boot_sector::VolumeFlags,
error::ExFatError,
io::{BLOCK_SIZE, BlockDevice},
upcase_table::UpcaseTable,
};
pub(crate) fn _chunk_at<const N: usize, T>(slice: &[T], index: usize) -> Option<&[T; N]> {
let start = index.checked_mul(N)?;
let end = start.checked_add(N)?;
slice.get(start..end)?.try_into().ok()
}
pub(crate) fn read_u16_le<const INDEX: usize, const N: usize>(value: &[u8; N]) -> u16 {
let mut tmp = [0u8; size_of::<u16>()];
tmp.copy_from_slice(&value[INDEX..INDEX + size_of::<u16>()]);
u16::from_le_bytes(tmp)
}
pub(crate) fn read_u32_le<const INDEX: usize, const N: usize>(value: &[u8; N]) -> u32 {
let mut tmp = [0u8; size_of::<u32>()];
tmp.copy_from_slice(&value[INDEX..INDEX + size_of::<u32>()]);
u32::from_le_bytes(tmp)
}
pub(crate) fn read_u64_le<const INDEX: usize, const N: usize>(value: &[u8; N]) -> u64 {
let mut tmp = [0u8; size_of::<u64>()];
tmp.copy_from_slice(&value[INDEX..INDEX + size_of::<u64>()]);
u64::from_le_bytes(tmp)
}
pub(crate) fn calc_dir_entry_set_len(name: &[u16]) -> usize {
2 + (name.len() as u32).div_ceil(15) as usize
}
pub(crate) fn encode_utf16_and_hash(s: &str, upcase_table: &UpcaseTable) -> (Vec<u16>, u16) {
let mut file_name: Vec<u16> = s.encode_utf16().collect();
for c in file_name.iter_mut() {
*c = upcase_table.upcase(*c)
}
let file_name_hash = calc_hash_u16(file_name.as_slice());
let file_name: Vec<u16> = s.encode_utf16().collect();
(file_name, file_name_hash)
}
pub(crate) fn encode_utf16_upcase_and_hash(s: &str, upcase_table: &UpcaseTable) -> (Vec<u16>, u16) {
let mut file_name: Vec<u16> = s.encode_utf16().collect();
for c in file_name.iter_mut() {
*c = upcase_table.upcase(*c)
}
let file_name_hash = calc_hash_u16(file_name.as_slice());
(file_name, file_name_hash)
}
pub(crate) fn calc_hash_u16(utf16_file_name: &[u16]) -> u16 {
let mut hash = 0u16;
for byte in utf16_file_name
.iter()
.flat_map(|x| vec![(x & 0xFF) as u8, (x >> 8) as u8])
{
hash = if hash & 1 > 0 { 0x8000 } else { 0 } + hash.wrapping_shr(1) + byte as u16;
}
hash
}
pub(crate) fn _decode_utf16<D: BlockDevice>(buf: Vec<u16>) -> Result<String, ExFatError<D>> {
let decoded = core::char::decode_utf16(buf)
.map(|r| {
r.map_err(|_| ExFatError::InvalidUtf16String {
reason: "invalid u16 char detected",
})
})
.collect::<Result<String, ExFatError<D>>>()?;
Ok(decoded)
}
pub(crate) fn split_path(path: &str) -> (&str, &str) {
path.rfind(['/', '\\']).map_or(("", path.trim()), |index| {
(path[..index].trim(), path[index + 1..].trim())
})
}
#[bisync]
pub async fn set_volume_dirty<D: BlockDevice>(
io: &mut D,
is_dirty: bool,
) -> Result<(), ExFatError<D>> {
let sector_id = 0; let mut block = [0u8; BLOCK_SIZE];
io.read(sector_id, &mut block)
.await
.map_err(ExFatError::Io)?;
let mut volume_flags = VolumeFlags::from_bits_truncate(read_u16_le::<106, _>(&block));
volume_flags.set(VolumeFlags::VolumeDirty, is_dirty);
block[106..108].copy_from_slice(&volume_flags.bits().to_le_bytes());
io.write(sector_id, &block).await.map_err(ExFatError::Io)?;
Ok(())
}
pub(crate) fn decode_utf16<D: BlockDevice>(buf: Vec<u16>) -> Result<String, ExFatError<D>> {
let decoded = core::char::decode_utf16(buf)
.map(|r| {
r.map_err(|_| ExFatError::InvalidUtf16String {
reason: "invalid u16 char detected",
})
})
.collect::<Result<String, ExFatError<D>>>()?;
Ok(decoded)
}