93 lines
2.2 KiB
C#
93 lines
2.2 KiB
C#
|
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<Byte>(_Buffer, _DataEnd, free);
|
||
|
|
||
|
_DataEnd += Read(span);
|
||
|
}
|
||
|
|
||
|
private Int32 Read(Span<Byte> 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<Byte> ReadSegment(Int32 size)
|
||
|
{
|
||
|
var segment = PeekSegment(size);
|
||
|
BytesRead += size;
|
||
|
return segment;
|
||
|
}
|
||
|
|
||
|
public ArraySegment<Byte> PeekSegment(Int32 size)
|
||
|
{
|
||
|
while (Available < size)
|
||
|
FetchData();
|
||
|
|
||
|
return new ArraySegment<Byte>(_Buffer, BytesRead, size);
|
||
|
}
|
||
|
|
||
|
public void Reset()
|
||
|
{
|
||
|
if (BytesRead > 0 && Available > 0)
|
||
|
Array.Copy(_Buffer, BytesRead, _Buffer, 0, Available);
|
||
|
|
||
|
_DataEnd = Available;
|
||
|
BytesRead = 0;
|
||
|
}
|
||
|
|
||
|
}
|