use crate::mode::Mode;
use crate::ec::ECLevel;
use crate::version::Version;
use crate::info;
use bitvec::*;
use std::cmp;
pub fn encode(s: &str, version: Version, ecl: ECLevel) -> (Mode, BitVec) {
let mode = Mode::from_str(s);
let encoded = encode_with_mode(s, mode, version, ecl);
(mode, encoded)
}
pub fn encode_with_mode(s: &str, mode: Mode, version: Version, ecl: ECLevel) -> BitVec {
let total_capacity = info::total_bits(version, ecl);
let mut bv = mode.to_bitvec();
bv.reserve(total_capacity);
bv.append(&mut bitvec_char_count(s.len(), mode, version));
bv.append(&mut bitvec_data(s, mode));
assert!(bv.len() <= total_capacity);
let zero_bits = cmp::min(total_capacity - bv.len(), 4);
append(&mut bv, 0, zero_bits);
assert!(bv.len() <= total_capacity);
let zero_bits = (total_capacity - bv.len()) % 8;
append(&mut bv, 0, zero_bits);
assert!(bv.len() % 8 == 0);
for pad in [0xEC, 0x11].iter().cycle() {
if bv.len() >= total_capacity {
break;
}
append(&mut bv, *pad, 8);
}
assert_eq!(bv.len(), total_capacity);
bv
}
pub fn append(bv: &mut BitVec, v: u32, len: usize) {
bv.extend((0..len).rev().map(|i| (v >> i) & 1 != 0));
}
fn bitvec_char_count(len: usize, mode: Mode, v: Version) -> BitVec {
let mut bv = BitVec::new();
append(&mut bv, len as u32, v.char_count_len(mode));
bv
}
fn bitvec_data(s: &str, mode: Mode) -> BitVec {
let bytes = string_to_bytes(s, mode);
match mode {
Mode::Numeric => encode_numeric_data(&bytes),
Mode::Alphanumeric => encode_alphanumeric_data(&bytes),
Mode::Byte => encode_byte_data(&bytes),
}
}
fn encode_numeric_data(v: &Vec<u8>) -> BitVec {
let bit_len = |num: u32| {
if num > 99 {
10
} else if num > 9 {
7
} else {
4
}
};
let mut bv = BitVec::new();
bv.reserve(v.len() * 8);
let mut add = |s: &str| {
let num: u32 = s.parse().unwrap();
let len = bit_len(num);
append(&mut bv, num, len);
};
let mut acc = String::new();
for x in v.iter() {
acc.push_str(x.to_string().as_str());
if acc.len() == 3 {
add(acc.as_str());
acc.clear();
}
}
if !acc.is_empty() {
add(acc.as_str());
}
bv
}
fn encode_alphanumeric_data(v: &Vec<u8>) -> BitVec {
let mut bv = BitVec::new();
bv.reserve(v.len() * 8);
for i in (0..v.len()).step_by(2) {
if i + 1 < v.len() {
let num = 45 * (v[i] as u32) + (v[i + 1] as u32);
append(&mut bv, num, 11);
} else {
let num = v[i] as u32;
append(&mut bv, num, 6);
}
}
bv
}
fn encode_byte_data(v: &Vec<u8>) -> BitVec {
v[..].into()
}
fn string_to_bytes(s: &str, mode: Mode) -> Vec<u8> {
match mode {
Mode::Numeric =>
s.bytes().map(convert_numeric).collect(),
Mode::Alphanumeric =>
s.chars().map(convert_alphanumeric).collect(),
Mode::Byte =>
s.bytes().collect(),
}
}
fn convert_numeric(b: u8) -> u8 {
b - 48
}
fn convert_alphanumeric(c: char) -> u8 {
match c {
'0' => 0,
'1' => 1,
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
'6' => 6,
'7' => 7,
'8' => 8,
'9' => 9,
'A' => 10,
'B' => 11,
'C' => 12,
'D' => 13,
'E' => 14,
'F' => 15,
'G' => 16,
'H' => 17,
'I' => 18,
'J' => 19,
'K' => 20,
'L' => 21,
'M' => 22,
'N' => 23,
'O' => 24,
'P' => 25,
'Q' => 26,
'R' => 27,
'S' => 28,
'T' => 29,
'U' => 30,
'V' => 31,
'W' => 32,
'X' => 33,
'Y' => 34,
'Z' => 35,
' ' => 36,
'$' => 37,
'%' => 38,
'*' => 39,
'+' => 40,
'-' => 41,
'.' => 42,
'/' => 43,
':' => 44,
_ => panic!("Unsupported alphanumeric '{}'", c),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_full() {
let hello_res: BitVec = vec![0b00100000, 0b01011011, 0b00001011, 0b01111000,
0b11010001, 0b01110010, 0b11011100, 0b01001101,
0b01000011, 0b01000000,
0b11101100, 0b00010001, 0b11101100].into();
let (_, encoded) = encode("HELLO WORLD", Version::new(1), ECLevel::Q);
assert_eq!(encoded, hello_res);
}
#[test]
fn internal() {
assert_eq!(bitvec_char_count(3, Mode::Numeric, Version::new(1)),
bitvec![0, 0, 0, 0, 0, 0, 0, 0, 1, 1]);
assert_eq!(bitvec_char_count("HELLO WORLD".len(), Mode::Alphanumeric, Version::new(1)),
bitvec![0, 0, 0, 0, 0, 1, 0, 1, 1]);
assert_eq!(encode_numeric_data(&vec![8, 6, 7, 5, 3, 0, 9]),
bitvec![1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1]); assert_eq!(encode_alphanumeric_data(&vec![17, 14]),
bitvec![0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1]);
assert_eq!(encode_alphanumeric_data(&vec![45]),
bitvec![1, 0, 1, 1, 0, 1]);
assert_eq!(string_to_bytes("0123456789", Mode::Numeric),
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(string_to_bytes("ABCXYZ 0123456789$%*+-./:", Mode::Alphanumeric),
vec![10, 11, 12, 33, 34, 35, 36,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
37, 38, 39, 40, 41, 42, 43, 44]);
assert_eq!(string_to_bytes("☃", Mode::Byte),
vec![0b11100010, 0b10011000, 0b10000011]);
}
}