/* * 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; } public bool IsEndOfStream => _stream.Position >= _stream.Size && _stream.Capacity == 0; public int Position => _stream.Position; public int BitPosition => EXIConstantsExact.BITS_IN_BYTE - _stream.Capacity; } /// /// 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 of 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)); // Process bits in chunks that fit in current buffer while (numBits > 0) { // Calculate how many bits can fit in current buffer int bitsToWrite = Math.Min(numBits, _stream.Capacity); // Extract bits to write (from MSB side of value) int mask = (0xFF >> (EXIConstantsExact.BITS_IN_BYTE - bitsToWrite)); int bitsValue = (val >> (numBits - bitsToWrite)) & mask; // Pack bits into buffer (shift left and OR) _stream.Buffer = (byte)((_stream.Buffer << bitsToWrite) | bitsValue); _stream.Capacity -= (byte)bitsToWrite; // If buffer is full, write it to stream if (_stream.Capacity == 0) { if (_stream.Position >= _stream.Size) throw new InvalidOperationException("Output buffer overflow"); _stream.Data[_stream.Position++] = _stream.Buffer; _stream.Buffer = 0; _stream.Capacity = EXIConstantsExact.BITS_IN_BYTE; } numBits -= bitsToWrite; } } /// /// Write single bit - exact implementation /// public void WriteBit(int bit) { WriteBits(1, bit); } /// /// Write N-bit unsigned integer - exact implementation /// public void WriteNBitUnsignedInteger(int numBits, int val) { if (numBits > 0) WriteBits(numBits, val); } /// /// Write variable length unsigned integer - exact implementation of encodeUnsignedInteger() /// Uses 7-bit continuation encoding exactly like C implementation /// public void WriteUnsignedInteger(long val) { const int MASK_7_BITS = 0x7F; const int CONTINUATION_BIT = 0x80; if (val < 0) throw new ArgumentException("Value must be non-negative", nameof(val)); // 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); } /// /// Flush remaining bits - exact implementation of flush() /// public void Flush() { // If there are remaining bits in buffer, flush with zero padding if (_stream.Capacity < EXIConstantsExact.BITS_IN_BYTE) { // Shift remaining bits to MSB and write byte paddedBuffer = (byte)(_stream.Buffer << _stream.Capacity); if (_stream.Position >= _stream.Size) throw new InvalidOperationException("Output buffer overflow"); _stream.Data[_stream.Position++] = paddedBuffer; _stream.Buffer = 0; _stream.Capacity = EXIConstantsExact.BITS_IN_BYTE; } } public byte[] ToArray() { return _stream.ToArray(); } public int Position => _stream.Position; public int BitPosition => EXIConstantsExact.BITS_IN_BYTE - _stream.Capacity; } }