use alloc::vec::Vec;
use crate::{convert::transcode, encoding::Encoding, utf8::Utf8, utf16::Utf16, utf32::Utf32};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Bom {
None,
Utf8,
Utf16Native,
Utf16Swapped,
Utf32Native,
Utf32Swapped,
}
pub fn detect_bom(data: &[u8]) -> (Bom, usize) {
if data.len() >= 3 && data[..3] == [0xef, 0xbb, 0xbf] {
return (Bom::Utf8, 3);
}
if data.len() >= 4 {
let w = u32::from_ne_bytes(data[..4].try_into().unwrap());
if w == 0xfeff {
return (Bom::Utf32Native, 4);
}
if w == 0xfeff_u32.swap_bytes() {
return (Bom::Utf32Swapped, 4);
}
}
if data.len() >= 2 {
let w = u16::from_ne_bytes(data[..2].try_into().unwrap());
if w == 0xfeff {
return (Bom::Utf16Native, 2);
}
if w == 0xfeff_u16.swap_bytes() {
return (Bom::Utf16Swapped, 2);
}
}
(Bom::None, 0)
}
fn units<const W: usize, U>(data: &[u8], read: impl Fn([u8; W]) -> U) -> Vec<U> {
data.as_chunks::<W>().0.iter().map(|c| read(*c)).collect()
}
pub fn from_bytes<T: Encoding>(data: &[u8]) -> Vec<T::Unit> {
let (bom, skip) = detect_bom(data);
let body = &data[skip..];
match bom {
Bom::None | Bom::Utf8 => transcode::<Utf8, T>(body),
Bom::Utf16Native => transcode::<Utf16<false>, T>(&units(body, u16::from_ne_bytes)),
Bom::Utf16Swapped => transcode::<Utf16<true>, T>(&units(body, u16::from_ne_bytes)),
Bom::Utf32Native => transcode::<Utf32<false>, T>(&units(body, u32::from_ne_bytes)),
Bom::Utf32Swapped => transcode::<Utf32<true>, T>(&units(body, u32::from_ne_bytes)),
}
}