From faff0797bf682611a820c987b50662edfaa66652 Mon Sep 17 00:00:00 2001 From: Tyler Young Date: Tue, 23 Jun 2020 22:09:20 -0400 Subject: [PATCH] Make Lidgren Use Spans & Shared Pool (#1140) * make lidgren use spans everywhere where it can convert custom pooling to shared array pool impl add unit tests for read/write add native socket extensions to socket so we can legit pass spans for SendTo/ReceiveFrom bump version in lidgren csproj replace some random "% 8" w/ "& 7" more minor nullability hacks to fix static analysis complaints made receiving packets use span minor native sockets refactor to use pinvoke add read/write constrained/prealloc'd bit stream impl to lidgren and update usages fixed missing stream cleanup remove outstanding stream cleanup since it refs buffer thru the class, can't read some other buf apply suggestions from code review remove unsafe cruft * add tests to gh actions * make stats use interpolation in tostring and remove m_bytesAllocated since it's all in the shared pool now * this pr still open so fuck it stats, human readability, faster BitsToHold methods * add api compatible version of ReadBytes * rename ReadOnlyStreamWrapper -> ReadOnlyWrapperStream rename WriteOnlyStreamWrapper -> WriteOnlyWrapperStream add AppendViaStream, AppenderStream impl add and update documentation on read/write bytes methods also fix some goofs --- .github/workflows/build-test.yml | 4 +- .../Lidgren.Network.UnitTests.csproj | 19 ++ Lidgren.Network.UnitTests/ReadWriteTests.cs | 229 +++++++++++++ Lidgren.Network.UnitTests/TestsBase.cs | 76 +++++ .../Encryption/NetAESEncryption.cs | 2 +- .../Encryption/NetBlockEncryptionBase.cs | 30 +- .../Encryption/NetCryptoProviderBase.cs | 22 +- .../Encryption/NetCryptoProviderEncryption.cs | 16 +- Lidgren.Network/Encryption/NetEncryption.cs | 2 +- .../Encryption/NetXorEncryption.cs | 20 +- .../Encryption/NetXteaEncryption.cs | 22 +- Lidgren.Network/Lidgren.Network.csproj | 6 +- Lidgren.Network/NetBigInteger.cs | 12 +- Lidgren.Network/NetBitWriter.cs | 320 ++++++++++++------ Lidgren.Network/NetBuffer.Peek.cs | 26 +- Lidgren.Network/NetBuffer.Read.cs | 247 +++++--------- Lidgren.Network/NetBuffer.Write.cs | 215 ++++++------ Lidgren.Network/NetBuffer.cs | 10 +- Lidgren.Network/NetConnection.Handshake.cs | 19 +- Lidgren.Network/NetConnection.MTU.cs | 3 +- Lidgren.Network/NetConnectionStatistics.cs | 147 +++++--- Lidgren.Network/NetFragmentationHelper.cs | 4 +- Lidgren.Network/NetOutgoingMessage.cs | 10 +- Lidgren.Network/NetPeer.Fragmentation.cs | 17 +- Lidgren.Network/NetPeer.Internal.cs | 48 ++- Lidgren.Network/NetPeer.LatencySimulation.cs | 19 +- Lidgren.Network/NetPeer.MessagePools.cs | 78 +---- Lidgren.Network/NetPeerStatistics.cs | 54 +-- Lidgren.Network/NetSRP.cs | 42 ++- Lidgren.Network/NetUtility.cs | 164 +++++---- .../Platform/NativeSocket.Structs.cs | 52 +++ Lidgren.Network/Platform/NativeSockets.cs | 195 +++++++++++ .../Platform/PlatformConstrained.cs | 7 +- Lidgren.Network/Platform/PlatformWin32.cs | 13 +- Lidgren.Network/Properties/AssemblyInfo.cs | 8 +- .../Lidgren.Network/NetBuffer.Streams.cs | 216 ++++++++++++ .../Appearance/AppearanceComponent.cs | 2 +- .../GameObjects/ServerEntityManager.cs | 4 +- Robust.Shared/Network/Messages/MsgEntity.cs | 22 +- .../Network/Messages/MsgScriptResponse.cs | 8 +- Robust.Shared/Network/Messages/MsgState.cs | 9 +- .../Messages/MsgViewVariablesModifyRemote.cs | 14 +- .../Messages/MsgViewVariablesRemoteData.cs | 7 +- .../Messages/MsgViewVariablesReqData.cs | 8 +- .../Messages/MsgViewVariablesReqSession.cs | 7 +- ...tMappedStringsSerializerServerHandshake.cs | 3 +- ...MsgRobustMappedStringsSerializerStrings.cs | 14 +- 47 files changed, 1655 insertions(+), 817 deletions(-) create mode 100644 Lidgren.Network.UnitTests/Lidgren.Network.UnitTests.csproj create mode 100644 Lidgren.Network.UnitTests/ReadWriteTests.cs create mode 100644 Lidgren.Network.UnitTests/TestsBase.cs create mode 100644 Lidgren.Network/Platform/NativeSocket.Structs.cs create mode 100644 Lidgren.Network/Platform/NativeSockets.cs create mode 100644 Lidgren.Network/RobustToolbox/Lidgren.Network/NetBuffer.Streams.cs diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 3f7bff33a..2fc15b4df 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -21,5 +21,7 @@ jobs: run: dotnet restore - name: Build run: dotnet build --no-restore /p:WarningsAsErrors=nullable - - name: Test + - name: Test Engine run: dotnet test --no-build Robust.UnitTesting/Robust.UnitTesting.csproj -v n + - name: Test Lidgren + run: dotnet test --no-build Lidgren.Network.UnitTests/Lidgren.Network.UnitTests.csproj -v n diff --git a/Lidgren.Network.UnitTests/Lidgren.Network.UnitTests.csproj b/Lidgren.Network.UnitTests/Lidgren.Network.UnitTests.csproj new file mode 100644 index 000000000..17889f534 --- /dev/null +++ b/Lidgren.Network.UnitTests/Lidgren.Network.UnitTests.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + diff --git a/Lidgren.Network.UnitTests/ReadWriteTests.cs b/Lidgren.Network.UnitTests/ReadWriteTests.cs new file mode 100644 index 000000000..e6fd6938a --- /dev/null +++ b/Lidgren.Network.UnitTests/ReadWriteTests.cs @@ -0,0 +1,229 @@ +using System; +using System.Text; +using NUnit.Framework; +using static System.Reflection.BindingFlags; + +namespace Lidgren.Network.UnitTests +{ + + public class ReadWriteTests : TestsBase + { + + [Test] + public void TestSmallMessage1() + { + var peer = Peer; + var msg = peer.CreateMessage(); + + msg.Write(false); + msg.Write(-3, 6); + msg.Write(true); + + msg.WritePadBits(); + + var data = msg.Buffer; + + var inc = CreateIncomingMessage(data, msg.LengthBits); + + var boolean = inc.ReadBoolean(); + Assert.IsFalse(boolean); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + + var value = inc.ReadInt32(6); + Assert.AreEqual(-3, value); + + boolean = inc.ReadBoolean(); + Assert.IsTrue(boolean); + + inc.SkipPadBits(); + } + + [Test] + public void TestSmallMessage2() + { + var peer = Peer; + var msg = peer.CreateMessage(); + + msg.Write(false); + msg.Write(-3, 6); + msg.Write(42); + msg.Write("duke of earl"); + msg.Write((byte) 43); + msg.Write((ushort) 44); + msg.Write(UInt64.MaxValue, 64); + var c = msg.WriteVariableInt64(long.MaxValue >> 16); + msg.Write(true); + Assert.AreEqual(7, c); + + msg.WritePadBits(); + + var data = msg.Buffer; + + var inc = CreateIncomingMessage(data, msg.LengthBits); + + var boolean = inc.ReadBoolean(); + Assert.IsFalse(boolean); + + var value = inc.ReadInt32(6); + Assert.AreEqual(-3, value); + + value = inc.ReadInt32(); + Assert.AreEqual(42, value); + + var ok = inc.ReadString(out var strResult); + Assert.IsTrue(ok, "Read/write failure"); + Assert.AreEqual("duke of earl", strResult); + + var byteVal = inc.ReadByte(); + Assert.AreEqual(43,byteVal); + + Assert.AreEqual((ushort) 44, inc.ReadUInt16(), "Read/write failure"); + + Assert.AreEqual(UInt64.MaxValue, inc.ReadUInt64(64), "Read/write failure"); + + var longVal = inc.ReadVariableInt64(); + Assert.AreEqual(long.MaxValue >> 16, longVal); + + boolean = inc.ReadBoolean(); + Assert.IsTrue(boolean); + + inc.SkipPadBits(); + } + + [Test] + public void TestComplexMessage() + { + var peer = Peer; + var msg = peer.CreateMessage(); + + msg.Write(false); + msg.Write(-3, 6); + msg.Write(42); + msg.Write("duke of earl"); + msg.Write((byte) 43); + msg.Write((ushort) 44); + msg.Write(UInt64.MaxValue, 64); + msg.Write(true); + + msg.WritePadBits(); + + var bcnt = 0; + + msg.Write(567845.0f); + msg.WriteVariableInt32(2115998022); + msg.Write(46.0); + msg.Write((ushort) 14, 9); + bcnt += msg.WriteVariableInt32(-47); + msg.WriteVariableInt32(470000); + msg.WriteVariableUInt32(48); + bcnt += msg.WriteVariableInt64(-49); + + Assert.AreEqual(2, bcnt, "WriteVariable* wrote too many bytes!"); + + var data = msg.Buffer; + + var inc = CreateIncomingMessage(data, msg.LengthBits); + + var boolean = inc.ReadBoolean(); + Assert.IsFalse(boolean); + var value = inc.ReadInt32(6); + Assert.AreEqual(-3, value); + value = inc.ReadInt32(); + Assert.AreEqual(42, value); + + var ok = inc.ReadString(out var strResult); + Assert.IsTrue(ok, "Read/write failure"); + Assert.AreEqual("duke of earl", strResult); + + var byteVal = inc.ReadByte(); + Assert.AreEqual(43, byteVal); + + Assert.AreEqual((ushort) 44, inc.ReadUInt16(), "Read/write failure"); + + Assert.AreEqual(UInt64.MaxValue, inc.ReadUInt64(64), "Read/write failure"); + + boolean = inc.ReadBoolean(); + Assert.IsTrue(boolean); + + inc.SkipPadBits(); + + Assert.AreEqual(567845.0f, inc.ReadSingle()); + Assert.AreEqual(2115998022, inc.ReadVariableInt32()); + Assert.AreEqual(46.0, inc.ReadDouble()); + Assert.AreEqual(14, inc.ReadUInt32(9)); + Assert.AreEqual(-47, inc.ReadVariableInt32()); + Assert.AreEqual(470000, inc.ReadVariableInt32()); + Assert.AreEqual(48, inc.ReadVariableUInt32()); + Assert.AreEqual(-49, inc.ReadVariableInt64()); + } + + [Test] + public void TestWriteAllFields() + { + var peer = Peer; + + var msg = peer.CreateMessage(); + + var tmp = peer.CreateMessage(); + tmp.Write((int) 42, 14); + + msg.Write(tmp); + msg.Write(tmp); + + Assert.AreEqual(tmp.LengthBits * 2, msg.LengthBits, "NetOutgoingMessage.Write(NetOutgoingMessage) failed!"); + + tmp = peer.CreateMessage(); + + var testItem = new TestItem + { + Number = 42, + Name = "Hallon", + Age = 8.2f + }; + + tmp.WriteAllFields(testItem, Public | Instance); + + var data = tmp.Buffer; + + var inc = CreateIncomingMessage(data, tmp.LengthBits); + + var readTestItem = new TestItem(); + inc.ReadAllFields(readTestItem, Public | Instance); + + Assert.AreEqual(42, readTestItem.Number); + Assert.AreEqual("Hallon", readTestItem.Name); + Assert.AreEqual(8.2f, readTestItem.Age); + + // test aligned WriteBytes/ReadBytes + msg = peer.CreateMessage(); + var tmparr = new byte[] {5, 6, 7, 8, 9}; + msg.Write(tmparr); + + inc = CreateIncomingMessage(msg.Buffer, msg.LengthBits); + var result = inc.ReadBytes(stackalloc byte[tmparr.Length]); + + for (var i = 0; i < tmparr.Length; i++) + { + Assert.AreEqual(result[i], tmparr[i], "readbytes fail"); + } + } + + public class TestItemBase + { + + public int Number; + + } + + public class TestItem : TestItemBase + { + + public float Age; + + public string Name; + + } + + } + +} diff --git a/Lidgren.Network.UnitTests/TestsBase.cs b/Lidgren.Network.UnitTests/TestsBase.cs new file mode 100644 index 000000000..37149be9e --- /dev/null +++ b/Lidgren.Network.UnitTests/TestsBase.cs @@ -0,0 +1,76 @@ +using System; +using System.Text; +using NUnit.Framework; +using static System.Reflection.BindingFlags; + +namespace Lidgren.Network.UnitTests +{ + + public abstract class TestsBase + { + + protected NetPeerConfiguration Config; + + protected NetPeer Peer; + + [SetUp] + public void Setup() + { + Config = new NetPeerConfiguration(GetType().Name) + { + EnableUPnP = true + }; + Peer = new NetPeer(Config); + Peer.Start(); + + TestContext.Out.WriteLine("Unique identifier is " + NetUtility.ToHexString(Peer.UniqueIdentifier)); + } + + [TearDown] + public void TearDown() + => Peer.Shutdown("Unit test teardown."); + + protected static NetIncomingMessage CreateIncomingMessage(byte[] fromData, int bitLength) + { + var inc = (NetIncomingMessage) Activator.CreateInstance(typeof(NetIncomingMessage), true); + typeof(NetIncomingMessage).GetField("m_buf", NonPublic | Instance)! + .SetValue(inc, fromData); + typeof(NetIncomingMessage).GetField("m_bitLength", NonPublic | Instance)! + .SetValue(inc, bitLength); + return inc; + } + + protected static string ToBinaryString(ulong value, int bits, bool includeSpaces) + { + var numSpaces = Math.Max(0, bits / 8 - 1); + if (includeSpaces == false) + { + numSpaces = 0; + } + + var bdr = new StringBuilder(bits + numSpaces); + for (var i = 0; i < bits + numSpaces; i++) + { + bdr.Append(' '); + } + + for (var i = 0; i < bits; i++) + { + var shifted = value >> i; + var isSet = (shifted & 1) != 0; + + var pos = bits - 1 - i; + if (includeSpaces) + { + pos += Math.Max(0, pos / 8); + } + + bdr[pos] = isSet ? '1' : '0'; + } + + return bdr.ToString(); + } + + } + +} diff --git a/Lidgren.Network/Encryption/NetAESEncryption.cs b/Lidgren.Network/Encryption/NetAESEncryption.cs index 04bbe8b26..8f64d7b00 100644 --- a/Lidgren.Network/Encryption/NetAESEncryption.cs +++ b/Lidgren.Network/Encryption/NetAESEncryption.cs @@ -25,7 +25,7 @@ namespace Lidgren.Network SetKey(key); } - public NetAESEncryption(NetPeer peer, byte[] data, int offset, int count) + public NetAESEncryption(NetPeer peer, ReadOnlySpan data, int offset, int count) #if UNITY : base(peer, new RijndaelManaged()) #else diff --git a/Lidgren.Network/Encryption/NetBlockEncryptionBase.cs b/Lidgren.Network/Encryption/NetBlockEncryptionBase.cs index f16aeba1e..8a6d7b2c7 100644 --- a/Lidgren.Network/Encryption/NetBlockEncryptionBase.cs +++ b/Lidgren.Network/Encryption/NetBlockEncryptionBase.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; namespace Lidgren.Network @@ -8,8 +9,6 @@ namespace Lidgren.Network /// public abstract class NetBlockEncryptionBase : NetEncryption { - // temporary space for one block to avoid reallocating every time - private byte[] m_tmp; /// /// Block size in bytes for this cipher @@ -22,7 +21,6 @@ namespace Lidgren.Network public NetBlockEncryptionBase(NetPeer peer) : base(peer) { - m_tmp = new byte[BlockSize]; } /// @@ -33,16 +31,20 @@ namespace Lidgren.Network int payloadBitLength = msg.LengthBits; int numBytes = msg.LengthBytes; int blockSize = BlockSize; - int numBlocks = (int)Math.Ceiling((double)numBytes / (double)blockSize); + Span tmp = stackalloc byte[BlockSize]; + int numBlocks = (int)Math.Ceiling(numBytes / (double)blockSize); int dstSize = numBlocks * blockSize; msg.EnsureBufferSize(dstSize * 8 + (4 * 8)); // add 4 bytes for payload length at end msg.LengthBits = dstSize * 8; // length will automatically adjust +4 bytes when payload length is written + var dataSpan = msg.m_data; for(int i=0;i tmp = stackalloc byte[BlockSize]; int numBlocks = numEncryptedBytes / blockSize; if (numBlocks * blockSize != numEncryptedBytes) return false; + var dataSpan = msg.m_data; for (int i = 0; i < numBlocks; i++) { - DecryptBlock(msg.m_data, (i * blockSize), m_tmp); - Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length); + // TODO: honestly is tmp even necessary here? + DecryptBlock(dataSpan, (i * blockSize), tmp); + //Buffer.BlockCopy(m_tmp, 0, dataSpan, (i * blockSize), m_tmp.Length); + tmp.CopyTo(dataSpan.Slice(i * blockSize, tmp.Length)); } // read 32 bits of true payload length - uint realSize = NetBitWriter.ReadUInt32(msg.m_data, 32, (numEncryptedBytes * 8)); + uint realSize = NetBitWriter.ReadUInt32(dataSpan, 32, (numEncryptedBytes * 8)); msg.m_bitLength = (int)realSize; return true; } @@ -79,11 +85,13 @@ namespace Lidgren.Network /// /// Encrypt a block of bytes /// - protected abstract void EncryptBlock(byte[] source, int sourceOffset, byte[] destination); + protected abstract void EncryptBlock(ReadOnlySpan source, int sourceOffset, Span destination); /// /// Decrypt a block of bytes /// - protected abstract void DecryptBlock(byte[] source, int sourceOffset, byte[] destination); + protected abstract void DecryptBlock(ReadOnlySpan source, int sourceOffset, Span destination); + + } } diff --git a/Lidgren.Network/Encryption/NetCryptoProviderBase.cs b/Lidgren.Network/Encryption/NetCryptoProviderBase.cs index e3ff3f4d5..60d39dc3a 100644 --- a/Lidgren.Network/Encryption/NetCryptoProviderBase.cs +++ b/Lidgren.Network/Encryption/NetCryptoProviderBase.cs @@ -4,8 +4,10 @@ using System.Security.Cryptography; namespace Lidgren.Network { + public abstract class NetCryptoProviderBase : NetEncryption { + protected SymmetricAlgorithm m_algorithm; public NetCryptoProviderBase(NetPeer peer, SymmetricAlgorithm algo) @@ -16,9 +18,10 @@ namespace Lidgren.Network m_algorithm.GenerateIV(); } - public override void SetKey(byte[] data, int offset, int count) + public override void SetKey(ReadOnlySpan data, int offset, int count) { int len = m_algorithm.Key.Length; + // ReSharper disable once SuggestVarOrType_Elsewhere var key = new byte[len]; for (int i = 0; i < len; i++) key[i] = data[offset + (i % count)]; @@ -35,9 +38,9 @@ namespace Lidgren.Network { int unEncLenBits = msg.LengthBits; - var ms = new MemoryStream(); + using var ms = new MemoryStream(msg.LengthBytes); var cs = new CryptoStream(ms, m_algorithm.CreateEncryptor(), CryptoStreamMode.Write); - cs.Write(msg.m_data, 0, msg.LengthBytes); + cs.Write(msg.m_data.Slice(0, msg.LengthBytes)); cs.Close(); // get results @@ -46,7 +49,7 @@ namespace Lidgren.Network msg.EnsureBufferSize((arr.Length + 4) * 8); msg.LengthBits = 0; // reset write pointer - msg.Write((uint)unEncLenBits); + msg.Write((uint) unEncLenBits); msg.Write(arr); msg.LengthBits = (arr.Length + 4) * 8; @@ -55,23 +58,26 @@ namespace Lidgren.Network public override bool Decrypt(NetIncomingMessage msg) { - int unEncLenBits = (int)msg.ReadUInt32(); + int unEncLenBits = (int) msg.ReadUInt32(); - var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4); + byte[] result; + using var ms = new MemoryStream(msg.m_buf, 4, msg.LengthBytes - 4); var cs = new CryptoStream(ms, m_algorithm.CreateDecryptor(), CryptoStreamMode.Read); var byteLen = NetUtility.BytesToHoldBits(unEncLenBits); - var result = m_peer.GetStorage(byteLen); + result = m_peer.GetStorage(byteLen); cs.Read(result, 0, byteLen); cs.Close(); // TODO: recycle existing msg - msg.m_data = result; + msg.m_buf = result; msg.m_bitLength = unEncLenBits; msg.m_readPosition = 0; return true; } + } + } diff --git a/Lidgren.Network/Encryption/NetCryptoProviderEncryption.cs b/Lidgren.Network/Encryption/NetCryptoProviderEncryption.cs index d1120465b..a1b3b1b45 100644 --- a/Lidgren.Network/Encryption/NetCryptoProviderEncryption.cs +++ b/Lidgren.Network/Encryption/NetCryptoProviderEncryption.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; using System.Security.Cryptography; namespace Lidgren.Network @@ -11,17 +12,18 @@ namespace Lidgren.Network { } - protected abstract CryptoStream GetEncryptStream(MemoryStream ms); + protected abstract CryptoStream GetEncryptStream(Stream ms); - protected abstract CryptoStream GetDecryptStream(MemoryStream ms); + protected abstract CryptoStream GetDecryptStream(Stream ms); public override bool Encrypt(NetOutgoingMessage msg) { int unEncLenBits = msg.LengthBits; - var ms = new MemoryStream(); + using var ms = new MemoryStream(msg.LengthBytes); var cs = GetEncryptStream(ms); - cs.Write(msg.m_data, 0, msg.LengthBytes); + var dataSpan = msg.m_data; + cs.Write(dataSpan.Slice(0,msg.LengthBytes)); cs.Close(); // get results @@ -41,8 +43,8 @@ namespace Lidgren.Network { int unEncLenBits = (int)msg.ReadUInt32(); - var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4); - var cs = GetDecryptStream(ms); + using var ums = new MemoryStream(msg.m_buf, 4, msg.LengthBytes - 4); + var cs = GetDecryptStream(ums); var result = m_peer.GetStorage(unEncLenBits); cs.Read(result, 0, NetUtility.BytesToHoldBits(unEncLenBits)); @@ -50,7 +52,7 @@ namespace Lidgren.Network // TODO: recycle existing msg - msg.m_data = result; + msg.m_buf = result; msg.m_bitLength = unEncLenBits; return true; diff --git a/Lidgren.Network/Encryption/NetEncryption.cs b/Lidgren.Network/Encryption/NetEncryption.cs index fea1e7c33..83569f018 100644 --- a/Lidgren.Network/Encryption/NetEncryption.cs +++ b/Lidgren.Network/Encryption/NetEncryption.cs @@ -30,7 +30,7 @@ namespace Lidgren.Network SetKey(bytes, 0, bytes.Length); } - public abstract void SetKey(byte[] data, int offset, int count); + public abstract void SetKey(ReadOnlySpan data, int offset, int count); /// /// Encrypt an outgoing message in place diff --git a/Lidgren.Network/Encryption/NetXorEncryption.cs b/Lidgren.Network/Encryption/NetXorEncryption.cs index 04d8e50fd..5a646f0a2 100644 --- a/Lidgren.Network/Encryption/NetXorEncryption.cs +++ b/Lidgren.Network/Encryption/NetXorEncryption.cs @@ -9,21 +9,21 @@ namespace Lidgren.Network /// public class NetXorEncryption : NetEncryption { - private byte[] m_key; + private Memory m_key; /// /// NetXorEncryption constructor /// - public NetXorEncryption(NetPeer peer, byte[] key) + public NetXorEncryption(NetPeer peer, Memory key) : base(peer) { m_key = key; } - public override void SetKey(byte[] data, int offset, int count) + public override void SetKey(ReadOnlySpan data, int offset, int count) { m_key = new byte[count]; - Array.Copy(data, offset, m_key, 0, count); + data.CopyTo(m_key.Span); } /// @@ -41,10 +41,12 @@ namespace Lidgren.Network public override bool Encrypt(NetOutgoingMessage msg) { int numBytes = msg.LengthBytes; + var dataSpan = msg.m_data; + var keySpan = m_key.Span; for (int i = 0; i < numBytes; i++) { - int offset = i % m_key.Length; - msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]); + int offset = i % keySpan.Length; + dataSpan[i] = (byte)(dataSpan[i] ^ keySpan[offset]); } return true; } @@ -55,10 +57,12 @@ namespace Lidgren.Network public override bool Decrypt(NetIncomingMessage msg) { int numBytes = msg.LengthBytes; + var keySpan = m_key.Span; + var dataSpan = msg.m_data; for (int i = 0; i < numBytes; i++) { - int offset = i % m_key.Length; - msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]); + int offset = i % keySpan.Length; + dataSpan[i] = (byte)(dataSpan[i] ^ keySpan[offset]); } return true; } diff --git a/Lidgren.Network/Encryption/NetXteaEncryption.cs b/Lidgren.Network/Encryption/NetXteaEncryption.cs index 600e408e6..7de298854 100644 --- a/Lidgren.Network/Encryption/NetXteaEncryption.cs +++ b/Lidgren.Network/Encryption/NetXteaEncryption.cs @@ -79,17 +79,11 @@ namespace Lidgren.Network { } - /// - /// String to hash for key - /// - public NetXtea(NetPeer peer, string key) - : this(peer, NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(key)), 32) + public override void SetKey(ReadOnlySpan data, int offset, int length) { - } - - public override void SetKey(byte[] data, int offset, int length) - { - var key = NetUtility.ComputeSHAHash(data, offset, length); + // ReSharper disable once SuggestVarOrType_Elsewhere + Span key = stackalloc byte[32]; + NetUtility.ComputeSHAHash(data.Slice( offset, length), key); NetException.Assert(key.Length >= 16); SetKey(key, 0, 16); } @@ -97,7 +91,7 @@ namespace Lidgren.Network /// /// Encrypts a block of bytes /// - protected override void EncryptBlock(byte[] source, int sourceOffset, byte[] destination) + protected override void EncryptBlock(ReadOnlySpan source, int sourceOffset, Span destination) { uint v0 = BytesToUInt(source, sourceOffset); uint v1 = BytesToUInt(source, sourceOffset + 4); @@ -117,7 +111,7 @@ namespace Lidgren.Network /// /// Decrypts a block of bytes /// - protected override void DecryptBlock(byte[] source, int sourceOffset, byte[] destination) + protected override void DecryptBlock(ReadOnlySpan source, int sourceOffset, Span destination) { // Pack bytes into integers uint v0 = BytesToUInt(source, sourceOffset); @@ -135,7 +129,7 @@ namespace Lidgren.Network return; } - private static uint BytesToUInt(byte[] bytes, int offset) + private static uint BytesToUInt(ReadOnlySpan bytes, int offset) { uint retval = (uint)(bytes[offset] << 24); retval |= (uint)(bytes[++offset] << 16); @@ -143,7 +137,7 @@ namespace Lidgren.Network return (retval | bytes[++offset]); } - private static void UIntToBytes(uint value, byte[] destination, int destinationOffset) + private static void UIntToBytes(uint value, Span destination, int destinationOffset) { destination[destinationOffset++] = (byte)(value >> 24); destination[destinationOffset++] = (byte)(value >> 16); diff --git a/Lidgren.Network/Lidgren.Network.csproj b/Lidgren.Network/Lidgren.Network.csproj index d8519b33b..a1768290a 100644 --- a/Lidgren.Network/Lidgren.Network.csproj +++ b/Lidgren.Network/Lidgren.Network.csproj @@ -1,11 +1,13 @@  - netstandard2.0 + netstandard2.1 false false Debug;Release AnyCPU true - $(DefineConstants);USE_RELEASE_STATISTICS + $(DefineConstants);USE_RELEASE_STATISTICS;UNSAFE + 8 + true diff --git a/Lidgren.Network/NetBigInteger.cs b/Lidgren.Network/NetBigInteger.cs index f9a607d85..302b7909e 100644 --- a/Lidgren.Network/NetBigInteger.cs +++ b/Lidgren.Network/NetBigInteger.cs @@ -256,13 +256,13 @@ namespace Lidgren.Network } public NetBigInteger( - byte[] bytes) + ReadOnlySpan bytes) : this(bytes, 0, bytes.Length) { } public NetBigInteger( - byte[] bytes, + ReadOnlySpan bytes, int offset, int length) { @@ -287,7 +287,7 @@ namespace Lidgren.Network else { int numBytes = end - iBval; - byte[] inverse = new byte[numBytes]; + Span inverse = stackalloc byte[numBytes]; int index = 0; while (index < numBytes) @@ -316,7 +316,7 @@ namespace Lidgren.Network } private static int[] MakeMagnitude( - byte[] bytes, + ReadOnlySpan bytes, int offset, int length) { @@ -374,14 +374,14 @@ namespace Lidgren.Network public NetBigInteger( int sign, - byte[] bytes) + ReadOnlySpan bytes) : this(sign, bytes, 0, bytes.Length) { } public NetBigInteger( int sign, - byte[] bytes, + ReadOnlySpan bytes, int offset, int length) { diff --git a/Lidgren.Network/NetBitWriter.cs b/Lidgren.Network/NetBitWriter.cs index c641c49c8..516bbfbbf 100644 --- a/Lidgren.Network/NetBitWriter.cs +++ b/Lidgren.Network/NetBitWriter.cs @@ -19,10 +19,11 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR TH USE OR OTHER DEALINGS IN THE SOFTWARE. */ -using System; -using System.Collections.Generic; +using System; +using System.Buffers.Binary; using System.Diagnostics; +using System.Runtime.InteropServices; namespace Lidgren.Network { @@ -34,7 +35,7 @@ namespace Lidgren.Network /// /// Read 1-8 bits from a buffer into a byte /// - public static byte ReadByte(byte[] fromBuffer, int numberOfBits, int readBitOffset) + public static byte ReadByte(ReadOnlySpan fromBuffer, int numberOfBits, int readBitOffset) { NetException.Assert(((numberOfBits > 0) && (numberOfBits < 9)), "Read() can only read between 1 and 8 bits"); @@ -67,14 +68,16 @@ namespace Lidgren.Network /// /// Read several bytes from a buffer /// - public static void ReadBytes(byte[] fromBuffer, int numberOfBytes, int readBitOffset, byte[] destination, int destinationByteOffset) + public static void ReadBytes(ReadOnlySpan fromBuffer, int numberOfBytes, int readBitOffset, Span destination, int destinationByteOffset) { int readPtr = readBitOffset >> 3; int startReadAtIndex = readBitOffset - (readPtr * 8); // (readBitOffset % 8); if (startReadAtIndex == 0) { - Buffer.BlockCopy(fromBuffer, readPtr, destination, destinationByteOffset, numberOfBytes); + //Buffer.BlockCopy(fromBuffer, readPtr, destination, destinationByteOffset, numberOfBytes); + fromBuffer.Slice(readPtr,numberOfBytes) + .CopyTo(destination.Slice(destinationByteOffset, numberOfBytes)); return; } @@ -93,14 +96,48 @@ namespace Lidgren.Network destination[destinationByteOffset++] = (byte)(b | (second << secondPartLen)); } + } - return; + + /// + /// Read several bytes from a buffer + /// + public static void ReadBytes(ReadOnlySpan fromBuffer, int readBitOffset, Span destination) + { + var destinationByteOffset = 0; + var numberOfBytes = (fromBuffer.Length << 3 - readBitOffset) >> 3; + int readPtr = readBitOffset >> 3; + int startReadAtIndex = readBitOffset - (readPtr * 8); // (readBitOffset % 8); + + if (startReadAtIndex == 0) + { + //Buffer.BlockCopy(fromBuffer, readPtr, destination, destinationByteOffset, numberOfBytes); + fromBuffer.Slice(readPtr,numberOfBytes) + .CopyTo(destination.Slice(0, numberOfBytes)); + return; + } + + int secondPartLen = 8 - startReadAtIndex; + int secondMask = 255 >> secondPartLen; + + for (int i = 0; i < numberOfBytes; i++) + { + // mask away unused bits lower than (right of) relevant bits in byte + int b = fromBuffer[readPtr] >> startReadAtIndex; + + readPtr++; + + // mask away unused bits higher than (left of) relevant bits in second byte + int second = fromBuffer[readPtr] & secondMask; + + destination[destinationByteOffset++] = (byte)(b | (second << secondPartLen)); + } } /// /// Write 0-8 bits of data to buffer /// - public static void WriteByte(byte source, int numberOfBits, byte[] destination, int destBitOffset) + public static void WriteByte(byte source, int numberOfBits, Span destination, int destBitOffset) { if (numberOfBits == 0) return; @@ -149,18 +186,63 @@ namespace Lidgren.Network (source >> bitsFree) ); } + + /// + /// Zero a number of bits + /// + public static void Zero(int numberOfBits, Span destination, int destBitOffset) + { + var dstBytePtr = destBitOffset >> 3; + var firstPartLen = destBitOffset & 7; + var numberOfBytes = numberOfBits >> 3; + var endBits = numberOfBits & 7; + + if (firstPartLen == 0) + { + destination.Slice(destBitOffset / 8, numberOfBytes).Fill(0); + + if (endBits <= 0) + { + return; + } + + var endByteSpan = destination.Slice(numberOfBytes, 1); + + endByteSpan[0] = (byte) (endByteSpan[0] & ~(byte.MaxValue >> (8 - endBits))); + + return; + } + + + var lastPartLen = 8 - firstPartLen; + + destination[dstBytePtr] &= (byte)(255 >> lastPartLen); + + ++dstBytePtr; + + destination.Slice(dstBytePtr, numberOfBytes-2).Fill(0); + + dstBytePtr = numberOfBytes - 2; + + destination[dstBytePtr] &= (byte)(255 << firstPartLen); + } + + + /// /// /// Write several whole bytes /// - public static void WriteBytes(byte[] source, int sourceByteOffset, int numberOfBytes, byte[] destination, int destBitOffset) + public static void WriteBytes(ReadOnlySpan source, int sourceByteOffset, int numberOfBytes, Span destination, int destBitOffset) { int dstBytePtr = destBitOffset >> 3; - int firstPartLen = (destBitOffset % 8); + int firstPartLen = (destBitOffset & 7); if (firstPartLen == 0) { - Buffer.BlockCopy(source, sourceByteOffset, destination, dstBytePtr, numberOfBytes); + //Buffer.BlockCopy(source, sourceByteOffset, destination, dstBytePtr, numberOfBytes); + source.Slice(sourceByteOffset,numberOfBytes) + .CopyTo(destination.Slice(dstBytePtr, numberOfBytes)); return; } @@ -180,31 +262,57 @@ namespace Lidgren.Network destination[dstBytePtr] &= (byte)(255 << firstPartLen); // clear before writing destination[dstBytePtr] |= (byte)(src >> lastPartLen); // write second half } + } - return; + + /// + /// Write several whole bytes + /// + public static void WriteBytes(ReadOnlySpan source, Span destination, int destBitOffset) + { + int sourceByteOffset = 0; + int numberOfBytes = source.Length; + int dstBytePtr = destBitOffset >> 3; + int firstPartLen = destBitOffset & 7; + + if (firstPartLen == 0) + { + //Buffer.BlockCopy(source, sourceByteOffset, destination, dstBytePtr, numberOfBytes); + source + .CopyTo(destination.Slice(dstBytePtr, numberOfBytes)); + return; + } + + int lastPartLen = 8 - firstPartLen; + + for (int i = 0; i < numberOfBytes; i++) + { + byte src = source[sourceByteOffset + i]; + + // write last part of this byte + destination[dstBytePtr] &= (byte)(255 >> lastPartLen); // clear before writing + destination[dstBytePtr] |= (byte)(src << firstPartLen); // write first half + + dstBytePtr++; + + // write first part of next byte + destination[dstBytePtr] &= (byte)(255 << firstPartLen); // clear before writing + destination[dstBytePtr] |= (byte)(src >> lastPartLen); // write second half + } } /// /// Reads an unsigned 16 bit integer /// [CLSCompliant(false)] -#if UNSAFE - public static unsafe ushort ReadUInt16(byte[] fromBuffer, int numberOfBits, int readBitOffset) + public static ushort ReadUInt16(ReadOnlySpan fromBuffer, int numberOfBits, int readBitOffset) { Debug.Assert(((numberOfBits > 0) && (numberOfBits <= 16)), "ReadUInt16() can only read between 1 and 16 bits"); - if (numberOfBits == 16 && ((readBitOffset % 8) == 0)) + if (numberOfBits == 16 && ((readBitOffset & 7) == 0)) { - fixed (byte* ptr = &(fromBuffer[readBitOffset / 8])) - { - return *(((ushort*)ptr)); - } + return MemoryMarshal.Read(fromBuffer.Slice(readBitOffset / 8)); } -#else - public static ushort ReadUInt16(byte[] fromBuffer, int numberOfBits, int readBitOffset) - { - Debug.Assert(((numberOfBits > 0) && (numberOfBits <= 16)), "ReadUInt16() can only read between 1 and 16 bits"); -#endif ushort returnValue; if (numberOfBits <= 8) { @@ -220,38 +328,27 @@ namespace Lidgren.Network returnValue |= (ushort)(ReadByte(fromBuffer, numberOfBits, readBitOffset) << 8); } -#if BIGENDIAN - // reorder bytes - uint retVal = returnValue; - retVal = ((retVal & 0x0000ff00) >> 8) | ((retVal & 0x000000ff) << 8); - return (ushort)retVal; -#else + if (!BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(returnValue); + } + return returnValue; -#endif } /// /// Reads the specified number of bits into an UInt32 /// [CLSCompliant(false)] -#if UNSAFE - public static unsafe uint ReadUInt32(byte[] fromBuffer, int numberOfBits, int readBitOffset) + public static uint ReadUInt32(ReadOnlySpan fromBuffer, int numberOfBits, int readBitOffset) { NetException.Assert(((numberOfBits > 0) && (numberOfBits <= 32)), "ReadUInt32() can only read between 1 and 32 bits"); - if (numberOfBits == 32 && ((readBitOffset % 8) == 0)) + if (numberOfBits == 32 && ((readBitOffset & 7) == 0)) { - fixed (byte* ptr = &(fromBuffer[readBitOffset / 8])) - { - return *(((uint*)ptr)); - } + return MemoryMarshal.Read(fromBuffer.Slice(readBitOffset / 8)); } -#else - - public static uint ReadUInt32(byte[] fromBuffer, int numberOfBits, int readBitOffset) - { - NetException.Assert(((numberOfBits > 0) && (numberOfBits <= 32)), "ReadUInt32() can only read between 1 and 32 bits"); -#endif + uint returnValue; if (numberOfBits <= 8) { @@ -284,16 +381,12 @@ namespace Lidgren.Network returnValue |= (uint)(ReadByte(fromBuffer, numberOfBits, readBitOffset) << 24); -#if BIGENDIAN - // reorder bytes - return - ((returnValue & 0xff000000) >> 24) | - ((returnValue & 0x00ff0000) >> 8) | - ((returnValue & 0x0000ff00) << 8) | - ((returnValue & 0x000000ff) << 24); -#else + if (!BitConverter.IsLittleEndian) + { + return BinaryPrimitives.ReverseEndianness(returnValue); + } + return returnValue; -#endif } //[CLSCompliant(false)] @@ -303,74 +396,76 @@ namespace Lidgren.Network /// Writes an unsigned 16 bit integer /// [CLSCompliant(false)] - public static void WriteUInt16(ushort source, int numberOfBits, byte[] destination, int destinationBitOffset) + public static void WriteUInt16(ushort source, int numberOfBits, Span destination, int destinationBitOffset) { if (numberOfBits == 0) - return; - - NetException.Assert((numberOfBits >= 0 && numberOfBits <= 16), "numberOfBits must be between 0 and 16"); -#if BIGENDIAN - // reorder bytes - uint intSource = source; - intSource = ((intSource & 0x0000ff00) >> 8) | ((intSource & 0x000000ff) << 8); - source = (ushort)intSource; -#endif - if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)source, numberOfBits, destination, destinationBitOffset); return; } - NetBitWriter.WriteByte((byte)source, 8, destination, destinationBitOffset); + NetException.Assert((numberOfBits >= 0 && numberOfBits <= 16), "numberOfBits must be between 0 and 16"); + if (!BitConverter.IsLittleEndian) + { + source = BinaryPrimitives.ReverseEndianness(source); + } + + + if (numberOfBits <= 8) + { + WriteByte((byte)source, numberOfBits, destination, destinationBitOffset); + return; + } + + WriteByte((byte)source, 8, destination, destinationBitOffset); numberOfBits -= 8; - if (numberOfBits > 0) - NetBitWriter.WriteByte((byte)(source >> 8), numberOfBits, destination, destinationBitOffset + 8); + + WriteByte((byte)(source >> 8), numberOfBits, destination, destinationBitOffset + 8); } /// /// Writes the specified number of bits into a byte array /// [CLSCompliant(false)] - public static int WriteUInt32(uint source, int numberOfBits, byte[] destination, int destinationBitOffset) + public static int WriteUInt32(uint source, int numberOfBits, Span destination, int destinationBitOffset) { -#if BIGENDIAN - // reorder bytes - source = ((source & 0xff000000) >> 24) | - ((source & 0x00ff0000) >> 8) | - ((source & 0x0000ff00) << 8) | - ((source & 0x000000ff) << 24); -#endif + if (!BitConverter.IsLittleEndian) + { + source = BinaryPrimitives.ReverseEndianness(source); + } int returnValue = destinationBitOffset + numberOfBits; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)source, numberOfBits, destination, destinationBitOffset); + WriteByte((byte)source, numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)source, 8, destination, destinationBitOffset); + + WriteByte((byte)source, 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 8), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 8), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 8), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 8), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 16), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 16), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 16), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 16), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; - NetBitWriter.WriteByte((byte)(source >> 24), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 24), numberOfBits, destination, destinationBitOffset); return returnValue; } @@ -378,89 +473,90 @@ namespace Lidgren.Network /// Writes the specified number of bits into a byte array /// [CLSCompliant(false)] - public static int WriteUInt64(ulong source, int numberOfBits, byte[] destination, int destinationBitOffset) + public static int WriteUInt64(ulong source, int numberOfBits, Span destination, int destinationBitOffset) { -#if BIGENDIAN - source = ((source & 0xff00000000000000L) >> 56) | - ((source & 0x00ff000000000000L) >> 40) | - ((source & 0x0000ff0000000000L) >> 24) | - ((source & 0x000000ff00000000L) >> 8) | - ((source & 0x00000000ff000000L) << 8) | - ((source & 0x0000000000ff0000L) << 24) | - ((source & 0x000000000000ff00L) << 40) | - ((source & 0x00000000000000ffL) << 56); -#endif + if (!BitConverter.IsLittleEndian) + source = BinaryPrimitives.ReverseEndianness(source); + int returnValue = destinationBitOffset + numberOfBits; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)source, numberOfBits, destination, destinationBitOffset); + WriteByte((byte)source, numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)source, 8, destination, destinationBitOffset); + + WriteByte((byte)source, 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 8), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 8), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 8), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 8), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 16), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 16), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 16), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 16), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 24), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 24), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 24), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 24), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 32), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 32), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 32), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 32), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 40), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 40), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 40), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 40), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 48), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 48), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 48), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 48), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; if (numberOfBits <= 8) { - NetBitWriter.WriteByte((byte)(source >> 56), numberOfBits, destination, destinationBitOffset); + WriteByte((byte)(source >> 56), numberOfBits, destination, destinationBitOffset); return returnValue; } - NetBitWriter.WriteByte((byte)(source >> 56), 8, destination, destinationBitOffset); + + WriteByte((byte)(source >> 56), 8, destination, destinationBitOffset); destinationBitOffset += 8; numberOfBits -= 8; @@ -476,10 +572,10 @@ namespace Lidgren.Network /// /// number of bytes written [CLSCompliant(false)] - public static int WriteVariableUInt32(byte[] intoBuffer, int offset, uint value) + public static int WriteVariableUInt32(Span intoBuffer, int offset, uint value) { int retval = 0; - uint num1 = (uint)value; + uint num1 = value; while (num1 >= 0x80) { intoBuffer[offset + retval] = (byte)(num1 | 0x80); @@ -494,7 +590,7 @@ namespace Lidgren.Network /// Reads a UInt32 written using WriteUnsignedVarInt(); will increment offset! /// [CLSCompliant(false)] - public static uint ReadVariableUInt32(byte[] buffer, ref int offset) + public static uint ReadVariableUInt32(Span buffer, ref int offset) { int num1 = 0; int num2 = 0; diff --git a/Lidgren.Network/NetBuffer.Peek.cs b/Lidgren.Network/NetBuffer.Peek.cs index 818f94fc1..f5764b7ee 100644 --- a/Lidgren.Network/NetBuffer.Peek.cs +++ b/Lidgren.Network/NetBuffer.Peek.cs @@ -28,7 +28,7 @@ namespace Lidgren.Network /// /// Gets the internal data buffer /// - public byte[] PeekDataBuffer() { return m_data; } + public Span PeekDataBuffer() { return m_data; } // // 1 bit @@ -79,25 +79,25 @@ namespace Lidgren.Network /// /// Reads the specified number of bytes without advancing the read pointer /// - public byte[] PeekBytes(int numberOfBytes) + public Span PeekBytes(Span bytes) { + var numberOfBytes = bytes.Length; NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError); - byte[] retval = new byte[numberOfBytes]; - NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0); - return retval; + NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, bytes, 0); + return bytes; } /// /// Reads the specified number of bytes without advancing the read pointer /// - public void PeekBytes(byte[] into, int offset, int numberOfBytes) + public Span PeekBytes(Span into, int offset, int numberOfBytes) { NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError); NetException.Assert(offset + numberOfBytes <= into.Length); NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset); - return; + return into; } // @@ -271,12 +271,12 @@ namespace Lidgren.Network if ((m_readPosition & 7) == 0) // read directly { - float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3); + float retval = BitConverter.ToSingle(m_data.Slice( m_readPosition >> 3)); return retval; } - byte[] bytes = PeekBytes(4); - return BitConverter.ToSingle(bytes, 0); + var bytes = PeekBytes(stackalloc byte[4]); + return BitConverter.ToSingle(bytes); } /// @@ -289,12 +289,12 @@ namespace Lidgren.Network if ((m_readPosition & 7) == 0) // read directly { // read directly - double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3); + double retval = BitConverter.ToDouble(m_data.Slice( m_readPosition >> 3)); return retval; } - byte[] bytes = PeekBytes(8); - return BitConverter.ToDouble(bytes, 0); + var bytes = PeekBytes(stackalloc byte[8]); + return BitConverter.ToDouble(bytes); } /// diff --git a/Lidgren.Network/NetBuffer.Read.cs b/Lidgren.Network/NetBuffer.Read.cs index 1b9f3e034..447c277f5 100644 --- a/Lidgren.Network/NetBuffer.Read.cs +++ b/Lidgren.Network/NetBuffer.Read.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; using System.Reflection; using System.Net; @@ -17,18 +18,16 @@ namespace Lidgren.Network public partial class NetBuffer { private const string c_readOverflowError = "Trying to read past the buffer size - likely caused by mismatching Write/Reads, different size or order."; - private const int c_bufferSize = 64; // Min 8 to hold anything but strings. Increase it if readed strings usally don't fit inside the buffer - private static object s_buffer; + private const string c_writeOverflowError = "Trying to write past the end of a constrained buffer - likely caused by mismatching Write/Reads, different size or order."; /// /// Reads a boolean value (stored as a single bit) written using Write(bool) /// public bool ReadBoolean() { - NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError); - byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition); + var retval = PeekBoolean(); m_readPosition += 1; - return (retval > 0 ? true : false); + return retval; } /// @@ -36,8 +35,7 @@ namespace Lidgren.Network /// public byte ReadByte() { - NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError); - byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + var retval = PeekByte(); m_readPosition += 8; return retval; } @@ -52,7 +50,8 @@ namespace Lidgren.Network result = 0; return false; } - result = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + + result = PeekByte(); m_readPosition += 8; return true; } @@ -63,10 +62,9 @@ namespace Lidgren.Network [CLSCompliant(false)] public sbyte ReadSByte() { - NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError); - byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition); + sbyte retval = PeekSByte(); m_readPosition += 8; - return (sbyte)retval; + return retval; } /// @@ -74,65 +72,76 @@ namespace Lidgren.Network /// public byte ReadByte(int numberOfBits) { - NetException.Assert(numberOfBits > 0 && numberOfBits <= 8, "ReadByte(bits) can only read between 1 and 8 bits"); - byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition); + byte retval = PeekByte(numberOfBits); m_readPosition += numberOfBits; return retval; } /// - /// Reads the specified number of bytes + /// Creates a stream out of a constrained region of the buffer for reading. /// - public byte[] ReadBytes(int numberOfBytes) + /// The length of the constrained region in bytes. + /// A readable constrained buffer region wrapped by a stream. + public Stream ReadAsStream(int byteLength) { - NetException.Assert(m_bitLength - m_readPosition + 7 >= (numberOfBytes * 8), c_readOverflowError); - - byte[] retval = new byte[numberOfBytes]; - NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0); - m_readPosition += (8 * numberOfBytes); - return retval; + var bitLength = byteLength*8; + var stream = new ReadOnlyWrapperStream(this, m_readPosition, bitLength); + m_readPosition += bitLength; + return stream; } - + /// - /// Reads the specified number of bytes and returns true for success - /// - public bool ReadBytes(int numberOfBytes, out byte[] result) - { - if (m_bitLength - m_readPosition + 7 < (numberOfBytes * 8)) - { - result = null; - return false; - } - - result = new byte[numberOfBytes]; - NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, result, 0); - m_readPosition += (8 * numberOfBytes); - return true; - } - - /// - /// Reads the specified number of bytes into a preallocated array + /// Prefer using instead. + /// Reads the specified number of bytes into a allocated array. /// /// The destination array /// The offset where to start writing in the destination array /// The number of bytes to read - public void ReadBytes(byte[] into, int offset, int numberOfBytes) + /// The same as + //[Obsolete("Use the Span parameter version of ReadBytes instead")] + public byte[] ReadBytes(int length) { - NetException.Assert(m_bitLength - m_readPosition + 7 >= (numberOfBytes * 8), c_readOverflowError); - NetException.Assert(offset + numberOfBytes <= into.Length); - - NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset); - m_readPosition += (8 * numberOfBytes); - return; + var into = new byte[length]; + PeekBytes(into); + m_readPosition += 8 * into.Length; + return into; } /// - /// Reads the specified number of bits into a preallocated array + /// Reads the specified number of bytes into a pre-allocated array. + /// + /// The destination + /// The same as + public Span ReadBytes(Span into) + { + var bytes = PeekBytes(into); + m_readPosition += 8 * into.Length; + return bytes; + } + + /// + /// Reads the specified number of bytes into a pre-allocated array. + /// + /// The destination array + /// The offset where to start writing in the destination array + /// The number of bytes to read + /// The same as + //[Obsolete("Use Span alternative instead with slicing.")] + public Span ReadBytes(Span into, int offset, int numberOfBytes) + { + var bytes = PeekBytes(into, offset, numberOfBytes); + m_readPosition += 8 * numberOfBytes; + return bytes; + } + + /// + /// Reads the specified number of bits into a pre-allocated array. /// /// The destination array /// The offset where to start writing in the destination array /// The number of bits to read - public void ReadBits(byte[] into, int offset, int numberOfBits) + //[Obsolete("Use Span alternative instead with slicing.")] + public Span ReadBits(Span into, int offset, int numberOfBits) { NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); NetException.Assert(offset + NetUtility.BytesToHoldBits(numberOfBits) <= into.Length); @@ -146,7 +155,7 @@ namespace Lidgren.Network if (extraBits > 0) into[offset + numberOfWholeBytes] = ReadByte(extraBits); - return; + return into.Slice(offset,(numberOfBits+extraBits) >> 3); } /// @@ -154,10 +163,9 @@ namespace Lidgren.Network /// public Int16 ReadInt16() { - NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError); - uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition); + var retval = PeekInt16(); m_readPosition += 16; - return (short)retval; + return retval; } /// @@ -166,8 +174,7 @@ namespace Lidgren.Network [CLSCompliant(false)] public UInt16 ReadUInt16() { - NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError); - uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition); + var retval = PeekUInt16(); m_readPosition += 16; return (ushort)retval; } @@ -177,8 +184,7 @@ namespace Lidgren.Network /// public Int32 ReadInt32() { - NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); - uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + var retval = PeekInt32(); m_readPosition += 32; return (Int32)retval; } @@ -195,7 +201,7 @@ namespace Lidgren.Network return false; } - result = (Int32)NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + result = PeekInt32(); m_readPosition += 32; return true; } @@ -205,26 +211,9 @@ namespace Lidgren.Network /// public Int32 ReadInt32(int numberOfBits) { - NetException.Assert(numberOfBits > 0 && numberOfBits <= 32, "ReadInt32(bits) can only read between 1 and 32 bits"); - NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); - - uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + var retval = PeekInt32(numberOfBits); m_readPosition += numberOfBits; - - if (numberOfBits == 32) - return (int)retval; - - int signBit = 1 << (numberOfBits - 1); - if ((retval & signBit) == 0) - return (int)retval; // positive - - // negative - unchecked - { - uint mask = ((uint)-1) >> (33 - numberOfBits); - uint tmp = (retval & mask) + 1; - return -((int)tmp); - } + return retval; } /// @@ -233,8 +222,7 @@ namespace Lidgren.Network [CLSCompliant(false)] public UInt32 ReadUInt32() { - NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); - uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + var retval = PeekUInt32(); m_readPosition += 32; return retval; } @@ -250,7 +238,7 @@ namespace Lidgren.Network result = 0; return false; } - result = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); + result = PeekUInt32(); m_readPosition += 32; return true; } @@ -261,10 +249,7 @@ namespace Lidgren.Network [CLSCompliant(false)] public UInt32 ReadUInt32(int numberOfBits) { - NetException.Assert(numberOfBits > 0 && numberOfBits <= 32, "ReadUInt32(bits) can only read between 1 and 32 bits"); - //NetException.Assert(m_bitLength - m_readBitPtr >= numberOfBits, "tried to read past buffer size"); - - UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); + var retval = PeekUInt32(numberOfBits); m_readPosition += numberOfBits; return retval; } @@ -275,15 +260,8 @@ namespace Lidgren.Network [CLSCompliant(false)] public UInt64 ReadUInt64() { - NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); - - ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); - m_readPosition += 32; - ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); - - ulong retval = low + (high << 32); - - m_readPosition += 32; + var retval = PeekUInt64(); + m_readPosition += 64; return retval; } @@ -307,19 +285,7 @@ namespace Lidgren.Network [CLSCompliant(false)] public UInt64 ReadUInt64(int numberOfBits) { - NetException.Assert(numberOfBits > 0 && numberOfBits <= 64, "ReadUInt64(bits) can only read between 1 and 64 bits"); - NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); - - ulong retval; - if (numberOfBits <= 32) - { - retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); - } - else - { - retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); - retval |= (UInt64)NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition + 32) << 32; - } + var retval = PeekUInt64(numberOfBits); m_readPosition += numberOfBits; return retval; } @@ -348,18 +314,9 @@ namespace Lidgren.Network { NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); - if ((m_readPosition & 7) == 0) // read directly - { - float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3); - m_readPosition += 32; - return retval; - } - - byte[] bytes = (byte[]) Interlocked.Exchange(ref s_buffer, null) ?? new byte[c_bufferSize]; - ReadBytes(bytes, 0, 4); - float res = BitConverter.ToSingle(bytes, 0); - s_buffer = bytes; - return res; + var retval = PeekSingle(); + m_readPosition += 32; + return retval; } /// @@ -373,17 +330,7 @@ namespace Lidgren.Network return false; } - if ((m_readPosition & 7) == 0) // read directly - { - result = BitConverter.ToSingle(m_data, m_readPosition >> 3); - m_readPosition += 32; - return true; - } - - byte[] bytes = (byte[]) Interlocked.Exchange(ref s_buffer, null) ?? new byte[c_bufferSize]; - ReadBytes(bytes, 0, 4); - result = BitConverter.ToSingle(bytes, 0); - s_buffer = bytes; + result = ReadSingle(); return true; } @@ -394,18 +341,8 @@ namespace Lidgren.Network { NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); - if ((m_readPosition & 7) == 0) // read directly - { - // read directly - double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3); - m_readPosition += 64; - return retval; - } - - byte[] bytes = (byte[]) Interlocked.Exchange(ref s_buffer, null) ?? new byte[c_bufferSize]; - ReadBytes(bytes, 0, 8); - double res = BitConverter.ToDouble(bytes, 0); - s_buffer = bytes; + var res = PeekDouble(); + m_readPosition += 64; return res; } @@ -599,21 +536,13 @@ namespace Lidgren.Network if ((m_readPosition & 7) == 0) { // read directly - string retval = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, byteLen); + string retval = Encoding.UTF8.GetString(m_data.Slice( m_readPosition >> 3, byteLen)); m_readPosition += (8 * byteLen); return retval; } - if (byteLen <= c_bufferSize) { - byte[] buffer = (byte[]) Interlocked.Exchange(ref s_buffer, null) ?? new byte[c_bufferSize]; - ReadBytes(buffer, 0, byteLen); - string retval = Encoding.UTF8.GetString(buffer, 0, byteLen); - s_buffer = buffer; - return retval; - } else { - byte[] bytes = ReadBytes(byteLen); - return Encoding.UTF8.GetString(bytes, 0, bytes.Length); - } + var bytes = ReadBytes(stackalloc byte[byteLen]); + return Encoding.UTF8.GetString(bytes); } /// @@ -643,19 +572,13 @@ namespace Lidgren.Network if ((m_readPosition & 7) == 0) { // read directly - result = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, (int)byteLen); + result = Encoding.UTF8.GetString(m_data.Slice(m_readPosition >> 3, (int)byteLen)); m_readPosition += (8 * (int)byteLen); return true; } - byte[] bytes; - if (ReadBytes((int)byteLen, out bytes) == false) - { - result = String.Empty; - return false; - } - - result = System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length); + var bytes = ReadBytes(stackalloc byte[(int)byteLen]); + result = Encoding.UTF8.GetString(bytes); return true; } @@ -679,7 +602,7 @@ namespace Lidgren.Network public NetEndPoint ReadIPEndPoint() { byte len = ReadByte(); - byte[] addressBytes = ReadBytes(len); + var addressBytes = ReadBytes(stackalloc byte[len]); int port = (int)ReadUInt16(); var address = NetUtility.CreateAddressFromBytes(addressBytes); diff --git a/Lidgren.Network/NetBuffer.Write.cs b/Lidgren.Network/NetBuffer.Write.cs index 9673594e5..5bad1a458 100644 --- a/Lidgren.Network/NetBuffer.Write.cs +++ b/Lidgren.Network/NetBuffer.Write.cs @@ -19,7 +19,10 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR TH USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; +using System.Buffers; +using System.Buffers.Binary; using System.Collections.Generic; +using System.IO; using System.Net; using System.Reflection; using System.Text; @@ -55,14 +58,20 @@ namespace Lidgren.Network public void EnsureBufferSize(int numberOfBits) { int byteLen = ((numberOfBits + 7) >> 3); - if (m_data == null) + if (m_buf == null) { - m_data = new byte[byteLen + c_overAllocateAmount]; + m_buf = ArrayPool.Shared.Rent(byteLen + c_overAllocateAmount); return; } - if (m_data.Length < byteLen) - Array.Resize(ref m_data, byteLen + c_overAllocateAmount); - return; + + if (m_buf.Length < byteLen) + { + var pool = ArrayPool.Shared; + var oldBuf = m_buf; + m_buf = pool.Rent(byteLen + c_overAllocateAmount); + new Span(oldBuf).CopyTo(m_buf); + pool.Return(oldBuf); + } } /// @@ -71,14 +80,20 @@ namespace Lidgren.Network internal void InternalEnsureBufferSize(int numberOfBits) { int byteLen = ((numberOfBits + 7) >> 3); - if (m_data == null) + if (m_buf == null) { - m_data = new byte[byteLen]; + m_buf = ArrayPool.Shared.Rent(byteLen); return; } - if (m_data.Length < byteLen) - Array.Resize(ref m_data, byteLen); - return; + + if (m_buf.Length < byteLen) + { + var pool = ArrayPool.Shared; + var oldBuf = m_buf; + m_buf = pool.Rent(byteLen); + new Span(oldBuf).CopyTo(m_buf); + pool.Return(oldBuf); + } } /// @@ -134,12 +149,49 @@ namespace Lidgren.Network } /// - /// Writes all bytes in an array + /// Writes a number of zeroed bytes /// - public void Write(byte[] source) + public void Zero(int length) + { + if (length < 0) + throw new ArgumentOutOfRangeException(nameof(length), length, "Must be positive."); + int bits = length * 8; + EnsureBufferSize(m_bitLength + bits); + NetBitWriter.Zero(bits, m_data, m_bitLength); + m_bitLength += bits; + } + + + /// + /// Creates a stream out of a constrained region of the buffer for writing. + /// + /// The length of the constrained region in bytes. + /// A writable constrained buffer region wrapped by a stream. + public Stream WriteAsStream(int byteLength) + { + var bitLength = byteLength*8; + EnsureBufferSize(m_bitLength + byteLength); + var stream = new WriteOnlyWrapperStream(this, m_bitLength, bitLength); + m_bitLength += bitLength; + return stream; + } + + /// + /// Creates a stream for appending to the end of the buffer. + /// + /// A writable stream that appends to the buffer. + public Stream AppendViaStream() + { + return new AppenderStream(this); + } + + /// + /// Writes all bytes in an array. + /// + public void Write(ReadOnlySpan source) { if (source == null) - throw new ArgumentNullException("source"); + throw new ArgumentNullException(nameof(source)); int bits = source.Length * 8; EnsureBufferSize(m_bitLength + bits); NetBitWriter.WriteBytes(source, 0, source.Length, m_data, m_bitLength); @@ -149,10 +201,10 @@ namespace Lidgren.Network /// /// Writes the specified number of bytes from an array /// - public void Write(byte[] source, int offsetInBytes, int numberOfBytes) + public void Write(Span source, int offsetInBytes, int numberOfBytes) { if (source == null) - throw new ArgumentNullException("source"); + throw new ArgumentNullException(nameof(source)); int bits = numberOfBytes * 8; EnsureBufferSize(m_bitLength + bits); NetBitWriter.WriteBytes(source, offsetInBytes, numberOfBytes, m_data, m_bitLength); @@ -216,39 +268,24 @@ namespace Lidgren.Network m_bitLength = newBitLength; } -#if UNSAFE - /// - /// Writes a 32 bit signed integer - /// - public unsafe void Write(Int32 source) - { - EnsureBufferSize(m_bitLength + 32); - - // can write fast? - if (m_bitLength % 8 == 0) - { - fixed (byte* numRef = &Data[m_bitLength / 8]) - { - *((int*)numRef) = source; - } - } - else - { - NetBitWriter.WriteUInt32((UInt32)source, 32, Data, m_bitLength); - } - m_bitLength += 32; - } -#else /// /// Writes a 32 bit signed integer /// public void Write(Int32 source) { EnsureBufferSize(m_bitLength + 32); - NetBitWriter.WriteUInt32((UInt32)source, 32, m_data, m_bitLength); + + // can write fast? + if ((m_bitLength & 7) == 0) + { + MemoryMarshal.Write(new Span(Buffer, m_bitLength / 8, 4), ref source); + } + else + { + NetBitWriter.WriteUInt32((UInt32)source, 32, Buffer, m_bitLength); + } m_bitLength += 32; } -#endif /// /// Writes a 32 bit signed integer at a given offset in the buffer @@ -261,41 +298,25 @@ namespace Lidgren.Network m_bitLength = newBitLength; } -#if UNSAFE /// /// Writes a 32 bit unsigned integer /// - public unsafe void Write(UInt32 source) + public void Write(UInt32 source) { EnsureBufferSize(m_bitLength + 32); // can write fast? - if (m_bitLength % 8 == 0) + if ((m_bitLength & 7) == 0) { - fixed (byte* numRef = &Data[m_bitLength / 8]) - { - *((uint*)numRef) = source; - } + MemoryMarshal.Write(new Span(Buffer, m_bitLength / 8, 4), ref source); } else { - NetBitWriter.WriteUInt32(source, 32, Data, m_bitLength); + NetBitWriter.WriteUInt32(source, 32, Buffer, m_bitLength); } m_bitLength += 32; } -#else - /// - /// Writes a 32 bit unsigned integer - /// - [CLSCompliant(false)] - public void Write(UInt32 source) - { - EnsureBufferSize(m_bitLength + 32); - NetBitWriter.WriteUInt32(source, 32, m_data, m_bitLength); - m_bitLength += 32; - } -#endif /// /// Writes a 32 bit unsigned integer at a given offset in the buffer @@ -403,79 +424,33 @@ namespace Lidgren.Network // // Floating point // -#if UNSAFE - /// - /// Writes a 32 bit floating point value - /// - public unsafe void Write(float source) - { - uint val = *((uint*)&source); -#if BIGENDIAN - val = NetUtility.SwapByteOrder(val); -#endif - Write(val); - } -#else /// /// Writes a 32 bit floating point value /// public void Write(float source) { - // Use union to avoid BitConverter.GetBytes() which allocates memory on the heap - SingleUIntUnion su; - su.UIntValue = 0; // must initialize every member of the union to avoid warning - su.SingleValue = source; - -#if BIGENDIAN - // swap byte order - su.UIntValue = NetUtility.SwapByteOrder(su.UIntValue); -#endif - Write(su.UIntValue); - } -#endif - -#if UNSAFE - /// - /// Writes a 64 bit floating point value - /// - public unsafe void Write(double source) - { - ulong val = *((ulong*)&source); -#if BIGENDIAN - val = NetUtility.SwapByteOrder(val); -#endif + uint val = MemoryMarshal.Cast(MemoryMarshal.CreateReadOnlySpan(ref source, 1))[0]; + if (!BitConverter.IsLittleEndian) + { + val = BinaryPrimitives.ReverseEndianness(val); + } Write(val); } -#else + /// /// Writes a 64 bit floating point value /// public void Write(double source) { - byte[] val = BitConverter.GetBytes(source); -#if BIGENDIAN - // 0 1 2 3 4 5 6 7 + ulong val = MemoryMarshal.Cast(MemoryMarshal.CreateReadOnlySpan(ref source, 1))[0]; - // swap byte order - byte tmp = val[7]; - val[7] = val[0]; - val[0] = tmp; + if (!BitConverter.IsLittleEndian) + { + val = BinaryPrimitives.ReverseEndianness(val); + } - tmp = val[6]; - val[6] = val[1]; - val[1] = tmp; - - tmp = val[5]; - val[5] = val[2]; - val[2] = tmp; - - tmp = val[4]; - val[4] = val[3]; - val[3] = tmp; -#endif Write(val); } -#endif // // Variable bits @@ -624,7 +599,9 @@ namespace Lidgren.Network return; } - byte[] bytes = Encoding.UTF8.GetBytes(source); + // ReSharper disable once SuggestVarOrType_Elsewhere + Span bytes = stackalloc byte[Encoding.UTF8.GetByteCount(source)]; + Encoding.UTF8.GetBytes(source, bytes); EnsureBufferSize(m_bitLength + 8 + (bytes.Length * 8)); WriteVariableUInt32((uint)bytes.Length); Write(bytes); @@ -692,7 +669,7 @@ namespace Lidgren.Network Write(buffer.m_data, 0, buffer.LengthBytes); // did we write excessive bits? - int bitsInLastByte = (buffer.m_bitLength % 8); + int bitsInLastByte = (buffer.m_bitLength & 7); if (bitsInLastByte != 0) { int excessBits = 8 - bitsInLastByte; diff --git a/Lidgren.Network/NetBuffer.cs b/Lidgren.Network/NetBuffer.cs index 34c783fde..e52cfdd94 100644 --- a/Lidgren.Network/NetBuffer.cs +++ b/Lidgren.Network/NetBuffer.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; @@ -14,17 +15,17 @@ namespace Lidgren.Network private static readonly Dictionary s_readMethods; private static readonly Dictionary s_writeMethods; - internal byte[] m_data; + internal byte[] m_buf; + internal Span m_data => m_buf; internal int m_bitLength; internal int m_readPosition; /// /// Gets or sets the internal data buffer /// - public byte[] Data + public byte[] Buffer { - get { return m_data; } - set { m_data = value; } + get { return m_buf; } } /// @@ -94,5 +95,6 @@ namespace Lidgren.Network } } } + } } diff --git a/Lidgren.Network/NetConnection.Handshake.cs b/Lidgren.Network/NetConnection.Handshake.cs index c84605741..32906eda2 100644 --- a/Lidgren.Network/NetConnection.Handshake.cs +++ b/Lidgren.Network/NetConnection.Handshake.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Text; using System.Threading; @@ -200,12 +201,12 @@ namespace Lidgren.Network { if (m_localHailMessage != null) { - byte[] hi = m_localHailMessage.Data; + var hi = m_localHailMessage.Buffer; if (hi != null && hi.Length >= m_localHailMessage.LengthBytes) { if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10) m_peer.ThrowOrLog("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes)); - om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes); + om.Write(m_localHailMessage.Buffer, 0, m_localHailMessage.LengthBytes); } } } @@ -283,20 +284,19 @@ namespace Lidgren.Network { m_peer.VerifyNetworkThread(); - byte[] hail; switch (tp) { case NetMessageType.Connect: if (m_status == NetConnectionStatus.ReceivedInitiation) { // Whee! Server full has already been checked - bool ok = ValidateHandshakeData(ptr, payloadLength, out hail); + bool ok = ValidateHandshakeData(ptr, payloadLength, out var hail); if (ok) { if (hail != null) { m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail); - m_remoteHailMessage.LengthBits = (hail.Length * 8); + m_remoteHailMessage.LengthBits = (hail.Count * 8); } else { @@ -402,18 +402,17 @@ namespace Lidgren.Network private void HandleConnectResponse(double now, NetMessageType tp, int ptr, int payloadLength) { - byte[] hail; switch (m_status) { case NetConnectionStatus.InitiatedConnect: // awesome - bool ok = ValidateHandshakeData(ptr, payloadLength, out hail); + bool ok = ValidateHandshakeData(ptr, payloadLength, out var hail); if (ok) { if (hail != null) { m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail); - m_remoteHailMessage.LengthBits = (hail.Length * 8); + m_remoteHailMessage.LengthBits = (hail.Count * 8); } else { @@ -441,7 +440,7 @@ namespace Lidgren.Network } } - private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail) + private bool ValidateHandshakeData(int ptr, int payloadLength, out ArraySegment hail) { hail = null; @@ -455,7 +454,7 @@ namespace Lidgren.Network int remainingBytes = payloadLength - (msg.PositionInBytes - ptr); if (remainingBytes > 0) - hail = msg.ReadBytes(remainingBytes); + msg.ReadBytes(hail = new ArraySegment(ArrayPool.Shared.Rent(remainingBytes),0, remainingBytes)); if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier) { diff --git a/Lidgren.Network/NetConnection.MTU.cs b/Lidgren.Network/NetConnection.MTU.cs index d5f64da04..a30ebec96 100644 --- a/Lidgren.Network/NetConnection.MTU.cs +++ b/Lidgren.Network/NetConnection.MTU.cs @@ -104,8 +104,7 @@ namespace Lidgren.Network private void SendExpandMTU(double now, int size) { NetOutgoingMessage om = m_peer.CreateMessage(size); - byte[] tmp = new byte[size]; - om.Write(tmp); + om.Zero(size); om.m_messageType = NetMessageType.ExpandMTURequest; int len = om.Encode(m_peer.m_sendBuffer, 0, 0); diff --git a/Lidgren.Network/NetConnectionStatistics.cs b/Lidgren.Network/NetConnectionStatistics.cs index 7f3145464..4ee84e448 100644 --- a/Lidgren.Network/NetConnectionStatistics.cs +++ b/Lidgren.Network/NetConnectionStatistics.cs @@ -76,45 +76,113 @@ namespace Lidgren.Network /// /// Gets the number of sent packets for this connection /// - public long SentPackets { get { return m_sentPackets; } } + public long SentPackets => m_sentPackets; /// /// Gets the number of received packets for this connection /// - public long ReceivedPackets { get { return m_receivedPackets; } } + public long ReceivedPackets => m_receivedPackets; /// /// Gets the number of sent bytes for this connection /// - public long SentBytes { get { return m_sentBytes; } } + public long SentBytes => m_sentBytes; /// /// Gets the number of received bytes for this connection /// - public long ReceivedBytes { get { return m_receivedBytes; } } + public long ReceivedBytes => m_receivedBytes; - /// + /// /// Gets the number of sent messages for this connection /// - public long SentMessages { get { return m_sentMessages; } } + public long SentMessages => m_sentMessages; - /// + /// /// Gets the number of received messages for this connection /// - public long ReceivedMessages { get { return m_receivedMessages; } } + public long ReceivedMessages => m_receivedMessages; /// /// Gets the number of resent reliable messages for this connection /// - public long ResentMessages { get { return m_resentMessagesDueToHole + m_resentMessagesDueToDelay; } } + public long ResentMessages => m_resentMessagesDueToHole + m_resentMessagesDueToDelay; - /// + /// + /// Gets the number of resent reliable messages for this connection due to holes + /// + public long ResentMessagesDueToHoles => m_resentMessagesDueToHole; + + /// + /// Gets the number of resent reliable messages for this connection due to delays + /// + public long ResentMessagesDueToDelays => m_resentMessagesDueToDelay; + + /// /// Gets the number of dropped messages for this connection /// - public long DroppedMessages { get { return m_droppedMessages; } } + public long DroppedMessages => m_droppedMessages; + + /// + /// Gets the number of dropped messages for this connection + /// + public long ReceivedFragments => m_receivedFragments; + // public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } } + /// + /// Gets the number of withheld messages for this connection + /// + private int WithheldMessages + { + get + { + int numWithheld = 0; + foreach (NetReceiverChannelBase recChan in m_connection.m_receiveChannels) + { + var relRecChan = recChan as NetReliableOrderedReceiver; + if (relRecChan == null) + { + continue; + } + + for (int i = 0; i < relRecChan.m_withheldMessages.Length; i++) + if (relRecChan.m_withheldMessages[i] != null) + numWithheld++; + } + + return numWithheld; + } + } + + /// + /// Gets the number of unsent and stored messages for this connection + /// + private void GetUnsentAndStoredMessages(out int numUnsent, out int numStored) + { + numUnsent = 0; + numStored = 0; + foreach (NetSenderChannelBase sendChan in m_connection.m_sendChannels) + { + if (sendChan == null) + continue; + + numUnsent += sendChan.QueuedSendsCount; + + var relSendChan = sendChan as NetReliableSenderChannel; + if (relSendChan == null) + { + continue; + } + + for (int i = 0; i < relSendChan.m_storedMessages.Length; i++) + if (relSendChan.m_storedMessages[i].Message != null) + numStored++; + } + } + + #if !USE_RELEASE_STATISTICS [Conditional("DEBUG")] #endif @@ -157,57 +225,26 @@ namespace Lidgren.Network m_droppedMessages++; } + /// /// Returns a string that represents this object /// public override string ToString() { - StringBuilder bdr = new StringBuilder(); - //bdr.AppendLine("Average roundtrip time: " + NetTime.ToReadable(m_connection.m_averageRoundtripTime)); - bdr.AppendLine("Current MTU: " + m_connection.m_currentMTU); - bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets"); - bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages (of which " + m_receivedFragments + " fragments) in " + m_receivedPackets + " packets"); - bdr.AppendLine("Dropped " + m_droppedMessages + " messages (dupes/late/early)"); + GetUnsentAndStoredMessages(out var numUnsent, out var numStored); - if (m_resentMessagesDueToDelay > 0) - bdr.AppendLine("Resent messages (delay): " + m_resentMessagesDueToDelay); - if (m_resentMessagesDueToHole > 0) - bdr.AppendLine("Resent messages (holes): " + m_resentMessagesDueToHole); + var numWithheld = WithheldMessages; - int numUnsent = 0; - int numStored = 0; - foreach (NetSenderChannelBase sendChan in m_connection.m_sendChannels) - { - if (sendChan == null) - continue; - numUnsent += sendChan.QueuedSendsCount; - - var relSendChan = sendChan as NetReliableSenderChannel; - if (relSendChan != null) - { - for (int i = 0; i < relSendChan.m_storedMessages.Length; i++) - if (relSendChan.m_storedMessages[i].Message != null) - numStored++; - } - } - - int numWithheld = 0; - foreach (NetReceiverChannelBase recChan in m_connection.m_receiveChannels) - { - var relRecChan = recChan as NetReliableOrderedReceiver; - if (relRecChan != null) - { - for (int i = 0; i < relRecChan.m_withheldMessages.Length; i++) - if (relRecChan.m_withheldMessages[i] != null) - numWithheld++; - } - } - - bdr.AppendLine("Unsent messages: " + numUnsent); - bdr.AppendLine("Stored messages: " + numStored); - bdr.AppendLine("Withheld messages: " + numWithheld); - - return bdr.ToString(); + return @$"Current MTU: {m_connection.m_currentMTU} +Sent {m_sentBytes} bytes in {m_sentMessages} messages in {m_sentPackets} packets +Received {m_receivedBytes} bytes in {m_receivedMessages} messages (of which {m_receivedFragments} fragments) in {m_receivedPackets} packets +Dropped {m_droppedMessages} messages (dupes/late/early) +Resent messages (delay): {m_resentMessagesDueToDelay} +Resent messages (holes): {m_resentMessagesDueToHole} +Unsent messages: {numUnsent} +Stored messages: {numStored} +Withheld messages: {numWithheld}"; } + } } \ No newline at end of file diff --git a/Lidgren.Network/NetFragmentationHelper.cs b/Lidgren.Network/NetFragmentationHelper.cs index 85efe4d13..cd82f9cb6 100644 --- a/Lidgren.Network/NetFragmentationHelper.cs +++ b/Lidgren.Network/NetFragmentationHelper.cs @@ -5,7 +5,7 @@ namespace Lidgren.Network internal static class NetFragmentationHelper { internal static int WriteHeader( - byte[] destination, + Span destination, int ptr, int group, int totalBits, @@ -50,7 +50,7 @@ namespace Lidgren.Network return ptr; } - internal static int ReadHeader(byte[] buffer, int ptr, out int group, out int totalBits, out int chunkByteSize, out int chunkNumber) + internal static int ReadHeader(Span buffer, int ptr, out int group, out int totalBits, out int chunkByteSize, out int chunkNumber) { int num1 = 0; int num2 = 0; diff --git a/Lidgren.Network/NetOutgoingMessage.cs b/Lidgren.Network/NetOutgoingMessage.cs index d982cfdba..9f302b25a 100644 --- a/Lidgren.Network/NetOutgoingMessage.cs +++ b/Lidgren.Network/NetOutgoingMessage.cs @@ -57,7 +57,7 @@ namespace Lidgren.Network m_fragmentGroup = 0; } - internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber) + internal int Encode(Span intoBuffer, int ptr, int sequenceNumber) { // 8 bits - NetMessageType // 1 bit - Fragment? @@ -78,7 +78,9 @@ namespace Lidgren.Network int byteLen = NetUtility.BytesToHoldBits(m_bitLength); if (byteLen > 0) { - Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen); + m_data.Slice(0,byteLen) + .CopyTo(intoBuffer.Slice(ptr,byteLen)); + //Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen); ptr += byteLen; } } @@ -102,7 +104,9 @@ namespace Lidgren.Network int byteLen = NetUtility.BytesToHoldBits(m_bitLength); if (byteLen > 0) { - Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen); + m_data.Slice(m_fragmentChunkNumber * m_fragmentChunkByteSize,byteLen) + .CopyTo(intoBuffer.Slice(ptr, byteLen)); + //Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen); ptr += byteLen; } } diff --git a/Lidgren.Network/NetPeer.Fragmentation.cs b/Lidgren.Network/NetPeer.Fragmentation.cs index 2628b8e68..b486e528c 100644 --- a/Lidgren.Network/NetPeer.Fragmentation.cs +++ b/Lidgren.Network/NetPeer.Fragmentation.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Threading; using System.Collections.Generic; @@ -53,7 +54,7 @@ namespace Lidgren.Network NetOutgoingMessage chunk = CreateMessage(0); chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft); - chunk.m_data = msg.m_data; + chunk.m_buf = msg.m_buf; chunk.m_fragmentGroup = group; chunk.m_fragmentGroupTotalBits = totalBytes * 8; chunk.m_fragmentChunkByteSize = bytesPerChunk; @@ -127,9 +128,11 @@ namespace Lidgren.Network ReceivedFragmentGroup info; if (!groups.TryGetValue(group, out info)) { - info = new ReceivedFragmentGroup(); - info.Data = new byte[totalBytes]; - info.ReceivedChunks = new NetBitVector(totalNumChunks); + info = new ReceivedFragmentGroup + { + Data = ArrayPool.Shared.Rent(totalBytes), + ReceivedChunks = new NetBitVector(totalNumChunks) + }; groups[group] = info; } @@ -138,7 +141,9 @@ namespace Lidgren.Network // copy to data int offset = (chunkNumber * chunkByteSize); - Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr); + //Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr); + im.m_data.Slice(ptr,im.LengthBytes - ptr) + .CopyTo(new Span(info.Data,offset,im.LengthBytes - ptr)); int cnt = info.ReceivedChunks.Count(); //LogVerbose("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")"); @@ -148,7 +153,7 @@ namespace Lidgren.Network if (info.ReceivedChunks.Count() == totalNumChunks) { // Done! Transform this incoming message - im.m_data = info.Data; + im.m_buf = info.Data; im.m_bitLength = (int)totalBits; im.m_isFragment = false; diff --git a/Lidgren.Network/NetPeer.Internal.cs b/Lidgren.Network/NetPeer.Internal.cs index feb7c56da..3d0ffd899 100644 --- a/Lidgren.Network/NetPeer.Internal.cs +++ b/Lidgren.Network/NetPeer.Internal.cs @@ -1,11 +1,12 @@ using System; +using System.Buffers; using System.Net; using System.Threading; using System.Diagnostics; using System.Security.Cryptography; using System.Net.Sockets; using System.Collections.Generic; - +using System.Runtime.InteropServices; #if !__NOIPENDPOINT__ using NetEndPoint = System.Net.IPEndPoint; #endif @@ -152,7 +153,7 @@ namespace Lidgren.Network const uint IOC_IN = 0x80000000; const uint IOC_VENDOR = 0x18000000; uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12; - m_socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null); + m_socket.IOControl((int)SIO_UDP_CONNRESET, new[] { Convert.ToByte(false) }, null); } catch { @@ -191,19 +192,25 @@ namespace Lidgren.Network // bind to socket BindSocket(false); - m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize]; - m_sendBuffer = new byte[m_configuration.SendBufferSize]; - m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error); - m_readHelperMessage.m_data = m_receiveBuffer; + m_receiveBuffer = ArrayPool.Shared.Rent(m_configuration.ReceiveBufferSize); + m_sendBuffer = ArrayPool.Shared.Rent(m_configuration.SendBufferSize); + m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error) + { + m_buf = m_receiveBuffer + }; - byte[] macBytes = NetUtility.GetMacAddressBytes(); + var macBytes = NetUtility.GetMacAddressBytes(); var boundEp = m_socket.LocalEndPoint as NetEndPoint; - byte[] epBytes = BitConverter.GetBytes(boundEp.GetHashCode()); - byte[] combined = new byte[epBytes.Length + macBytes.Length]; - Array.Copy(epBytes, 0, combined, 0, epBytes.Length); - Array.Copy(macBytes, 0, combined, epBytes.Length, macBytes.Length); - m_uniqueIdentifier = BitConverter.ToInt64(NetUtility.ComputeSHAHash(combined), 0); + var hashCode = boundEp!.GetHashCode(); + var epBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref hashCode,1)); + // ReSharper disable once SuggestVarOrType_Elsewhere + Span combined = stackalloc byte[epBytes.Length + macBytes.Length]; + //Array.Copy(epBytes, 0, combined, 0, epBytes.Length); + epBytes.CopyTo(combined.Slice(0,epBytes.Length)); + //Array.Copy(macBytes, 0, combined, epBytes.Length, macBytes.Length); + macBytes.CopyTo(combined.Slice(epBytes.Length, macBytes.Length)); + m_uniqueIdentifier = MemoryMarshal.Cast(NetUtility.ComputeSHAHash(combined, stackalloc byte[32]))[0]; m_status = NetPeerStatus.Running; } @@ -556,7 +563,9 @@ namespace Lidgren.Network msg.m_senderEndPoint = ipsender; msg.m_bitLength = payloadBitLength; - Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength); + //Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength); + new Memory(m_receiveBuffer,ptr,payloadByteLength).Span + .CopyTo(msg.m_data.Slice(0, payloadByteLength)); if (sender != null) { if (tp == NetMessageType.Unconnected) @@ -606,7 +615,12 @@ namespace Lidgren.Network { NetIncomingMessage dm = CreateIncomingMessage(NetIncomingMessageType.DiscoveryRequest, payloadByteLength); if (payloadByteLength > 0) - Buffer.BlockCopy(m_receiveBuffer, ptr, dm.m_data, 0, payloadByteLength); + { + //Buffer.BlockCopy(m_receiveBuffer, ptr, dm.m_data, 0, payloadByteLength); + new Memory(m_receiveBuffer, ptr, payloadByteLength).Span + .CopyTo(dm.m_data.Slice(0, payloadByteLength)); + } + dm.m_receiveTime = now; dm.m_bitLength = payloadByteLength * 8; dm.m_senderEndPoint = senderEndPoint; @@ -620,7 +634,11 @@ namespace Lidgren.Network { NetIncomingMessage dr = CreateIncomingMessage(NetIncomingMessageType.DiscoveryResponse, payloadByteLength); if (payloadByteLength > 0) - Buffer.BlockCopy(m_receiveBuffer, ptr, dr.m_data, 0, payloadByteLength); + { + //Buffer.BlockCopy(m_receiveBuffer, ptr, dr.m_data, 0, payloadByteLength); + new Memory(m_receiveBuffer, ptr, payloadByteLength).Span + .CopyTo(dr.m_data.Slice(0, payloadByteLength)); + } dr.m_receiveTime = now; dr.m_bitLength = payloadByteLength * 8; dr.m_senderEndPoint = senderEndPoint; diff --git a/Lidgren.Network/NetPeer.LatencySimulation.cs b/Lidgren.Network/NetPeer.LatencySimulation.cs index 6ef891f99..a3ad69a64 100644 --- a/Lidgren.Network/NetPeer.LatencySimulation.cs +++ b/Lidgren.Network/NetPeer.LatencySimulation.cs @@ -20,6 +20,7 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. //#define USE_RELEASE_STATISTICS using System; +using System.Buffers; using System.Collections.Generic; using System.Net; using System.Net.Sockets; @@ -89,8 +90,10 @@ namespace Lidgren.Network // Enqueue delayed packet DelayedPacket p = new DelayedPacket(); p.Target = target; - p.Data = new byte[numBytes]; - Buffer.BlockCopy(m_sendBuffer, 0, p.Data, 0, numBytes); + p.Data = ArrayPool.Shared.Rent(numBytes); + //Buffer.BlockCopy(m_sendBuffer, 0, p.Data, 0, numBytes); + new Memory(m_sendBuffer, 0, numBytes) + .CopyTo(p.Data); p.DelayedUntil = NetTime.Now + delay; m_delayedPackets.Add(p); @@ -113,8 +116,9 @@ namespace Lidgren.Network { if (now > p.DelayedUntil) { - ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset); + ActuallySendPacket(new Span(p.Data), p.Data.Length, p.Target, out connectionReset); m_delayedPackets.Remove(p); + ArrayPool.Shared.Return(p.Data); goto RestartDelaySending; } } @@ -126,7 +130,10 @@ namespace Lidgren.Network { bool connectionReset; foreach (DelayedPacket p in m_delayedPackets) - ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset); + { + ActuallySendPacket(new Span(p.Data), p.Data.Length, p.Target, out connectionReset); + ArrayPool.Shared.Return(p.Data); + } m_delayedPackets.Clear(); } catch { } @@ -135,7 +142,7 @@ namespace Lidgren.Network //Avoids allocation on mapping to IPv6 private IPEndPoint targetCopy = new IPEndPoint(IPAddress.Any, 0); - internal bool ActuallySendPacket(byte[] data, int numBytes, NetEndPoint target, out bool connectionReset) + internal bool ActuallySendPacket(ReadOnlySpan data, int numBytes, NetEndPoint target, out bool connectionReset) { connectionReset = false; IPAddress ba = default(IPAddress); @@ -161,7 +168,7 @@ namespace Lidgren.Network targetCopy.Address = target.Address; } - int bytesSent = m_socket.SendTo(data, 0, numBytes, SocketFlags.None, targetCopy); + int bytesSent = m_socket.SendTo(data.Slice( 0, numBytes ), 0, targetCopy); if (numBytes != bytesSent) LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!"); diff --git a/Lidgren.Network/NetPeer.MessagePools.cs b/Lidgren.Network/NetPeer.MessagePools.cs index ea7020884..02d30a09c 100644 --- a/Lidgren.Network/NetPeer.MessagePools.cs +++ b/Lidgren.Network/NetPeer.MessagePools.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Text; @@ -6,27 +7,21 @@ namespace Lidgren.Network { public partial class NetPeer { - internal List m_storagePool; private NetQueue m_outgoingMessagesPool; private NetQueue m_incomingMessagesPool; - internal int m_storagePoolBytes; - internal int m_storageSlotsUsedCount; private int m_maxCacheCount; private void InitializePools() { - m_storageSlotsUsedCount = 0; if (m_configuration.UseMessageRecycling) { - m_storagePool = new List(16); m_outgoingMessagesPool = new NetQueue(4); m_incomingMessagesPool = new NetQueue(4); } else { - m_storagePool = null; m_outgoingMessagesPool = null; m_incomingMessagesPool = null; } @@ -36,63 +31,12 @@ namespace Lidgren.Network internal byte[] GetStorage(int minimumCapacityInBytes) { - if (m_storagePool == null) - return new byte[minimumCapacityInBytes]; - - lock (m_storagePool) - { - for (int i = 0; i < m_storagePool.Count; i++) - { - byte[] retval = m_storagePool[i]; - if (retval != null && retval.Length >= minimumCapacityInBytes) - { - m_storagePool[i] = null; - m_storageSlotsUsedCount--; - m_storagePoolBytes -= retval.Length; - return retval; - } - } - } - m_statistics.m_bytesAllocated += minimumCapacityInBytes; - return new byte[minimumCapacityInBytes]; + return ArrayPool.Shared.Rent(minimumCapacityInBytes); } internal void Recycle(byte[] storage) { - if (m_storagePool == null || storage == null) - return; - - lock (m_storagePool) - { - int cnt = m_storagePool.Count; - for (int i = 0; i < cnt; i++) - { - if (m_storagePool[i] == null) - { - m_storageSlotsUsedCount++; - m_storagePoolBytes += storage.Length; - m_storagePool[i] = storage; - return; - } - } - - if (m_storagePool.Count >= m_maxCacheCount) - { - // pool is full; replace randomly chosen entry to keep size distribution - var idx = NetRandom.Instance.Next(m_storagePool.Count); - - m_storagePoolBytes -= m_storagePool[idx].Length; - m_storagePoolBytes += storage.Length; - - m_storagePool[idx] = storage; // replace - } - else - { - m_storageSlotsUsedCount++; - m_storagePoolBytes += storage.Length; - m_storagePool.Add(storage); - } - } + ArrayPool.Shared.Return(storage); } /// @@ -137,19 +81,19 @@ namespace Lidgren.Network NetException.Assert(retval.m_recyclingCount == 0, "Wrong recycling count! Should be zero" + retval.m_recyclingCount); if (initialCapacity > 0) - retval.m_data = GetStorage(initialCapacity); + retval.m_buf = GetStorage(initialCapacity); return retval; } - internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] useStorageData) + internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, ArraySegment useStorageData) { NetIncomingMessage retval; if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval)) retval = new NetIncomingMessage(tp); else retval.m_incomingMessageType = tp; - retval.m_data = useStorageData; + retval.m_buf = useStorageData.Array; return retval; } @@ -160,7 +104,7 @@ namespace Lidgren.Network retval = new NetIncomingMessage(tp); else retval.m_incomingMessageType = tp; - retval.m_data = GetStorage(minimumByteSize); + retval.m_buf = GetStorage(minimumByteSize); return retval; } @@ -174,8 +118,8 @@ namespace Lidgren.Network NetException.Assert(m_incomingMessagesPool.Contains(msg) == false, "Recyling already recycled incoming message! Thread race?"); - byte[] storage = msg.m_data; - msg.m_data = null; + byte[] storage = msg.m_buf; + msg.m_buf = null; Recycle(storage); msg.Reset(); @@ -207,8 +151,8 @@ namespace Lidgren.Network // however, in RELEASE, we'll just have to accept this and move on with life msg.m_recyclingCount = 0; - byte[] storage = msg.m_data; - msg.m_data = null; + byte[] storage = msg.m_buf; + msg.m_buf = null; // message fragments cannot be recycled // TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them diff --git a/Lidgren.Network/NetPeerStatistics.cs b/Lidgren.Network/NetPeerStatistics.cs index c55dafc73..a3bcbd863 100644 --- a/Lidgren.Network/NetPeerStatistics.cs +++ b/Lidgren.Network/NetPeerStatistics.cs @@ -44,8 +44,6 @@ namespace Lidgren.Network internal int m_sentBytes; internal int m_receivedBytes; - internal long m_bytesAllocated; - internal NetPeerStatistics(NetPeer peer) { m_peer = peer; @@ -63,56 +61,42 @@ namespace Lidgren.Network m_sentBytes = 0; m_receivedBytes = 0; - - m_bytesAllocated = 0; } /// /// Gets the number of sent packets since the NetPeer was initialized /// - public int SentPackets { get { return m_sentPackets; } } + public int SentPackets => m_sentPackets; /// /// Gets the number of received packets since the NetPeer was initialized /// - public int ReceivedPackets { get { return m_receivedPackets; } } + public int ReceivedPackets => m_receivedPackets; /// /// Gets the number of sent messages since the NetPeer was initialized /// - public int SentMessages { get { return m_sentMessages; } } + public int SentMessages => m_sentMessages; /// /// Gets the number of received messages since the NetPeer was initialized /// - public int ReceivedMessages { get { return m_receivedMessages; } } + public int ReceivedMessages => m_receivedMessages; + + /// + /// Gets the number of received fragments since the NetPeer was initialized + /// + public int ReceivedFragments => m_receivedFragments; /// /// Gets the number of sent bytes since the NetPeer was initialized /// - public int SentBytes { get { return m_sentBytes; } } + public int SentBytes => m_sentBytes; /// /// Gets the number of received bytes since the NetPeer was initialized /// - public int ReceivedBytes { get { return m_receivedBytes; } } - - /// - /// Gets the number of bytes allocated (and possibly garbage collected) for message storage - /// - public long StorageBytesAllocated { get { return m_bytesAllocated; } } - - /// - /// Gets the number of bytes in the recycled pool - /// - public int BytesInRecyclePool - { - get - { - lock (m_peer.m_storagePool) - return m_peer.m_storagePoolBytes; - } - } + public int ReceivedBytes => m_receivedBytes; #if !USE_RELEASE_STATISTICS [Conditional("DEBUG")] @@ -140,19 +124,15 @@ namespace Lidgren.Network /// public override string ToString() { - StringBuilder bdr = new StringBuilder(); - bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections"); #if DEBUG || USE_RELEASE_STATISTICS - bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets"); - bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages (of which " + m_receivedFragments + " fragments) in " + m_receivedPackets + " packets"); + return @$"{m_peer.ConnectionsCount} connections +Sent {m_sentBytes} bytes in {m_sentMessages} messages in {m_sentPackets} packets +Received {m_receivedBytes} bytes in {m_receivedMessages} messages (of which {m_receivedFragments} fragments) in {m_receivedPackets} packets"; #else - bdr.AppendLine("Sent (n/a) bytes in (n/a) messages in (n/a) packets"); - bdr.AppendLine("Received (n/a) bytes in (n/a) messages in (n/a) packets"); + return @$"{m_peer.ConnectionsCount} connections +Sent (n/a) bytes in (n/a) messages in (n/a) packets +Received (n/a) bytes in (n/a) messages in (n/a) packets"; #endif - bdr.AppendLine("Storage allocated " + m_bytesAllocated + " bytes"); - if (m_peer.m_storagePool != null) - bdr.AppendLine("Recycled pool " + m_peer.m_storagePoolBytes + " bytes (" + m_peer.m_storageSlotsUsedCount + " entries)"); - return bdr.ToString(); } } } diff --git a/Lidgren.Network/NetSRP.cs b/Lidgren.Network/NetSRP.cs index ffbfc65fc..662daf8a0 100644 --- a/Lidgren.Network/NetSRP.cs +++ b/Lidgren.Network/NetSRP.cs @@ -24,9 +24,9 @@ namespace Lidgren.Network string two = NetUtility.ToHexString(g.ToByteArrayUnsigned()); string ccstr = one + two.PadLeft(one.Length, '0'); - byte[] cc = NetUtility.ToByteArray(ccstr); + var cc = NetUtility.HexToBytes(ccstr, stackalloc byte[ccstr.Length*2]); - var ccHashed = NetUtility.ComputeSHAHash(cc); + var ccHashed = NetUtility.ComputeSHAHash(cc, stackalloc byte[NetUtility.SHAHashSize]); return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16); } @@ -53,23 +53,29 @@ namespace Lidgren.Network /// /// Computer private key (x) /// - public static byte[] ComputePrivateKey(string username, string password, byte[] salt) + public static byte[] ComputePrivateKey(string username, string password, ReadOnlySpan salt) { - byte[] tmp = Encoding.UTF8.GetBytes(username + ":" + password); - byte[] innerHash = NetUtility.ComputeSHAHash(tmp); + var tmpStr = username + ":" + password; + // ReSharper disable once SuggestVarOrType_Elsewhere + Span tmp = stackalloc byte[Encoding.UTF8.GetByteCount(tmpStr)]; + Encoding.UTF8.GetBytes(tmpStr, tmp); + var innerHash = NetUtility.ComputeSHAHash(tmp, stackalloc byte[32]); - byte[] total = new byte[innerHash.Length + salt.Length]; - Buffer.BlockCopy(salt, 0, total, 0, salt.Length); - Buffer.BlockCopy(innerHash, 0, total, salt.Length, innerHash.Length); + // ReSharper disable once SuggestVarOrType_Elsewhere + Span total = stackalloc byte[innerHash.Length + salt.Length]; + //Buffer.BlockCopy(salt, 0, total, 0, salt.Length); + salt.CopyTo(total); + //Buffer.BlockCopy(innerHash, 0, total, salt.Length, innerHash.Length); + innerHash.CopyTo(total.Slice(salt.Length, innerHash.Length)); // x ie. H(salt || H(username || ":" || password)) - return new NetBigInteger(NetUtility.ToHexString(NetUtility.ComputeSHAHash(total)), 16).ToByteArrayUnsigned(); + return new NetBigInteger(NetUtility.ToHexString(NetUtility.ComputeSHAHash(total, stackalloc byte[32])), 16).ToByteArrayUnsigned(); } /// /// Creates a verifier that the server can later use to authenticate users later on (v) /// - public static byte[] ComputeServerVerifier(byte[] privateKey) + public static byte[] ComputeServerVerifier(ReadOnlySpan privateKey) { NetBigInteger x = new NetBigInteger(NetUtility.ToHexString(privateKey), 16); @@ -82,7 +88,7 @@ namespace Lidgren.Network /// /// Compute client public ephemeral value (A) /// - public static byte[] ComputeClientEphemeral(byte[] clientPrivateEphemeral) // a + public static byte[] ComputeClientEphemeral(ReadOnlySpan clientPrivateEphemeral) // a { // A= g^a (mod N) NetBigInteger a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16); @@ -94,7 +100,7 @@ namespace Lidgren.Network /// /// Compute server ephemeral value (B) /// - public static byte[] ComputeServerEphemeral(byte[] serverPrivateEphemeral, byte[] verifier) // b + public static byte[] ComputeServerEphemeral(ReadOnlySpan serverPrivateEphemeral, ReadOnlySpan verifier) // b { var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16); var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16); @@ -110,7 +116,7 @@ namespace Lidgren.Network /// /// Compute intermediate value (u) /// - public static byte[] ComputeU(byte[] clientPublicEphemeral, byte[] serverPublicEphemeral) + public static byte[] ComputeU(ReadOnlySpan clientPublicEphemeral, ReadOnlySpan serverPublicEphemeral) { // u = SHA-1(A || B) string one = NetUtility.ToHexString(clientPublicEphemeral); @@ -119,9 +125,9 @@ namespace Lidgren.Network int len = 66; // Math.Max(one.Length, two.Length); string ccstr = one.PadLeft(len, '0') + two.PadLeft(len, '0'); - byte[] cc = NetUtility.ToByteArray(ccstr); + var cc = NetUtility.HexToBytes(ccstr, stackalloc byte[ccstr.Length*2]); - var ccHashed = NetUtility.ComputeSHAHash(cc); + var ccHashed = NetUtility.ComputeSHAHash(cc, stackalloc byte[32]); return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16).ToByteArrayUnsigned(); } @@ -129,7 +135,7 @@ namespace Lidgren.Network /// /// Computes the server session value /// - public static byte[] ComputeServerSessionValue(byte[] clientPublicEphemeral, byte[] verifier, byte[] udata, byte[] serverPrivateEphemeral) + public static byte[] ComputeServerSessionValue(ReadOnlySpan clientPublicEphemeral, ReadOnlySpan verifier, ReadOnlySpan udata, ReadOnlySpan serverPrivateEphemeral) { // S = (Av^u) ^ b (mod N) var A = new NetBigInteger(NetUtility.ToHexString(clientPublicEphemeral), 16); @@ -161,9 +167,9 @@ namespace Lidgren.Network /// /// Create XTEA symmetrical encryption object from sessionValue /// - public static NetXtea CreateEncryption(NetPeer peer, byte[] sessionValue) + public static NetXtea CreateEncryption(NetPeer peer, ReadOnlySpan sessionValue) { - var hash = NetUtility.ComputeSHAHash(sessionValue); + var hash = NetUtility.ComputeSHAHash(sessionValue, stackalloc byte[32]); var key = new byte[16]; for(int i=0;i<16;i++) diff --git a/Lidgren.Network/NetUtility.cs b/Lidgren.Network/NetUtility.cs index 600447a43..f43898940 100644 --- a/Lidgren.Network/NetUtility.cs +++ b/Lidgren.Network/NetUtility.cs @@ -23,12 +23,14 @@ using NetAddress = System.Net.IPAddress; #endif using System; +using System.Buffers.Binary; using System.Net; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Security.Cryptography; namespace Lidgren.Network @@ -78,6 +80,13 @@ namespace Lidgren.Network } private static IPAddress s_broadcastAddress; + +#if NON_IEC_UNITS + private static readonly string[] ByteStringSuffixes = { "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; +#else + private static readonly string[] ByteStringSuffixes = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB" }; +#endif + public static IPAddress GetCachedBroadcastAddress() { if (s_broadcastAddress == null) @@ -219,27 +228,62 @@ namespace Lidgren.Network /// /// Create a hex string from an array of bytes /// - public static string ToHexString(byte[] data) + public static string ToHexString(ReadOnlySpan data, int offset, int length) { - return ToHexString(data, 0, data.Length); + return ToHexString(data.Slice(offset, length)); } /// /// Create a hex string from an array of bytes /// - public static string ToHexString(byte[] data, int offset, int length) +#if UNSAFE + public static unsafe string ToHexString(ReadOnlySpan data) { - char[] c = new char[length * 2]; - byte b; - for (int i = 0; i < length; ++i) + var l = data.Length; + fixed (void* p = data) { - b = ((byte)(data[offset + i] >> 4)); - c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30); - b = ((byte)(data[offset + i] & 0xF)); - c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30); + return string.Create(data.Length * 2, (p: (IntPtr) p, l), (c, d) => + { + var s = new ReadOnlySpan((void*) d.p, d.l); + var u = MemoryMarshal.Cast(c); + for (var i = 0; i < l; ++i) + { + var b = s[i]; + var nibLo = b >> 4; + var isDigLo = (nibLo - 10) >> 31; + var chLo = 55 + nibLo + (isDigLo & -7); + var nibHi = b & 0xF; + var isDigHi = (nibHi - 10) >> 31; + var chHi = 55 + nibHi + (isDigHi & -7); + u[i] = (chHi << 16) | chLo; + } + }); } + } +#else + + public static string ToHexString(ReadOnlySpan data) + { + var l = data.Length; + // ReSharper disable once SuggestVarOrType_Elsewhere + Span c = stackalloc char[l*2]; + var u = MemoryMarshal.Cast(c); + + for (var i = 0; i < l; ++i) + { + var b = data[i]; + var nibLo = b >> 4; + var isDigLo = (nibLo - 10) >> 31; + var chLo = 55 + nibLo + (isDigLo & -7); + var nibHi = b & 0xF; + var isDigHi = (nibHi - 10) >> 31; + var chHi = 55 + nibHi + (isDigHi & -7); + u[i] = (chHi << 16) | chLo; + } + return new string(c); } +#endif /// /// Returns true if the endpoint supplied is on the same subnet as this host @@ -276,10 +320,20 @@ namespace Lidgren.Network [CLSCompliant(false)] public static int BitsToHoldUInt(uint value) { - int bits = 1; - while ((value >>= 1) != 0) - bits++; - return bits; +#if NETCOREAPP3_1 + return 32 - System.Numerics.BitOperations.LeadingZeroCount(value); +#else + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value -= ((value >> 1) & 0x55555555U); + value = (value & 0x33333333U) + ((value >> 2) & 0x33333333U); + value = (value + (value >> 4)) & 0x0F0F0F0FU; + value = (value * 0x01010101U) >> 24; + return (int)(value & 127); +#endif } /// @@ -288,10 +342,22 @@ namespace Lidgren.Network [CLSCompliant(false)] public static int BitsToHoldUInt64(ulong value) { - int bits = 1; - while ((value >>= 1) != 0) - bits++; - return bits; +#if NETCOREAPP3_1 + return 64 - System.Numerics.BitOperations.LeadingZeroCount(value); +#else + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + value -= ((value >> 1) & 0x5555555555555555U); + value = (value & 0x3333333333333333U) + + ((value >> 2) & 0x3333333333333333U); + value = (value + (value >> 4)) & 0x0F0F0F0F0F0F0F0FU; + value = (value * 0x0101010101010101U) >> 56; + return (int)(value & 127); +#endif } /// @@ -302,28 +368,6 @@ namespace Lidgren.Network return (numBits + 7) / 8; } - internal static UInt32 SwapByteOrder(UInt32 value) - { - return - ((value & 0xff000000) >> 24) | - ((value & 0x00ff0000) >> 8) | - ((value & 0x0000ff00) << 8) | - ((value & 0x000000ff) << 24); - } - - internal static UInt64 SwapByteOrder(UInt64 value) - { - return - ((value & 0xff00000000000000L) >> 56) | - ((value & 0x00ff000000000000L) >> 40) | - ((value & 0x0000ff0000000000L) >> 24) | - ((value & 0x000000ff00000000L) >> 8) | - ((value & 0x00000000ff000000L) << 8) | - ((value & 0x0000000000ff0000L) << 24) | - ((value & 0x000000000000ff00L) << 40) | - ((value & 0x00000000000000ffL) << 56); - } - internal static bool CompareElements(byte[] one, byte[] two) { if (one.Length != two.Length) @@ -337,12 +381,25 @@ namespace Lidgren.Network /// /// Convert a hexadecimal string to a byte array /// - public static byte[] ToByteArray(String hexString) + public static Span HexToBytes(String hexString, Span buffer) { - byte[] retval = new byte[hexString.Length / 2]; - for (int i = 0; i < hexString.Length; i += 2) - retval[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16); - return retval; + if (buffer.Length < hexString.Length/2) + throw new ArgumentOutOfRangeException(nameof(buffer), buffer.Length,"Buffer too small"); + + var hexStrEnum = hexString.GetEnumerator(); + for (var i = 0; i+1 < hexString.Length; i += 2) + { + hexStrEnum.MoveNext(); + var chHi = hexStrEnum.Current; + hexStrEnum.MoveNext(); + var chLo = hexStrEnum.Current; + buffer[i / 2] = (byte)( + (((chHi & 0xF) << 4) + ((chHi & 0x40)>>2) * 9) + |((chLo & 0xF) + ((chLo & 0x40)>>6) * 9) + ); + } + + return buffer; } /// @@ -350,11 +407,10 @@ namespace Lidgren.Network /// public static string ToHumanReadable(long bytes) { - if (bytes < 4000) // 1-4 kb is printed in bytes - return bytes + " bytes"; - if (bytes < 1000 * 1000) // 4-999 kb is printed in kb - return Math.Round(((double)bytes / 1000.0), 2) + " kilobytes"; - return Math.Round(((double)bytes / (1000.0 * 1000.0)), 2) + " megabytes"; // else megabytes + var index = (long)Math.Round(Math.Max(0,Math.Log(bytes, 1024) - 2/3d), MidpointRounding.AwayFromZero); + var denominator = Math.Pow(1024, index); + + return $"{bytes / denominator:0.##} {ByteStringSuffixes[index]}"; } internal static int RelativeSequenceNumber(int nr, int expected) @@ -460,13 +516,7 @@ namespace Lidgren.Network return bdr.ToString(); } - public static byte[] ComputeSHAHash(byte[] bytes) - { - // this is defined in the platform specific files - return ComputeSHAHash(bytes, 0, bytes.Length); - } - - /// + /// /// Copies from to . Maps to an IPv6 address /// /// Source. diff --git a/Lidgren.Network/Platform/NativeSocket.Structs.cs b/Lidgren.Network/Platform/NativeSocket.Structs.cs new file mode 100644 index 000000000..71804db53 --- /dev/null +++ b/Lidgren.Network/Platform/NativeSocket.Structs.cs @@ -0,0 +1,52 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +#pragma warning disable 649 +namespace Lidgren.Network { + + public static partial class NativeSockets { + + [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")] + [StructLayout(LayoutKind.Sequential, Size = SockAddrStorageSize)] + private struct SockAddr { + + public ushort SocketAddressFamily; + + } + + [SuppressMessage("ReSharper", "NotAccessedField.Local")] + private struct SockAddrIn { + + public ushort SocketAddressFamily; + + public ushort Port; + + public uint InAddr; + + public ulong Zero; + + } + + [StructLayout(LayoutKind.Sequential, Size = 16)] + private struct InAddr6 { + + } + + [SuppressMessage("ReSharper", "NotAccessedField.Local")] + private struct SockAddrIn6 { + + public ushort SocketAddressFamily; + + public ushort Port; + + public uint FlowInfo; + + public InAddr6 InAddr; + + public uint InScopeId; + + } + + } + +} \ No newline at end of file diff --git a/Lidgren.Network/Platform/NativeSockets.cs b/Lidgren.Network/Platform/NativeSockets.cs new file mode 100644 index 000000000..428060ca2 --- /dev/null +++ b/Lidgren.Network/Platform/NativeSockets.cs @@ -0,0 +1,195 @@ +#nullable enable +using System; +using System.Buffers.Binary; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Lidgren.Network +{ + + public static partial class NativeSockets + { + + private const int SockAddrStorageSize = 128; // sockaddr_storage + + private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + internal static unsafe int _SendTo(int socket, byte* buf, int len, int flags, void* to, int toLen) + => IsWindows + ? WindowsSockets.SendTo(socket, buf, len, flags, to, toLen) + : PosixSockets.SendTo(socket, buf, len, flags, to, toLen); + + internal static unsafe int _RecvFrom(int socket, byte* buf, int len, int flags, void* from, int* fromLen) + => IsWindows + ? WindowsSockets.RecvFrom(socket, buf, len, flags, from, fromLen) + : PosixSockets.RecvFrom(socket, buf, len, flags, from, fromLen); + + public static int GetError(Socket socket) + => (int) socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Error); + + public static unsafe int SendTo(this Socket socket, ReadOnlySpan buffer, int flags, IPEndPoint to) + { + switch (to.AddressFamily) + { + default: + throw new NotImplementedException(to.AddressFamily.ToString()); + + case AddressFamily.InterNetwork: + { + var error = 0; + var addrIn = new SockAddrIn(); + var port = (ushort) to.Port; + addrIn.SocketAddressFamily = (ushort) to.AddressFamily; + addrIn.Port = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(port) : port; + if (!to.Address.TryWriteBytes(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref addrIn.InAddr, 1)), out _)) + throw new NotImplementedException("Can't write address."); + + int result; + fixed (byte* pBuf = buffer) + { + result = _SendTo(socket.Handle.ToInt32(), pBuf, buffer.Length, flags, &addrIn, sizeof(SockAddrIn)); + } + + if (result == -1) + { + error = GetError(socket); + if (error == 0) + error = Marshal.GetLastWin32Error(); + } + + if (result == -1) + throw new SocketException(error); + + return result; + } + + case AddressFamily.InterNetworkV6: + { + var error = 0; + var addrIn6 = new SockAddrIn6(); + var port = (ushort) to.Port; + addrIn6.SocketAddressFamily = (ushort) to.AddressFamily; + addrIn6.Port = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(port) : port; + if (!to.Address.TryWriteBytes(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref addrIn6.InAddr, 1)), out _)) + throw new NotImplementedException("Can't write address."); + + addrIn6.InScopeId = checked((uint) to.Address.ScopeId); + int result; + fixed (byte* pBuf = buffer) + { + result = _SendTo(socket.Handle.ToInt32(), pBuf, buffer.Length, flags, &addrIn6, sizeof(SockAddrIn6)); + } + + if (result == -1) + { + error = GetError(socket); + if (error == 0) + error = Marshal.GetLastWin32Error(); + } + + if (result == -1) + throw new SocketException(error); + + return result; + } + } + } + + public static unsafe int ReceiveFrom(this Socket socket, Span buffer, int flags, out IPEndPoint? from) + { + var error = 0; + int result; + var fromSockAddrSize = SockAddrStorageSize; + var pFrom = stackalloc byte[SockAddrStorageSize]; + + fixed (byte* pBuf = buffer) + { + result = _RecvFrom(socket.Handle.ToInt32(), pBuf, buffer.Length, flags, pFrom, &fromSockAddrSize); + } + + if (result == -1) + { + error = GetError(socket); + if (error == 0) + { + error = Marshal.GetLastWin32Error(); + } + } + + ReadIp(out from, (SockAddr*) pFrom); + + if (result == -1) + { + throw new SocketException(error); + } + + return result; + } + + private static unsafe void ReadIp(out IPEndPoint? from, SockAddr* pFrom) + { + switch (pFrom->SocketAddressFamily) + { + default: + throw new NotSupportedException(((AddressFamily) pFrom->SocketAddressFamily).ToString()); + case (ushort) AddressFamily.Unspecified: + from = null; + break; + case (ushort) AddressFamily.InterNetwork: + ReadIPv4(out from, (SockAddrIn*) pFrom); + break; + case (ushort) AddressFamily.InterNetworkV6: + ReadIPv6(out from, (SockAddrIn6*) pFrom); + break; + } + } + + private static unsafe void ReadIPv4(out IPEndPoint from, SockAddrIn* addr) + { + var port = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(addr->Port) : addr->Port; + var ip = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(addr->InAddr) : addr->InAddr; + + from = new IPEndPoint( + ip, + port + ); + } + + private static unsafe void ReadIPv6(out IPEndPoint from, SockAddrIn6* addr) + { + var port = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(addr->Port) : addr->Port; + + var ip = new IPAddress(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref addr->InAddr, 1)), addr->InScopeId); + + from = new IPEndPoint(ip, port); + } + + } + + internal static class PosixSockets + { + + [DllImport("libc", EntryPoint = "sendto")] + internal static extern unsafe int SendTo(int socket, byte* buf, int len, int flags, void* to, int toLen); + + [DllImport("libc", EntryPoint = "recvfrom")] + internal static extern unsafe int RecvFrom(int socket, byte* buf, int len, int flags, void* from, int* fromLen); + + } + + internal static class WindowsSockets + { + + [DllImport("ws2_32", EntryPoint = "sendto")] + internal static extern unsafe int SendTo(int socket, byte* buf, int len, int flags, void* to, int toLen); + + [DllImport("ws2_32", EntryPoint = "recvfrom")] + internal static extern unsafe int RecvFrom(int socket, byte* buf, int len, int flags, void* from, int* fromLen); + + } + +} diff --git a/Lidgren.Network/Platform/PlatformConstrained.cs b/Lidgren.Network/Platform/PlatformConstrained.cs index 47888e961..b5e917178 100644 --- a/Lidgren.Network/Platform/PlatformConstrained.cs +++ b/Lidgren.Network/Platform/PlatformConstrained.cs @@ -69,10 +69,11 @@ namespace Lidgren.Network return new IPAddress(bytes); } - private static readonly SHA1 s_sha = SHA1.Create(); - public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count) + private static readonly SHA256 s_sha = SHA256.Create(); + + public static bool ComputeSHAHash(ReadOnlySpan src, Span dest) { - return s_sha.ComputeHash(bytes, offset, count); + return s_sha.TryComputeHash(src, dest, out _); } } diff --git a/Lidgren.Network/Platform/PlatformWin32.cs b/Lidgren.Network/Platform/PlatformWin32.cs index 79f9ced34..3bf9cdfeb 100644 --- a/Lidgren.Network/Platform/PlatformWin32.cs +++ b/Lidgren.Network/Platform/PlatformWin32.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; @@ -130,15 +131,21 @@ namespace Lidgren.Network System.Threading.Thread.Sleep(milliseconds); } - public static IPAddress CreateAddressFromBytes(byte[] bytes) + public static IPAddress CreateAddressFromBytes(ReadOnlySpan bytes) { return new IPAddress(bytes); } private static readonly SHA256 s_sha = SHA256.Create(); - public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count) + + public static readonly int SHAHashSize = 256; + + public static Span ComputeSHAHash(ReadOnlySpan src, Span dest) { - return s_sha.ComputeHash(bytes, offset, count); + if (!s_sha.TryComputeHash(src, dest, out _)) + throw new InvalidOperationException("Can't compute hash"); + + return dest; } } diff --git a/Lidgren.Network/Properties/AssemblyInfo.cs b/Lidgren.Network/Properties/AssemblyInfo.cs index d70056050..57842c429 100644 --- a/Lidgren.Network/Properties/AssemblyInfo.cs +++ b/Lidgren.Network/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Lidgren.Network")] -[assembly: AssemblyCopyright("Copyright © 2012")] +[assembly: AssemblyCopyright("Copyright © Lidgren 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,6 +32,6 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2012.1.7.0")] -[assembly: AssemblyFileVersion("2012.1.7.0")] -[assembly: System.CLSCompliant(true)] \ No newline at end of file +[assembly: AssemblyVersion("2020.7.0.0")] +[assembly: AssemblyFileVersion("2020.7.0.0")] +[assembly: System.CLSCompliant(false)] \ No newline at end of file diff --git a/Lidgren.Network/RobustToolbox/Lidgren.Network/NetBuffer.Streams.cs b/Lidgren.Network/RobustToolbox/Lidgren.Network/NetBuffer.Streams.cs new file mode 100644 index 000000000..0d21c0df7 --- /dev/null +++ b/Lidgren.Network/RobustToolbox/Lidgren.Network/NetBuffer.Streams.cs @@ -0,0 +1,216 @@ +using System; +using System.IO; + +namespace Lidgren.Network +{ + + public partial class NetBuffer + { + + public class AppenderStream : Stream + { + + internal AppenderStream(NetBuffer netBuffer) + { + Buffer = netBuffer; + } + + protected NetBuffer Buffer; + + protected override void Dispose(bool _) + => Buffer = null; + + protected void DisposedCheck() + { + if (Buffer == null) throw new ObjectDisposedException("Stream"); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override void Flush() => DisposedCheck(); + + public override int Read(byte[] buffer, int offset, int count) + => throw new NotSupportedException(); + + public override int Read(Span buffer) + => throw new NotSupportedException(); + + public override void SetLength(long value) + => throw new NotSupportedException(); + + public override void Write(byte[] buffer, int offset, int count) + => Write(new ReadOnlySpan(buffer, offset, count)); + + public override void Write(ReadOnlySpan buffer) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + Buffer.Write(buffer); + } + + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + } + + public abstract class WrapperStream : Stream, IDisposable + { + + protected WrapperStream(NetBuffer netBuffer, in int start, int length, bool isReadMode) + { + NetException.Assert(netBuffer.m_bitLength - start >= length, + isReadMode ? c_readOverflowError : c_writeOverflowError); + + Buffer = netBuffer; + BitOffsetStart = start; + BitOffset = start; + BitOffsetEnd = start + length; + } + + protected NetBuffer Buffer; + + protected int BitOffsetStart; + + protected int BitOffsetEnd; + + protected int BitOffset; + + protected override void Dispose(bool _) + => Buffer = null; + + protected void DisposedCheck() + { + if (Buffer == null) throw new ObjectDisposedException("Stream"); + } + + public override long Seek(long offset, SeekOrigin origin) + { + DisposedCheck(); + switch (origin) + { + case SeekOrigin.Begin: break; + case SeekOrigin.Current: + offset += Position; + break; + case SeekOrigin.End: + offset = Position - offset; + break; + default: throw new ArgumentOutOfRangeException(nameof(origin), origin, "SeekOrigin invalid."); + } + + return Position = offset; + } + + public override long Length => (BitOffsetStart - BitOffsetEnd) >> 3; + + public override long Position + { + get => (BitOffset - BitOffsetStart) >> 3; + set => BitOffset = BitOffsetStart + (checked((int)value) << 3); + } + + } + + public class ReadOnlyWrapperStream : WrapperStream + { + + internal ReadOnlyWrapperStream(NetBuffer netBuffer, int start, int length) + : base(netBuffer, start, length, true) + { + } + + public override void Flush() => DisposedCheck(); + + public override int Read(byte[] buffer, int offset, int count) + => Read(new Span(buffer, offset, count)); + + public override int Read(Span buffer) + { + DisposedCheck(); + var numberOfBytes = buffer.Length; + var numberOfBits = numberOfBytes * 8; + NetException.Assert(BitOffsetEnd - BitOffset >= numberOfBits, c_readOverflowError); + NetBitWriter.ReadBytes(Buffer.m_data, numberOfBytes, BitOffset, buffer, 0); + BitOffset += numberOfBits; + return buffer.Length; + } + + public override void SetLength(long value) + => throw new NotSupportedException(); + + public override void Write(byte[] buffer, int offset, int count) + => throw new NotSupportedException(); + + public override bool CanRead => BitOffset < BitOffsetEnd; + + public override bool CanSeek => true; + + public override bool CanWrite => false; + + } + + + public class WriteOnlyWrapperStream : WrapperStream + { + + internal WriteOnlyWrapperStream(NetBuffer netBuffer, int start, int length) + : base(netBuffer, start, length, false) + { + } + + public override void Flush() => DisposedCheck(); + + public override int Read(byte[] buffer, int offset, int count) + => throw new NotSupportedException(); + + public override int Read(Span buffer) + => throw new NotSupportedException(); + + public override void SetLength(long value) + => throw new NotSupportedException(); + + public override void Write(byte[] buffer, int offset, int count) + => Write(new ReadOnlySpan(buffer, offset, count)); + + public override void Write(ReadOnlySpan buffer) + { + if (buffer == null) + { + throw new ArgumentNullException(nameof(buffer)); + } + + var numberOfBytes = buffer.Length; + var numberOfBits = numberOfBytes * 8; + NetException.Assert(BitOffsetEnd - BitOffset >= numberOfBits, c_writeOverflowError); + NetBitWriter.WriteBytes(buffer, 0, numberOfBytes, Buffer.m_data, BitOffset); + BitOffset += numberOfBits; + } + + public override bool CanRead => false; + + public override bool CanSeek => true; + + public override bool CanWrite => BitOffset < BitOffsetEnd; + + } + + } + +} diff --git a/Robust.Server/GameObjects/Components/Appearance/AppearanceComponent.cs b/Robust.Server/GameObjects/Components/Appearance/AppearanceComponent.cs index 516c2c5fc..5b957acf6 100644 --- a/Robust.Server/GameObjects/Components/Appearance/AppearanceComponent.cs +++ b/Robust.Server/GameObjects/Components/Appearance/AppearanceComponent.cs @@ -50,7 +50,7 @@ namespace Robust.Server.GameObjects return true; } - data = default; + data = default!; return false; } diff --git a/Robust.Server/GameObjects/ServerEntityManager.cs b/Robust.Server/GameObjects/ServerEntityManager.cs index 42e39b9b7..9cab62d58 100644 --- a/Robust.Server/GameObjects/ServerEntityManager.cs +++ b/Robust.Server/GameObjects/ServerEntityManager.cs @@ -272,7 +272,7 @@ namespace Robust.Server.GameObjects { foreach (var child in children) { - var ent = child; + var ent = child!; do { @@ -280,7 +280,7 @@ namespace Robust.Server.GameObjects { AddContainedRecursive(ent, set); - ent = ent.Transform.Parent?.Owner; + ent = ent.Transform.Parent?.Owner!; } else { diff --git a/Robust.Shared/Network/Messages/MsgEntity.cs b/Robust.Shared/Network/Messages/MsgEntity.cs index bba9fd127..7890f8f84 100644 --- a/Robust.Shared/Network/Messages/MsgEntity.cs +++ b/Robust.Shared/Network/Messages/MsgEntity.cs @@ -42,11 +42,9 @@ namespace Robust.Shared.Network.Messages case EntityMessageType.SystemMessage: { var serializer = IoCManager.Resolve(); - int messageLength = buffer.ReadInt32(); - using (var stream = new MemoryStream(buffer.ReadBytes(messageLength))) - { - SystemMessage = serializer.Deserialize(stream); - } + int length = buffer.ReadInt32(); + using var stream = buffer.ReadAsStream(length); + SystemMessage = serializer.Deserialize(stream); } break; @@ -56,11 +54,9 @@ namespace Robust.Shared.Network.Messages NetId = buffer.ReadUInt32(); var serializer = IoCManager.Resolve(); - int messageLength = buffer.ReadInt32(); - using (var stream = new MemoryStream(buffer.ReadBytes(messageLength))) - { - ComponentMessage = serializer.Deserialize(stream); - } + int length = buffer.ReadInt32(); + using var stream = buffer.ReadAsStream(length); + ComponentMessage = serializer.Deserialize(stream); } break; } @@ -188,6 +184,7 @@ namespace Robust.Shared.Network.Messages } } +#if false private List UnPackParams(NetIncomingMessage message) { var messageParams = new List(); @@ -236,12 +233,15 @@ namespace Robust.Shared.Network.Messages break; case NetworkDataType.d_byteArray: int length = message.ReadInt32(); - messageParams.Add(message.ReadBytes(length)); + var buf = new byte[length]; + message.ReadBytes(buf); + messageParams.Add(buf) break; } } return messageParams; } +#endif #endregion Parameter Packing diff --git a/Robust.Shared/Network/Messages/MsgScriptResponse.cs b/Robust.Shared/Network/Messages/MsgScriptResponse.cs index 0057159b8..1ed5a34d9 100644 --- a/Robust.Shared/Network/Messages/MsgScriptResponse.cs +++ b/Robust.Shared/Network/Messages/MsgScriptResponse.cs @@ -39,11 +39,9 @@ namespace Robust.Shared.Network.Messages var serializer = IoCManager.Resolve(); var length = buffer.ReadVariableInt32(); - var stateData = buffer.ReadBytes(length); - - using var memoryStream = new MemoryStream(stateData); - Echo = serializer.Deserialize(memoryStream); - Response = serializer.Deserialize(memoryStream); + using var stream = buffer.ReadAsStream(length); + Echo = serializer.Deserialize(stream); + Response = serializer.Deserialize(stream); } } diff --git a/Robust.Shared/Network/Messages/MsgState.cs b/Robust.Shared/Network/Messages/MsgState.cs index b377afdf8..f85b1dac8 100644 --- a/Robust.Shared/Network/Messages/MsgState.cs +++ b/Robust.Shared/Network/Messages/MsgState.cs @@ -37,12 +37,9 @@ namespace Robust.Shared.Network.Messages { MsgSize = buffer.LengthBytes; var length = buffer.ReadVariableInt32(); - var stateData = buffer.ReadBytes(length); - using (var stateStream = new MemoryStream(stateData)) - { - var serializer = IoCManager.Resolve(); - State = serializer.Deserialize(stateStream); - } + using var stream = buffer.ReadAsStream(length); + var serializer = IoCManager.Resolve(); + State = serializer.Deserialize(stream); State.PayloadSize = length; } diff --git a/Robust.Shared/Network/Messages/MsgViewVariablesModifyRemote.cs b/Robust.Shared/Network/Messages/MsgViewVariablesModifyRemote.cs index ca966ab0a..ddb8ffeea 100644 --- a/Robust.Shared/Network/Messages/MsgViewVariablesModifyRemote.cs +++ b/Robust.Shared/Network/Messages/MsgViewVariablesModifyRemote.cs @@ -42,19 +42,13 @@ namespace Robust.Shared.Network.Messages SessionId = buffer.ReadUInt32(); { var length = buffer.ReadInt32(); - var bytes = buffer.ReadBytes(length); - using (var stream = new MemoryStream(bytes)) - { - PropertyIndex = serializer.Deserialize(stream); - } + using var stream = buffer.ReadAsStream(length); + PropertyIndex = serializer.Deserialize(stream); } { var length = buffer.ReadInt32(); - var bytes = buffer.ReadBytes(length); - using (var stream = new MemoryStream(bytes)) - { - Value = serializer.Deserialize(stream); - } + using var stream = buffer.ReadAsStream(length); + Value = serializer.Deserialize(stream); } } diff --git a/Robust.Shared/Network/Messages/MsgViewVariablesRemoteData.cs b/Robust.Shared/Network/Messages/MsgViewVariablesRemoteData.cs index e0e7591e2..c5d84c407 100644 --- a/Robust.Shared/Network/Messages/MsgViewVariablesRemoteData.cs +++ b/Robust.Shared/Network/Messages/MsgViewVariablesRemoteData.cs @@ -41,11 +41,8 @@ namespace Robust.Shared.Network.Messages RequestId = buffer.ReadUInt32(); var serializer = IoCManager.Resolve(); var length = buffer.ReadInt32(); - var bytes = buffer.ReadBytes(length); - using (var stream = new MemoryStream(bytes)) - { - Blob = serializer.Deserialize(stream); - } + using var stream = buffer.ReadAsStream(length); + Blob = serializer.Deserialize(stream); } public override void WriteToBuffer(NetOutgoingMessage buffer) diff --git a/Robust.Shared/Network/Messages/MsgViewVariablesReqData.cs b/Robust.Shared/Network/Messages/MsgViewVariablesReqData.cs index b355897e9..2add4282d 100644 --- a/Robust.Shared/Network/Messages/MsgViewVariablesReqData.cs +++ b/Robust.Shared/Network/Messages/MsgViewVariablesReqData.cs @@ -47,11 +47,8 @@ namespace Robust.Shared.Network.Messages SessionId = buffer.ReadUInt32(); var serializer = IoCManager.Resolve(); var length = buffer.ReadInt32(); - var bytes = buffer.ReadBytes(length); - using (var stream = new MemoryStream(bytes)) - { - RequestMeta = serializer.Deserialize(stream); - } + using var stream = buffer.ReadAsStream(length); + RequestMeta = serializer.Deserialize(stream); } public override void WriteToBuffer(NetOutgoingMessage buffer) @@ -68,4 +65,3 @@ namespace Robust.Shared.Network.Messages } } } - diff --git a/Robust.Shared/Network/Messages/MsgViewVariablesReqSession.cs b/Robust.Shared/Network/Messages/MsgViewVariablesReqSession.cs index 6c00d08e7..61b372267 100644 --- a/Robust.Shared/Network/Messages/MsgViewVariablesReqSession.cs +++ b/Robust.Shared/Network/Messages/MsgViewVariablesReqSession.cs @@ -43,11 +43,8 @@ namespace Robust.Shared.Network.Messages RequestId = buffer.ReadUInt32(); var serializer = IoCManager.Resolve(); var length = buffer.ReadInt32(); - var bytes = buffer.ReadBytes(length); - using (var stream = new MemoryStream(bytes)) - { - Selector = serializer.Deserialize(stream); - } + using var stream = buffer.ReadAsStream(length); + Selector = serializer.Deserialize(stream); } public override void WriteToBuffer(NetOutgoingMessage buffer) diff --git a/Robust.Shared/Serialization/Messages/MsgRobustMappedStringsSerializerServerHandshake.cs b/Robust.Shared/Serialization/Messages/MsgRobustMappedStringsSerializerServerHandshake.cs index 7ab23887b..1ec2a40ee 100644 --- a/Robust.Shared/Serialization/Messages/MsgRobustMappedStringsSerializerServerHandshake.cs +++ b/Robust.Shared/Serialization/Messages/MsgRobustMappedStringsSerializerServerHandshake.cs @@ -6,6 +6,7 @@ using Robust.Shared.Network; namespace Robust.Shared.Serialization { + /// /// The server part of the string-exchange handshake. Sent as the /// first message in the handshake. Tells the client the hash of @@ -35,7 +36,7 @@ namespace Robust.Shared.Serialization throw new InvalidOperationException("Hash too long."); } - Hash = buffer.ReadBytes(len); + buffer.ReadBytes(Hash = new byte[len]); } public override void WriteToBuffer(NetOutgoingMessage buffer) diff --git a/Robust.Shared/Serialization/Messages/MsgRobustMappedStringsSerializerStrings.cs b/Robust.Shared/Serialization/Messages/MsgRobustMappedStringsSerializerStrings.cs index c4eb187a4..26c59358c 100644 --- a/Robust.Shared/Serialization/Messages/MsgRobustMappedStringsSerializerStrings.cs +++ b/Robust.Shared/Serialization/Messages/MsgRobustMappedStringsSerializerStrings.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using JetBrains.Annotations; using Lidgren.Network; @@ -7,6 +8,7 @@ using Robust.Shared.Network; namespace Robust.Shared.Serialization { + /// /// The meat of the string-exchange handshake sandwich. Sent by the /// server after the client requests an updated copy of the mapping. @@ -22,6 +24,8 @@ namespace Robust.Shared.Serialization { } + public int PackageSize { get; set; } + /// /// The raw bytes of the string mapping held by the server. /// @@ -29,14 +33,8 @@ namespace Robust.Shared.Serialization public override void ReadFromBuffer(NetIncomingMessage buffer) { - var l = buffer.ReadVariableInt32(); - var success = buffer.ReadBytes(l, out var buf); - if (!success) - { - throw new InvalidDataException("Not all of the bytes were available in the message."); - } - - Package = buf; + PackageSize = buffer.ReadVariableInt32(); + buffer.ReadBytes(Package = new byte[PackageSize]); } public override void WriteToBuffer(NetOutgoingMessage buffer)