Make Lidgren Use Spans & Shared Pool (#1140)

* make lidgren use spans everywhere where it can

convert custom pooling to shared array pool impl

add unit tests for read/write

add native socket extensions to socket so we can legit pass spans for SendTo/ReceiveFrom

bump version in lidgren csproj

replace some random "% 8" w/ "& 7"

more minor nullability hacks to fix static analysis complaints

made receiving packets use span

minor native sockets refactor to use pinvoke

add read/write constrained/prealloc'd bit stream impl to lidgren and update usages

fixed missing stream cleanup

remove outstanding stream cleanup since it refs buffer thru the class, can't read some other buf

apply suggestions from code review

remove unsafe cruft

* add tests to gh actions

* make stats use interpolation in tostring and remove m_bytesAllocated since it's all in the shared pool now

* this pr still open so fuck it

stats, human readability, faster BitsToHold methods

* add api compatible version of ReadBytes

* rename ReadOnlyStreamWrapper -> ReadOnlyWrapperStream

rename WriteOnlyStreamWrapper -> WriteOnlyWrapperStream

add AppendViaStream, AppenderStream impl

add and update documentation on read/write bytes methods

also fix some goofs
This commit is contained in:
Tyler Young
2020-06-23 22:09:20 -04:00
committed by GitHub
parent 2558201ae4
commit faff0797bf
47 changed files with 1655 additions and 817 deletions

View File

@@ -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

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
</ItemGroup>
</Project>

View File

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

View File

@@ -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();
}
}
}

View File

@@ -25,7 +25,7 @@ namespace Lidgren.Network
SetKey(key);
}
public NetAESEncryption(NetPeer peer, byte[] data, int offset, int count)
public NetAESEncryption(NetPeer peer, ReadOnlySpan<byte> data, int offset, int count)
#if UNITY
: base(peer, new RijndaelManaged())
#else

View File

@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
namespace Lidgren.Network
@@ -8,8 +9,6 @@ namespace Lidgren.Network
/// </summary>
public abstract class NetBlockEncryptionBase : NetEncryption
{
// temporary space for one block to avoid reallocating every time
private byte[] m_tmp;
/// <summary>
/// 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];
}
/// <summary>
@@ -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<byte> 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<numBlocks;i++)
{
EncryptBlock(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?
EncryptBlock(dataSpan, (i * blockSize), tmp);
//Buffer.BlockCopy(m_tmp, 0, dataSpan, (i * blockSize), m_tmp.Length);
tmp.CopyTo(dataSpan.Slice(i * blockSize, tmp.Length));
}
// add true payload length last
@@ -60,18 +62,22 @@ namespace Lidgren.Network
{
int numEncryptedBytes = msg.LengthBytes - 4; // last 4 bytes is true bit length
int blockSize = BlockSize;
Span<byte> 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
/// <summary>
/// Encrypt a block of bytes
/// </summary>
protected abstract void EncryptBlock(byte[] source, int sourceOffset, byte[] destination);
protected abstract void EncryptBlock(ReadOnlySpan<byte> source, int sourceOffset, Span<byte> destination);
/// <summary>
/// Decrypt a block of bytes
/// </summary>
protected abstract void DecryptBlock(byte[] source, int sourceOffset, byte[] destination);
protected abstract void DecryptBlock(ReadOnlySpan<byte> source, int sourceOffset, Span<byte> destination);
}
}

View File

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

View File

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

View File

@@ -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<byte> data, int offset, int count);
/// <summary>
/// Encrypt an outgoing message in place

View File

@@ -9,21 +9,21 @@ namespace Lidgren.Network
/// </summary>
public class NetXorEncryption : NetEncryption
{
private byte[] m_key;
private Memory<byte> m_key;
/// <summary>
/// NetXorEncryption constructor
/// </summary>
public NetXorEncryption(NetPeer peer, byte[] key)
public NetXorEncryption(NetPeer peer, Memory<byte> key)
: base(peer)
{
m_key = key;
}
public override void SetKey(byte[] data, int offset, int count)
public override void SetKey(ReadOnlySpan<byte> data, int offset, int count)
{
m_key = new byte[count];
Array.Copy(data, offset, m_key, 0, count);
data.CopyTo(m_key.Span);
}
/// <summary>
@@ -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;
}

View File

@@ -79,17 +79,11 @@ namespace Lidgren.Network
{
}
/// <summary>
/// String to hash for key
/// </summary>
public NetXtea(NetPeer peer, string key)
: this(peer, NetUtility.ComputeSHAHash(Encoding.UTF8.GetBytes(key)), 32)
public override void SetKey(ReadOnlySpan<byte> 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<byte> 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
/// <summary>
/// Encrypts a block of bytes
/// </summary>
protected override void EncryptBlock(byte[] source, int sourceOffset, byte[] destination)
protected override void EncryptBlock(ReadOnlySpan<byte> source, int sourceOffset, Span<byte> destination)
{
uint v0 = BytesToUInt(source, sourceOffset);
uint v1 = BytesToUInt(source, sourceOffset + 4);
@@ -117,7 +111,7 @@ namespace Lidgren.Network
/// <summary>
/// Decrypts a block of bytes
/// </summary>
protected override void DecryptBlock(byte[] source, int sourceOffset, byte[] destination)
protected override void DecryptBlock(ReadOnlySpan<byte> source, int sourceOffset, Span<byte> 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<byte> 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<byte> destination, int destinationOffset)
{
destination[destinationOffset++] = (byte)(value >> 24);
destination[destinationOffset++] = (byte)(value >> 16);

View File

@@ -1,11 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU</Platforms>
<SonarQubeExclude>true</SonarQubeExclude>
<DefineConstants>$(DefineConstants);USE_RELEASE_STATISTICS</DefineConstants>
<DefineConstants>$(DefineConstants);USE_RELEASE_STATISTICS;UNSAFE</DefineConstants>
<LangVersion>8</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>

View File

@@ -256,13 +256,13 @@ namespace Lidgren.Network
}
public NetBigInteger(
byte[] bytes)
ReadOnlySpan<byte> bytes)
: this(bytes, 0, bytes.Length)
{
}
public NetBigInteger(
byte[] bytes,
ReadOnlySpan<byte> bytes,
int offset,
int length)
{
@@ -287,7 +287,7 @@ namespace Lidgren.Network
else
{
int numBytes = end - iBval;
byte[] inverse = new byte[numBytes];
Span<byte> inverse = stackalloc byte[numBytes];
int index = 0;
while (index < numBytes)
@@ -316,7 +316,7 @@ namespace Lidgren.Network
}
private static int[] MakeMagnitude(
byte[] bytes,
ReadOnlySpan<byte> bytes,
int offset,
int length)
{
@@ -374,14 +374,14 @@ namespace Lidgren.Network
public NetBigInteger(
int sign,
byte[] bytes)
ReadOnlySpan<byte> bytes)
: this(sign, bytes, 0, bytes.Length)
{
}
public NetBigInteger(
int sign,
byte[] bytes,
ReadOnlySpan<byte> bytes,
int offset,
int length)
{

View File

@@ -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
/// <summary>
/// Read 1-8 bits from a buffer into a byte
/// </summary>
public static byte ReadByte(byte[] fromBuffer, int numberOfBits, int readBitOffset)
public static byte ReadByte(ReadOnlySpan<byte> 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
/// <summary>
/// Read several bytes from a buffer
/// </summary>
public static void ReadBytes(byte[] fromBuffer, int numberOfBytes, int readBitOffset, byte[] destination, int destinationByteOffset)
public static void ReadBytes(ReadOnlySpan<byte> fromBuffer, int numberOfBytes, int readBitOffset, Span<byte> 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;
/// <summary>
/// Read several bytes from a buffer
/// </summary>
public static void ReadBytes(ReadOnlySpan<byte> fromBuffer, int readBitOffset, Span<byte> 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));
}
}
/// <summary>
/// Write 0-8 bits of data to buffer
/// </summary>
public static void WriteByte(byte source, int numberOfBits, byte[] destination, int destBitOffset)
public static void WriteByte(byte source, int numberOfBits, Span<byte> destination, int destBitOffset)
{
if (numberOfBits == 0)
return;
@@ -149,18 +186,63 @@ namespace Lidgren.Network
(source >> bitsFree)
);
}
/// <summary>
/// Zero a number of bits
/// </summary>
public static void Zero(int numberOfBits, Span<byte> 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);
}
/// <summary>
/// <summary>
/// Write several whole bytes
/// </summary>
public static void WriteBytes(byte[] source, int sourceByteOffset, int numberOfBytes, byte[] destination, int destBitOffset)
public static void WriteBytes(ReadOnlySpan<byte> source, int sourceByteOffset, int numberOfBytes, Span<byte> 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;
/// <summary>
/// Write several whole bytes
/// </summary>
public static void WriteBytes(ReadOnlySpan<byte> source, Span<byte> 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
}
}
/// <summary>
/// Reads an unsigned 16 bit integer
/// </summary>
[CLSCompliant(false)]
#if UNSAFE
public static unsafe ushort ReadUInt16(byte[] fromBuffer, int numberOfBits, int readBitOffset)
public static ushort ReadUInt16(ReadOnlySpan<byte> 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<ushort>(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
}
/// <summary>
/// Reads the specified number of bits into an UInt32
/// </summary>
[CLSCompliant(false)]
#if UNSAFE
public static unsafe uint ReadUInt32(byte[] fromBuffer, int numberOfBits, int readBitOffset)
public static uint ReadUInt32(ReadOnlySpan<byte> 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<uint>(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
/// </summary>
[CLSCompliant(false)]
public static void WriteUInt16(ushort source, int numberOfBits, byte[] destination, int destinationBitOffset)
public static void WriteUInt16(ushort source, int numberOfBits, Span<byte> 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);
}
/// <summary>
/// Writes the specified number of bits into a byte array
/// </summary>
[CLSCompliant(false)]
public static int WriteUInt32(uint source, int numberOfBits, byte[] destination, int destinationBitOffset)
public static int WriteUInt32(uint source, int numberOfBits, Span<byte> 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
/// </summary>
[CLSCompliant(false)]
public static int WriteUInt64(ulong source, int numberOfBits, byte[] destination, int destinationBitOffset)
public static int WriteUInt64(ulong source, int numberOfBits, Span<byte> 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
/// </summary>
/// <returns>number of bytes written</returns>
[CLSCompliant(false)]
public static int WriteVariableUInt32(byte[] intoBuffer, int offset, uint value)
public static int WriteVariableUInt32(Span<byte> 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!
/// </summary>
[CLSCompliant(false)]
public static uint ReadVariableUInt32(byte[] buffer, ref int offset)
public static uint ReadVariableUInt32(Span<byte> buffer, ref int offset)
{
int num1 = 0;
int num2 = 0;

View File

@@ -28,7 +28,7 @@ namespace Lidgren.Network
/// <summary>
/// Gets the internal data buffer
/// </summary>
public byte[] PeekDataBuffer() { return m_data; }
public Span<byte> PeekDataBuffer() { return m_data; }
//
// 1 bit
@@ -79,25 +79,25 @@ namespace Lidgren.Network
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public byte[] PeekBytes(int numberOfBytes)
public Span<byte> PeekBytes(Span<byte> 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;
}
/// <summary>
/// Reads the specified number of bytes without advancing the read pointer
/// </summary>
public void PeekBytes(byte[] into, int offset, int numberOfBytes)
public Span<byte> PeekBytes(Span<byte> 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);
}
/// <summary>
@@ -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);
}
/// <summary>

View File

@@ -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.";
/// <summary>
/// Reads a boolean value (stored as a single bit) written using Write(bool)
/// </summary>
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;
}
/// <summary>
@@ -36,8 +35,7 @@ namespace Lidgren.Network
/// </summary>
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;
}
/// <summary>
@@ -74,65 +72,76 @@ namespace Lidgren.Network
/// </summary>
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;
}
/// <summary>
/// Reads the specified number of bytes
/// Creates a stream out of a constrained region of the buffer for reading.
/// </summary>
public byte[] ReadBytes(int numberOfBytes)
/// <param name="byteLength">The length of the constrained region in bytes.</param>
/// <returns>A readable constrained buffer region wrapped by a stream.</returns>
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;
}
/// <summary>
/// Reads the specified number of bytes and returns true for success
/// </summary>
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;
}
/// <summary>
/// Reads the specified number of bytes into a preallocated array
/// Prefer using <see cref="NetBuffer.ReadBytes(Span{byte})" /> instead.
/// Reads the specified number of bytes into a allocated array.
/// </summary>
/// <param name="into">The destination array</param>
/// <param name="offset">The offset where to start writing in the destination array</param>
/// <param name="numberOfBytes">The number of bytes to read</param>
public void ReadBytes(byte[] into, int offset, int numberOfBytes)
/// <returns>The same as <paramref name="into"/></returns>
//[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;
}
/// <summary>
/// Reads the specified number of bits into a preallocated array
/// Reads the specified number of bytes into a pre-allocated array.
/// </summary>
/// <param name="into">The destination</param>
/// <returns>The same as <paramref name="into"/></returns>
public Span<byte> ReadBytes(Span<byte> into)
{
var bytes = PeekBytes(into);
m_readPosition += 8 * into.Length;
return bytes;
}
/// <summary>
/// Reads the specified number of bytes into a pre-allocated array.
/// </summary>
/// <param name="into">The destination array</param>
/// <param name="offset">The offset where to start writing in the destination array</param>
/// <param name="numberOfBytes">The number of bytes to read</param>
/// <returns>The same as <paramref name="into"/></returns>
//[Obsolete("Use Span alternative instead with slicing.")]
public Span<byte> ReadBytes(Span<byte> into, int offset, int numberOfBytes)
{
var bytes = PeekBytes(into, offset, numberOfBytes);
m_readPosition += 8 * numberOfBytes;
return bytes;
}
/// <summary>
/// Reads the specified number of bits into a pre-allocated array.
/// </summary>
/// <param name="into">The destination array</param>
/// <param name="offset">The offset where to start writing in the destination array</param>
/// <param name="numberOfBits">The number of bits to read</param>
public void ReadBits(byte[] into, int offset, int numberOfBits)
//[Obsolete("Use Span alternative instead with slicing.")]
public Span<byte> ReadBits(Span<byte> 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);
}
/// <summary>
@@ -154,10 +163,9 @@ namespace Lidgren.Network
/// </summary>
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;
}
/// <summary>
@@ -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
/// </summary>
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
/// </summary>
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;
}
/// <summary>
@@ -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;
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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);

View File

@@ -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<byte>.Shared.Rent(byteLen + c_overAllocateAmount);
return;
}
if (m_data.Length < byteLen)
Array.Resize<byte>(ref m_data, byteLen + c_overAllocateAmount);
return;
if (m_buf.Length < byteLen)
{
var pool = ArrayPool<byte>.Shared;
var oldBuf = m_buf;
m_buf = pool.Rent(byteLen + c_overAllocateAmount);
new Span<byte>(oldBuf).CopyTo(m_buf);
pool.Return(oldBuf);
}
}
/// <summary>
@@ -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<byte>.Shared.Rent(byteLen);
return;
}
if (m_data.Length < byteLen)
Array.Resize<byte>(ref m_data, byteLen);
return;
if (m_buf.Length < byteLen)
{
var pool = ArrayPool<byte>.Shared;
var oldBuf = m_buf;
m_buf = pool.Rent(byteLen);
new Span<byte>(oldBuf).CopyTo(m_buf);
pool.Return(oldBuf);
}
}
/// <summary>
@@ -134,12 +149,49 @@ namespace Lidgren.Network
}
/// <summary>
/// Writes all bytes in an array
/// Writes a number of zeroed bytes
/// </summary>
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;
}
/// <summary>
/// Creates a stream out of a constrained region of the buffer for writing.
/// </summary>
/// <param name="byteLength">The length of the constrained region in bytes.</param>
/// <returns>A writable constrained buffer region wrapped by a stream.</returns>
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;
}
/// <summary>
/// Creates a stream for appending to the end of the buffer.
/// </summary>
/// <returns>A writable stream that appends to the buffer.</returns>
public Stream AppendViaStream()
{
return new AppenderStream(this);
}
/// <summary>
/// Writes all bytes in an array.
/// </summary>
public void Write(ReadOnlySpan<byte> 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
/// <summary>
/// Writes the specified number of bytes from an array
/// </summary>
public void Write(byte[] source, int offsetInBytes, int numberOfBytes)
public void Write(Span<byte> 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
/// <summary>
/// Writes a 32 bit signed integer
/// </summary>
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
/// <summary>
/// Writes a 32 bit signed integer
/// </summary>
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<byte>(Buffer, m_bitLength / 8, 4), ref source);
}
else
{
NetBitWriter.WriteUInt32((UInt32)source, 32, Buffer, m_bitLength);
}
m_bitLength += 32;
}
#endif
/// <summary>
/// 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
/// <summary>
/// Writes a 32 bit unsigned integer
/// </summary>
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<byte>(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
/// <summary>
/// Writes a 32 bit unsigned integer
/// </summary>
[CLSCompliant(false)]
public void Write(UInt32 source)
{
EnsureBufferSize(m_bitLength + 32);
NetBitWriter.WriteUInt32(source, 32, m_data, m_bitLength);
m_bitLength += 32;
}
#endif
/// <summary>
/// Writes a 32 bit unsigned integer at a given offset in the buffer
@@ -403,79 +424,33 @@ namespace Lidgren.Network
//
// Floating point
//
#if UNSAFE
/// <summary>
/// Writes a 32 bit floating point value
/// </summary>
public unsafe void Write(float source)
{
uint val = *((uint*)&source);
#if BIGENDIAN
val = NetUtility.SwapByteOrder(val);
#endif
Write(val);
}
#else
/// <summary>
/// Writes a 32 bit floating point value
/// </summary>
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
/// <summary>
/// Writes a 64 bit floating point value
/// </summary>
public unsafe void Write(double source)
{
ulong val = *((ulong*)&source);
#if BIGENDIAN
val = NetUtility.SwapByteOrder(val);
#endif
uint val = MemoryMarshal.Cast<float,uint>(MemoryMarshal.CreateReadOnlySpan(ref source, 1))[0];
if (!BitConverter.IsLittleEndian)
{
val = BinaryPrimitives.ReverseEndianness(val);
}
Write(val);
}
#else
/// <summary>
/// Writes a 64 bit floating point value
/// </summary>
public void Write(double source)
{
byte[] val = BitConverter.GetBytes(source);
#if BIGENDIAN
// 0 1 2 3 4 5 6 7
ulong val = MemoryMarshal.Cast<double,ulong>(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<byte> 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;

View File

@@ -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<Type, MethodInfo> s_readMethods;
private static readonly Dictionary<Type, MethodInfo> s_writeMethods;
internal byte[] m_data;
internal byte[] m_buf;
internal Span<byte> m_data => m_buf;
internal int m_bitLength;
internal int m_readPosition;
/// <summary>
/// Gets or sets the internal data buffer
/// </summary>
public byte[] Data
public byte[] Buffer
{
get { return m_data; }
set { m_data = value; }
get { return m_buf; }
}
/// <summary>
@@ -94,5 +95,6 @@ namespace Lidgren.Network
}
}
}
}
}

View File

@@ -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<byte> 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<byte>(ArrayPool<byte>.Shared.Rent(remainingBytes),0, remainingBytes));
if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier)
{

View File

@@ -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);

View File

@@ -76,45 +76,113 @@ namespace Lidgren.Network
/// <summary>
/// Gets the number of sent packets for this connection
/// </summary>
public long SentPackets { get { return m_sentPackets; } }
public long SentPackets => m_sentPackets;
/// <summary>
/// Gets the number of received packets for this connection
/// </summary>
public long ReceivedPackets { get { return m_receivedPackets; } }
public long ReceivedPackets => m_receivedPackets;
/// <summary>
/// Gets the number of sent bytes for this connection
/// </summary>
public long SentBytes { get { return m_sentBytes; } }
public long SentBytes => m_sentBytes;
/// <summary>
/// Gets the number of received bytes for this connection
/// </summary>
public long ReceivedBytes { get { return m_receivedBytes; } }
public long ReceivedBytes => m_receivedBytes;
/// <summary>
/// <summary>
/// Gets the number of sent messages for this connection
/// </summary>
public long SentMessages { get { return m_sentMessages; } }
public long SentMessages => m_sentMessages;
/// <summary>
/// <summary>
/// Gets the number of received messages for this connection
/// </summary>
public long ReceivedMessages { get { return m_receivedMessages; } }
public long ReceivedMessages => m_receivedMessages;
/// <summary>
/// Gets the number of resent reliable messages for this connection
/// </summary>
public long ResentMessages { get { return m_resentMessagesDueToHole + m_resentMessagesDueToDelay; } }
public long ResentMessages => m_resentMessagesDueToHole + m_resentMessagesDueToDelay;
/// <summary>
/// <summary>
/// Gets the number of resent reliable messages for this connection due to holes
/// </summary>
public long ResentMessagesDueToHoles => m_resentMessagesDueToHole;
/// <summary>
/// Gets the number of resent reliable messages for this connection due to delays
/// </summary>
public long ResentMessagesDueToDelays => m_resentMessagesDueToDelay;
/// <summary>
/// Gets the number of dropped messages for this connection
/// </summary>
public long DroppedMessages { get { return m_droppedMessages; } }
public long DroppedMessages => m_droppedMessages;
/// <summary>
/// Gets the number of dropped messages for this connection
/// </summary>
public long ReceivedFragments => m_receivedFragments;
// public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } }
/// <summary>
/// Gets the number of withheld messages for this connection
/// </summary>
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;
}
}
/// <summary>
/// Gets the number of unsent and stored messages for this connection
/// </summary>
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++;
}
/// <summary>
/// Returns a string that represents this object
/// </summary>
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}";
}
}
}

View File

@@ -5,7 +5,7 @@ namespace Lidgren.Network
internal static class NetFragmentationHelper
{
internal static int WriteHeader(
byte[] destination,
Span<byte> 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<byte> buffer, int ptr, out int group, out int totalBits, out int chunkByteSize, out int chunkNumber)
{
int num1 = 0;
int num2 = 0;

View File

@@ -57,7 +57,7 @@ namespace Lidgren.Network
m_fragmentGroup = 0;
}
internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
internal int Encode(Span<byte> 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;
}
}

View File

@@ -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<byte>.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<byte>(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;

View File

@@ -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<byte>.Shared.Rent(m_configuration.ReceiveBufferSize);
m_sendBuffer = ArrayPool<byte>.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<byte> 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<byte,long>(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<byte>(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<byte>(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<byte>(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;

View File

@@ -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<byte>.Shared.Rent(numBytes);
//Buffer.BlockCopy(m_sendBuffer, 0, p.Data, 0, numBytes);
new Memory<byte>(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<byte>(p.Data), p.Data.Length, p.Target, out connectionReset);
m_delayedPackets.Remove(p);
ArrayPool<byte>.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<byte>(p.Data), p.Data.Length, p.Target, out connectionReset);
ArrayPool<byte>.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<byte> 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!");

View File

@@ -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<byte[]> m_storagePool;
private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool;
private NetQueue<NetIncomingMessage> 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<byte[]>(16);
m_outgoingMessagesPool = new NetQueue<NetOutgoingMessage>(4);
m_incomingMessagesPool = new NetQueue<NetIncomingMessage>(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<byte>.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<byte>.Shared.Return(storage);
}
/// <summary>
@@ -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<byte> 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

View File

@@ -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;
}
/// <summary>
/// Gets the number of sent packets since the NetPeer was initialized
/// </summary>
public int SentPackets { get { return m_sentPackets; } }
public int SentPackets => m_sentPackets;
/// <summary>
/// Gets the number of received packets since the NetPeer was initialized
/// </summary>
public int ReceivedPackets { get { return m_receivedPackets; } }
public int ReceivedPackets => m_receivedPackets;
/// <summary>
/// Gets the number of sent messages since the NetPeer was initialized
/// </summary>
public int SentMessages { get { return m_sentMessages; } }
public int SentMessages => m_sentMessages;
/// <summary>
/// Gets the number of received messages since the NetPeer was initialized
/// </summary>
public int ReceivedMessages { get { return m_receivedMessages; } }
public int ReceivedMessages => m_receivedMessages;
/// <summary>
/// Gets the number of received fragments since the NetPeer was initialized
/// </summary>
public int ReceivedFragments => m_receivedFragments;
/// <summary>
/// Gets the number of sent bytes since the NetPeer was initialized
/// </summary>
public int SentBytes { get { return m_sentBytes; } }
public int SentBytes => m_sentBytes;
/// <summary>
/// Gets the number of received bytes since the NetPeer was initialized
/// </summary>
public int ReceivedBytes { get { return m_receivedBytes; } }
/// <summary>
/// Gets the number of bytes allocated (and possibly garbage collected) for message storage
/// </summary>
public long StorageBytesAllocated { get { return m_bytesAllocated; } }
/// <summary>
/// Gets the number of bytes in the recycled pool
/// </summary>
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
/// </summary>
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();
}
}
}

View File

@@ -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
/// <summary>
/// Computer private key (x)
/// </summary>
public static byte[] ComputePrivateKey(string username, string password, byte[] salt)
public static byte[] ComputePrivateKey(string username, string password, ReadOnlySpan<byte> salt)
{
byte[] tmp = Encoding.UTF8.GetBytes(username + ":" + password);
byte[] innerHash = NetUtility.ComputeSHAHash(tmp);
var tmpStr = username + ":" + password;
// ReSharper disable once SuggestVarOrType_Elsewhere
Span<byte> 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<byte> 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();
}
/// <summary>
/// Creates a verifier that the server can later use to authenticate users later on (v)
/// </summary>
public static byte[] ComputeServerVerifier(byte[] privateKey)
public static byte[] ComputeServerVerifier(ReadOnlySpan<byte> privateKey)
{
NetBigInteger x = new NetBigInteger(NetUtility.ToHexString(privateKey), 16);
@@ -82,7 +88,7 @@ namespace Lidgren.Network
/// <summary>
/// Compute client public ephemeral value (A)
/// </summary>
public static byte[] ComputeClientEphemeral(byte[] clientPrivateEphemeral) // a
public static byte[] ComputeClientEphemeral(ReadOnlySpan<byte> clientPrivateEphemeral) // a
{
// A= g^a (mod N)
NetBigInteger a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
@@ -94,7 +100,7 @@ namespace Lidgren.Network
/// <summary>
/// Compute server ephemeral value (B)
/// </summary>
public static byte[] ComputeServerEphemeral(byte[] serverPrivateEphemeral, byte[] verifier) // b
public static byte[] ComputeServerEphemeral(ReadOnlySpan<byte> serverPrivateEphemeral, ReadOnlySpan<byte> 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
/// <summary>
/// Compute intermediate value (u)
/// </summary>
public static byte[] ComputeU(byte[] clientPublicEphemeral, byte[] serverPublicEphemeral)
public static byte[] ComputeU(ReadOnlySpan<byte> clientPublicEphemeral, ReadOnlySpan<byte> 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
/// <summary>
/// Computes the server session value
/// </summary>
public static byte[] ComputeServerSessionValue(byte[] clientPublicEphemeral, byte[] verifier, byte[] udata, byte[] serverPrivateEphemeral)
public static byte[] ComputeServerSessionValue(ReadOnlySpan<byte> clientPublicEphemeral, ReadOnlySpan<byte> verifier, ReadOnlySpan<byte> udata, ReadOnlySpan<byte> serverPrivateEphemeral)
{
// S = (Av^u) ^ b (mod N)
var A = new NetBigInteger(NetUtility.ToHexString(clientPublicEphemeral), 16);
@@ -161,9 +167,9 @@ namespace Lidgren.Network
/// <summary>
/// Create XTEA symmetrical encryption object from sessionValue
/// </summary>
public static NetXtea CreateEncryption(NetPeer peer, byte[] sessionValue)
public static NetXtea CreateEncryption(NetPeer peer, ReadOnlySpan<byte> 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++)

View File

@@ -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
/// <summary>
/// Create a hex string from an array of bytes
/// </summary>
public static string ToHexString(byte[] data)
public static string ToHexString(ReadOnlySpan<byte> data, int offset, int length)
{
return ToHexString(data, 0, data.Length);
return ToHexString(data.Slice(offset, length));
}
/// <summary>
/// Create a hex string from an array of bytes
/// </summary>
public static string ToHexString(byte[] data, int offset, int length)
#if UNSAFE
public static unsafe string ToHexString(ReadOnlySpan<byte> 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<byte>((void*) d.p, d.l);
var u = MemoryMarshal.Cast<char,int>(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<byte> data)
{
var l = data.Length;
// ReSharper disable once SuggestVarOrType_Elsewhere
Span<char> c = stackalloc char[l*2];
var u = MemoryMarshal.Cast<char,int>(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
/// <summary>
/// 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
}
/// <summary>
@@ -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
}
/// <summary>
@@ -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
/// <summary>
/// Convert a hexadecimal string to a byte array
/// </summary>
public static byte[] ToByteArray(String hexString)
public static Span<byte> HexToBytes(String hexString, Span<byte> 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;
}
/// <summary>
@@ -350,11 +407,10 @@ namespace Lidgren.Network
/// </summary>
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);
}
/// <summary>
/// <summary>
/// Copies from <paramref name="src"/> to <paramref name="dst"/>. Maps to an IPv6 address
/// </summary>
/// <param name="src">Source.</param>

View File

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

View File

@@ -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<byte> 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<byte> 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);
}
}

View File

@@ -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<byte> src, Span<byte> dest)
{
return s_sha.ComputeHash(bytes, offset, count);
return s_sha.TryComputeHash(src, dest, out _);
}
}

View File

@@ -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<byte> 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<byte> ComputeSHAHash(ReadOnlySpan<byte> src, Span<byte> dest)
{
return s_sha.ComputeHash(bytes, offset, count);
if (!s_sha.TryComputeHash(src, dest, out _))
throw new InvalidOperationException("Can't compute hash");
return dest;
}
}

View File

@@ -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)]
[assembly: AssemblyVersion("2020.7.0.0")]
[assembly: AssemblyFileVersion("2020.7.0.0")]
[assembly: System.CLSCompliant(false)]

View File

@@ -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<byte> 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<byte>(buffer, offset, count));
public override void Write(ReadOnlySpan<byte> 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<byte>(buffer, offset, count));
public override int Read(Span<byte> 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<byte> 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<byte>(buffer, offset, count));
public override void Write(ReadOnlySpan<byte> 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;
}
}
}

View File

@@ -50,7 +50,7 @@ namespace Robust.Server.GameObjects
return true;
}
data = default;
data = default!;
return false;
}

View File

@@ -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
{

View File

@@ -42,11 +42,9 @@ namespace Robust.Shared.Network.Messages
case EntityMessageType.SystemMessage:
{
var serializer = IoCManager.Resolve<IRobustSerializer>();
int messageLength = buffer.ReadInt32();
using (var stream = new MemoryStream(buffer.ReadBytes(messageLength)))
{
SystemMessage = serializer.Deserialize<EntitySystemMessage>(stream);
}
int length = buffer.ReadInt32();
using var stream = buffer.ReadAsStream(length);
SystemMessage = serializer.Deserialize<EntitySystemMessage>(stream);
}
break;
@@ -56,11 +54,9 @@ namespace Robust.Shared.Network.Messages
NetId = buffer.ReadUInt32();
var serializer = IoCManager.Resolve<IRobustSerializer>();
int messageLength = buffer.ReadInt32();
using (var stream = new MemoryStream(buffer.ReadBytes(messageLength)))
{
ComponentMessage = serializer.Deserialize<ComponentMessage>(stream);
}
int length = buffer.ReadInt32();
using var stream = buffer.ReadAsStream(length);
ComponentMessage = serializer.Deserialize<ComponentMessage>(stream);
}
break;
}
@@ -188,6 +184,7 @@ namespace Robust.Shared.Network.Messages
}
}
#if false
private List<object> UnPackParams(NetIncomingMessage message)
{
var messageParams = new List<object>();
@@ -236,12 +233,15 @@ namespace Robust.Shared.Network.Messages
break;
case NetworkDataType.d_byteArray:
int length = message.ReadInt32();
messageParams.Add(message.ReadBytes(length));
var buf = new byte[length];
message.ReadBytes(buf);
messageParams.Add(buf)
break;
}
}
return messageParams;
}
#endif
#endregion Parameter Packing

View File

@@ -39,11 +39,9 @@ namespace Robust.Shared.Network.Messages
var serializer = IoCManager.Resolve<IRobustSerializer>();
var length = buffer.ReadVariableInt32();
var stateData = buffer.ReadBytes(length);
using var memoryStream = new MemoryStream(stateData);
Echo = serializer.Deserialize<FormattedMessage>(memoryStream);
Response = serializer.Deserialize<FormattedMessage>(memoryStream);
using var stream = buffer.ReadAsStream(length);
Echo = serializer.Deserialize<FormattedMessage>(stream);
Response = serializer.Deserialize<FormattedMessage>(stream);
}
}

View File

@@ -37,12 +37,9 @@ namespace Robust.Shared.Network.Messages
{
MsgSize = buffer.LengthBytes;
var length = buffer.ReadVariableInt32();
var stateData = buffer.ReadBytes(length);
using (var stateStream = new MemoryStream(stateData))
{
var serializer = IoCManager.Resolve<IRobustSerializer>();
State = serializer.Deserialize<GameState>(stateStream);
}
using var stream = buffer.ReadAsStream(length);
var serializer = IoCManager.Resolve<IRobustSerializer>();
State = serializer.Deserialize<GameState>(stream);
State.PayloadSize = length;
}

View File

@@ -42,19 +42,13 @@ namespace Robust.Shared.Network.Messages
SessionId = buffer.ReadUInt32();
{
var length = buffer.ReadInt32();
var bytes = buffer.ReadBytes(length);
using (var stream = new MemoryStream(bytes))
{
PropertyIndex = serializer.Deserialize<object[]>(stream);
}
using var stream = buffer.ReadAsStream(length);
PropertyIndex = serializer.Deserialize<object[]>(stream);
}
{
var length = buffer.ReadInt32();
var bytes = buffer.ReadBytes(length);
using (var stream = new MemoryStream(bytes))
{
Value = serializer.Deserialize(stream);
}
using var stream = buffer.ReadAsStream(length);
Value = serializer.Deserialize(stream);
}
}

View File

@@ -41,11 +41,8 @@ namespace Robust.Shared.Network.Messages
RequestId = buffer.ReadUInt32();
var serializer = IoCManager.Resolve<IRobustSerializer>();
var length = buffer.ReadInt32();
var bytes = buffer.ReadBytes(length);
using (var stream = new MemoryStream(bytes))
{
Blob = serializer.Deserialize<ViewVariablesBlob>(stream);
}
using var stream = buffer.ReadAsStream(length);
Blob = serializer.Deserialize<ViewVariablesBlob>(stream);
}
public override void WriteToBuffer(NetOutgoingMessage buffer)

View File

@@ -47,11 +47,8 @@ namespace Robust.Shared.Network.Messages
SessionId = buffer.ReadUInt32();
var serializer = IoCManager.Resolve<IRobustSerializer>();
var length = buffer.ReadInt32();
var bytes = buffer.ReadBytes(length);
using (var stream = new MemoryStream(bytes))
{
RequestMeta = serializer.Deserialize<ViewVariablesRequest>(stream);
}
using var stream = buffer.ReadAsStream(length);
RequestMeta = serializer.Deserialize<ViewVariablesRequest>(stream);
}
public override void WriteToBuffer(NetOutgoingMessage buffer)
@@ -68,4 +65,3 @@ namespace Robust.Shared.Network.Messages
}
}
}

View File

@@ -43,11 +43,8 @@ namespace Robust.Shared.Network.Messages
RequestId = buffer.ReadUInt32();
var serializer = IoCManager.Resolve<IRobustSerializer>();
var length = buffer.ReadInt32();
var bytes = buffer.ReadBytes(length);
using (var stream = new MemoryStream(bytes))
{
Selector = serializer.Deserialize<ViewVariablesObjectSelector>(stream);
}
using var stream = buffer.ReadAsStream(length);
Selector = serializer.Deserialize<ViewVariablesObjectSelector>(stream);
}
public override void WriteToBuffer(NetOutgoingMessage buffer)

View File

@@ -6,6 +6,7 @@ using Robust.Shared.Network;
namespace Robust.Shared.Serialization
{
/// <summary>
/// The server part of the string-exchange handshake. Sent as the
/// first message in the handshake. Tells the client the hash of
@@ -35,7 +36,7 @@ namespace Robust.Shared.Serialization
throw new InvalidOperationException("Hash too long.");
}
Hash = buffer.ReadBytes(len);
buffer.ReadBytes(Hash = new byte[len]);
}
public override void WriteToBuffer(NetOutgoingMessage buffer)

View File

@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.IO;
using JetBrains.Annotations;
using Lidgren.Network;
@@ -7,6 +8,7 @@ using Robust.Shared.Network;
namespace Robust.Shared.Serialization
{
/// <summary>
/// The meat of the string-exchange handshake sandwich. Sent by the
/// server after the client requests an updated copy of the mapping.
@@ -22,6 +24,8 @@ namespace Robust.Shared.Serialization
{
}
public int PackageSize { get; set; }
/// <value>
/// The raw bytes of the string mapping held by the server.
/// </value>
@@ -29,14 +33,8 @@ namespace Robust.Shared.Serialization
public override void ReadFromBuffer(NetIncomingMessage buffer)
{
var l = buffer.ReadVariableInt32();
var success = buffer.ReadBytes(l, out var buf);
if (!success)
{
throw new InvalidDataException("Not all of the bytes were available in the message.");
}
Package = buf;
PackageSize = buffer.ReadVariableInt32();
buffer.ReadBytes(Package = new byte[PackageSize]);
}
public override void WriteToBuffer(NetOutgoingMessage buffer)