/* * Copyright (C) 2007-2024 C# Port * Original Copyright (C) 2007-2018 Siemens AG * * Exact BitStream implementation - byte-compatible with OpenV2G C implementation * Matches BitInputStream.c and BitOutputStream.c exactly */ using System; namespace V2GDecoderNet.EXI { /// /// Exact bit input stream implementation matching OpenV2G BitInputStream.c /// public class BitInputStreamExact { private readonly BitstreamExact _stream; public BitInputStreamExact(byte[] buffer) { _stream = new BitstreamExact(buffer); } public BitInputStreamExact(BitstreamExact stream) { _stream = stream ?? throw new ArgumentNullException(nameof(stream)); } /// /// Read specified number of bits - exact implementation of readBits() /// public int ReadBits(int numBits) { if (numBits < 1 || numBits > 32) throw new ArgumentException("Number of bits must be between 1 and 32", nameof(numBits)); int val = 0; while (numBits > 0) { // If buffer is empty, read next byte if (_stream.Capacity == 0) { if (_stream.Position >= _stream.Size) return -1; // End of stream _stream.Buffer = _stream.Data[_stream.Position++]; _stream.Capacity = EXIConstantsExact.BITS_IN_BYTE; } // Calculate how many bits to read from current buffer int bitsToRead = Math.Min(numBits, _stream.Capacity); // Extract bits from buffer (from MSB side) int mask = (0xFF >> (EXIConstantsExact.BITS_IN_BYTE - bitsToRead)); int bits = (_stream.Buffer >> (_stream.Capacity - bitsToRead)) & mask; // Add to result value val = (val << bitsToRead) | bits; // Update state _stream.Capacity -= (byte)bitsToRead; numBits -= bitsToRead; } return val; } /// /// Read single bit - exact implementation /// public int ReadBit() { return ReadBits(1); } /// /// Read N-bit unsigned integer - exact implementation of decodeNBitUnsignedInteger() /// public int ReadNBitUnsignedInteger(int numBits) { if (numBits == 0) return 0; return ReadBits(numBits); } /// /// Read variable length unsigned integer - exact implementation of decodeUnsignedInteger() /// Uses 7-bit continuation encoding exactly like C implementation /// public long ReadUnsignedInteger() { const int MASK_7_BITS = 0x7F; const int CONTINUATION_BIT = 0x80; byte[] maskedOctets = new byte[8]; // Max 8 bytes for 64-bit value int i = 0; byte b; // Read continuation bytes exactly like C implementation do { int byteVal = ReadBits(8); if (byteVal < 0) throw new InvalidOperationException("Unexpected end of stream"); b = (byte)byteVal; maskedOctets[i++] = (byte)(b & MASK_7_BITS); if (i >= maskedOctets.Length) throw new InvalidOperationException("Variable length integer too long"); } while ((b & CONTINUATION_BIT) != 0); // Assemble value from bytes (reverse order) - exact C algorithm long value = 0; for (int j = i - 1; j >= 0; j--) { value = (value << 7) | maskedOctets[j]; } return value; } /// /// Read variable length signed integer - exact implementation /// public long ReadInteger() { long magnitude = ReadUnsignedInteger(); // Check sign bit (LSB of magnitude) bool isNegative = (magnitude & 1) != 0; // Remove sign bit and adjust value long value = magnitude >> 1; return isNegative ? -(value + 1) : value; } /// /// Read 16-bit unsigned integer - exact implementation of decodeUnsignedInteger16() /// Uses VC2022 DecoderChannel.c algorithm exactly /// VC2022 function name: decodeUnsignedInteger16 /// public ushort ReadUnsignedInteger16() { // Console.Error.WriteLine($"🔬 [ReadUnsignedInteger16] Starting at pos={Position}, bit={BitPosition}"); uint mShift = 0; ushort result = 0; byte b; int iterCount = 0; do { // 1. Read the next octet (8 bits) b = (byte)ReadBits(8); // Console.Error.WriteLine($"🔬 [ReadUnsignedInteger16] Iter {iterCount}: read byte=0x{b:X2}, pos={Position}, bit={BitPosition}"); // 2. Multiply the value of the unsigned number represented by the 7 // least significant bits of the octet by the current multiplier and add the result to // the current value ushort addition = (ushort)((b & 127) << (int)mShift); result = (ushort)(result + addition); // Console.Error.WriteLine($"🔬 [ReadUnsignedInteger16] Iter {iterCount}: (b & 127)={b & 127}, mShift={mShift}, addition={addition}, result={result}"); // 3. Multiply the multiplier by 128 mShift += 7; // 4. If the most significant bit of the octet was 1, go back to step 1 bool continues = (b >> 7) == 1; // Console.Error.WriteLine($"🔬 [ReadUnsignedInteger16] Iter {iterCount}: MSB={(b >> 7)}, continues={continues}"); iterCount++; } while ((b >> 7) == 1); // Console.Error.WriteLine($"🔬 [ReadUnsignedInteger16] Final result={result}"); return result; } /// /// Read 16-bit signed integer using C decodeInteger16 algorithm /// First bit is sign bit: 0=positive, 1=negative /// For negative: -(magnitude + 1) /// public short ReadInteger16() { // Read sign bit (1 bit) bool isNegative = ReadBit() != 0; // Read unsigned magnitude uint magnitude = (uint)ReadUnsignedInteger(); if (isNegative) { return (short)(-(magnitude + 1)); } else { return (short)magnitude; } } public bool IsEndOfStream => _stream.Position >= _stream.Size && _stream.Capacity == 0; public int Position => _stream.Position; public int BitPosition => EXIConstantsExact.BITS_IN_BYTE - _stream.Capacity; /// /// Get remaining bytes from current position /// public byte[] GetRemainingBytes() { int remainingBits = _stream.Capacity; int currentBytePos = Position; if (remainingBits > 0) { // If there are remaining bits in current byte, we need to include it currentBytePos--; } int remainingByteCount = _stream.Size - currentBytePos; if (remainingByteCount <= 0) return new byte[0]; byte[] remaining = new byte[remainingByteCount]; Array.Copy(_stream.Data, currentBytePos, remaining, 0, remainingByteCount); return remaining; } } /// /// Exact bit output stream implementation matching OpenV2G BitOutputStream.c /// public class BitOutputStreamExact { private readonly BitstreamExact _stream; public BitOutputStreamExact(int capacity = EXIConstantsExact.BUFFER_SIZE) { _stream = new BitstreamExact(capacity); } public BitOutputStreamExact(BitstreamExact stream) { _stream = stream ?? throw new ArgumentNullException(nameof(stream)); } /// /// Write specified number of bits - EXACT implementation matching VC2022 writeBits() /// Based on BitOutputStream.c lines 40-108 - BYTE FOR BYTE IDENTICAL /// VC2022 function name: writeBits /// public void writeBits(int numBits, int val) { if (numBits < 1 || numBits > 32) throw new ArgumentException("Number of bits must be between 1 and 32", nameof(numBits)); // Console.Error.WriteLine($"🔬 [writeBits] ENTRY: pos={_stream.Position}, nbits={numBits}, val={val:X}, capacity={_stream.Capacity}, buffer=0x{_stream.Buffer:X2}"); // VC2022 line 43: if (nbits <= stream->capacity) if (numBits <= _stream.Capacity) { // Console.Error.WriteLine($"🔬 [writeBits] Using single-byte path (nbits <= capacity)"); // VC2022 line 45: stream->buffer = (uint8_t)(stream->buffer << (nbits)) | (uint8_t)(val & (uint32_t)(0xff >> (uint32_t)(BITS_IN_BYTE - nbits))); uint mask = (uint)(0xFF >> (EXIConstantsExact.BITS_IN_BYTE - numBits)); // Console.Error.WriteLine($"🔬 [writeBits] mask=0x{mask:X2}"); if (_stream.Position >= 28 && _stream.Position <= 35 && _stream.Capacity == 1 && numBits == 1) { Console.Error.WriteLine($"🔍 [writeBits] LAST BIT: pos={_stream.Position}, cap={_stream.Capacity}, buf=0x{_stream.Buffer:X2}, val={val}, writing to LSB"); } _stream.Buffer = (byte)((_stream.Buffer << numBits) | (val & mask)); // Console.Error.WriteLine($"🔬 [writeBits] new buffer=0x{_stream.Buffer:X2}"); // VC2022 line 46: stream->capacity = (uint8_t)(stream->capacity - nbits); _stream.Capacity = (byte)(_stream.Capacity - numBits); // Console.Error.WriteLine($"🔬 [writeBits] new capacity={_stream.Capacity}"); // VC2022 line 48: if (stream->capacity == 0) if (_stream.Capacity == 0) { // Console.Error.WriteLine($"🔬 [writeBits] Flushing buffer 0x{_stream.Buffer:X2} to position {_stream.Position}"); // VC2022 line 53: stream->data[(*stream->pos)++] = stream->buffer; if (_stream.Position >= _stream.Size) throw new InvalidOperationException("Output buffer overflow"); _stream.Data[_stream.Position++] = _stream.Buffer; // VC2022 line 61-62: stream->capacity = BITS_IN_BYTE; stream->buffer = 0; _stream.Capacity = EXIConstantsExact.BITS_IN_BYTE; _stream.Buffer = 0; } } else { // VC2022 line 67-68: stream->buffer = (uint8_t)(stream->buffer << stream->capacity) | ( (uint8_t)(val >> (nbits - stream->capacity)) & (uint8_t)(0xff >> (BITS_IN_BYTE - stream->capacity)) ); if (_stream.Position >= 28 && _stream.Position <= 35) { Console.Error.WriteLine($"🔍 [writeBits] BOUNDARY: pos={_stream.Position}, cap={_stream.Capacity}, buf=0x{_stream.Buffer:X2}, val={val}, nbits={numBits}"); Console.Error.WriteLine($"🔍 [writeBits] shift_amount={numBits - _stream.Capacity}, val_shifted={(byte)(val >> (numBits - _stream.Capacity))}"); } _stream.Buffer = (byte)((_stream.Buffer << _stream.Capacity) | (((byte)(val >> (numBits - _stream.Capacity))) & (byte)(0xFF >> (EXIConstantsExact.BITS_IN_BYTE - _stream.Capacity)))); // VC2022 line 70: nbits = (nbits - stream->capacity); numBits = numBits - _stream.Capacity; // VC2022 line 75: stream->data[(*stream->pos)++] = stream->buffer; if (_stream.Position >= _stream.Size) throw new InvalidOperationException("Output buffer overflow"); if (_stream.Position >= 28 && _stream.Position <= 35) Console.Error.WriteLine($"🔍 [writeBits] Writing byte 0x{_stream.Buffer:X2} to position {_stream.Position}"); _stream.Data[_stream.Position++] = _stream.Buffer; // VC2022 line 83: stream->buffer = 0; _stream.Buffer = 0; // VC2022 line 86-92: while (errn == 0 && nbits >= BITS_IN_BYTE) while (numBits >= EXIConstantsExact.BITS_IN_BYTE) { // VC2022 line 87: nbits = (nbits - BITS_IN_BYTE); numBits = numBits - EXIConstantsExact.BITS_IN_BYTE; // VC2022 line 92: stream->data[(*stream->pos)++] = (uint8_t)(val >> (nbits)); if (_stream.Position >= _stream.Size) throw new InvalidOperationException("Output buffer overflow"); _stream.Data[_stream.Position++] = (byte)(val >> numBits); } // VC2022 line 103-104: stream->buffer = (uint8_t)val; stream->capacity = (uint8_t)(BITS_IN_BYTE - (nbits)); _stream.Buffer = (byte)val; // Note: the high bits will be shifted out during further filling _stream.Capacity = (byte)(EXIConstantsExact.BITS_IN_BYTE - numBits); } } /// /// Write single bit - exact implementation /// public void WriteBit(int bit) { if (Position >= 28 && Position <= 35) Console.Error.WriteLine($"🔍 [WriteBit] pos={Position}:{BitPosition}, bit={bit}"); writeBits(1, bit); } /// /// Compatibility wrapper - keep C# naming for internal use /// public void WriteBits(int numBits, int val) { if (Position >= 28 && Position <= 45) Console.Error.WriteLine($"🔍 [WriteBits] pos={Position}, writing {numBits} bits, val={val:X}"); writeBits(numBits, val); if (Position >= 28 && Position <= 45) Console.Error.WriteLine($"🔍 [WriteBits] pos after={Position}"); } /// /// Write N-bit unsigned integer - exact implementation of encodeNBitUnsignedInteger() /// VC2022 function name: encodeNBitUnsignedInteger /// public void encodeNBitUnsignedInteger(int numBits, int val) { if (numBits > 0) { if (Position >= 28 && Position <= 35) Console.Error.WriteLine($"🔍 [encodeNBit] pos={Position}:{BitPosition}, writing {numBits} bits, val={val}"); writeBits(numBits, val); // Console.Error.WriteLine($"🔬 [encodeNBit] After write pos_after={Position}, buf=0x{BufferState:X2}, cap={CapacityState}"); } } /// /// Compatibility wrapper - keep C# naming for internal use /// /// /// Legacy C# style alias for backward compatibility /// public void WriteNBitUnsignedInteger(int numBits, int val) => encodeNBitUnsignedInteger(numBits, val); /// /// Compatibility wrapper - keep C# naming for internal use /// public void WriteUnsignedInteger16(ushort val) => encodeUnsignedInteger16(val); /// /// Helper method - exact implementation of numberOf7BitBlocksToRepresent() /// private byte NumberOf7BitBlocksToRepresent(ushort n) { if (n < 128) return 1; if (n < 16384) return 2; // 128 * 128 = 16384 return 3; } /// /// Number of 7-bit blocks needed to represent a value - exact VC2022 algorithm /// private static byte NumberOf7BitBlocksToRepresent(uint n) { /* 7 bits */ if (n < 128) { return 1; } /* 14 bits */ else if (n < 16384) { return 2; } /* 21 bits */ else if (n < 2097152) { return 3; } /* 28 bits */ else if (n < 268435456) { return 4; } /* 35 bits */ else { /* int, 32 bits */ return 5; } } /// /// Encode unsigned integer using VC2022 encodeUnsignedInteger32 exact algorithm /// public void encodeUnsignedInteger32(uint n) { if (n < 128) { // Write byte as is WriteBits(8, (byte)n); } else { byte n7BitBlocks = NumberOf7BitBlocksToRepresent(n); switch (n7BitBlocks) { case 5: WriteBits(8, (byte)(128 | n)); n = n >> 7; goto case 4; case 4: WriteBits(8, (byte)(128 | n)); n = n >> 7; goto case 3; case 3: WriteBits(8, (byte)(128 | n)); n = n >> 7; goto case 2; case 2: WriteBits(8, (byte)(128 | n)); n = n >> 7; goto case 1; case 1: // 0 .. 7 (last byte) WriteBits(8, (byte)(0 | n)); break; } } } /// /// Encode unsigned integer using VC2022 encodeUnsignedInteger16 exact algorithm /// public void encodeUnsignedInteger16(ushort n) { // if (n == 471) Console.Error.WriteLine($"🔍 [encodeUnsignedInteger16] Encoding 471, pos={Position}"); if (n < 128) { // Write byte as is // if (n == 471) Console.Error.WriteLine($"🔍 [encodeUnsignedInteger16] 471 < 128, writing {n}"); WriteBits(8, (byte)n); } else { byte n7BitBlocks = NumberOf7BitBlocksToRepresent(n); // if (n == 471) Console.Error.WriteLine($"🔍 [encodeUnsignedInteger16] 471 >= 128, n7BitBlocks={n7BitBlocks}"); switch (n7BitBlocks) { case 3: // if (n == 471) Console.Error.WriteLine($"🔍 [encodeUnsignedInteger16] case 3: writing {(byte)(128 | n)} = {128 | n}"); WriteBits(8, (byte)(128 | n)); n = (ushort)(n >> 7); goto case 2; case 2: // if (n == 471) Console.Error.WriteLine($"🔍 [encodeUnsignedInteger16] case 2: writing {(byte)(128 | n)} = {128 | n}"); WriteBits(8, (byte)(128 | n)); n = (ushort)(n >> 7); // if (n == 3) Console.Error.WriteLine($"🔍 [encodeUnsignedInteger16] after >>7, n=3, going to case 1"); goto case 1; case 1: // 0 .. 7 (last byte) // if (n == 3) Console.Error.WriteLine($"🔍 [encodeUnsignedInteger16] case 1: writing final {(byte)(0 | n)} = {0 | n}"); WriteBits(8, (byte)(0 | n)); break; } } } /// /// Write variable length unsigned integer - exact implementation of encodeUnsignedInteger() /// Uses 7-bit continuation encoding exactly like C implementation /// public void WriteUnsignedInteger(long val) { if (val < 0) throw new ArgumentException("Value must be non-negative", nameof(val)); // Use VC2022 exact algorithm for 32-bit values if (val <= uint.MaxValue) { encodeUnsignedInteger32((uint)val); return; } const int MASK_7_BITS = 0x7F; const int CONTINUATION_BIT = 0x80; // Handle zero as special case if (val == 0) { WriteBits(8, 0); return; } // Split into 7-bit chunks with continuation bits - exact C algorithm byte[] bytes = new byte[10]; // Max bytes needed for 64-bit value int numBytes = 0; while (val > 0) { byte chunk = (byte)(val & MASK_7_BITS); val >>= 7; // Set continuation bit if more bytes follow if (val > 0) chunk |= CONTINUATION_BIT; bytes[numBytes++] = chunk; } // Write bytes in forward order for (int i = 0; i < numBytes; i++) { WriteBits(8, bytes[i]); } } /// /// Write variable length signed integer - exact implementation /// public void WriteInteger(long val) { // Encode sign in LSB and magnitude in remaining bits bool isNegative = val < 0; long magnitude = isNegative ? (-val - 1) : val; // Shift magnitude left and set sign bit long encodedValue = (magnitude << 1) | (isNegative ? 1 : 0); WriteUnsignedInteger(encodedValue); } /// /// Write 16-bit signed integer using VC2022 encodeInteger16 algorithm /// First bit is sign bit: 0=positive, 1=negative /// For negative: -(magnitude + 1) /// Exactly matches VC2022's encodeInteger16() implementation /// public void WriteInteger16(short val) { Console.Error.WriteLine($"🔢 [WriteInteger16] Input: {val}"); // Write sign bit (1 bit) bool isNegative = val < 0; WriteBit(isNegative ? 1 : 0); Console.Error.WriteLine($"🔢 [WriteInteger16] Sign bit: {(isNegative ? 1 : 0)} (negative: {isNegative})"); // Calculate unsigned magnitude uint magnitude; if (isNegative) { // For negative: magnitude = (-val) - 1 magnitude = (uint)((-val) - 1); } else { // For positive: magnitude = val magnitude = (uint)val; } Console.Error.WriteLine($"🔢 [WriteInteger16] Magnitude: {magnitude}"); // Write unsigned magnitude using VC2022's encodeUnsignedInteger16 encodeUnsignedInteger16((ushort)magnitude); } /// /// Flush remaining bits - exact implementation of VC2022 flush() /// VC2022: if (stream->capacity == BITS_IN_BYTE) { /* nothing */ } else { writeBits(stream, stream->capacity, 0); } /// public void Flush() { // Console.Error.WriteLine($"🔍 [Flush] capacity={_stream.Capacity}, BITS_IN_BYTE={EXIConstantsExact.BITS_IN_BYTE}"); // VC2022 exact implementation if (_stream.Capacity == EXIConstantsExact.BITS_IN_BYTE) { // nothing to do, no bits in buffer // Console.Error.WriteLine($"🔍 [Flush] nothing to do"); } else { // errn = writeBits(stream, stream->capacity, 0); // Console.Error.WriteLine($"🔍 [Flush] calling writeBits({_stream.Capacity}, 0)"); writeBits(_stream.Capacity, 0); } } /// /// Reset buffer state - exact match to VC2022 writeEXIHeader initialization /// stream->buffer = 0; stream->capacity = 8; /// public void ResetBuffer() { _stream.Buffer = 0; _stream.Capacity = 8; } public byte[] ToArray() { // VC2022 equivalent: encodeFinish() calls flush() Flush(); return _stream.ToArray(); } public int Position => _stream.Position; public int BitPosition => EXIConstantsExact.BITS_IN_BYTE - _stream.Capacity; public byte BufferState => _stream.Buffer; public byte CapacityState => _stream.Capacity; } }