include!(concat!(env!("OUT_DIR"), "/tier2_table.rs"));
use crate::raw::error::SdmpError;
use crate::raw::table::{ESCAPE, TIER1_DECODE, build_tier1_encode};
use std::collections::HashMap;
use std::sync::LazyLock;
static TIER2_DECODE: LazyLock<Vec<Option<char>>> = LazyLock::new(build_tier2_decode);
static TIER2_ENCODE: LazyLock<HashMap<char, u16>> = LazyLock::new(build_tier2_encode);
pub fn decode(input: &[u8]) -> Result<String, SdmpError> {
let mut output = String::new();
let mut i = 0;
while i < input.len() {
let byte = input[i];
if byte == ESCAPE {
i += 1;
if i >= input.len() {
return Err(SdmpError::UnexpectedEof);
}
output.push(input[i] as char);
i += 1;
continue;
}
if byte < 0x80 {
match TIER1_DECODE[byte as usize] {
Some(c) => output.push(c),
None => return Err(SdmpError::InvalidUtf8),
}
i += 1;
continue;
}
if byte >> 6 == 0b10 {
i += 1;
if i >= input.len() {
return Err(SdmpError::UnexpectedEof);
}
let second = input[i];
let index = u16::from(byte & 0x3F) << 8 | u16::from(second);
match TIER2_DECODE.get(index as usize) {
Some(Some(c)) => output.push(*c),
_ => return Err(SdmpError::InvalidUtf8),
}
i += 1;
continue;
}
if byte >> 6 == 0b11 {
i += 1;
if i + 1 >= input.len() {
return Err(SdmpError::UnexpectedEof);
}
let second = input[i];
let third = input[i + 1];
let index = u32::from(byte & 0x3F) << 16 | u32::from(second) << 8 | u32::from(third);
match char::from_u32(index) {
Some(c) => output.push(c),
None => return Err(SdmpError::InvalidUtf8),
}
i += 2;
continue;
}
return Err(SdmpError::InvalidUtf8);
}
Ok(output)
}
pub fn encode(input: &str) -> Vec<u8> {
let tier1 = build_tier1_encode();
let mut output = Vec::new();
for c in input.chars() {
if let Some(&index) = tier1.get(&c) {
output.push(index);
continue;
}
if let Some(&index) = TIER2_ENCODE.get(&c) {
let high = 0b10000000 | ((index >> 8) & 0x3F) as u8;
let low = (index & 0xFF) as u8;
output.push(high);
output.push(low);
continue;
}
let codepoint = c as u32;
let high = 0b11000000 | ((codepoint >> 16) & 0x3F) as u8;
let mid = ((codepoint >> 8) & 0xFF) as u8;
let low = (codepoint & 0xFF) as u8;
output.push(high);
output.push(mid);
output.push(low);
}
output
}