#![doc = include_str!("../README.md")]
#![no_std]
#[macro_use]
extern crate alloc;
use alloc::{string::String, vec::Vec};
pub const ENC_TABLE: &'static [char; 2048] = &include!("./enc_table.src");
pub const DEC_TABLE: &'static [u16; 4339] = &include!("./dec_table.src");
pub const TAIL: &'static [char; 8] = &['།', '༎', '༏', '༐', '༑', '༆', '༈', '༒'];
pub fn encode(bytes: &[u8]) -> String {
let mut ret = String::new();
let mut stage = 0x0000u16;
let mut remaining = 0;
for byte in bytes {
let byte = *byte as u16;
let need = 11 - remaining;
if need <= 8 {
remaining = 8 - need;
let index = (stage << need) | (byte >> remaining);
ret.push(ENC_TABLE[index as usize]);
stage = byte & ((1 << remaining) - 1);
} else {
stage = (stage << 8) | byte;
remaining += 8;
}
}
if remaining > 0 {
if remaining <= 3 {
ret.push(TAIL[stage as usize]);
} else {
ret.push(ENC_TABLE[stage as usize])
}
}
ret
}
pub fn decode(string: &str) -> Option<Vec<u8>> {
let mut ret = vec![];
let mut remaining = 0u8;
let mut stage = 0x00u32;
let mut chars = string.chars().peekable();
let mut residue = 0;
while let Some(c) = chars.next() {
residue = (residue + 11) % 8;
let (n_new_bits, new_bits) = match DEC_TABLE[c as usize] {
0xFFFF => {
if chars.peek().is_some() {
return None;
}
match TAIL.iter().enumerate().find(|(_, t)| *t == &c) {
Some((index, _)) => {
let need = 8 - remaining;
if index < (1 << need) {
(need, index as u16)
} else {
return None;
}
}
None => return None,
}
}
new_bits => {
if chars.peek().is_none() {
(11 - residue, new_bits)
} else {
(11, new_bits)
}
}
};
remaining += n_new_bits;
stage = (stage << n_new_bits) | new_bits as u32;
while remaining >= 8 {
remaining -= 8;
ret.push((stage >> remaining) as u8);
stage = stage & ((1 << remaining) - 1)
}
}
if remaining > 0 {
ret.push((stage >> (8 - remaining)) as u8)
}
Some(ret)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn encode_decode_roundtrip() {
for tv in &[
vec![],
vec![0],
vec![216, 110, 27, 35, 23, 49, 153, 7, 161, 234, 63],
vec![0b11111111, 0b11111111],
vec![
0b11111000, 0b00011111, 0b00000011, 0b11100000, 0b01111100, 0b00001111, 0b10000001,
0b11110000, 0b00111110, 0b00000111, 0b11000000,
],
vec![
0b11111000, 0b00011111, 0b00000011, 0b11100000, 0b01111100, 0b00001111, 0b10000001,
0b11110000, 0b00111110, 0b00000111, 0b11000000, 0b11111000,
],
vec![0b10101010; 3],
vec![0b10101010; 7],
vec![0b10101010; 10],
vec![0b00000000; 3],
vec![0b00000000; 7],
vec![0b00000000; 10],
vec![0b11111111; 3],
vec![0b11111111; 7],
vec![0b11111111; 10],
vec![0b01010101; 3],
vec![0b01010101; 7],
vec![0b01010101; 10],
vec![0b11110100; 3],
vec![0b11110100; 7],
vec![0b11110100; 10],
vec![0b11110110; 3],
vec![0b11110110; 7],
vec![0b11110110; 10],
vec![0b11110001; 3],
vec![0b11110001; 7],
vec![0b11110001; 10],
] {
let encoded = encode(&tv[..]);
let decoded = decode(&encoded).unwrap();
assert_eq!(tv[..], decoded[..]);
}
}
#[test]
fn wrong_tail_character() {
assert!(decode("ետћζы༎").is_some());
assert!(decode("ետћζы༑").is_none());
assert!(decode("ետћζыX").is_none());
assert!(decode("ետћζы༎X").is_none());
}
}