use std::io::{BufWriter, Read, Write};
#[inline(always)] unsafe fn morse_to_binary_fast(bytes: *const u8, len: usize) -> u8 {
let a = unsafe { (bytes as *const u64).read_unaligned() };
let b = 0x0101010101010101;
let a = a & b;
let a = a.wrapping_mul(0x102040810204080) >> 56;
let a = a & !(0xff << len);
let a = a | (b << len);
a as u8
}
#[test]
fn test_morse_to_binary_fast() {
unsafe {
assert_eq!(morse_to_binary_fast(b"________".as_ptr(), 0), 1);
assert_eq!(morse_to_binary_fast(b"..._____".as_ptr(), 0), 1);
assert_eq!(morse_to_binary_fast(b"---_____".as_ptr(), 0), 1);
assert_eq!(morse_to_binary_fast(b"._______".as_ptr(), 1), 0b10);
assert_eq!(morse_to_binary_fast(b"-_______".as_ptr(), 1), 0b11);
assert_eq!(morse_to_binary_fast(b"..-.____".as_ptr(), 4), 0b10100);
}
}
fn morse_to_binary_safe(bytes: &[u8], len: usize) -> u8 {
let mut ret = 1;
for byte in bytes[..len].iter().rev() {
ret *= 2;
ret |= byte & 1;
}
ret
}
#[test]
fn test_morse_to_binary_safe() {
assert_eq!(morse_to_binary_safe(b"", 0), 1);
assert_eq!(morse_to_binary_safe(b"...", 0), 1);
assert_eq!(morse_to_binary_safe(b"---", 0), 1);
assert_eq!(morse_to_binary_safe(b".", 1), 0b10);
assert_eq!(morse_to_binary_safe(b"-", 1), 0b11);
assert_eq!(morse_to_binary_safe(b"..-.", 4), 0b10100);
}
fn morse_to_binary(bytes: &[u8], len: usize) -> u8 {
if len + 8 <= bytes.len() {
unsafe { morse_to_binary_fast(bytes.as_ptr(), len) }
} else {
morse_to_binary_safe(bytes, len)
}
}
fn decode_buffer(
output: &mut impl Write,
input: &[u8],
char_decode: fn(u8) -> char,
output_buf: &mut [char; 1 << 15],
) -> Result<usize, std::io::Error> {
let mut cur = 0;
let mut chunk_start = 0;
let last_seven_bytes = input.len().saturating_sub(7);
for i in 0..last_seven_bytes {
let c = input[i];
if c <= b' ' {
let binary =
unsafe { morse_to_binary_fast(input.as_ptr().add(chunk_start), i - chunk_start) };
let decoded = char_decode(binary);
if decoded != '\0' {
output_buf[cur] = decoded;
cur += 1;
}
chunk_start = i + 1;
if c != b' ' {
output_buf[cur] = c as char;
cur += 1;
}
} else if c == b'/' {
output_buf[cur] = ' ';
cur += 1;
chunk_start = i + 1;
}
if cur > output_buf.len() - 2 {
let decoded: String = output_buf[..cur].iter().collect();
output.write_all(decoded.as_bytes())?;
cur = 0;
}
}
for i in last_seven_bytes..input.len() {
let c = input[i];
if c <= b' ' {
let binary = morse_to_binary(&input[chunk_start..], i - chunk_start);
let decoded = char_decode(binary);
if decoded != '\0' {
output_buf[cur] = decoded;
cur += 1;
}
chunk_start = i + 1;
if c != b' ' {
output_buf[cur] = c as char;
cur += 1;
}
} else if c == b'/' {
output_buf[cur] = ' ';
cur += 1;
chunk_start = i + 1;
}
}
if cur > 0 {
let decoded: String = output_buf[..cur].iter().collect();
output.write_all(decoded.as_bytes())?;
}
Ok(chunk_start)
}
fn decode_buffer_end(
output: &mut impl Write,
input: &[u8],
char_decode: fn(u8) -> char,
) -> Result<(), std::io::Error> {
let mut output_buf = ['\0'; 1 << 15];
let chunk_start = decode_buffer(output, input, char_decode, &mut output_buf)?;
let binary = morse_to_binary(&input[chunk_start..], input.len() - chunk_start);
let decoded = char_decode(binary);
if decoded != '\0' {
output.write_all(decoded.to_string().as_bytes())?;
}
Ok(())
}
pub fn decode_string(input: &[u8], char_decode: fn(u8) -> char) -> String {
let mut writer = BufWriter::new(Vec::new());
decode_buffer_end(&mut writer, input, char_decode).unwrap();
let vec = writer.into_inner().unwrap();
String::from_utf8(vec).unwrap()
}
pub fn decode_stream(input: &mut impl Read, output: &mut impl Write, char_decode: fn(u8) -> char) {
let mut input_buf = vec![0u8; 1 << 15];
let mut bytes_available = 0;
let mut output_buf = ['\0'; 1 << 15];
loop {
let bytes_read = input.read(&mut input_buf[bytes_available..]).unwrap();
if bytes_read == 0 {
break;
}
bytes_available += bytes_read;
let bytes_used = decode_buffer(
output,
&input_buf[..bytes_available],
char_decode,
&mut output_buf,
)
.unwrap();
input_buf.copy_within(bytes_used..bytes_available, 0);
bytes_available -= bytes_used;
}
if bytes_available != 0 {
decode_buffer_end(output, &input_buf[..bytes_available], char_decode).unwrap();
}
}
#[test]
fn test_standard_decode() {
use crate::decode_mapping::to_standard;
let f = |s| decode_string(s, to_standard);
assert_eq!(f(b".--. .- .-. .. ..."), "PARIS");
assert_eq!(
f(b".... . .-.. .-.. --- --..-- / .-- --- .-. .-.. -.. ..--."),
"HELLO, WORLD!",
);
}