/* * Copyright (C) 2007-2024 C# Port * Original Copyright (C) 2007-2018 Siemens AG * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. */ namespace V2GDecoderNet.EXI { /// /// Bit input stream for reading EXI encoded data /// public class BitInputStream { private readonly byte[] _buffer; private int _position; private int _bitPosition; private readonly int _size; public BitInputStream(byte[] buffer) { _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); _size = buffer.Length; _position = 0; _bitPosition = 0; } public int Position => _position; public int BitPosition => _bitPosition; public int Size => _size; public bool IsEOF => _position >= _size; /// /// Read a single bit /// /// Bit value (0 or 1), or -1 on EOF public int ReadBit() { if (_position >= _size) return -1; int bit = (_buffer[_position] >> (7 - _bitPosition)) & 1; _bitPosition++; if (_bitPosition == 8) { _bitPosition = 0; _position++; } return bit; } /// /// Read multiple bits as unsigned integer /// /// Number of bits to read (1-32) /// Unsigned integer value public uint ReadBits(int numBits) { if (numBits < 1 || numBits > 32) throw new ArgumentException("Number of bits must be between 1 and 32", nameof(numBits)); uint result = 0; for (int i = 0; i < numBits; i++) { int bit = ReadBit(); if (bit == -1) throw new EXIException(EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF); result = (result << 1) | (uint)bit; } return result; } /// /// Read unsigned integer using EXI encoding /// /// Unsigned integer value public uint ReadUnsignedInteger() { uint result = 0; bool continueBit; do { if (_position >= _size) throw new EXIException(EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF); byte currentByte = _buffer[_position++]; continueBit = (currentByte & 0x80) != 0; result = (result << 7) | (uint)(currentByte & 0x7F); } while (continueBit); return result; } /// /// Read signed integer using EXI encoding /// /// Signed integer value public int ReadInteger() { uint unsignedValue = ReadUnsignedInteger(); // Check sign bit (LSB) bool isNegative = (unsignedValue & 1) != 0; int value = (int)(unsignedValue >> 1); return isNegative ? -value : value; } /// /// Read a byte aligned to byte boundary /// /// Byte value public byte ReadByte() { // Align to byte boundary if (_bitPosition != 0) { _bitPosition = 0; _position++; } if (_position >= _size) throw new EXIException(EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF); return _buffer[_position++]; } /// /// Read multiple bytes /// /// Number of bytes to read /// Byte array public byte[] ReadBytes(int count) { if (count < 0) throw new ArgumentException("Count cannot be negative", nameof(count)); // Align to byte boundary if (_bitPosition != 0) { _bitPosition = 0; _position++; } if (_position + count > _size) throw new EXIException(EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF); var result = new byte[count]; Array.Copy(_buffer, _position, result, 0, count); _position += count; return result; } /// /// Skip to next byte boundary /// public void AlignToByteBank() { if (_bitPosition != 0) { _bitPosition = 0; _position++; } } /// /// Reset stream position to beginning /// public void Reset() { _position = 0; _bitPosition = 0; } /// /// Set stream position /// /// Byte position /// Bit position within byte (0-7) public void SetPosition(int position, int bitPosition = 0) { if (position < 0 || position > _size) throw new ArgumentException("Position out of range", nameof(position)); if (bitPosition < 0 || bitPosition > 7) throw new ArgumentException("Bit position must be 0-7", nameof(bitPosition)); _position = position; _bitPosition = bitPosition; } /// /// Get remaining bytes in stream /// /// Number of remaining bytes public int GetRemainingBytes() { int remaining = _size - _position; if (_bitPosition > 0 && remaining > 0) remaining--; return Math.Max(0, remaining); } } }