Add C# dotnet exact EXI codec implementation
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
302
csharp/dotnet/EXI/BitStreamExact.cs
Normal file
302
csharp/dotnet/EXI/BitStreamExact.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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>
|
||||
/// 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 of 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));
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write single bit - exact implementation
|
||||
/// </summary>
|
||||
public void WriteBit(int bit)
|
||||
{
|
||||
WriteBits(1, bit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write N-bit unsigned integer - exact implementation
|
||||
/// </summary>
|
||||
public void WriteNBitUnsignedInteger(int numBits, int val)
|
||||
{
|
||||
if (numBits > 0)
|
||||
WriteBits(numBits, val);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Flush remaining bits - exact implementation of flush()
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
174
csharp/dotnet/EXI/EXIHeaderExact.cs
Normal file
174
csharp/dotnet/EXI/EXIHeaderExact.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2024 C# Port
|
||||
* Original Copyright (C) 2007-2018 Siemens AG
|
||||
*
|
||||
* Exact EXI Header implementation - byte-compatible with OpenV2G
|
||||
* Matches EXIHeaderDecoder.c and EXIHeaderEncoder.c exactly
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace V2GDecoderNet.EXI
|
||||
{
|
||||
/// <summary>
|
||||
/// EXI Error codes - exact match to C implementation
|
||||
/// </summary>
|
||||
public static class EXIErrorCodesExact
|
||||
{
|
||||
public const int EXI_OK = 0;
|
||||
public const int EXI_ERROR_UNEXPECTED_END_OF_STREAM = -1;
|
||||
public const int EXI_UNSUPPORTED_HEADER_COOKIE = -2;
|
||||
public const int EXI_UNSUPPORTED_HEADER_OPTIONS = -3;
|
||||
public const int EXI_ERROR_UNKNOWN_EVENT = -4;
|
||||
public const int EXI_ERROR_OUT_OF_BYTE_BUFFER = -5;
|
||||
public const int EXI_ERROR_OUT_OF_BOUNDS = -6;
|
||||
public const int EXI_ERROR_STRINGVALUES_NOT_SUPPORTED = -7;
|
||||
public const int EXI_ERROR_NOT_IMPLEMENTED_YET = -8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Header decoder - exact implementation of EXIHeaderDecoder.c
|
||||
/// </summary>
|
||||
public static class EXIHeaderDecoderExact
|
||||
{
|
||||
/// <summary>
|
||||
/// Decode EXI header - exact implementation of decodeEXIHeader()
|
||||
/// </summary>
|
||||
public static int DecodeHeader(BitInputStreamExact stream, EXIHeaderExact header)
|
||||
{
|
||||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||
if (header == null) throw new ArgumentNullException(nameof(header));
|
||||
|
||||
// Read the header byte
|
||||
int headerByte = stream.ReadBits(8);
|
||||
if (headerByte < 0)
|
||||
return EXIErrorCodesExact.EXI_ERROR_UNEXPECTED_END_OF_STREAM;
|
||||
|
||||
byte header_b = (byte)headerByte;
|
||||
|
||||
// Check for EXI Cookie - not supported in this implementation
|
||||
if (header_b == 0x24) // '$' character
|
||||
{
|
||||
return EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_COOKIE;
|
||||
}
|
||||
|
||||
// Check presence bit for EXI Options (bit 5, value 0x20)
|
||||
if ((header_b & 0x20) != 0)
|
||||
{
|
||||
return EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_OPTIONS;
|
||||
}
|
||||
|
||||
// Parse simple header format (distinguishing bits = "1")
|
||||
// Bit pattern: 1 | Version[4] | Presence[1] | Format[2]
|
||||
|
||||
// Extract format version (bits 6-3, mask 0x1E, shift right 1)
|
||||
header.FormatVersion = (byte)((header_b & 0x1E) >> 1);
|
||||
|
||||
// Extract format field (bits 1-0, mask 0x03)
|
||||
byte format = (byte)(header_b & 0x03);
|
||||
|
||||
// Set preservation options based on format field
|
||||
switch (format)
|
||||
{
|
||||
case 0: // Format 00: No preservation
|
||||
header.PreserveComments = false;
|
||||
header.PreservePIs = false;
|
||||
header.PreserveDTD = false;
|
||||
header.PreservePrefixes = false;
|
||||
break;
|
||||
case 1: // Format 01: Preserve comments and PIs
|
||||
header.PreserveComments = true;
|
||||
header.PreservePIs = true;
|
||||
header.PreserveDTD = false;
|
||||
header.PreservePrefixes = false;
|
||||
break;
|
||||
case 2: // Format 10: Preserve DTD and prefixes
|
||||
header.PreserveComments = false;
|
||||
header.PreservePIs = false;
|
||||
header.PreserveDTD = true;
|
||||
header.PreservePrefixes = true;
|
||||
break;
|
||||
case 3: // Format 11: Preserve all
|
||||
header.PreserveComments = true;
|
||||
header.PreservePIs = true;
|
||||
header.PreserveDTD = true;
|
||||
header.PreservePrefixes = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Header always has no cookie in this implementation
|
||||
header.HasCookie = false;
|
||||
|
||||
return EXIErrorCodesExact.EXI_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Header encoder - exact implementation of EXIHeaderEncoder.c
|
||||
/// </summary>
|
||||
public static class EXIHeaderEncoderExact
|
||||
{
|
||||
/// <summary>
|
||||
/// Encode EXI header - exact implementation of encodeEXIHeader()
|
||||
/// Always writes simple header format (0x80 = 128)
|
||||
/// </summary>
|
||||
public static int EncodeHeader(BitOutputStreamExact stream, EXIHeaderExact header)
|
||||
{
|
||||
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||
if (header == null) throw new ArgumentNullException(nameof(header));
|
||||
|
||||
try
|
||||
{
|
||||
// Simple header format: always write 128 (0x80)
|
||||
// Bit pattern: 1 0000 0 00 = 10000000 = 0x80 = 128
|
||||
// - Distinguishing bit: 1
|
||||
// - Version: 0000 (format version 0)
|
||||
// - Presence bit: 0 (no options)
|
||||
// - Format: 00 (no preservation)
|
||||
stream.WriteBits(8, EXIConstantsExact.EXI_HEADER_SIMPLE);
|
||||
|
||||
return EXIErrorCodesExact.EXI_OK;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return EXIErrorCodesExact.EXI_ERROR_OUT_OF_BYTE_BUFFER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Exception for exact error handling
|
||||
/// </summary>
|
||||
public class EXIExceptionExact : Exception
|
||||
{
|
||||
public int ErrorCode { get; }
|
||||
|
||||
public EXIExceptionExact(int errorCode, string message) : base(message)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
public EXIExceptionExact(int errorCode, string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
public static string GetErrorMessage(int errorCode)
|
||||
{
|
||||
return errorCode switch
|
||||
{
|
||||
EXIErrorCodesExact.EXI_OK => "No error",
|
||||
EXIErrorCodesExact.EXI_ERROR_UNEXPECTED_END_OF_STREAM => "Unexpected end of stream",
|
||||
EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_COOKIE => "EXI header cookie not supported",
|
||||
EXIErrorCodesExact.EXI_UNSUPPORTED_HEADER_OPTIONS => "EXI header options not supported",
|
||||
EXIErrorCodesExact.EXI_ERROR_UNKNOWN_EVENT => "Unknown EXI event",
|
||||
EXIErrorCodesExact.EXI_ERROR_OUT_OF_BYTE_BUFFER => "Output buffer overflow",
|
||||
EXIErrorCodesExact.EXI_ERROR_OUT_OF_BOUNDS => "Index out of bounds",
|
||||
EXIErrorCodesExact.EXI_ERROR_STRINGVALUES_NOT_SUPPORTED => "String values not supported",
|
||||
EXIErrorCodesExact.EXI_ERROR_NOT_IMPLEMENTED_YET => "Feature not implemented",
|
||||
_ => $"Unknown error code: {errorCode}"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
203
csharp/dotnet/EXI/EXITypesExact.cs
Normal file
203
csharp/dotnet/EXI/EXITypesExact.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2024 C# Port
|
||||
* Original Copyright (C) 2007-2018 Siemens AG
|
||||
*
|
||||
* Exact EXI Types - Byte-compatible port of OpenV2G EXI implementation
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace V2GDecoderNet.EXI
|
||||
{
|
||||
/// <summary>
|
||||
/// Exact EXI constants matching OpenV2G C implementation
|
||||
/// </summary>
|
||||
public static class EXIConstantsExact
|
||||
{
|
||||
// Core EXI constants from EXITypes.h
|
||||
public const int BITS_IN_BYTE = 8;
|
||||
public const int EXI_ELEMENT_STACK_SIZE = 24;
|
||||
public const int UINT_MAX_VALUE = 65535;
|
||||
|
||||
// EXI Date-Time constants
|
||||
public const int DATETIME_YEAR_OFFSET = 2000;
|
||||
public const int DATETIME_NUMBER_BITS_MONTHDAY = 9;
|
||||
public const int DATETIME_NUMBER_BITS_TIME = 17;
|
||||
public const int DATETIME_NUMBER_BITS_TIMEZONE = 11;
|
||||
public const int DATETIME_MONTH_MULTIPLICATOR = 32;
|
||||
public const int DATETIME_TIMEZONE_OFFSET_IN_MINUTES = 896;
|
||||
|
||||
// EXI Float special values
|
||||
public const int FLOAT_EXPONENT_SPECIAL_VALUES = -16384;
|
||||
public const long FLOAT_MANTISSA_INFINITY = 1;
|
||||
public const long FLOAT_MANTISSA_MINUS_INFINITY = -1;
|
||||
public const long FLOAT_MANTISSA_NOT_A_NUMBER = 0;
|
||||
|
||||
// Buffer and stream configuration
|
||||
public const int BUFFER_SIZE = 4096;
|
||||
|
||||
// EXI Header byte - always 0x80 for simple headers
|
||||
public const byte EXI_HEADER_SIMPLE = 0x80;
|
||||
|
||||
// Stream type configuration
|
||||
public const int EXI_STREAM_BYTE_ARRAY = 0;
|
||||
public const int EXI_STREAM_FILE = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Events enumeration - exact match to C implementation
|
||||
/// </summary>
|
||||
public enum EXIEventExact
|
||||
{
|
||||
START_DOCUMENT = 0,
|
||||
END_DOCUMENT = 1,
|
||||
START_ELEMENT = 2,
|
||||
START_ELEMENT_NS = 3,
|
||||
START_ELEMENT_GENERIC = 4,
|
||||
START_ELEMENT_GENERIC_UNDECLARED = 5,
|
||||
END_ELEMENT = 6,
|
||||
END_ELEMENT_UNDECLARED = 7,
|
||||
CHARACTERS = 8,
|
||||
CHARACTERS_GENERIC = 9,
|
||||
ATTRIBUTE = 10,
|
||||
ATTRIBUTE_NS = 11,
|
||||
ATTRIBUTE_GENERIC = 12,
|
||||
ATTRIBUTE_GENERIC_UNDECLARED = 13,
|
||||
ATTRIBUTE_XSI_TYPE = 14,
|
||||
ATTRIBUTE_XSI_NIL = 15,
|
||||
SELF_CONTAINED = 16,
|
||||
ENTITY_REFERENCE = 17,
|
||||
COMMENT = 18,
|
||||
PROCESSING_INSTRUCTION = 19,
|
||||
DOCTYPE_DECLARATION = 20,
|
||||
NAMESPACE_DECLARATION = 21
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Integer types - exact match to C implementation
|
||||
/// </summary>
|
||||
public enum EXIIntegerTypeExact
|
||||
{
|
||||
UNSIGNED_INTEGER_8 = 0,
|
||||
UNSIGNED_INTEGER_16 = 1,
|
||||
UNSIGNED_INTEGER_32 = 2,
|
||||
UNSIGNED_INTEGER_64 = 3,
|
||||
INTEGER_8 = 4,
|
||||
INTEGER_16 = 5,
|
||||
INTEGER_32 = 6,
|
||||
INTEGER_64 = 7,
|
||||
UNSIGNED_INTEGER_BIG = 8
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Stream configuration - exact match to C bitstream_t
|
||||
/// </summary>
|
||||
public class BitstreamExact
|
||||
{
|
||||
// Core buffer state
|
||||
public byte[] Data { get; set; }
|
||||
public int Size { get; set; }
|
||||
public int Position { get; set; }
|
||||
|
||||
// Bit-level state - exact match to C implementation
|
||||
public byte Buffer { get; set; } // Current bit buffer
|
||||
public byte Capacity { get; set; } // Remaining bits in buffer
|
||||
|
||||
public BitstreamExact(byte[] data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
||||
Data = data;
|
||||
Size = data.Length;
|
||||
Position = 0;
|
||||
Buffer = 0;
|
||||
Capacity = 0; // 0 = empty for input, 8 = empty for output
|
||||
}
|
||||
|
||||
public BitstreamExact(int size)
|
||||
{
|
||||
Data = new byte[size];
|
||||
Size = size;
|
||||
Position = 0;
|
||||
Buffer = 0;
|
||||
Capacity = 8; // Output stream starts with empty buffer (8 available bits)
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Position = 0;
|
||||
Buffer = 0;
|
||||
Capacity = 0;
|
||||
}
|
||||
|
||||
public byte[] ToArray()
|
||||
{
|
||||
int resultSize = Position;
|
||||
if (Capacity < 8) resultSize++; // Include partial buffer
|
||||
|
||||
var result = new byte[resultSize];
|
||||
Array.Copy(Data, result, Position);
|
||||
|
||||
// Include partial buffer if any bits written
|
||||
if (Capacity < 8 && resultSize > Position)
|
||||
{
|
||||
result[Position] = (byte)(Buffer << Capacity);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Header structure - exact match to C exi_header_t
|
||||
/// </summary>
|
||||
public class EXIHeaderExact
|
||||
{
|
||||
public bool HasCookie { get; set; }
|
||||
public byte FormatVersion { get; set; }
|
||||
public bool PreserveComments { get; set; }
|
||||
public bool PreservePIs { get; set; }
|
||||
public bool PreserveDTD { get; set; }
|
||||
public bool PreservePrefixes { get; set; }
|
||||
|
||||
public EXIHeaderExact()
|
||||
{
|
||||
HasCookie = false;
|
||||
FormatVersion = 0;
|
||||
PreserveComments = false;
|
||||
PreservePIs = false;
|
||||
PreserveDTD = false;
|
||||
PreservePrefixes = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Document structure - matching C implementation
|
||||
/// </summary>
|
||||
public class EXIDocumentExact
|
||||
{
|
||||
public EXIHeaderExact Header { get; set; }
|
||||
public BitstreamExact Body { get; set; }
|
||||
|
||||
public EXIDocumentExact()
|
||||
{
|
||||
Header = new EXIHeaderExact();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Grammar state structure
|
||||
/// </summary>
|
||||
public class EXIGrammarState
|
||||
{
|
||||
public int GrammarID { get; set; }
|
||||
public int EventCode { get; set; }
|
||||
public int ElementStackSize { get; set; }
|
||||
|
||||
public EXIGrammarState()
|
||||
{
|
||||
GrammarID = 0;
|
||||
EventCode = 0;
|
||||
ElementStackSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user