#![allow(clippy::unreadable_literal, clippy::unusual_byte_groupings)]
#[cfg(not(feature = "std"))]
#[allow(unused_imports)]
use alloc::{
borrow::ToOwned,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::cmp::min;
use crate::cast::{As, Truncate};
use crate::mode::{AlphanumericMode, EncodingMode, KanjiMode, NumericMode};
use crate::optimize::{Parser, Segment, optimize_segments, total_encoded_len};
use crate::types::{EcLevel, Mode, QrError, QrResult, Version};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct EncodingModes {
bits: u8,
}
impl EncodingModes {
#[must_use]
pub const fn empty() -> Self {
Self { bits: 0 }
}
#[must_use]
pub const fn from_mode(mode: Mode) -> Self {
Self { bits: mode_bit(mode) }
}
#[must_use]
pub const fn contains(self, mode: Mode) -> bool {
self.bits & mode_bit(mode) != 0
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.bits == 0
}
#[must_use]
pub const fn len(self) -> usize {
(self.contains(Mode::Numeric) as usize)
+ (self.contains(Mode::Alphanumeric) as usize)
+ (self.contains(Mode::Byte) as usize)
+ (self.contains(Mode::Kanji) as usize)
}
pub const fn iter(self) -> EncodingModesIter {
EncodingModesIter { modes: self, index: 0 }
}
fn insert(&mut self, mode: Mode) {
self.bits |= mode_bit(mode);
}
}
#[derive(Clone, Debug)]
pub struct EncodingModesIter {
modes: EncodingModes,
index: u8,
}
impl Iterator for EncodingModesIter {
type Item = Mode;
fn next(&mut self) -> Option<Self::Item> {
while self.index < 4 {
let mode = match self.index {
0 => Mode::Numeric,
1 => Mode::Alphanumeric,
2 => Mode::Byte,
_ => Mode::Kanji,
};
self.index += 1;
if self.modes.contains(mode) {
return Some(mode);
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = ((self.modes.bits & 0b1111) >> self.index).count_ones() as usize;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for EncodingModesIter {}
impl core::iter::FusedIterator for EncodingModesIter {}
const fn mode_bit(mode: Mode) -> u8 {
match mode {
Mode::Numeric => 1 << 0,
Mode::Alphanumeric => 1 << 1,
Mode::Byte => 1 << 2,
Mode::Kanji => 1 << 3,
}
}
pub struct Bits {
data: Vec<u8>,
bit_offset: usize,
version: Version,
encoding_modes: EncodingModes,
payload_bits_len: Option<usize>,
}
fn additional_bytes_for_bits(bit_offset: usize, bit_count: usize) -> usize {
debug_assert!(bit_offset < 8);
let existing_space = if bit_offset == 0 { 0 } else { 8 - bit_offset };
bit_count.saturating_sub(existing_space).div_ceil(8)
}
impl Bits {
pub const fn new(version: Version) -> Self {
Self {
data: Vec::new(),
bit_offset: 0,
version,
encoding_modes: EncodingModes::empty(),
payload_bits_len: None,
}
}
fn push_number(&mut self, n: usize, number: u16) {
debug_assert!(n == 16 || n < 16 && number < (1 << n), "{number} is too big as a {n}-bit number");
let b = self.bit_offset + n;
let last_index = self.data.len().wrapping_sub(1);
match (self.bit_offset, b) {
(0, 0..=8) => {
self.data.push((number << (8 - b)).truncate_as_u8());
}
(0, _) => {
self.data.push((number >> (b - 8)).truncate_as_u8());
self.data.push((number << (16 - b)).truncate_as_u8());
}
(_, 0..=8) => {
self.data[last_index] |= (number << (8 - b)).truncate_as_u8();
}
(_, 9..=16) => {
self.data[last_index] |= (number >> (b - 8)).truncate_as_u8();
self.data.push((number << (16 - b)).truncate_as_u8());
}
_ => {
self.data[last_index] |= (number >> (b - 8)).truncate_as_u8();
self.data.push((number >> (b - 16)).truncate_as_u8());
self.data.push((number << (24 - b)).truncate_as_u8());
}
}
self.bit_offset = b & 7;
}
pub fn push_number_checked(&mut self, n: usize, number: usize) -> QrResult<()> {
if n > 16 || number >= (1 << n) {
Err(QrError::DataTooLong)
} else {
if n > 0 {
self.push_number(n, number.as_u16());
}
Ok(())
}
}
pub fn reserve(&mut self, n: usize) {
let extra_bytes = additional_bytes_for_bits(self.bit_offset, n);
self.data.reserve(extra_bytes);
}
pub fn into_bytes(self) -> Vec<u8> {
self.data
}
pub fn len(&self) -> usize {
if self.bit_offset == 0 { self.data.len() * 8 } else { (self.data.len() - 1) * 8 + self.bit_offset }
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn max_len(&self, ec_level: EcLevel) -> QrResult<usize> {
self.version.fetch(ec_level, &DATA_LENGTHS)
}
pub fn version(&self) -> Version {
self.version
}
#[must_use]
pub const fn encoding_modes(&self) -> EncodingModes {
self.encoding_modes
}
#[must_use]
pub fn payload_bits_len(&self) -> usize {
match self.payload_bits_len {
Some(len) => len,
None => self.len(),
}
}
pub fn remaining_capacity_bits(&self, ec_level: EcLevel) -> QrResult<usize> {
Ok(self.max_len(ec_level)?.saturating_sub(self.payload_bits_len()))
}
}
#[test]
fn test_push_number() {
let mut bits = Bits::new(Version::Normal(1));
bits.push_number(3, 0b010); bits.push_number(3, 0b110); bits.push_number(3, 0b101); bits.push_number(7, 0b001_1010); bits.push_number(4, 0b1100); bits.push_number(12, 0b1011_0110_1101); bits.push_number(10, 0b01_1001_0001); bits.push_number(15, 0b111_0010_1110_0011);
let bytes = bits.into_bytes();
assert_eq!(
bytes,
vec![
0b010_110_10, 0b1_001_1010, 0b1100_1011, 0b0110_1101, 0b01_1001_00, 0b01_111_001, 0b0_1110_001, 0b1_0000000, ]
);
}
#[cfg(test)]
mod reservation_tests {
use crate::bits::{Bits, additional_bytes_for_bits};
use crate::types::{EcLevel, Version};
fn reference_byte_increment(offset: usize, count: usize) -> usize {
let total_bytes = (count as u128 + offset as u128).div_ceil(8);
usize::try_from(total_bytes - u128::from(offset > 0)).unwrap()
}
#[test]
fn additional_byte_increment_matches_all_small_bit_counts_and_offsets() {
for offset in 0..8 {
for count in 0..=256 {
assert_eq!(
additional_bytes_for_bits(offset, count),
reference_byte_increment(offset, count),
"offset {offset}, count {count}"
);
}
}
assert_eq!(additional_bytes_for_bits(0, 1), 1);
assert_eq!(additional_bytes_for_bits(7, 2), 1);
assert_eq!(additional_bytes_for_bits(1, 1), 0);
}
#[test]
fn additional_byte_increment_handles_usize_max_without_allocation() {
for offset in 0..8 {
for count in [usize::MAX - 8, usize::MAX - 7, usize::MAX - 1, usize::MAX] {
assert_eq!(additional_bytes_for_bits(offset, count), reference_byte_increment(offset, count));
}
}
}
fn filled_to_capacity(offset: usize) -> Bits {
let mut bits = Bits::new(Version::Normal(40));
bits.reserve(64);
let capacity = bits.data.capacity();
let whole_bytes = capacity - usize::from(offset > 0);
for _ in 0..whole_bytes {
bits.push_number_checked(8, 0xa5).unwrap();
}
if offset > 0 {
bits.push_number_checked(offset, (1 << offset) - 1).unwrap();
}
assert_eq!(bits.data.len(), capacity);
assert_eq!(bits.bit_offset, offset);
bits
}
fn write_bits(bits: &mut Bits, mut count: usize) {
while count > 0 {
let width = count.min(16);
bits.push_number_checked(width, (1 << width) - 1).unwrap();
count -= width;
}
}
#[test]
fn reserved_bits_can_be_written_without_growing_capacity() {
for offset in 0..8 {
for count in 0..=128 {
let mut reserved = filled_to_capacity(offset);
let mut reference = filled_to_capacity(offset);
let before_bytes = reserved.data.clone();
let before_len = reserved.len();
let before_modes = reserved.encoding_modes();
let before_payload = reserved.payload_bits_len;
reserved.reserve(count);
let reserved_capacity = reserved.data.capacity();
assert_eq!(reserved.data, before_bytes);
assert_eq!(reserved.len(), before_len);
assert_eq!(reserved.encoding_modes(), before_modes);
assert_eq!(reserved.payload_bits_len, before_payload);
write_bits(&mut reserved, count);
write_bits(&mut reference, count);
assert_eq!(reserved.data.capacity(), reserved_capacity, "offset {offset}, count {count}");
assert_eq!(reserved.data, reference.data);
assert_eq!(reserved.len(), before_len + count);
assert_eq!(reserved.bit_offset, reference.bit_offset);
assert_eq!(reserved.encoding_modes(), reference.encoding_modes());
assert_eq!(reserved.payload_bits_len, reference.payload_bits_len);
}
}
}
#[test]
fn reserving_empty_or_padded_streams_keeps_contents_and_metadata() {
let mut empty = Bits::new(Version::Normal(1));
empty.reserve(0);
assert!(empty.is_empty());
assert_eq!(empty.data.capacity(), 0);
empty.reserve(1);
let capacity = empty.data.capacity();
assert!(capacity >= 1);
assert!(empty.is_empty());
empty.push_number_checked(1, 1).unwrap();
assert_eq!(empty.data.capacity(), capacity);
let mut padded = Bits::new(Version::Normal(1));
padded.push_byte_data(b"abc").unwrap();
padded.push_terminator(EcLevel::M).unwrap();
let before_bytes = padded.data.clone();
let before_len = padded.len();
let before_modes = padded.encoding_modes();
let before_payload = padded.payload_bits_len;
padded.reserve(65);
assert_eq!(padded.data, before_bytes);
assert_eq!(padded.len(), before_len);
assert_eq!(padded.encoding_modes(), before_modes);
assert_eq!(padded.payload_bits_len, before_payload);
}
}
#[cfg(test)]
mod metadata_tests {
use crate::bits::{Bits, EncodingModes};
use crate::types::{EcLevel, Mode, Version};
#[test]
fn encoding_modes_iterates_in_stable_mode_order() {
let modes = EncodingModes::from_mode(Mode::Byte);
let mut modes_with_numeric = modes;
modes_with_numeric.insert(Mode::Numeric);
assert_eq!(modes_with_numeric.iter().collect::<Vec<_>>(), vec![Mode::Numeric, Mode::Byte]);
}
#[test]
fn encoding_modes_iterator_reports_exact_remaining_length_for_every_subset() {
let order = [Mode::Numeric, Mode::Alphanumeric, Mode::Byte, Mode::Kanji];
for mask in 0..=0b1111 {
let modes = EncodingModes { bits: mask };
let expected = order.iter().copied().filter(|&mode| modes.contains(mode)).collect::<Vec<_>>();
let mut iter = modes.iter();
for (index, &mode) in expected.iter().enumerate() {
let remaining = expected.len() - index;
assert_eq!(iter.size_hint(), (remaining, Some(remaining)), "mask {mask}");
assert_eq!(iter.len(), remaining, "mask {mask}");
assert_eq!(iter.next(), Some(mode));
}
assert_eq!(iter.size_hint(), (0, Some(0)), "mask {mask}");
assert_eq!(iter.len(), 0);
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
}
}
#[test]
fn bits_records_successful_payload_modes() {
let mut bits = Bits::new(Version::Normal(1));
bits.push_numeric_data(b"012").unwrap();
bits.push_byte_data(b"abc").unwrap();
assert!(bits.encoding_modes().contains(Mode::Numeric));
assert!(bits.encoding_modes().contains(Mode::Byte));
assert!(!bits.encoding_modes().contains(Mode::Kanji));
}
#[test]
fn remaining_capacity_uses_payload_len_before_padding() {
let mut bits = Bits::new(Version::Normal(1));
bits.push_numeric_data(b"01234567").unwrap();
bits.push_terminator(EcLevel::M).unwrap();
assert_eq!(bits.len(), 128);
assert_eq!(bits.payload_bits_len(), 41);
assert_eq!(bits.remaining_capacity_bits(EcLevel::M), Ok(87));
}
}
#[derive(Copy, Clone)]
pub enum ExtendedMode {
Eci,
Data(Mode),
Fnc1First,
Fnc1Second,
StructuredAppend,
}
impl Bits {
pub fn push_mode_indicator(&mut self, mode: ExtendedMode) -> QrResult<()> {
let (bits, number) = self.mode_indicator_bits(mode)?;
if bits > 0 {
self.push_number(bits, number.as_u16());
}
Ok(())
}
fn mode_indicator_bits(&self, mode: ExtendedMode) -> QrResult<(usize, usize)> {
#[allow(clippy::match_same_arms)]
let number = match (self.version, mode) {
(Version::Micro(_), ExtendedMode::Data(Mode::Numeric)) => 0,
(Version::Micro(_), ExtendedMode::Data(Mode::Alphanumeric)) => 1,
(Version::Micro(_), ExtendedMode::Data(Mode::Byte)) => 0b10,
(Version::Micro(_), ExtendedMode::Data(Mode::Kanji)) => 0b11,
(Version::Micro(_), _) => return Err(QrError::UnsupportedCharacterSet),
(_, ExtendedMode::Data(Mode::Numeric)) => 0b0001,
(_, ExtendedMode::Data(Mode::Alphanumeric)) => 0b0010,
(_, ExtendedMode::Data(Mode::Byte)) => 0b0100,
(_, ExtendedMode::Data(Mode::Kanji)) => 0b1000,
(_, ExtendedMode::Eci) => 0b0111,
(_, ExtendedMode::Fnc1First) => 0b0101,
(_, ExtendedMode::Fnc1Second) => 0b1001,
(_, ExtendedMode::StructuredAppend) => 0b0011,
};
let bits = self.version.mode_bits_count();
if bits > 16 || number >= (1 << bits) {
return Err(QrError::UnsupportedCharacterSet);
}
Ok((bits, number))
}
}
impl Bits {
pub fn push_eci_designator(&mut self, eci_designator: u32) -> QrResult<()> {
self.reserve(12); self.push_mode_indicator(ExtendedMode::Eci)?;
match eci_designator {
0..=127 => {
self.push_number(8, eci_designator.as_u16());
}
128..=16383 => {
self.push_number(2, 0b10);
self.push_number(14, eci_designator.as_u16());
}
16384..=999_999 => {
self.push_number(3, 0b110);
self.push_number(5, (eci_designator >> 16).as_u16());
self.push_number(16, (eci_designator & 0xffff).as_u16());
}
_ => return Err(QrError::InvalidEciDesignator { value: eci_designator }),
}
Ok(())
}
}
#[cfg(test)]
mod eci_tests {
use crate::bits::Bits;
use crate::types::{QrError, Version};
#[test]
fn test_9() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_eci_designator(9), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0111_0000, 0b1001_0000]);
}
#[test]
fn test_899() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_eci_designator(899), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0111_10_00, 0b00111000, 0b0011_0000]);
}
#[test]
fn test_999999() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_eci_designator(999999), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0111_110_0, 0b11110100, 0b00100011, 0b1111_0000]);
}
#[test]
fn test_invalid_designator() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_eci_designator(1000000), Err(QrError::InvalidEciDesignator { value: 1000000 }));
}
#[test]
fn test_unsupported_character_set() {
let mut bits = Bits::new(Version::Micro(4));
assert_eq!(bits.push_eci_designator(9), Err(QrError::UnsupportedCharacterSet));
}
}
impl Bits {
fn push_header(&mut self, mode: Mode, raw_data_len: usize) -> QrResult<()> {
let (mode_bits, mode_number) = self.mode_indicator_bits(ExtendedMode::Data(mode))?;
let length_bits = mode.length_bits_count(self.version);
if raw_data_len >= (1_usize << length_bits) {
return Err(QrError::DataTooLong);
}
self.reserve(length_bits + mode_bits + mode.data_bits_count(raw_data_len));
if mode_bits > 0 {
self.push_number(mode_bits, mode_number.as_u16());
}
self.push_number(length_bits, raw_data_len.as_u16());
Ok(())
}
pub fn push_numeric_data(&mut self, data: &[u8]) -> QrResult<()> {
self.mode_indicator_bits(ExtendedMode::Data(Mode::Numeric))?;
if let Some((position, byte)) = NumericMode::invalid_character(data) {
return Err(QrError::InvalidCharacter { position, byte });
}
self.push_header(Mode::Numeric, data.len())?;
for chunk in data.chunks(3) {
let number = chunk.iter().map(|b| u16::from(*b - b'0')).fold(0, |a, b| a * 10 + b);
let length = chunk.len() * 3 + 1;
self.push_number(length, number);
}
self.encoding_modes.insert(Mode::Numeric);
self.payload_bits_len = None;
Ok(())
}
}
#[cfg(test)]
mod numeric_tests {
use crate::bits::Bits;
use crate::types::{QrError, Version};
#[test]
fn test_iso_18004_2006_example_1() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_numeric_data(b"01234567"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0001_0000, 0b001000_00, 0b00001100, 0b01010110, 0b01_100001, 0b1_0000000]);
}
#[test]
fn test_iso_18004_2000_example_2() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
assert_eq!(
bits.into_bytes(),
vec![
0b0001_0000,
0b010000_00,
0b00001100,
0b01010110,
0b01_101010,
0b0110_1110,
0b000101_00,
0b11101010,
0b0101_0000,
]
);
}
#[test]
fn test_iso_18004_2006_example_2() {
let mut bits = Bits::new(Version::Micro(3));
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
assert_eq!(
bits.into_bytes(),
vec![0b00_10000_0, 0b00000110, 0b0_0101011, 0b001_10101, 0b00110_111, 0b0000101_0, 0b01110101, 0b00101_000,]
);
}
#[test]
fn test_data_too_long_error() {
let mut bits = Bits::new(Version::Micro(1));
assert_eq!(bits.push_numeric_data(b"12345678"), Err(QrError::DataTooLong));
}
}
#[inline]
fn alphanumeric_digit(character: u8) -> u16 {
match character {
b'0'..=b'9' => u16::from(character - b'0'),
b'A'..=b'Z' => u16::from(character - b'A') + 10,
b' ' => 36,
b'$' => 37,
b'%' => 38,
b'*' => 39,
b'+' => 40,
b'-' => 41,
b'.' => 42,
b'/' => 43,
b':' => 44,
_ => 0,
}
}
impl Bits {
pub fn push_alphanumeric_data(&mut self, data: &[u8]) -> QrResult<()> {
self.mode_indicator_bits(ExtendedMode::Data(Mode::Alphanumeric))?;
if let Some((position, byte)) = AlphanumericMode::invalid_character(data) {
return Err(QrError::InvalidCharacter { position, byte });
}
self.push_header(Mode::Alphanumeric, data.len())?;
for chunk in data.chunks(2) {
let number = chunk.iter().map(|b| alphanumeric_digit(*b)).fold(0, |a, b| a * 45 + b);
let length = chunk.len() * 5 + 1;
self.push_number(length, number);
}
self.encoding_modes.insert(Mode::Alphanumeric);
self.payload_bits_len = None;
Ok(())
}
}
#[cfg(test)]
mod alphanumeric_tests {
use crate::bits::Bits;
use crate::types::{QrError, Version};
#[test]
fn test_iso_18004_2006_example() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_alphanumeric_data(b"AC-42"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b0010_0000, 0b00101_001, 0b11001110, 0b11100111, 0b001_00001, 0b0_0000000]);
}
#[test]
fn test_micro_qr_unsupported() {
let mut bits = Bits::new(Version::Micro(1));
assert_eq!(bits.push_alphanumeric_data(b"A"), Err(QrError::UnsupportedCharacterSet));
}
#[test]
fn test_data_too_long() {
let mut bits = Bits::new(Version::Micro(2));
assert_eq!(bits.push_alphanumeric_data(b"ABCDEFGH"), Err(QrError::DataTooLong));
}
}
impl Bits {
pub fn push_byte_data(&mut self, data: &[u8]) -> QrResult<()> {
self.push_header(Mode::Byte, data.len())?;
let offset = self.bit_offset;
if offset == 0 {
self.data.extend_from_slice(data);
} else if let Some((&last, _)) = data.split_last() {
let last_index = self.data.len() - 1;
self.data[last_index] |= data[0] >> offset;
let shift = 8 - offset;
self.data.extend(data.windows(2).map(|pair| (pair[0] << shift) | (pair[1] >> offset)));
self.data.push(last << shift);
}
self.encoding_modes.insert(Mode::Byte);
self.payload_bits_len = None;
Ok(())
}
#[cfg(feature = "bench-internals")]
#[doc(hidden)]
pub fn push_byte_data_scalar_for_bench(&mut self, data: &[u8]) -> QrResult<()> {
self.push_header(Mode::Byte, data.len())?;
for &byte in data {
self.push_number(8, u16::from(byte));
}
self.encoding_modes.insert(Mode::Byte);
self.payload_bits_len = None;
Ok(())
}
}
#[cfg(test)]
mod byte_tests {
use crate::bits::Bits;
use crate::types::{EcLevel, Mode, QrError, Version};
fn reference_write(bytes: &mut Vec<u8>, bit_len: &mut usize, width: usize, value: usize) {
for index in (0..width).rev() {
if (*bit_len).is_multiple_of(8) {
bytes.push(0);
}
let byte_index = bytes.len() - 1;
bytes[byte_index] |= (((value >> index) & 1) as u8) << (7 - *bit_len % 8);
*bit_len += 1;
}
}
fn reference_byte_segment(bytes: &mut Vec<u8>, bit_len: &mut usize, version: Version, data: &[u8]) {
let mode_number = if version.is_micro() { 2 } else { 4 };
reference_write(bytes, bit_len, version.mode_bits_count(), mode_number);
reference_write(bytes, bit_len, Mode::Byte.length_bits_count(version), data.len());
for &byte in data {
reference_write(bytes, bit_len, 8, usize::from(byte));
}
}
fn check_byte_segment(data: &[u8], version: Version, prefix_len: usize) {
let mut bits = Bits::new(version);
let mut expected = Vec::new();
let mut bit_len = 0;
if prefix_len > 0 {
let prefix = (1 << prefix_len) - 1;
bits.push_number_checked(prefix_len, prefix).unwrap();
reference_write(&mut expected, &mut bit_len, prefix_len, prefix);
}
bits.push_byte_data(data).unwrap();
reference_byte_segment(&mut expected, &mut bit_len, version, data);
assert_eq!(bits.len(), bit_len, "{version:?} prefix {prefix_len}, data {data:?}");
assert_eq!(bits.bit_offset, bit_len % 8);
assert_eq!(bits.data, expected);
assert_eq!(bits.encoding_modes(), crate::bits::EncodingModes::from_mode(Mode::Byte));
assert_eq!(bits.payload_bits_len, None);
#[cfg(feature = "bench-internals")]
{
let mut scalar = Bits::new(version);
if prefix_len > 0 {
scalar.push_number_checked(prefix_len, (1 << prefix_len) - 1).unwrap();
}
scalar.push_byte_data_scalar_for_bench(data).unwrap();
assert_eq!(bits.data, scalar.data);
assert_eq!(bits.len(), scalar.len());
}
}
#[test]
fn byte_packing_matches_bit_reference_at_every_offset_and_version_group() {
for version in [
Version::Normal(1),
Version::Normal(9),
Version::Normal(10),
Version::Normal(26),
Version::Normal(27),
Version::Normal(40),
Version::Micro(3),
Version::Micro(4),
] {
let max_length = (1 << Mode::Byte.length_bits_count(version)) - 1;
for len in [0, 1, 2, 15, 31, 255, 256, 2048].into_iter().filter(|&len| len <= max_length) {
let data = (0..len).map(|index| (index % 256) as u8).collect::<Vec<_>>();
for prefix_len in 0..8 {
check_byte_segment(&data, version, prefix_len);
}
}
}
}
#[test]
fn micro_byte_packing_preserves_every_byte_value_at_every_offset() {
for version in [Version::Micro(3), Version::Micro(4)] {
for byte in 0..=255 {
for prefix_len in 0..8 {
check_byte_segment(&[byte], version, prefix_len);
}
}
}
}
#[test]
fn byte_packing_preserves_consecutive_and_mixed_segment_boundaries() {
let version = Version::Normal(40);
for prefix_len in 0..8 {
let mut bits = Bits::new(version);
let mut expected = Vec::new();
let mut bit_len = 0;
if prefix_len > 0 {
let prefix = (1 << prefix_len) - 1;
bits.push_number_checked(prefix_len, prefix).unwrap();
reference_write(&mut expected, &mut bit_len, prefix_len, prefix);
}
for data in [&b""[..], &[0xff], &[0, 0xa5, 0x7f], &b"longer byte segment"[..], &b""[..]] {
bits.push_byte_data(data).unwrap();
reference_byte_segment(&mut expected, &mut bit_len, version, data);
}
bits.push_numeric_data(b"12345").unwrap();
reference_write(&mut expected, &mut bit_len, 4, 1);
reference_write(&mut expected, &mut bit_len, 14, 5);
reference_write(&mut expected, &mut bit_len, 10, 123);
reference_write(&mut expected, &mut bit_len, 7, 45);
bits.push_byte_data(&[0xf0, 0x0f, 0xaa, 0x55]).unwrap();
reference_byte_segment(&mut expected, &mut bit_len, version, &[0xf0, 0x0f, 0xaa, 0x55]);
assert_eq!(bits.data, expected);
assert_eq!(bits.len(), bit_len);
assert!(bits.encoding_modes().contains(Mode::Numeric));
assert!(bits.encoding_modes().contains(Mode::Byte));
}
}
#[test]
fn empty_byte_segment_after_padding_keeps_header_and_resets_payload_metadata() {
let version = Version::Normal(1);
let mut bits = Bits::new(version);
bits.push_numeric_data(b"1").unwrap();
bits.push_terminator(EcLevel::L).unwrap();
assert!(bits.payload_bits_len.is_some());
let mut expected = bits.data.clone();
let mut bit_len = bits.len();
bits.push_byte_data(&[]).unwrap();
reference_byte_segment(&mut expected, &mut bit_len, version, &[]);
assert_eq!(bits.data, expected);
assert_eq!(bits.len(), bit_len);
assert_eq!(bits.payload_bits_len, None);
assert!(bits.encoding_modes().contains(Mode::Byte));
}
#[test]
fn zero_width_checked_writes_preserve_empty_aligned_and_mixed_streams() {
let empty = Bits::new(Version::Normal(40));
let mut aligned = Bits::new(Version::Normal(40));
aligned.push_number_checked(4, 0xa).unwrap();
aligned.push_byte_data(b"abc").unwrap();
assert_eq!(aligned.bit_offset, 0);
let mut unaligned = Bits::new(Version::Normal(40));
unaligned.push_numeric_data(b"12345").unwrap();
assert_ne!(unaligned.bit_offset, 0);
let mut terminated = Bits::new(Version::Normal(1));
terminated.push_numeric_data(b"1").unwrap();
terminated.push_terminator(EcLevel::M).unwrap();
assert!(terminated.payload_bits_len.is_some());
let mut mixed_byte = Bits::new(Version::Normal(40));
mixed_byte.push_numeric_data(b"12345").unwrap();
mixed_byte.push_byte_data(&[0xab, 0xcd]).unwrap();
let mut mixed_terminated = Bits::new(Version::Normal(40));
mixed_terminated.push_numeric_data(b"12345").unwrap();
mixed_terminated.push_byte_data(&[0xab, 0xcd]).unwrap();
mixed_terminated.push_terminator(EcLevel::M).unwrap();
for (name, mut bits) in [
("empty", empty),
("aligned", aligned),
("unaligned", unaligned),
("terminated", terminated),
("mixed_byte", mixed_byte),
("mixed_terminated", mixed_terminated),
] {
let expected = bits.data.clone();
let bit_len = bits.len();
let offset = bits.bit_offset;
let capacity = bits.data.capacity();
let modes = bits.encoding_modes();
let payload_len = bits.payload_bits_len;
for number in [0, 1, usize::MAX] {
let result = if number == 0 { Ok(()) } else { Err(QrError::DataTooLong) };
assert_eq!(bits.push_number_checked(0, number), result, "{name} value {number}");
assert_eq!(bits.data, expected, "{name}");
assert_eq!(bits.len(), bit_len, "{name}");
assert_eq!(bits.bit_offset, offset, "{name}");
assert_eq!(bits.data.capacity(), capacity, "{name}");
assert_eq!(bits.encoding_modes(), modes, "{name}");
assert_eq!(bits.payload_bits_len, payload_len, "{name}");
}
}
}
#[test]
fn byte_packing_errors_leave_existing_bits_and_metadata_unchanged() {
for (version, len, error) in [
(Version::Normal(1), 256, QrError::DataTooLong),
(Version::Micro(3), 16, QrError::DataTooLong),
(Version::Micro(4), 32, QrError::DataTooLong),
(Version::Micro(1), 256, QrError::UnsupportedCharacterSet),
(Version::Micro(2), 256, QrError::UnsupportedCharacterSet),
] {
let mut bits = Bits::new(version);
bits.push_numeric_data(b"1").unwrap();
let expected = bits.data.clone();
let bit_len = bits.len();
let modes = bits.encoding_modes();
let payload_len = bits.payload_bits_len;
assert_eq!(bits.push_byte_data(&vec![0xff; len]), Err(error));
assert_eq!(bits.data, expected);
assert_eq!(bits.len(), bit_len);
assert_eq!(bits.encoding_modes(), modes);
assert_eq!(bits.payload_bits_len, payload_len);
}
}
#[test]
fn test() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_byte_data(b"\x12\x34\x56\x78\x9a\xbc\xde\xf0"), Ok(()));
assert_eq!(
bits.into_bytes(),
vec![
0b0100_0000,
0b1000_0001,
0b0010_0011,
0b0100_0101,
0b0110_0111,
0b1000_1001,
0b1010_1011,
0b1100_1101,
0b1110_1111,
0b0000_0000,
]
);
}
#[test]
fn test_micro_qr_unsupported() {
let mut bits = Bits::new(Version::Micro(2));
assert_eq!(bits.push_byte_data(b"?"), Err(QrError::UnsupportedCharacterSet));
}
#[test]
fn test_data_too_long() {
let mut bits = Bits::new(Version::Micro(3));
assert_eq!(bits.push_byte_data(b"0123456701234567"), Err(QrError::DataTooLong));
}
}
impl Bits {
pub fn push_kanji_data(&mut self, data: &[u8]) -> QrResult<()> {
self.mode_indicator_bits(ExtendedMode::Data(Mode::Kanji))?;
if let Some((position, byte)) = KanjiMode::invalid_character(data) {
return Err(QrError::InvalidCharacter { position, byte });
}
self.push_header(Mode::Kanji, data.len() / 2)?;
for kanji in data.as_chunks::<2>().0 {
let cp = u16::from(kanji[0]) * 256 + u16::from(kanji[1]);
let bytes = if cp < 0xe040 { cp - 0x8140 } else { cp - 0xc140 };
let number = (bytes >> 8) * 0xc0 + (bytes & 0xff);
self.push_number(13, number);
}
self.encoding_modes.insert(Mode::Kanji);
self.payload_bits_len = None;
Ok(())
}
}
impl Bits {
pub fn push_mode_data<M: EncodingMode>(&mut self, data: &[u8]) -> QrResult<()> {
if let Some((position, byte)) = M::invalid_character(data) {
return Err(QrError::InvalidCharacter { position, byte });
}
match M::MODE {
Mode::Numeric => self.push_numeric_data(data),
Mode::Alphanumeric => self.push_alphanumeric_data(data),
Mode::Byte => self.push_byte_data(data),
Mode::Kanji => self.push_kanji_data(data),
}
}
}
#[cfg(test)]
mod typed_mode_tests {
use crate::bits::Bits;
use crate::mode::{AlphanumericMode, ByteMode, KanjiMode, NumericMode};
use crate::types::{EcLevel, Mode, QrError, Version};
#[test]
fn push_mode_data_matches_numeric_specific_encoder() {
let mut typed = Bits::new(Version::Normal(1));
let mut direct = Bits::new(Version::Normal(1));
assert_eq!(typed.push_mode_data::<NumericMode>(b"01234567"), Ok(()));
assert_eq!(direct.push_numeric_data(b"01234567"), Ok(()));
assert_eq!(typed.into_bytes(), direct.into_bytes());
}
#[test]
fn push_mode_data_matches_other_specific_encoders() {
let mut alphanumeric = Bits::new(Version::Normal(1));
let mut byte = Bits::new(Version::Normal(1));
let mut kanji = Bits::new(Version::Normal(1));
assert_eq!(alphanumeric.push_mode_data::<AlphanumericMode>(b"AC-42"), Ok(()));
assert_eq!(byte.push_mode_data::<ByteMode>(b"\x12\x34"), Ok(()));
assert_eq!(kanji.push_mode_data::<KanjiMode>(b"\x93\x5f\xe4\xaa"), Ok(()));
}
#[test]
fn push_mode_data_rejects_invalid_mode_input_before_writing() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(
bits.push_mode_data::<NumericMode>(b"12a"),
Err(QrError::InvalidCharacter { position: 2, byte: b'a' })
);
assert!(bits.into_bytes().is_empty());
}
#[test]
fn direct_modes_reject_invalid_input_without_changing_existing_bits() {
let cases: &[(Mode, &[u8], usize, u8)] = &[
(Mode::Numeric, b"12/4", 2, b'/'),
(Mode::Alphanumeric, b"ABc", 2, b'c'),
(Mode::Kanji, b"\x00\x00", 0, 0),
(Mode::Kanji, b"\xeb\xc0", 0, 0xeb),
(Mode::Kanji, b"\x93\x5f\x81", 2, 0x81),
(Mode::Kanji, b"\x93\x5f\xe0\x00", 2, 0xe0),
];
for &(mode, data, position, byte) in cases {
let mut bits = Bits::new(Version::Normal(1));
bits.push_byte_data(b"seed").unwrap();
bits.push_terminator(EcLevel::L).unwrap();
let previous_data = bits.data.clone();
let previous_modes = bits.encoding_modes();
let previous_payload_len = bits.payload_bits_len();
let result = match mode {
Mode::Numeric => bits.push_numeric_data(data),
Mode::Alphanumeric => bits.push_alphanumeric_data(data),
Mode::Kanji => bits.push_kanji_data(data),
Mode::Byte => unreachable!(),
};
assert_eq!(result, Err(QrError::InvalidCharacter { position, byte }), "{mode:?} {data:?}");
assert_eq!(bits.data, previous_data);
assert_eq!(bits.encoding_modes(), previous_modes);
assert_eq!(bits.payload_bits_len(), previous_payload_len);
}
}
#[test]
fn mode_length_overflow_does_not_write_a_mode_header() {
let mut numeric = Bits::new(Version::Micro(2));
let mut alphanumeric = Bits::new(Version::Micro(2));
let mut byte = Bits::new(Version::Micro(3));
let mut kanji = Bits::new(Version::Micro(3));
let kanji_data = b"\x93\x5f".repeat(8);
assert_eq!(numeric.push_numeric_data(b"0123456789012345"), Err(QrError::DataTooLong));
assert_eq!(alphanumeric.push_alphanumeric_data(b"ABCDEFGH"), Err(QrError::DataTooLong));
assert_eq!(byte.push_byte_data(b"0123456789012345"), Err(QrError::DataTooLong));
assert_eq!(kanji.push_kanji_data(&kanji_data), Err(QrError::DataTooLong));
for bits in [numeric, alphanumeric, byte, kanji] {
assert!(bits.encoding_modes().is_empty());
assert!(bits.into_bytes().is_empty());
}
}
#[test]
fn unsupported_mode_is_rejected_before_length_overflow_without_writing() {
let mut alphanumeric = Bits::new(Version::Micro(1));
let mut byte = Bits::new(Version::Micro(2));
assert_eq!(alphanumeric.push_alphanumeric_data(b"ABCDEFGH"), Err(QrError::UnsupportedCharacterSet));
assert_eq!(byte.push_byte_data(b"0123456789012345"), Err(QrError::UnsupportedCharacterSet));
assert!(alphanumeric.into_bytes().is_empty());
assert!(byte.into_bytes().is_empty());
}
#[test]
fn direct_mode_errors_preserve_unsupported_mode_precedence() {
let cases: &[(Mode, Version, &[u8], QrError)] = &[
(Mode::Alphanumeric, Version::Micro(1), b"c", QrError::UnsupportedCharacterSet),
(Mode::Kanji, Version::Micro(2), b"?", QrError::UnsupportedCharacterSet),
(Mode::Numeric, Version::Micro(1), b"a", QrError::InvalidCharacter { position: 0, byte: b'a' }),
(Mode::Alphanumeric, Version::Micro(2), b"c", QrError::InvalidCharacter { position: 0, byte: b'c' }),
(Mode::Kanji, Version::Micro(3), b"?", QrError::InvalidCharacter { position: 0, byte: b'?' }),
];
for &(mode, version, data, error) in cases {
let mut bits = Bits::new(version);
bits.push_numeric_data(b"1").unwrap();
let previous_data = bits.data.clone();
let previous_len = bits.len();
let previous_modes = bits.encoding_modes();
let result = match mode {
Mode::Numeric => bits.push_numeric_data(data),
Mode::Alphanumeric => bits.push_alphanumeric_data(data),
Mode::Kanji => bits.push_kanji_data(data),
Mode::Byte => unreachable!(),
};
assert_eq!(result, Err(error), "{mode:?} {version:?} {data:?}");
assert_eq!(bits.data, previous_data);
assert_eq!(bits.len(), previous_len);
assert_eq!(bits.encoding_modes(), previous_modes);
}
}
}
#[cfg(test)]
mod kanji_tests {
use crate::bits::Bits;
use crate::types::{QrError, Version};
#[test]
fn test_iso_18004_example() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_kanji_data(b"\x93\x5f\xe4\xaa"), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b1000_0000, 0b0010_0110, 0b11001111, 0b1_1101010, 0b101010_00]);
}
#[test]
fn test_micro_qr_unsupported() {
let mut bits = Bits::new(Version::Micro(2));
assert_eq!(bits.push_kanji_data(b"?"), Err(QrError::UnsupportedCharacterSet));
}
#[test]
fn test_data_too_long() {
let mut bits = Bits::new(Version::Micro(3));
assert_eq!(bits.push_kanji_data(b"\x93_\x93_\x93_\x93_\x93_\x93_\x93_\x93_"), Err(QrError::DataTooLong));
}
}
impl Bits {
pub fn push_fnc1_first_position(&mut self) -> QrResult<()> {
self.push_mode_indicator(ExtendedMode::Fnc1First)
}
pub fn push_fnc1_second_position(&mut self, application_indicator: u8) -> QrResult<()> {
self.push_mode_indicator(ExtendedMode::Fnc1Second)?;
self.push_number(8, u16::from(application_indicator));
Ok(())
}
}
impl Bits {
pub fn push_structured_append_header(&mut self, position: u8, total: u8, parity: u8) -> QrResult<()> {
if self.version.is_micro() {
return Err(QrError::UnsupportedCharacterSet);
}
if !(2..=16).contains(&total) || !(1..=total).contains(&position) {
return Err(QrError::InvalidStructuredAppend {
value: if !(2..=16).contains(&total) { total } else { position },
});
}
let sequence = (u16::from(position - 1) << 4) | u16::from(total - 1);
self.reserve(20);
self.push_mode_indicator(ExtendedMode::StructuredAppend)?;
self.push_number(8, sequence);
self.push_number(8, u16::from(parity));
Ok(())
}
}
#[cfg(test)]
mod structured_append_tests {
use crate::bits::Bits;
use crate::types::{EcLevel, QrError, Version};
#[test]
fn test_header_bit_layout() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_structured_append_header(1, 3, 0x5a), Ok(()));
assert_eq!(bits.into_bytes(), vec![0x30, 0x25, 0xA0]);
}
#[test]
fn test_header_bit_layout_second_of_two() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_structured_append_header(2, 2, 0xff), Ok(()));
assert_eq!(bits.into_bytes(), vec![0x31, 0x1F, 0xF0]);
}
#[test]
fn test_header_value_16_uses_max_nibble() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_structured_append_header(16, 16, 0x00), Ok(()));
assert_eq!(bits.into_bytes(), vec![0x3F, 0xF0, 0x00]);
}
#[test]
fn test_micro_rejected() {
let mut bits = Bits::new(Version::Micro(2));
assert_eq!(bits.push_structured_append_header(1, 2, 0), Err(QrError::UnsupportedCharacterSet));
}
#[test]
fn test_invalid_total() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_structured_append_header(1, 1, 0), Err(QrError::InvalidStructuredAppend { value: 1 }));
assert_eq!(bits.push_structured_append_header(1, 17, 0), Err(QrError::InvalidStructuredAppend { value: 17 }));
}
#[test]
fn test_invalid_position() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_structured_append_header(0, 3, 0), Err(QrError::InvalidStructuredAppend { value: 0 }));
assert_eq!(bits.push_structured_append_header(4, 3, 0), Err(QrError::InvalidStructuredAppend { value: 4 }));
}
#[test]
fn test_header_then_data_round_trips() {
let mut bits = Bits::new(Version::Normal(1));
bits.push_structured_append_header(1, 2, 0).unwrap();
bits.push_byte_data(b"ab").unwrap();
assert!(bits.push_terminator(EcLevel::M).is_ok());
}
}
static DATA_LENGTHS: [[usize; 4]; 44] = [
[152, 128, 104, 72],
[272, 224, 176, 128],
[440, 352, 272, 208],
[640, 512, 384, 288],
[864, 688, 496, 368],
[1088, 864, 608, 480],
[1248, 992, 704, 528],
[1552, 1232, 880, 688],
[1856, 1456, 1056, 800],
[2192, 1728, 1232, 976],
[2592, 2032, 1440, 1120],
[2960, 2320, 1648, 1264],
[3424, 2672, 1952, 1440],
[3688, 2920, 2088, 1576],
[4184, 3320, 2360, 1784],
[4712, 3624, 2600, 2024],
[5176, 4056, 2936, 2264],
[5768, 4504, 3176, 2504],
[6360, 5016, 3560, 2728],
[6888, 5352, 3880, 3080],
[7456, 5712, 4096, 3248],
[8048, 6256, 4544, 3536],
[8752, 6880, 4912, 3712],
[9392, 7312, 5312, 4112],
[10208, 8000, 5744, 4304],
[10960, 8496, 6032, 4768],
[11744, 9024, 6464, 5024],
[12248, 9544, 6968, 5288],
[13048, 10136, 7288, 5608],
[13880, 10984, 7880, 5960],
[14744, 11640, 8264, 6344],
[15640, 12328, 8920, 6760],
[16568, 13048, 9368, 7208],
[17528, 13800, 9848, 7688],
[18448, 14496, 10288, 7888],
[19472, 15312, 10832, 8432],
[20528, 15936, 11408, 8768],
[21616, 16816, 12016, 9136],
[22496, 17728, 12656, 9776],
[23648, 18672, 13328, 10208],
[20, 0, 0, 0],
[40, 32, 0, 0],
[84, 68, 0, 0],
[128, 112, 80, 0],
];
impl Bits {
pub fn push_terminator(&mut self, ec_level: EcLevel) -> QrResult<()> {
let terminator_size = match self.version {
Version::Micro(a) => a.as_usize() * 2 + 1,
Version::Normal(_) => 4,
};
let cur_length = self.len();
let data_length = self.max_len(ec_level)?;
if cur_length > data_length {
return Err(QrError::DataTooLong);
}
self.payload_bits_len = Some(cur_length);
let terminator_size = min(terminator_size, data_length - cur_length);
if terminator_size > 0 {
self.push_number(terminator_size, 0);
}
if self.len() < data_length {
const PADDING_BYTES: &[u8] = &[0b1110_1100, 0b0001_0001];
self.bit_offset = 0;
let data_bytes_length = data_length / 8;
let padding_bytes_count = data_bytes_length.saturating_sub(self.data.len());
let padding = PADDING_BYTES.iter().copied().cycle().take(padding_bytes_count);
self.data.extend(padding);
}
if self.len() < data_length {
self.data.push(0);
}
Ok(())
}
}
#[cfg(test)]
mod finish_tests {
use crate::bits::Bits;
use crate::types::{EcLevel, QrError, Version};
#[test]
fn test_hello_world() {
let mut bits = Bits::new(Version::Normal(1));
assert_eq!(bits.push_alphanumeric_data(b"HELLO WORLD"), Ok(()));
assert_eq!(bits.push_terminator(EcLevel::Q), Ok(()));
assert_eq!(
bits.into_bytes(),
vec![
0b00100000, 0b01011011, 0b00001011, 0b01111000, 0b11010001, 0b01110010, 0b11011100, 0b01001101,
0b01000011, 0b01000000, 0b11101100, 0b00010001, 0b11101100,
]
);
}
#[test]
fn test_too_long() {
let mut bits = Bits::new(Version::Micro(1));
assert_eq!(bits.push_numeric_data(b"9999999"), Ok(()));
assert_eq!(bits.push_terminator(EcLevel::L), Err(QrError::DataTooLong));
}
#[test]
fn test_no_terminator() {
let mut bits = Bits::new(Version::Micro(1));
assert_eq!(bits.push_numeric_data(b"99999"), Ok(()));
assert_eq!(bits.push_terminator(EcLevel::L), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b101_11111, 0b00111_110, 0b0011_0000]);
}
#[test]
fn test_no_padding() {
let mut bits = Bits::new(Version::Micro(1));
assert_eq!(bits.push_numeric_data(b"9999"), Ok(()));
assert_eq!(bits.push_terminator(EcLevel::L), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b100_11111, 0b00111_100, 0b1_000_0000]);
}
#[test]
fn test_micro_version_1_half_byte_padding() {
let mut bits = Bits::new(Version::Micro(1));
assert_eq!(bits.push_numeric_data(b"999"), Ok(()));
assert_eq!(bits.push_terminator(EcLevel::L), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b011_11111, 0b00111_000, 0b0000_0000]);
}
#[test]
fn test_micro_version_1_full_byte_padding() {
let mut bits = Bits::new(Version::Micro(1));
assert_eq!(bits.push_numeric_data(b""), Ok(()));
assert_eq!(bits.push_terminator(EcLevel::L), Ok(()));
assert_eq!(bits.into_bytes(), vec![0b000_000_00, 0b11101100, 0]);
}
}
impl Bits {
pub fn push_segments<I>(&mut self, data: &[u8], segments_iter: I) -> QrResult<()>
where
I: Iterator<Item = Segment>,
{
for segment in segments_iter {
let slice = &data[segment.begin..segment.end];
match segment.mode {
Mode::Numeric => self.push_numeric_data(slice),
Mode::Alphanumeric => self.push_alphanumeric_data(slice),
Mode::Byte => self.push_byte_data(slice),
Mode::Kanji => self.push_kanji_data(slice),
}?;
}
Ok(())
}
pub fn push_optimal_data(&mut self, data: &[u8]) -> QrResult<()> {
let segments = Parser::new(data).optimize(self.version);
self.push_segments(data, segments)
}
}
#[cfg(test)]
mod encode_tests {
use crate::bits::Bits;
use crate::types::{EcLevel, QrError, QrResult, Version};
fn encode(data: &[u8], version: Version, ec_level: EcLevel) -> QrResult<Vec<u8>> {
let mut bits = Bits::new(version);
bits.push_optimal_data(data)?;
bits.push_terminator(ec_level)?;
Ok(bits.into_bytes())
}
#[test]
fn test_alphanumeric() {
let res = encode(b"HELLO WORLD", Version::Normal(1), EcLevel::Q);
assert_eq!(
res,
Ok(vec![
0b00100000, 0b01011011, 0b00001011, 0b01111000, 0b11010001, 0b01110010, 0b11011100, 0b01001101,
0b01000011, 0b01000000, 0b11101100, 0b00010001, 0b11101100,
])
);
}
#[test]
fn test_auto_mode_switch() {
let res = encode(b"123A", Version::Micro(2), EcLevel::L);
assert_eq!(res, Ok(vec![0b0_0011_000, 0b1111011_1, 0b001_00101, 0b0_00000_00, 0b11101100]));
}
#[test]
fn test_too_long() {
let res = encode(b">>>>>>>>", Version::Normal(1), EcLevel::H);
assert_eq!(res, Err(QrError::DataTooLong));
}
}
pub fn data_capacity_bits(version: Version, ec_level: EcLevel) -> QrResult<usize> {
version.fetch(ec_level, &DATA_LENGTHS)
}
pub fn encode_auto(data: &[u8], ec_level: EcLevel) -> QrResult<Bits> {
if data.len() > crate::limits::DEFAULT_MAX_DATA_LENGTH {
return Err(QrError::DataTooLong);
}
encode_auto_with_max_version(data, ec_level, 40)
}
pub fn encode_auto_with_max_version(data: &[u8], ec_level: EcLevel, max_version: i16) -> QrResult<Bits> {
if !(1..=40).contains(&max_version) {
return Err(QrError::InvalidResourceLimits);
}
let segments = Parser::new(data).collect::<Vec<Segment>>();
let mut checkpoints = [0_i16; 4];
let mut checkpoint_count = 0;
for candidate in [9_i16, 26, 40, max_version] {
if candidate <= max_version && !checkpoints[..checkpoint_count].contains(&candidate) {
checkpoints[checkpoint_count] = candidate;
checkpoint_count += 1;
}
}
for candidate in checkpoints[..checkpoint_count].iter() {
let version = Version::Normal(*candidate);
let opt_segments = optimize_segments(&segments, version);
let total_len = total_encoded_len(&opt_segments, version);
let data_capacity = version.fetch(ec_level, &DATA_LENGTHS)?;
if total_len <= data_capacity {
let min_version = find_min_version_up_to(total_len, ec_level, *candidate);
let mut bits = Bits::new(min_version);
bits.reserve(total_len);
bits.push_segments(data, opt_segments.into_iter())?;
bits.push_terminator(ec_level)?;
return Ok(bits);
}
}
Err(QrError::DataTooLong)
}
fn find_min_version_up_to(length: usize, ec_level: EcLevel, max_version: i16) -> Version {
for version in 1..=max_version {
if DATA_LENGTHS[(version - 1) as usize][ec_level as usize] >= length {
return Version::Normal(version);
}
}
Version::Normal(max_version)
}
pub fn encode_auto_micro(data: &[u8], ec_level: EcLevel) -> QrResult<Bits> {
Version::Micro(4).fetch(ec_level, &DATA_LENGTHS)?;
if data.len() > crate::limits::DEFAULT_MAX_DATA_LENGTH {
return Err(QrError::DataTooLong);
}
let segments = Parser::new(data).collect::<Vec<Segment>>();
for micro_version in 1..=4 {
let version = Version::Micro(micro_version);
let data_capacity = match version.fetch(ec_level, &DATA_LENGTHS) {
Ok(cap) if cap > 0 => cap,
_ => continue,
};
if !segments.iter().all(|segment| match segment.mode {
Mode::Numeric => true,
Mode::Alphanumeric => micro_version >= 2,
Mode::Byte | Mode::Kanji => micro_version >= 3,
}) {
continue;
}
let opt_segments = optimize_segments(&segments, version);
let total_len = total_encoded_len(&opt_segments, version);
if total_len <= data_capacity {
let mut bits = Bits::new(version);
bits.reserve(total_len);
bits.push_segments(data, opt_segments.into_iter())?;
bits.push_terminator(ec_level)?;
return Ok(bits);
}
}
Err(QrError::DataTooLong)
}
pub fn find_min_version(length: usize, ec_level: EcLevel) -> Version {
let mut base = 0_usize;
let mut size = 39;
while size > 1 {
let half = size / 2;
let mid = base + half;
base = if DATA_LENGTHS[mid][ec_level as usize] > length { base } else { mid };
size -= half;
}
base = if DATA_LENGTHS[base][ec_level as usize] >= length { base } else { base + 1 };
Version::Normal((base + 1).as_i16())
}
#[cfg(test)]
mod encode_auto_tests {
use crate::bits::{encode_auto, encode_auto_micro, encode_auto_with_max_version, find_min_version};
use crate::types::{EcLevel, QrError, Version};
#[test]
fn test_find_min_version() {
assert_eq!(find_min_version(60, EcLevel::L), Version::Normal(1));
assert_eq!(find_min_version(200, EcLevel::L), Version::Normal(2));
assert_eq!(find_min_version(200, EcLevel::H), Version::Normal(3));
assert_eq!(find_min_version(20000, EcLevel::L), Version::Normal(37));
assert_eq!(find_min_version(640, EcLevel::L), Version::Normal(4));
assert_eq!(find_min_version(641, EcLevel::L), Version::Normal(5));
assert_eq!(find_min_version(999999, EcLevel::H), Version::Normal(40));
}
#[test]
fn test_alpha_q() {
let bits = encode_auto(b"HELLO WORLD", EcLevel::Q).unwrap();
assert_eq!(bits.version(), Version::Normal(1));
}
#[test]
fn test_alpha_h() {
let bits = encode_auto(b"HELLO WORLD", EcLevel::H).unwrap();
assert_eq!(bits.version(), Version::Normal(2));
}
#[test]
fn test_mixed() {
let bits = encode_auto(b"This is a mixed data test. 1234567890", EcLevel::H).unwrap();
assert_eq!(bits.version(), Version::Normal(4));
}
#[test]
fn bounded_auto_encoding_rejects_version_overflow_and_caps_search() {
assert!(matches!(encode_auto_with_max_version(b"x", EcLevel::M, 0), Err(QrError::InvalidResourceLimits)));
assert!(matches!(encode_auto_with_max_version(&[0_u8; 128], EcLevel::M, 1), Err(QrError::DataTooLong)));
}
#[test]
fn micro_auto_selection_skips_versions_that_cannot_encode_the_payload_mode() {
let cases: &[(&[u8], EcLevel, i16)] = &[
(b"1", EcLevel::L, 1),
(b"A", EcLevel::L, 2),
(b"a", EcLevel::L, 3),
(b"abc", EcLevel::L, 3),
(b"\x93\x5f", EcLevel::L, 3),
(b"123A", EcLevel::L, 2),
(b"123a", EcLevel::L, 3),
(b"\x93\x5f1", EcLevel::L, 3),
(b"A", EcLevel::Q, 4),
];
for &(data, ec_level, expected) in cases {
let bits = encode_auto_micro(data, ec_level).unwrap();
assert_eq!(bits.version(), Version::Micro(expected), "{data:?} {ec_level:?}");
let mut fixed = crate::bits::Bits::new(Version::Micro(expected));
fixed.push_optimal_data(data).unwrap();
fixed.push_terminator(ec_level).unwrap();
assert_eq!(bits.into_bytes(), fixed.into_bytes(), "{data:?} {ec_level:?}");
}
}
#[test]
fn micro_auto_selection_distinguishes_unsupported_ec_from_payload_overflow() {
assert!(matches!(
encode_auto_micro(b"1", EcLevel::H),
Err(QrError::InvalidVersion { version: Version::Micro(4), ec_level: EcLevel::H })
));
assert!(matches!(encode_auto_micro(&[b'1'; 100], EcLevel::L), Err(QrError::DataTooLong)));
}
}