#![forbid(unsafe_code)]
use crate::common::{
buffer::SliceWriter, errors::EncodeError, traits::BarcodeEncoder, types::Encoded,
};
const MAX_DATA: usize = 256;
const CODE93_CHARS: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
const CODE93_PATTERNS: [u16; 48] = [
0b100010100, 0b101001000, 0b101000100, 0b101000010, 0b100101000, 0b100100100, 0b100100010, 0b101010000, 0b100010010, 0b100001010, 0b110101000, 0b110100100, 0b110100010, 0b110010100, 0b110010010, 0b110001010, 0b101101000, 0b101100100, 0b101100010, 0b100110100, 0b100011010, 0b101011000, 0b101001100, 0b101000110, 0b100101100, 0b100010110, 0b110110100, 0b110110010, 0b110101100, 0b110100110, 0b110010110, 0b110011010, 0b101101100, 0b101100110, 0b100110110, 0b100111010, 0b100101110, 0b111010100, 0b111010010, 0b111001010, 0b101101110, 0b101110110, 0b110101110, 0b100100110, 0b111011010, 0b111010110, 0b100110010, 0b101011110, ];
const START_STOP: usize = 47;
pub struct Code93;
impl BarcodeEncoder for Code93 {
type Input = str;
fn encode_into(input: &str, buf: &mut [bool]) -> Result<Encoded, EncodeError> {
if input.is_empty() {
return Err(EncodeError::InvalidInput("Code 93 input must not be empty"));
}
let mut values = [0usize; MAX_DATA + 2];
let mut n = 0;
for ch in input.chars() {
let v = char_value(ch).ok_or(EncodeError::InvalidCharacter(ch))?;
if n >= MAX_DATA {
return Err(EncodeError::DataTooLong);
}
values[n] = v;
n += 1;
}
let c = check_value(&values[..n], 20);
values[n] = c;
n += 1;
let k = check_value(&values[..n], 15);
values[n] = k;
n += 1;
let len = encode_bars(&values[..n], buf)?;
Ok(Encoded::Linear { len, height: 50 })
}
fn symbology_name() -> &'static str {
"Code 93"
}
}
fn char_value(ch: char) -> Option<usize> {
let byte = u8::try_from(ch as u32).ok()?;
CODE93_CHARS.iter().position(|&c| c == byte)
}
fn check_value(values: &[usize], max_weight: u32) -> usize {
let mut weight = 1u32;
let mut sum = 0u32;
for &v in values.iter().rev() {
sum += weight * v as u32;
weight = if weight == max_weight { 1 } else { weight + 1 };
}
(sum % 47) as usize
}
fn append_pattern(w: &mut SliceWriter, value: usize) -> Result<(), EncodeError> {
let pattern = CODE93_PATTERNS[value];
for i in (0..9).rev() {
w.push((pattern >> i) & 1 == 1)?;
}
Ok(())
}
fn encode_bars(values: &[usize], buf: &mut [bool]) -> Result<usize, EncodeError> {
let mut w = SliceWriter::new(buf);
append_pattern(&mut w, START_STOP)?;
for &v in values {
append_pattern(&mut w, v)?;
}
append_pattern(&mut w, START_STOP)?;
w.push(true)?;
Ok(w.len())
}
#[cfg(test)]
mod tests {
use super::*;
fn encode_len(input: &str) -> usize {
let mut buf = [false; 4096];
match Code93::encode_into(input, &mut buf).unwrap() {
Encoded::Linear { len, .. } => len,
_ => panic!("expected linear"),
}
}
#[test]
fn test_encode_basic() {
assert!(encode_len("CODE93") > 0);
}
#[test]
fn test_encode_special_chars() {
assert!(encode_len("HELLO WORLD") > 0);
}
#[test]
fn test_invalid_lowercase() {
let mut buf = [false; 4096];
assert!(Code93::encode_into("hello", &mut buf).is_err());
}
#[test]
fn test_invalid_symbol() {
let mut buf = [false; 4096];
assert!(Code93::encode_into("ABC!DEF", &mut buf).is_err());
}
#[test]
fn test_empty_input() {
let mut buf = [false; 4096];
assert!(Code93::encode_into("", &mut buf).is_err());
}
#[test]
fn test_buffer_too_small() {
let mut buf = [false; 4];
assert_eq!(
Code93::encode_into("CODE93", &mut buf),
Err(EncodeError::BufferTooSmall)
);
}
#[test]
fn test_symbology_name() {
assert_eq!(Code93::symbology_name(), "Code 93");
}
#[test]
fn test_bar_count() {
assert_eq!(encode_len("A"), 5 * 9 + 1);
}
#[test]
fn test_check_values_known() {
let mut values = [0usize; 8];
let mut n = 0;
for ch in "CODE93".chars() {
values[n] = char_value(ch).unwrap();
n += 1;
}
let c = check_value(&values[..n], 20);
assert_eq!(c, char_value('P').unwrap());
values[n] = c;
n += 1;
let k = check_value(&values[..n], 15);
assert_eq!(k, char_value('V').unwrap());
}
#[cfg(feature = "alloc")]
#[test]
fn test_svg_output() {
let svg = Code93::encode("TEST").unwrap().to_svg_string();
assert!(svg.starts_with("<svg "));
}
}