 c6dc6735fa
			
		
	
	c6dc6735fa
	
	
	
		
			
			- Reorganize project structure: Port/ → DotNet/, VC/, C++/ - Add comprehensive cross-platform build automation - Windows: build_all.bat, build.bat files for all components - Linux/macOS: build_all.sh, build.sh files for all components - Update all build scripts with correct folder paths - Create test automation scripts (test_all.bat/sh) - Update documentation to reflect new structure - Maintain 100% roundtrip accuracy for test5.exi (pure EXI) - Support both Windows MSBuild and Linux GCC compilation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			643 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			643 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| /*
 | |
|  * 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
 | |
| {
 | |
|     /// <summary>
 | |
|     /// Exact bit input stream implementation matching OpenV2G BitInputStream.c
 | |
|     /// </summary>
 | |
|     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));
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Read specified number of bits - exact implementation of readBits()
 | |
|         /// </summary>
 | |
|         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;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Read single bit - exact implementation
 | |
|         /// </summary>
 | |
|         public int ReadBit()
 | |
|         {
 | |
|             return ReadBits(1);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Read N-bit unsigned integer - exact implementation of decodeNBitUnsignedInteger()
 | |
|         /// </summary>
 | |
|         public int ReadNBitUnsignedInteger(int numBits)
 | |
|         {
 | |
|             if (numBits == 0) return 0;
 | |
|             return ReadBits(numBits);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Read variable length unsigned integer - exact implementation of decodeUnsignedInteger()
 | |
|         /// Uses 7-bit continuation encoding exactly like C implementation
 | |
|         /// </summary>
 | |
|         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;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Read variable length signed integer - exact implementation
 | |
|         /// </summary>
 | |
|         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;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Read 16-bit unsigned integer - exact implementation of decodeUnsignedInteger16()
 | |
|         /// Uses VC2022 DecoderChannel.c algorithm exactly
 | |
|         /// VC2022 function name: decodeUnsignedInteger16
 | |
|         /// </summary>
 | |
|         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;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Read 16-bit signed integer using C decodeInteger16 algorithm
 | |
|         /// First bit is sign bit: 0=positive, 1=negative
 | |
|         /// For negative: -(magnitude + 1)
 | |
|         /// </summary>
 | |
|         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;
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Get remaining bytes from current position
 | |
|         /// </summary>
 | |
|         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;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// <summary>
 | |
|     /// Exact bit output stream implementation matching OpenV2G BitOutputStream.c
 | |
|     /// </summary>
 | |
|     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));
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 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
 | |
|         /// </summary>
 | |
|         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);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Write single bit - exact implementation
 | |
|         /// </summary>
 | |
|         public void WriteBit(int bit)
 | |
|         {
 | |
|             if (Position >= 28 && Position <= 35)
 | |
|                 Console.Error.WriteLine($"🔍 [WriteBit] pos={Position}:{BitPosition}, bit={bit}");
 | |
|             writeBits(1, bit);
 | |
|         }
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Compatibility wrapper - keep C# naming for internal use
 | |
|         /// </summary>
 | |
|         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}");
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Write N-bit unsigned integer - exact implementation of encodeNBitUnsignedInteger()
 | |
|         /// VC2022 function name: encodeNBitUnsignedInteger
 | |
|         /// </summary>
 | |
|         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}");
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Compatibility wrapper - keep C# naming for internal use
 | |
|         /// </summary>
 | |
|         /// <summary>
 | |
|         /// Legacy C# style alias for backward compatibility
 | |
|         /// </summary>
 | |
|         public void WriteNBitUnsignedInteger(int numBits, int val) => encodeNBitUnsignedInteger(numBits, val);
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Compatibility wrapper - keep C# naming for internal use
 | |
|         /// </summary>
 | |
|         public void WriteUnsignedInteger16(ushort val) => encodeUnsignedInteger16(val);
 | |
|         
 | |
|         /// <summary>
 | |
|         /// Helper method - exact implementation of numberOf7BitBlocksToRepresent()
 | |
|         /// </summary>
 | |
|         private byte NumberOf7BitBlocksToRepresent(ushort n)
 | |
|         {
 | |
|             if (n < 128) return 1;
 | |
|             if (n < 16384) return 2; // 128 * 128 = 16384
 | |
|             return 3;
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Number of 7-bit blocks needed to represent a value - exact VC2022 algorithm
 | |
|         /// </summary>
 | |
|         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;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Encode unsigned integer using VC2022 encodeUnsignedInteger32 exact algorithm
 | |
|         /// </summary>
 | |
|         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;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Encode unsigned integer using VC2022 encodeUnsignedInteger16 exact algorithm
 | |
|         /// </summary>
 | |
|         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;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Write variable length unsigned integer - exact implementation of encodeUnsignedInteger()
 | |
|         /// Uses 7-bit continuation encoding exactly like C implementation
 | |
|         /// </summary>
 | |
|         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]);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Write variable length signed integer - exact implementation
 | |
|         /// </summary>
 | |
|         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);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// 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
 | |
|         /// </summary>
 | |
|         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);
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Flush remaining bits - exact implementation of VC2022 flush()
 | |
|         /// VC2022: if (stream->capacity == BITS_IN_BYTE) { /* nothing */ } else { writeBits(stream, stream->capacity, 0); }
 | |
|         /// </summary>
 | |
|         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);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /// <summary>
 | |
|         /// Reset buffer state - exact match to VC2022 writeEXIHeader initialization
 | |
|         /// stream->buffer = 0; stream->capacity = 8;
 | |
|         /// </summary>
 | |
|         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;
 | |
|     }
 | |
| } |