From 0636a632915c1925997fb7cb595678ebdcc16216 Mon Sep 17 00:00:00 2001 From: Henry Schimke Date: Mon, 27 Mar 2023 11:45:26 -0500 Subject: [PATCH] partial stub out of decoder --- src/common/bit_source.rs | 4 + .../base_extentions/qrcode_version.rs | 4 + src/common/cpp_essentials/decoder_result.rs | 200 +++++ src/common/cpp_essentials/mod.rs | 4 + .../cpp_essentials/structured_append.rs | 16 + src/common/cpp_essentials/util.rs | 26 + src/common/eci_string_builder.rs | 68 +- src/qrcode/cpp_port/bitmatrix_parser.rs | 203 +++++ src/qrcode/cpp_port/decoder.rs | 725 ++++++++++-------- src/qrcode/cpp_port/mod.rs | 2 + src/qrcode/decoder/error_correction_level.rs | 13 + src/qrcode/decoder/format_information.rs | 3 + src/qrcode/decoder/mode.rs | 26 + 13 files changed, 977 insertions(+), 317 deletions(-) create mode 100644 src/common/cpp_essentials/decoder_result.rs create mode 100644 src/common/cpp_essentials/structured_append.rs create mode 100644 src/qrcode/cpp_port/bitmatrix_parser.rs diff --git a/src/common/bit_source.rs b/src/common/bit_source.rs index 7e10f4e..16cba87 100644 --- a/src/common/bit_source.rs +++ b/src/common/bit_source.rs @@ -116,6 +116,10 @@ impl BitSource { Ok(result) } + pub fn peak_bits(&self, numBits: usize) -> Result { + todo!() + } + /** * @return number of bits that can be read successfully */ diff --git a/src/common/cpp_essentials/base_extentions/qrcode_version.rs b/src/common/cpp_essentials/base_extentions/qrcode_version.rs index a63a2aa..49cad90 100644 --- a/src/common/cpp_essentials/base_extentions/qrcode_version.rs +++ b/src/common/cpp_essentials/base_extentions/qrcode_version.rs @@ -65,4 +65,8 @@ impl Version { // If we didn't find a close enough match, fail return Err(Exceptions::ILLEGAL_STATE); } + + pub fn isMicroQRCode(&self) -> bool { + todo!() + } } diff --git a/src/common/cpp_essentials/decoder_result.rs b/src/common/cpp_essentials/decoder_result.rs new file mode 100644 index 0000000..0906e13 --- /dev/null +++ b/src/common/cpp_essentials/decoder_result.rs @@ -0,0 +1,200 @@ +use std::{any::Any, rc::Rc}; + +use crate::common::ECIStringBuilder; + +use super::StructuredAppendInfo; + +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + content: ECIStringBuilder, + ecLevel: String, + lineCount: u32, // = 0; + versionNumber: u32, // = 0; + structuredAppend: StructuredAppendInfo, + isMirrored: bool, // = false; + readerInit: bool, // = false; + //Error _error; + //std::shared_ptr _extra; + extra: Rc, +} + +impl Default for DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + fn default() -> Self { + Self { + content: Default::default(), + ecLevel: Default::default(), + lineCount: 0, + versionNumber: 0, + structuredAppend: Default::default(), + isMirrored: false, + readerInit: false, + extra: Default::default(), + } + } +} + +impl DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + pub fn new() -> Self { + Self::default() + } + pub fn with_eci_string_builder(src: ECIStringBuilder) -> Self { + todo!() + } + + pub fn isValid(&self) -> bool { + //return includeErrors || (_content.symbology.code != 0 && !_error); + todo!() + } + + pub fn content(&self) -> &ECIStringBuilder { + &self.content + } +} + +// DecoderResult(const DecoderResult &) = delete; +// DecoderResult& operator=(const DecoderResult &) = delete; +// } + +// public: +// DecoderResult() = default; +// DecoderResult(Error error) : _error(std::move(error)) {} +// DecoderResult(Content&& bytes) : _content(std::move(bytes)) {} + +// DecoderResult(DecoderResult&&) noexcept = default; +// DecoderResult& operator=(DecoderResult&&) noexcept = default; + +// bool isValid(bool includeErrors = false) const +// { +// return includeErrors || (_content.symbology.code != 0 && !_error); +// } + +// const Content& content() const & { return _content; } +// Content&& content() && { return std::move(_content); } + +// to keep the unit tests happy for now: +// std::wstring text() const { return _content.utfW(); } +// std::string symbologyIdentifier() const { return _content.symbology.toString(false); } + +// Simple macro to set up getter/setter methods that save lots of boilerplate. +// It sets up a standard 'const & () const', 2 setters for setting lvalues via +// copy and 2 for setting rvalues via move. They are provided each to work +// either on lvalues (normal 'void (...)') or on rvalues (returning '*this' as +// rvalue). The latter can be used to optionally initialize a temporary in a +// return statement, e.g. +// return DecoderResult(bytes, text).setEcLevel(level); +// #define ZX_PROPERTY(TYPE, GETTER, SETTER) \ +// const TYPE& GETTER() const & { return _##GETTER; } \ +// TYPE&& GETTER() && { return std::move(_##GETTER); } \ +// void SETTER(const TYPE& v) & { _##GETTER = v; } \ +// void SETTER(TYPE&& v) & { _##GETTER = std::move(v); } \ +// DecoderResult&& SETTER(const TYPE& v) && { _##GETTER = v; return std::move(*this); } \ +// DecoderResult&& SETTER(TYPE&& v) && { _##GETTER = std::move(v); return std::move(*this); } + +// ZX_PROPERTY(std::string, ecLevel, setEcLevel) +// ZX_PROPERTY(int, lineCount, setLineCount) +// ZX_PROPERTY(int, versionNumber, setVersionNumber) +// ZX_PROPERTY(StructuredAppendInfo, structuredAppend, setStructuredAppend) + +// ZX_PROPERTY(Error, error, setError) + +// ZX_PROPERTY(bool, isMirrored, setIsMirrored) +// ZX_PROPERTY(bool, readerInit, setReaderInit) +// ZX_PROPERTY(std::shared_ptr, extra, setExtra) + +// #undef ZX_PROPERTY +// }; + +impl DecoderResult +where + T: Copy + Clone + Default + Eq + PartialEq, +{ + pub fn ecLevel(&self) -> &str { + &self.ecLevel + } + pub fn setEcLevel(&mut self, ecLevel: String) { + self.ecLevel = ecLevel + } + pub fn withEcLevel(mut self, ecLevel: String) -> DecoderResult { + self.setEcLevel(ecLevel); + self + } + + pub fn lineCount(&self) -> u32 { + self.lineCount + } + pub fn setLineCount(&mut self, lc: u32) { + self.lineCount = lc + } + pub fn withLineCount(mut self, lc: u32) -> DecoderResult { + self.setLineCount(lc); + self + } + + pub fn versionNumber(&self) -> u32 { + self.versionNumber + } + pub fn setVersionNumber(&mut self, vn: u32) { + self.versionNumber = vn + } + pub fn withVersionNumber(mut self, vn: u32) -> DecoderResult { + self.setVersionNumber(vn); + self + } + + pub fn structuredAppend(&self) -> &StructuredAppendInfo { + &self.structuredAppend + } + pub fn setStructuredAppend(&mut self, sai: StructuredAppendInfo) { + self.structuredAppend = sai + } + pub fn withStructuredAppend(mut self, sai: StructuredAppendInfo) -> DecoderResult { + self.setStructuredAppend(sai); + self + } + + pub fn isMirrored(&self) -> bool { + self.isMirrored + } + pub fn setIsMirrored(&mut self, is_mirrored: bool) { + self.isMirrored = is_mirrored + } + pub fn withIsMirrored(mut self, is_mirrored: bool) -> DecoderResult { + self.setIsMirrored(is_mirrored); + self + } + + pub fn readerInit(&self) -> bool { + self.readerInit + } + pub fn setReaderInit(&mut self, reader_init: bool) { + self.readerInit = reader_init + } + pub fn withReaderInit(mut self, reader_init: bool) -> DecoderResult { + self.setReaderInit(reader_init); + self + } + + pub fn extra(&self) -> Rc { + self.extra.clone() + } + pub fn setExtra(&mut self, extra: Rc) { + self.extra = extra + } + pub fn withExtra(mut self, extra: Rc) -> DecoderResult { + self.setExtra(extra); + self + } + + // pub fn build(self) -> DecoderResult { + + // } +} diff --git a/src/common/cpp_essentials/mod.rs b/src/common/cpp_essentials/mod.rs index 817e38b..07120a8 100644 --- a/src/common/cpp_essentials/mod.rs +++ b/src/common/cpp_essentials/mod.rs @@ -3,6 +3,7 @@ mod base_extentions; pub mod bitmatrix_cursor; pub mod bitmatrix_cursor_trait; pub mod concentric_finder; +pub mod decoder_result; pub mod direction; pub mod dm_regression_line; pub mod edge_tracer; @@ -12,12 +13,14 @@ pub mod pattern; pub mod regression_line; pub mod regression_line_trait; pub mod step_result; +pub mod structured_append; pub mod util; pub mod value; pub use bitmatrix_cursor::*; pub use bitmatrix_cursor_trait::*; pub use concentric_finder::*; +pub use decoder_result::*; pub use direction::*; pub use dm_regression_line::*; pub use edge_tracer::*; @@ -27,5 +30,6 @@ pub use pattern::*; pub use regression_line::*; pub use regression_line_trait::*; pub use step_result::*; +pub use structured_append::*; pub use util::*; pub use value::*; diff --git a/src/common/cpp_essentials/structured_append.rs b/src/common/cpp_essentials/structured_append.rs new file mode 100644 index 0000000..d992e03 --- /dev/null +++ b/src/common/cpp_essentials/structured_append.rs @@ -0,0 +1,16 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StructuredAppendInfo { + pub index: i32, // -1; + pub count: i32, // = -1; + pub id: String, +} + +impl Default for StructuredAppendInfo { + fn default() -> Self { + Self { + index: -1, + count: -1, + id: Default::default(), + } + } +} diff --git a/src/common/cpp_essentials/util.rs b/src/common/cpp_essentials/util.rs index d98b866..3947164 100644 --- a/src/common/cpp_essentials/util.rs +++ b/src/common/cpp_essentials/util.rs @@ -38,3 +38,29 @@ pub fn UpdateMinMaxFloat(min: &mut f64, max: &mut f64, val: f64) { *min = f64::min(*min, val); *max = f64::max(*max, val); } + +// template>> +pub fn ToString>(val: T, len: usize) -> Result { + let mut len = len; + let mut val = val.into(); + + let mut result: String = vec!['0'; len].iter().collect(); + len -= 1; + // std::string result(len--, '0'); + if (val < 0) { + return Err(Exceptions::format_with("Invalid value")); + } + while len >= 0 && val != 0 { + result.replace_range(len..len, &char::from(b'0' + (val % 10) as u8).to_string()); + + len -= 1; + val /= 10; + } + // for (; len >= 0 && val != 0; --len, val /= 10) { + // result[len] = '0' + val % 10;} + if (val != 0) { + return Err(Exceptions::format_with("Invalid value")); + } + + Ok(result) +} diff --git a/src/common/eci_string_builder.rs b/src/common/eci_string_builder.rs index a43dd6a..72bfa6a 100644 --- a/src/common/eci_string_builder.rs +++ b/src/common/eci_string_builder.rs @@ -30,12 +30,13 @@ use super::{CharacterSet, Eci}; * * @author Alex Geller */ -#[derive(Default)] +#[derive(Default, PartialEq, Eq, Debug, Clone)] pub struct ECIStringBuilder { is_eci: bool, eci_result: Option, bytes: Vec, eci_positions: Vec<(Eci, usize, usize)>, // (Eci, start, end) + pub symbology: SymbologyIdentifier, } impl ECIStringBuilder { @@ -45,6 +46,7 @@ impl ECIStringBuilder { bytes: Vec::with_capacity(initial_capacity), eci_positions: Vec::default(), is_eci: false, + symbology: SymbologyIdentifier::default(), } } @@ -120,6 +122,11 @@ impl ECIStringBuilder { } } + /// Change the current encoding characterset, finding an eci to do so + pub fn switch_encoding(&mut self, charset: CharacterSet) { + self.append_eci(Eci::from(charset)) + } + /// Finishes encoding anything in the buffer using the current ECI and resets. /// /// This function can panic @@ -198,6 +205,11 @@ impl ECIStringBuilder { self.bytes.len() } + /// Reserve an additional number of bytes for storage + pub fn reserve(&mut self, additional: usize) { + self.bytes.reserve(additional); + } + /** * @return true iff nothing has been appended */ @@ -221,3 +233,57 @@ impl fmt::Display for ECIStringBuilder { } } } + +impl std::ops::AddAssign for ECIStringBuilder { + fn add_assign(&mut self, rhs: u8) { + self.append_byte(rhs) + } +} + +impl std::ops::AddAssign for ECIStringBuilder { + fn add_assign(&mut self, rhs: String) { + self.append_string(&rhs) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ContentType { + Text, + Binary, + Mixed, + GS1, + ISO15434, + UnknownECI, +} +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum AIFlag { + None, + GS1, + AIM, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct SymbologyIdentifier { + //char code = 0, modifier = 0, eciModifierOffset = 0; + pub code: u8, + pub modifier: u8, + pub eciModifierOffset: u8, + pub aiFlag: AIFlag, + // AIFlag aiFlag = AIFlag::None; + + // std::string toString(bool hasECI = false) const + // { + // return code ? ']' + std::string(1, code) + static_cast(modifier + eciModifierOffset * hasECI) : std::string(); + // } +} + +impl Default for SymbologyIdentifier { + fn default() -> Self { + Self { + code: 0, + modifier: 0, + eciModifierOffset: 0, + aiFlag: AIFlag::None, + } + } +} diff --git a/src/qrcode/cpp_port/bitmatrix_parser.rs b/src/qrcode/cpp_port/bitmatrix_parser.rs new file mode 100644 index 0000000..40a98e8 --- /dev/null +++ b/src/qrcode/cpp_port/bitmatrix_parser.rs @@ -0,0 +1,203 @@ +// /* +// * Copyright 2016 Nu-book Inc. +// * Copyright 2016 ZXing authors +// */ +// // SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::{BitMatrix, Result}, + qrcode::decoder::{FormatInformation, Version, VersionRef}, + Exceptions, +}; + +pub fn getBit(bitMatrix: &BitMatrix, x: u32, y: u32, mirrored: Option) -> bool { + let mirrored = mirrored.unwrap_or(false); + if mirrored { + bitMatrix.get(y, x) + } else { + bitMatrix.get(x, y) + } +} + +pub fn hasValidDimension(bitMatrix: &BitMatrix, isMicro: bool) -> bool { + let dimension = bitMatrix.height(); + if (isMicro) { + dimension >= 11 && dimension <= 17 && (dimension % 2) == 1 + } else { + dimension >= 21 && dimension <= 177 && (dimension % 4) == 1 + } +} + +pub fn ReadVersion(bitMatrix: &BitMatrix) -> Result { + todo!() + // int dimension = bitMatrix.height(); + + // const Version* version = Version::FromDimension(dimension); + + // if (!version || version->versionNumber() < 7) + // return version; + + // for (bool mirror : {false, true}) { + // // Read top-right/bottom-left version info: 3 wide by 6 tall (depending on mirrored) + // int versionBits = 0; + // for (int y = 5; y >= 0; --y) + // for (int x = dimension - 9; x >= dimension - 11; --x) + // AppendBit(versionBits, getBit(bitMatrix, x, y, mirror)); + + // version = Version::DecodeVersionInformation(versionBits); + // if (version && version->dimension() == dimension) + // return version; + // } + + // return nullptr; +} + +pub fn ReadFormatInformation(bitMatrix: &BitMatrix, isMicro: bool) -> Result { + todo!() + // if (!hasValidDimension(bitMatrix, isMicro)) + // return {}; + + // if (isMicro) { + // // Read top-left format info bits + // int formatInfoBits = 0; + // for (int x = 1; x < 9; x++) + // AppendBit(formatInfoBits, getBit(bitMatrix, x, 8)); + // for (int y = 7; y >= 1; y--) + // AppendBit(formatInfoBits, getBit(bitMatrix, 8, y)); + + // return FormatInformation::DecodeMQR(formatInfoBits); + // } + + // // Read top-left format info bits + // int formatInfoBits1 = 0; + // for (int x = 0; x < 6; x++) + // AppendBit(formatInfoBits1, getBit(bitMatrix, x, 8)); + // // .. and skip a bit in the timing pattern ... + // AppendBit(formatInfoBits1, getBit(bitMatrix, 7, 8)); + // AppendBit(formatInfoBits1, getBit(bitMatrix, 8, 8)); + // AppendBit(formatInfoBits1, getBit(bitMatrix, 8, 7)); + // // .. and skip a bit in the timing pattern ... + // for (int y = 5; y >= 0; y--) + // AppendBit(formatInfoBits1, getBit(bitMatrix, 8, y)); + + // // Read the top-right/bottom-left pattern including the 'Dark Module' from the bottom-left + // // part that has to be considered separately when looking for mirrored symbols. + // // See also FormatInformation::DecodeQR + // int dimension = bitMatrix.height(); + // int formatInfoBits2 = 0; + // for (int y = dimension - 1; y >= dimension - 8; y--) + // AppendBit(formatInfoBits2, getBit(bitMatrix, 8, y)); + // for (int x = dimension - 8; x < dimension; x++) + // AppendBit(formatInfoBits2, getBit(bitMatrix, x, 8)); + + // return FormatInformation::DecodeQR(formatInfoBits1, formatInfoBits2); +} + +pub fn ReadQRCodewords( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + todo!() + // BitMatrix functionPattern = version.buildFunctionPattern(); + + // ByteArray result; + // result.reserve(version.totalCodewords()); + // uint8_t currentByte = 0; + // bool readingUp = true; + // int bitsRead = 0; + // int dimension = bitMatrix.height(); + // // Read columns in pairs, from right to left + // for (int x = dimension - 1; x > 0; x -= 2) { + // // Skip whole column with vertical timing pattern. + // if (x == 6) + // x--; + // // Read alternatingly from bottom to top then top to bottom + // for (int row = 0; row < dimension; row++) { + // int y = readingUp ? dimension - 1 - row : row; + // for (int col = 0; col < 2; col++) { + // int xx = x - col; + // // Ignore bits covered by the function pattern + // if (!functionPattern.get(xx, y)) { + // // Read a bit + // AppendBit(currentByte, + // GetDataMaskBit(formatInfo.dataMask, xx, y) != getBit(bitMatrix, xx, y, formatInfo.isMirrored)); + // // If we've made a whole byte, save it off + // if (++bitsRead % 8 == 0) + // result.push_back(std::exchange(currentByte, 0)); + // } + // } + // } + // readingUp = !readingUp; // switch directions + // } + // if (Size(result) != version.totalCodewords()) + // return {}; + + // return result; +} + +pub fn ReadMQRCodewords( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + todo!() + // BitMatrix functionPattern = version.buildFunctionPattern(); + + // // D3 in a Version M1 symbol, D11 in a Version M3-L symbol and D9 + // // in a Version M3-M symbol is a 2x2 square 4-module block. + // // See ISO 18004:2006 6.7.3. + // bool hasD4mBlock = version.versionNumber() % 2 == 1; + // int d4mBlockIndex = + // version.versionNumber() == 1 ? 3 : (formatInfo.ecLevel == QRCode::ErrorCorrectionLevel::Low ? 11 : 9); + + // ByteArray result; + // result.reserve(version.totalCodewords()); + // uint8_t currentByte = 0; + // bool readingUp = true; + // int bitsRead = 0; + // int dimension = bitMatrix.height(); + // // Read columns in pairs, from right to left + // for (int x = dimension - 1; x > 0; x -= 2) { + // // Read alternatingly from bottom to top then top to bottom + // for (int row = 0; row < dimension; row++) { + // int y = readingUp ? dimension - 1 - row : row; + // for (int col = 0; col < 2; col++) { + // int xx = x - col; + // // Ignore bits covered by the function pattern + // if (!functionPattern.get(xx, y)) { + // // Read a bit + // AppendBit(currentByte, + // GetDataMaskBit(formatInfo.dataMask, xx, y, true) != getBit(bitMatrix, xx, y, formatInfo.isMirrored)); + // ++bitsRead; + // // If we've made a whole byte, save it off; save early if 2x2 data block. + // if (bitsRead == 8 || (bitsRead == 4 && hasD4mBlock && Size(result) == d4mBlockIndex - 1)) { + // result.push_back(std::exchange(currentByte, 0)); + // bitsRead = 0; + // } + // } + // } + // } + // readingUp = !readingUp; // switch directions + // } + // if (Size(result) != version.totalCodewords()) + // return {}; + + // return result; +} + +pub fn ReadCodewords( + bitMatrix: &BitMatrix, + version: VersionRef, + formatInfo: &FormatInformation, +) -> Result> { + if (!hasValidDimension(bitMatrix, version.isMicroQRCode())) { + return Err(Exceptions::FORMAT); + } + + if version.isMicroQRCode() { + ReadMQRCodewords(bitMatrix, version, formatInfo) + } else { + ReadQRCodewords(bitMatrix, version, formatInfo) + } +} diff --git a/src/qrcode/cpp_port/decoder.rs b/src/qrcode/cpp_port/decoder.rs index 3772591..211bc17 100644 --- a/src/qrcode/cpp_port/decoder.rs +++ b/src/qrcode/cpp_port/decoder.rs @@ -4,357 +4,450 @@ // */ // // SPDX-License-Identifier: Apache-2.0 -// #include "QRDecoder.h" +use crate::common::cpp_essentials::{DecoderResult, StructuredAppendInfo}; +use crate::common::reedsolomon::{ + get_predefined_genericgf, PredefinedGenericGF, ReedSolomonDecoder, +}; +use crate::common::{ + AIFlag, BitMatrix, BitSource, CharacterSet, DecoderRXingResult, ECIStringBuilder, Eci, Result, + SymbologyIdentifier, +}; +use crate::qrcode::cpp_port::bitmatrix_parser::{ + ReadCodewords, ReadFormatInformation, ReadVersion, +}; +use crate::qrcode::decoder::{DataBlock, ErrorCorrectionLevel, Mode, Version}; +use crate::Exceptions; -// #include "BitMatrix.h" -// #include "BitSource.h" -// #include "CharacterSet.h" -// #include "DecoderResult.h" -// #include "GenericGF.h" -// #include "QRBitMatrixParser.h" -// #include "QRCodecMode.h" -// #include "QRDataBlock.h" -// #include "QRFormatInformation.h" -// #include "QRVersion.h" -// #include "ReedSolomonDecoder.h" -// #include "StructuredAppend.h" -// #include "ZXAlgorithms.h" -// #include "ZXTestSupport.h" +/** +*

Given data and error-correction codewords received, possibly corrupted by errors, attempts to +* correct the errors in-place using Reed-Solomon error correction.

+* +* @param codewordBytes data and error correction codewords +* @param numDataCodewords number of codewords that are data bytes +* @return false if error correction fails +*/ +pub fn CorrectErrors(codewordBytes: &mut [u8], numDataCodewords: u32) -> Result { + // First read into an array of ints + // std::vector codewordsInts(codewordBytes.begin(), codewordBytes.end()); + let mut codewordsInts = codewordBytes.iter().copied().map(|b| b as i32).collect(); -// #include -// #include -// #include -// #include + let numECCodewords = ((codewordBytes.len() as u32) - numDataCodewords) as i32; + let rs = ReedSolomonDecoder::new(get_predefined_genericgf( + PredefinedGenericGF::QrCodeField256, + )); + if rs.decode(&mut codewordsInts, numECCodewords)? == 0 + // if (!ReedSolomonDecode(GenericGF::QRCodeField256(), codewordsInts, numECCodewords)) + { + return Ok(false); + } -// namespace ZXing::QRCode { + // Copy back into array of bytes -- only need to worry about the bytes that were data + // We don't care about errors in the error-correction codewords + codewordBytes[..numDataCodewords as usize].copy_from_slice( + &codewordsInts[..numDataCodewords as usize] + .into_iter() + .copied() + .map(|i| i as u8) + .collect::>(), + ); + // std::copy_n(codewordsInts.begin(), numDataCodewords, codewordBytes.begin()); -// /** -// *

Given data and error-correction codewords received, possibly corrupted by errors, attempts to -// * correct the errors in-place using Reed-Solomon error correction.

-// * -// * @param codewordBytes data and error correction codewords -// * @param numDataCodewords number of codewords that are data bytes -// * @return false if error correction fails -// */ -// static bool CorrectErrors(ByteArray& codewordBytes, int numDataCodewords) -// { -// // First read into an array of ints -// std::vector codewordsInts(codewordBytes.begin(), codewordBytes.end()); + Ok(true) +} -// int numECCodewords = Size(codewordBytes) - numDataCodewords; -// if (!ReedSolomonDecode(GenericGF::QRCodeField256(), codewordsInts, numECCodewords)) -// return false; +/** +* See specification GBT 18284-2000 +*/ +pub fn DecodeHanziSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + let mut count = count; -// // Copy back into array of bytes -- only need to worry about the bytes that were data -// // We don't care about errors in the error-correction codewords -// std::copy_n(codewordsInts.begin(), numDataCodewords, codewordBytes.begin()); -// return true; -// } + // Each character will require 2 bytes, decode as GB2312 + // There is no ECI value for GB2312, use GB18030 which is a superset + result.switch_encoding(CharacterSet::GB18030); + result.reserve(2 * count as usize); + while (count > 0) { + // Each 13 bits encodes a 2-byte character + let twoBytes = bits.readBits(13)?; + let mut assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060); + if (assembledTwoBytes < 0x00A00) { + // In the 0xA1A1 to 0xAAFE range + assembledTwoBytes += 0x0A1A1; + } else { + // In the 0xB0A1 to 0xFAFE range + assembledTwoBytes += 0x0A6A1; + } + *result += ((assembledTwoBytes >> 8) & 0xFF) as u8; + *result += (assembledTwoBytes & 0xFF) as u8; + count -= 1; + } + Ok(()) +} -// /** -// * See specification GBT 18284-2000 -// */ -// static void DecodeHanziSegment(BitSource& bits, int count, Content& result) -// { -// // Each character will require 2 bytes, decode as GB2312 -// // There is no ECI value for GB2312, use GB18030 which is a superset -// result.switchEncoding(CharacterSet::GB18030); -// result.reserve(2 * count); +pub fn DecodeKanjiSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + let mut count = count; + // Each character will require 2 bytes. Read the characters as 2-byte pairs + // and decode as Shift_JIS afterwards + result.switch_encoding(CharacterSet::Shift_JIS); + result.reserve(2 * count as usize); -// while (count > 0) { -// // Each 13 bits encodes a 2-byte character -// int twoBytes = bits.readBits(13); -// int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060); -// if (assembledTwoBytes < 0x00A00) { -// // In the 0xA1A1 to 0xAAFE range -// assembledTwoBytes += 0x0A1A1; -// } else { -// // In the 0xB0A1 to 0xFAFE range -// assembledTwoBytes += 0x0A6A1; -// } -// result += narrow_cast((assembledTwoBytes >> 8) & 0xFF); -// result += narrow_cast(assembledTwoBytes & 0xFF); -// count--; -// } -// } + while (count > 0) { + // Each 13 bits encodes a 2-byte character + let twoBytes = bits.readBits(13)?; + let mut assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); + if (assembledTwoBytes < 0x01F00) { + // In the 0x8140 to 0x9FFC range + assembledTwoBytes += 0x08140; + } else { + // In the 0xE040 to 0xEBBF range + assembledTwoBytes += 0x0C140; + } + *result += (assembledTwoBytes >> 8) as u8; + *result += (assembledTwoBytes) as u8; + count -= 1; + } + Ok(()) +} -// static void DecodeKanjiSegment(BitSource& bits, int count, Content& result) -// { -// // Each character will require 2 bytes. Read the characters as 2-byte pairs -// // and decode as Shift_JIS afterwards -// result.switchEncoding(CharacterSet::Shift_JIS); -// result.reserve(2 * count); +pub fn DecodeByteSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + result.switch_encoding(CharacterSet::Unknown); + result.reserve(count as usize); -// while (count > 0) { -// // Each 13 bits encodes a 2-byte character -// int twoBytes = bits.readBits(13); -// int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); -// if (assembledTwoBytes < 0x01F00) { -// // In the 0x8140 to 0x9FFC range -// assembledTwoBytes += 0x08140; -// } else { -// // In the 0xE040 to 0xEBBF range -// assembledTwoBytes += 0x0C140; -// } -// result += narrow_cast(assembledTwoBytes >> 8); -// result += narrow_cast(assembledTwoBytes); -// count--; -// } -// } + for i in 0..count { + // for (int i = 0; i < count; i++) + *result += (bits.readBits(8)?) as u8; + } + Ok(()) +} -// static void DecodeByteSegment(BitSource& bits, int count, Content& result) -// { -// result.switchEncoding(CharacterSet::Unknown); -// result.reserve(count); +pub fn ToAlphaNumericChar(value: u32) -> Result { + let value = value as usize; + /** + * See ISO 18004:2006, 6.4.4 Table 5 + */ + const ALPHANUMERIC_CHARS: [char; 45] = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + ' ', '$', '%', '*', '+', '-', '.', '/', ':', + ]; -// for (int i = 0; i < count; i++) -// result += narrow_cast(bits.readBits(8)); -// } + if (value < 0 || value >= (ALPHANUMERIC_CHARS.len())) { + return Err(Exceptions::index_out_of_bounds_with( + "oAlphaNumericChar: out of range", + )); + } -// static char ToAlphaNumericChar(int value) -// { -// /** -// * See ISO 18004:2006, 6.4.4 Table 5 -// */ -// static const char ALPHANUMERIC_CHARS[] = { -// '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', -// 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', -// 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', -// ' ', '$', '%', '*', '+', '-', '.', '/', ':' -// }; + Ok(ALPHANUMERIC_CHARS[value]) +} -// if (value < 0 || value >= Size(ALPHANUMERIC_CHARS)) -// throw std::out_of_range("ToAlphaNumericChar: out of range"); +pub fn DecodeAlphanumericSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + let mut count = count; -// return ALPHANUMERIC_CHARS[value]; -// } + // Read two characters at a time + let mut buffer = String::new(); -// static void DecodeAlphanumericSegment(BitSource& bits, int count, Content& result) -// { -// // Read two characters at a time -// std::string buffer; -// while (count > 1) { -// int nextTwoCharsBits = bits.readBits(11); -// buffer += ToAlphaNumericChar(nextTwoCharsBits / 45); -// buffer += ToAlphaNumericChar(nextTwoCharsBits % 45); -// count -= 2; -// } -// if (count == 1) { -// // special case: one character left -// buffer += ToAlphaNumericChar(bits.readBits(6)); -// } -// // See section 6.4.8.1, 6.4.8.2 -// if (result.symbology.aiFlag != AIFlag::None) { -// // We need to massage the result a bit if in an FNC1 mode: -// for (size_t i = 0; i < buffer.length(); i++) { -// if (buffer[i] == '%') { -// if (i < buffer.length() - 1 && buffer[i + 1] == '%') { -// // %% is rendered as % -// buffer.erase(i + 1); -// } else { -// // In alpha mode, % should be converted to FNC1 separator 0x1D -// buffer[i] = static_cast(0x1D); -// } -// } -// } -// } + while count > 1 { + let nextTwoCharsBits = bits.readBits(11)?; + buffer.push(ToAlphaNumericChar(nextTwoCharsBits / 45)?); + buffer.push(ToAlphaNumericChar(nextTwoCharsBits % 45)?); + count -= 2; + } + if (count == 1) { + // special case: one character left + buffer.push(ToAlphaNumericChar(bits.readBits(6)?)?); + } + // See section 6.4.8.1, 6.4.8.2 + if (result.symbology.aiFlag != AIFlag::None) { + // We need to massage the result a bit if in an FNC1 mode: + for i in 0..buffer.len() { + // for (size_t i = 0; i < buffer.length(); i++) { + if (buffer + .chars() + .nth(i) + .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? + == '%') + { + if (i < buffer.len() - 1 + && buffer + .chars() + .nth(i + 1) + .ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? + == '%') + { + // %% is rendered as % + buffer.remove(i + 1); + // buffer.erase(i + 1); + } else { + // In alpha mode, % should be converted to FNC1 separator 0x1D + buffer.replace_range(i..i, &char::from(0x1D).to_string()); + // buffer[i] = static_cast(0x1D); + } + } + } + } -// result.switchEncoding(CharacterSet::ISO8859_1); -// result += buffer; -// } + result.switch_encoding(CharacterSet::ISO8859_1); + *result += buffer; -// static void DecodeNumericSegment(BitSource& bits, int count, Content& result) -// { -// result.switchEncoding(CharacterSet::ISO8859_1); -// result.reserve(count); + Ok(()) +} -// while (count) { -// int n = std::min(count, 3); -// int nDigits = bits.readBits(1 + 3 * n); // read 4, 7 or 10 bits into 1, 2 or 3 digits -// result.append(ZXing::ToString(nDigits, n)); -// count -= n; -// } -// } +pub fn DecodeNumericSegment( + bits: &mut BitSource, + count: u32, + result: &mut ECIStringBuilder, +) -> Result<()> { + let mut count = count; -// static ECI ParseECIValue(BitSource& bits) -// { -// int firstByte = bits.readBits(8); -// if ((firstByte & 0x80) == 0) { -// // just one byte -// return ECI(firstByte & 0x7F); -// } -// if ((firstByte & 0xC0) == 0x80) { -// // two bytes -// int secondByte = bits.readBits(8); -// return ECI(((firstByte & 0x3F) << 8) | secondByte); -// } -// if ((firstByte & 0xE0) == 0xC0) { -// // three bytes -// int secondThirdBytes = bits.readBits(16); -// return ECI(((firstByte & 0x1F) << 16) | secondThirdBytes); -// } -// throw FormatError("ParseECIValue: invalid value"); -// } + result.switch_encoding(CharacterSet::ISO8859_1); + result.reserve(count as usize); -// /** -// * QR codes encode mode indicators and terminator codes into a constant bit length of 4. -// * Micro QR codes have terminator codes that vary in bit length but are always longer than -// * the mode indicators. -// * M1 - 0 length mode code, 3 bits terminator code -// * M2 - 1 bit mode code, 5 bits terminator code -// * M3 - 2 bit mode code, 7 bits terminator code -// * M4 - 3 bit mode code, 9 bits terminator code -// * IsTerminator peaks into the bit stream to see if the current position is at the start of -// * a terminator code. If true, then the decoding can finish. If false, then the decoding -// * can read off the next mode code. -// * -// * See ISO 18004:2015, 7.4.1 Table 2 -// * -// * @param bits the stream of bits that might have a terminator code -// * @param version the QR or micro QR code version -// */ -// bool IsEndOfStream(const BitSource& bits, const Version& version) -// { -// const int bitsRequired = TerminatorBitsLength(version); -// const int bitsAvailable = std::min(bits.available(), bitsRequired); -// return bitsAvailable == 0 || bits.peakBits(bitsAvailable) == 0; -// } + while (count > 0) { + let n = std::cmp::min(count, 3); + let nDigits = bits.readBits(1 + 3 * n as usize)?; // read 4, 7 or 10 bits into 1, 2 or 3 digits + result.append_string(&crate::common::cpp_essentials::util::ToString( + nDigits as usize, + n as usize, + )?); + count -= n; + } -// /** -// *

QR Codes can encode text as bits in one of several modes, and can use multiple modes -// * in one QR Code. This method decodes the bits back into text.

-// * -// *

See ISO 18004:2006, 6.4.3 - 6.4.7

-// */ + Ok(()) +} + +pub fn ParseECIValue(bits: &mut BitSource) -> Result { + let firstByte = bits.readBits(8)?; + if ((firstByte & 0x80) == 0) { + // just one byte + return Ok(Eci::from(firstByte & 0x7F)); + } + if ((firstByte & 0xC0) == 0x80) { + // two bytes + let secondByte = bits.readBits(8)?; + return Ok(Eci::from(((firstByte & 0x3F) << 8) | secondByte)); + } + if ((firstByte & 0xE0) == 0xC0) { + // three bytes + let secondThirdBytes = bits.readBits(16)?; + return Ok(Eci::from(((firstByte & 0x1F) << 16) | secondThirdBytes)); + } + Err(Exceptions::format_with("ParseECIValue: invalid value")) +} + +/** + * QR codes encode mode indicators and terminator codes into a constant bit length of 4. + * Micro QR codes have terminator codes that vary in bit length but are always longer than + * the mode indicators. + * M1 - 0 length mode code, 3 bits terminator code + * M2 - 1 bit mode code, 5 bits terminator code + * M3 - 2 bit mode code, 7 bits terminator code + * M4 - 3 bit mode code, 9 bits terminator code + * IsTerminator peaks into the bit stream to see if the current position is at the start of + * a terminator code. If true, then the decoding can finish. If false, then the decoding + * can read off the next mode code. + * + * See ISO 18004:2015, 7.4.1 Table 2 + * + * @param bits the stream of bits that might have a terminator code + * @param version the QR or micro QR code version + */ +pub fn IsEndOfStream(bits: &mut BitSource, version: &Version) -> Result { + let bitsRequired = Mode::get_terminator_bit_length(version); //super::qr_codec_mode::TerminatorBitsLength(version); + let bitsAvailable = std::cmp::min(bits.available(), bitsRequired as usize); + Ok(bitsAvailable == 0 || bits.peak_bits(bitsAvailable)? == 0) +} + +/** +*

QR Codes can encode text as bits in one of several modes, and can use multiple modes +* in one QR Code. This method decodes the bits back into text.

+* +*

See ISO 18004:2006, 6.4.3 - 6.4.7

+*/ // ZXING_EXPORT_TEST_ONLY -// DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel) -// { -// BitSource bits(bytes); -// Content result; -// Error error; -// result.symbology = {'Q', '1', 1}; -// StructuredAppendInfo structuredAppend; -// const int modeBitLength = CodecModeBitsLength(version); +pub fn DecodeBitStream( + bytes: &[u8], + version: &Version, + ecLevel: ErrorCorrectionLevel, +) -> Result> { + let mut bits = BitSource::new(bytes.to_vec()); + let mut result = ECIStringBuilder::default(); + // Error error; + result.symbology = SymbologyIdentifier { + code: b'Q', + modifier: b'1', + eciModifierOffset: 1, + aiFlag: AIFlag::None, + }; //{'Q', '1', 1}; + let mut structuredAppend = StructuredAppendInfo::default(); + let modeBitLength = Mode::get_codec_mode_bits_length(version); -// try -// { -// while(!IsEndOfStream(bits, version)) { -// CodecMode mode; -// if (modeBitLength == 0) -// mode = CodecMode::NUMERIC; // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0 -// else -// mode = CodecModeForBits(bits.readBits(modeBitLength), version.isMicroQRCode()); + let res = (|| { + while (!IsEndOfStream(&mut bits, version)?) { + let mode: Mode; + if (modeBitLength == 0) { + mode = Mode::NUMERIC; // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0 + } else { + mode = Mode::CodecModeForBits( + bits.readBits(modeBitLength as usize)?, + Some(version.isMicroQRCode()), + ); + } -// switch (mode) { -// case CodecMode::FNC1_FIRST_POSITION: -// // if (!result.empty()) // uncomment to enforce specification -// // throw FormatError("GS1 Indicator (FNC1 in first position) at illegal position"); -// result.symbology.modifier = '3'; -// result.symbology.aiFlag = AIFlag::GS1; // In Alphanumeric mode undouble doubled '%' and treat single '%' as -// break; -// case CodecMode::FNC1_SECOND_POSITION: -// if (!result.empty()) -// throw FormatError("AIM Application Indicator (FNC1 in second position) at illegal position"); -// result.symbology.modifier = '5'; // As above -// // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position), "00-99" or "A-Za-z" -// if (int appInd = bits.readBits(8); appInd < 100) // "00-09" -// result += ZXing::ToString(appInd, 2); -// else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) // "A-Za-z" -// result += narrow_cast(appInd - 100); -// else -// throw FormatError("Invalid AIM Application Indicator"); -// result.symbology.aiFlag = AIFlag::AIM; // see also above -// break; -// case CodecMode::STRUCTURED_APPEND: -// // sequence number and parity is added later to the result metadata -// // Read next 4 bits of index, 4 bits of symbol count, and 8 bits of parity data, then continue -// structuredAppend.index = bits.readBits(4); -// structuredAppend.count = bits.readBits(4) + 1; -// structuredAppend.id = std::to_string(bits.readBits(8)); -// break; -// case CodecMode::ECI: -// // Count doesn't apply to ECI -// result.switchEncoding(ParseECIValue(bits)); -// break; -// case CodecMode::HANZI: { -// // First handle Hanzi mode which does not start with character count -// // chinese mode contains a sub set indicator right after mode indicator -// if (int subset = bits.readBits(4); subset != 1) // GB2312_SUBSET is the only supported one right now -// throw FormatError("Unsupported HANZI subset"); -// int count = bits.readBits(CharacterCountBits(mode, version)); -// DecodeHanziSegment(bits, count, result); -// break; -// } -// default: { -// // "Normal" QR code modes: -// // How many characters will follow, encoded in this mode? -// int count = bits.readBits(CharacterCountBits(mode, version)); -// switch (mode) { -// case CodecMode::NUMERIC: DecodeNumericSegment(bits, count, result); break; -// case CodecMode::ALPHANUMERIC: DecodeAlphanumericSegment(bits, count, result); break; -// case CodecMode::BYTE: DecodeByteSegment(bits, count, result); break; -// case CodecMode::KANJI: DecodeKanjiSegment(bits, count, result); break; -// default: throw FormatError("Invalid CodecMode"); -// } -// break; -// } -// } -// } -// } catch (std::out_of_range& e) { // see BitSource::readBits -// error = FormatError("Truncated bit stream"); -// } catch (Error e) { -// error = std::move(e); -// } + match (mode) { + Mode::FNC1_FIRST_POSITION => { + // if (!result.empty()) // uncomment to enforce specification + // throw FormatError("GS1 Indicator (FNC1 in first position) at illegal position"); + result.symbology.modifier = b'3'; + result.symbology.aiFlag = AIFlag::GS1; // In Alphanumeric mode undouble doubled '%' and treat single '%' as + } + Mode::FNC1_SECOND_POSITION => { + if (!result.is_empty()) { + return Err(Exceptions::format_with("AIM Application Indicator (FNC1 in second position) at illegal position")); + // throw FormatError("AIM Application Indicator (FNC1 in second position) at illegal position"); + } + result.symbology.modifier = b'5'; // As above + // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position), "00-99" or "A-Za-z" + let appInd = bits.readBits(8)?; + if (appInd < 100) + // "00-09" + { + result += + crate::common::cpp_essentials::util::ToString(appInd as usize, 2)?; + } else if ((appInd >= 165 && appInd <= 190) || (appInd >= 197 && appInd <= 222)) + // "A-Za-z" + { + result += (appInd - 100) as u8; + } else { + return Err(Exceptions::format_with("Invalid AIM Application Indicator")); + // throw FormatError("Invalid AIM Application Indicator"); + } + result.symbology.aiFlag = AIFlag::AIM; // see also above + } + Mode::STRUCTURED_APPEND => { + // sequence number and parity is added later to the result metadata + // Read next 4 bits of index, 4 bits of symbol count, and 8 bits of parity data, then continue + structuredAppend.index = bits.readBits(4)? as i32; + structuredAppend.count = bits.readBits(4)? as i32 + 1; + structuredAppend.id = (bits.readBits(8)?).to_string(); //std::to_string(bits.readBits(8)); + } + Mode::ECI => { + // Count doesn't apply to ECI + result.switch_encoding(ParseECIValue(&mut bits)?.into()); + } + Mode::HANZI => { + // First handle Hanzi mode which does not start with character count + // chinese mode contains a sub set indicator right after mode indicator + let subset = bits.readBits(4)?; + if (subset != 1) + // GB2312_SUBSET is the only supported one right now + { + return Err(Exceptions::format_with("Unsupported HANZI subset")); + // throw FormatError("Unsupported HANZI subset"); + } + let count = bits.readBits(mode.CharacterCountBits(version) as usize)?; + DecodeHanziSegment(&mut bits, count, &mut result); + } + _ => { + // "Normal" QR code modes: + // How many characters will follow, encoded in this mode? + let count = bits.readBits(mode.CharacterCountBits(version) as usize)?; + match (mode) { + Mode::NUMERIC => DecodeNumericSegment(&mut bits, count, &mut result), + Mode::ALPHANUMERIC => { + DecodeAlphanumericSegment(&mut bits, count, &mut result) + } + Mode::BYTE => DecodeByteSegment(&mut bits, count, &mut result), + Mode::KANJI => DecodeKanjiSegment(&mut bits, count, &mut result), + _ => return Err(Exceptions::format_with("Invalid CodecMode")), //throw FormatError("Invalid CodecMode"); + }; + } + } + } + Ok(()) + })(); + // } catch (std::out_of_range& e) { // see BitSource::readBits + // error = FormatError("Truncated bit stream"); + // } catch (Error e) { + // error = std::move(e); + // } -// return DecoderResult(std::move(result)) -// .setError(std::move(error)) -// .setEcLevel(ToString(ecLevel)) -// .setVersionNumber(version.versionNumber()) -// .setStructuredAppend(structuredAppend); -// } + Ok(DecoderResult::with_eci_string_builder(result) + .withEcLevel(ecLevel.to_string()) + .withVersionNumber(version.getVersionNumber()) + .withStructuredAppend(structuredAppend)) -// DecoderResult Decode(const BitMatrix& bits) -// { -// const Version* pversion = ReadVersion(bits); -// if (!pversion) -// return FormatError("Invalid version"); -// const Version& version = *pversion; + // return DecoderResult(std::move(result)) + // .setError(std::move(error)) + // .setEcLevel(ToString(ecLevel)) + // .setVersionNumber(version.versionNumber()) + // .setStructuredAppend(structuredAppend); +} -// auto formatInfo = ReadFormatInformation(bits, version.isMicroQRCode()); -// if (!formatInfo.isValid()) -// return FormatError("Invalid format information"); +pub fn Decode(bits: &BitMatrix) -> Result> { + let Ok(pversion) = ReadVersion(bits) else { + return Err(Exceptions::format_with("Invalid version")) + }; + let version = pversion; -// // Read codewords -// ByteArray codewords = ReadCodewords(bits, version, formatInfo); -// if (codewords.empty()) -// return FormatError("Failed to read codewords"); + let Ok(formatInfo) = ReadFormatInformation(bits, version.isMicroQRCode()) else { + return Err(Exceptions::format_with("Invalid format information")) + }; -// // Separate into data blocks -// std::vector dataBlocks = DataBlock::GetDataBlocks(codewords, version, formatInfo.ecLevel); -// if (dataBlocks.empty()) -// return FormatError("Failed to get data blocks"); + // Read codewords + let codewords = ReadCodewords(bits, &version, &formatInfo)?; + if (codewords.is_empty()) { + return Err(Exceptions::format_with("Failed to read codewords")); + } -// // Count total number of data bytes -// const auto op = [](auto totalBytes, const auto& dataBlock){ return totalBytes + dataBlock.numDataCodewords();}; -// const auto totalBytes = std::accumulate(std::begin(dataBlocks), std::end(dataBlocks), int{}, op); -// ByteArray resultBytes(totalBytes); -// auto resultIterator = resultBytes.begin(); + // Separate into data blocks + let dataBlocks: Vec = + DataBlock::getDataBlocks(&codewords, &version, formatInfo.error_correction_level)?; + if (dataBlocks.is_empty()) { + return Err(Exceptions::format_with("Failed to get data blocks")); + } -// // Error-correct and copy data blocks together into a stream of bytes -// for (auto& dataBlock : dataBlocks) -// { -// ByteArray& codewordBytes = dataBlock.codewords(); -// int numDataCodewords = dataBlock.numDataCodewords(); + // Count total number of data bytes + let op = |totalBytes, dataBlock: &DataBlock| totalBytes + dataBlock.getNumDataCodewords(); + let totalBytes = dataBlocks.iter().fold(0, op); // std::accumulate(std::begin(dataBlocks), std::end(dataBlocks), int{}, op); + let mut resultBytes = vec![0u8; totalBytes as usize]; + let mut resultIterator = 0; //resultBytes.begin(); -// if (!CorrectErrors(codewordBytes, numDataCodewords)) -// return ChecksumError(); + // Error-correct and copy data blocks together into a stream of bytes + for dataBlock in dataBlocks.iter() { + let mut codewordBytes = dataBlock.getCodewords().to_vec(); + let numDataCodewords = dataBlock.getNumDataCodewords() as usize; -// resultIterator = std::copy_n(codewordBytes.begin(), numDataCodewords, resultIterator); -// } + if (!CorrectErrors(&mut codewordBytes, numDataCodewords as u32)?) { + return Err(Exceptions::CHECKSUM); + } -// // Decode the contents of that stream of bytes -// return DecodeBitStream(std::move(resultBytes), version, formatInfo.ecLevel).setIsMirrored(formatInfo.isMirrored); -// } + // resultIterator = std::copy_n(codewordBytes.begin(), numDataCodewords, resultIterator); + resultBytes[resultIterator..numDataCodewords] + .copy_from_slice(&codewordBytes[..numDataCodewords]); + resultIterator += numDataCodewords; + } + + // Decode the contents of that stream of bytes + Ok( + DecodeBitStream(&resultBytes, &version, formatInfo.error_correction_level)? + .withIsMirrored(formatInfo.isMirrored), + ) +} // } // namespace ZXing::QRCode diff --git a/src/qrcode/cpp_port/mod.rs b/src/qrcode/cpp_port/mod.rs index 7b4f317..464babf 100644 --- a/src/qrcode/cpp_port/mod.rs +++ b/src/qrcode/cpp_port/mod.rs @@ -1,2 +1,4 @@ pub mod decoder; pub mod detector; + +mod bitmatrix_parser; diff --git a/src/qrcode/decoder/error_correction_level.rs b/src/qrcode/decoder/error_correction_level.rs index 0d5c734..b3be3bc 100644 --- a/src/qrcode/decoder/error_correction_level.rs +++ b/src/qrcode/decoder/error_correction_level.rs @@ -14,6 +14,7 @@ * limitations under the License. */ +use std::fmt::{self, Display}; use std::str::FromStr; use crate::common::Result; @@ -118,3 +119,15 @@ impl FromStr for ErrorCorrectionLevel { ))); } } + +impl Display for ErrorCorrectionLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + ErrorCorrectionLevel::L => "L", + ErrorCorrectionLevel::M => "M", + ErrorCorrectionLevel::Q => "Q", + ErrorCorrectionLevel::H => "H", + ErrorCorrectionLevel::Invalid => "_", + }) + } +} diff --git a/src/qrcode/decoder/format_information.rs b/src/qrcode/decoder/format_information.rs index c0256a6..65800c2 100644 --- a/src/qrcode/decoder/format_information.rs +++ b/src/qrcode/decoder/format_information.rs @@ -72,6 +72,7 @@ pub struct FormatInformation { pub error_correction_level: ErrorCorrectionLevel, pub data_mask: u8, pub microVersion: u32, + pub isMirrored: bool, } impl Default for FormatInformation { @@ -81,6 +82,7 @@ impl Default for FormatInformation { error_correction_level: ErrorCorrectionLevel::Invalid, data_mask: Default::default(), microVersion: 0, + isMirrored: false, } } } @@ -96,6 +98,7 @@ impl FormatInformation { microVersion: 0, error_correction_level: errorCorrectionLevel, data_mask: dataMask, + isMirrored: false, }) } diff --git a/src/qrcode/decoder/mode.rs b/src/qrcode/decoder/mode.rs index ab726f1..06440d7 100644 --- a/src/qrcode/decoder/mode.rs +++ b/src/qrcode/decoder/mode.rs @@ -121,6 +121,32 @@ impl Mode { Mode::HANZI => 0x0D, } } + + pub const fn get_terminator_bit_length(version: &Version) -> u8 { + todo!() + } + pub const fn get_codec_mode_bits_length(version: &Version) -> u8 { + todo!() + } + /** + * @param bits variable number of bits encoding a QR Code data mode + * @param isMicro is this a MicroQRCode + * @return Mode encoded by these bits + * @throws FormatError if bits do not correspond to a known mode + */ + pub fn CodecModeForBits(bits: u32, isMicro: Option) -> Self { + let isMicro = isMicro.unwrap_or(false); + todo!() + } + + /** + * @param version version in question + * @return number of bits used, in this QR Code symbol {@link Version}, to encode the + * count of characters that will follow encoded in this Mode + */ + pub fn CharacterCountBits(&self, version: &Version) -> u32 { + todo!() + } } impl From for u8 {