#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use alloc::vec::Vec;
use crate::structured_append::SaError;
use crate::types::{Mode, Version};
const ALPHA_REV: [u8; 45] = *b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SaSymbolData {
pub position: u8,
pub total: u8,
pub parity: u8,
pub data: Vec<u8>,
}
struct BitReader<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> BitReader<'a> {
fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
fn read_bits(&mut self, n: usize) -> Option<u32> {
if n == 0 {
return Some(0);
}
if self.pos.checked_add(n)? > self.data.len() * 8 {
return None;
}
let mut val: u32 = 0;
for _ in 0..n {
let byte = self.data[self.pos >> 3];
let bit = (byte >> (7 - (self.pos & 7))) & 1;
val = (val << 1) | u32::from(bit);
self.pos += 1;
}
Some(val)
}
fn remaining(&self) -> usize {
self.data.len() * 8 - self.pos
}
}
fn nibble_to_value(nibble: u32) -> u8 {
if nibble == 0 { 16 } else { nibble as u8 }
}
pub fn parse_sa_datastream(bits: &[u8], version: Version) -> Result<SaSymbolData, SaError> {
let mut r = BitReader::new(bits);
let mode = r.read_bits(4).ok_or(SaError::MalformedStream)?;
if mode != 0b0011 {
return Err(SaError::NotStructuredAppend);
}
let sequence = r.read_bits(8).ok_or(SaError::MalformedStream)?;
let position = nibble_to_value(sequence >> 4);
let total = nibble_to_value(sequence & 0x0f);
let parity = r.read_bits(8).ok_or(SaError::MalformedStream)? as u8;
let mut data = Vec::new();
while r.remaining() >= 4 {
let segment_mode = r.read_bits(4).ok_or(SaError::MalformedStream)?;
match segment_mode {
0b0000 => break, 0b0001 => decode_numeric(&mut r, version, &mut data)?,
0b0010 => decode_alpha(&mut r, version, &mut data)?,
0b0100 => decode_byte(&mut r, version, &mut data)?,
0b1000 => decode_kanji(&mut r, version, &mut data)?,
_ => break,
}
}
Ok(SaSymbolData { position, total, parity, data })
}
fn decode_byte(r: &mut BitReader<'_>, version: Version, out: &mut Vec<u8>) -> Result<(), SaError> {
let count = r.read_bits(Mode::Byte.length_bits_count(version)).ok_or(SaError::MalformedStream)? as usize;
for _ in 0..count {
let byte = r.read_bits(8).ok_or(SaError::MalformedStream)? as u8;
out.push(byte);
}
Ok(())
}
fn decode_numeric(r: &mut BitReader<'_>, version: Version, out: &mut Vec<u8>) -> Result<(), SaError> {
let mut remaining = r.read_bits(Mode::Numeric.length_bits_count(version)).ok_or(SaError::MalformedStream)? as usize;
while remaining >= 3 {
let v = r.read_bits(10).ok_or(SaError::MalformedStream)?;
out.push(b'0' + (v / 100) as u8);
out.push(b'0' + ((v / 10) % 10) as u8);
out.push(b'0' + (v % 10) as u8);
remaining -= 3;
}
if remaining == 2 {
let v = r.read_bits(7).ok_or(SaError::MalformedStream)?;
out.push(b'0' + (v / 10) as u8);
out.push(b'0' + (v % 10) as u8);
} else if remaining == 1 {
let v = r.read_bits(4).ok_or(SaError::MalformedStream)?;
out.push(b'0' + v as u8);
}
Ok(())
}
fn decode_alpha(r: &mut BitReader<'_>, version: Version, out: &mut Vec<u8>) -> Result<(), SaError> {
let mut remaining =
r.read_bits(Mode::Alphanumeric.length_bits_count(version)).ok_or(SaError::MalformedStream)? as usize;
while remaining >= 2 {
let v = r.read_bits(11).ok_or(SaError::MalformedStream)? as usize;
out.push(ALPHA_REV[v / 45]);
out.push(ALPHA_REV[v % 45]);
remaining -= 2;
}
if remaining == 1 {
let v = r.read_bits(6).ok_or(SaError::MalformedStream)? as usize;
out.push(ALPHA_REV[v]);
}
Ok(())
}
fn decode_kanji(r: &mut BitReader<'_>, version: Version, out: &mut Vec<u8>) -> Result<(), SaError> {
let count = r.read_bits(Mode::Kanji.length_bits_count(version)).ok_or(SaError::MalformedStream)? as usize;
for _ in 0..count {
let n = r.read_bits(13).ok_or(SaError::MalformedStream)?;
let high = n / 0xc0;
let low = n % 0xc0;
let bytes = (high << 8) | low;
let cp = if bytes < 0x1f00 { bytes + 0x8140 } else { bytes + 0xc140 };
out.push((cp >> 8) as u8);
out.push((cp & 0xff) as u8);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::parse_sa_datastream;
use crate::bits::Bits;
use crate::structured_append::SaError;
use crate::types::{EcLevel, Version};
fn sa_bytes<F>(position: u8, total: u8, parity: u8, push: F) -> Vec<u8>
where
F: FnOnce(&mut Bits),
{
let mut bits = Bits::new(Version::Normal(1));
bits.push_structured_append_header(position, total, parity).unwrap();
push(&mut bits);
bits.push_terminator(EcLevel::M).unwrap();
bits.into_bytes()
}
#[test]
fn header_only_parses_back() {
let bytes = sa_bytes(1, 3, 0x5a, |_| {});
let parsed = parse_sa_datastream(&bytes, Version::Normal(1)).unwrap();
assert_eq!(parsed.position, 1);
assert_eq!(parsed.total, 3);
assert_eq!(parsed.parity, 0x5a);
assert!(parsed.data.is_empty());
}
#[test]
fn value_16_decodes_from_zero_nibble() {
let bytes = sa_bytes(16, 16, 0x00, |_| {});
let parsed = parse_sa_datastream(&bytes, Version::Normal(1)).unwrap();
assert_eq!(parsed.position, 16);
assert_eq!(parsed.total, 16);
}
#[test]
fn byte_segment_round_trips() {
let bytes = sa_bytes(1, 2, 0x03, |b| {
b.push_byte_data(b"ab").unwrap();
});
let parsed = parse_sa_datastream(&bytes, Version::Normal(1)).unwrap();
assert_eq!(parsed.data, b"ab");
}
#[test]
fn numeric_segment_round_trips() {
let bytes = sa_bytes(1, 2, 0x00, |b| {
b.push_numeric_data(b"01234567").unwrap();
});
let parsed = parse_sa_datastream(&bytes, Version::Normal(1)).unwrap();
assert_eq!(parsed.data, b"01234567");
}
#[test]
fn alphanumeric_segment_round_trips() {
let bytes = sa_bytes(1, 2, 0x00, |b| {
b.push_alphanumeric_data(b"AC-42").unwrap();
});
let parsed = parse_sa_datastream(&bytes, Version::Normal(1)).unwrap();
assert_eq!(parsed.data, b"AC-42");
}
#[test]
fn kanji_segment_round_trips() {
let bytes = sa_bytes(1, 2, 0x00, |b| {
b.push_kanji_data(b"\x93\x5f\xe4\xaa").unwrap();
});
let parsed = parse_sa_datastream(&bytes, Version::Normal(1)).unwrap();
assert_eq!(parsed.data, b"\x93\x5f\xe4\xaa");
}
#[test]
fn non_structured_append_stream_is_rejected() {
let mut bits = Bits::new(Version::Normal(1));
bits.push_byte_data(b"hello").unwrap();
bits.push_terminator(EcLevel::M).unwrap();
let bytes = bits.into_bytes();
assert_eq!(parse_sa_datastream(&bytes, Version::Normal(1)), Err(SaError::NotStructuredAppend));
}
#[test]
fn truncated_stream_is_malformed() {
assert_eq!(parse_sa_datastream(&[0b0011_0000], Version::Normal(1)), Err(SaError::MalformedStream));
}
}