Changes Huffman to in-place & Adds CircularBuffer (#287)

- [X] Adds CircularBuffer
- [X] Updates Packet Encoding/Decoding
- [X] Changes Huffman to in-place

Bumps release version
This commit is contained in:
Kamron Batman 2020-10-25 12:54:37 -07:00 committed by GitHub
parent 3337cfa4e2
commit 293e691539
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 237 additions and 190 deletions

View file

@ -33,12 +33,12 @@ namespace Server.Network
{
public delegate void NetStateCreatedCallback(NetState ns);
public delegate void EncodePacket(ReadOnlySpan<byte> inputBuffer, CircularBufferWriter outputBuffer);
public delegate void EncodePacket(CircularBuffer<byte> buffer, ref int length);
public partial class NetState : IComparable<NetState>
{
private static int IncomingPipeSize = 1024 * 64;
private static int OutgoingPipeSize = 1024 * 256;
private static int RecvPipeSize = 1024 * 64;
private static int SendPipeSize = 1024 * 256;
private static int GumpCap = 512;
private static int HuePickerCap = 512;
private static int MenuCap = 512;
@ -51,13 +51,13 @@ namespace Server.Network
private readonly string m_ToString;
private int m_Disposing;
private ClientVersion m_Version;
private byte[] m_IncomingBuffer;
private Pipe<byte> m_IncomingPipe;
private byte[] m_OutgoingBuffer;
private Pipe<byte> m_OutgoingPipe;
private byte[] _recvBuffer;
private Pipe<byte> _recvPipe;
private byte[] _sendBuffer;
private Pipe<byte> _sendPipe;
private long m_NextCheckActivity;
private volatile bool m_Running;
private Thread _sendThread;
private readonly Thread _sendThread;
private volatile EncodePacket _packetDecoder;
private volatile EncodePacket _packetEncoder;
@ -66,8 +66,8 @@ namespace Server.Network
public static void Configure()
{
IncomingPipeSize = ServerConfiguration.GetOrUpdateSetting("netstate.incomingPipeSize", IncomingPipeSize);
OutgoingPipeSize = ServerConfiguration.GetOrUpdateSetting("netstate.outgoingPipeSize", OutgoingPipeSize);
RecvPipeSize = ServerConfiguration.GetOrUpdateSetting("netstate.recvPipeSize", RecvPipeSize);
SendPipeSize = ServerConfiguration.GetOrUpdateSetting("netstate.sendPipeSize", SendPipeSize);
GumpCap = ServerConfiguration.GetOrUpdateSetting("netstate.gumpCap", GumpCap);
HuePickerCap = ServerConfiguration.GetOrUpdateSetting("netstate.huePickerCap", HuePickerCap);
MenuCap = ServerConfiguration.GetOrUpdateSetting("netstate.menuCap", MenuCap);
@ -88,10 +88,10 @@ namespace Server.Network
HuePickers = new List<HuePicker>();
Menus = new List<IMenu>();
Trades = new List<SecureTrade>();
m_IncomingBuffer = new byte[IncomingPipeSize];
m_IncomingPipe = new Pipe<byte>(m_IncomingBuffer);
m_OutgoingBuffer = new byte[OutgoingPipeSize];
m_OutgoingPipe = new Pipe<byte>(m_OutgoingBuffer);
_recvBuffer = new byte[RecvPipeSize];
_recvPipe = new Pipe<byte>(_recvBuffer);
_sendBuffer = new byte[SendPipeSize];
_sendPipe = new Pipe<byte>(_sendBuffer);
m_NextCheckActivity = Core.TickCount + 30000;
_sendThread = sendThread ?? Core.Thread;
@ -370,7 +370,9 @@ namespace Server.Network
NetworkState.Resume(ref m_NetworkState);
}
public virtual void Send(Span<byte> buffer)
public Pipe<byte>.Result<byte> GetAvailableSendPipe() => _recvPipe.Writer.GetAvailable();
public virtual void Send(CircularBuffer<byte> buffer, int length)
{
if (Connection == null || BlockAllPackets || buffer.Length == 0)
{
@ -387,25 +389,10 @@ namespace Server.Network
#endif
}
var writer = m_OutgoingPipe.Writer;
try
{
var result = writer.GetAvailable();
int length;
if (PacketEncoder != null)
{
var bufferWriter = new CircularBufferWriter(result.Buffer);
PacketEncoder?.Invoke(buffer, bufferWriter);
length = bufferWriter.Position;
}
else
{
result.CopyFrom(buffer);
length = buffer.Length;
}
writer.Advance((uint)length);
_packetEncoder?.Invoke(buffer, ref length);
_sendPipe.Writer.Advance((uint)length);
}
catch (Exception ex)
{
@ -435,7 +422,7 @@ namespace Server.Network
#endif
}
var writer = m_OutgoingPipe.Writer;
var writer = _sendPipe.Writer;
try
{
@ -490,7 +477,7 @@ namespace Server.Network
private async void SendTask(object state)
{
var reader = m_OutgoingPipe.Reader;
var reader = _sendPipe.Reader;
try
{
@ -527,19 +514,16 @@ namespace Server.Network
}
}
private int DecodePacket(ReadOnlySpan<byte> input, ArraySegment<byte>[] output)
private void DecodePacket(ArraySegment<byte>[] buffer, ref int length)
{
var writer = new CircularBufferWriter(output);
PacketDecoder(input, writer);
return writer.Position;
CircularBuffer<byte> cBuffer = new CircularBuffer<byte>(buffer);
_packetDecoder?.Invoke(cBuffer, ref length);
}
private async void RecvTask(object state)
{
var socket = Connection;
var writer = m_IncomingPipe.Writer;
byte[] encodingBuffer = null;
var writer = _recvPipe.Writer;
try
{
@ -557,33 +541,13 @@ namespace Server.Network
continue;
}
int bytesWritten;
if (PacketDecoder != null)
var bytesWritten = await socket.ReceiveAsync(result.Buffer, SocketFlags.None);
if (bytesWritten <= 0)
{
encodingBuffer ??= ArrayPool<byte>.Shared.Rent(0x10000);
bytesWritten = await socket.ReceiveAsync(encodingBuffer, SocketFlags.None);
if (bytesWritten <= 0)
{
break;
}
bytesWritten = DecodePacket(encodingBuffer.AsSpan(0, bytesWritten), result.Buffer);
break;
}
else
{
if (encodingBuffer != null)
{
var returnBuffer = encodingBuffer;
encodingBuffer = null;
ArrayPool<byte>.Shared.Return(returnBuffer);
}
bytesWritten = await socket.ReceiveAsync(result.Buffer, SocketFlags.None);
if (bytesWritten <= 0)
{
break;
}
}
DecodePacket(result.Buffer, ref bytesWritten);
writer.Advance((uint)bytesWritten);
m_NextCheckActivity = Core.TickCount + 90000;
@ -600,10 +564,6 @@ namespace Server.Network
}
finally
{
if (encodingBuffer != null)
{
ArrayPool<byte>.Shared.Return(encodingBuffer);
}
Dispose();
}
}
@ -627,7 +587,7 @@ namespace Server.Network
try
{
var reader = m_IncomingPipe.Reader;
var reader = _recvPipe.Reader;
// Process as many packets as we can synchronously
while (true)
@ -670,7 +630,7 @@ namespace Server.Network
{
if (Connection != null)
{
m_OutgoingPipe.Writer.Flush();
_sendPipe.Writer.Flush();
}
}
@ -755,7 +715,7 @@ namespace Server.Network
return;
}
m_OutgoingPipe.Writer.Close();
_sendPipe.Writer.Close();
try
{
@ -793,10 +753,10 @@ namespace Server.Network
ns.m_Running = false;
ns.Connection = null;
ns.m_IncomingBuffer = null;
ns.m_IncomingPipe = null;
ns.m_OutgoingBuffer = null;
ns.m_OutgoingPipe = null;
ns._recvBuffer = null;
ns._recvPipe = null;
ns._sendBuffer = null;
ns._sendPipe = null;
ns.Gumps.Clear();
ns.Menus.Clear();
ns.HuePickers.Clear();

View file

@ -12,7 +12,7 @@ namespace Server.Network
private const int ValueIndex = 1;
// UO packets may not exceed 64kb in length
private const int BufferSize = 0x10000;
public const int BufferSize = 0x10000;
// Optimal compression ratio is 2 / 8; worst compression ratio is 11 / 8
private const int MinimalCodeLength = 2;
@ -61,170 +61,127 @@ namespace Server.Network
0x4, 0x00D
};
public static void Compress(ReadOnlySpan<byte> input, CircularBufferWriter output)
public static void Compress(CircularBuffer<byte> buffer, ref int length)
{
int inputCapacity = input.Length;
length = Compress(buffer, length, buffer);
}
if (inputCapacity > DefiniteOverflow)
public static int Compress(CircularBuffer<byte> input, int inputLength, CircularBuffer<byte> output)
{
if (inputLength > DefiniteOverflow)
{
return;
return 0;
}
int bitCount = 0;
int bitValue = 0;
int inputIdx = 0;
int outputIdx = 0;
while (inputIdx < inputCapacity)
while (inputIdx < inputLength)
{
int i = input[inputIdx++] << 1;
bitCount += _huffmanTable[i];
bitValue <<= _huffmanTable[i];
bitValue |= _huffmanTable[i + 1];
bitValue = (bitValue << _huffmanTable[i]) | _huffmanTable[i + 1];
while (bitCount >= 8)
{
bitCount -= 8;
if (output.Length < output.Position + 1)
if (output.Length < outputIdx + 1)
{
return;
return 0;
}
output.Write((byte)(bitValue >> bitCount));
output[outputIdx++] = (byte)(bitValue >> bitCount);
}
}
// terminal code
bitCount += _huffmanTable[0x200];
bitValue <<= _huffmanTable[0x200];
bitValue |= _huffmanTable[0x201];
bitValue = (bitValue << _huffmanTable[0x200]) | _huffmanTable[0x201];
// align on byte boundary
if ((bitCount & 7) != 0)
{
bitValue <<= (8 - (bitCount & 7));
bitCount += (8 - (bitCount & 7));
bitValue <<= 8 - (bitCount & 7);
bitCount += 8 - (bitCount & 7);
}
while (bitCount >= 8)
{
bitCount -= 8;
if (output.Length < output.Position + 1)
if (output.Length < outputIdx + 1)
{
return;
return 0;
}
output.Write((byte)(bitValue >> bitCount));
output[outputIdx++] = (byte)(bitValue >> bitCount);
}
return outputIdx;
}
public static unsafe void Compress(
ReadOnlySpan<byte> input, int offset, int count, Span<byte> output, out int length
)
public static int Compress(ReadOnlySpan<byte> input, Span<byte> output)
{
if (input == null)
if (input.Length > DefiniteOverflow)
{
throw new ArgumentNullException(nameof(input));
return 0;
}
if (offset < 0 || offset >= input.Length)
int bitCount = 0;
int bitValue = 0;
int inputIdx = 0;
int outputIdx = 0;
while (inputIdx < input.Length)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
int i = input[inputIdx++] << 1;
if (count < 0 || count > input.Length)
{
throw new ArgumentOutOfRangeException(nameof(count));
}
bitCount += _huffmanTable[i];
bitValue = (bitValue << _huffmanTable[i]) | _huffmanTable[i + 1];
if (input.Length - offset < count)
{
throw new ArgumentOutOfRangeException(nameof(offset));
}
length = 0;
if (count > DefiniteOverflow)
{
return;
}
var bitCount = 0;
var bitValue = 0;
fixed (int* pTable = _huffmanTable)
{
fixed (byte* pInputBuffer = input)
while (bitCount >= 8)
{
byte* pInput = pInputBuffer + offset, pInputEnd = pInput + count;
bitCount -= 8;
fixed (byte* pOutputBuffer = output)
if (output.Length < outputIdx + 1)
{
byte* pOutput = pOutputBuffer, pOutputEnd = pOutput + BufferSize;
int* pEntry;
while (pInput < pInputEnd)
{
pEntry = &pTable[*pInput++ << 1];
bitCount += pEntry[CountIndex];
bitValue <<= pEntry[CountIndex];
bitValue |= pEntry[ValueIndex];
while (bitCount >= 8)
{
bitCount -= 8;
if (pOutput < pOutputEnd)
{
*pOutput++ = (byte)(bitValue >> bitCount);
}
else
{
length = 0;
return;
}
}
}
// terminal code
pEntry = &pTable[0x200];
bitCount += pEntry[CountIndex];
bitValue <<= pEntry[CountIndex];
bitValue |= pEntry[ValueIndex];
// align on byte boundary
if ((bitCount & 7) != 0)
{
bitValue <<= 8 - (bitCount & 7);
bitCount += 8 - (bitCount & 7);
}
while (bitCount >= 8)
{
bitCount -= 8;
if (pOutput < pOutputEnd)
{
*pOutput++ = (byte)(bitValue >> bitCount);
}
else
{
length = 0;
return;
}
}
length = (int)(pOutput - pOutputBuffer);
return 0;
}
output[outputIdx++] = (byte)(bitValue >> bitCount);
}
}
// terminal code
bitCount += _huffmanTable[0x200];
bitValue = (bitValue << _huffmanTable[0x200]) | _huffmanTable[0x201];
// align on byte boundary
if ((bitCount & 7) != 0)
{
bitValue <<= 8 - (bitCount & 7);
bitCount += 8 - (bitCount & 7);
}
while (bitCount >= 8)
{
bitCount -= 8;
if (output.Length < outputIdx + 1)
{
return 0;
}
output[outputIdx++] = (byte)(bitValue >> bitCount);
}
return outputIdx;
}
}
}

View file

@ -194,7 +194,7 @@ namespace Server.Network
if (compress)
{
var compressorBuffer = ArrayPool<byte>.Shared.Rent(CompressorBufferSize);
NetworkCompression.Compress(m_CompiledBuffer, 0, length, compressorBuffer, out var compressedLength);
var compressedLength = NetworkCompression.Compress(m_CompiledBuffer.AsSpan(0, length), compressorBuffer);
if (length <= 0)
{