use super::tables::{
DIGIT, LOWER, MIXED, PUNCT, UPPER, char_for_code, code_for_char, mode_bits, punct_pair_for_code,
};
#[derive(Default)]
pub struct BitVec {
bits: Vec<bool>,
}
impl BitVec {
fn new() -> Self {
BitVec::default()
}
pub fn push(&mut self, value: u32, len: usize) {
for k in (0..len).rev() {
self.bits.push((value >> k) & 1 != 0);
}
}
pub fn into_bits(self) -> Vec<bool> {
self.bits
}
}
fn is_binary(b: u8) -> bool {
code_for_char(UPPER, b).is_none()
&& code_for_char(LOWER, b).is_none()
&& code_for_char(MIXED, b).is_none()
&& code_for_char(PUNCT, b).is_none()
&& code_for_char(DIGIT, b).is_none()
}
const LATCH_EDGES: &[(usize, usize, u32, usize)] = &[
(UPPER, LOWER, 28, 5),
(UPPER, MIXED, 29, 5),
(UPPER, DIGIT, 30, 5),
(LOWER, DIGIT, 30, 5),
(LOWER, MIXED, 29, 5),
(MIXED, UPPER, 29, 5),
(DIGIT, UPPER, 14, 4),
];
fn latch_path(from: usize, to: usize) -> Vec<(u32, usize)> {
if from == to {
return Vec::new();
}
let mut prev: [Option<(usize, u32, usize)>; 5] = [None; 5];
let mut visited = [false; 5];
let mut queue = std::collections::VecDeque::new();
queue.push_back(from);
visited[from] = true;
while let Some(node) = queue.pop_front() {
if node == to {
break;
}
for &(f, t, code, bits) in LATCH_EDGES {
if f == node && !visited[t] {
visited[t] = true;
prev[t] = Some((node, code, bits));
queue.push_back(t);
}
}
}
let mut path = Vec::new();
let mut cur = to;
while cur != from {
let (p, code, bits) = prev[cur].expect("modes are fully connected");
path.push((code, bits));
cur = p;
}
path.reverse();
path
}
fn emit_latch(out: &mut BitVec, mode: &mut usize, target: usize) {
for (code, bits) in latch_path(*mode, target) {
out.push(code, bits);
}
*mode = target;
}
fn emit_binary(out: &mut BitVec, mode: usize, run: &[u8]) {
let mut idx = 0;
while idx < run.len() {
let chunk = (run.len() - idx).min(2078);
out.push(31, mode_bits(mode)); if chunk <= 31 {
out.push(chunk as u32, 5);
} else {
out.push(0, 5);
out.push((chunk - 31) as u32, 11);
}
for &byte in &run[idx..idx + chunk] {
out.push(byte as u32, 8);
}
idx += chunk;
}
}
pub fn encode(data: &[u8]) -> Vec<bool> {
let mut out = BitVec::new();
let mut mode = UPPER;
let mut i = 0;
let n = data.len();
while i < n {
let b = data[i];
if is_binary(b) {
let mut j = i;
while j < n && is_binary(data[j]) {
j += 1;
}
if mode == DIGIT {
emit_latch(&mut out, &mut mode, UPPER);
}
emit_binary(&mut out, mode, &data[i..j]);
i = j;
continue;
}
if b.is_ascii_digit() {
if mode != DIGIT {
emit_latch(&mut out, &mut mode, DIGIT);
}
out.push(code_for_char(DIGIT, b).unwrap() as u32, 4);
i += 1;
continue;
}
if let Some(code) = code_for_char(mode, b) {
out.push(code as u32, mode_bits(mode));
i += 1;
continue;
}
if b.is_ascii_uppercase() {
emit_latch(&mut out, &mut mode, UPPER);
out.push(code_for_char(UPPER, b).unwrap() as u32, 5);
} else if b.is_ascii_lowercase() {
emit_latch(&mut out, &mut mode, LOWER);
out.push(code_for_char(LOWER, b).unwrap() as u32, 5);
} else if let Some(code) = code_for_char(MIXED, b) {
emit_latch(&mut out, &mut mode, MIXED);
out.push(code as u32, 5);
} else if let Some(code) = code_for_char(PUNCT, b) {
out.push(0, mode_bits(mode));
out.push(code as u32, 5);
} else {
unreachable!("byte {b} is neither binary nor representable");
}
i += 1;
}
out.into_bits()
}
struct Reader<'a> {
bits: &'a [bool],
pos: usize,
}
impl<'a> Reader<'a> {
fn new(bits: &'a [bool]) -> Self {
Reader { bits, pos: 0 }
}
fn remaining(&self) -> usize {
self.bits.len() - self.pos
}
fn read(&mut self, n: usize) -> Option<u32> {
if self.remaining() < n {
return None;
}
let mut v = 0u32;
for _ in 0..n {
v = (v << 1) | self.bits[self.pos] as u32;
self.pos += 1;
}
Some(v)
}
}
fn decode_punct(code: u16, out: &mut Vec<u8>) {
if let Some(pair) = punct_pair_for_code(code) {
out.extend_from_slice(&pair);
} else if let Some(byte) = char_for_code(PUNCT, code) {
out.push(byte);
}
}
pub fn decode(bits: &[bool]) -> Vec<u8> {
let mut out = Vec::new();
let mut r = Reader::new(bits);
let mut mode = UPPER;
loop {
let mb = mode_bits(mode);
if r.remaining() < mb {
break;
}
let code = r.read(mb).unwrap() as u16;
match code {
0 => {
match r.read(5) {
Some(pc) => decode_punct(pc as u16, &mut out),
None => break,
}
}
31 if mode != DIGIT => {
let mut len = match r.read(5) {
Some(v) => v as usize,
None => break,
};
if len == 0 {
match r.read(11) {
Some(v) => len = v as usize + 31,
None => break,
}
}
if r.remaining() < len * 8 {
break;
}
for _ in 0..len {
out.push(r.read(8).unwrap() as u8);
}
}
_ => match mode {
UPPER => match code {
28 => mode = LOWER,
29 => mode = MIXED,
30 => mode = DIGIT,
_ => push_char(UPPER, code, &mut out),
},
LOWER => match code {
28 => {
match r.read(5) {
Some(uc) => push_char(UPPER, uc as u16, &mut out),
None => break,
}
}
29 => mode = MIXED,
30 => mode = DIGIT,
_ => push_char(LOWER, code, &mut out),
},
MIXED => match code {
28 => mode = LOWER,
29 => mode = UPPER,
30 => mode = PUNCT,
_ => push_char(MIXED, code, &mut out),
},
DIGIT => match code {
14 => mode = UPPER,
15 => {
match r.read(5) {
Some(uc) => push_char(UPPER, uc as u16, &mut out),
None => break,
}
}
_ => push_char(DIGIT, code, &mut out),
},
_ => {}
},
}
}
out
}
fn push_char(mode: usize, code: u16, out: &mut Vec<u8>) {
if let Some(byte) = char_for_code(mode, code) {
out.push(byte);
}
}
pub fn stuff_bits(bits: &[bool], w: usize) -> Vec<bool> {
let mut out = Vec::new();
let n = bits.len();
let mut i = 0;
while i < n {
let mut all_one = true;
let mut all_zero = true;
for j in 0..w - 1 {
let bit = if i + j < n { bits[i + j] } else { true };
if bit {
all_zero = false;
} else {
all_one = false;
}
}
if all_one {
out.extend(std::iter::repeat_n(true, w - 1));
out.push(false);
i += w - 1;
} else if all_zero {
out.extend(std::iter::repeat_n(false, w - 1));
out.push(true);
i += w - 1;
} else {
for j in 0..w {
out.push(if i + j < n { bits[i + j] } else { true });
}
i += w;
}
}
out
}
pub fn unstuff_bits(bits: &[bool], w: usize) -> Vec<bool> {
let mut out = Vec::new();
let mut i = 0;
while i + w <= bits.len() {
let word = &bits[i..i + w];
let head = &word[0..w - 1];
if head.iter().all(|&b| b) || head.iter().all(|&b| !b) {
out.extend_from_slice(head);
} else {
out.extend_from_slice(word);
}
i += w;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn highlevel_upper_digit_vector() {
let bits = encode(b"AB12");
let expected = [
false, false, false, true, false, false, false, false, true, true, true, true, true, true, false, false, false, true, true, false, true, false, false, ];
assert_eq!(bits, expected.to_vec());
}
#[test]
fn highlevel_lower_punct_vector() {
let bits = encode(b"aZ!");
let expected = [
true, true, true, false, false, false, false, false, true, false, true, true, true, true, false, true, true, true, false, true, true, false, true, true, false, false, false, false, false, false, false, true, true, false, ];
assert_eq!(bits, expected.to_vec());
}
#[test]
fn highlevel_binary_shift_vector() {
let bits = encode(&[0x80]);
let expected = [
true, true, true, true, true, false, false, false, false, true, true, false, false, false, false, false, false, false, ];
assert_eq!(bits, expected.to_vec());
}
#[test]
fn highlevel_roundtrip_modes() {
for payload in [
&b"HELLO"[..],
b"Hello, World!",
b"abc XYZ 0123 def.",
b"mixed\x01\x02control",
b"\x00\xff\x80binary\xfe",
b"UPPERlower123!@#",
] {
let bits = encode(payload);
let decoded = decode(&bits);
assert_eq!(decoded, payload, "roundtrip failed for {payload:?}");
}
}
#[test]
fn stuffing_roundtrip() {
for w in [6usize, 8, 10] {
let raw = encode(b"Some data to stuff 000111 \x00\xff");
let stuffed = stuff_bits(&raw, w);
assert_eq!(stuffed.len() % w, 0);
let unstuffed = unstuff_bits(&stuffed, w);
assert_eq!(decode(&unstuffed), decode(&raw));
}
}
}