Files
aistudio-wpf-diagram/zxing.core/zxing.core/pdf417/decoder/DecodedBitStreamParser.cs
2021-07-23 09:42:22 +08:00

862 lines
38 KiB
C#

/*
* Copyright 2009 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.
*/
using System;
using System.Text;
#if (SILVERLIGHT4 || SILVERLIGHT5 || NET40 || NET45 || NET46 || NET47 || NET48 || NETFX_CORE || NETSTANDARD) && !NETSTANDARD1_0
using System.Numerics;
#else
using BigIntegerLibrary;
#endif
using ZXing.Common;
namespace ZXing.PDF417.Internal
{
/// <summary>
/// <p>This class contains the methods for decoding the PDF417 codewords.</p>
///
/// <author>SITA Lab (kevin.osullivan@sita.aero)</author>
/// </summary>
internal static class DecodedBitStreamParser
{
private enum Mode
{
ALPHA,
LOWER,
MIXED,
PUNCT,
ALPHA_SHIFT,
PUNCT_SHIFT
}
private const int TEXT_COMPACTION_MODE_LATCH = 900;
private const int BYTE_COMPACTION_MODE_LATCH = 901;
private const int NUMERIC_COMPACTION_MODE_LATCH = 902;
private const int BYTE_COMPACTION_MODE_LATCH_6 = 924;
private const int ECI_USER_DEFINED = 925;
private const int ECI_GENERAL_PURPOSE = 926;
private const int ECI_CHARSET = 927;
private const int BEGIN_MACRO_PDF417_CONTROL_BLOCK = 928;
private const int BEGIN_MACRO_PDF417_OPTIONAL_FIELD = 923;
private const int MACRO_PDF417_TERMINATOR = 922;
private const int MODE_SHIFT_TO_BYTE_COMPACTION_MODE = 913;
private const int MAX_NUMERIC_CODEWORDS = 15;
private const int MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME = 0;
private const int MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT = 1;
private const int MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP = 2;
private const int MACRO_PDF417_OPTIONAL_FIELD_SENDER = 3;
private const int MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE = 4;
private const int MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE = 5;
private const int MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM = 6;
private const int PL = 25;
private const int LL = 27;
private const int AS = 27;
private const int ML = 28;
private const int AL = 28;
private const int PS = 29;
private const int PAL = 29;
private static readonly char[] PUNCT_CHARS = ";<>@[\\]_`~!\r\t,:\n-.$/\"|*()?{}'".ToCharArray();
private static readonly char[] MIXED_CHARS = "0123456789&\r\t,:#-.$/+%*=^".ToCharArray();
#if (SILVERLIGHT4 || SILVERLIGHT5 || NET40 || NET45 || NET46 || NET47 || NET48 || NETFX_CORE || NETSTANDARD) && !NETSTANDARD1_0
/// <summary>
/// Table containing values for the exponent of 900.
/// This is used in the numeric compaction decode algorithm.
/// </summary>
private static readonly BigInteger[] EXP900;
static DecodedBitStreamParser()
{
EXP900 = new BigInteger[16];
EXP900[0] = BigInteger.One;
BigInteger nineHundred = new BigInteger(900);
EXP900[1] = nineHundred;
for (int i = 2; i < EXP900.Length; i++)
{
EXP900[i] = BigInteger.Multiply(EXP900[i - 1], nineHundred);
}
}
#else
/// <summary>
/// Table containing values for the exponent of 900.
/// This is used in the numeric compaction decode algorithm.
/// </summary>
private static readonly BigInteger[] EXP900;
static DecodedBitStreamParser()
{
EXP900 = new BigInteger[16];
EXP900[0] = BigInteger.One;
BigInteger nineHundred = new BigInteger(900);
EXP900[1] = nineHundred;
for (int i = 2; i < EXP900.Length; i++)
{
EXP900[i] = BigInteger.Multiplication(EXP900[i - 1], nineHundred);
}
}
#endif
private const int NUMBER_OF_SEQUENCE_CODEWORDS = 2;
internal static DecoderResult decode(int[] codewords, String ecLevel)
{
var result = new StringBuilder(codewords.Length * 2);
// Get compaction mode
int codeIndex = 1;
int code = codewords[codeIndex++];
var resultMetadata = new PDF417ResultMetadata();
Encoding encoding = null;
while (codeIndex <= codewords[0])
{
switch (code)
{
case TEXT_COMPACTION_MODE_LATCH:
codeIndex = textCompaction(codewords, codeIndex, result);
break;
case BYTE_COMPACTION_MODE_LATCH:
case BYTE_COMPACTION_MODE_LATCH_6:
codeIndex = byteCompaction(code, codewords, encoding ?? (encoding = CharacterSetECI.getEncoding(PDF417HighLevelEncoder.DEFAULT_ENCODING_NAME)), codeIndex, result);
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
if (encoding == null)
encoding = CharacterSetECI.getEncoding(PDF417HighLevelEncoder.DEFAULT_ENCODING_NAME);
result.Append(encoding.GetString(new []{(byte)codewords[codeIndex++]}, 0, 1));
break;
case NUMERIC_COMPACTION_MODE_LATCH:
codeIndex = numericCompaction(codewords, codeIndex, result);
break;
case ECI_CHARSET:
var charsetECI = CharacterSetECI.getCharacterSetECIByValue(codewords[codeIndex++]);
encoding = CharacterSetECI.getEncoding(charsetECI.EncodingName);
break;
case ECI_GENERAL_PURPOSE:
// Can't do anything with generic ECI; skip its 2 characters
codeIndex += 2;
break;
case ECI_USER_DEFINED:
// Can't do anything with user ECI; skip its 1 character
codeIndex++;
break;
case BEGIN_MACRO_PDF417_CONTROL_BLOCK:
codeIndex = decodeMacroBlock(codewords, codeIndex, resultMetadata);
break;
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
case MACRO_PDF417_TERMINATOR:
// Should not see these outside a macro block
return null;
default:
// Default to text compaction. During testing numerous barcodes
// appeared to be missing the starting mode. In these cases defaulting
// to text compaction seems to work.
codeIndex--;
codeIndex = textCompaction(codewords, codeIndex, result);
break;
}
if (codeIndex < 0)
return null;
if (codeIndex < codewords.Length)
{
code = codewords[codeIndex++];
}
else
{
return null;
}
}
if (result.Length == 0)
{
return null;
}
var decoderResult = new DecoderResult(null, result.ToString(), null, ecLevel);
decoderResult.Other = resultMetadata;
return decoderResult;
}
internal static int decodeMacroBlock(int[] codewords, int codeIndex, PDF417ResultMetadata resultMetadata)
{
if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0])
{
// we must have at least two bytes left for the segment index
return -1;
}
var segmentIndexArray = new int[NUMBER_OF_SEQUENCE_CODEWORDS];
for (var i = 0; i < NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++)
{
segmentIndexArray[i] = codewords[codeIndex];
}
var s = decodeBase900toBase10(segmentIndexArray, NUMBER_OF_SEQUENCE_CODEWORDS);
if (s == null)
return -1;
resultMetadata.SegmentIndex = Int32.Parse(s);
var fileId = new StringBuilder();
codeIndex = textCompaction(codewords, codeIndex, fileId);
resultMetadata.FileId = fileId.ToString();
int optionalFieldsStart = -1;
if (codewords[codeIndex] == BEGIN_MACRO_PDF417_OPTIONAL_FIELD)
{
optionalFieldsStart = codeIndex + 1;
}
while (codeIndex < codewords[0])
{
switch (codewords[codeIndex])
{
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
codeIndex++;
switch (codewords[codeIndex])
{
case MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME:
var fileName = new StringBuilder();
codeIndex = textCompaction(codewords, codeIndex + 1, fileName);
resultMetadata.FileName = fileName.ToString();
break;
case MACRO_PDF417_OPTIONAL_FIELD_SENDER:
var sender = new StringBuilder();
codeIndex = textCompaction(codewords, codeIndex + 1, sender);
resultMetadata.Sender = sender.ToString();
break;
case MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE:
var addressee = new StringBuilder();
codeIndex = textCompaction(codewords, codeIndex + 1, addressee);
resultMetadata.Addressee = addressee.ToString();
break;
case MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT:
{
var segmentCount = new StringBuilder();
codeIndex = numericCompaction(codewords, codeIndex + 1, segmentCount);
#if WindowsCE
try { resultMetadata.SegmentCount = Int32.Parse(segmentCount.ToString()); }
catch { }
#else
int intResult;
if (Int32.TryParse(segmentCount.ToString(), out intResult))
resultMetadata.SegmentCount = intResult;
#endif
}
break;
case MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP:
{
var timestamp = new StringBuilder();
codeIndex = numericCompaction(codewords, codeIndex + 1, timestamp);
#if WindowsCE
try { resultMetadata.Timestamp = Int64.Parse(timestamp.ToString()); }
catch { }
#else
long longResult;
if (Int64.TryParse(timestamp.ToString(), out longResult))
resultMetadata.Timestamp = longResult;
#endif
}
break;
case MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM:
{
var checksum = new StringBuilder();
codeIndex = numericCompaction(codewords, codeIndex + 1, checksum);
#if WindowsCE
try { resultMetadata.Checksum = Int32.Parse(checksum.ToString()); }
catch { }
#else
int intResult;
if (Int32.TryParse(checksum.ToString(), out intResult))
resultMetadata.Checksum = intResult;
#endif
}
break;
case MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE:
{
var fileSize = new StringBuilder();
codeIndex = numericCompaction(codewords, codeIndex + 1, fileSize);
#if WindowsCE
try { resultMetadata.FileSize = Int64.Parse(fileSize.ToString()); }
catch { }
#else
long longResult;
if (Int64.TryParse(fileSize.ToString(), out longResult))
resultMetadata.FileSize = longResult;
#endif
}
break;
default:
break;
}
break;
case MACRO_PDF417_TERMINATOR:
codeIndex++;
resultMetadata.IsLastSegment = true;
break;
default:
break;
}
}
return codeIndex;
}
/// <summary>
/// Text Compaction mode (see 5.4.1.5) permits all printable ASCII characters to be
/// encoded, i.e. values 32 - 126 inclusive in accordance with ISO/IEC 646 (IRV), as
/// well as selected control characters.
///
/// <param name="codewords">The array of codewords (data + error)</param>
/// <param name="codeIndex">The current index into the codeword array.</param>
/// <param name="result">The decoded data is appended to the result.</param>
/// <returns>The next index into the codeword array.</returns>
/// </summary>
private static int textCompaction(int[] codewords, int codeIndex, StringBuilder result)
{
// 2 character per codeword
int[] textCompactionData = new int[(codewords[0] - codeIndex) << 1];
// Used to hold the byte compaction value if there is a mode shift
int[] byteCompactionData = new int[(codewords[0] - codeIndex) << 1];
int index = 0;
bool end = false;
while ((codeIndex < codewords[0]) && !end)
{
int code = codewords[codeIndex++];
if (code < TEXT_COMPACTION_MODE_LATCH)
{
textCompactionData[index] = code / 30;
textCompactionData[index + 1] = code % 30;
index += 2;
}
else
{
switch (code)
{
case TEXT_COMPACTION_MODE_LATCH:
// reinitialize text compaction mode to alpha sub mode
textCompactionData[index++] = TEXT_COMPACTION_MODE_LATCH;
break;
case BYTE_COMPACTION_MODE_LATCH:
case BYTE_COMPACTION_MODE_LATCH_6:
case NUMERIC_COMPACTION_MODE_LATCH:
case BEGIN_MACRO_PDF417_CONTROL_BLOCK:
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
case MACRO_PDF417_TERMINATOR:
codeIndex--;
end = true;
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
// The Mode Shift codeword 913 shall cause a temporary
// switch from Text Compaction mode to Byte Compaction mode.
// This switch shall be in effect for only the next codeword,
// after which the mode shall revert to the prevailing sub-mode
// of the Text Compaction mode. Codeword 913 is only available
// in Text Compaction mode; its use is described in 5.4.2.4.
textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE;
code = codewords[codeIndex++];
byteCompactionData[index] = code;
index++;
break;
}
}
}
decodeTextCompaction(textCompactionData, byteCompactionData, index, result);
return codeIndex;
}
/// <summary>
/// The Text Compaction mode includes all the printable ASCII characters
/// (i.e. values from 32 to 126) and three ASCII control characters: HT or tab
/// (ASCII value 9), LF or line feed (ASCII value 10), and CR or carriage
/// return (ASCII value 13). The Text Compaction mode also includes various latch
/// and shift characters which are used exclusively within the mode. The Text
/// Compaction mode encodes up to 2 characters per codeword. The compaction rules
/// for converting data into PDF417 codewords are defined in 5.4.2.2. The sub-mode
/// switches are defined in 5.4.2.3.
///
/// <param name="textCompactionData">The text compaction data.</param>
/// <param name="byteCompactionData">The byte compaction data if there</param>
/// was a mode shift.
/// <param name="length">The size of the text compaction and byte compaction data.</param>
/// <param name="result">The decoded data is appended to the result.</param>
/// </summary>
private static void decodeTextCompaction(int[] textCompactionData,
int[] byteCompactionData,
int length,
StringBuilder result)
{
// Beginning from an initial state of the Alpha sub-mode
// The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text
// Compaction mode Alpha sub-mode (uppercase alphabetic). A latch codeword from another mode to the Text
// Compaction mode shall always switch to the Text Compaction Alpha sub-mode.
Mode subMode = Mode.ALPHA;
Mode priorToShiftMode = Mode.ALPHA;
int i = 0;
while (i < length)
{
int subModeCh = textCompactionData[i];
char? ch = null;
switch (subMode)
{
case Mode.ALPHA:
// Alpha (uppercase alphabetic)
if (subModeCh < 26)
{
// Upper case Alpha Character
ch = (char)('A' + subModeCh);
}
else
{
switch (subModeCh)
{
case 26:
ch = ' ';
break;
case LL:
subMode = Mode.LOWER;
break;
case ML:
subMode = Mode.MIXED;
break;
case PS:
// Shift to punctuation
priorToShiftMode = subMode;
subMode = Mode.PUNCT_SHIFT;
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
// TODO Does this need to use the current character encoding? See other occurrences below
result.Append((char)byteCompactionData[i]);
break;
case TEXT_COMPACTION_MODE_LATCH:
subMode = Mode.ALPHA;
break;
}
}
break;
case Mode.LOWER:
// Lower (lowercase alphabetic)
if (subModeCh < 26)
{
ch = (char)('a' + subModeCh);
}
else
{
switch (subModeCh)
{
case 26:
ch = ' ';
break;
case AS:
// Shift to alpha
priorToShiftMode = subMode;
subMode = Mode.ALPHA_SHIFT;
break;
case ML:
subMode = Mode.MIXED;
break;
case PS:
// Shift to punctuation
priorToShiftMode = subMode;
subMode = Mode.PUNCT_SHIFT;
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
// TODO Does this need to use the current character encoding? See other occurrences below
result.Append((char)byteCompactionData[i]);
break;
case TEXT_COMPACTION_MODE_LATCH:
subMode = Mode.ALPHA;
break;
}
}
break;
case Mode.MIXED:
// Mixed (numeric and some punctuation)
if (subModeCh < PL)
{
ch = MIXED_CHARS[subModeCh];
}
else
{
switch (subModeCh)
{
case PL:
subMode = Mode.PUNCT;
break;
case 26:
ch = ' ';
break;
case LL:
subMode = Mode.LOWER;
break;
case AL:
subMode = Mode.ALPHA;
break;
case PS:
// Shift to punctuation
priorToShiftMode = subMode;
subMode = Mode.PUNCT_SHIFT;
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
result.Append((char)byteCompactionData[i]);
break;
case TEXT_COMPACTION_MODE_LATCH:
subMode = Mode.ALPHA;
break;
}
}
break;
case Mode.PUNCT:
// Punctuation
if (subModeCh < PAL)
{
ch = PUNCT_CHARS[subModeCh];
}
else
{
switch (subModeCh)
{
case PAL:
subMode = Mode.ALPHA;
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
result.Append((char)byteCompactionData[i]);
break;
case TEXT_COMPACTION_MODE_LATCH:
subMode = Mode.ALPHA;
break;
}
}
break;
case Mode.ALPHA_SHIFT:
// Restore sub-mode
subMode = priorToShiftMode;
if (subModeCh < 26)
{
ch = (char)('A' + subModeCh);
}
else
{
switch (subModeCh)
{
case 26:
ch = ' ';
break;
case TEXT_COMPACTION_MODE_LATCH:
subMode = Mode.ALPHA;
break;
}
}
break;
case Mode.PUNCT_SHIFT:
// Restore sub-mode
subMode = priorToShiftMode;
if (subModeCh < PAL)
{
ch = PUNCT_CHARS[subModeCh];
}
else
{
switch (subModeCh)
{
case PAL:
subMode = Mode.ALPHA;
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
// PS before Shift-to-Byte is used as a padding character,
// see 5.4.2.4 of the specification
result.Append((char)byteCompactionData[i]);
break;
case TEXT_COMPACTION_MODE_LATCH:
subMode = Mode.ALPHA;
break;
}
}
break;
}
if (ch != null)
{
// Append decoded character to result
result.Append(ch.Value);
}
i++;
}
}
/// <summary>
/// Byte Compaction mode (see 5.4.3) permits all 256 possible 8-bit byte values to be encoded.
/// This includes all ASCII characters value 0 to 127 inclusive and provides for international
/// character set support.
///
/// <param name="mode">The byte compaction mode i.e. 901 or 924</param>
/// <param name="codewords">The array of codewords (data + error)</param>
/// <param name="encoding">Currently active character encoding</param>
/// <param name="codeIndex">The current index into the codeword array.</param>
/// <param name="result">The decoded data is appended to the result.</param>
/// <returns>The next index into the codeword array.</returns>
/// </summary>
private static int byteCompaction(int mode, int[] codewords, Encoding encoding, int codeIndex, StringBuilder result)
{
var count = 0;
var value = 0L;
var end = false;
using (var decodedBytes = new System.IO.MemoryStream())
{
switch (mode)
{
case BYTE_COMPACTION_MODE_LATCH:
// Total number of Byte Compaction characters to be encoded
// is not a multiple of 6
int[] byteCompactedCodewords = new int[6];
int nextCode = codewords[codeIndex++];
while ((codeIndex < codewords[0]) && !end)
{
byteCompactedCodewords[count++] = nextCode;
// Base 900
value = 900 * value + nextCode;
nextCode = codewords[codeIndex++];
// perhaps it should be ok to check only nextCode >= TEXT_COMPACTION_MODE_LATCH
switch (nextCode)
{
case TEXT_COMPACTION_MODE_LATCH:
case BYTE_COMPACTION_MODE_LATCH:
case NUMERIC_COMPACTION_MODE_LATCH:
case BYTE_COMPACTION_MODE_LATCH_6:
case BEGIN_MACRO_PDF417_CONTROL_BLOCK:
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
case MACRO_PDF417_TERMINATOR:
codeIndex--;
end = true;
break;
default:
if ((count % 5 == 0) && (count > 0))
{
// Decode every 5 codewords
// Convert to Base 256
for (int j = 0; j < 6; ++j)
{
decodedBytes.WriteByte((byte)(value >> (8 * (5 - j))));
}
value = 0;
count = 0;
}
break;
}
}
// if the end of all codewords is reached the last codeword needs to be added
if (codeIndex == codewords[0] && nextCode < TEXT_COMPACTION_MODE_LATCH)
{
byteCompactedCodewords[count++] = nextCode;
}
// If Byte Compaction mode is invoked with codeword 901,
// the last group of codewords is interpreted directly
// as one byte per codeword, without compaction.
for (int i = 0; i < count; i++)
{
decodedBytes.WriteByte((byte)byteCompactedCodewords[i]);
}
break;
case BYTE_COMPACTION_MODE_LATCH_6:
// Total number of Byte Compaction characters to be encoded
// is an integer multiple of 6
while (codeIndex < codewords[0] && !end)
{
int code = codewords[codeIndex++];
if (code < TEXT_COMPACTION_MODE_LATCH)
{
count++;
// Base 900
value = 900 * value + code;
}
else
{
switch (code)
{
case TEXT_COMPACTION_MODE_LATCH:
case BYTE_COMPACTION_MODE_LATCH:
case NUMERIC_COMPACTION_MODE_LATCH:
case BYTE_COMPACTION_MODE_LATCH_6:
case BEGIN_MACRO_PDF417_CONTROL_BLOCK:
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
case MACRO_PDF417_TERMINATOR:
codeIndex--;
end = true;
break;
}
}
if ((count % 5 == 0) && (count > 0))
{
// Decode every 5 codewords
// Convert to Base 256
for (int j = 0; j < 6; ++j)
{
decodedBytes.WriteByte((byte)(value >> (8 * (5 - j))));
}
value = 0;
count = 0;
}
}
break;
}
var bytes = decodedBytes.ToArray();
result.Append(encoding.GetString(bytes, 0, bytes.Length));
}
return codeIndex;
}
/// <summary>
/// Numeric Compaction mode (see 5.4.4) permits efficient encoding of numeric data strings.
///
/// <param name="codewords">The array of codewords (data + error)</param>
/// <param name="codeIndex">The current index into the codeword array.</param>
/// <param name="result">The decoded data is appended to the result.</param>
/// <returns>The next index into the codeword array.</returns>
/// </summary>
private static int numericCompaction(int[] codewords, int codeIndex, StringBuilder result)
{
int count = 0;
bool end = false;
int[] numericCodewords = new int[MAX_NUMERIC_CODEWORDS];
while (codeIndex < codewords[0] && !end)
{
int code = codewords[codeIndex++];
if (codeIndex == codewords[0])
{
end = true;
}
if (code < TEXT_COMPACTION_MODE_LATCH)
{
numericCodewords[count] = code;
count++;
}
else
{
switch (code)
{
case TEXT_COMPACTION_MODE_LATCH:
case BYTE_COMPACTION_MODE_LATCH:
case BYTE_COMPACTION_MODE_LATCH_6:
case BEGIN_MACRO_PDF417_CONTROL_BLOCK:
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD:
case MACRO_PDF417_TERMINATOR:
codeIndex--;
end = true;
break;
}
}
if ((count % MAX_NUMERIC_CODEWORDS == 0 ||
code == NUMERIC_COMPACTION_MODE_LATCH ||
end) &&
count > 0)
{
// Re-invoking Numeric Compaction mode (by using codeword 902
// while in Numeric Compaction mode) serves to terminate the
// current Numeric Compaction mode grouping as described in 5.4.4.2,
// and then to start a new one grouping.
var s = decodeBase900toBase10(numericCodewords, count);
if (s == null)
return -1;
result.Append(s);
count = 0;
}
}
return codeIndex;
}
/// <summary>
/// Convert a list of Numeric Compacted codewords from Base 900 to Base 10.
/// EXAMPLE
/// Encode the fifteen digit numeric string 000213298174000
/// Prefix the numeric string with a 1 and set the initial value of
/// t = 1 000 213 298 174 000
/// Calculate codeword 0
/// d0 = 1 000 213 298 174 000 mod 900 = 200
///
/// t = 1 000 213 298 174 000 div 900 = 1 111 348 109 082
/// Calculate codeword 1
/// d1 = 1 111 348 109 082 mod 900 = 282
///
/// t = 1 111 348 109 082 div 900 = 1 234 831 232
/// Calculate codeword 2
/// d2 = 1 234 831 232 mod 900 = 632
///
/// t = 1 234 831 232 div 900 = 1 372 034
/// Calculate codeword 3
/// d3 = 1 372 034 mod 900 = 434
///
/// t = 1 372 034 div 900 = 1 524
/// Calculate codeword 4
/// d4 = 1 524 mod 900 = 624
///
/// t = 1 524 div 900 = 1
/// Calculate codeword 5
/// d5 = 1 mod 900 = 1
/// t = 1 div 900 = 0
/// Codeword sequence is: 1, 624, 434, 632, 282, 200
///
/// Decode the above codewords involves
/// 1 x 900 power of 5 + 624 x 900 power of 4 + 434 x 900 power of 3 +
/// 632 x 900 power of 2 + 282 x 900 power of 1 + 200 x 900 power of 0 = 1000213298174000
///
/// Remove leading 1 => Result is 000213298174000
/// <param name="codewords">The array of codewords</param>
/// <param name="count">The number of codewords</param>
/// <returns>The decoded string representing the Numeric data.</returns>
/// </summary>
private static String decodeBase900toBase10(int[] codewords, int count)
{
#if (SILVERLIGHT4 || SILVERLIGHT5 || NET40 || NET45 || NET46 || NET47 || NET48 || NETFX_CORE || NETSTANDARD) && !NETSTANDARD1_0
BigInteger result = BigInteger.Zero;
for (int i = 0; i < count; i++)
{
result = BigInteger.Add(result, BigInteger.Multiply(EXP900[count - i - 1], new BigInteger(codewords[i])));
}
String resultString = result.ToString();
if (resultString[0] != '1')
{
return null;
}
return resultString.Substring(1);
#else
BigInteger result = BigInteger.Zero;
for (int i = 0; i < count; i++)
{
result = BigInteger.Addition(result, BigInteger.Multiplication(EXP900[count - i - 1], new BigInteger(codewords[i])));
}
String resultString = result.ToString();
if (resultString[0] != '1')
{
return null;
}
return resultString.Substring(1);
#endif
}
}
}