diff --git a/src/qrcode/decoder/DecodedBitStreamParser.java b/src/qrcode/decoder/DecodedBitStreamParser.java
deleted file mode 100644
index 56ef3f3..0000000
--- a/src/qrcode/decoder/DecodedBitStreamParser.java
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright 2007 ZXing authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.zxing.qrcode.decoder;
-
-import com.google.zxing.DecodeHintType;
-import com.google.zxing.FormatException;
-import com.google.zxing.common.BitSource;
-import com.google.zxing.common.CharacterSetECI;
-import com.google.zxing.common.DecoderRXingResult;
-import com.google.zxing.common.StringUtils;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- *
QR Codes can encode text as bits in one of several modes, and can use multiple modes
- * in one QR Code. This class decodes the bits back into text.
- *
- * See ISO 18004:2006, 6.4.3 - 6.4.7
- *
- * @author Sean Owen
- */
-final class DecodedBitStreamParser {
-
- /**
- * See ISO 18004:2006, 6.4.4 Table 5
- */
- private static final char[] ALPHANUMERIC_CHARS =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".toCharArray();
- private static final int GB2312_SUBSET = 1;
-
- private DecodedBitStreamParser() {
- }
-
- static DecoderRXingResult decode(byte[] bytes,
- Version version,
- ErrorCorrectionLevel ecLevel,
- Map hints) throws FormatException {
- BitSource bits = new BitSource(bytes);
- StringBuilder result = new StringBuilder(50);
- List byteSegments = new ArrayList<>(1);
- int symbolSequence = -1;
- int parityData = -1;
- int symbologyModifier;
-
- try {
- CharacterSetECI currentCharacterSetECI = null;
- boolean fc1InEffect = false;
- boolean hasFNC1first = false;
- boolean hasFNC1second = false;
- Mode mode;
- do {
- // While still another segment to read...
- if (bits.available() < 4) {
- // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
- mode = Mode.TERMINATOR;
- } else {
- mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
- }
- switch (mode) {
- case TERMINATOR:
- break;
- case FNC1_FIRST_POSITION:
- hasFNC1first = true; // symbology detection
- // We do little with FNC1 except alter the parsed result a bit according to the spec
- fc1InEffect = true;
- break;
- case FNC1_SECOND_POSITION:
- hasFNC1second = true; // symbology detection
- // We do little with FNC1 except alter the parsed result a bit according to the spec
- fc1InEffect = true;
- break;
- case STRUCTURED_APPEND:
- if (bits.available() < 16) {
- throw FormatException.getFormatInstance();
- }
- // sequence number and parity is added later to the result metadata
- // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
- symbolSequence = bits.readBits(8);
- parityData = bits.readBits(8);
- break;
- case ECI:
- // Count doesn't apply to ECI
- int value = parseECIValue(bits);
- currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);
- if (currentCharacterSetECI == null) {
- throw FormatException.getFormatInstance();
- }
- break;
- case HANZI:
- // First handle Hanzi mode which does not start with character count
- // Chinese mode contains a sub set indicator right after mode indicator
- int subset = bits.readBits(4);
- int countHanzi = bits.readBits(mode.getCharacterCountBits(version));
- if (subset == GB2312_SUBSET) {
- decodeHanziSegment(bits, result, countHanzi);
- }
- break;
- default:
- // "Normal" QR code modes:
- // How many characters will follow, encoded in this mode?
- int count = bits.readBits(mode.getCharacterCountBits(version));
- switch (mode) {
- case NUMERIC:
- decodeNumericSegment(bits, result, count);
- break;
- case ALPHANUMERIC:
- decodeAlphanumericSegment(bits, result, count, fc1InEffect);
- break;
- case BYTE:
- decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints);
- break;
- case KANJI:
- decodeKanjiSegment(bits, result, count);
- break;
- default:
- throw FormatException.getFormatInstance();
- }
- break;
- }
- } while (mode != Mode.TERMINATOR);
-
- if (currentCharacterSetECI != null) {
- if (hasFNC1first) {
- symbologyModifier = 4;
- } else if (hasFNC1second) {
- symbologyModifier = 6;
- } else {
- symbologyModifier = 2;
- }
- } else {
- if (hasFNC1first) {
- symbologyModifier = 3;
- } else if (hasFNC1second) {
- symbologyModifier = 5;
- } else {
- symbologyModifier = 1;
- }
- }
-
- } catch (IllegalArgumentException iae) {
- // from readBits() calls
- throw FormatException.getFormatInstance();
- }
-
- return new DecoderRXingResult(bytes,
- result.toString(),
- byteSegments.isEmpty() ? null : byteSegments,
- ecLevel == null ? null : ecLevel.toString(),
- symbolSequence,
- parityData,
- symbologyModifier);
- }
-
- /**
- * See specification GBT 18284-2000
- */
- private static void decodeHanziSegment(BitSource bits,
- StringBuilder result,
- int count) throws FormatException {
- // Don't crash trying to read more bits than we have available.
- if (count * 13 > bits.available()) {
- throw FormatException.getFormatInstance();
- }
-
- // Each character will require 2 bytes. Read the characters as 2-byte pairs
- // and decode as GB2312 afterwards
- byte[] buffer = new byte[2 * count];
- int offset = 0;
- 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;
- }
- buffer[offset] = (byte) ((assembledTwoBytes >> 8) & 0xFF);
- buffer[offset + 1] = (byte) (assembledTwoBytes & 0xFF);
- offset += 2;
- count--;
- }
-
- result.append(new String(buffer, StringUtils.GB2312_CHARSET));
- }
-
- private static void decodeKanjiSegment(BitSource bits,
- StringBuilder result,
- int count) throws FormatException {
- // Don't crash trying to read more bits than we have available.
- if (count * 13 > bits.available()) {
- throw FormatException.getFormatInstance();
- }
-
- // Each character will require 2 bytes. Read the characters as 2-byte pairs
- // and decode as Shift_JIS afterwards
- byte[] buffer = new byte[2 * count];
- int offset = 0;
- 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;
- }
- buffer[offset] = (byte) (assembledTwoBytes >> 8);
- buffer[offset + 1] = (byte) assembledTwoBytes;
- offset += 2;
- count--;
- }
- result.append(new String(buffer, StringUtils.SHIFT_JIS_CHARSET));
- }
-
- private static void decodeByteSegment(BitSource bits,
- StringBuilder result,
- int count,
- CharacterSetECI currentCharacterSetECI,
- Collection byteSegments,
- Map hints) throws FormatException {
- // Don't crash trying to read more bits than we have available.
- if (8 * count > bits.available()) {
- throw FormatException.getFormatInstance();
- }
-
- byte[] readBytes = new byte[count];
- for (int i = 0; i < count; i++) {
- readBytes[i] = (byte) bits.readBits(8);
- }
- Charset encoding;
- if (currentCharacterSetECI == null) {
- // The spec isn't clear on this mode; see
- // section 6.4.5: t does not say which encoding to assuming
- // upon decoding. I have seen ISO-8859-1 used as well as
- // Shift_JIS -- without anything like an ECI designator to
- // give a hint.
- encoding = StringUtils.guessCharset(readBytes, hints);
- } else {
- encoding = currentCharacterSetECI.getCharset();
- }
- result.append(new String(readBytes, encoding));
- byteSegments.add(readBytes);
- }
-
- private static char toAlphaNumericChar(int value) throws FormatException {
- if (value >= ALPHANUMERIC_CHARS.length) {
- throw FormatException.getFormatInstance();
- }
- return ALPHANUMERIC_CHARS[value];
- }
-
- private static void decodeAlphanumericSegment(BitSource bits,
- StringBuilder result,
- int count,
- boolean fc1InEffect) throws FormatException {
- // Read two characters at a time
- int start = result.length();
- while (count > 1) {
- if (bits.available() < 11) {
- throw FormatException.getFormatInstance();
- }
- int nextTwoCharsBits = bits.readBits(11);
- result.append(toAlphaNumericChar(nextTwoCharsBits / 45));
- result.append(toAlphaNumericChar(nextTwoCharsBits % 45));
- count -= 2;
- }
- if (count == 1) {
- // special case: one character left
- if (bits.available() < 6) {
- throw FormatException.getFormatInstance();
- }
- result.append(toAlphaNumericChar(bits.readBits(6)));
- }
- // See section 6.4.8.1, 6.4.8.2
- if (fc1InEffect) {
- // We need to massage the result a bit if in an FNC1 mode:
- for (int i = start; i < result.length(); i++) {
- if (result.charAt(i) == '%') {
- if (i < result.length() - 1 && result.charAt(i + 1) == '%') {
- // %% is rendered as %
- result.deleteCharAt(i + 1);
- } else {
- // In alpha mode, % should be converted to FNC1 separator 0x1D
- result.setCharAt(i, (char) 0x1D);
- }
- }
- }
- }
- }
-
- private static void decodeNumericSegment(BitSource bits,
- StringBuilder result,
- int count) throws FormatException {
- // Read three digits at a time
- while (count >= 3) {
- // Each 10 bits encodes three digits
- if (bits.available() < 10) {
- throw FormatException.getFormatInstance();
- }
- int threeDigitsBits = bits.readBits(10);
- if (threeDigitsBits >= 1000) {
- throw FormatException.getFormatInstance();
- }
- result.append(toAlphaNumericChar(threeDigitsBits / 100));
- result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10));
- result.append(toAlphaNumericChar(threeDigitsBits % 10));
- count -= 3;
- }
- if (count == 2) {
- // Two digits left over to read, encoded in 7 bits
- if (bits.available() < 7) {
- throw FormatException.getFormatInstance();
- }
- int twoDigitsBits = bits.readBits(7);
- if (twoDigitsBits >= 100) {
- throw FormatException.getFormatInstance();
- }
- result.append(toAlphaNumericChar(twoDigitsBits / 10));
- result.append(toAlphaNumericChar(twoDigitsBits % 10));
- } else if (count == 1) {
- // One digit left over to read
- if (bits.available() < 4) {
- throw FormatException.getFormatInstance();
- }
- int digitBits = bits.readBits(4);
- if (digitBits >= 10) {
- throw FormatException.getFormatInstance();
- }
- result.append(toAlphaNumericChar(digitBits));
- }
- }
-
- private static int parseECIValue(BitSource bits) throws FormatException {
- int firstByte = bits.readBits(8);
- if ((firstByte & 0x80) == 0) {
- // just one byte
- return firstByte & 0x7F;
- }
- if ((firstByte & 0xC0) == 0x80) {
- // two bytes
- int secondByte = bits.readBits(8);
- return ((firstByte & 0x3F) << 8) | secondByte;
- }
- if ((firstByte & 0xE0) == 0xC0) {
- // three bytes
- int secondThirdBytes = bits.readBits(16);
- return ((firstByte & 0x1F) << 16) | secondThirdBytes;
- }
- throw FormatException.getFormatInstance();
- }
-
-}
diff --git a/src/qrcode/decoder/decoded_bit_stream_parser.rs b/src/qrcode/decoder/decoded_bit_stream_parser.rs
new file mode 100644
index 0000000..ca37e06
--- /dev/null
+++ b/src/qrcode/decoder/decoded_bit_stream_parser.rs
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2007 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::{Exceptions, common::{BitSource, StringUtils, DecoderRXingResult, CharacterSetECI}, DecodingHintDictionary};
+
+use super::{VersionRef, Mode, ErrorCorrectionLevel};
+
+
+/**
+ * QR Codes can encode text as bits in one of several modes, and can use multiple modes
+ * in one QR Code. This class decodes the bits back into text.
+ *
+ * See ISO 18004:2006, 6.4.3 - 6.4.7
+ *
+ * @author Sean Owen
+ */
+
+ /**
+ * See ISO 18004:2006, 6.4.4 Table 5
+ */
+ const ALPHANUMERIC_CHARS : &str=
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
+ const GB2312_SUBSET :u8 = 1;
+
+
+ pub fn decode(bytes : &[u8],
+ version:VersionRef,
+ ecLevel:ErrorCorrectionLevel,
+ hints:DecodingHintDictionary) -> Result {
+ let bits = BitSource::new(bytes);
+ let result = String::with_capacity(50);
+ let byteSegments = vec![vec![0u8;0];0];
+ let symbolSequence = -1;
+ let parityData = -1;
+ let symbologyModifier;
+
+ // try {
+ let currentCharacterSetECI = None;
+ let fc1InEffect = false;
+ let hasFNC1first = false;
+ let hasFNC1second = false;
+ let mode;
+ loop {
+ // While still another segment to read...
+ if (bits.available() < 4) {
+ // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
+ mode = Mode::TERMINATOR;
+ } else {
+ mode = Mode::forBits(bits.readBits(4)); // mode is encoded by 4 bits
+ }
+ match mode {
+ Mode::TERMINATOR => {},
+ Mode::FNC1_FIRST_POSITION=> {
+ hasFNC1first = true; // symbology detection
+ // We do little with FNC1 except alter the parsed result a bit according to the spec
+ fc1InEffect = true;},
+ Mode::FNC1_SECOND_POSITION=> {
+ hasFNC1second = true; // symbology detection
+ // We do little with FNC1 except alter the parsed result a bit according to the spec
+ fc1InEffect = true;},
+ Mode::STRUCTURED_APPEND=> {
+ if (bits.available() < 16) {
+ return Err(Exceptions::FormatException(format!("Mode::Structured append expected bits.available() < 16, found bits of {}", bits.available())))
+ }
+ // sequence number and parity is added later to the result metadata
+ // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
+ symbolSequence = bits.readBits(8);
+ parityData = bits.readBits(8);},
+ Mode::ECI=> {
+ // Count doesn't apply to ECI
+ let value = parseECIValue(&bits)?;
+ currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue(value);
+ if (currentCharacterSetECI.is_none()) {
+ return Err(Exceptions::FormatException(format!("Value of {} not valid", value)))
+
+ }},
+ 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);
+ let countHanzi = bits.readBits(mode.getCharacterCountBits(version));
+ if (subset == GB2312_SUBSET) {
+ decodeHanziSegment(&bits, &result, countHanzi)?;
+ }},
+ _=>{
+ // "Normal" QR code modes:
+ // How many characters will follow, encoded in this mode?
+ let count = bits.readBits(mode.getCharacterCountBits(version));
+ match mode {
+ Mode::NUMERIC=>
+ decodeNumericSegment(&bits, &result, count)?,
+ Mode::ALPHANUMERIC=>decodeAlphanumericSegment(&bits, &result, count, fc1InEffect)?,
+ Mode::BYTE=>decodeByteSegment(&bits, &result, count, currentCharacterSetECI, &byteSegments, hints)?,
+ Mode::KANJI=>decodeKanjiSegment(&bits, &result, count)?,
+ _=>
+ return Err(Exceptions::FormatException("".to_owned()))
+ }},
+ }
+
+ if mode != Mode::TERMINATOR {break}
+ }
+
+ if (currentCharacterSetECI != null) {
+ if (hasFNC1first) {
+ symbologyModifier = 4;
+ } else if (hasFNC1second) {
+ symbologyModifier = 6;
+ } else {
+ symbologyModifier = 2;
+ }
+ } else {
+ if (hasFNC1first) {
+ symbologyModifier = 3;
+ } else if (hasFNC1second) {
+ symbologyModifier = 5;
+ } else {
+ symbologyModifier = 1;
+ }
+ }
+
+ // } catch (IllegalArgumentException iae) {
+ // // from readBits() calls
+ // throw FormatException.getFormatInstance();
+ // }
+
+ return DecoderRXingResult::new(bytes,
+ result.toString(),
+ if byteSegments.isEmpty() {None} else {byteSegments},
+ if ecLevel.is_none() {None} else {ecLevel.toString()},
+ symbolSequence,
+ parityData,
+ symbologyModifier);
+ }
+
+ /**
+ * See specification GBT 18284-2000
+ */
+ fn decodeHanziSegment( bits:&BitSource,
+ result:&String,
+ count:u32) -> Result<(),Exceptions> {
+ // Don't crash trying to read more bits than we have available.
+ if (count * 13 > bits.available()) {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs
+ // and decode as GB2312 afterwards
+ let buffer = vec![0u8;2 * count];
+ let offset = 0;
+ while (count > 0) {
+ // Each 13 bits encodes a 2-byte character
+ let twoBytes = bits.readBits(13)?;
+ let 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;
+ }
+ buffer[offset] = ((assembledTwoBytes >> 8) & 0xFF);
+ buffer[offset + 1] = (assembledTwoBytes & 0xFF);
+ offset += 2;
+ count-=1;
+ }
+
+ let gb_encoder = encoding::label::encoding_from_whatwg_label("GB2312").unwrap();
+ let encode_string = gb_encoder.decode(&buffer, encoding::DecoderTrap::Strict).unwrap();
+ result.append(encode_string);
+ Ok(())
+ }
+
+ fn decodeKanjiSegment( bits:&BitSource,
+ result:&String,
+ count:u32) -> Result<(),Exceptions> {
+ // Don't crash trying to read more bits than we have available.
+ if (count * 13 > bits.available()) {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs
+ // and decode as Shift_JIS afterwards
+ let buffer = vec![0u8;2 * count];
+ let offset = 0;
+ while (count > 0) {
+ // Each 13 bits encodes a 2-byte character
+ let twoBytes = bits.readBits(13);
+ let 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;
+ }
+ buffer[offset] = (assembledTwoBytes >> 8);
+ buffer[offset + 1] = assembledTwoBytes;
+ offset += 2;
+ count-=1;
+ }
+
+ let sjs_encoder = encoding::label::encoding_from_whatwg_label("SJIS").unwrap();
+ let encode_string = sjs_encoder.decode(&buffer, encoding::DecoderTrap::Strict).unwrap();
+ result.append(encode_string);
+
+ Ok(())
+ }
+
+ fn decodeByteSegment( bits:&BitSource,
+ result:&String,
+ count:u32,
+ currentCharacterSetECI:Option<&CharacterSetECI>,
+ byteSegments:&Vec>,
+ hints:DecodingHintDictionary) -> Result<(),Exceptions> {
+ // Don't crash trying to read more bits than we have available.
+ if (8 * count > bits.available()) {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+
+ let readBytes = vec![0u8;count];
+ for i in 0..count {
+ // for (int i = 0; i < count; i++) {
+ readBytes[i] = bits.readBits(8);
+ }
+ let encoding;
+ if (currentCharacterSetECI.is_none()) {
+ // The spec isn't clear on this mode; see
+ // section 6.4.5: t does not say which encoding to assuming
+ // upon decoding. I have seen ISO-8859-1 used as well as
+ // Shift_JIS -- without anything like an ECI designator to
+ // give a hint.
+ encoding = StringUtils::guessCharset(&readBytes, hints);
+ } else {
+ encoding = currentCharacterSetECI.getCharset();
+ }
+ let encode_string = encoding.decode(&readBytes, encoding::DecoderTrap::Strict).unwrap();
+ result.append(encode_string);
+ byteSegments.add(readBytes);
+
+ Ok(())
+ }
+
+ fn toAlphaNumericChar( value:u32) -> Result {
+ if (value >= ALPHANUMERIC_CHARS.len()) {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ Ok( ALPHANUMERIC_CHARS[value])
+ }
+
+ fn decodeAlphanumericSegment( bits:&BitSource,
+ result:&String,
+ count:u32,
+ fc1InEffect:bool) -> Result<(),Exceptions> {
+ // Read two characters at a time
+ let start = result.len();
+ while count > 1 {
+ if bits.available() < 11 {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ let nextTwoCharsBits = bits.readBits(11);
+ result.append(toAlphaNumericChar(nextTwoCharsBits / 45));
+ result.append(toAlphaNumericChar(nextTwoCharsBits % 45));
+ count -= 2;
+ }
+ if (count == 1) {
+ // special case: one character left
+ if (bits.available() < 6) {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ result.append(toAlphaNumericChar(bits.readBits(6)));
+ }
+ // See section 6.4.8.1, 6.4.8.2
+ if fc1InEffect {
+ // We need to massage the result a bit if in an FNC1 mode:
+ for i in start..result.len() {
+ // for (int i = start; i < result.length(); i++) {
+ if (result.charAt(i) == '%') {
+ if (i < result.length() - 1 && result.charAt(i + 1) == '%') {
+ // %% is rendered as %
+ result.deleteCharAt(i + 1);
+ } else {
+ // In alpha mode, % should be converted to FNC1 separator 0x1D
+ result.setCharAt(i, 0x1D);
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ fn decodeNumericSegment( bits:&BitSource,
+ result:&String,
+ count:u32) -> Result<(),Exceptions> {
+ // Read three digits at a time
+ while count >= 3 {
+ // Each 10 bits encodes three digits
+ if bits.available() < 10 {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ let threeDigitsBits = bits.readBits(10)?;
+ if threeDigitsBits >= 1000 {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ result.append(toAlphaNumericChar(threeDigitsBits / 100));
+ result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10));
+ result.append(toAlphaNumericChar(threeDigitsBits % 10));
+ count -= 3;
+ }
+ if count == 2 {
+ // Two digits left over to read, encoded in 7 bits
+ if bits.available() < 7 {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ let twoDigitsBits = bits.readBits(7)?;
+ if twoDigitsBits >= 100 {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ result.append(toAlphaNumericChar(twoDigitsBits / 10));
+ result.append(toAlphaNumericChar(twoDigitsBits % 10));
+ } else if count == 1 {
+ // One digit left over to read
+ if bits.available() < 4 {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ let digitBits = bits.readBits(4)?;
+ if digitBits >= 10 {
+ return Err(Exceptions::FormatException("".to_owned()))
+ }
+ result.append(toAlphaNumericChar(digitBits));
+ }
+
+ Ok(())
+ }
+
+ fn parseECIValue( bits:&BitSource) -> Result {
+ let firstByte = bits.readBits(8)?;
+ if (firstByte & 0x80) == 0 {
+ // just one byte
+ return Ok(firstByte & 0x7F);
+ }
+ if (firstByte & 0xC0) == 0x80 {
+ // two bytes
+ let secondByte = bits.readBits(8);
+ return Ok(((firstByte & 0x3F) << 8) | secondByte);
+ }
+ if (firstByte & 0xE0) == 0xC0 {
+ // three bytes
+ let secondThirdBytes = bits.readBits(16);
+ return Ok(((firstByte & 0x1F) << 16) | secondThirdBytes);
+ }
+
+ Err(Exceptions::FormatException("".to_owned()))
+ }
+
diff --git a/src/qrcode/decoder/mod.rs b/src/qrcode/decoder/mod.rs
index 172a2c1..74eb7b6 100644
--- a/src/qrcode/decoder/mod.rs
+++ b/src/qrcode/decoder/mod.rs
@@ -6,6 +6,7 @@ mod data_block;
mod qr_code_decoder_meta_data;
mod data_mask;
mod bit_matrix_parser;
+pub mod decoded_bit_stream_parser;
#[cfg(test)]
mod ErrorCorrectionLevelTestCase;