using CSNetwork.Protocols;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
// using System.Reflection.PortableExecutable;
using System.Text;
using static CSNetwork.Protocols.selectrole;
namespace CSNetwork
{
public class OctetsStream : IDisposable
{
private Octets _octets;
private int _position;
private static readonly Encoding GbkEncoding = Encoding.GetEncoding(936);
private const int MaxSpare = 16384;
private int _transactionPosition;
public enum Transaction
{
Begin,
Commit,
Rollback
}
public int Length => _octets.Length;
// Position within the stream for reading/writing
public int Position
{
get => _position;
set
{
// Basic bounds check, could be more robust
if (value < 0 || value > _octets.Length)
throw new ArgumentOutOfRangeException(nameof(value), "Position is outside the bounds of the stream.");
_position = value;
}
}
// Access to the underlying Octets object
public Octets UnderlyingOctets => _octets;
// Access to the raw buffer for in-place operations (like ISecurity.Update)
public byte[] RawBuffer => _octets.RawBuffer;
public bool IsEof => _position >= _octets.Length;
public OctetsStream(Octets octets = null)
{
_octets = octets ?? new Octets();
_position = 0;
}
// Helper methods for big-endian conversion
private byte[] GetBigEndianBytes(ushort value)
{
if (BitConverter.IsLittleEndian)
{
return new[] { (byte)(value >> 8), (byte)value };
}
return BitConverter.GetBytes(value);
}
private byte[] GetBigEndianBytes(uint value)
{
if (BitConverter.IsLittleEndian)
{
return new[]
{
(byte)(value >> 24),
(byte)(value >> 16),
(byte)(value >> 8),
(byte)value
};
}
return BitConverter.GetBytes(value);
}
// Primitive type support
public void Write(byte value) => _octets.Insert(_position++, new[] { value });
public void Write(short value)
{
var bytes = GetBigEndianBytes((ushort)value);
_octets.Insert(_position, bytes);
_position += 2;
}
public void Write(ushort value)
{
var bytes = GetBigEndianBytes(value);
_octets.Insert(_position, bytes);
_position += 2;
}
public void Write(int value)
{
var bytes = GetBigEndianBytes((uint)value);
_octets.Insert(_position, bytes);
_position += 4;
}
public void Write(uint value)
{
var bytes = GetBigEndianBytes(value);
_octets.Insert(_position, bytes);
_position += 4;
}
/// Write int32 in little-endian (for GNET protocol compatibility with C++ client/server).
public void WriteInt32LE(int value)
{
var bytes = BitConverter.GetBytes(value);
_octets.Insert(_position, bytes);
_position += 4;
}
/// Read int32 in little-endian (for GNET protocol compatibility with C++ client/server).
public int ReadInt32LE()
{
if (_position + 4 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var bytes = ReadBytes(4);
return BitConverter.ToInt32(bytes, 0);
}
public void Write(float value)
{
var bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
_octets.Insert(_position, bytes);
_position += 4;
}
public void Write(double value)
{
var bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
_octets.Insert(_position, bytes);
_position += 8;
}
// Read methods for primitive types
public byte ReadByte()
{
if (_position + 1 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
return ReadBytes(1)[0];
}
public short ReadInt16()
{
if (_position + 2 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var bytes = ReadBytes(2);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToInt16(bytes, 0);
}
public ushort ReadUInt16()
{
if (_position + 2 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var bytes = ReadBytes(2);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToUInt16(bytes, 0);
}
public int ReadInt32()
{
if (_position + 4 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var bytes = ReadBytes(4);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToInt32(bytes, 0);
}
public uint ReadUInt32()
{
if (_position + 4 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var bytes = ReadBytes(4);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToUInt32(bytes, 0);
}
public float ReadFloat()
{
if (_position + 4 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var bytes = ReadBytes(4);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToSingle(bytes, 0);
}
public double ReadDouble()
{
if (_position + 8 > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var bytes = ReadBytes(8);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToDouble(bytes, 0);
}
// Compact integer support
public void WriteCompactInt(int value)
{
if (value >= 0)
{
WriteCompactUInt((uint)value);
}
else
{
uint unsignedValue = (uint)(-value);
unsignedValue = (unsignedValue << 1) | 1;
WriteCompactUInt(unsignedValue);
}
}
public int ReadCompactInt()
{
uint value = ReadCompactUInt();
if ((value & 1) == 0)
return (int)(value >> 1);
else
return -(int)(value >> 1);
}
// Transaction support
public void HandleTransaction(Transaction trans)
{
switch (trans)
{
case Transaction.Begin:
_transactionPosition = _position;
break;
case Transaction.Rollback:
_position = _transactionPosition;
break;
case Transaction.Commit:
if (_position >= MaxSpare)
{
// Optimize memory usage
var newOctets = new byte[_octets.Length - _position];
Array.Copy(_octets.ToArray(), _position, newOctets, 0, newOctets.Length);
_octets.Dispose();
_octets = new Octets(newOctets.Length);
_position = 0;
}
break;
}
}
// Container support
public void WriteList(IList list)
where T : IMarshallable
{
WriteCompactUInt((uint)list.Count);
foreach (var item in list)
{
item.Marshal(this);
}
}
public void ReadList(IList list)
where T : IMarshallable, new()
{
uint count = ReadCompactUInt();
for (int i = 0; i < count; i++)
{
var item = new T();
item.Unmarshal(this);
list.Add(item);
}
}
public void WriteListInt(IList listInt)
{
if (listInt?.Count <= 0) return;
WriteCompactUInt((uint)listInt.Count);
foreach(var item in listInt)
{
Write(item);
}
}
public void ReadListInt(IList listInt)
{
uint count = ReadCompactUInt();
for (int i = 0; i < count; i++)
{
listInt.Add(ReadInt32());
}
}
public void WriteCompactUInt(uint value)
{
if (value < 0x80)
{
_octets.Insert(_position, new[] { (byte)value });
_position++;
}
else if (value < 0x4000)
{
var bytes = GetBigEndianBytes((ushort)(value | 0x8000));
_octets.Insert(_position, bytes);
_position += 2;
}
else if (value < 0x20000000)
{
var bytes = GetBigEndianBytes(value | 0xc0000000);
_octets.Insert(_position, bytes);
_position += 4;
}
else
{
_octets.Insert(_position, new[] { (byte)0xe0 });
_octets.Insert(_position + 1, GetBigEndianBytes(value));
_position += 5;
}
}
public void GetFirstBytes()
{
IPrefixedLogger _logger = LoggerFactory.GetLogger(nameof(Protocol));
var tempBytes = ReadByteWithoutAdvancePos(41);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 41; i++)
{
sb.Append(tempBytes[i]);
sb.Append(' ');
}
_logger.Debug($"Try Decode: Protocol 10 first bytes : {sb}");
}
public uint ReadCompactUInt()
{
byte flag = ReadBytes(1)[0];
IPrefixedLogger _logger = LoggerFactory.GetLogger(nameof(Protocol));
if ((flag & 0xE0) == 0xE0)
{
var bytes = ReadBytes(4);
return BitConverter.IsLittleEndian
? ((uint)bytes[0] << 24)
| ((uint)bytes[1] << 16)
| ((uint)bytes[2] << 8)
| bytes[3]
: BitConverter.ToUInt32(bytes, 0);
}
else if ((flag & 0xE0) == 0xC0)
{
var bytes = ReadBytes(3);
uint value = 0;
if (BitConverter.IsLittleEndian)
{
value = ((uint)bytes[0] << 16) | ((uint)bytes[1] << 8) | bytes[2];
}
else
{
value = BitConverter.ToUInt32(bytes, 0);
}
return (value | ((uint)flag << 24)) & ~0xC0000000;
}
else if ((flag & 0xE0) == 0x80 || (flag & 0xE0) == 0xA0)
{
var bytes = ReadBytes(1);
return (uint)((bytes[0] | (flag << 8)) & ~0x8000);
}
return flag;
}
public void WriteString(string value)
{
var bytes = GbkEncoding.GetBytes(value);
WriteCompactUInt((uint)bytes.Length);
_octets.Insert(_position, bytes);
_position += bytes.Length;
}
public string ReadString()
{
uint length = ReadCompactUInt();
var bytes = ReadBytes((int)length);
return GbkEncoding.GetString(bytes);
}
public byte[] ReadBytes(int count)
{
if (_position + count > _octets.Length)
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
var result = new byte[count];
Array.Copy(_octets.ToArray(), _position, result, 0, count);
_position += count;
return result;
}
public byte[] ReadByteWithoutAdvancePos(int count)
{
if (_position + count > _octets.Length)
return new byte[count];
var result = new byte[count];
Array.Copy(_octets.ToArray(), _position, result, 0, count);
return result;
}
public byte[] ToArray() => _octets.ToArray();
public void Dispose() => _octets?.Dispose();
public void WriteBytes(byte[] data)
{
_octets.Insert(_position, data);
_position += data.Length;
}
// Add these methods for Octets support
public void Write(Octets value)
{
WriteCompactUInt((uint)value.Size);
WriteBytes(value.ToArray());
}
public Octets ReadOctets()
{
uint size = ReadCompactUInt();
byte[] data = ReadBytes((int)size);
return new Octets(data);
}
public void Write(List list)
{
WriteCompactUInt((uint)list.Count);
foreach (var octets in list)
{
Write(octets);
}
}
public List ReadOctetsList()
{
uint count = ReadCompactUInt();
var list = new List();
for (int i = 0; i < count; i++)
{
list.Add(ReadOctets());
}
return list;
}
// Erase a section of the underlying buffer
public void Erase(int pos, int length)
{
_octets.Erase(pos, length);
// Adjust position if the erasure happened before the current position
if (pos < _position)
{
_position = Math.Max(pos, _position - length);
}
}
// Compact the stream by removing processed data (0 to Position)
public void Compact()
{
if (_position > 0)
{
_octets.Erase(0, _position);
_position = 0; // Reset position after compacting
}
}
}
}