diff --git a/Projects/Server/Buffers/CircularBufferWriter.cs b/Projects/Server/Buffers/CircularBufferWriter.cs index c80aad159..11a8f0874 100644 --- a/Projects/Server/Buffers/CircularBufferWriter.cs +++ b/Projects/Server/Buffers/CircularBufferWriter.cs @@ -88,7 +88,11 @@ namespace System.Buffers { if (!BinaryPrimitives.TryWriteInt16BigEndian(_first.Slice(Position), value)) { - // Not enough space. Split the spans + if (BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + Write((byte)(value >> 8)); Write((byte)value); } @@ -114,7 +118,11 @@ namespace System.Buffers { if (!BinaryPrimitives.TryWriteUInt16BigEndian(_first.Slice(Position), value)) { - // Not enough space. Split the spans + if (BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + Write((byte)(value >> 8)); Write((byte)value); } @@ -140,7 +148,11 @@ namespace System.Buffers { if (!BinaryPrimitives.TryWriteInt32BigEndian(_first.Slice(Position), value)) { - // Not enough space. Split the spans + if (BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + Write((byte)(value >> 24)); Write((byte)(value >> 16)); Write((byte)(value >> 8)); @@ -168,7 +180,11 @@ namespace System.Buffers { if (!BinaryPrimitives.TryWriteInt32LittleEndian(_first.Slice(Position), value)) { - // Not enough space. Split the spans + if (!BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + Write((byte)(value >> 24)); Write((byte)(value >> 16)); Write((byte)(value >> 8)); @@ -199,7 +215,11 @@ namespace System.Buffers { if (!BinaryPrimitives.TryWriteUInt32BigEndian(_first.Slice(Position), value)) { - // Not enough space. Split the spans + if (BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + Write((byte)(value >> 24)); Write((byte)(value >> 16)); Write((byte)(value >> 8)); @@ -220,6 +240,38 @@ namespace System.Buffers } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLE(uint value) + { + if (Position < _first.Length) + { + if (!BinaryPrimitives.TryWriteUInt32LittleEndian(_first.Slice(Position), value)) + { + if (!BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + + Write((byte)(value >> 24)); + Write((byte)(value >> 16)); + Write((byte)(value >> 8)); + Write((byte)value); + } + else + { + Position += 4; + } + } + else if (BinaryPrimitives.TryWriteUInt32LittleEndian(_second.Slice(Position - _first.Length), value)) + { + Position += 4; + } + else + { + throw new OutOfMemoryException(); + } + } + /// /// Writes a 8-byte signed integer value to the underlying stream. /// @@ -230,7 +282,11 @@ namespace System.Buffers { if (!BinaryPrimitives.TryWriteInt64BigEndian(_first.Slice(Position), value)) { - // Not enough space. Split the spans + if (BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + Write((byte)(value >> 56)); Write((byte)(value >> 48)); Write((byte)(value >> 40)); @@ -239,6 +295,7 @@ namespace System.Buffers Write((byte)(value >> 16)); Write((byte)(value >> 8)); Write((byte)value); + } else { @@ -265,7 +322,11 @@ namespace System.Buffers { if (!BinaryPrimitives.TryWriteUInt64BigEndian(_first.Slice(Position), value)) { - // Not enough space. Split the spans + if (BitConverter.IsLittleEndian) + { + value = BinaryPrimitives.ReverseEndianness(value); + } + Write((byte)(value >> 56)); Write((byte)(value >> 48)); Write((byte)(value >> 40)); diff --git a/Projects/Server/Serialization/BufferWriter.cs b/Projects/Server/Serialization/BufferWriter.cs index 74ff4668e..d1e87756d 100644 --- a/Projects/Server/Serialization/BufferWriter.cs +++ b/Projects/Server/Serialization/BufferWriter.cs @@ -25,53 +25,60 @@ namespace Server { public class BufferWriter : IGenericWriter { - private const int LargeByteBufferSize = 256; - private readonly Encoding m_Encoding; private readonly bool m_PrefixStrings; protected long Index { get; set; } - - private byte[] m_CharacterBuffer; - - private int m_MaxBufferChars; + private byte[] _buffer; public BufferWriter(byte[] buffer, bool prefixStr) { m_PrefixStrings = prefixStr; m_Encoding = Utility.UTF8; - Buffer = buffer; + _buffer = buffer; } public BufferWriter(bool prefixStr) { m_PrefixStrings = prefixStr; m_Encoding = Utility.UTF8; - Buffer = new byte[BufferSize]; + _buffer = new byte[BufferSize]; } public virtual long Position => Index; protected virtual int BufferSize => 256; - public byte[] Buffer { get; protected set; } + public byte[] Buffer => _buffer; public virtual void Close() { } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Resize(int size) { - var copy = new byte[size]; - System.Buffer.BlockCopy(Buffer, 0, copy, 0, Math.Min(size, Buffer.Length)); - Buffer = copy; + // We shouldn't ever resize to a 0 length buffer. That is dangerous + if (size <= 0) + { + size = BufferSize; + } + + Array.Resize(ref _buffer, size); } public virtual void Flush() { - Resize(Buffer.Length * 2); + Resize(_buffer.Length * 2); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void FlushIfNeeded(int amount) + { + if (Index + amount > _buffer.Length) + { + Flush(); + } } public void Reset() @@ -79,6 +86,23 @@ namespace Server Index = 0; } + public void Write(ReadOnlySpan bytes) + { + var remaining = bytes.Length; + var idx = 0; + + while (remaining > 0) + { + FlushIfNeeded(remaining); + var count = Math.Min((int)(_buffer.Length - Index), remaining); + bytes.Slice(idx, count).CopyTo(_buffer.AsSpan((int)Index, count)); + + idx += count; + Index += count; + remaining -= count; + } + } + public virtual long Seek(long offset, SeekOrigin origin) { return origin switch @@ -96,21 +120,11 @@ namespace Server while (v >= 0x80) { - if (Index + 1 > Buffer.Length) - { - Flush(); - } - - Buffer[Index++] = (byte)(v | 0x80); + Write((byte)(v | 0x80)); v >>= 7; } - if (Index + 1 > Buffer.Length) - { - Flush(); - } - - Buffer[Index++] = (byte)v; + Write((byte)v); } public void Write(string value) @@ -119,22 +133,11 @@ namespace Server { if (value == null) { - if (Index + 1 > Buffer.Length) - { - Flush(); - } - - Buffer[Index++] = 0; + Write((byte)0); } else { - if (Index + 1 > Buffer.Length) - { - Flush(); - } - - Buffer[Index++] = 1; - + Write((byte)1); InternalWriteString(value); } } @@ -176,7 +179,10 @@ namespace Server public void Write(IPAddress value) { - Write(Utility.GetLongAddressValue(value)); + Span stack = stackalloc byte[16]; + value.TryWriteBytes(stack, out var bytesWritten); + Write((byte)bytesWritten); + Write(stack.Slice(0, bytesWritten)); } public void Write(TimeSpan value) @@ -196,100 +202,79 @@ namespace Server public void Write(long value) { - if (Index + 8 > Buffer.Length) - { - Flush(); - } + FlushIfNeeded(8); - Buffer[Index] = (byte)value; - Buffer[Index + 1] = (byte)(value >> 8); - Buffer[Index + 2] = (byte)(value >> 16); - Buffer[Index + 3] = (byte)(value >> 24); - Buffer[Index + 4] = (byte)(value >> 32); - Buffer[Index + 5] = (byte)(value >> 40); - Buffer[Index + 6] = (byte)(value >> 48); - Buffer[Index + 7] = (byte)(value >> 56); + _buffer[Index] = (byte)value; + _buffer[Index + 1] = (byte)(value >> 8); + _buffer[Index + 2] = (byte)(value >> 16); + _buffer[Index + 3] = (byte)(value >> 24); + _buffer[Index + 4] = (byte)(value >> 32); + _buffer[Index + 5] = (byte)(value >> 40); + _buffer[Index + 6] = (byte)(value >> 48); + _buffer[Index + 7] = (byte)(value >> 56); Index += 8; } public void Write(ulong value) { - if (Index + 8 > Buffer.Length) - { - Flush(); - } + FlushIfNeeded(8); - Buffer[Index] = (byte)value; - Buffer[Index + 1] = (byte)(value >> 8); - Buffer[Index + 2] = (byte)(value >> 16); - Buffer[Index + 3] = (byte)(value >> 24); - Buffer[Index + 4] = (byte)(value >> 32); - Buffer[Index + 5] = (byte)(value >> 40); - Buffer[Index + 6] = (byte)(value >> 48); - Buffer[Index + 7] = (byte)(value >> 56); + _buffer[Index] = (byte)value; + _buffer[Index + 1] = (byte)(value >> 8); + _buffer[Index + 2] = (byte)(value >> 16); + _buffer[Index + 3] = (byte)(value >> 24); + _buffer[Index + 4] = (byte)(value >> 32); + _buffer[Index + 5] = (byte)(value >> 40); + _buffer[Index + 6] = (byte)(value >> 48); + _buffer[Index + 7] = (byte)(value >> 56); Index += 8; } public void Write(int value) { - if (Index + 4 > Buffer.Length) - { - Flush(); - } + FlushIfNeeded(4); - Buffer[Index] = (byte)value; - Buffer[Index + 1] = (byte)(value >> 8); - Buffer[Index + 2] = (byte)(value >> 16); - Buffer[Index + 3] = (byte)(value >> 24); + _buffer[Index] = (byte)value; + _buffer[Index + 1] = (byte)(value >> 8); + _buffer[Index + 2] = (byte)(value >> 16); + _buffer[Index + 3] = (byte)(value >> 24); Index += 4; } public void Write(uint value) { - if (Index + 4 > Buffer.Length) - { - Flush(); - } + FlushIfNeeded(4); - Buffer[Index] = (byte)value; - Buffer[Index + 1] = (byte)(value >> 8); - Buffer[Index + 2] = (byte)(value >> 16); - Buffer[Index + 3] = (byte)(value >> 24); + _buffer[Index] = (byte)value; + _buffer[Index + 1] = (byte)(value >> 8); + _buffer[Index + 2] = (byte)(value >> 16); + _buffer[Index + 3] = (byte)(value >> 24); Index += 4; } public void Write(short value) { - if (Index + 2 > Buffer.Length) - { - Flush(); - } + FlushIfNeeded(2); - Buffer[Index] = (byte)value; - Buffer[Index + 1] = (byte)(value >> 8); + _buffer[Index] = (byte)value; + _buffer[Index + 1] = (byte)(value >> 8); Index += 2; } public void Write(ushort value) { - if (Index + 2 > Buffer.Length) - { - Flush(); - } + FlushIfNeeded(2); - Buffer[Index] = (byte)value; - Buffer[Index + 1] = (byte)(value >> 8); + _buffer[Index] = (byte)value; + _buffer[Index + 1] = (byte)(value >> 8); Index += 2; } public unsafe void Write(double value) { - if (Index + 8 > Buffer.Length) - { - Flush(); - } + FlushIfNeeded(8); - fixed (byte* pBuffer = Buffer) + fixed (byte* pBuffer = _buffer) { *(double*)(pBuffer + Index) = value; } @@ -299,12 +284,9 @@ namespace Server public unsafe void Write(float value) { - if (Index + 4 > Buffer.Length) - { - Flush(); - } + FlushIfNeeded(4); - fixed (byte* pBuffer = Buffer) + fixed (byte* pBuffer = _buffer) { *(float*)(pBuffer + Index) = value; } @@ -312,56 +294,23 @@ namespace Server Index += 4; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write(byte value) { - if (Index + 1 > Buffer.Length) - { - Flush(); - } - - Buffer[Index++] = value; - } - - public void Write(byte[] value, int length) - { - var remaining = length; - var idx = 0; - - while (remaining > 0) - { - int size = Math.Min(Buffer.Length - (int)Index, remaining); - System.Buffer.BlockCopy(value, idx, Buffer, (int)Index, size); - // value.Slice(idx).CopyTo(m_Buffer.AsSpan(m_Index, size)); - - remaining -= size; - Index += size; - idx += size; - - if (Index == Buffer.Length) - { - Flush(); - } - } + FlushIfNeeded(1); + _buffer[Index++] = value; } public void Write(sbyte value) { - if (Index + 1 > Buffer.Length) - { - Flush(); - } - - Buffer[Index++] = (byte)value; + FlushIfNeeded(1); + _buffer[Index++] = (byte)value; } public void Write(bool value) { - if (Index + 1 > Buffer.Length) - { - Flush(); - } - - Buffer[Index++] = (byte)(value ? 1 : 0); + FlushIfNeeded(1); + _buffer[Index++] = value ? 1 : 0; } public void Write(Point3D value) @@ -391,26 +340,12 @@ namespace Server public void Write(Map value) { - if (value != null) - { - Write((byte)value.MapIndex); - } - else - { - Write((byte)0xFF); - } + Write((byte)(value?.MapIndex ?? 0xFF)); } public void Write(Race value) { - if (value != null) - { - Write((byte)value.RaceIndex); - } - else - { - Write((byte)0xFF); - } + Write((byte)(value?.RaceIndex ?? 0xFF)); } public void WriteEntity(IEntity value) @@ -430,14 +365,7 @@ namespace Server public void Write(BaseGuild value) { - if (value == null) - { - Write(0); - } - else - { - Write(value.Serial); - } + Write(value?.Serial ?? 0); } public void WriteItem(T value) where T : Item @@ -737,49 +665,29 @@ namespace Server internal void InternalWriteString(string value) { - var length = m_Encoding.GetByteCount(value); + var remaining = m_Encoding.GetByteCount(value); - WriteEncodedInt(length); - - if (m_CharacterBuffer == null) + WriteEncodedInt(remaining); + if (remaining == 0) { - m_CharacterBuffer = new byte[LargeByteBufferSize]; - m_MaxBufferChars = LargeByteBufferSize / m_Encoding.GetMaxByteCount(1); + return; } - if (length > LargeByteBufferSize) + // It is much faster to encode to stack buffer, then copy to the real buffer + Span span = stackalloc byte[Math.Min(BufferSize, 256)]; + var maxChars = span.Length / m_Encoding.GetMaxByteCount(1); + var charsLeft = value.Length; + var current = 0; + + while (charsLeft > 0) { - var current = 0; - var charsLeft = value.Length; + var charCount = Math.Min(charsLeft, maxChars); + var bytesWritten = m_Encoding.GetBytes(value.AsSpan(current, charCount), span); + remaining -= bytesWritten; + charsLeft -= charCount; + current += charCount; - while (charsLeft > 0) - { - var charCount = Math.Min(charsLeft, m_MaxBufferChars); - var byteLength = m_Encoding.GetBytes(value, current, charCount, m_CharacterBuffer, 0); - - if (Index + byteLength > Buffer.Length) - { - Flush(); - } - - System.Buffer.BlockCopy(m_CharacterBuffer, 0, Buffer, (int)Index, byteLength); - Index += byteLength; - - current += charCount; - charsLeft -= charCount; - } - } - else - { - var byteLength = m_Encoding.GetBytes(value, 0, value.Length, m_CharacterBuffer, 0); - - if (Index + byteLength > Buffer.Length) - { - Flush(); - } - - System.Buffer.BlockCopy(m_CharacterBuffer, 0, Buffer, (int)Index, byteLength); - Index += byteLength; + Write(span.Slice(0, bytesWritten)); } } } diff --git a/Projects/Server/Serialization/IGenericWriter.cs b/Projects/Server/Serialization/IGenericWriter.cs index 3fcbdbbce..001215cdf 100644 --- a/Projects/Server/Serialization/IGenericWriter.cs +++ b/Projects/Server/Serialization/IGenericWriter.cs @@ -39,7 +39,6 @@ namespace Server void Write(double value); void Write(float value); void Write(byte value); - void Write(byte[] value, int length); void Write(sbyte value); void Write(bool value); void WriteEncodedInt(int value); @@ -82,6 +81,7 @@ namespace Server void Write(HashSet list, bool tidy); void WriteGuildSet(HashSet set) where T : BaseGuild; void WriteGuildSet(HashSet set, bool tidy) where T : BaseGuild; + void Write(ReadOnlySpan bytes); long Seek(long offset, SeekOrigin origin); } diff --git a/Projects/Server/World/World.cs b/Projects/Server/World/World.cs index 984910ffb..1a9d8a709 100644 --- a/Projects/Server/World/World.cs +++ b/Projects/Server/World/World.cs @@ -528,15 +528,15 @@ namespace Server var bin = new BinaryFileWriter(binPath, true); idx.Write(entities.Count); - foreach (var mob in entities.Values) + foreach (var e in entities.Values) { long start = bin.Position; - idx.Write(mob.TypeRef); - idx.Write(mob.Serial); + idx.Write(e.TypeRef); + idx.Write(e.Serial); idx.Write(start); - mob.SerializeTo(bin); + e.SerializeTo(bin); idx.Write((int)(bin.Position - start)); } @@ -775,7 +775,7 @@ namespace Server public static void SerializeTo(this ISerializable entity, IGenericWriter writer) { var saveBuffer = entity.SaveBuffer; - writer.Write(saveBuffer.Buffer, (int)saveBuffer.Position); + writer.Write(saveBuffer.Buffer.AsSpan(0, (int)saveBuffer.Position)); // Resize to exact buffer size entity.SaveBuffer.Resize((int)entity.SaveBuffer.Position); diff --git a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs index d2134f9b1..97f2d53e7 100644 --- a/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs +++ b/Projects/UOContent/Mobiles/Townfolk/BaseEscortable.cs @@ -735,7 +735,7 @@ namespace Server.Mobiles public bool Contains(Point3D p) => Region.Contains(p); - public static void LoadTable() + public static void Initialize() { ICollection list = Map.Felucca.Regions.Values; @@ -757,11 +757,6 @@ namespace Server.Mobiles public static EDI Find(string name) { - if (m_Table == null) - { - LoadTable(); - } - if (name == null || m_Table == null) { return null;