using System.Net.Sockets; namespace InnovEnergy.Lib.Protocols.DBus.Transport; // one could use new BufferedStream(new NetworkStream(socket)) instead. // I did not know this when I wrote this code... // then again, the Streams API kinda sucks... public class BufferedSocketReader { private readonly Socket _Socket; private Byte[] _Buffer ; private Int32 _DataEnd = 0; private Int32 Free => _Buffer.Length - _DataEnd; private Int32 Available => _DataEnd - BytesRead; public Int32 BytesRead { get; private set; } = 0; // TODO: un-expose this? public BufferedSocketReader(Socket socket, Byte[]? buffer = null) { _Socket = socket; _Buffer = buffer ?? new Byte[1024]; } private void FetchData() { var free = GetFreeSpace(); var span = new Span(_Buffer, _DataEnd, free); _DataEnd += Read(span); } private Int32 Read(Span targetArea) { var nReceived = _Socket.Receive(targetArea, SocketFlags.None, out var err); if (err != SocketError.Success) throw new Exception($"Failed to read from socket: {err}"); if (nReceived <= 0) throw new Exception("Socket closed"); return nReceived; } private Int32 GetFreeSpace() { if (Free != 0) return Free; Array.Resize(ref _Buffer, _Buffer.Length * 2); return Free; } public Byte PeekByte(Int32 pos = 0) { if (Available < 1 + pos) FetchData(); return _Buffer[BytesRead + pos]; } public Byte ReadByte() { if (Available < 1) FetchData(); return _Buffer[BytesRead++]; } public ArraySegment ReadSegment(Int32 size) { var segment = PeekSegment(size); BytesRead += size; return segment; } public ArraySegment PeekSegment(Int32 size) { while (Available < size) FetchData(); return new ArraySegment(_Buffer, BytesRead, size); } public void Reset() { if (BytesRead > 0 && Available > 0) Array.Copy(_Buffer, BytesRead, _Buffer, 0, Available); _DataEnd = Available; BytesRead = 0; } }