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