use crate::engine::Engine;
pub struct Mixed816LEEngine {}
impl Engine for Mixed816LEEngine
{
fn get_name(&self) -> String { "mixed UTF-8/UTF-16LE".to_string() }
fn encode(&self, string: &str) -> Option<Vec<u8>>
{
let mut encoded = Vec::new();
for c in string.chars()
{
if c.is_ascii()
{
encoded.push(c as u8);
}
else
{
let mut buf = [0u16; 2];
let _ = c.encode_utf16(&mut buf);
for i in 0..c.len_utf16()
{
encoded.extend_from_slice(&buf[i].to_le_bytes());
}
}
}
Some(encoded)
}
fn decode(&self, bytes: &[u8]) -> String
{
let mut decoded = String::new();
let mut i = 0;
while i < bytes.len()
{
if bytes[i].is_ascii()
{
decoded.push(bytes[i] as char);
i += 1;
}
else
{
if i + 1 == bytes.len()
{
decoded.push(char::REPLACEMENT_CHARACTER);
i += 1;
continue;
}
let unit1 =
u16::from_le_bytes(bytes[i..i+2].try_into().unwrap());
if unit1 < 0xD800 || 0xE000 <= unit1
{
decoded.extend(
char::decode_utf16([unit1])
.map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)));
i += 2;
}
else
{
if bytes.len() <= i + 3
{
decoded.push(char::REPLACEMENT_CHARACTER);
i += 2;
continue;
}
let unit2 =
u16::from_le_bytes(bytes[i+2..i+4].try_into().unwrap());
decoded.extend(
char::decode_utf16([unit1, unit2])
.map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)));
i += 4;
}
}
}
decoded
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode() {
let engine = Mixed816LEEngine{};
let encoded = engine.encode("Hello").unwrap();
assert_eq!(encoded, b"Hello");
let encoded = engine.encode("é").unwrap();
assert_eq!(encoded, &[0xe9, 0x00]);
let encoded = engine.encode("😀").unwrap();
assert_eq!(encoded, &[0x3d, 0xd8, 0x00, 0xde])
}
#[test]
fn decode()
{
let engine = Mixed816LEEngine{};
let decoded = engine.decode(&[0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21]);
assert_eq!(decoded, "world!");
let decoded = engine.decode(&[0xe8, 0x00]);
assert_eq!(decoded, "è");
let decoded = engine.decode(&[0xa4, 0x20]);
assert_eq!(decoded, "₤");
}
}