/**
* Thrift 호환 전용 별도 구현. Apache Thrift TBinaryProtocol과 동일한 API·와이어 포맷.
* DpProtocolLibrary 모듈화.
*/
using System;
using System.IO;
using System.Text;
namespace DeukPack.Protocol
{
///
/// Thrift 호환 전용 별도 구현. Apache Thrift TBinaryProtocol과 동일한 API·와이어 포맷 제공.
/// DpBinaryProtocol과 독립된 코드 경로로, Protobuf 계열 최적화 없이 Thrift 동작만 보장.
///
public sealed class TBinaryProtocol : DpProtocol, IDisposable
{
private readonly Stream _stream;
private readonly bool _bigEndian;
private const int ReadBufferSize = 4096;
private const int MaxBinaryLength = 1024 * 1024 * 10; // 10MB
private byte[] _readBuffer;
private int _readPosition;
private int _readLength;
private readonly byte[] _smallBuffer = new byte[8];
public TBinaryProtocol(Stream stream, bool bigEndian = true, bool strictRead = true, bool strictWrite = true)
{
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
_bigEndian = bigEndian;
_readBuffer = new byte[ReadBufferSize];
}
public void Dispose() { }
public void Flush() => _stream?.Flush();
public void WriteStructBegin(DpRecord structBegin) { }
public void WriteStructEnd() { }
public void WriteFieldBegin(DpColumn field) { WriteByte((byte)field.Type); WriteI16(field.ID); }
public void WriteFieldEnd() { }
public void WriteFieldStop() { WriteByte((byte)DpWireType.Stop); }
public void WriteBool(bool b) { WriteByte(b ? (byte)1 : (byte)0); }
public void WriteByte(byte b) { _stream.WriteByte(b); }
public void WriteI16(short i16)
{
if (_bigEndian) { _smallBuffer[0] = (byte)((i16 >> 8) & 0xFF); _smallBuffer[1] = (byte)(i16 & 0xFF); }
else { _smallBuffer[0] = (byte)(i16 & 0xFF); _smallBuffer[1] = (byte)((i16 >> 8) & 0xFF); }
_stream.Write(_smallBuffer, 0, 2);
}
public void WriteI32(int i32)
{
if (_bigEndian)
{
_smallBuffer[0] = (byte)((i32 >> 24) & 0xFF);
_smallBuffer[1] = (byte)((i32 >> 16) & 0xFF);
_smallBuffer[2] = (byte)((i32 >> 8) & 0xFF);
_smallBuffer[3] = (byte)(i32 & 0xFF);
}
else
{
_smallBuffer[0] = (byte)(i32 & 0xFF);
_smallBuffer[1] = (byte)((i32 >> 8) & 0xFF);
_smallBuffer[2] = (byte)((i32 >> 16) & 0xFF);
_smallBuffer[3] = (byte)((i32 >> 24) & 0xFF);
}
_stream.Write(_smallBuffer, 0, 4);
}
public void WriteI64(long i64)
{
if (_bigEndian)
{
_smallBuffer[0] = (byte)((i64 >> 56) & 0xFF);
_smallBuffer[1] = (byte)((i64 >> 48) & 0xFF);
_smallBuffer[2] = (byte)((i64 >> 40) & 0xFF);
_smallBuffer[3] = (byte)((i64 >> 32) & 0xFF);
_smallBuffer[4] = (byte)((i64 >> 24) & 0xFF);
_smallBuffer[5] = (byte)((i64 >> 16) & 0xFF);
_smallBuffer[6] = (byte)((i64 >> 8) & 0xFF);
_smallBuffer[7] = (byte)(i64 & 0xFF);
}
else
{
_smallBuffer[0] = (byte)(i64 & 0xFF);
_smallBuffer[1] = (byte)((i64 >> 8) & 0xFF);
_smallBuffer[2] = (byte)((i64 >> 16) & 0xFF);
_smallBuffer[3] = (byte)((i64 >> 24) & 0xFF);
_smallBuffer[4] = (byte)((i64 >> 32) & 0xFF);
_smallBuffer[5] = (byte)((i64 >> 40) & 0xFF);
_smallBuffer[6] = (byte)((i64 >> 48) & 0xFF);
_smallBuffer[7] = (byte)((i64 >> 56) & 0xFF);
}
_stream.Write(_smallBuffer, 0, 8);
}
public void WriteDouble(double d) { WriteI64(BitConverter.DoubleToInt64Bits(d)); }
public void WriteString(string? s)
{
if (s == null) { WriteI32(0); return; }
byte[] bytes = Encoding.UTF8.GetBytes(s);
WriteI32(bytes.Length);
if (bytes.Length > 0) _stream.Write(bytes, 0, bytes.Length);
}
public void WriteBinary(byte[]? b)
{
if (b == null) { WriteI32(0); return; }
WriteI32(b.Length);
if (b.Length > 0) _stream.Write(b, 0, b.Length);
}
public void WriteListBegin(DpList list) { WriteByte((byte)list.ElementType); WriteI32(list.Count); }
public void WriteListEnd() { }
public void WriteSetBegin(DpSet set) { WriteByte((byte)set.ElementType); WriteI32(set.Count); }
public void WriteSetEnd() { }
public void WriteMapBegin(DpDict map) { WriteByte((byte)map.KeyType); WriteByte((byte)map.ValueType); WriteI32(map.Count); }
public void WriteMapEnd() { }
public DpRecord ReadStructBegin() { return new DpRecord(); }
public void ReadStructEnd() { }
public DpColumn ReadFieldBegin()
{
byte type = ReadByte();
if (type == (byte)DpWireType.Stop) return new DpColumn("", DpWireType.Stop, 0);
short id = ReadI16();
return new DpColumn("", (DpWireType)type, id);
}
public void ReadFieldEnd() { }
public bool ReadBool() { return ReadByte() != 0; }
private void EnsureReadBuffer(int bytesNeeded)
{
if (_readPosition + bytesNeeded <= _readLength) return;
int remaining = _readLength - _readPosition;
if (bytesNeeded > _readBuffer.Length)
{
byte[] newBuf = new byte[bytesNeeded + ReadBufferSize];
if (remaining > 0) Array.Copy(_readBuffer, _readPosition, newBuf, 0, remaining);
_readBuffer = newBuf;
_readPosition = 0;
_readLength = remaining;
}
else if (remaining > 0)
{
Array.Copy(_readBuffer, _readPosition, _readBuffer, 0, remaining);
_readPosition = 0;
_readLength = remaining;
}
else { _readPosition = 0; _readLength = 0; }
while (_readLength < bytesNeeded)
{
int n = _stream.Read(_readBuffer, _readLength, _readBuffer.Length - _readLength);
if (n == 0) throw new EndOfStreamException();
_readLength += n;
}
}
public byte ReadByte() { EnsureReadBuffer(1); return _readBuffer[_readPosition++]; }
public short ReadI16()
{
EnsureReadBuffer(2);
short v = _bigEndian
? (short)((_readBuffer[_readPosition] << 8) | _readBuffer[_readPosition + 1])
: (short)(_readBuffer[_readPosition] | (_readBuffer[_readPosition + 1] << 8));
_readPosition += 2;
return v;
}
public int ReadI32()
{
EnsureReadBuffer(4);
int b0 = _readBuffer[_readPosition], b1 = _readBuffer[_readPosition + 1], b2 = _readBuffer[_readPosition + 2], b3 = _readBuffer[_readPosition + 3];
_readPosition += 4;
return _bigEndian ? (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 : (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
}
public long ReadI64()
{
EnsureReadBuffer(8);
long b0 = _readBuffer[_readPosition], b1 = _readBuffer[_readPosition + 1], b2 = _readBuffer[_readPosition + 2], b3 = _readBuffer[_readPosition + 3],
b4 = _readBuffer[_readPosition + 4], b5 = _readBuffer[_readPosition + 5], b6 = _readBuffer[_readPosition + 6], b7 = _readBuffer[_readPosition + 7];
_readPosition += 8;
return _bigEndian
? ((long)b0 << 56) | ((long)b1 << 48) | ((long)b2 << 40) | ((long)b3 << 32) | ((long)b4 << 24) | ((long)b5 << 16) | ((long)b6 << 8) | b7
: ((long)b7 << 56) | ((long)b6 << 48) | ((long)b5 << 40) | ((long)b4 << 32) | ((long)b3 << 24) | ((long)b2 << 16) | ((long)b1 << 8) | b0;
}
public double ReadDouble() { return BitConverter.Int64BitsToDouble(ReadI64()); }
public string ReadString()
{
int length = ReadI32();
if (length == 0) return string.Empty;
if (length < 0 || length > MaxBinaryLength) throw new InvalidOperationException($"Invalid string length: {length}");
EnsureReadBuffer(length);
string result = Encoding.UTF8.GetString(_readBuffer, _readPosition, length);
_readPosition += length;
return result;
}
public byte[] ReadBinary()
{
int length = ReadI32();
if (length == 0) return Array.Empty();
if (length < 0 || length > MaxBinaryLength) throw new InvalidOperationException($"Invalid binary length: {length}");
EnsureReadBuffer(length);
byte[] result = new byte[length];
Array.Copy(_readBuffer, _readPosition, result, 0, length);
_readPosition += length;
return result;
}
public DpList ReadListBegin() { byte elementType = ReadByte(); int count = ReadI32(); return new DpList { ElementType = (DpWireType)elementType, Count = count }; }
public void ReadListEnd() { }
public DpSet ReadSetBegin() { byte elementType = ReadByte(); int count = ReadI32(); return new DpSet { ElementType = (DpWireType)elementType, Count = count }; }
public void ReadSetEnd() { }
public DpDict ReadMapBegin() { byte keyType = ReadByte(); byte valueType = ReadByte(); int count = ReadI32(); return new DpDict { KeyType = (DpWireType)keyType, ValueType = (DpWireType)valueType, Count = count }; }
public void ReadMapEnd() { }
}
}