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
};
}
}

View file

@ -15,35 +15,29 @@
using System;
using System.Buffers.Binary;
using System.Data;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace Server.Network
{
public ref struct PacketReader
public ref struct CircularBufferReader
{
public Span<byte> First;
public Span<byte> Second;
private readonly ReadOnlySpan<byte> _first;
private readonly ReadOnlySpan<byte> _second;
public int Length { get; }
public int Position { get; private set; }
public int Remaining => Length - Position;
public PacketReader(ArraySegment<byte>[] buffers)
public CircularBufferReader(ArraySegment<byte>[] buffers) : this(buffers[0], buffers[1])
{
First = buffers[0];
Second = buffers[1];
Position = 0;
Length = First.Length + Second.Length;
}
public PacketReader(Span<byte> first, Span<byte> second)
public CircularBufferReader(ReadOnlySpan<byte> first, ReadOnlySpan<byte> second)
{
First = first;
Second = second;
_first = first;
_second = second;
Position = 0;
Length = first.Length + second.Length;
}
@ -51,7 +45,7 @@ namespace Server.Network
public void Trace(NetState state)
{
// We don't have data, so nothing to trace
if (First.Length == 0)
if (_first.Length == 0)
{
return;
}
@ -60,9 +54,9 @@ namespace Server.Network
{
using var sw = new StreamWriter("Packets.log", true);
sw.WriteLine("Client: {0}: Unhandled packet 0x{1:X2}", state, First[0]);
sw.WriteLine("Client: {0}: Unhandled packet 0x{1:X2}", state, _first[0]);
Utility.FormatBuffer(sw, First.ToArray(), new Memory<byte>(Second.ToArray()));
Utility.FormatBuffer(sw, _first.ToArray(), new Memory<byte>(_second.ToArray()));
sw.WriteLine();
sw.WriteLine();
@ -76,14 +70,14 @@ namespace Server.Network
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte()
{
if (Position < First.Length)
if (Position < _first.Length)
{
return First[Position++];
return _first[Position++];
}
if (Position < Length)
{
return Second[Position++ - First.Length];
return _second[Position++ - _first.Length];
}
throw new OutOfMemoryException();
@ -100,15 +94,15 @@ namespace Server.Network
{
short value;
if (Position < First.Length)
if (Position < _first.Length)
{
if (!BinaryPrimitives.TryReadInt16BigEndian(First.Slice(Position), out value))
if (!BinaryPrimitives.TryReadInt16BigEndian(_first.Slice(Position), out value))
{
// Not enough space. Split the spans
return (short)((ReadByte() >> 8) | ReadByte());
}
}
else if (!BinaryPrimitives.TryReadInt16BigEndian(Second.Slice(Position - First.Length), out value))
else if (!BinaryPrimitives.TryReadInt16BigEndian(_second.Slice(Position - _first.Length), out value))
{
throw new OutOfMemoryException();
}
@ -122,15 +116,15 @@ namespace Server.Network
{
ushort value;
if (Position < First.Length)
if (Position < _first.Length)
{
if (!BinaryPrimitives.TryReadUInt16BigEndian(First.Slice(Position), out value))
if (!BinaryPrimitives.TryReadUInt16BigEndian(_first.Slice(Position), out value))
{
// Not enough space. Split the spans
return (ushort)((ReadByte() >> 8) | ReadByte());
}
}
else if (!BinaryPrimitives.TryReadUInt16BigEndian(Second.Slice(Position - First.Length), out value))
else if (!BinaryPrimitives.TryReadUInt16BigEndian(_second.Slice(Position - _first.Length), out value))
{
throw new OutOfMemoryException();
}
@ -144,15 +138,15 @@ namespace Server.Network
{
int value;
if (Position < First.Length)
if (Position < _first.Length)
{
if (!BinaryPrimitives.TryReadInt32BigEndian(First.Slice(Position), out value))
if (!BinaryPrimitives.TryReadInt32BigEndian(_first.Slice(Position), out value))
{
// Not enough space. Split the spans
return (ReadByte() >> 24) | (ReadByte() >> 16) | (ReadByte() >> 8) | ReadByte();
}
}
else if (!BinaryPrimitives.TryReadInt32BigEndian(Second.Slice(Position - First.Length), out value))
else if (!BinaryPrimitives.TryReadInt32BigEndian(_second.Slice(Position - _first.Length), out value))
{
throw new OutOfMemoryException();
}
@ -166,15 +160,15 @@ namespace Server.Network
{
uint value;
if (Position < First.Length)
if (Position < _first.Length)
{
if (!BinaryPrimitives.TryReadUInt32BigEndian(First.Slice(Position), out value))
if (!BinaryPrimitives.TryReadUInt32BigEndian(_first.Slice(Position), out value))
{
// Not enough space. Split the spans
return (uint)((ReadByte() >> 24) | (ReadByte() >> 16) | (ReadByte() >> 8) | ReadByte());
}
}
else if (!BinaryPrimitives.TryReadUInt32BigEndian(Second.Slice(Position - First.Length), out value))
else if (!BinaryPrimitives.TryReadUInt32BigEndian(_second.Slice(Position - _first.Length), out value))
{
throw new OutOfMemoryException();
}
@ -183,28 +177,20 @@ namespace Server.Network
return value;
}
private static bool IsSafeChar(ushort c) => c >= 0x20 && c < 0xFFFE;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadString<T>(Encoding encoding, bool safeString = false, int fixedLength = -1) where T : struct, IEquatable<T>
public string ReadString(Encoding encoding, bool safeString = false, int fixedLength = -1)
{
int sizeT = Unsafe.SizeOf<T>();
if (sizeT > 2)
{
throw new InvalidConstraintException("ReadString only accepts byte, sbyte, char, short, and ushort as a constraint");
}
int sizeT = Utility.GetByteLengthForEncoding(encoding);
bool isFixedLength = fixedLength > -1;
var remaining = Remaining;
int size;
// Not fixed length
if (isFixedLength)
{
size = fixedLength * sizeT;
if (fixedLength > Remaining)
if (size > Remaining)
{
throw new OutOfMemoryException();
}
@ -214,130 +200,107 @@ namespace Server.Network
size = remaining - (remaining & (sizeT - 1));
}
Span<byte> span;
ReadOnlySpan<byte> span;
int index;
if (Position < First.Length)
if (Position < _first.Length)
{
var firstLength = Math.Min(_first.Length - Position, size);
// Find terminator
index = MemoryMarshal
.Cast<byte, T>(First.Slice(Position, Math.Min(size, First.Length)))
.IndexOf(default(T)) * sizeT;
index = Utility.IndexOfTerminator(_first.Slice(Position, firstLength), sizeT);
if (index < 0)
{
remaining = size - First.Length;
remaining = size - firstLength;
// We don't have a terminator, but a fixed size to the end of the first span, so stop there
if (remaining <= 0)
{
index = First.Length;
index = firstLength;
}
else
{
index = MemoryMarshal
.Cast<byte, T>(Second.Slice(0, remaining))
.IndexOf(default(T)) * sizeT;
index = Utility.IndexOfTerminator(_second.Slice(0, remaining), sizeT);
int secondLength = index < 0 ? remaining : index;
int length = First.Length + secondLength;
int length = firstLength + secondLength;
// Assume no strings should be too long for the stack
Span<byte> bytes = stackalloc byte[length];
First.Slice(Position).CopyTo(bytes);
Second.Slice(0, secondLength).CopyTo(bytes.Slice(First.Length));
_first.Slice(Position).CopyTo(bytes);
_second.Slice(0, secondLength).CopyTo(bytes.Slice(firstLength));
Position += length;
return GetString(bytes, encoding, safeString);
Position += length + (index >= 0 ? sizeT : 0);
return Utility.GetString(bytes, encoding, safeString);
}
}
span = First.Slice(Position, index);
span = _first.Slice(Position, index);
}
else
{
span = Second.Slice( Position - First.Length, Math.Min(remaining, size));
index = MemoryMarshal.Cast<byte, T>(span).IndexOf(default(T)) * sizeT;
size = Math.Min(remaining, size);
span = _second.Slice( Position - _first.Length, size);
index = Utility.IndexOfTerminator(span, sizeT);
if (index >= 0)
{
span = span.Slice(0, index);
}
}
Position += isFixedLength ? size : index;
return GetString(span, encoding, safeString);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string GetString(Span<byte> span, Encoding encoding, bool safeString = false)
{
string s = encoding.GetString(span);
if (!safeString)
{
return s;
}
ReadOnlySpan<char> chars = s.AsSpan();
StringBuilder stringBuilder = null;
for (int i = 0, last = 0; i < chars.Length; i++)
{
if (!IsSafeChar(chars[i]) || stringBuilder != null && i == chars.Length - 1)
else
{
(stringBuilder ??= new StringBuilder()).Append(chars.Slice(last, i - last));
last = i + 1; // Skip the unsafe char
index = size;
}
}
return stringBuilder?.ToString() ?? s;
Position += isFixedLength ? size : index + sizeT;
return Utility.GetString(span, encoding, safeString);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadLittleUniSafe(int fixedLength) => ReadString<char>(Utility.UnicodeLE, true, fixedLength);
public string ReadLittleUniSafe(int fixedLength) => ReadString(Utility.UnicodeLE, true, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadLittleUniSafe() => ReadString<char>(Utility.UnicodeLE, true);
public string ReadLittleUniSafe() => ReadString(Utility.UnicodeLE, true);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadLittleUni(int fixedLength) => ReadString<char>(Utility.UnicodeLE, false, fixedLength);
public string ReadLittleUni(int fixedLength) => ReadString(Utility.UnicodeLE, false, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadLittleUni() => ReadString<char>(Utility.UnicodeLE);
public string ReadLittleUni() => ReadString(Utility.UnicodeLE);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadBigUniSafe(int fixedLength) => ReadString<char>(Utility.Unicode, true, fixedLength);
public string ReadBigUniSafe(int fixedLength) => ReadString(Utility.Unicode, true, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadBigUniSafe() => ReadString<char>(Utility.Unicode, true);
public string ReadBigUniSafe() => ReadString(Utility.Unicode, true);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadBigUni(int fixedLength) => ReadString<char>(Utility.Unicode, false, fixedLength);
public string ReadBigUni(int fixedLength) => ReadString(Utility.Unicode, false, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadBigUni() => ReadString<char>(Utility.Unicode);
public string ReadBigUni() => ReadString(Utility.Unicode);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadUTF8Safe(int fixedLength) => ReadString<byte>(Utility.UTF8, true, fixedLength);
public string ReadUTF8Safe(int fixedLength) => ReadString(Utility.UTF8, true, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadUTF8Safe() => ReadString<byte>(Utility.UTF8, true);
public string ReadUTF8Safe() => ReadString(Utility.UTF8, true);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadUTF8() => ReadString<byte>(Utility.UTF8);
public string ReadUTF8() => ReadString(Utility.UTF8);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadAsciiSafe(int fixedLength) => ReadString<byte>(Encoding.ASCII, true, fixedLength);
public string ReadAsciiSafe(int fixedLength) => ReadString(Encoding.ASCII, true, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadAsciiSafe() => ReadString<byte>(Encoding.ASCII, true);
public string ReadAsciiSafe() => ReadString(Encoding.ASCII, true);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadAscii(int fixedLength) => ReadString<byte>(Encoding.ASCII, false, fixedLength);
public string ReadAscii(int fixedLength) => ReadString(Encoding.ASCII, false, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadAscii() => ReadString<byte>(Encoding.ASCII);
public string ReadAscii() => ReadString(Encoding.ASCII);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Seek(int offset, SeekOrigin origin) =>

View file

@ -0,0 +1,372 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: CircularBufferWriter.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using Server;
namespace System.Buffers
{
public ref struct CircularBufferWriter
{
private readonly Span<byte> _first;
private readonly Span<byte> _second;
public int Length { get; }
public int Position { get; private set; }
public CircularBufferWriter(ArraySegment<byte>[] buffers) : this(buffers[0], buffers[1])
{
}
public CircularBufferWriter(Span<byte> first, Span<byte> second)
{
_first = first;
_second = second;
Position = 0;
Length = first.Length + second.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(byte value)
{
if (Position < _first.Length)
{
_first[Position++] = value;
}
else if (Position < Length)
{
_second[Position++ - _first.Length] = value;
}
else
{
throw new OutOfMemoryException();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(bool value)
{
if (value)
{
Write((byte)1);
}
else
{
Write((byte)0);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(sbyte value)
{
Write((byte)value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(short value)
{
if (Position < _first.Length)
{
if (!BinaryPrimitives.TryWriteInt16BigEndian(_first.Slice(Position), value))
{
// Not enough space. Split the spans
Write((byte)(value >> 8));
Write((byte)value);
}
else
{
Position += 2;
}
}
else if (BinaryPrimitives.TryWriteInt16BigEndian(_second.Slice(Position - _first.Length), value))
{
Position += 2;
}
else
{
throw new OutOfMemoryException();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(ushort value)
{
if (Position < _first.Length)
{
if (!BinaryPrimitives.TryWriteUInt16BigEndian(_first.Slice(Position), value))
{
// Not enough space. Split the spans
Write((byte)(value >> 8));
Write((byte)value);
}
else
{
Position += 2;
}
}
else if (BinaryPrimitives.TryWriteUInt16BigEndian(_second.Slice(Position - _first.Length), value))
{
Position += 2;
}
else
{
throw new OutOfMemoryException();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(int value)
{
if (Position < _first.Length)
{
if (!BinaryPrimitives.TryWriteInt32BigEndian(_first.Slice(Position), value))
{
// Not enough space. Split the spans
Write((byte)(value >> 24));
Write((byte)(value >> 16));
Write((byte)(value >> 8));
Write((byte)value);
}
else
{
Position += 4;
}
}
else if (BinaryPrimitives.TryWriteInt32BigEndian(_second.Slice(Position - _first.Length), value))
{
Position += 4;
}
else
{
throw new OutOfMemoryException();
}
}
/// <summary>
/// Writes a 4-byte unsigned integer value to the underlying stream.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(uint value)
{
if (Position < _first.Length)
{
if (!BinaryPrimitives.TryWriteUInt32BigEndian(_first.Slice(Position), value))
{
// Not enough space. Split the spans
Write((byte)(value >> 24));
Write((byte)(value >> 16));
Write((byte)(value >> 8));
Write((byte)value);
}
else
{
Position += 4;
}
}
else if (BinaryPrimitives.TryWriteUInt32BigEndian(_second.Slice(Position - _first.Length), value))
{
Position += 4;
}
else
{
throw new OutOfMemoryException();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(ReadOnlySpan<byte> buffer)
{
if (Position + buffer.Length > Length)
{
throw new OutOfMemoryException();
}
if (Position < _first.Length)
{
var sz = Math.Min(buffer.Length, _first.Length - Position);
buffer.CopyTo(_first.Slice(Position));
buffer.Slice(sz).CopyTo(_second);
Position += buffer.Length;
}
else if (Position < Length)
{
buffer.CopyTo(_second.Slice(Position - _first.Length));
}
else
{
throw new OutOfMemoryException();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteString(ReadOnlySpan<char> value, Encoding encoding)
{
var byteCount = encoding.GetByteCount(value);
if (Position + byteCount > Length)
{
throw new OutOfMemoryException();
}
Span<byte> bytes = stackalloc byte[byteCount];
encoding.GetBytes(value, bytes);
int count;
if (Position < _first.Length)
{
count = Math.Min(_first.Length - Position, byteCount);
bytes.Slice(0, count).CopyTo(_first.Slice(Position));
byteCount -= count;
Position += count;
}
else
{
count = 0;
}
if (byteCount > 0)
{
bytes.Slice(count).CopyTo(_second.Slice(Math.Max(0, Position - _first.Length)));
Position += byteCount;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteLittleUni(string value, int fixedLength = -1)
{
if (fixedLength < 0) { fixedLength = value.Length; }
WriteString(value.AsSpan(0, Math.Min(fixedLength, value.Length)), Utility.UnicodeLE);
var count = fixedLength - value.Length;
if (count > 0)
{
Clear(count * 2);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteLittleUniNull(string value)
{
WriteString(value, Utility.UnicodeLE);
Write((ushort)0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBigUni(string value, int fixedLength = -1)
{
if (fixedLength < 0) { fixedLength = value.Length; }
WriteString(value.AsSpan(0, Math.Min(fixedLength, value.Length)), Utility.Unicode);
var count = fixedLength - value.Length;
if (count > 0)
{
Clear(count * 2);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBigUniNull(string value)
{
WriteString(value, Utility.Unicode);
Write((ushort)0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUTF8(string value) => WriteString(value, Utility.UTF8);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUTF8Null(string value)
{
WriteString(value, Utility.UTF8);
Write((byte)0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteAscii(string value, int fixedLength = -1)
{
if (fixedLength < 0) { fixedLength = value.Length; }
WriteString(value.AsSpan(0, Math.Min(fixedLength, value.Length)), Encoding.ASCII);
var count = fixedLength - value.Length;
if (count > 0)
{
Clear(count);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteAsciiNull(string value)
{
WriteString(value, Encoding.ASCII);
Write((byte)0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
if (Position < _first.Length)
{
_first.Slice(Position).Clear();
_second.Clear();
}
else
{
_second.Slice(Position - _first.Length).Clear();
}
Position = Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear(int amount)
{
if (Position + amount > Length)
{
throw new OutOfMemoryException();
}
int count;
if (Position < _first.Length)
{
count = Math.Min(amount, _first.Length - Position);
_first.Slice(Position, count).Clear();
count = amount - count;
}
else
{
count = amount;
}
if (count > 0)
{
_second.Slice(Position - _first.Length, count).Clear();
}
Position += amount;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Seek(int offset, SeekOrigin origin) =>
Position = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.End => Length - offset,
_ => Position + offset // Current
};
}
}

View file

@ -0,0 +1,187 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: SpanReader.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
namespace Server.Buffers
{
ref struct SpanReader
{
private readonly ReadOnlySpan<byte> _buffer;
public int Length { get; }
public int Position { get; private set; }
public int Remaining => Length - Position;
public SpanReader(ReadOnlySpan<byte> span)
{
_buffer = span;
Position = 0;
Length = span.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte()
{
if (Position >= Length)
{
throw new OutOfMemoryException();
}
return _buffer[Position++];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ReadBoolean() => ReadByte() > 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public sbyte ReadSByte() => (sbyte)ReadByte();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public short ReadInt16()
{
if (!BinaryPrimitives.TryReadInt16BigEndian(_buffer.Slice(Position), out var value))
{
throw new OutOfMemoryException();
}
Position += 2;
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ushort ReadUInt16()
{
if (!BinaryPrimitives.TryReadUInt16BigEndian(_buffer.Slice(Position), out var value))
{
throw new OutOfMemoryException();
}
Position += 2;
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int ReadInt32()
{
if (!BinaryPrimitives.TryReadInt32BigEndian(_buffer.Slice(Position), out var value))
{
throw new OutOfMemoryException();
}
Position += 4;
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint ReadUInt32()
{
if (!BinaryPrimitives.TryReadUInt32BigEndian(_buffer.Slice(Position), out var value))
{
throw new OutOfMemoryException();
}
Position += 4;
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadString(Encoding encoding, bool safeString = false, int fixedLength = -1)
{
int sizeT = Utility.GetByteLengthForEncoding(encoding);
bool isFixedLength = fixedLength > -1;
var remaining = Remaining;
int size;
if (isFixedLength)
{
size = fixedLength * sizeT;
if (size > Remaining)
{
throw new OutOfMemoryException();
}
}
else
{
size = remaining - (remaining & (sizeT - 1));
}
int index = Utility.IndexOfTerminator(_buffer.Slice(Position, size), sizeT);
Position += isFixedLength || index < 0 ? size : index + sizeT;
return Utility.GetString(_buffer.Slice(Position, index < 0 ? size : index), encoding, safeString);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadLittleUniSafe(int fixedLength) => ReadString(Utility.UnicodeLE, true, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadLittleUniSafe() => ReadString(Utility.UnicodeLE, true);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadLittleUni(int fixedLength) => ReadString(Utility.UnicodeLE, false, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadLittleUni() => ReadString(Utility.UnicodeLE);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadBigUniSafe(int fixedLength) => ReadString(Utility.Unicode, true, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadBigUniSafe() => ReadString(Utility.Unicode, true);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadBigUni(int fixedLength) => ReadString(Utility.Unicode, false, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadBigUni() => ReadString(Utility.Unicode);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadUTF8Safe(int fixedLength) => ReadString(Utility.UTF8, true, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadUTF8Safe() => ReadString(Utility.UTF8, true);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadUTF8() => ReadString(Utility.UTF8);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadAsciiSafe(int fixedLength) => ReadString(Encoding.ASCII, true, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadAsciiSafe() => ReadString(Encoding.ASCII, true);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadAscii(int fixedLength) => ReadString(Encoding.ASCII, false, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string ReadAscii() => ReadString(Encoding.ASCII);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Seek(int offset, SeekOrigin origin) =>
Position = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.End => Length - offset,
_ => Position + offset // Current
};
}
}

View file

@ -0,0 +1,244 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2020 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: SpanWriter.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Buffers.Binary;
using System.Data;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
namespace Server.Buffers
{
public ref struct SpanWriter
{
private readonly Span<byte> _buffer;
public int Length => _buffer.Length;
public ReadOnlySpan<byte> Span => _buffer.Slice(0, Position);
public int Position { get; private set; }
public SpanWriter(Span<byte> span)
{
_buffer = span;
Position = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void Write(bool value)
{
if (Position >= Length)
{
throw new OutOfMemoryException();
}
_buffer[Position++] = *(byte*) & value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(byte value)
{
_buffer[Position++] = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(sbyte value)
{
_buffer[Position++] = (byte)value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(short value)
{
if (!BinaryPrimitives.TryWriteInt16BigEndian(_buffer.Slice(Position), value))
{
throw new OutOfMemoryException();
}
Position += 2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(ushort value)
{
if (!BinaryPrimitives.TryWriteUInt16BigEndian(_buffer.Slice(Position), value))
{
throw new OutOfMemoryException();
}
Position += 2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(int value)
{
if (!BinaryPrimitives.TryWriteInt32BigEndian(_buffer.Slice(Position), value))
{
throw new OutOfMemoryException();
}
Position += 4;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(uint value)
{
if (!BinaryPrimitives.TryWriteUInt32BigEndian(_buffer.Slice(Position), value))
{
throw new OutOfMemoryException();
}
Position += 4;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(long value)
{
if (!BinaryPrimitives.TryWriteInt64BigEndian(_buffer.Slice(Position), value))
{
throw new OutOfMemoryException();
}
Position += 8;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(ulong value)
{
if (!BinaryPrimitives.TryWriteUInt64BigEndian(_buffer.Slice(Position), value))
{
throw new OutOfMemoryException();
}
Position += 8;
}
public void Write(ReadOnlySpan<byte> buffer)
{
var size = buffer.Length;
if (Position + size > Length)
{
throw new OutOfMemoryException();
}
buffer.Slice(0, size).CopyTo(_buffer.Slice(Position));
Position += size;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteString<T>(string value, Encoding encoding, int fixedLength = -1) where T : struct, IEquatable<T>
{
int sizeT = Unsafe.SizeOf<T>();
if (sizeT > 2)
{
throw new InvalidConstraintException("WriteString only accepts byte, sbyte, char, short, and ushort as a constraint");
}
value ??= string.Empty;
var charLength = Math.Min(fixedLength > -1 ? fixedLength : value.Length, value.Length);
var src = value.AsSpan(0, charLength);
var byteCount = fixedLength > -1 ? fixedLength * sizeT : encoding.GetByteCount(value);
if (Position + byteCount > Length)
{
throw new OutOfMemoryException();
}
Position += encoding.GetBytes(src, _buffer.Slice(Position));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteLittleUni(string value) => WriteString<char>(value, Utility.UnicodeLE);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteLittleUniNull(string value)
{
WriteString<char>(value, Utility.UnicodeLE);
Write((ushort)0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteLittleUni(string value, int fixedLength) => WriteString<char>(value, Utility.UnicodeLE, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBigUni(string value) => WriteString<char>(value, Utility.Unicode);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBigUniNull(string value)
{
WriteString<char>(value, Utility.Unicode);
Write((ushort)0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBigUni(string value, int fixedLength) => WriteString<char>(value, Utility.Unicode, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUTF8(string value) => WriteString<byte>(value, Utility.UTF8);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUTF8Null(string value)
{
WriteString<byte>(value, Utility.UTF8);
Write((byte)0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteAscii(string value) => WriteString<byte>(value, Encoding.ASCII);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteAsciiNull(string value)
{
WriteString<byte>(value, Encoding.ASCII);
Write((byte)0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteAscii(string value, int fixedLength) => WriteString<byte>(value, Encoding.ASCII, fixedLength);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
_buffer.Slice(Position).Clear();
Position = Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear(int amount)
{
if (Position + amount > Length)
{
throw new OutOfMemoryException();
}
_buffer.Slice(Position, amount).Clear();
Position += amount;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Seek(int offset, SeekOrigin origin) =>
Position = origin switch
{
SeekOrigin.Begin => offset,
SeekOrigin.End => Length - offset,
_ => Position + offset // Current
};
}
}

View file

@ -2,9 +2,9 @@ namespace Server.Network
{
public ref struct EncodedReader
{
private PacketReader m_Reader;
private CircularBufferReader m_Reader;
public EncodedReader(PacketReader reader) => m_Reader = reader;
public EncodedReader(CircularBufferReader reader) => m_Reader = reader;
public void Trace(NetState state)
{

View file

@ -14,6 +14,7 @@
*************************************************************************/
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;

View file

@ -227,22 +227,22 @@ namespace Server.Network
m_CompiledLength = length;
}
if (length > 0)
if (m_CompiledLength > 0)
{
var old = m_CompiledBuffer;
if ((m_State & State.Static) != 0)
{
m_CompiledBuffer = new byte[length];
m_CompiledBuffer = new byte[m_CompiledLength];
}
else
{
// Release it later using Release()
m_CompiledBuffer = ArrayPool<byte>.Shared.Rent(length);
m_CompiledBuffer = ArrayPool<byte>.Shared.Rent(m_CompiledLength);
m_State |= State.Buffered;
}
Buffer.BlockCopy(old, 0, m_CompiledBuffer, 0, length);
Buffer.BlockCopy(old, 0, m_CompiledBuffer, 0, m_CompiledLength);
if (compress)
{

View file

@ -2,7 +2,7 @@ using System;
namespace Server.Network
{
public delegate void OnPacketReceive(NetState state, PacketReader reader);
public delegate void OnPacketReceive(NetState state, CircularBufferReader reader);
public delegate TimeSpan ThrottlePacketCallback(NetState state);

View file

@ -283,7 +283,7 @@ namespace Server.Network
public static int ProcessPacket(NetState ns, ArraySegment<byte>[] segments)
{
var reader = new PacketReader(segments);
var reader = new CircularBufferReader(segments);
var packetId = reader.ReadByte();
@ -360,11 +360,11 @@ namespace Server.Network
return packetLength;
}
private static void UnhandledBF(NetState state, PacketReader reader)
private static void UnhandledBF(NetState state, CircularBufferReader reader)
{
}
public static void Empty(NetState state, PacketReader reader)
public static void Empty(NetState state, CircularBufferReader reader)
{
}
@ -383,7 +383,7 @@ namespace Server.Network
EventSink.InvokeQuestGumpRequest(state.Mobile);
}
public static void EncodedCommand(NetState state, PacketReader reader)
public static void EncodedCommand(NetState state, CircularBufferReader reader)
{
var e = World.FindEntity(reader.ReadUInt32());
int packetId = reader.ReadUInt16();
@ -415,7 +415,7 @@ namespace Server.Network
}
}
public static void RenameRequest(NetState state, PacketReader reader)
public static void RenameRequest(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
var targ = World.FindMobile(reader.ReadUInt32());
@ -426,7 +426,7 @@ namespace Server.Network
}
}
public static void SecureTrade(NetState state, PacketReader reader)
public static void SecureTrade(NetState state, CircularBufferReader reader)
{
switch (reader.ReadByte())
{
@ -498,7 +498,7 @@ namespace Server.Network
}
}
public static void VendorBuyReply(NetState state, PacketReader reader)
public static void VendorBuyReply(NetState state, CircularBufferReader reader)
{
var vendor = World.FindMobile(reader.ReadUInt32());
var flag = reader.ReadByte();
@ -545,7 +545,7 @@ namespace Server.Network
}
}
public static void VendorSellReply(NetState state, PacketReader reader)
public static void VendorSellReply(NetState state, CircularBufferReader reader)
{
Serial serial = reader.ReadUInt32();
var vendor = World.FindMobile(serial);
@ -587,7 +587,7 @@ namespace Server.Network
}
}
public static void DeleteCharacter(NetState state, PacketReader reader)
public static void DeleteCharacter(NetState state, CircularBufferReader reader)
{
reader.Seek(30, SeekOrigin.Current);
var index = reader.ReadInt32();
@ -595,12 +595,12 @@ namespace Server.Network
EventSink.InvokeDeleteRequest(state, index);
}
public static void DeathStatusResponse(NetState state, PacketReader reader)
public static void DeathStatusResponse(NetState state, CircularBufferReader reader)
{
// Ignored
}
public static void ObjectHelpRequest(NetState state, PacketReader reader)
public static void ObjectHelpRequest(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -629,7 +629,7 @@ namespace Server.Network
}
}
public static void MobileNameRequest(NetState state, PacketReader reader)
public static void MobileNameRequest(NetState state, CircularBufferReader reader)
{
var m = World.FindMobile(reader.ReadUInt32());
@ -639,13 +639,13 @@ namespace Server.Network
}
}
public static void RequestScrollWindow(NetState state, PacketReader reader)
public static void RequestScrollWindow(NetState state, CircularBufferReader reader)
{
int lastTip = reader.ReadInt16();
int type = reader.ReadByte();
}
public static void AttackReq(NetState state, PacketReader reader)
public static void AttackReq(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
var m = World.FindMobile(reader.ReadUInt32());
@ -656,7 +656,7 @@ namespace Server.Network
}
}
public static void HuePickerResponse(NetState state, PacketReader reader)
public static void HuePickerResponse(NetState state, CircularBufferReader reader)
{
var serial = reader.ReadUInt32();
_ = reader.ReadInt16(); // Item ID
@ -677,7 +677,7 @@ namespace Server.Network
}
}
public static void SystemInfo(NetState state, PacketReader reader)
public static void SystemInfo(NetState state, CircularBufferReader reader)
{
int v1 = reader.ReadByte();
int v2 = reader.ReadUInt16();
@ -693,11 +693,11 @@ namespace Server.Network
var v8 = reader.ReadInt32();
}
public static void AccountID(NetState state, PacketReader reader)
public static void AccountID(NetState state, CircularBufferReader reader)
{
}
public static void TextCommand(NetState state, PacketReader reader)
public static void TextCommand(NetState state, CircularBufferReader reader)
{
int type = reader.ReadByte();
var command = reader.ReadAscii();
@ -790,7 +790,7 @@ namespace Server.Network
}
}
public static void AsciiPromptResponse(NetState state, PacketReader reader)
public static void AsciiPromptResponse(NetState state, CircularBufferReader reader)
{
var serial = reader.ReadUInt32();
var prompt = reader.ReadInt32();
@ -820,7 +820,7 @@ namespace Server.Network
}
}
public static void UnicodePromptResponse(NetState state, PacketReader reader)
public static void UnicodePromptResponse(NetState state, CircularBufferReader reader)
{
var serial = reader.ReadUInt32();
var prompt = reader.ReadInt32();
@ -851,7 +851,7 @@ namespace Server.Network
}
}
public static void MenuResponse(NetState state, PacketReader reader)
public static void MenuResponse(NetState state, CircularBufferReader reader)
{
var serial = reader.ReadUInt32();
int menuID = reader.ReadInt16(); // unused in our implementation
@ -881,7 +881,7 @@ namespace Server.Network
}
}
public static void ProfileReq(NetState state, PacketReader reader)
public static void ProfileReq(NetState state, CircularBufferReader reader)
{
int type = reader.ReadByte();
Serial serial = reader.ReadUInt32();
@ -921,12 +921,12 @@ namespace Server.Network
}
}
public static void Disconnect(NetState state, PacketReader reader)
public static void Disconnect(NetState state, CircularBufferReader reader)
{
var minusOne = reader.ReadInt32();
}
public static void LiftReq(NetState state, PacketReader reader)
public static void LiftReq(NetState state, CircularBufferReader reader)
{
Serial serial = reader.ReadUInt32();
int amount = reader.ReadUInt16();
@ -935,7 +935,7 @@ namespace Server.Network
state.Mobile.Lift(item, amount, out var rejected, out var reject);
}
public static void EquipReq(NetState state, PacketReader reader)
public static void EquipReq(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
var item = from.Holding;
@ -960,7 +960,7 @@ namespace Server.Network
item.ClearBounce();
}
public static void DropReq(NetState state, PacketReader reader)
public static void DropReq(NetState state, CircularBufferReader reader)
{
reader.ReadInt32(); // serial, ignored
int x = reader.ReadInt16();
@ -997,7 +997,7 @@ namespace Server.Network
}
}
public static void DropReq6017(NetState state, PacketReader reader)
public static void DropReq6017(NetState state, CircularBufferReader reader)
{
reader.ReadInt32(); // serial, ignored
int x = reader.ReadInt16();
@ -1035,28 +1035,28 @@ namespace Server.Network
}
}
public static void ConfigurationFile(NetState state, PacketReader reader)
public static void ConfigurationFile(NetState state, CircularBufferReader reader)
{
}
public static void LogoutReq(NetState state, PacketReader reader)
public static void LogoutReq(NetState state, CircularBufferReader reader)
{
state.Send(new LogoutAck());
}
public static void ChangeSkillLock(NetState state, PacketReader reader)
public static void ChangeSkillLock(NetState state, CircularBufferReader reader)
{
var s = state.Mobile.Skills[reader.ReadInt16()];
s?.SetLockNoRelay((SkillLock)reader.ReadByte());
}
public static void HelpRequest(NetState state, PacketReader reader)
public static void HelpRequest(NetState state, CircularBufferReader reader)
{
EventSink.InvokeHelpRequest(state.Mobile);
}
public static void TargetResponse(NetState state, PacketReader reader)
public static void TargetResponse(NetState state, CircularBufferReader reader)
{
int type = reader.ReadByte();
var targetID = reader.ReadInt32();
@ -1170,7 +1170,7 @@ namespace Server.Network
}
}
public static void DisplayGumpResponse(NetState state, PacketReader reader)
public static void DisplayGumpResponse(NetState state, CircularBufferReader reader)
{
var serial = reader.ReadUInt32();
var typeID = reader.ReadInt32();
@ -1292,12 +1292,12 @@ namespace Server.Network
}
}
public static void SetWarMode(NetState state, PacketReader reader)
public static void SetWarMode(NetState state, CircularBufferReader reader)
{
state.Mobile.DelayChangeWarmode(reader.ReadBoolean());
}
public static void Resynchronize(NetState state, PacketReader reader)
public static void Resynchronize(NetState state, CircularBufferReader reader)
{
var m = state.Mobile;
@ -1319,7 +1319,7 @@ namespace Server.Network
m.ClearFastwalkStack();
}
public static void AsciiSpeech(NetState state, PacketReader reader)
public static void AsciiSpeech(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -1341,7 +1341,7 @@ namespace Server.Network
from.DoSpeech(text, m_EmptyInts, type, Utility.ClipDyedHue(hue));
}
public static void UnicodeSpeech(NetState state, PacketReader reader)
public static void UnicodeSpeech(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -1420,7 +1420,7 @@ namespace Server.Network
from.DoSpeech(text, keywords, type, Utility.ClipDyedHue(hue));
}
public static void UseReq(NetState state, PacketReader reader)
public static void UseReq(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -1464,7 +1464,7 @@ namespace Server.Network
}
}
public static void LookReq(NetState state, PacketReader reader)
public static void LookReq(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -1513,17 +1513,17 @@ namespace Server.Network
}
}
public static void PingReq(NetState state, PacketReader reader)
public static void PingReq(NetState state, CircularBufferReader reader)
{
state.Send(PingAck.Instantiate(reader.ReadByte()));
}
public static void SetUpdateRange(NetState state, PacketReader reader)
public static void SetUpdateRange(NetState state, CircularBufferReader reader)
{
state.Send(ChangeUpdateRange.Instantiate(18));
}
public static void MovementReq(NetState state, PacketReader reader)
public static void MovementReq(NetState state, CircularBufferReader reader)
{
var dir = (Direction)reader.ReadByte();
int seq = reader.ReadByte();
@ -1551,7 +1551,7 @@ namespace Server.Network
}
}
public static void Animate(NetState state, PacketReader reader)
public static void Animate(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
var action = reader.ReadInt32();
@ -1569,7 +1569,7 @@ namespace Server.Network
}
}
public static void QuestArrow(NetState state, PacketReader reader)
public static void QuestArrow(NetState state, CircularBufferReader reader)
{
var rightClick = reader.ReadBoolean();
var from = state.Mobile;
@ -1577,7 +1577,7 @@ namespace Server.Network
from?.QuestArrow?.OnClick(rightClick);
}
public static void ExtendedCommand(NetState state, PacketReader reader)
public static void ExtendedCommand(NetState state, CircularBufferReader reader)
{
int packetID = reader.ReadUInt16();
@ -1607,7 +1607,7 @@ namespace Server.Network
}
}
public static void CastSpell(NetState state, PacketReader reader)
public static void CastSpell(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -1628,7 +1628,7 @@ namespace Server.Network
EventSink.InvokeCastSpellRequest(from, spellID, spellbook);
}
public static void BandageTarget(NetState state, PacketReader reader)
public static void BandageTarget(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -1663,12 +1663,12 @@ namespace Server.Network
}
}
public static void ToggleFlying(NetState state, PacketReader reader)
public static void ToggleFlying(NetState state, CircularBufferReader reader)
{
state.Mobile.ToggleFlying();
}
public static void BatchQueryProperties(NetState state, PacketReader reader)
public static void BatchQueryProperties(NetState state, CircularBufferReader reader)
{
if (!ObjectPropertyList.Enabled)
{
@ -1710,7 +1710,7 @@ namespace Server.Network
}
}
public static void QueryProperties(NetState state, PacketReader reader)
public static void QueryProperties(NetState state, CircularBufferReader reader)
{
if (!ObjectPropertyList.Enabled)
{
@ -1742,7 +1742,7 @@ namespace Server.Network
}
}
public static void PartyMessage(NetState state, PacketReader reader)
public static void PartyMessage(NetState state, CircularBufferReader reader)
{
if (state.Mobile == null)
{
@ -1778,17 +1778,17 @@ namespace Server.Network
}
}
public static void PartyMessage_AddMember(NetState state, PacketReader reader)
public static void PartyMessage_AddMember(NetState state, CircularBufferReader reader)
{
PartyCommands.Handler?.OnAdd(state.Mobile);
}
public static void PartyMessage_RemoveMember(NetState state, PacketReader reader)
public static void PartyMessage_RemoveMember(NetState state, CircularBufferReader reader)
{
PartyCommands.Handler?.OnRemove(state.Mobile, World.FindMobile(reader.ReadUInt32()));
}
public static void PartyMessage_PrivateMessage(NetState state, PacketReader reader)
public static void PartyMessage_PrivateMessage(NetState state, CircularBufferReader reader)
{
PartyCommands.Handler?.OnPrivateMessage(
state.Mobile,
@ -1797,37 +1797,37 @@ namespace Server.Network
);
}
public static void PartyMessage_PublicMessage(NetState state, PacketReader reader)
public static void PartyMessage_PublicMessage(NetState state, CircularBufferReader reader)
{
PartyCommands.Handler?.OnPublicMessage(state.Mobile, reader.ReadBigUniSafe());
}
public static void PartyMessage_SetCanLoot(NetState state, PacketReader reader)
public static void PartyMessage_SetCanLoot(NetState state, CircularBufferReader reader)
{
PartyCommands.Handler?.OnSetCanLoot(state.Mobile, reader.ReadBoolean());
}
public static void PartyMessage_Accept(NetState state, PacketReader reader)
public static void PartyMessage_Accept(NetState state, CircularBufferReader reader)
{
PartyCommands.Handler?.OnAccept(state.Mobile, World.FindMobile(reader.ReadUInt32()));
}
public static void PartyMessage_Decline(NetState state, PacketReader reader)
public static void PartyMessage_Decline(NetState state, CircularBufferReader reader)
{
PartyCommands.Handler?.OnDecline(state.Mobile, World.FindMobile(reader.ReadUInt32()));
}
public static void StunRequest(NetState state, PacketReader reader)
public static void StunRequest(NetState state, CircularBufferReader reader)
{
EventSink.InvokeStunRequest(state.Mobile);
}
public static void DisarmRequest(NetState state, PacketReader reader)
public static void DisarmRequest(NetState state, CircularBufferReader reader)
{
EventSink.InvokeDisarmRequest(state.Mobile);
}
public static void StatLockChange(NetState state, PacketReader reader)
public static void StatLockChange(NetState state, CircularBufferReader reader)
{
int stat = reader.ReadByte();
int lockValue = reader.ReadByte();
@ -1856,13 +1856,13 @@ namespace Server.Network
}
}
public static void ScreenSize(NetState state, PacketReader reader)
public static void ScreenSize(NetState state, CircularBufferReader reader)
{
var width = reader.ReadInt32();
var unk = reader.ReadInt32();
}
public static void ContextMenuResponse(NetState state, PacketReader reader)
public static void ContextMenuResponse(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -1918,7 +1918,7 @@ namespace Server.Network
}
}
public static void ContextMenuRequest(NetState state, PacketReader reader)
public static void ContextMenuRequest(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
var target = World.FindEntity(reader.ReadUInt32());
@ -1962,12 +1962,12 @@ namespace Server.Network
}
}
public static void CloseStatus(NetState state, PacketReader reader)
public static void CloseStatus(NetState state, CircularBufferReader reader)
{
Serial serial = reader.ReadUInt32();
}
public static void Language(NetState state, PacketReader reader)
public static void Language(NetState state, CircularBufferReader reader)
{
var lang = reader.ReadAscii(4);
@ -1977,20 +1977,20 @@ namespace Server.Network
}
}
public static void AssistVersion(NetState state, PacketReader reader)
public static void AssistVersion(NetState state, CircularBufferReader reader)
{
var unk = reader.ReadInt32();
var av = reader.ReadAscii();
}
public static void ClientVersion(NetState state, PacketReader reader)
public static void ClientVersion(NetState state, CircularBufferReader reader)
{
var version = state.Version = new CV(reader.ReadAscii());
EventSink.InvokeClientVersionReceived(state, version);
}
public static void ClientType(NetState state, PacketReader reader)
public static void ClientType(NetState state, CircularBufferReader reader)
{
reader.ReadUInt16();
@ -2000,7 +2000,7 @@ namespace Server.Network
EventSink.InvokeClientVersionReceived(state, version);
}
public static void MobileQuery(NetState state, PacketReader reader)
public static void MobileQuery(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -2031,7 +2031,7 @@ namespace Server.Network
}
}
public static void PlayCharacter(NetState state, PacketReader reader)
public static void PlayCharacter(NetState state, CircularBufferReader reader)
{
reader.ReadInt32(); // 0xEDEDEDED
@ -2093,7 +2093,7 @@ namespace Server.Network
}
}
public static void ShowPublicHouseContent(NetState state, PacketReader reader)
public static void ShowPublicHouseContent(NetState state, CircularBufferReader reader)
{
var showPublicHouseContent = reader.ReadBoolean();
}
@ -2201,7 +2201,7 @@ namespace Server.Network
m.ClearFastwalkStack();
}
public static void CreateCharacter(NetState state, PacketReader reader)
public static void CreateCharacter(NetState state, CircularBufferReader reader)
{
var unk1 = reader.ReadInt32();
var unk2 = reader.ReadInt32();
@ -2337,7 +2337,7 @@ namespace Server.Network
}
}
public static void CreateCharacter70160(NetState state, PacketReader reader)
public static void CreateCharacter70160(NetState state, CircularBufferReader reader)
{
var unk1 = reader.ReadInt32();
var unk2 = reader.ReadInt32();
@ -2499,7 +2499,7 @@ namespace Server.Network
return authID;
}
public static void GameLogin(NetState state, PacketReader reader)
public static void GameLogin(NetState state, CircularBufferReader reader)
{
if (state.SentFirstPacket)
{
@ -2567,7 +2567,7 @@ namespace Server.Network
}
}
public static void PlayServer(NetState state, PacketReader reader)
public static void PlayServer(NetState state, CircularBufferReader reader)
{
int index = reader.ReadInt16();
var info = state.ServerInfo;
@ -2588,7 +2588,7 @@ namespace Server.Network
}
}
public static void LoginServerSeed(NetState state, PacketReader reader)
public static void LoginServerSeed(NetState state, CircularBufferReader reader)
{
state.m_Seed = reader.ReadInt32();
state.Seeded = true;
@ -2608,7 +2608,7 @@ namespace Server.Network
state.Version = new ClientVersion(clientMaj, clientMin, clientRev, clientPat);
}
public static void CrashReport(NetState state, PacketReader reader)
public static void CrashReport(NetState state, CircularBufferReader reader)
{
var clientMaj = reader.ReadByte();
var clientMin = reader.ReadByte();
@ -2642,7 +2642,7 @@ namespace Server.Network
}
}
public static void AccountLogin(NetState state, PacketReader reader)
public static void AccountLogin(NetState state, CircularBufferReader reader)
{
if (state.SentFirstPacket)
{
@ -2696,7 +2696,7 @@ namespace Server.Network
state.Dispose();
}
public static void EquipMacro(NetState state, PacketReader reader)
public static void EquipMacro(NetState state, CircularBufferReader reader)
{
int count = reader.ReadByte();
var serialList = new List<Serial>(count);
@ -2708,7 +2708,7 @@ namespace Server.Network
EventSink.InvokeEquipMacro(state.Mobile, serialList);
}
public static void UnequipMacro(NetState state, PacketReader reader)
public static void UnequipMacro(NetState state, CircularBufferReader reader)
{
int count = reader.ReadByte();
var layers = new List<Layer>(count);
@ -2720,21 +2720,21 @@ namespace Server.Network
EventSink.InvokeUnequipMacro(state.Mobile, layers);
}
public static void TargetedSpell(NetState state, PacketReader reader)
public static void TargetedSpell(NetState state, CircularBufferReader reader)
{
var spellId = (short)(reader.ReadInt16() - 1); // zero based;
EventSink.InvokeTargetedSpell(state.Mobile, World.FindEntity(reader.ReadUInt32()), spellId);
}
public static void TargetedSkillUse(NetState state, PacketReader reader)
public static void TargetedSkillUse(NetState state, CircularBufferReader reader)
{
var skillId = reader.ReadInt16();
EventSink.InvokeTargetedSkillUse(state.Mobile, World.FindEntity(reader.ReadUInt32()), skillId);
}
public static void TargetByResourceMacro(NetState state, PacketReader reader)
public static void TargetByResourceMacro(NetState state, CircularBufferReader reader)
{
Serial serial = reader.ReadUInt32();

View file

@ -118,55 +118,55 @@ namespace Server.Network
return result;
}
public void Advance(uint bytes)
public void Advance(uint count)
{
var read = _pipe._readIdx;
var write = _pipe._writeIdx;
if (bytes == 0)
if (count == 0)
{
return;
}
if (bytes > _pipe.Size - 1)
if (count > _pipe.Size - 1)
{
throw new InvalidOperationException();
}
if (read <= write)
{
if (bytes > read + _pipe.Size - write - 1)
if (count > read + _pipe.Size - write - 1)
{
throw new InvalidOperationException();
}
var sz = Math.Min(bytes, _pipe.Size - write);
var sz = Math.Min(count, _pipe.Size - write);
write += sz;
if (write > _pipe.Size - 1)
{
write = 0;
}
bytes -= sz;
count -= sz;
if (bytes > 0)
if (count > 0)
{
if (bytes >= read)
if (count >= read)
{
throw new InvalidOperationException();
}
write = bytes;
write = count;
}
}
else
{
if (bytes > read - write - 1)
if (count > read - write - 1)
{
throw new InvalidOperationException();
}
write += bytes;
write += count;
}
// It's never valid to advance the write pointer to become equal to
@ -216,7 +216,7 @@ namespace Server.Network
internal PipeReader(Pipe<T> pipe) => _pipe = pipe;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetRemaining()
public uint GetAvailable()
{
var read = _pipe._readIdx;
var write = _pipe._writeIdx;
@ -260,39 +260,39 @@ namespace Server.Network
return this;
}
public void Advance(uint bytes)
public void Advance(uint count)
{
var read = _pipe._readIdx;
var write = _pipe._writeIdx;
if (read <= write)
{
if (bytes > write - read)
if (count > write - read)
{
throw new InvalidOperationException();
}
read += bytes;
read += count;
}
else
{
var sz = Math.Min(bytes, _pipe.Size - read);
var sz = Math.Min(count, _pipe.Size - read);
read += sz;
if (read > _pipe.Size - 1)
{
read = 0;
}
bytes -= sz;
count -= sz;
if (bytes > 0)
if (count > 0)
{
if (bytes > write)
if (count > write)
{
throw new InvalidOperationException();
}
read = bytes;
read = count;
}
}
@ -309,7 +309,7 @@ namespace Server.Network
{
get
{
if (GetRemaining() > 0)
if (GetAvailable() > 0)
{
return true;
}

View file

@ -6,6 +6,7 @@ using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using Server.Random;
@ -106,6 +107,55 @@ namespace Server
public static Encoding Unicode => m_Unicode ??= new UnicodeEncoding(true, false, false);
public static Encoding UnicodeLE => m_UnicodeLE ??= new UnicodeEncoding(false, false, false);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetByteLengthForEncoding(Encoding encoding) =>
encoding.BodyName switch
{
"utf-16BE" => 2,
"utf-16" => 2,
"utf-32BE" => 4,
"utf-32" => 4,
_ => 1
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int IndexOfTerminator(ReadOnlySpan<byte> buffer, int sizeT) =>
sizeT switch
{
2 => MemoryMarshal.Cast<byte, char>(buffer).IndexOf((char)0) * 2,
4 => MemoryMarshal.Cast<byte, uint>(buffer).IndexOf((uint)0) * 4,
_ => buffer.IndexOf((byte)0)
};
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsSafeChar(ushort c) => c >= 0x20 && c < 0xFFFE;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetString(ReadOnlySpan<byte> span, Encoding encoding, bool safeString = false)
{
string s = encoding.GetString(span);
if (!safeString)
{
return s;
}
ReadOnlySpan<char> chars = s.AsSpan();
StringBuilder stringBuilder = null;
for (int i = 0, last = 0; i < chars.Length; i++)
{
if (!IsSafeChar(chars[i]) || stringBuilder != null && i == chars.Length - 1)
{
(stringBuilder ??= new StringBuilder()).Append(chars.Slice(last, i - last));
last = i + 1; // Skip the unsafe char
}
}
return stringBuilder?.ToString() ?? s;
}
public static void Separate(StringBuilder sb, string value, string separator)
{
if (sb.Length > 0)

View file

@ -23,7 +23,7 @@ namespace Server.Engines.Chat
to?.Send(new ChatMessagePacket(null, (int)type + 20, param1, param2));
}
public static void OpenChatWindowRequest(NetState state, PacketReader reader)
public static void OpenChatWindowRequest(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -56,7 +56,7 @@ namespace Server.Engines.Chat
return user;
}
public static void ChatAction(NetState state, PacketReader reader)
public static void ChatAction(NetState state, CircularBufferReader reader)
{
if (!Enabled)
{

View file

@ -193,7 +193,7 @@ namespace Server.Engines.MLQuests.Gumps
return false;
}
private static void RaceChangeReply(NetState state, PacketReader reader)
private static void RaceChangeReply(NetState state, CircularBufferReader reader)
{
if (!m_Pending.TryGetValue(state, out var raceChangeState))
{

View file

@ -366,7 +366,7 @@ namespace Server.Items
PacketHandlers.Register(0x93, 99, true, OldHeaderChange);
}
public static void OldHeaderChange(NetState state, PacketReader reader)
public static void OldHeaderChange(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -385,7 +385,7 @@ namespace Server.Items
book.Author = Utility.FixHtml(author);
}
public static void HeaderChange(NetState state, PacketReader reader)
public static void HeaderChange(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -419,7 +419,7 @@ namespace Server.Items
book.Author = Utility.FixHtml(author);
}
public static void ContentChange(NetState state, PacketReader reader)
public static void ContentChange(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;

View file

@ -2,7 +2,7 @@ using Server.Network;
namespace Server.Engines.Mahjong
{
public delegate void OnMahjongPacketReceive(MahjongGame game, NetState state, PacketReader reader);
public delegate void OnMahjongPacketReceive(MahjongGame game, NetState state, CircularBufferReader reader);
public sealed class MahjongPacketHandlers
{
@ -41,7 +41,7 @@ namespace Server.Engines.Mahjong
RegisterSubCommand(0x18, MoveDealerIndicator);
}
public static void OnPacket(NetState state,PacketReader reader)
public static void OnPacket(NetState state,CircularBufferReader reader)
{
var game = World.FindItem(reader.ReadUInt32()) as MahjongGame;
@ -85,7 +85,7 @@ namespace Server.Engines.Mahjong
};
}
public static void ExitGame(MahjongGame game, NetState state, PacketReader reader)
public static void ExitGame(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game == null)
{
@ -97,7 +97,7 @@ namespace Server.Engines.Mahjong
game.Players.LeaveGame(from);
}
public static void GivePoints(MahjongGame game, NetState state, PacketReader reader)
public static void GivePoints(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGamePlayer(state.Mobile) != true)
{
@ -110,7 +110,7 @@ namespace Server.Engines.Mahjong
game.Players.TransferScore(state.Mobile, to, amount);
}
public static void RollDice(MahjongGame game, NetState state, PacketReader reader)
public static void RollDice(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGamePlayer(state.Mobile) != true)
{
@ -120,7 +120,7 @@ namespace Server.Engines.Mahjong
game.Dices.RollDices(state.Mobile);
}
public static void BuildWalls(MahjongGame game, NetState state, PacketReader reader)
public static void BuildWalls(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGameDealer(state.Mobile) != true)
{
@ -130,7 +130,7 @@ namespace Server.Engines.Mahjong
game.ResetWalls(state.Mobile);
}
public static void ResetScores(MahjongGame game, NetState state, PacketReader reader)
public static void ResetScores(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGameDealer(state.Mobile) != true)
{
@ -140,7 +140,7 @@ namespace Server.Engines.Mahjong
game.Players.ResetScores(MahjongGame.BaseScore);
}
public static void AssignDealer(MahjongGame game, NetState state, PacketReader reader)
public static void AssignDealer(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGameDealer(state.Mobile) != true)
{
@ -152,7 +152,7 @@ namespace Server.Engines.Mahjong
game.Players.AssignDealer(position);
}
public static void OpenSeat(MahjongGame game, NetState state, PacketReader reader)
public static void OpenSeat(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGameDealer(state.Mobile) != true)
{
@ -169,7 +169,7 @@ namespace Server.Engines.Mahjong
game.Players.OpenSeat(position);
}
public static void ChangeOption(MahjongGame game, NetState state, PacketReader reader)
public static void ChangeOption(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGameDealer(state.Mobile) != true)
{
@ -185,7 +185,7 @@ namespace Server.Engines.Mahjong
game.SpectatorVision = (options & 0x2) != 0;
}
public static void MoveWallBreakIndicator(MahjongGame game, NetState state, PacketReader reader)
public static void MoveWallBreakIndicator(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGameDealer(state.Mobile) != true)
{
@ -198,7 +198,7 @@ namespace Server.Engines.Mahjong
game.WallBreakIndicator.Move(new Point2D(x, y));
}
public static void TogglePublicHand(MahjongGame game, NetState state, PacketReader reader)
public static void TogglePublicHand(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGamePlayer(state.Mobile) != true)
{
@ -213,7 +213,7 @@ namespace Server.Engines.Mahjong
game.Players.SetPublic(game.Players.GetPlayerIndex(state.Mobile), publicHand);
}
public static void MoveTile(MahjongGame game, NetState state, PacketReader reader)
public static void MoveTile(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGamePlayer(state.Mobile) != true)
{
@ -248,7 +248,7 @@ namespace Server.Engines.Mahjong
game.Tiles[number].Move(new Point2D(x, y), direction, flip, game.Players.GetPlayerIndex(state.Mobile));
}
public static void MoveDealerIndicator(MahjongGame game, NetState state, PacketReader reader)
public static void MoveDealerIndicator(MahjongGame game, NetState state, CircularBufferReader reader)
{
if (game?.Players.IsInGameDealer(state.Mobile) != true)
{

View file

@ -334,7 +334,7 @@ namespace Server.Items
PacketHandlers.Register(0x56, 11, true, OnMapCommand);
}
private static void OnMapCommand(NetState state, PacketReader reader)
private static void OnMapCommand(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;

View file

@ -232,7 +232,7 @@ namespace Server.Items
PacketHandlers.Register(0x71, 0, true, BBClientRequest);
}
public static void BBClientRequest(NetState state, PacketReader reader)
public static void BBClientRequest(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
@ -260,7 +260,7 @@ namespace Server.Items
}
}
public static void BBRequestContent(Mobile from, BaseBulletinBoard board, PacketReader reader)
public static void BBRequestContent(Mobile from, BaseBulletinBoard board, CircularBufferReader reader)
{
if (!(World.FindItem(reader.ReadUInt32()) is BulletinMessage msg) || msg.Parent != board)
{
@ -270,7 +270,7 @@ namespace Server.Items
from.Send(new BBMessageContent(board, msg));
}
public static void BBRequestHeader(Mobile from, BaseBulletinBoard board, PacketReader reader)
public static void BBRequestHeader(Mobile from, BaseBulletinBoard board, CircularBufferReader reader)
{
if (!(World.FindItem(reader.ReadUInt32()) is BulletinMessage msg) || msg.Parent != board)
{
@ -280,7 +280,7 @@ namespace Server.Items
from.Send(new BBMessageHeader(board, msg));
}
public static void BBPostMessage(Mobile from, BaseBulletinBoard board, PacketReader reader)
public static void BBPostMessage(Mobile from, BaseBulletinBoard board, CircularBufferReader reader)
{
var thread = World.FindItem(reader.ReadUInt32()) as BulletinMessage;
@ -337,7 +337,7 @@ namespace Server.Items
board.PostMessage(from, thread, subject, lines);
}
public static void BBRemoveMessage(Mobile from, BaseBulletinBoard board, PacketReader reader)
public static void BBRemoveMessage(Mobile from, BaseBulletinBoard board, CircularBufferReader reader)
{
if (!(World.FindItem(reader.ReadUInt32()) is BulletinMessage msg) || msg.Parent != board)
{

View file

@ -142,7 +142,7 @@ namespace Server
}
}
public static void OnReceive(NetState state, PacketReader reader)
public static void OnReceive(NetState state, CircularBufferReader reader)
{
reader.ReadByte(); // 1: <4.0.1a, 2>=4.0.1a

View file

@ -20,7 +20,7 @@ namespace Server.Misc
}
}
private static void OnPartyTrack(NetState state, PacketReader reader)
private static void OnPartyTrack(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;
var party = Party.Get(from);
@ -36,7 +36,7 @@ namespace Server.Misc
}
}
private static void OnGuildTrack(NetState state, PacketReader reader)
private static void OnGuildTrack(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;

View file

@ -19,7 +19,7 @@ namespace Server.Misc
public static PacketHandler GetHandler(int packetID) =>
packetID >= 0 && packetID < m_Handlers.Length ? m_Handlers[packetID] : null;
public static void DecodeBundledPacket(NetState state, PacketReader reader)
public static void DecodeBundledPacket(NetState state, CircularBufferReader reader)
{
int packetID = reader.ReadByte();

View file

@ -1770,7 +1770,7 @@ namespace Server.Multis
context.Foundation.SendInfoTo(state);
}
public static void QueryDesignDetails(NetState state, PacketReader reader)
public static void QueryDesignDetails(NetState state, CircularBufferReader reader)
{
var from = state.Mobile;