Update Readers/Writers for Buffers & Fixes bugs (#283)

- [X] Renames PacketReader to CircularBufferReader
- [X] Fixes bugs with CircularBufferReader
- [X] Adds CircularBufferWriter
- [X] Adds SpanWriter
- [X] Adds SpanReader

Bumps release version
This commit is contained in:
Kamron Batman 2020-10-24 14:46:59 -07:00 committed by GitHub
parent 3b7f648c27
commit 6bd4f0d265
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1302 additions and 351 deletions

View file

@ -0,0 +1,119 @@
using System;
using System.IO;
using System.Text;
using Server.Network;
using Xunit;
namespace Server.Tests.Network
{
public class CircularBufferReaderTests
{
[Theory]
// First only, beginning
[InlineData("Test String", "us-ascii", false, -1, 1024, 1024, 0)]
[InlineData("Test String", "utf-u", false, -1, 1024, 1024, 0)]
[InlineData("Test String", "utf-16BE", false, -1, 1024, 1024, 0)]
[InlineData("Test String", "utf-16", false, -1, 1024, 1024, 0)]
// Second only
[InlineData("Test String", "us-ascii", false, -1, 1024, 1024, 1030)]
[InlineData("Test String", "utf-u", false, -1, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16BE", false, -1, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16", false, -1, 1024, 1024, 1030)]
// Split
[InlineData("Test String", "us-ascii", false, -1, 1024, 1024, 1020)]
[InlineData("Test String", "utf-u", false, -1, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16BE", false, -1, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16", false, -1, 1024, 1024, 1020)]
// First only, beginning, fixed length smaller
[InlineData("Test String", "us-ascii", false, 8, 1024, 1024, 0)]
[InlineData("Test String", "utf-u", false, 8, 1024, 1024, 0)]
[InlineData("Test String", "utf-16BE", false, 8, 1024, 1024, 0)]
[InlineData("Test String", "utf-16", false, 8, 1024, 1024, 0)]
// Second only, fixed length smaller
[InlineData("Test String", "us-ascii", false, 8, 1024, 1024, 1030)]
[InlineData("Test String", "utf-u", false, 8, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16BE", false, 8, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16", false, 8, 1024, 1024, 1030)]
// Split, fixed length smaller
[InlineData("Test String", "us-ascii", false, 8, 1024, 1024, 1020)]
[InlineData("Test String", "utf-u", false, 8, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16BE", false, 8, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16", false, 8, 1024, 1024, 1020)]
// First only, beginning, fixed length bigger
[InlineData("Test String", "us-ascii", false, 20, 1024, 1024, 0)]
[InlineData("Test String", "utf-u", false, 20, 1024, 1024, 0)]
[InlineData("Test String", "utf-16BE", false, 20, 1024, 1024, 0)]
[InlineData("Test String", "utf-16", false, 20, 1024, 1024, 0)]
// Second only, fixed length bigger
[InlineData("Test String", "us-ascii", false, 20, 1024, 1024, 1030)]
[InlineData("Test String", "utf-u", false, 20, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16BE", false, 20, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16", false, 20, 1024, 1024, 1030)]
// Split, fixed length bigger
[InlineData("Test String", "us-ascii", false, 20, 1024, 1024, 1020)]
[InlineData("Test String", "utf-u", false, 20, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16BE", false, 20, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16", false, 20, 1024, 1024, 1020)]
public void TestReadString(
string value,
string encodingStr,
bool isSafe,
int fixedLength,
int firstSize,
int secondSize,
int offset
)
{
Span<byte> buffer = stackalloc byte[firstSize + secondSize];
buffer.Clear();
var encoding = EncodingHelpers.GetEncoding(encodingStr);
var strLength = fixedLength > -1 ? Math.Min(value.Length, fixedLength) : value.Length;
var chars = value.AsSpan(0, strLength);
;
encoding.GetBytes(chars, buffer.Slice(offset));
var reader = new CircularBufferReader(buffer.Slice(0, firstSize), buffer.Slice(firstSize));
reader.Seek(offset, SeekOrigin.Begin);
var actual = reader.ReadString(encoding, isSafe, fixedLength);
Assert.Equal(value.Substring(0, strLength), actual);
}
[Fact]
public void TestReadStringBetween()
{
Span<byte> expected = stackalloc byte[19];
expected[0] = 0x1;
expected[1] = 0x1;
expected[2] = 0x1;
expected[3] = 0x1;
Encoding.ASCII.GetBytes("TestString", expected.Slice(4, 10));
expected[14] = 0x0; // Null
expected[15] = 0x2;
expected[16] = 0x2;
expected[17] = 0x2;
expected[18] = 0x2;
var reader = new CircularBufferReader(expected, stackalloc byte[0]);
var num1 = reader.ReadInt32();
var str = reader.ReadAscii();
var num2 = reader.ReadInt32();
Assert.Equal(0x01010101, num1);
Assert.Equal("TestString", str);
Assert.Equal(0x02020202, num2);
}
}
}

View file

@ -0,0 +1,99 @@
using System;
using System.Buffers;
using System.IO;
using Xunit;
namespace Server.Tests.Buffers
{
public class CircularBufferWriterTests
{
[Theory]
// First only, beginning
[InlineData("Test String", "us-ascii", -1, 1024, 1024, 0)]
[InlineData("Test String", "utf-8", -1, 1024, 1024, 0)]
[InlineData("Test String", "utf-16BE", -1, 1024, 1024, 0)]
[InlineData("Test String", "utf-16", -1, 1024, 1024, 0)]
// Second only
[InlineData("Test String", "us-ascii", -1, 1024, 1024, 1030)]
[InlineData("Test String", "utf-8", -1, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16BE", -1, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16", -1, 1024, 1024, 1030)]
// Split
[InlineData("Test String", "us-ascii", -1, 1024, 1024, 1020)]
[InlineData("Test String", "utf-8", -1, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16BE", -1, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16", -1, 1024, 1024, 1020)]
// First only, beginning, fixed length smaller
[InlineData("Test String", "us-ascii", 8, 1024, 1024, 0)]
[InlineData("Test String", "utf-16BE", 8, 1024, 1024, 0)]
[InlineData("Test String", "utf-16", 8, 1024, 1024, 0)]
// Second only, fixed length smaller
[InlineData("Test String", "us-ascii", 8, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16BE", 8, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16", 8, 1024, 1024, 1030)]
// Split, fixed length smaller
[InlineData("Test String", "us-ascii", 8, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16BE", 8, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16", 8, 1024, 1024, 1020)]
// First only, beginning, fixed length bigger
[InlineData("Test String", "us-ascii", 20, 1024, 1024, 0)]
[InlineData("Test String", "utf-16BE", 20, 1024, 1024, 0)]
[InlineData("Test String", "utf-16", 20, 1024, 1024, 0)]
// Second only, fixed length bigger
[InlineData("Test String", "us-ascii", 20, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16BE", 20, 1024, 1024, 1030)]
[InlineData("Test String", "utf-16", 20, 1024, 1024, 1030)]
// Split, fixed length bigger
[InlineData("Test String", "us-ascii", 20, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16BE", 20, 1024, 1024, 1020)]
[InlineData("Test String", "utf-16", 20, 1024, 1024, 1020)]
public void TestWriteString(
string value,
string encodingStr,
int fixedLength,
int firstSize,
int secondSize,
int offset
)
{
Span<byte> buffer = stackalloc byte[firstSize + secondSize];
buffer.Clear();
var encoding = EncodingHelpers.GetEncoding(encodingStr);
var strLength = fixedLength > -1 ? Math.Min(value.Length, fixedLength) : value.Length;
var chars = value.AsSpan(0, strLength);
var writer = new CircularBufferWriter(buffer.Slice(0, firstSize), buffer.Slice(firstSize));
writer.Seek(offset, SeekOrigin.Begin);
writer.WriteString(chars, encoding);
if (offset > 0)
{
Span<byte> testEmpty = stackalloc byte[offset];
testEmpty.Clear();
AssertThat.Equal(buffer.Slice(0, offset), testEmpty);
}
Span<byte> expectedStr = stackalloc byte[encoding.GetByteCount(chars)];
encoding.GetBytes(chars, expectedStr.Slice(0));
AssertThat.Equal(buffer.Slice(offset, expectedStr.Length), expectedStr);
offset += expectedStr.Length;
if (offset < buffer.Length)
{
Span<byte> testEmpty = stackalloc byte[buffer.Length - offset];
testEmpty.Clear();
AssertThat.Equal(buffer.Slice(offset), testEmpty);
}
}
}
}

View file

@ -1,101 +0,0 @@
using System;
using System.IO;
using System.Text;
using Server.Network;
using Xunit;
using Xunit.Abstractions;
namespace Server.Tests.Network
{
public class PacketReaderTests
{
private (Encoding, Type) GetEncoding(string value) =>
value.ToUpper() switch
{
"UTF8" => (Utility.UTF8, typeof(byte)),
"UNICODELE" => (Utility.UnicodeLE, typeof(char)),
"UNICODE" => (Utility.Unicode, typeof(char)),
_ => (Encoding.ASCII, typeof(byte))
};
private ITestOutputHelper _outputHelper;
public PacketReaderTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper;
[Theory]
// First only, beginning
[InlineData("Test String", "ASCII", false, -1, 1024, 1024, 0)]
[InlineData("Test String", "UTF8", false, -1, 1024, 1024, 0)]
[InlineData("Test String", "Unicode", false, -1, 1024, 1024, 0)]
[InlineData("Test String", "UnicodeLE", false, -1, 1024, 1024, 0)]
// Second only
[InlineData("Test String", "ASCII", false, -1, 1024, 1024, 1030)]
[InlineData("Test String", "UTF8", false, -1, 1024, 1024, 1030)]
[InlineData("Test String", "Unicode", false, -1, 1024, 1024, 1030)]
[InlineData("Test String", "UnicodeLE", false, -1, 1024, 1024, 1030)]
public void TestReadString(
string value,
string encodingStr,
bool isSafe,
int fixedLength,
int firstSize,
int secondSize,
int offset
)
{
var (encoding, byteType) = GetEncoding(encodingStr);
var bytes = encoding.GetBytes(value);
Span<byte> expectedBytes = bytes.AsSpan(0, fixedLength > -1 ? Math.Min(fixedLength, bytes.Length) : bytes.Length);
if (offset + expectedBytes.Length > firstSize + secondSize)
{
throw new ArgumentException("Test failed due to bad assumptions. Out of memory exception would be thrown");
}
Span<byte> first = stackalloc byte[firstSize];
first.Clear(); // Just in case
int bytesWrittenFirst = 0;
if (offset < first.Length)
{
bytesWrittenFirst = Math.Min(first.Length, expectedBytes.Length);
expectedBytes.Slice(offset, bytesWrittenFirst).CopyTo(first);
}
Span<byte> second = stackalloc byte[secondSize];
second.Clear(); // Just in case
if (offset > first.Length || expectedBytes.Length > bytesWrittenFirst)
{
var secondOffset = Math.Max(offset - first.Length, 0);
var secondSlice = second.Slice(secondOffset, second.Length - secondOffset);
expectedBytes.Slice(bytesWrittenFirst, expectedBytes.Length - bytesWrittenFirst).CopyTo(secondSlice);
}
// _outputHelper.WriteLine(HexStringConverter.GetString(first));
// _outputHelper.WriteLine(HexStringConverter.GetString(second));
var reader = new PacketReader(first, second);
reader.Seek(offset, SeekOrigin.Begin);
_outputHelper.WriteLine(reader.Position.ToString());
var actual = byteType switch
{
var b when b == typeof(char) => reader.ReadString<char>(encoding, isSafe, fixedLength),
_ => reader.ReadString<byte>(encoding, isSafe, fixedLength)
};
_outputHelper.WriteLine(actual);
if (fixedLength > 0 && fixedLength < value.Length)
{
value = value.Substring(0, fixedLength);
}
Assert.Equal(value, actual);
}
}
}

View file

@ -131,28 +131,28 @@ namespace Server.Tests.Network
var result = writer.GetAvailable();
Assert.True(result.Length == 9);
Assert.True(reader.GetRemaining() == 0);
Assert.True(reader.GetAvailable() == 0);
result = reader.TryRead();
Assert.True(result.Length == 0);
writer.Advance(7);
result = writer.GetAvailable();
Assert.True(result.Length == 2);
Assert.True(reader.GetRemaining() == 7);
Assert.True(reader.GetAvailable() == 7);
result = reader.TryRead();
Assert.True(result.Length == 7);
reader.Advance(4);
result = writer.GetAvailable();
Assert.True(result.Length == 6);
Assert.True(reader.GetRemaining() == 3);
Assert.True(reader.GetAvailable() == 3);
result = reader.TryRead();
Assert.True(result.Length == 3);
writer.Advance(3);
result = writer.GetAvailable();
Assert.True(result.Length == 3);
Assert.True(reader.GetRemaining() == 6);
Assert.True(reader.GetAvailable() == 6);
result = reader.TryRead();
Assert.True(result.Length == 6);
@ -171,7 +171,7 @@ namespace Server.Tests.Network
writer.Advance(i);
writer.Flush();
Assert.True(reader.GetRemaining() == i);
Assert.True(reader.GetAvailable() == i);
reader.Advance(i);
}
}
@ -192,7 +192,7 @@ namespace Server.Tests.Network
writer.Advance(9);
writer.Flush();
Assert.True(reader.GetRemaining() == 9);
Assert.True(reader.GetAvailable() == 9);
buffer = reader.TryRead();

View file

@ -0,0 +1,17 @@
using System;
using System.Text;
namespace Server.Tests
{
public static class EncodingHelpers
{
public static Encoding GetEncoding(string bodyname) =>
bodyname switch
{
"utf-8" => Utility.UTF8,
"utf-16" => Utility.UnicodeLE,
"utf-16BE" => Utility.Unicode,
_ => Encoding.ASCII
};
}
}