From 6f5b7f7a6b3e4265232663c7609acbe6301bbb42 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 13 Nov 2025 00:00:19 -0800 Subject: [PATCH] fix: Fixes SpanWriter/SpanReader tests (#2263) --- .../Tests/Buffers/SpanReaderTests.cs | 597 +++++++++++++++++ .../Tests/Buffers/SpanWriterTests.cs | 618 ++++++++++++++++-- Projects/Server/Buffers/SpanReader.cs | 2 +- 3 files changed, 1171 insertions(+), 46 deletions(-) create mode 100644 Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs diff --git a/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs new file mode 100644 index 000000000..9a68576c8 --- /dev/null +++ b/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs @@ -0,0 +1,597 @@ +using System; +using System.Buffers; +using System.IO; +using Xunit; + +namespace Server.Tests; + +public class SpanReaderTests +{ + [Fact] + public void TestReadByte() + { + ReadOnlySpan buffer = [0x12, 0x34]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x12, reader.ReadByte()); + Assert.Equal(0x34, reader.ReadByte()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadByteAtEnd() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x12]; + var reader = new SpanReader(buffer); + reader.ReadByte(); + reader.ReadByte(); + } + ); + } + + [Fact] + public void TestReadBoolean() + { + ReadOnlySpan buffer = [0, 1, 2, 255]; + var reader = new SpanReader(buffer); + + Assert.False(reader.ReadBoolean()); + Assert.True(reader.ReadBoolean()); + Assert.True(reader.ReadBoolean()); + Assert.True(reader.ReadBoolean()); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadSByte() + { + ReadOnlySpan buffer = [0xFF, 0x7F]; + var reader = new SpanReader(buffer); + + Assert.Equal(-1, reader.ReadSByte()); + Assert.Equal(127, reader.ReadSByte()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadInt16BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x1234, reader.ReadInt16()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadInt16LittleEndian() + { + ReadOnlySpan buffer = [0x34, 0x12]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x1234, reader.ReadInt16LE()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadInt16AtEnd() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x12]; + var reader = new SpanReader(buffer); + reader.ReadInt16(); + } + ); + } + + [Fact] + public void TestReadUInt16BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34]; + var reader = new SpanReader(buffer); + + Assert.Equal((ushort)0x1234, reader.ReadUInt16()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadUInt16LittleEndian() + { + ReadOnlySpan buffer = [0x34, 0x12]; + var reader = new SpanReader(buffer); + + Assert.Equal((ushort)0x1234, reader.ReadUInt16LE()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadInt32BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x12345678, reader.ReadInt32()); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadInt32AtEnd() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56]; + var reader = new SpanReader(buffer); + reader.ReadInt32(); + } + ); + } + + [Fact] + public void TestReadUInt32BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x12345678u, reader.ReadUInt32()); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadUInt32LittleEndian() + { + ReadOnlySpan buffer = [0x78, 0x56, 0x34, 0x12]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x12345678u, reader.ReadUInt32LE()); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadInt64BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x123456789ABCDEF0L, reader.ReadInt64()); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadInt64AtEnd() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE]; + var reader = new SpanReader(buffer); + reader.ReadInt64(); + } + ); + } + + [Fact] + public void TestReadUInt64BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x123456789ABCDEF0UL, reader.ReadUInt64()); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadAsciiString() + { + ReadOnlySpan buffer = "Hello"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadAscii(); + + Assert.Equal("Hello", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadAsciiStringWithNull() + { + ReadOnlySpan buffer = "Hel\0o"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadAscii(); + + Assert.Equal("Hel", result); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadAsciiStringFixedLength() + { + ReadOnlySpan buffer = "Hello\0\0\0\0\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadAscii(10); + + Assert.Equal("Hello", result); + Assert.Equal(10, reader.Position); + } + + [Fact] + public void TestReadAsciiStringFixedLengthTooLarge() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = "Hel"u8; + var reader = new SpanReader(buffer); + reader.ReadAscii(10); + } + ); + } + + [Fact] + public void TestReadAsciiSafe() + { + ReadOnlySpan buffer = [(byte)'H', (byte)'e', 0xFF, (byte)'l', (byte)'o']; + var reader = new SpanReader(buffer); + + var result = reader.ReadAsciiSafe(); + + Assert.Equal("He?lo", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadUTF8String() + { + ReadOnlySpan buffer = "Hello"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadUTF8(); + + Assert.Equal("Hello", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadUTF8StringWithNull() + { + ReadOnlySpan buffer = "Hi\0Bye"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadUTF8(); + + Assert.Equal("Hi", result); + Assert.Equal(3, reader.Position); + } + + [Fact] + public void TestReadLittleUniString() + { + ReadOnlySpan buffer = "H\0i\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUni(); + + Assert.Equal("Hi", result); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadLittleUniStringWithNull() + { + ReadOnlySpan buffer = "H\0i\0\0\0X\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUni(); + + Assert.Equal("Hi", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadLittleUniStringFixedLength() + { + ReadOnlySpan buffer = "H\0i\0\0\0\0\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUni(4); + + Assert.Equal("Hi", result); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadLittleUniSafe() + { + ReadOnlySpan buffer = [(byte)'H', 0, 0xFF, 0xD8, (byte)'i', 0]; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUniSafe(); + + Assert.Equal("H\uFFFDi", result); + Assert.Equal(6, reader.Position); + } + + [Fact] + public void TestReadBigUniString() + { + ReadOnlySpan buffer = "\0H\0i"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUni(); + + Assert.Equal("Hi", result); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadBigUniStringWithNull() + { + ReadOnlySpan buffer = "\0H\0i\0\0\0X"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUni(); + + Assert.Equal("Hi", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadBigUniStringFixedLength() + { + ReadOnlySpan buffer = "\0H\0i\0\0\0\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUni(4); + + Assert.Equal("Hi", result); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadBigUniSafe() + { + ReadOnlySpan buffer = [0, (byte)'H', 0xD8, 0xFF, 0, (byte)'i']; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUniSafe(); + + Assert.Equal("H\uFFFDi", result); + Assert.Equal(6, reader.Position); + } + + [Fact] + public void TestSeekBegin() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + reader.ReadByte(); + reader.ReadByte(); + + var pos = reader.Seek(0, SeekOrigin.Begin); + + Assert.Equal(0, pos); + Assert.Equal(0, reader.Position); + Assert.Equal(0x01, reader.ReadByte()); + } + + [Fact] + public void TestSeekCurrent() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + reader.ReadByte(); + var pos = reader.Seek(2, SeekOrigin.Current); + + Assert.Equal(3, pos); + Assert.Equal(3, reader.Position); + Assert.Equal(0x04, reader.ReadByte()); + } + + [Fact] + public void TestSeekEnd() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + var pos = reader.Seek(-2, SeekOrigin.End); + + Assert.Equal(3, pos); + Assert.Equal(3, reader.Position); + Assert.Equal(0x04, reader.ReadByte()); + } + + [Fact] + public void TestSeekNegativeThrows() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + reader.Seek(-1, SeekOrigin.Begin); + } + ); + } + + [Fact] + public void TestSeekBeyondEndThrows() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + reader.Seek(10, SeekOrigin.Begin); + } + ); + } + + [Fact] + public void TestRead() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + Span dest = stackalloc byte[3]; + var bytesRead = reader.Read(dest); + + Assert.Equal(3, bytesRead); + Assert.Equal(3, reader.Position); + AssertThat.Equal(dest, [0x01, 0x02, 0x03]); + } + + [Fact] + public void TestReadPartial() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + Span dest = stackalloc byte[5]; + var bytesRead = reader.Read(dest); + + Assert.Equal(3, bytesRead); + Assert.Equal(3, reader.Position); + AssertThat.Equal(dest[..3], [0x01, 0x02, 0x03]); + } + + [Fact] + public void TestReadEmpty() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + Span dest = []; + var bytesRead = reader.Read(dest); + + Assert.Equal(0, bytesRead); + Assert.Equal(0, reader.Position); + } + + [Fact] + public void TestReadAtEnd() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + reader.Seek(3, SeekOrigin.Begin); + + Span dest = stackalloc byte[5]; + var bytesRead = reader.Read(dest); + + Assert.Equal(0, bytesRead); + Assert.Equal(3, reader.Position); + } + + [Fact] + public void TestLength() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + Assert.Equal(5, reader.Length); + } + + [Fact] + public void TestPosition() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + Assert.Equal(0, reader.Position); + + reader.ReadByte(); + Assert.Equal(1, reader.Position); + + reader.ReadUInt16(); + Assert.Equal(3, reader.Position); + } + + [Fact] + public void TestRemaining() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + Assert.Equal(5, reader.Remaining); + + reader.ReadByte(); + Assert.Equal(4, reader.Remaining); + + reader.ReadUInt16(); + Assert.Equal(2, reader.Remaining); + + reader.ReadUInt16(); + Assert.Equal(0, reader.Remaining); + } + + [Fact] + public void TestBuffer() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + var bufferProperty = reader.Buffer; + + Assert.Equal(3, bufferProperty.Length); + AssertThat.Equal(bufferProperty, buffer); + } + + [Fact] + public void TestReadStringEmptyFixedLength() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + var result = reader.ReadAscii(0); + + Assert.Equal("", result); + Assert.Equal(0, reader.Position); + } + + [Fact] + public void TestReadMultipleStringsWithNullTerminators() + { + ReadOnlySpan buffer = "AB\0CD\0EF"u8; + var reader = new SpanReader(buffer); + + Assert.Equal("AB", reader.ReadAscii()); + Assert.Equal("CD", reader.ReadAscii()); + Assert.Equal("EF", reader.ReadAscii()); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadLittleUniOddByteCount() + { + // If buffer has odd number of bytes, the last byte should be ignored + ReadOnlySpan buffer = [(byte)'H', 0, (byte)'i', 0, 0xFF]; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUni(); + + Assert.Equal("Hi", result); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadBigUniOddByteCount() + { + // If buffer has odd number of bytes, the last byte should be ignored + ReadOnlySpan buffer = [0, (byte)'H', 0, (byte)'i', 0xFF]; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUni(); + + Assert.Equal("Hi", result); + Assert.Equal(4, reader.Position); + } +} diff --git a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs index 0aeffe574..a0e7ed18c 100644 --- a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs @@ -1,63 +1,591 @@ using System; using System.Buffers; +using System.IO; using Xunit; -namespace Server.Tests +namespace Server.Tests; + +public class SpanWriterTests { - public class SpanWriterTests + [Fact] + public unsafe void TestSpanWriterResizes() { - [Fact] - public unsafe void TestSpanWriterResizes() - { - Span smallStack = stackalloc byte[8]; - using var writer = new SpanWriter(smallStack, true); - writer.Write(0x1024L); - writer.Write(0x1024L); + Span smallStack = stackalloc byte[8]; + var writer = new SpanWriter(smallStack, true); + writer.Write(0x1024L); + writer.Write(0x1024L); - var span = writer.RawBuffer; - fixed (byte* spanPtr = span) + var span = writer.RawBuffer; + fixed (byte* spanPtr = span) + { + fixed (byte* stackPtr = smallStack) { - fixed (byte* stackPtr = smallStack) - { - Assert.True(spanPtr != stackPtr); - } + Assert.True(spanPtr != stackPtr); } - - Assert.True(span.Length > smallStack.Length); } - [Fact] - public unsafe void TestSpanWriterOnlyStackAlloc() - { - Span smallStack = stackalloc byte[8]; - using var writer = new SpanWriter(smallStack, true); - writer.Write(0x1024L); + Assert.True(span.Length > smallStack.Length); + writer.Dispose(); + } - var span = writer.RawBuffer; - fixed (byte* spanPtr = span) + [Fact] + public unsafe void TestSpanWriterOnlyStackAlloc() + { + Span smallStack = stackalloc byte[8]; + var writer = new SpanWriter(smallStack, true); + writer.Write(0x1024L); + + var span = writer.RawBuffer; + fixed (byte* spanPtr = span) + { + fixed (byte* stackPtr = smallStack) { - fixed (byte* stackPtr = smallStack) - { - Assert.True(spanPtr == stackPtr); - } + Assert.True(spanPtr == stackPtr); } - - Assert.True(span.Length == smallStack.Length); - AssertThat.Equal(smallStack, stackalloc byte[] { 0, 0, 0, 0, 0, 0, 0x10, 0x24 }); } - [Fact] - public void TestSpanWriterNoResizeThrows() - { - Assert.Throws( - () => - { - Span smallStack = stackalloc byte[8]; - using var writer = new SpanWriter(smallStack); - writer.Write(0x1024L); - writer.Write(0x1024L); - } - ); - } + Assert.Equal(8, span.Length); + AssertThat.Equal(smallStack, [0, 0, 0, 0, 0, 0, 0x10, 0x24]); + } + + [Fact] + public void TestSpanWriterNoResizeThrows() + { + Assert.Throws( + () => + { + Span smallStack = stackalloc byte[8]; + var writer = new SpanWriter(smallStack); + writer.Write(0x1024L); + writer.Write(0x1024L); + } + ); + } + + [Fact] + public void TestWriteBool() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write(true); + writer.Write(false); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [1, 0]); + } + + [Fact] + public void TestWriteByte() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x12); + writer.Write((byte)0x34); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34]); + } + + [Fact] + public void TestWriteSByte() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write((sbyte)-1); + writer.Write((sbyte)127); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0xFF, 0x7F]); + } + + [Fact] + public void TestWriteInt16BigEndian() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write((short)0x1234); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34]); + } + + [Fact] + public void TestWriteInt16LittleEndian() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.WriteLE((short)0x1234); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x34, 0x12]); + } + + [Fact] + public void TestWriteUInt16BigEndian() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write((ushort)0x1234); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34]); + } + + [Fact] + public void TestWriteUInt16LittleEndian() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.WriteLE((ushort)0x1234); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x34, 0x12]); + } + + [Fact] + public void TestWriteInt32BigEndian() + { + Span buffer = stackalloc byte[4]; + var writer = new SpanWriter(buffer); + + writer.Write(0x12345678); + + Assert.Equal(4, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78]); + } + + [Fact] + public void TestWriteInt32LittleEndian() + { + Span buffer = stackalloc byte[4]; + var writer = new SpanWriter(buffer); + + writer.WriteLE(0x12345678); + + Assert.Equal(4, writer.Position); + AssertThat.Equal(writer.Span, [0x78, 0x56, 0x34, 0x12]); + } + + [Fact] + public void TestWriteUInt32BigEndian() + { + Span buffer = stackalloc byte[4]; + var writer = new SpanWriter(buffer); + + writer.Write(0x12345678u); + + Assert.Equal(4, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78]); + } + + [Fact] + public void TestWriteUInt32LittleEndian() + { + Span buffer = stackalloc byte[4]; + var writer = new SpanWriter(buffer); + + writer.WriteLE(0x12345678u); + + Assert.Equal(4, writer.Position); + AssertThat.Equal(writer.Span, [0x78, 0x56, 0x34, 0x12]); + } + + [Fact] + public void TestWriteInt64BigEndian() + { + Span buffer = stackalloc byte[8]; + var writer = new SpanWriter(buffer); + + writer.Write(0x123456789ABCDEF0L); + + Assert.Equal(8, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]); + } + + [Fact] + public void TestWriteUInt64BigEndian() + { + Span buffer = stackalloc byte[8]; + var writer = new SpanWriter(buffer); + + writer.Write(0x123456789ABCDEF0UL); + + Assert.Equal(8, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]); + } + + [Fact] + public void TestWriteSpan() + { + Span buffer = stackalloc byte[6]; + var writer = new SpanWriter(buffer); + + ReadOnlySpan data = [0x01, 0x02, 0x03]; + writer.Write(data); + writer.Write(data); + + Assert.Equal(6, writer.Position); + AssertThat.Equal(writer.Span, [0x01, 0x02, 0x03, 0x01, 0x02, 0x03]); + } + + [Fact] + public void TestWriteAsciiChar() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.WriteAscii('A'); + writer.WriteAscii('B'); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, "AB"u8); + } + + [Fact] + public void TestWriteAsciiString() + { + Span buffer = stackalloc byte[5]; + var writer = new SpanWriter(buffer); + + writer.WriteAscii("Hello"); + + Assert.Equal(5, writer.Position); + AssertThat.Equal(writer.Span, "Hello"u8); + } + + [Fact] + public void TestWriteAsciiStringNull() + { + Span buffer = stackalloc byte[6]; + var writer = new SpanWriter(buffer); + + writer.WriteAsciiNull("Hello"); + + Assert.Equal(6, writer.Position); + AssertThat.Equal(writer.Span, "Hello\0"u8); + } + + [Fact] + public void TestWriteAsciiStringFixedLength() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.WriteAscii("Hello", 10); + + Assert.Equal(10, writer.Position); + AssertThat.Equal(writer.Span, "Hello\0\0\0\0\0"u8); + } + + [Fact] + public void TestWriteUTF8String() + { + Span buffer = stackalloc byte[5]; + var writer = new SpanWriter(buffer); + + writer.WriteUTF8("Hello"); + + Assert.Equal(5, writer.Position); + AssertThat.Equal(writer.Span, "Hello"u8); + } + + [Fact] + public void TestWriteUTF8StringNull() + { + Span buffer = stackalloc byte[6]; + var writer = new SpanWriter(buffer); + + writer.WriteUTF8Null("Hello"); + + Assert.Equal(6, writer.Position); + AssertThat.Equal(writer.Span, "Hello\0"u8); + } + + [Fact] + public void TestWriteLittleUniString() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.WriteLittleUni("Hello"); + + Assert.Equal(10, writer.Position); + AssertThat.Equal(writer.Span, "H\0e\0l\0l\0o\0"u8); + } + + [Fact] + public void TestWriteLittleUniStringNull() + { + Span buffer = stackalloc byte[12]; + var writer = new SpanWriter(buffer); + + writer.WriteLittleUniNull("Hello"); + + Assert.Equal(12, writer.Position); + AssertThat.Equal(writer.Span, "H\0e\0l\0l\0o\0\0\0"u8); + } + + [Fact] + public void TestWriteBigUniString() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.WriteBigUni("Hello"); + + Assert.Equal(10, writer.Position); + AssertThat.Equal(writer.Span, "\0H\0e\0l\0l\0o"u8); + } + + [Fact] + public void TestWriteBigUniStringNull() + { + Span buffer = stackalloc byte[12]; + var writer = new SpanWriter(buffer); + + writer.WriteBigUniNull("Hello"); + + Assert.Equal(12, writer.Position); + AssertThat.Equal(writer.Span, "\0H\0e\0l\0l\0o\0\0"u8); + } + + [Fact] + public void TestClear() + { + Span buffer = stackalloc byte[10]; + buffer.Fill(0xFF); + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Clear(5); + writer.Write((byte)0x02); + + Assert.Equal(7, writer.Position); + AssertThat.Equal(writer.Span, [0x01, 0, 0, 0, 0, 0, 0x02]); + } + + [Fact] + public void TestSeekBegin() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + writer.Write((byte)0x03); + + var pos = writer.Seek(1, SeekOrigin.Begin); + + Assert.Equal(1, pos); + Assert.Equal(1, writer.Position); + Assert.Equal(3, writer.BytesWritten); + } + + [Fact] + public void TestSeekCurrent() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + + var pos = writer.Seek(2, SeekOrigin.Current); + + Assert.Equal(4, pos); + Assert.Equal(4, writer.Position); + } + + [Fact] + public void TestSeekEnd() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + writer.Write((byte)0x03); + + var pos = writer.Seek(-1, SeekOrigin.End); + + Assert.Equal(2, pos); + Assert.Equal(2, writer.Position); + } + + [Fact] + public void TestSeekNegativeThrows() + { + Assert.Throws( + () => + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + writer.Seek(-1, SeekOrigin.Begin); + } + ); + } + + [Fact] + public void TestSeekBeyondCapacityNoResizeThrows() + { + Assert.Throws( + () => + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + writer.Seek(20, SeekOrigin.Begin); + } + ); + } + + [Fact] + public void TestSeekBeyondCapacityWithResize() + { + var writer = new SpanWriter(10, true); + + var pos = writer.Seek(20, SeekOrigin.Begin); + + Assert.Equal(20, pos); + Assert.True(writer.Capacity >= 20); + writer.Dispose(); + } + + [Fact] + public void TestEnsureCapacityNoResize() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.EnsureCapacity(10); + Assert.Equal(10, writer.Capacity); + } + + [Fact] + public void TestEnsureCapacityNoResizeThrows() + { + Assert.Throws( + () => + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + writer.EnsureCapacity(20); + } + ); + } + + [Fact] + public void TestEnsureCapacityWithResize() + { + var writer = new SpanWriter(10, true); + + writer.EnsureCapacity(50); + + Assert.True(writer.Capacity >= 50); + writer.Dispose(); + } + + [Fact] + public void TestToSpanWithStackAlloc() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + writer.Write((byte)0x03); + + using var owner = writer.ToSpan(); + + Assert.Equal(3, owner.Span.Length); + AssertThat.Equal(owner.Span, [0x01, 0x02, 0x03]); + } + + [Fact] + public void TestToSpanWithRentedBuffer() + { + var writer = new SpanWriter(10); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + writer.Write((byte)0x03); + + using var owner = writer.ToSpan(); + + Assert.Equal(3, owner.Span.Length); + AssertThat.Equal(owner.Span, [0x01, 0x02, 0x03]); + } + + [Fact] + public void TestToSpanEmpty() + { + var writer = new SpanWriter(10); + + using var owner = writer.ToSpan(); + + Assert.Equal(0, owner.Span.Length); + } + + [Fact] + public void TestBytesWritten() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + Assert.Equal(0, writer.BytesWritten); + + writer.Write((byte)0x01); + Assert.Equal(1, writer.BytesWritten); + + writer.Write((ushort)0x0203); + Assert.Equal(3, writer.BytesWritten); + + writer.Seek(1, SeekOrigin.Begin); + Assert.Equal(3, writer.BytesWritten); + + writer.Write((byte)0xFF); + Assert.Equal(3, writer.BytesWritten); + } + + [Fact] + public void TestCapacity() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + Assert.Equal(10, writer.Capacity); + } + + [Fact] + public void TestRawBuffer() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + + var raw = writer.RawBuffer; + Assert.Equal(10, raw.Length); + Assert.Equal(0x01, raw[0]); + } + + [Fact] + public void TestSpan() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + + var span = writer.Span; + Assert.Equal(2, span.Length); + AssertThat.Equal(span, [0x01, 0x02]); } } diff --git a/Projects/Server/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs index 290ba5a94..dd35e8702 100644 --- a/Projects/Server/Buffers/SpanReader.cs +++ b/Projects/Server/Buffers/SpanReader.cs @@ -275,7 +275,7 @@ public ref struct SpanReader } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Read(Span bytes) + public int Read(scoped Span bytes) { if (bytes.Length == 0) {