You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

115 lines
3.0 KiB
Rust

//! This crate provides an encoding from byte to uwu and back.
//! Encode:
//! ```
//! use uwucodec::encode;
//!
//! fn main() {
//! println!("Hello World in uwu is: {}", encode("Hello World".as_bytes()));
//! }
//! ```
//!
//! Decode:
//! ```
//! use uwucodec::decode;
//! fn main() {
//! let text = "omo o~o q_p o_o q_p OmO q_p OmO q_p Nya umu uwu o_o u_u q_p Nya u_u umu q_p OmO q_p omo";
//! println!("{} is {:?}", text, decode(text));
//! }
//! ```
use std::cell::RefCell;
use std::collections::HashMap;
static VALUE_MAPPINGS: [&str; 16] = [
"uwu", "owo", "umu", "nya", "omo", "o_o", "q_p", "u_u", "o~o", "UwU", "OwO", "UmU", "OmO",
"O_O", "U_U", "Nya",
];
thread_local! { static WORD_MAP: RefCell<HashMap<&'static str, u8>> = RefCell::new(get_word_map()); }
static SEPARATOR: &str = " ";
/// Encodes into the best encoding in existence
pub fn encode<'a, I: IntoIterator<Item = &'a u8> + 'a>(data: I) -> String {
data.into_iter()
.map(|b| encode_byte(*b))
.flatten()
.collect::<Vec<&'static str>>()
.join(SEPARATOR)
}
fn encode_byte(byte: u8) -> [&'static str; 2] {
[
VALUE_MAPPINGS[(byte >> 4) as usize],
VALUE_MAPPINGS[(byte & 15) as usize],
]
}
/// Decodes the best encoding in existence back into bytes
pub fn decode<S: AsRef<str>>(encoded_data: S) -> Vec<u8> {
let mut data = Vec::new();
let mut words = encoded_data.as_ref().split(SEPARATOR);
loop {
let byte = if let (Some(head), Some(tail)) = (words.next(), words.next()) {
[head, tail]
} else {
break;
};
data.push(decode_byte(byte))
}
data
}
fn decode_byte(enc_byte: [&str; 2]) -> u8 {
(decode_word(enc_byte[0]) << 4) | decode_word(enc_byte[1])
}
fn decode_word(word: &str) -> u8 {
WORD_MAP.with(|m| *m.borrow().get(word).unwrap_or(&0u8))
}
fn get_word_map() -> HashMap<&'static str, u8> {
let mut value_map = HashMap::with_capacity(VALUE_MAPPINGS.len());
for i in 0..VALUE_MAPPINGS.len() {
value_map.insert(VALUE_MAPPINGS[i], i as u8);
}
value_map
}
#[cfg(test)]
mod tests {
use crate::{decode, encode};
#[test]
fn it_encodes() {
let data = vec![0u8, 16u8, 12u8];
let encoded = encode(&data);
assert_eq!(encoded, String::from("uwu uwu owo uwu uwu OmO"))
}
#[test]
fn it_decodes() {
let encoded_data = String::from("uwu uwu owo uwu uwu OmO");
let decoded = decode(encoded_data);
assert_eq!(decoded, vec![0u8, 16u8, 12u8])
}
#[test]
fn it_encodes_100000() {
let data = vec![0u8, 16u8, 12u8];
for _ in 0..100000 {
let encoded = encode(&data);
assert_eq!(encoded, String::from("uwu uwu owo uwu uwu OmO"))
}
}
#[test]
fn it_decodes_100000() {
let encoded_data = String::from("uwu uwu owo uwu uwu OmO");
for _ in 0..100000 {
let decoded = decode(&encoded_data);
assert_eq!(decoded, vec![0u8, 16u8, 12u8])
}
}
}