use crate::to_bytes::ToBytes;
use std::{
hint::unreachable_unchecked,
str::{self, Utf8Error},
};
pub fn check_separator(separator: &str) -> bool {
if separator.is_empty() {
return false;
}
for c in separator.chars() {
if matches!(
c,
'a' | 'n' | 'y' | 'w' | 'A' | 'N' | 'Y' | 'W' | '*' | '\\'
) {
return false;
}
}
true
}
pub fn encode<T: ToBytes, S: AsRef<str>>(input: T, separator: S) -> String {
encode_escape(input, separator, false)
}
pub fn encode_escaped<T: ToBytes, S: AsRef<str>>(input: T, separator: S) -> String {
encode_escape(input, separator, true)
}
pub fn encode_escape<T: ToBytes, S: AsRef<str>>(input: T, separator: S, escape: bool) -> String {
let separator = separator.as_ref();
let separator = match check_separator(separator) {
true => separator,
false => ", ",
};
let data = input.to_bytes();
let mut ret = String::with_capacity(data.len() * 6);
for val in data {
let tail = val & 0b11;
let body = (val & 0b11111100) >> 2;
let fix = match tail {
0 => "",
1 => "*",
2 => "**",
3 => "***",
_ => unsafe { unreachable_unchecked() },
};
let fix2 = match tail {
0 => "",
1 => "\\*",
2 => "\\*\\*",
3 => "\\*\\*\\*",
_ => unsafe { unreachable_unchecked() },
};
let line: &mut [u8] = &mut [0; 6];
line[0] = b'A' + (32 * (body & 1));
line[1] = b'N' + (32 * ((body >> 1) & 1));
line[2] = b'Y' + (32 * ((body >> 2) & 1));
line[3] = b'W' + (32 * ((body >> 3) & 1));
line[4] = b'A' + (32 * ((body >> 4) & 1));
line[5] = b'Y' + (32 * ((body >> 5) & 1));
let line_str: &str = unsafe { str::from_utf8_unchecked(line) };
if escape {
ret.push_str(fix2);
ret.push_str(fix);
ret.push_str(line_str);
ret.push_str(fix);
ret.push_str(fix2);
} else {
ret.push_str(fix);
ret.push_str(line_str);
ret.push_str(fix);
}
ret.push_str(separator);
}
for _ in 0..separator.len() {
ret.pop();
}
ret
}
pub fn decode_to_string(text: &str) -> Result<String, (Utf8Error, Vec<u8>)> {
let vec = decode(text);
let string = str::from_utf8(&vec);
match string {
Ok(_) => unsafe { Ok(String::from_utf8_unchecked(vec)) },
Err(e) => Err((e, vec)),
}
}
pub fn decode(text: &str) -> Vec<u8> {
let data = text.as_bytes();
let mut body_idx: u8 = 0;
let mut body: u8 = 0;
let mut stars: u8 = 0;
let mut ret = Vec::new();
let mut idx: usize = 0;
while idx < data.len() {
while idx < data.len()
&& !matches!(
data[idx],
b'a' | b'n' | b'y' | b'w' | b'A' | b'N' | b'Y' | b'W' | b'*' | b'\\'
)
{
idx += 1
}
if idx == data.len() {
break;
}
let val = data[idx];
match val {
b'\\' => {
idx += 2;
continue;
}
b'*' => stars += 1,
96.. => {
body += 1 << body_idx;
body_idx += 1;
}
..=95 => body_idx += 1,
}
#[allow(unused_assignments)]
if body_idx == 6 {
body_idx = 0;
stars %= 4;
let value = (body << 2) + stars;
ret.push(value);
while idx < data.len()
&& matches!(
data[idx],
b'a' | b'n' | b'y' | b'w' | b'A' | b'N' | b'Y' | b'W' | b'*' | b'\\'
)
{
idx += 1
}
body = 0;
stars = 0;
continue;
}
idx += 1;
}
ret
}