This provides an easy abstraction to read bits at a time from a sequence of bytes, where the number of bits read is not often a multiple of 8.

This class is thread-safe but not reentrant. Unless the caller modifies the bytes array it passed in, in which case all bets are off.

        internal static DecoderResult decode(sbyte[] bytes, Version version, ErrorCorrectionLevel ecLevel)
        {
            var bits = new BitSource(bytes);
            var result = new StringBuilder(50);
            CharacterSetECI currentCharacterSetECI = null;
            bool fc1InEffect = false;
            var byteSegments = new List<byte[]>(1);
            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
                {
                    try
                    {
                        mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
                    }
                    catch (ArgumentException)
                    {
                        throw ReaderException.Instance;
                    }
                }
                if (!mode.Equals(Mode.TERMINATOR))
                {
                    if (mode.Equals(Mode.FNC1_FIRST_POSITION) || mode.Equals(Mode.FNC1_SECOND_POSITION))
                    {
                        // We do little with FNC1 except alter the parsed result a bit according to the spec
                        fc1InEffect = true;
                    }
                    else if (mode.Equals(Mode.STRUCTURED_APPEND))
                    {
                        // not really supported; all we do is ignore it
                        // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
                        bits.readBits(16);
                    }
                    else if (mode.Equals(Mode.ECI))
                    {
                        // Count doesn't apply to ECI
                        int value_Renamed = parseECIValue(bits);
                        currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value_Renamed);
                        if (currentCharacterSetECI == null)
                        {
                            throw ReaderException.Instance;
                        }
                    }
                    else
                    {
                        // How many characters will follow, encoded in this mode?
                        int count = bits.readBits(mode.getCharacterCountBits(version));
                        if (mode.Equals(Mode.NUMERIC))
                        {
                            decodeNumericSegment(bits, result, count);
                        }
                        else if (mode.Equals(Mode.ALPHANUMERIC))
                        {
                            decodeAlphanumericSegment(bits, result, count, fc1InEffect);
                        }
                        else if (mode.Equals(Mode.BYTE))
                        {
                            decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments);
                        }
                        else if (mode.Equals(Mode.KANJI))
                        {
                            decodeKanjiSegment(bits, result, count);
                        }
                        else
                        {
                            throw ReaderException.Instance;
                        }
                    }
                }
            } while (!mode.Equals(Mode.TERMINATOR));

            return new DecoderResult(bytes, result.ToString(), (byteSegments.Count == 0) ? null : byteSegments, ecLevel);
        }
 private static void decodeNumericSegment(BitSource bits, StringBuilder result, int count)
 {
     // Read three digits at a time
     while (count >= 3)
     {
         // Each 10 bits encodes three digits
         int threeDigitsBits = bits.readBits(10);
         if (threeDigitsBits >= 1000)
         {
             throw ReaderException.Instance;
         }
         result.Append(ALPHANUMERIC_CHARS[threeDigitsBits/100]);
         result.Append(ALPHANUMERIC_CHARS[(threeDigitsBits/10)%10]);
         result.Append(ALPHANUMERIC_CHARS[threeDigitsBits%10]);
         count -= 3;
     }
     if (count == 2)
     {
         // Two digits left over to read, encoded in 7 bits
         int twoDigitsBits = bits.readBits(7);
         if (twoDigitsBits >= 100)
         {
             throw ReaderException.Instance;
         }
         result.Append(ALPHANUMERIC_CHARS[twoDigitsBits/10]);
         result.Append(ALPHANUMERIC_CHARS[twoDigitsBits%10]);
     }
     else if (count == 1)
     {
         // One digit left over to read
         int digitBits = bits.readBits(4);
         if (digitBits >= 10)
         {
             throw ReaderException.Instance;
         }
         result.Append(ALPHANUMERIC_CHARS[digitBits]);
     }
 }
 private static int parseECIValue(BitSource bits)
 {
     int firstByte = bits.readBits(8);
     if ((firstByte & 0x80) == 0)
     {
         // just one byte
         return firstByte & 0x7F;
     }
     else if ((firstByte & 0xC0) == 0x80)
     {
         // two bytes
         int secondByte = bits.readBits(8);
         return ((firstByte & 0x3F) << 8) | secondByte;
     }
     else if ((firstByte & 0xE0) == 0xC0)
     {
         // three bytes
         int secondThirdBytes = bits.readBits(16);
         return ((firstByte & 0x1F) << 16) | secondThirdBytes;
     }
     throw new ArgumentException("Bad ECI bits starting with byte " + firstByte);
 }
 private static void decodeByteSegment(BitSource bits, StringBuilder result, int count,
                                       CharacterSetECI currentCharacterSetECI, List<byte[]> byteSegments)
 {
     var readBytes = new sbyte[count];
     if (count << 3 > bits.available())
     {
         throw ReaderException.Instance;
     }
     for (int i = 0; i < count; i++)
     {
         readBytes[i] = (sbyte) bits.readBits(8);
     }
     String 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 = guessEncoding(readBytes);
     }
     else
     {
         encoding = currentCharacterSetECI.EncodingName;
     }
     try
     {
         byte[] byteArray = SupportClass.ToByteArray(readBytes);
         result.Append(Encoding.GetEncoding(encoding).GetString(byteArray, 0, byteArray.Length));
     }
     catch (IOException)
     {
         throw ReaderException.Instance;
     }
     byteSegments.Add(SupportClass.ToByteArray(readBytes));
 }
 private static void decodeAlphanumericSegment(BitSource bits, StringBuilder result, int count, bool fc1InEffect)
 {
     // Read two characters at a time
     int start = result.Length;
     while (count > 1)
     {
         int nextTwoCharsBits = bits.readBits(11);
         result.Append(ALPHANUMERIC_CHARS[nextTwoCharsBits/45]);
         result.Append(ALPHANUMERIC_CHARS[nextTwoCharsBits%45]);
         count -= 2;
     }
     if (count == 1)
     {
         // special case: one character left
         result.Append(ALPHANUMERIC_CHARS[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[i] == '%')
             {
                 if (i < result.Length - 1 && result[i + 1] == '%')
                 {
                     // %% is rendered as %
                     result.Remove(i + 1, 1);
                 }
                 else
                 {
                     // In alpha mode, % should be converted to FNC1 separator 0x1D
                     result[i] = (char) 0x1D;
                 }
             }
         }
     }
 }
        internal static DecoderResult decode(sbyte[] bytes)
        {
            var bits = new BitSource(bytes);
            var result = new StringBuilder(100);
            var resultTrailer = new StringBuilder(0);
            var byteSegments = new List<byte[]>(1);
            int mode = ASCII_ENCODE;
            do
            {
                if (mode == ASCII_ENCODE)
                {
                    mode = decodeAsciiSegment(bits, result, resultTrailer);
                }
                else
                {
                    switch (mode)
                    {
                        case C40_ENCODE:
                            decodeC40Segment(bits, result);
                            break;

                        case TEXT_ENCODE:
                            decodeTextSegment(bits, result);
                            break;

                        case ANSIX12_ENCODE:
                            decodeAnsiX12Segment(bits, result);
                            break;

                        case EDIFACT_ENCODE:
                            decodeEdifactSegment(bits, result);
                            break;

                        case BASE256_ENCODE:
                            decodeBase256Segment(bits, result, byteSegments);
                            break;

                        default:
                            throw ReaderException.Instance;
                    }
                    mode = ASCII_ENCODE;
                }
            } while (mode != PAD_ENCODE && bits.available() > 0);
            if (resultTrailer.Length > 0)
            {
                result.Append(resultTrailer.ToString());
            }
            return new DecoderResult(bytes, result.ToString(), (byteSegments.Count == 0) ? null : byteSegments, null);
        }
        private static void decodeKanjiSegment(BitSource bits, StringBuilder result, int count)
        {
            // Each character will require 2 bytes. Read the characters as 2-byte pairs
            // and decode as Shift_JIS afterwards
            var buffer = new sbyte[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] = (sbyte) (assembledTwoBytes >> 8);
                buffer[offset + 1] = (sbyte) assembledTwoBytes;
                offset += 2;
                count--;
            }
            // Shift_JIS may not be supported in some environments:
            try
            {

                byte[] byteArray = SupportClass.ToByteArray(buffer);
                result.Append(Encoding.GetEncoding(SHIFT_JIS).GetString(byteArray, 0, byteArray.Length));
            }
            catch (IOException)
            {
                throw ReaderException.Instance;
            }
        }
        /// <summary> See ISO 16022:2006, 5.2.9 and Annex B, B.2</summary>
        private static void decodeBase256Segment(BitSource bits, StringBuilder result, List<byte[]> byteSegments)
        {
            // Figure out how long the Base 256 Segment is.
            int d1 = bits.readBits(8);
            int count;
            if (d1 == 0)
            {
                // Read the remainder of the symbol
                count = bits.available()/8;
            }
            else if (d1 < 250)
            {
                count = d1;
            }
            else
            {
                count = 250*(d1 - 249) + bits.readBits(8);
            }
            var bytes = new sbyte[count];
            for (int i = 0; i < count; i++)
            {
                bytes[i] = unrandomize255State(bits.readBits(8), i);
            }
            byteSegments.Add(SupportClass.ToByteArray(bytes));
            try
            {
                var byteArray = SupportClass.ToByteArray(bytes);
                result.Append(Encoding.GetEncoding("ISO8859_1").GetString(byteArray, 0, byteArray.Length));
            }
            catch (IOException uee)
            {

                throw new SystemException("Platform does not support required encoding: " + uee);
            }
        }
        /// <summary> See ISO 16022:2006, 5.2.8 and Annex C Table C.3</summary>
        private static void decodeEdifactSegment(BitSource bits, StringBuilder result)
        {
            bool unlatch = false;
            do
            {
                // If there is only two or less bytes left then it will be encoded as ASCII
                if (bits.available() <= 16)
                {
                    return;
                }

                for (int i = 0; i < 4; i++)
                {
                    int edifactValue = bits.readBits(6);

                    // Check for the unlatch character
                    if (edifactValue == 0x2B67)
                    {
                        // 011111
                        unlatch = true;
                        // If we encounter the unlatch code then continue reading because the Codeword triple
                        // is padded with 0's
                    }

                    if (!unlatch)
                    {
                        if ((edifactValue & 32) == 0)
                        {
                            // no 1 in the leading (6th) bit
                            edifactValue |= 64; // Add a leading 01 to the 6 bit binary value
                        }
                        result.Append(edifactValue);
                    }
                }
            } while (!unlatch && bits.available() > 0);
        }
        /// <summary> See ISO 16022:2006, 5.2.7</summary>
        private static void decodeAnsiX12Segment(BitSource bits, StringBuilder result)
        {
            // Three ANSI X12 values are encoded in a 16-bit value as
            // (1600 * C1) + (40 * C2) + C3 + 1

            var cValues = new int[3];
            do
            {
                // If there is only one byte left then it will be encoded as ASCII
                if (bits.available() == 8)
                {
                    return;
                }
                int firstByte = bits.readBits(8);
                if (firstByte == 254)
                {
                    // Unlatch codeword
                    return;
                }

                parseTwoBytes(firstByte, bits.readBits(8), cValues);

                for (int i = 0; i < 3; i++)
                {
                    int cValue = cValues[i];
                    if (cValue == 0)
                    {
                        // X12 segment terminator <CR>
                        result.Append('\r');
                    }
                    else if (cValue == 1)
                    {
                        // X12 segment separator *
                        result.Append('*');
                    }
                    else if (cValue == 2)
                    {
                        // X12 sub-element separator >
                        result.Append('>');
                    }
                    else if (cValue == 3)
                    {
                        // space
                        result.Append(' ');
                    }
                    else if (cValue < 14)
                    {
                        // 0 - 9
                        result.Append((char) (cValue + 44));
                    }
                    else if (cValue < 40)
                    {
                        // A - Z
                        result.Append((char) (cValue + 51));
                    }
                    else
                    {
                        throw ReaderException.Instance;
                    }
                }
            } while (bits.available() > 0);
        }
        /// <summary> See ISO 16022:2006, 5.2.6 and Annex C, Table C.2</summary>
        private static void decodeTextSegment(BitSource bits, StringBuilder result)
        {
            // Three Text values are encoded in a 16-bit value as
            // (1600 * C1) + (40 * C2) + C3 + 1
            // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time
            bool upperShift = false;

            var cValues = new int[3];
            do
            {
                // If there is only one byte left then it will be encoded as ASCII
                if (bits.available() == 8)
                {
                    return;
                }
                int firstByte = bits.readBits(8);
                if (firstByte == 254)
                {
                    // Unlatch codeword
                    return;
                }

                parseTwoBytes(firstByte, bits.readBits(8), cValues);

                int shift = 0;
                for (int i = 0; i < 3; i++)
                {
                    int cValue = cValues[i];
                    switch (shift)
                    {
                        case 0:
                            if (cValue < 3)
                            {
                                shift = cValue + 1;
                            }
                            else
                            {
                                if (upperShift)
                                {
                                    result.Append((char) (TEXT_BASIC_SET_CHARS[cValue] + 128));
                                    upperShift = false;
                                }
                                else
                                {
                                    result.Append(TEXT_BASIC_SET_CHARS[cValue]);
                                }
                            }
                            break;

                        case 1:
                            if (upperShift)
                            {
                                result.Append((char) (cValue + 128));
                                upperShift = false;
                            }
                            else
                            {
                                result.Append(cValue);
                            }
                            shift = 0;
                            break;

                        case 2:
                            // Shift 2 for Text is the same encoding as C40
                            if (cValue < 27)
                            {
                                if (upperShift)
                                {
                                    result.Append((char) (C40_SHIFT2_SET_CHARS[cValue] + 128));
                                    upperShift = false;
                                }
                                else
                                {
                                    result.Append(C40_SHIFT2_SET_CHARS[cValue]);
                                }
                            }
                            else if (cValue == 27)
                            {
                                // FNC1
                                throw ReaderException.Instance;
                            }
                            else if (cValue == 30)
                            {
                                // Upper Shift
                                upperShift = true;
                            }
                            else
                            {
                                throw ReaderException.Instance;
                            }
                            shift = 0;
                            break;

                        case 3:
                            if (upperShift)
                            {
                                result.Append((char) (TEXT_SHIFT3_SET_CHARS[cValue] + 128));
                                upperShift = false;
                            }
                            else
                            {
                                result.Append(TEXT_SHIFT3_SET_CHARS[cValue]);
                            }
                            shift = 0;
                            break;

                        default:
                            throw ReaderException.Instance;
                    }
                }
            } while (bits.available() > 0);
        }
 /// <summary> See ISO 16022:2006, 5.2.3 and Annex C, Table C.2</summary>
 private static int decodeAsciiSegment(BitSource bits, StringBuilder result, StringBuilder resultTrailer)
 {
     bool upperShift = false;
     do
     {
         int oneByte = bits.readBits(8);
         if (oneByte == 0)
         {
             throw ReaderException.Instance;
         }
         else if (oneByte <= 128)
         {
             // ASCII data (ASCII value + 1)
             oneByte = upperShift ? (oneByte + 128) : oneByte;
             upperShift = false;
             result.Append((char) (oneByte - 1));
             return ASCII_ENCODE;
         }
         else if (oneByte == 129)
         {
             // Pad
             return PAD_ENCODE;
         }
         else if (oneByte <= 229)
         {
             // 2-digit data 00-99 (Numeric Value + 130)
             int value_Renamed = oneByte - 130;
             if (value_Renamed < 10)
             {
                 // padd with '0' for single digit values
                 result.Append('0');
             }
             result.Append(value_Renamed);
         }
         else if (oneByte == 230)
         {
             // Latch to C40 encodation
             return C40_ENCODE;
         }
         else if (oneByte == 231)
         {
             // Latch to Base 256 encodation
             return BASE256_ENCODE;
         }
         else if (oneByte == 232)
         {
             // FNC1
             //throw ReaderException.getInstance();
             // Ignore this symbol for now
         }
         else if (oneByte == 233)
         {
             // Structured Append
             //throw ReaderException.getInstance();
             // Ignore this symbol for now
         }
         else if (oneByte == 234)
         {
             // Reader Programming
             //throw ReaderException.getInstance();
             // Ignore this symbol for now
         }
         else if (oneByte == 235)
         {
             // Upper Shift (shift to Extended ASCII)
             upperShift = true;
         }
         else if (oneByte == 236)
         {
             // 05 Macro
             result.Append("[)>\u001E05\u001D");
             resultTrailer.Insert(0, "\u001E\u0004");
         }
         else if (oneByte == 237)
         {
             // 06 Macro
             result.Append("[)>\u001E06\u001D");
             resultTrailer.Insert(0, "\u001E\u0004");
         }
         else if (oneByte == 238)
         {
             // Latch to ANSI X12 encodation
             return ANSIX12_ENCODE;
         }
         else if (oneByte == 239)
         {
             // Latch to Text encodation
             return TEXT_ENCODE;
         }
         else if (oneByte == 240)
         {
             // Latch to EDIFACT encodation
             return EDIFACT_ENCODE;
         }
         else if (oneByte == 241)
         {
             // ECI Character
             // TODO(bbrown): I think we need to support ECI
             //throw ReaderException.getInstance();
             // Ignore this symbol for now
         }
         else if (oneByte >= 242)
         {
             // Not to be used in ASCII encodation
             throw ReaderException.Instance;
         }
     } while (bits.available() > 0);
     return ASCII_ENCODE;
 }