update eci_string_builder

This change modifies how the ECIStringBuilder works. Perviously it encoded data as it went, whenever the eci state changed. The new method only decodes data when it is requested, simply appending bytes as it goes otherwise.

This should improve performance in a few situations.

This release also uses the ECIStringBuilder in qrcodes, which previously handled their own decoding.
This commit is contained in:
Henry Schimke
2023-03-07 17:09:42 -06:00
parent 19d5b6276c
commit d5e6a5d0a7
4 changed files with 183 additions and 146 deletions

View File

@@ -23,8 +23,6 @@
use std::fmt; use std::fmt;
use crate::common::Result;
use super::{CharacterSet, Eci}; use super::{CharacterSet, Eci};
/** /**
@@ -32,35 +30,36 @@ use super::{CharacterSet, Eci};
* *
* @author Alex Geller * @author Alex Geller
*/ */
#[derive(Default)]
pub struct ECIStringBuilder { pub struct ECIStringBuilder {
current_bytes: Vec<u8>, is_eci: bool,
result: String, eci_result: Option<String>,
current_charset: CharacterSet, //= StandardCharsets.ISO_8859_1; bytes: Vec<u8>,
eci_positions: Vec<(Eci, usize, usize)>, // (Eci, start, end)
} }
impl ECIStringBuilder { impl ECIStringBuilder {
pub fn new() -> Self {
Self {
current_bytes: Vec::new(),
result: String::new(),
current_charset: CharacterSet::ISO8859_1,
}
}
pub fn with_capacity(initial_capacity: usize) -> Self { pub fn with_capacity(initial_capacity: usize) -> Self {
Self { Self {
current_bytes: Vec::with_capacity(initial_capacity), eci_result: None,
result: String::with_capacity(initial_capacity), bytes: Vec::with_capacity(initial_capacity),
current_charset: CharacterSet::ISO8859_1, eci_positions: Vec::default(),
is_eci: false,
} }
} }
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
/** /**
* Appends {@code value} as a byte value * Appends {@code value} as a byte value
* *
* @param value character whose lowest byte is to be appended * @param value character whose lowest byte is to be appended
*/ */
pub fn append_char(&mut self, value: char) { pub fn append_char(&mut self, value: char) {
self.current_bytes.push(value as u8); self.eci_result = None;
self.bytes.push(value as u8);
} }
/** /**
@@ -69,7 +68,13 @@ impl ECIStringBuilder {
* @param value byte to append * @param value byte to append
*/ */
pub fn append_byte(&mut self, value: u8) { pub fn append_byte(&mut self, value: u8) {
self.current_bytes.push(value); self.eci_result = None;
self.bytes.push(value)
}
pub fn append_bytes(&mut self, value: &[u8]) {
self.eci_result = None;
self.bytes.extend_from_slice(value)
} }
/** /**
@@ -78,12 +83,11 @@ impl ECIStringBuilder {
* @param value string to append * @param value string to append
*/ */
pub fn append_string(&mut self, value: &str) { pub fn append_string(&mut self, value: &str) {
value if !value.is_ascii() {
.as_bytes() self.append_eci(Eci::UTF8);
.iter() }
.map(|b| self.current_bytes.push(*b)) self.eci_result = None;
.count(); self.bytes.extend_from_slice(value.as_bytes());
// self.current_bytes.push(value.as_bytes());
} }
/** /**
@@ -101,49 +105,78 @@ impl ECIStringBuilder {
* @param value ECI value to append, as an int * @param value ECI value to append, as an int
* @throws FormatException on invalid ECI value * @throws FormatException on invalid ECI value
*/ */
pub fn appendECI(&mut self, eci: Eci) -> Result<()> { pub fn append_eci(&mut self, eci: Eci) {
self.encodeCurrentBytesIfAny(); self.eci_result = None;
if !self.is_eci && eci != Eci::ISO8859_1 {
self.is_eci = true;
}
self.current_charset = eci.into(); //CharacterSet::get_character_set_by_eci(value).ok(); if self.is_eci {
if let Some(last) = self.eci_positions.last_mut() {
last.2 = self.bytes.len()
}
// if let Ok(character_set_eci) = CharacterSetECI::getCharacterSetECIByValue(value) { self.eci_positions.push((eci, self.bytes.len(), 0));
// // dbg!( }
// // character_set_eci,
// // CharacterSetECI::getCharset(&character_set_eci).name(),
// // CharacterSetECI::getCharset(&character_set_eci).whatwg_name()
// // );
// self.current_charset = Some(character_set_eci);
// } else {
// self.current_charset = None
// }
// self.current_charset = CharacterSetECI::getCharset(&character_set_eci);
Ok(())
} }
/// Finishes encoding anything in the buffer using the current ECI and resets. /// Finishes encoding anything in the buffer using the current ECI and resets.
/// ///
/// This function can panic /// This function can panic
pub fn encodeCurrentBytesIfAny(&mut self) { pub fn encodeCurrentBytesIfAny(&self) -> String {
if ![CharacterSet::Binary, CharacterSet::Unknown].contains(&self.current_charset) { let mut encoded_string = String::with_capacity(self.bytes.len());
if self.current_charset == CharacterSet::UTF8 { // First encode the first set
if !self.current_bytes.is_empty() { let (_, end, _) =
self.result.push_str( *self
&String::from_utf8(std::mem::take(&mut self.current_bytes)).unwrap(), .eci_positions
); .first()
self.current_bytes.clear(); .unwrap_or(&(Eci::ISO8859_1, self.bytes.len(), 0));
encoded_string.push_str(
&Self::encode_segment(&self.bytes[0..end], Eci::ISO8859_1).unwrap_or_default(),
);
// If there are more sets, encode each of them in turn
for (eci, eci_start, eci_end) in &self.eci_positions {
// let (_,end) = *self.eci_positions.first().unwrap_or(&(*eci, self.bytes.len()));
let end = if *eci_end == 0 {
self.bytes.len()
} else {
*eci_end
};
encoded_string.push_str(
&Self::encode_segment(&self.bytes[*eci_start..end], *eci).unwrap_or_default(),
);
}
// Return the result
encoded_string
}
fn encode_segment(bytes: &[u8], eci: Eci) -> Option<String> {
let mut encoded_string = String::with_capacity(bytes.len());
if ![Eci::Binary, Eci::Unknown].contains(&eci) {
if eci == Eci::UTF8 {
if !bytes.is_empty() {
encoded_string.push_str(&CharacterSet::UTF8.decode(bytes).ok()?);
} else {
return None;
} }
} else if !self.current_bytes.is_empty() { } else if !bytes.is_empty() {
let bytes = std::mem::take(&mut self.current_bytes); encoded_string.push_str(&CharacterSet::from(eci).decode(bytes).ok()?);
self.current_bytes.clear(); } else {
let encoded_value = self.current_charset.decode(&bytes).unwrap(); return None;
self.result.push_str(&encoded_value);
} }
} else { } else {
for byte in &self.current_bytes { for byte in bytes {
self.result.push(char::from(*byte)) encoded_string.push(char::from(*byte))
} }
self.current_bytes.clear(); }
if encoded_string.is_empty() {
None
} else {
Some(encoded_string)
} }
} }
@@ -153,8 +186,7 @@ impl ECIStringBuilder {
* @param value characters to append * @param value characters to append
*/ */
pub fn appendCharacters(&mut self, value: &str) { pub fn appendCharacters(&mut self, value: &str) {
self.encodeCurrentBytesIfAny(); self.append_string(value);
self.result.push_str(value);
} }
/** /**
@@ -163,19 +195,18 @@ impl ECIStringBuilder {
* @return length of string representation in characters * @return length of string representation in characters
*/ */
pub fn len(&mut self) -> usize { pub fn len(&mut self) -> usize {
self.encodeCurrentBytesIfAny(); //return toString().length(); self.bytes.len()
self.result.chars().count()
} }
/** /**
* @return true iff nothing has been appended * @return true iff nothing has been appended
*/ */
pub fn is_empty(&mut self) -> bool { pub fn is_empty(&mut self) -> bool {
self.current_bytes.is_empty() && self.result.is_empty() self.bytes.is_empty()
} }
pub fn build_result(mut self) -> Self { pub fn build_result(mut self) -> Self {
self.encodeCurrentBytesIfAny(); self.eci_result = Some(self.encodeCurrentBytesIfAny());
self self
} }
@@ -183,13 +214,10 @@ impl ECIStringBuilder {
impl fmt::Display for ECIStringBuilder { impl fmt::Display for ECIStringBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
//self.encodeCurrentBytesIfAny(); if let Some(res) = &self.eci_result {
write!(f, "{}", self.result) write!(f, "{res}")
} } else {
} write!(f, "{}", self.encodeCurrentBytesIfAny())
}
impl Default for ECIStringBuilder {
fn default() -> Self {
Self::new()
} }
} }

View File

@@ -739,21 +739,21 @@ fn decodeBase256Segment(
fn decodeECISegment(bits: &mut BitSource, result: &mut ECIStringBuilder) -> Result<bool> { fn decodeECISegment(bits: &mut BitSource, result: &mut ECIStringBuilder) -> Result<bool> {
let firstByte = bits.readBits(8)?; let firstByte = bits.readBits(8)?;
if firstByte <= 127 { if firstByte <= 127 {
result.appendECI(Eci::from(firstByte - 1))?; result.append_eci(Eci::from(firstByte - 1));
return Ok(true); return Ok(true);
} }
let secondByte = bits.readBits(8)?; let secondByte = bits.readBits(8)?;
if firstByte <= 191 { if firstByte <= 191 {
result.appendECI(Eci::from((firstByte - 128) * 254 + 127 + secondByte - 1))?; result.append_eci(Eci::from((firstByte - 128) * 254 + 127 + secondByte - 1));
return Ok((firstByte - 128) * 254 + 127 + secondByte - 1 > 900); return Ok((firstByte - 128) * 254 + 127 + secondByte - 1 > 900);
} }
let thirdByte = bits.readBits(8)?; let thirdByte = bits.readBits(8)?;
result.appendECI(Eci::from( result.append_eci(Eci::from(
(firstByte - 192) * 64516 + 16383 + (secondByte - 1) * 254 + thirdByte - 1, (firstByte - 192) * 64516 + 16383 + (secondByte - 1) * 254 + thirdByte - 1,
))?; ));
Ok((firstByte - 192) * 64516 + 16383 + (secondByte - 1) * 254 + thirdByte - 1 > 900) Ok((firstByte - 192) * 64516 + 16383 + (secondByte - 1) * 254 + thirdByte - 1 > 900)
} }

View File

@@ -128,7 +128,7 @@ pub fn decode(codewords: &[u32], ecLevel: &str) -> Result<DecoderRXingResult> {
codeIndex = numericCompaction(codewords, codeIndex, &mut result)? codeIndex = numericCompaction(codewords, codeIndex, &mut result)?
} }
ECI_CHARSET => { ECI_CHARSET => {
result.appendECI(Eci::from(codewords[codeIndex]))?; result.append_eci(Eci::from(codewords[codeIndex]));
codeIndex += 1; codeIndex += 1;
} }
ECI_GENERAL_PURPOSE => ECI_GENERAL_PURPOSE =>
@@ -234,54 +234,47 @@ pub fn decodeMacroBlock(
codeIndex += 1; codeIndex += 1;
match codewords[codeIndex] { match codewords[codeIndex] {
MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME => { MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME => {
let mut fileName = ECIStringBuilder::new(); let mut fileName = ECIStringBuilder::default();
codeIndex = textCompaction(codewords, codeIndex + 1, &mut fileName)?; codeIndex = textCompaction(codewords, codeIndex + 1, &mut fileName)?;
fileName = fileName.build_result();
resultMetadata.setFileName(fileName.to_string()); resultMetadata.setFileName(fileName.to_string());
} }
MACRO_PDF417_OPTIONAL_FIELD_SENDER => { MACRO_PDF417_OPTIONAL_FIELD_SENDER => {
let mut sender = ECIStringBuilder::new(); let mut sender = ECIStringBuilder::default();
codeIndex = textCompaction(codewords, codeIndex + 1, &mut sender)?; codeIndex = textCompaction(codewords, codeIndex + 1, &mut sender)?;
sender = sender.build_result();
resultMetadata.setSender(sender.to_string()); resultMetadata.setSender(sender.to_string());
} }
MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE => { MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE => {
let mut addressee = ECIStringBuilder::new(); let mut addressee = ECIStringBuilder::default();
codeIndex = textCompaction(codewords, codeIndex + 1, &mut addressee)?; codeIndex = textCompaction(codewords, codeIndex + 1, &mut addressee)?;
addressee = addressee.build_result();
resultMetadata.setAddressee(addressee.to_string()); resultMetadata.setAddressee(addressee.to_string());
} }
MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT => { MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT => {
let mut segmentCount = ECIStringBuilder::new(); let mut segmentCount = ECIStringBuilder::default();
codeIndex = numericCompaction(codewords, codeIndex + 1, &mut segmentCount)?; codeIndex = numericCompaction(codewords, codeIndex + 1, &mut segmentCount)?;
segmentCount = segmentCount.build_result();
let Ok(parsed_segment_count) = segmentCount.to_string().parse() else { let Ok(parsed_segment_count) = segmentCount.to_string().parse() else {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
}; };
resultMetadata.setSegmentCount(parsed_segment_count); resultMetadata.setSegmentCount(parsed_segment_count);
} }
MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP => { MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP => {
let mut timestamp = ECIStringBuilder::new(); let mut timestamp = ECIStringBuilder::default();
codeIndex = numericCompaction(codewords, codeIndex + 1, &mut timestamp)?; codeIndex = numericCompaction(codewords, codeIndex + 1, &mut timestamp)?;
timestamp = timestamp.build_result();
let Ok(parsed_timestamp) = timestamp.to_string().parse() else { let Ok(parsed_timestamp) = timestamp.to_string().parse() else {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
}; };
resultMetadata.setTimestamp(parsed_timestamp); resultMetadata.setTimestamp(parsed_timestamp);
} }
MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM => { MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM => {
let mut checksum = ECIStringBuilder::new(); let mut checksum = ECIStringBuilder::default();
codeIndex = numericCompaction(codewords, codeIndex + 1, &mut checksum)?; codeIndex = numericCompaction(codewords, codeIndex + 1, &mut checksum)?;
checksum = checksum.build_result();
let Ok(parsed_checksum ) = checksum.to_string().parse() else { let Ok(parsed_checksum ) = checksum.to_string().parse() else {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
}; };
resultMetadata.setChecksum(parsed_checksum); resultMetadata.setChecksum(parsed_checksum);
} }
MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE => { MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE => {
let mut fileSize = ECIStringBuilder::new(); let mut fileSize = ECIStringBuilder::default();
codeIndex = numericCompaction(codewords, codeIndex + 1, &mut fileSize)?; codeIndex = numericCompaction(codewords, codeIndex + 1, &mut fileSize)?;
fileSize = fileSize.build_result();
let Ok(parsed_file_size)= fileSize.to_string().parse() else { let Ok(parsed_file_size)= fileSize.to_string().parse() else {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
}; };
@@ -387,7 +380,7 @@ fn textCompaction(
subMode, subMode,
) )
.ok_or(Exceptions::ILLEGAL_STATE)?; .ok_or(Exceptions::ILLEGAL_STATE)?;
result.appendECI(Eci::from(codewords[codeIndex]))?; result.append_eci(Eci::from(codewords[codeIndex]));
codeIndex += 1; codeIndex += 1;
textCompactionData = vec![0; (codewords[0] as usize - codeIndex) * 2]; textCompactionData = vec![0; (codewords[0] as usize - codeIndex) * 2];
byteCompactionData = vec![0; (codewords[0] as usize - codeIndex) * 2]; byteCompactionData = vec![0; (codewords[0] as usize - codeIndex) * 2];
@@ -616,7 +609,7 @@ fn byteCompaction(
//handle leading ECIs //handle leading ECIs
while codeIndex < codewords[0] as usize && codewords[codeIndex] == ECI_CHARSET { while codeIndex < codewords[0] as usize && codewords[codeIndex] == ECI_CHARSET {
codeIndex += 1; codeIndex += 1;
result.appendECI(Eci::from(codewords[codeIndex]))?; result.append_eci(Eci::from(codewords[codeIndex]));
codeIndex += 1; codeIndex += 1;
} }
@@ -654,7 +647,7 @@ fn byteCompaction(
if code < TEXT_COMPACTION_MODE_LATCH { if code < TEXT_COMPACTION_MODE_LATCH {
result.append_byte(code as u8); result.append_byte(code as u8);
} else if code == ECI_CHARSET { } else if code == ECI_CHARSET {
result.appendECI(Eci::from(codewords[codeIndex]))?; result.append_eci(Eci::from(codewords[codeIndex]));
codeIndex += 1; codeIndex += 1;
} else { } else {
codeIndex -= 1; codeIndex -= 1;

View File

@@ -15,7 +15,9 @@
*/ */
use crate::{ use crate::{
common::{BitSource, CharacterSet, DecoderRXingResult, Eci, Result, StringUtils}, common::{
BitSource, CharacterSet, DecoderRXingResult, ECIStringBuilder, Eci, Result, StringUtils,
},
DecodingHintDictionary, Exceptions, DecodingHintDictionary, Exceptions,
}; };
@@ -46,7 +48,7 @@ pub fn decode(
hints: &DecodingHintDictionary, hints: &DecodingHintDictionary,
) -> Result<DecoderRXingResult> { ) -> Result<DecoderRXingResult> {
let mut bits = BitSource::new(bytes.to_owned()); let mut bits = BitSource::new(bytes.to_owned());
let mut result = String::with_capacity(50); let mut result = ECIStringBuilder::with_capacity(50); //String::with_capacity(50);
let mut byteSegments = vec![vec![0u8; 0]; 0]; let mut byteSegments = vec![vec![0u8; 0]; 0];
let mut symbolSequence = -1; let mut symbolSequence = -1;
let mut parityData = -1; let mut parityData = -1;
@@ -91,7 +93,7 @@ pub fn decode(
Mode::ECI => { Mode::ECI => {
// Count doesn't apply to ECI // Count doesn't apply to ECI
let value = parseECIValue(&mut bits)?; let value = parseECIValue(&mut bits)?;
currentCharacterSetECI = CharacterSet::from(Eci::from(value)).into(); //CharacterSet::get_character_set_by_eci(value).ok(); currentCharacterSetECI = CharacterSet::from(value).into(); //CharacterSet::get_character_set_by_eci(value).ok();
if currentCharacterSetECI.is_none() { if currentCharacterSetECI.is_none() {
return Err(Exceptions::format_with(format!( return Err(Exceptions::format_with(format!(
"Value of {value} not valid" "Value of {value} not valid"
@@ -142,7 +144,27 @@ pub fn decode(
} }
} }
let symbologyModifier = if currentCharacterSetECI.is_some() { let symbologyModifier = get_symbology_identifier(
currentCharacterSetECI.is_some(),
hasFNC1first,
hasFNC1second,
);
Ok(DecoderRXingResult::with_all(
bytes.to_owned(),
result.build_result().to_string(),
byteSegments.to_vec(),
format!("{}", u8::from(ecLevel)),
symbolSequence,
parityData,
symbologyModifier,
String::default(),
false,
))
}
fn get_symbology_identifier(has_charset: bool, hasFNC1first: bool, hasFNC1second: bool) -> u32 {
if has_charset {
if hasFNC1first { if hasFNC1first {
4 4
} else if hasFNC1second { } else if hasFNC1second {
@@ -156,25 +178,17 @@ pub fn decode(
5 5
} else { } else {
1 1
}; }
Ok(DecoderRXingResult::with_all(
bytes.to_owned(),
result,
byteSegments.to_vec(),
format!("{}", u8::from(ecLevel)),
symbolSequence,
parityData,
symbologyModifier,
String::default(),
false,
))
} }
/** /**
* See specification GBT 18284-2000 * See specification GBT 18284-2000
*/ */
fn decodeHanziSegment(bits: &mut BitSource, result: &mut String, count: usize) -> Result<()> { fn decodeHanziSegment(
bits: &mut BitSource,
result: &mut ECIStringBuilder,
count: usize,
) -> Result<()> {
// Don't crash trying to read more bits than we have available. // Don't crash trying to read more bits than we have available.
if count * 13 > bits.available() { if count * 13 > bits.available() {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
@@ -203,17 +217,15 @@ fn decodeHanziSegment(bits: &mut BitSource, result: &mut String, count: usize) -
count -= 1; count -= 1;
} }
let gb_encoder = CharacterSet::GB18030; result.append_eci(Eci::GB18030);
let encode_string = gb_encoder result.append_bytes(&buffer);
.decode(&buffer)
.map_err(|e| Exceptions::parse_with(format!("unable to decode buffer {buffer:?}: {e}")))?;
result.push_str(&encode_string);
Ok(()) Ok(())
} }
fn decodeKanjiSegment( fn decodeKanjiSegment(
bits: &mut BitSource, bits: &mut BitSource,
result: &mut String, result: &mut ECIStringBuilder,
count: usize, count: usize,
currentCharacterSetECI: Option<CharacterSet>, currentCharacterSetECI: Option<CharacterSet>,
hints: &DecodingHintDictionary, hints: &DecodingHintDictionary,
@@ -265,18 +277,15 @@ fn decodeKanjiSegment(
CharacterSet::Shift_JIS CharacterSet::Shift_JIS
}; };
let encode_string = encoder result.append_eci(Eci::from(encoder));
.decode(&buffer) result.append_bytes(&buffer);
.map_err(|e| Exceptions::parse_with(format!("unable to decode buffer {buffer:?}: {e}")))?;
result.push_str(&encode_string);
Ok(()) Ok(())
} }
fn decodeByteSegment( fn decodeByteSegment(
bits: &mut BitSource, bits: &mut BitSource,
result: &mut String, result: &mut ECIStringBuilder,
count: usize, count: usize,
currentCharacterSetECI: Option<CharacterSet>, currentCharacterSetECI: Option<CharacterSet>,
byteSegments: &mut Vec<Vec<u8>>, byteSegments: &mut Vec<Vec<u8>>,
@@ -320,11 +329,9 @@ fn decodeByteSegment(
// ) // )
}; };
let encode_string = encoding.decode(&readBytes).map_err(|e| { result.append_eci(Eci::from(encoding));
Exceptions::parse_with(format!("unable to decode buffer {readBytes:?}: {e}")) result.append_bytes(&readBytes);
})?;
result.push_str(&encode_string);
byteSegments.push(readBytes); byteSegments.push(readBytes);
Ok(()) Ok(())
@@ -343,20 +350,21 @@ fn toAlphaNumericChar(value: u32) -> Result<char> {
fn decodeAlphanumericSegment( fn decodeAlphanumericSegment(
bits: &mut BitSource, bits: &mut BitSource,
result: &mut String, result: &mut ECIStringBuilder,
count: usize, count: usize,
fc1InEffect: bool, fc1InEffect: bool,
) -> Result<()> { ) -> Result<()> {
let mut r_hld = String::with_capacity(count);
// Read two characters at a time // Read two characters at a time
let start = result.len(); let start = 0;
let mut count = count; let mut count = count;
while count > 1 { while count > 1 {
if bits.available() < 11 { if bits.available() < 11 {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
} }
let nextTwoCharsBits = bits.readBits(11)?; let nextTwoCharsBits = bits.readBits(11)?;
result.push(toAlphaNumericChar(nextTwoCharsBits / 45)?); r_hld.push(toAlphaNumericChar(nextTwoCharsBits / 45)?);
result.push(toAlphaNumericChar(nextTwoCharsBits % 45)?); r_hld.push(toAlphaNumericChar(nextTwoCharsBits % 45)?);
count -= 2; count -= 2;
} }
if count == 1 { if count == 1 {
@@ -364,39 +372,47 @@ fn decodeAlphanumericSegment(
if bits.available() < 6 { if bits.available() < 6 {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
} }
result.push(toAlphaNumericChar(bits.readBits(6)?)?); r_hld.push(toAlphaNumericChar(bits.readBits(6)?)?);
} }
// See section 6.4.8.1, 6.4.8.2 // See section 6.4.8.1, 6.4.8.2
if fc1InEffect { if fc1InEffect {
// We need to massage the result a bit if in an FNC1 mode: // We need to massage the result a bit if in an FNC1 mode:
for i in start..result.len() { for i in start..r_hld.len() {
if result if r_hld
.chars() .chars()
.nth(i) .nth(i)
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
== '%' == '%'
{ {
if i < result.len() - 1 if i < result.len() - 1
&& result && r_hld
.chars() .chars()
.nth(i + 1) .nth(i + 1)
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
== '%' == '%'
{ {
// %% is rendered as % // %% is rendered as %
result.remove(i + 1); r_hld.remove(i + 1);
} else { } else {
// In alpha mode, % should be converted to FNC1 separator 0x1D // In alpha mode, % should be converted to FNC1 separator 0x1D
result.replace_range(i..i + 1, "\u{1D}"); r_hld.replace_range(i..i + 1, "\u{1D}");
} }
} }
} }
} }
result.append_eci(Eci::ISO8859_1);
result.append_string(&r_hld);
Ok(()) Ok(())
} }
fn decodeNumericSegment(bits: &mut BitSource, result: &mut String, count: usize) -> Result<()> { fn decodeNumericSegment(
bits: &mut BitSource,
result: &mut ECIStringBuilder,
count: usize,
) -> Result<()> {
result.append_eci(Eci::ISO8859_1);
let mut count = count; let mut count = count;
// Read three digits at a time // Read three digits at a time
while count >= 3 { while count >= 3 {
@@ -408,9 +424,9 @@ fn decodeNumericSegment(bits: &mut BitSource, result: &mut String, count: usize)
if threeDigitsBits >= 1000 { if threeDigitsBits >= 1000 {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
} }
result.push(toAlphaNumericChar(threeDigitsBits / 100)?); result.append_char(toAlphaNumericChar(threeDigitsBits / 100)?);
result.push(toAlphaNumericChar((threeDigitsBits / 10) % 10)?); result.append_char(toAlphaNumericChar((threeDigitsBits / 10) % 10)?);
result.push(toAlphaNumericChar(threeDigitsBits % 10)?); result.append_char(toAlphaNumericChar(threeDigitsBits % 10)?);
count -= 3; count -= 3;
} }
if count == 2 { if count == 2 {
@@ -422,8 +438,8 @@ fn decodeNumericSegment(bits: &mut BitSource, result: &mut String, count: usize)
if twoDigitsBits >= 100 { if twoDigitsBits >= 100 {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
} }
result.push(toAlphaNumericChar(twoDigitsBits / 10)?); result.append_char(toAlphaNumericChar(twoDigitsBits / 10)?);
result.push(toAlphaNumericChar(twoDigitsBits % 10)?); result.append_char(toAlphaNumericChar(twoDigitsBits % 10)?);
} else if count == 1 { } else if count == 1 {
// One digit left over to read // One digit left over to read
if bits.available() < 4 { if bits.available() < 4 {
@@ -433,27 +449,27 @@ fn decodeNumericSegment(bits: &mut BitSource, result: &mut String, count: usize)
if digitBits >= 10 { if digitBits >= 10 {
return Err(Exceptions::FORMAT); return Err(Exceptions::FORMAT);
} }
result.push(toAlphaNumericChar(digitBits)?); result.append_char(toAlphaNumericChar(digitBits)?);
} }
Ok(()) Ok(())
} }
fn parseECIValue(bits: &mut BitSource) -> Result<u32> { fn parseECIValue(bits: &mut BitSource) -> Result<Eci> {
let firstByte = bits.readBits(8)?; let firstByte = bits.readBits(8)?;
if (firstByte & 0x80) == 0 { if (firstByte & 0x80) == 0 {
// just one byte // just one byte
return Ok(firstByte & 0x7F); return Ok(Eci::from(firstByte & 0x7F));
} }
if (firstByte & 0xC0) == 0x80 { if (firstByte & 0xC0) == 0x80 {
// two bytes // two bytes
let secondByte = bits.readBits(8)?; let secondByte = bits.readBits(8)?;
return Ok(((firstByte & 0x3F) << 8) | secondByte); return Ok(Eci::from(((firstByte & 0x3F) << 8) | secondByte));
} }
if (firstByte & 0xE0) == 0xC0 { if (firstByte & 0xE0) == 0xC0 {
// three bytes // three bytes
let secondThirdBytes = bits.readBits(16)?; let secondThirdBytes = bits.readBits(16)?;
return Ok(((firstByte & 0x1F) << 16) | secondThirdBytes); return Ok(Eci::from(((firstByte & 0x1F) << 16) | secondThirdBytes));
} }
Err(Exceptions::FORMAT) Err(Exceptions::FORMAT)