From 2fa0f6d3f34b13cef84997204a4a26ff960dd816 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 16 Aug 2021 19:03:59 -0500 Subject: [PATCH 01/29] FastBufferWriter implemented and tested (still need to add and update some xmldoc comments though) --- .../Runtime/Serialization.meta | 8 + .../Runtime/Serialization/FastBufferWriter.cs | 254 ++ .../Serialization/FastBufferWriter.cs.meta | 11 + .../Runtime/Serialization/BitCounter.cs | 106 + .../Runtime/Serialization/BitCounter.cs.meta | 11 + .../Runtime/Serialization/BitWriter.cs | 190 ++ .../Runtime/Serialization/BitWriter.cs.meta | 11 + .../Runtime/Serialization/BytePacker.cs | 565 +++++ .../Runtime/Serialization/BytePacker.cs.meta | 11 + .../Runtime/Serialization/FastBufferWriter.cs | 835 +++++++ .../Serialization/FastBufferWriter.cs.meta | 11 + .../Serialization/SerializationTypeTable.cs | 100 + .../SerializationTypeTable.cs.meta | 11 + .../Tests/Editor/Serialization.meta | 3 + .../Editor/Serialization/BitCounterTests.cs | 63 + .../Serialization/BitCounterTests.cs.meta | 11 + .../Editor/Serialization/BitWriterTests.cs | 178 ++ .../Serialization/BitWriterTests.cs.meta | 11 + .../Editor/Serialization/BytePackerTests.cs | 788 ++++++ .../Serialization/BytePackerTests.cs.meta | 11 + .../Serialization/FastBufferWriterTests.cs | 2123 +++++++++++++++++ .../FastBufferWriterTests.cs.meta | 11 + .../com.unity.netcode.editortests.asmdef | 21 +- .../Assets/StreamingAssets/BuildInfo.json | 1 + .../StreamingAssets/BuildInfo.json.meta | 7 + 25 files changed, 5347 insertions(+), 5 deletions(-) create mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization.meta create mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs create mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs.meta create mode 100644 testproject/Assets/StreamingAssets/BuildInfo.json create mode 100644 testproject/Assets/StreamingAssets/BuildInfo.json.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization.meta b/com.unity.multiplayer.mlapi/Runtime/Serialization.meta new file mode 100644 index 0000000000..7ec99bd274 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Serialization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4fd73dafe8658fb4cb3c87f77e783073 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs new file mode 100644 index 0000000000..ddf56fcd51 --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs @@ -0,0 +1,254 @@ +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Netcode; + +namespace Unity.Multiplayer.Netcode +{ + public struct FastBufferWriter + { + private NativeArray m_buffer; + private readonly unsafe byte* m_bufferPointer; + private int m_position; + + public unsafe FastBufferWriter(NativeArray buffer, int position = 0) + { + m_buffer = buffer; + m_bufferPointer = (byte*) m_buffer.GetUnsafePtr(); + m_position = position; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Seek(int where) + { + m_position = where; + } + + public int Position => m_position; + + public NativeArray GetNativeArray() + { + return m_buffer; + } + + public byte[] ToArray() + { + return m_buffer.ToArray(); + } + + public unsafe byte* GetUnsafePtr() + { + return m_bufferPointer; + } + + public unsafe byte* GetUnsafePtrAtCurrentPosition() + { + return m_bufferPointer + m_position; + } + + /// + /// Write single-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteSinglePacked(float value) + { + WriteUInt32Packed(ToUint(value)); + } + + /// + /// Write double-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteDoublePacked(double value) + { + WriteUInt64Packed(ToUlong(value)); + } + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt16Packed(short value) => WriteInt64Packed(value); + + /// + /// Write an unsigned short (UInt16) as a varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt16Packed(ushort value) => WriteUInt64Packed(value); + + /// + /// Write a two-byte character as a varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteCharPacked(char c) => WriteUInt16Packed(c); + + /// + /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt32Packed(int value) => WriteInt64Packed(value); + + /// + /// Write an unsigned int (UInt32) as a varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteUInt32Packed(uint value) => WriteUInt64Packed(value); + + /// + /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteInt64Packed(long value) => WriteUInt64Packed(Arithmetic.ZigZagEncode(value)); + + /// + /// Write an unsigned long (UInt64) as a varint to the stream. + /// + /// Value to write + public void WriteUInt64Packed(ulong value) + { + if (value <= 240) + { + WriteULongByte(value); + } + else if (value <= 2287) + { + WriteULongByte(((value - 240) >> 8) + 241); + WriteULongByte(value - 240); + } + else if (value <= 67823) + { + WriteByte(249); + WriteULongByte((value - 2288) >> 8); + WriteULongByte(value - 2288); + } + else + { + ulong header = 255; + ulong match = 0x00FF_FFFF_FFFF_FFFFUL; + while (value <= match) + { + --header; + match >>= 8; + } + + WriteULongByte(header); + int max = (int)(header - 247); + for (int i = 0; i < max; ++i) + { + WriteULongByte(value >> (i << 3)); + } + } + } + + /// + /// Write a byte (in an int format) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteIntByte(int value) => WriteByte((byte)value); + + /// + /// Write a byte (in a ulong format) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteULongByte(ulong byteValue) => WriteByte((byte)byteValue); + + /// + /// Write a byte to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteByte(byte value) + { + m_bufferPointer[m_position++] = value; + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytes(byte* value, int size) + { + UnsafeUtility.MemCpy((m_bufferPointer + m_position), value, size); + m_position += size; + } + + /// + /// Copy the contents of this writer into another writer. + /// The contents will be copied from the beginning of this writer to its current position. + /// They will be copied to the other writer starting at the other writer's current position. + /// + /// Writer to copy to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyTo(FastBufferWriter other) + { + other.WriteBytes(m_bufferPointer, m_position); + } + + /// + /// Copy the contents of another writer into this writer. + /// The contents will be copied from the beginning of the other writer to its current position. + /// They will be copied to this writer starting at this writer's current position. + /// + /// Writer to copy to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyFrom(FastBufferWriter other) + { + WriteBytes(other.m_bufferPointer, other.m_position); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe uint ToUint(T value) where T : unmanaged + { + uint* asUint = (uint*) &value; + return *asUint; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe ulong ToUlong(T value) where T : unmanaged + { + ulong* asUlong = (ulong*) &value; + return *asUlong; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe float ToSingle(T value) where T : unmanaged + { + float* asFloat = (float*) &value; + return *asFloat; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe double ToDouble(T value) where T : unmanaged + { + double* asDouble = (double*) &value; + return *asDouble; + } + + /// + /// Write a value of any unmanaged type to the buffer. + /// It will be copied into the buffer exactly as it exists in memory. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValue(in T value) where T : unmanaged + { + int len = sizeof(T); + T* pointer = (T*)(m_bufferPointer+m_position); + *pointer = value; + m_position += len; + } + } +} \ No newline at end of file diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta new file mode 100644 index 0000000000..6a6eb1001b --- /dev/null +++ b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6cac99a38cd00a41a020d7f46dfb0f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs new file mode 100644 index 0000000000..a357077bb5 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs @@ -0,0 +1,106 @@ +using System.Runtime.CompilerServices; + +namespace Unity.Multiplayer.Netcode +{ + public static class BitCounter + { + // Since we don't have access to BitOperations.LeadingZeroCount() (which would have been the fastest) + // we use the De Bruijn sequence to do this calculation + // See https://en.wikipedia.org/wiki/De_Bruijn_sequence and https://www.chessprogramming.org/De_Bruijn_Sequence + private const ulong k_DeBruijnMagic64 = 0x37E84A99DAE458F; + private const uint k_DeBruijnMagic32 = 0x06EB14F9; + + // We're counting bytes, not bits, so these have all had the operation x/8 + 1 applied + private static readonly int[] k_deBruijnTableBytes64 = + { + 0/8+1, 1/8+1, 17/8+1, 2/8+1, 18/8+1, 50/8+1, 3/8+1, 57/8+1, + 47/8+1, 19/8+1, 22/8+1, 51/8+1, 29/8+1, 4/8+1, 33/8+1, 58/8+1, + 15/8+1, 48/8+1, 20/8+1, 27/8+1, 25/8+1, 23/8+1, 52/8+1, 41/8+1, + 54/8+1, 30/8+1, 38/8+1, 5/8+1, 43/8+1, 34/8+1, 59/8+1, 8/8+1, + 63/8+1, 16/8+1, 49/8+1, 56/8+1, 46/8+1, 21/8+1, 28/8+1, 32/8+1, + 14/8+1, 26/8+1, 24/8+1, 40/8+1, 53/8+1, 37/8+1, 42/8+1, 7/8+1, + 62/8+1, 55/8+1, 45/8+1, 31/8+1, 13/8+1, 39/8+1, 36/8+1, 6/8+1, + 61/8+1, 44/8+1, 12/8+1, 35/8+1, 60/8+1, 11/8+1, 10/8+1, 9/8+1, + }; + + private static readonly int[] k_deBruijnTableBytes32 = + { + 0/8+1, 1/8+1, 16/8+1, 2/8+1, 29/8+1, 17/8+1, 3/8+1, 22/8+1, + 30/8+1, 20/8+1, 18/8+1, 11/8+1, 13/8+1, 4/8+1, 7/8+1, 23/8+1, + 31/8+1, 15/8+1, 28/8+1, 21/8+1, 19/8+1, 10/8+1, 12/8+1, 6/8+1, + 14/8+1, 27/8+1, 9/8+1, 5/8+1, 26/8+1, 8/8+1, 25/8+1, 24/8+1, + }; + + // And here we're counting the number of set bits, not the position of the highest set, + // so these still have +1 applied - unfortunately 0 and 1 both return the same value. + private static readonly int[] k_deBruijnTableBits64 = + { + 0+1, 1+1, 17+1, 2+1, 18+1, 50+1, 3+1, 57+1, + 47+1, 19+1, 22+1, 51+1, 29+1, 4+1, 33+1, 58+1, + 15+1, 48+1, 20+1, 27+1, 25+1, 23+1, 52+1, 41+1, + 54+1, 30+1, 38+1, 5+1, 43+1, 34+1, 59+1, 8+1, + 63+1, 16+1, 49+1, 56+1, 46+1, 21+1, 28+1, 32+1, + 14+1, 26+1, 24+1, 40+1, 53+1, 37+1, 42+1, 7+1, + 62+1, 55+1, 45+1, 31+1, 13+1, 39+1, 36+1, 6+1, + 61+1, 44+1, 12+1, 35+1, 60+1, 11+1, 10+1, 9+1, + }; + + private static readonly int[] k_deBruijnTableBits32 = + { + 0+1, 1+1, 16+1, 2+1, 29+1, 17+1, 3+1, 22+1, + 30+1, 20+1, 18+1, 11+1, 13+1, 4+1, 7+1, 23+1, + 31+1, 15+1, 28+1, 21+1, 19+1, 10+1, 12+1, 6+1, + 14+1, 27+1, 9+1, 5+1, 26+1, 8+1, 25+1, 24+1, + }; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUsedByteCount(uint b) + { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b = b & ~(b >> 1); + return k_deBruijnTableBytes32[b*k_DeBruijnMagic32 >> 27]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUsedByteCount(ulong b) + { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b |= b >> 32; + b = b & ~(b >> 1); + return k_deBruijnTableBytes64[b*k_DeBruijnMagic64 >> 58]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUsedBitCount(uint b) + { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b = b & ~(b >> 1); + return k_deBruijnTableBits32[b*k_DeBruijnMagic32 >> 27]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetUsedBitCount(ulong b) + { + b |= b >> 1; + b |= b >> 2; + b |= b >> 4; + b |= b >> 8; + b |= b >> 16; + b |= b >> 32; + b = b & ~(b >> 1); + return k_deBruijnTableBits64[b*k_DeBruijnMagic64 >> 58]; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs.meta new file mode 100644 index 0000000000..f9d96d15b6 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6983de23935090341bf45d5564401b9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs new file mode 100644 index 0000000000..8b28d3d80d --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -0,0 +1,190 @@ +using System; +using System.Runtime.CompilerServices; +using Mono.Cecil; +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Multiplayer.Netcode +{ + public ref struct BitWriter + { + private unsafe FastBufferWriter.InternalData* m_InternalData; + private unsafe byte* m_BufferPointer; + private int m_Position; + private int m_BitPosition; + private const int BITS_PER_BYTE = 8; + + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + public bool BitAligned { get => (m_BitPosition & 7) == 0; } + + internal unsafe BitWriter(ref FastBufferWriter.InternalData internalData) + { + fixed (FastBufferWriter.InternalData* internalDataPtr = &internalData) + { + m_InternalData = internalDataPtr; + } + + m_BufferPointer = internalData.BufferPointer + internalData.Position; + m_Position = internalData.Position; + m_BitPosition = 0; + } + + public unsafe void Dispose() + { + var bytesWritten = m_BitPosition >> 3; + if (!BitAligned) + { + // Accounting for the partial write + ++bytesWritten; + } + + m_InternalData->CommitBitwiseWrites(bytesWritten); + } + + /// + /// Write s certain amount of bits to the stream. + /// + /// Value to get bits from. + /// Amount of bits to write + public unsafe void WriteBits(ulong value, int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (bitCount > 64) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write more than 64 bits from a 64-bit value!"); + } + + if (bitCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); + } + + int checkPos = (m_BitPosition + bitCount) >> 3; + if (checkPos > m_InternalData->AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + int wholeBytes = bitCount / BITS_PER_BYTE; + byte* asBytes = (byte*) &value; + if (BitAligned) + { + WritePartialValue(value, wholeBytes); + } + else + { + for (var i = 0; i < wholeBytes; ++i) + { + WriteMisaligned(asBytes[i]); + } + } + + for (var count = wholeBytes * BITS_PER_BYTE; count < bitCount; ++count) + { + WriteBit((value & (1UL << count)) != 0); + } + } + + /// + /// Write bits to stream. + /// + /// Value to get bits from. + /// Amount of bits to write. + public unsafe void WriteBits(byte value, int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + int checkPos = (m_BitPosition + bitCount) >> 3; + if (checkPos >= m_InternalData->AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + for (int i = 0; i < bitCount; ++i) + { + WriteBit(((value >> i) & 1) != 0); + } + } + + /// + /// Write a single bit to the buffer + /// + /// Value of the bit. True represents 1, False represents 0 + public unsafe void WriteBit(bool bit) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + int checkPos = (m_BitPosition + 1) >> 3; + if (checkPos >= m_InternalData->AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + int offset = m_BitPosition & 7; + int pos = m_BitPosition >> 3; + ++m_BitPosition; + m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset))); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged + { + // Switch statement to write small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + + byte* ptr = ((byte*) &value) + offsetBytes; + byte* bufferPointer = m_BufferPointer + m_Position; + switch (bytesToWrite) + { + case 1: + bufferPointer[0] = *ptr; + break; + case 2: + *(ushort*) bufferPointer = *(ushort*)ptr; + break; + case 3: + *(ushort*) bufferPointer = *(ushort*)ptr; + *(bufferPointer+2) = *(ptr+2); + break; + case 4: + *(uint*) bufferPointer = *(uint*)ptr; + break; + case 5: + *(uint*) bufferPointer = *(uint*)ptr; + *(bufferPointer+4) = *(ptr+4); + break; + case 6: + *(uint*) bufferPointer = *(uint*) &ptr; + *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); + break; + case 7: + *(uint*) bufferPointer = *(uint*) &value; + *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); + *(bufferPointer+6) = *(ptr+6); + break; + case 8: + *(ulong*) bufferPointer = *(ulong*)ptr; + break; + default: + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); + break; + } + + m_BitPosition += bytesToWrite * BITS_PER_BYTE; + } + + private unsafe void WriteMisaligned(byte value) + { + int off = (int)(m_BitPosition & 7); + int pos = m_BitPosition >> 3; + int shift1 = 8 - off; + m_BufferPointer[pos + 1] = (byte)((m_BufferPointer[pos + 1] & (0xFF << off)) | (value >> shift1)); + m_BufferPointer[pos] = (byte)((m_BufferPointer[pos] & (0xFF >> shift1)) | (value << off)); + + m_BitPosition += 8; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs.meta new file mode 100644 index 0000000000..604982f3c0 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d6360e096142c149a11a2e86560c350 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs new file mode 100644 index 0000000000..bf458eee20 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -0,0 +1,565 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class BytePacker + { + #region Managed TypePacking + /// + /// Writes a boxed object in a packed format + /// Named differently from other WriteValuePacked methods to avoid accidental boxing + /// + /// The object to write + public static void WriteObjectPacked(ref FastBufferWriter writer, object value, bool isNullable = false) + { +#if UNITY_NETCODE_DEBUG_NO_PACKING + writer.WriteObject(value, isNullable); + return; +#endif + if (isNullable || value.GetType().IsNullable()) + { + bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); + + WriteValuePacked(ref writer, isNull); + + if (isNull) + { + return; + } + } + + var type = value.GetType(); + var hasSerializer = SerializationTypeTable.SerializersPacked.TryGetValue(type, out var serializer); + if (hasSerializer) + { + serializer(ref writer, value); + return; + } + + if (value is Array array) + { + WriteValuePacked(ref writer, array.Length); + + for (int i = 0; i < array.Length; i++) + { + WriteObjectPacked(ref writer, array.GetValue(i)); + } + } + + if (value.GetType().IsEnum) + { + switch (Convert.GetTypeCode(value)) + { + case TypeCode.Boolean: + WriteValuePacked(ref writer, (byte)value); + break; + case TypeCode.Char: + WriteValuePacked(ref writer, (char)value); + break; + case TypeCode.SByte: + WriteValuePacked(ref writer, (sbyte)value); + break; + case TypeCode.Byte: + WriteValuePacked(ref writer, (byte)value); + break; + case TypeCode.Int16: + WriteValuePacked(ref writer, (short)value); + break; + case TypeCode.UInt16: + WriteValuePacked(ref writer, (ushort)value); + break; + case TypeCode.Int32: + WriteValuePacked(ref writer, (int)value); + break; + case TypeCode.UInt32: + WriteValuePacked(ref writer, (uint)value); + break; + case TypeCode.Int64: + WriteValuePacked(ref writer, (long)value); + break; + case TypeCode.UInt64: + WriteValuePacked(ref writer, (ulong)value); + break; + } + return; + } + if (value is GameObject) + { + var networkObject = ((GameObject)value).GetComponent(); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + WriteValuePacked(ref writer, networkObject.NetworkObjectId); + return; + } + if (value is NetworkObject) + { + if (!((NetworkObject)value).IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); + } + + WriteValuePacked(ref writer, ((NetworkObject)value).NetworkObjectId); + return; + } + if (value is NetworkBehaviour) + { + if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + } + + WriteValuePacked(ref writer, ((NetworkBehaviour)value).NetworkObjectId); + WriteValuePacked(ref writer, ((NetworkBehaviour)value).NetworkBehaviourId); + return; + } + if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + } + + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + } + #endregion + + #region Unmanaged Type Packing + +#if UNITY_NETCODE_DEBUG_NO_PACKING + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteValuePacked(T value) where T: unmanaged => writer.WriteValue(value); +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void WriteValuePacked(ref FastBufferWriter writer, ref TEnum value) where TEnum : unmanaged, Enum + { + TEnum enumValue = value; + switch (sizeof(TEnum)) + { + case sizeof(int): + WriteValuePacked(ref writer, *(int*)&enumValue); + break; + case sizeof(byte): + WriteValuePacked(ref writer, *(byte*)&enumValue); + break; + case sizeof(short): + WriteValuePacked(ref writer, *(short*)&enumValue); + break; + case sizeof(long): + WriteValuePacked(ref writer, *(long*)&enumValue); + break; + } + } + + /// + /// Write single-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, float value) + { + WriteUInt32Packed(ref writer, ToUint(value)); + } + + /// + /// Write double-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, double value) + { + WriteUInt64Packed(ref writer, ToUlong(value)); + } + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByte(value); + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, bool value) => writer.WriteValue(value); + + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, short value) => WriteUInt32Packed(ref writer, (ushort)Arithmetic.ZigZagEncode(value)); + + /// + /// Write an unsigned short (UInt16) as a varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, ushort value) => WriteUInt32Packed(ref writer, value); + + /// + /// Write a two-byte character as a varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, char c) => WriteUInt32Packed(ref writer, c); + + /// + /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, int value) => WriteUInt32Packed(ref writer, (uint)Arithmetic.ZigZagEncode(value)); + + /// + /// Write an unsigned int (UInt32) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, uint value) => WriteUInt32Packed(ref writer, value); + + /// + /// Write an unsigned long (UInt64) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, ulong value) => WriteUInt64Packed(ref writer, value); + + /// + /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, long value) => WriteUInt64Packed(ref writer, (ulong)Arithmetic.ZigZagEncode(value)); + + /// + /// Convenience method that writes two packed Vector3 from the ray to the stream + /// + /// Ray to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Ray ray) + { + WriteValuePacked(ref writer, ray.origin); + WriteValuePacked(ref writer, ray.direction); + } + + /// + /// Convenience method that writes two packed Vector2 from the ray to the stream + /// + /// Ray2D to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Ray2D ray2d) + { + WriteValuePacked(ref writer, ray2d.origin); + WriteValuePacked(ref writer, ray2d.direction); + } + + /// + /// Convenience method that writes four varint floats from the color to the stream + /// + /// Color to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Color color) + { + WriteValuePacked(ref writer, color.r); + WriteValuePacked(ref writer, color.g); + WriteValuePacked(ref writer, color.b); + WriteValuePacked(ref writer, color.a); + } + + /// + /// Convenience method that writes four varint floats from the color to the stream + /// + /// Color to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Color32 color) + { + WriteValuePacked(ref writer, color.r); + WriteValuePacked(ref writer, color.g); + WriteValuePacked(ref writer, color.b); + WriteValuePacked(ref writer, color.a); + } + + /// + /// Convenience method that writes two varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Vector2 vector2) + { + WriteValuePacked(ref writer, vector2.x); + WriteValuePacked(ref writer, vector2.y); + } + + /// + /// Convenience method that writes three varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Vector3 vector3) + { + WriteValuePacked(ref writer, vector3.x); + WriteValuePacked(ref writer, vector3.y); + WriteValuePacked(ref writer, vector3.z); + } + + /// + /// Convenience method that writes four varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Vector4 vector4) + { + WriteValuePacked(ref writer, vector4.x); + WriteValuePacked(ref writer, vector4.y); + WriteValuePacked(ref writer, vector4.z); + WriteValuePacked(ref writer, vector4.w); + } + + /// + /// Writes the rotation to the stream. + /// + /// Rotation to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, Quaternion rotation) + { + if (Mathf.Sign(rotation.w) < 0) + { + WriteValuePacked(ref writer, -rotation.x); + WriteValuePacked(ref writer, -rotation.y); + WriteValuePacked(ref writer, -rotation.z); + } + else + { + WriteValuePacked(ref writer, rotation.x); + WriteValuePacked(ref writer, rotation.y); + WriteValuePacked(ref writer, rotation.z); + } + } + + /// + /// Writes a string in a packed format + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, string s) + { + WriteValuePacked(ref writer, (uint)s.Length); + int target = s.Length; + for (int i = 0; i < target; ++i) + { + WriteValuePacked(ref writer, s[i]); + } + } +#endif + #endregion + + #region Bit Packing + +#if UNITY_NETCODE_DEBUG_NO_PACKING + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteValueBitPacked(T value) where T: unmanaged => writer.WriteValue(value); +#else + public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort) Arithmetic.ZigZagEncode(value)); + + public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value) + { + if (value >= 0b1000_0000_0000_0000) + { + throw new ArgumentException("BitPacked ushorts must be <= 15 bits"); + } + + if (value <= 0b0111_1111) + { + writer.WriteByte((byte)(value << 1)); + return; + } + + writer.WriteValue((ushort)((value << 1) | 0b1)); + } + + public static void WriteValueBitPacked(ref FastBufferWriter writer, int value) => WriteValueBitPacked(ref writer, (uint) Arithmetic.ZigZagEncode(value)); + + public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) + { + if (value >= 0b0100_0000_0000_0000_0000_0000_0000_0000) + { + throw new ArgumentException("BitPacked uints must be <= 30 bits"); + } + + if (value <= 0b0011_1111) + { + writer.WriteByte((byte)(value << 2)); + return; + } + + if (value <= 0b0011_1111_1111_1111) + { + writer.WriteValue((ushort)((value << 2) | 0b01)); + return; + } + + if (value <= 0b0011_1111_1111_1111_1111_1111) + { + writer.WritePartialValue(((value << 2) | 0b10), 3); + return; + } + + writer.WriteValue(((value << 2) | 0b11)); + } + + public static void WriteValueBitPacked(ref FastBufferWriter writer, long value) => WriteValueBitPacked(ref writer, (ulong) Arithmetic.ZigZagEncode(value)); + + public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) + { + if (value >= 0b0010_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000) + { + throw new ArgumentException("BitPacked ulongs must be <= 61 bits"); + } + + if (value <= 0b0001_1111) + { + writer.WriteByte((byte)(value << 3)); + return; + } + + if (value <= 0b0001_1111_1111_1111) + { + writer.WriteValue((ushort)((value << 3) | 0b001)); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111) + { + writer.WritePartialValue((value << 3) | 0b010, 3); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111) + { + writer.WriteValue((uint)((value << 3) | 0b011)); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + writer.WritePartialValue((value << 3) | 0b100, 5); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + writer.WritePartialValue((value << 3) | 0b101, 6); + return; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + writer.WritePartialValue((value << 3) | 0b110, 7); + return; + } + + writer.WriteValue((value << 3) | 0b111); + } +#endif + #endregion + + #region Private Methods + private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) + { + if (value <= 240) + { + writer.WriteByte((byte)value); + return; + } + if (value <= 2287) + { + writer.WriteByte((byte)(((value - 240) >> 8) + 241)); + writer.WriteByte((byte)(value - 240)); + return; + } + var writeBytes = BitCounter.GetUsedByteCount(value); + writer.WriteByte((byte)(247 + writeBytes)); + writer.WritePartialValue(value, writeBytes); + } + + // Looks like the same code as WriteUInt64Packed? + // It's actually different because it will call the more efficient 32-bit version + // of BytewiseUtility.GetUsedByteCount(). + private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) + { + if (value <= 240) + { + writer.WriteByte((byte)value); + return; + } + if (value <= 2287) + { + writer.WriteByte((byte)(((value - 240) >> 8) + 241)); + writer.WriteByte((byte)(value - 240)); + return; + } + var writeBytes = BitCounter.GetUsedByteCount(value); + writer.WriteByte((byte)(247 + writeBytes)); + writer.WritePartialValue(value, writeBytes); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint ToUint(T value) where T : unmanaged + { + uint* asUint = (uint*) &value; + return *asUint; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong ToUlong(T value) where T : unmanaged + { + ulong* asUlong = (ulong*) &value; + return *asUlong; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe float ToSingle(T value) where T : unmanaged + { + float* asFloat = (float*) &value; + return *asFloat; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe double ToDouble(T value) where T : unmanaged + { + double* asDouble = (double*) &value; + return *asDouble; + } + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs.meta new file mode 100644 index 0000000000..dfb5e75613 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a3ec13587ae68cb49b82af8612d47698 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs new file mode 100644 index 0000000000..c6b88f3b60 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -0,0 +1,835 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public struct FastBufferWriter : IDisposable + { + private NativeArray m_buffer; + + internal struct InternalData + { + public unsafe byte* BufferPointer; + public int Position; + public int Length; + public int Capacity; + public int MaxCapacity; + public Allocator Allocator; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + public int AllowedWriteMark; + public bool InBitwiseContext; +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CommitBitwiseWrites(int amount) + { + Position += amount; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + InBitwiseContext = false; +#endif + } + } + + private InternalData m_InternalData; + + public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + var buffer = new NativeArray(size, allocator, NativeArrayOptions.ClearMemory); +#else + var buffer = new NativeArray(size, allocator, NativeArrayOptions.UninitializedMemory); +#endif + m_buffer = buffer; + m_InternalData = new InternalData + { + BufferPointer = (byte*) m_buffer.GetUnsafePtr(), + Position = 0, + Length = 0, + Capacity = buffer.Length, + Allocator = allocator, + MaxCapacity = maxSize == -1 ? size : maxSize, +#if DEVELOPMENT_BUILD || UNITY_EDITOR + AllowedWriteMark = 0, + InBitwiseContext = false, +#endif + }; + } + + public void Dispose() + { + m_buffer.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Seek(int where) + { + // This avoids us having to synchronize length all the time. + // Writing things is a much more common operation than seeking + // or querying length. The length here is a high watermark of + // what's been written. So before we seek, if the current position + // is greater than the length, we update that watermark. + // When querying length later, we'll return whichever of the two + // values is greater, thus if we write past length, length increases + // because position increases, and if we seek backward, length remembers + // the position it was in. + // Seeking forward will not update the length. + if (m_InternalData.Position > m_InternalData.Length && where < m_InternalData.Position) + { + m_InternalData.Length = m_InternalData.Position; + } + m_InternalData.Position = where; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Truncate(int where = -1) + { + if (where == -1) + { + where = Position; + } + + if (m_InternalData.Position > where) + { + m_InternalData.Position = where; + } + if(m_InternalData.Length > where) + { + m_InternalData.Length = where; + } + } + + public unsafe BitWriter EnterBitwiseContext() + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.InBitwiseContext = true; +#endif + return new BitWriter(ref m_InternalData); + } + + public int Position => m_InternalData.Position; + public int Capacity => m_InternalData.Capacity; + public int Length => m_InternalData.Position > m_InternalData.Length ? m_InternalData.Position : m_InternalData.Length; + + private unsafe void Grow() + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + var buffer = new NativeArray(Math.Min(m_InternalData.Capacity * 2, m_InternalData.MaxCapacity), m_InternalData.Allocator, NativeArrayOptions.ClearMemory); +#else + var buffer = new NativeArray(m_InternalData.Capacity * 2, m_InternalData.Allocator, NativeArrayOptions.UninitializedMemory); +#endif + UnsafeUtility.MemCpy(buffer.GetUnsafePtr(), m_InternalData.BufferPointer, Length); + m_buffer.Dispose(); + m_buffer = buffer; + m_InternalData.BufferPointer = (byte*) m_buffer.GetUnsafePtr(); + m_InternalData.Capacity = buffer.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool VerifyCanWrite(int bytes) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + if (m_InternalData.Position + bytes > m_InternalData.Capacity) + { + if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + { + Grow(); + } + else + { + return false; + } + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedWriteMark = m_InternalData.Position + bytes; +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + int len = sizeof(T); + if (m_InternalData.Position + len > m_InternalData.Capacity) + { + if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + { + Grow(); + } + else + { + return false; + } + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedWriteMark = m_InternalData.Position + len; +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NativeArray GetNativeArray() + { + return m_buffer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte[] ToArray() + { + return m_buffer.ToArray(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe byte* GetUnsafePtr() + { + return m_InternalData.BufferPointer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe byte* GetUnsafePtrAtCurrentPosition() + { + return m_InternalData.BufferPointer + m_InternalData.Position; + } + + /// + /// Writes a boxed object in a standard format + /// Named differently from other WriteValue methods to avoid accidental boxing + /// + /// The object to write + public void WriteObject(object value, bool isNullable = false) + { + if (isNullable || value.GetType().IsNullable()) + { + bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); + + WriteValueSafe(isNull); + + if (isNull) + { + return; + } + } + + var type = value.GetType(); + var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); + if (hasSerializer) + { + serializer(ref this, value); + return; + } + + if (value is Array array) + { + WriteValueSafe(array.Length); + + for (int i = 0; i < array.Length; i++) + { + WriteObject(array.GetValue(i)); + } + + return; + } + + if (value.GetType().IsEnum) + { + switch (Convert.GetTypeCode(value)) + { + case TypeCode.Boolean: + WriteValueSafe((byte)value); + break; + case TypeCode.Char: + WriteValueSafe((char)value); + break; + case TypeCode.SByte: + WriteValueSafe((sbyte)value); + break; + case TypeCode.Byte: + WriteValueSafe((byte)value); + break; + case TypeCode.Int16: + WriteValueSafe((short)value); + break; + case TypeCode.UInt16: + WriteValueSafe((ushort)value); + break; + case TypeCode.Int32: + WriteValueSafe((int)value); + break; + case TypeCode.UInt32: + WriteValueSafe((uint)value); + break; + case TypeCode.Int64: + WriteValueSafe((long)value); + break; + case TypeCode.UInt64: + WriteValueSafe((ulong)value); + break; + } + return; + } + if (value is GameObject) + { + var networkObject = ((GameObject)value).GetComponent(); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + WriteValueSafe(networkObject.NetworkObjectId); + return; + } + if (value is NetworkObject) + { + if (!((NetworkObject)value).IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); + } + + WriteValueSafe(((NetworkObject)value).NetworkObjectId); + return; + } + if (value is NetworkBehaviour) + { + if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + } + + if (!VerifyCanWrite(sizeof(ulong) * 2)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + WriteValue(((NetworkBehaviour)value).NetworkObjectId); + WriteValue(((NetworkBehaviour)value).NetworkBehaviourId); + return; + } + if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + } + + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + } + + public void WriteValue(T value) where T : INetworkSerializable + { + // TODO + } + + public void WriteValue(GameObject value) + { + var networkObject = ((GameObject)value).GetComponent(); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + WriteValue(networkObject.NetworkObjectId); + } + + public void WriteValueSafe(GameObject value) + { + var networkObject = ((GameObject)value).GetComponent(); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + } + + WriteValueSafe(networkObject.NetworkObjectId); + } + + public static int GetWriteSize(GameObject value) + { + return sizeof(ulong); + } + + public void WriteValue(in NetworkObject value) + { + if (!value.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + } + + WriteValue(value.NetworkObjectId); + } + + public void WriteValueSafe(NetworkObject value) + { + if (!value.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + } + WriteValueSafe(value.NetworkObjectId); + } + + public static int GetWriteSize(NetworkObject value) + { + return sizeof(ulong); + } + + public void WriteValue(NetworkBehaviour value) + { + if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + } + + WriteValue(value.NetworkObjectId); + WriteValue(value.NetworkBehaviourId); + } + + public void WriteValueSafe(NetworkBehaviour value) + { + if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + } + + if (!VerifyCanWrite(sizeof(ulong) + sizeof(ushort))) + { + throw new OverflowException("Writing past the end of the buffer"); + } + WriteValue(value.NetworkObjectId); + WriteValue(value.NetworkBehaviourId); + } + + public static int GetWriteSize(NetworkBehaviour value) + { + return sizeof(ulong) + sizeof(ushort); + } + + // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays + /// + /// Writes a string + /// + /// The string to write + /// Whether or not to use one byte per character. This will only allow ASCII + public unsafe void WriteValue(string s, bool oneByteChars = false) + { + WriteValue((uint)s.Length); + int target = s.Length; + if (oneByteChars) + { + for (int i = 0; i < target; ++i) + { + if (oneByteChars) + { + WriteByte((byte) s[i]); + } + } + } + else + { + fixed (char* native = s) + { + WriteBytes((byte*)native, target * sizeof(char)); + } + } + } + + // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays + /// + /// Writes a string + /// + /// The string to write + /// Whether or not to use one byte per character. This will only allow ASCII + public unsafe void WriteValueSafe(string s, bool oneByteChars = false) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + int sizeInBytes = GetWriteSize(s, oneByteChars); + + if (!VerifyCanWrite(sizeInBytes)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + + WriteValue((uint)s.Length); + int target = s.Length; + if (oneByteChars) + { + for (int i = 0; i < target; ++i) + { + if (oneByteChars) + { + WriteByte((byte) s[i]); + } + } + } + else + { + fixed (char* native = s) + { + WriteBytes((byte*)native, target * sizeof(char)); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetWriteSize(string s, bool oneByteChars = false) + { + return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); + } + + /// + /// Writes an unmanaged array + /// + /// The array to write + /// The amount of elements to write + /// Where in the array to start + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) where T: unmanaged + { + int sizeInTs = count != -1 ? count : array.Length - offset; + int sizeInBytes = sizeInTs * sizeof(T); + WriteValue(sizeInTs); + fixed (T* native = array) + { + byte* bytes = (byte*)(native + offset); + WriteBytes(bytes, sizeInBytes); + } + } + + /// + /// Writes an unmanaged array + /// + /// The array to write + /// The amount of elements to write + /// Where in the array to start + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) where T: unmanaged + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + int sizeInTs = count != -1 ? count : array.Length - offset; + int sizeInBytes = sizeInTs * sizeof(T); + + if (!VerifyCanWrite(sizeInBytes + sizeof(int))) + { + throw new OverflowException("Writing past the end of the buffer"); + } + WriteValue(sizeInTs); + fixed (T* native = array) + { + byte* bytes = (byte*)(native + offset); + WriteBytes(bytes, sizeInBytes); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T: unmanaged + { + int sizeInTs = count != -1 ? count : array.Length - offset; + int sizeInBytes = sizeInTs * sizeof(T); + return sizeof(int) + sizeInBytes; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged + { + // Switch statement to write small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + bytesToWrite > m_InternalData.AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + byte* ptr = ((byte*) &value) + offsetBytes; + byte* bufferPointer = m_InternalData.BufferPointer + m_InternalData.Position; + switch (bytesToWrite) + { + case 1: + bufferPointer[0] = *ptr; + break; + case 2: + *(ushort*) bufferPointer = *(ushort*)ptr; + break; + case 3: + *(ushort*) bufferPointer = *(ushort*)ptr; + *(bufferPointer+2) = *(ptr+2); + break; + case 4: + *(uint*) bufferPointer = *(uint*)ptr; + break; + case 5: + *(uint*) bufferPointer = *(uint*)ptr; + *(bufferPointer+4) = *(ptr+4); + break; + case 6: + *(uint*) bufferPointer = *(uint*)ptr; + *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); + break; + case 7: + *(uint*) bufferPointer = *(uint*)ptr; + *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); + *(bufferPointer+6) = *(ptr+6); + break; + case 8: + *(ulong*) bufferPointer = *(ulong*)ptr; + break; + default: + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); + break; + } + + m_InternalData.Position += bytesToWrite; + } + + /// + /// Write a byte to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteByte(byte value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + 1 > m_InternalData.AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + m_InternalData.BufferPointer[m_InternalData.Position++] = value; + } + + /// + /// Write a byte to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteByteSafe(byte value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanWrite(1)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + m_InternalData.BufferPointer[m_InternalData.Position++] = value; + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytes(byte* value, int size, int offset = 0) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + size > m_InternalData.AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + UnsafeUtility.MemCpy((m_InternalData.BufferPointer + m_InternalData.Position), value + offset, size); + m_InternalData.Position += size; + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanWrite(size)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + UnsafeUtility.MemCpy((m_InternalData.BufferPointer + m_InternalData.Position), value + offset, size); + m_InternalData.Position += size; + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytes(byte[] value, int size, int offset = 0) + { + fixed (byte* ptr = value) + { + WriteBytes(ptr, size, offset); + } + } + + /// + /// Write multiple bytes to the stream + /// + /// Value to write + /// Number of bytes to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) + { + fixed (byte* ptr = value) + { + WriteBytesSafe(ptr, size, offset); + } + } + + /// + /// Copy the contents of this writer into another writer. + /// The contents will be copied from the beginning of this writer to its current position. + /// They will be copied to the other writer starting at the other writer's current position. + /// + /// Writer to copy to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyTo(FastBufferWriter other) + { + other.WriteBytes(m_InternalData.BufferPointer, m_InternalData.Position); + } + + /// + /// Copy the contents of another writer into this writer. + /// The contents will be copied from the beginning of the other writer to its current position. + /// They will be copied to this writer starting at this writer's current position. + /// + /// Writer to copy to + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void CopyFrom(FastBufferWriter other) + { + WriteBytes(other.m_InternalData.BufferPointer, other.m_InternalData.Position); + } + + /// + /// Write a value of any unmanaged type to the buffer. + /// It will be copied into the buffer exactly as it exists in memory. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValue(in T value) where T : unmanaged + { + int len = sizeof(T); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + len > m_InternalData.AllowedWriteMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + *pointer = value; + m_InternalData.Position += len; + } + + /// + /// Write a value of any unmanaged type to the buffer. + /// It will be copied into the buffer exactly as it exists in memory. + /// + /// The value to copy + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void WriteValueSafe(in T value) where T : unmanaged + { + int len = sizeof(T); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanWrite(len)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + + T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + *pointer = value; + m_InternalData.Position += len; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int GetWriteSize(in T value) where T : unmanaged + { + return sizeof(T); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs.meta new file mode 100644 index 0000000000..0c31b46524 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 819a511316a46104db673c8a0eab9e72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs new file mode 100644 index 0000000000..8ef5bdb1ed --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class SerializationTypeTable + { + public delegate void Serialize(ref FastBufferWriter writer, object value); + + public static Dictionary Serializers = new Dictionary + { + [typeof(byte)] = (ref FastBufferWriter writer, object value) => writer.WriteByteSafe((byte)value), + [typeof(sbyte)] = (ref FastBufferWriter writer, object value) => writer.WriteByteSafe((byte)(sbyte)value), + + [typeof(ushort)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ushort)value), + [typeof(short)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((short)value), + [typeof(uint)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((uint)value), + [typeof(int)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((int)value), + [typeof(ulong)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ulong)value), + [typeof(long)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((long)value), + + [typeof(float)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((float)value), + [typeof(double)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((double)value), + + [typeof(string)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((string)value), + + [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector2)value), + [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector3)value), + [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector4)value), + [typeof(Color)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color)value), + [typeof(Color32)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color32)value), + [typeof(Ray)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray)value), + [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D)value), + [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion)value), + + [typeof(char)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char)value), + + [typeof(bool)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool)value), + + + [typeof(byte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((byte[])value), + [typeof(sbyte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((sbyte[])value), + + [typeof(ushort[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ushort[])value), + [typeof(short[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((short[])value), + [typeof(uint[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((uint[])value), + [typeof(int[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((int[])value), + [typeof(ulong[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((ulong[])value), + [typeof(long[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((long[])value), + + [typeof(float[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((float[])value), + [typeof(double[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((double[])value), + + [typeof(Vector2[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector2[])value), + [typeof(Vector3[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector3[])value), + [typeof(Vector4[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector4[])value), + [typeof(Color[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color[])value), + [typeof(Color32[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Color32[])value), + [typeof(Ray[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray[])value), + [typeof(Ray2D[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D[])value), + [typeof(Quaternion[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion[])value), + + [typeof(char[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char[])value), + + [typeof(bool[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool[])value), + }; + + public static Dictionary SerializersPacked = new Dictionary + { + [typeof(byte)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (byte)value), + [typeof(sbyte)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (byte)(sbyte)value), + + [typeof(ushort)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (ushort)value), + [typeof(short)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (short)value), + [typeof(uint)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (uint)value), + [typeof(int)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (int)value), + [typeof(ulong)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (ulong)value), + [typeof(long)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (long)value), + + [typeof(float)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (float)value), + [typeof(double)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (double)value), + + [typeof(string)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (string)value), + + [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector2)value), + [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector3)value), + [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector4)value), + [typeof(Color)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Color)value), + [typeof(Color32)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Color32)value), + [typeof(Ray)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray)value), + [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray2D)value), + [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Quaternion)value), + + [typeof(char)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (char)value), + + [typeof(bool)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (bool)value), + }; + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta new file mode 100644 index 0000000000..58c25d1646 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49ee6a0e2ea6e9441a74b173c31cf389 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization.meta new file mode 100644 index 0000000000..b3c7beee0b --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e12c4be6e89f459aa2826abba8c8d301 +timeCreated: 1628799671 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs new file mode 100644 index 0000000000..12331d6ff5 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -0,0 +1,63 @@ +using NUnit.Framework; +using Unity.Multiplayer.Netcode; + +namespace Unity.Netcode.EditorTests +{ + public class BitCounterTests + { + [Test] + public void TestBitCounter64Bits() + { + ulong value = 0; + // 0 is a special case. All values are considered at least 1 bit. + Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); + + for (int i = 0; i < 64; ++i) + { + value = 1UL << i; + Assert.AreEqual(i+1, BitCounter.GetUsedBitCount(value)); + } + } + + [Test] + public void TestBitCounter32Bits() + { + uint value = 0; + // 0 is a special case. All values are considered at least 1 bit. + Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); + + for (int i = 0; i < 32; ++i) + { + value = 1U << i; + Assert.AreEqual(i+1, BitCounter.GetUsedBitCount(value)); + } + } + [Test] + public void TestByteCounter64Bits() + { + ulong value = 0; + // 0 is a special case. All values are considered at least 1 byte. + Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); + + for (int i = 0; i < 64; ++i) + { + value = 1UL << i; + Assert.AreEqual(i/8+1, BitCounter.GetUsedByteCount(value)); + } + } + + [Test] + public void TestByteCounter32Bits() + { + uint value = 0; + // 0 is a special case. All values are considered at least 1 byte. + Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); + + for (int i = 0; i < 32; ++i) + { + value = 1U << i; + Assert.AreEqual(i/8+1, BitCounter.GetUsedByteCount(value)); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs.meta new file mode 100644 index 0000000000..64a137560b --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 76e459b9c2aeea94ebf448c237061485 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs new file mode 100644 index 0000000000..d4a41ae2ea --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -0,0 +1,178 @@ +using System; +using NUnit.Framework; +using Unity.Collections; +using Unity.Multiplayer.Netcode; + +namespace Unity.Netcode.EditorTests +{ + public class BitWriterTests + { + [Test] + public unsafe void TestWritingOneBit() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBit(true); + Assert.AreEqual(0b1, *asInt); + + bitWriter.WriteBit(true); + Assert.AreEqual(0b11, *asInt); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + Assert.AreEqual(0b1011, *asInt); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(false); + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + Assert.AreEqual(0b10001011, *asInt); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + Assert.AreEqual(0b1010_10001011, *asInt); + } + + Assert.AreEqual(2, writer.Position); + Assert.AreEqual(0b1010_10001011, *asInt); + + writer.WriteByte(0b11111111); + Assert.AreEqual(0b11111111_00001010_10001011, *asInt); + } + } + + [Test] + public unsafe void TestWritingMultipleBits() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111, 1); + Assert.AreEqual(0b1, *asInt); + + bitWriter.WriteBits(0b11111111, 1); + Assert.AreEqual(0b11, *asInt); + + bitWriter.WriteBits(0b11111110, 2); + Assert.AreEqual(0b1011, *asInt); + + bitWriter.WriteBits(0b11111000, 4); + Assert.AreEqual(0b10001011, *asInt); + + bitWriter.WriteBits(0b11111010, 4); + Assert.AreEqual(0b1010_10001011, *asInt); + } + + Assert.AreEqual(2, writer.Position); + Assert.AreEqual(0b1010_10001011, *asInt); + + writer.WriteByte(0b11111111); + Assert.AreEqual(0b11111111_00001010_10001011, *asInt); + } + } + + [Test] + public unsafe void TestWritingMultipleBitsFromLongs() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111UL, 1); + Assert.AreEqual(0b1, *asInt); + + bitWriter.WriteBits(0b11111111UL, 1); + Assert.AreEqual(0b11, *asInt); + + bitWriter.WriteBits(0b11111110UL, 2); + Assert.AreEqual(0b1011, *asInt); + + bitWriter.WriteBits(0b11111000UL, 4); + Assert.AreEqual(0b10001011, *asInt); + + bitWriter.WriteBits(0b11111010UL, 4); + Assert.AreEqual(0b1010_10001011, *asInt); + } + + Assert.AreEqual(2, writer.Position); + Assert.AreEqual(0b1010_10001011, *asInt); + + writer.WriteByte(0b11111111); + Assert.AreEqual(0b11111111_00001010_10001011, *asInt); + } + } + + [Test] + public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + Assert.Throws(() => + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBit(true); + } + }); + + Assert.Throws(() => + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBit(false); + } + }); + + Assert.Throws(() => + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111, 1); + } + }); + + Assert.Throws(() => + { + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111UL, 1); + } + }); + + Assert.AreEqual(0, writer.Position); + Assert.AreEqual(0, *asInt); + + writer.WriteByteSafe(0b11111111); + Assert.AreEqual(0b11111111, *asInt); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs.meta new file mode 100644 index 0000000000..3a8da0e2cc --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fed657e0516a72f469fbf886e3e5149a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs new file mode 100644 index 0000000000..4fc34219fa --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -0,0 +1,788 @@ +using System; +using NUnit.Framework; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.EditorTests +{ + public class BytePackerTests + { + private void CheckUnsignedPackedSize64(ref FastBufferWriter writer, ulong value) + { + + if (value <= 240) + { + Assert.AreEqual(1, writer.Position); + } + else if (value <= 2287) + { + Assert.AreEqual(2, writer.Position); + } + else + { + Assert.AreEqual(BitCounter.GetUsedByteCount(value) + 1, writer.Position); + } + } + + private unsafe void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value) + { + byte* asBytes = writer.GetUnsafePtr(); + ulong readValue; + if (asBytes[0] <= 240) + { + Assert.AreEqual(asBytes[0], value); + return; + } + + if (asBytes[0] <= 248) + { + readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; + Assert.AreEqual(readValue, value); + return; + } + + var numBytes = asBytes[0] - 247; + readValue = 0; + UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + Assert.AreEqual(readValue, value); + } + + private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) + { + + if (value <= 240) + { + Assert.AreEqual(1, writer.Position); + } + else if (value <= 2287) + { + Assert.AreEqual(2, writer.Position); + } + else + { + Assert.AreEqual(BitCounter.GetUsedByteCount(value) + 1, writer.Position); + } + } + + private unsafe void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) + { + byte* asBytes = writer.GetUnsafePtr(); + ulong readValue; + if (asBytes[0] <= 240) + { + Assert.AreEqual(asBytes[0], value); + return; + } + + if (asBytes[0] <= 248) + { + readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; + Assert.AreEqual(readValue, value); + return; + } + + var numBytes = asBytes[0] - 247; + readValue = 0; + UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + Assert.AreEqual(readValue, value); + } + + private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) + { + ulong asUlong = Arithmetic.ZigZagEncode(value); + + if (asUlong <= 240) + { + Assert.AreEqual(1, writer.Position); + } + else if (asUlong <= 2287) + { + Assert.AreEqual(2, writer.Position); + } + else + { + Assert.AreEqual(BitCounter.GetUsedByteCount(asUlong) + 1, writer.Position); + } + } + + private unsafe void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) + { + byte* asBytes = writer.GetUnsafePtr(); + ulong readValue; + if (asBytes[0] <= 240) + { + Assert.AreEqual(Arithmetic.ZigZagDecode(asBytes[0]), value); + return; + } + + if (asBytes[0] <= 248) + { + readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; + Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + return; + } + + var numBytes = asBytes[0] - 247; + readValue = 0; + UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + } + + private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) + { + ulong asUlong = Arithmetic.ZigZagEncode(value); + + if (asUlong <= 240) + { + Assert.AreEqual(1, writer.Position); + } + else if (asUlong <= 2287) + { + Assert.AreEqual(2, writer.Position); + } + else + { + Assert.AreEqual(BitCounter.GetUsedByteCount(asUlong) + 1, writer.Position); + } + } + + private unsafe void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) + { + byte* asBytes = writer.GetUnsafePtr(); + ulong readValue; + if (asBytes[0] <= 240) + { + Assert.AreEqual(Arithmetic.ZigZagDecode(asBytes[0]), value); + return; + } + + if (asBytes[0] <= 248) + { + readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; + Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + return; + } + + var numBytes = asBytes[0] - 247; + readValue = 0; + UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + } + + [Test] + public void TestPacking64BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(9); + ulong value = 0; + BytePacker.WriteValuePacked(ref writer, value); + Assert.AreEqual(1, writer.Position); + + for (var i = 0; i < 64; ++i) + { + value = 1UL << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckUnsignedPackedSize64(ref writer, value); + CheckUnsignedPackedValue64(ref writer, value); + for (var j = 0; j < 8; ++j) + { + value = (1UL << i) | (1UL << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckUnsignedPackedSize64(ref writer, value); + CheckUnsignedPackedValue64(ref writer, value); + } + } + } + } + + [Test] + public void TestPacking32BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(9); + uint value = 0; + BytePacker.WriteValuePacked(ref writer, value); + Assert.AreEqual(1, writer.Position); + + for (var i = 0; i < 64; ++i) + { + value = 1U << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckUnsignedPackedSize32(ref writer, value); + CheckUnsignedPackedValue32(ref writer, value); + for (var j = 0; j < 8; ++j) + { + value = (1U << i) | (1U << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckUnsignedPackedSize32(ref writer, value); + CheckUnsignedPackedValue32(ref writer, value); + } + } + } + } + + [Test] + public void TestPacking64BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(9); + long value = 0; + BytePacker.WriteValuePacked(ref writer, value); + Assert.AreEqual(1, writer.Position); + + for (var i = 0; i < 64; ++i) + { + value = 1L << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckSignedPackedSize64(ref writer, value); + CheckSignedPackedValue64(ref writer, value); + + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, -value); + CheckSignedPackedSize64(ref writer, -value); + CheckSignedPackedValue64(ref writer, -value); + for (var j = 0; j < 8; ++j) + { + value = (1L << i) | (1L << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckSignedPackedSize64(ref writer, value); + CheckSignedPackedValue64(ref writer, value); + + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, -value); + CheckSignedPackedSize64(ref writer, -value); + CheckSignedPackedValue64(ref writer, -value); + } + } + } + } + + [Test] + public void TestPacking32BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(5); + int value = 0; + BytePacker.WriteValuePacked(ref writer, value); + Assert.AreEqual(1, writer.Position); + + for (var i = 0; i < 64; ++i) + { + value = 1 << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckSignedPackedSize32(ref writer, value); + CheckSignedPackedValue32(ref writer, value); + + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, -value); + CheckSignedPackedSize32(ref writer, -value); + CheckSignedPackedValue32(ref writer, -value); + for (var j = 0; j < 8; ++j) + { + value = (1 << i) | (1 << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, value); + CheckSignedPackedSize32(ref writer, value); + CheckSignedPackedValue32(ref writer, value); + + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValuePacked(ref writer, -value); + CheckSignedPackedSize32(ref writer, -value); + CheckSignedPackedValue32(ref writer, -value); + } + } + } + } + + private int GetByteCount61Bits(ulong value) + { + + if (value <= 0b0001_1111) + { + return 1; + } + + if (value <= 0b0001_1111_1111_1111) + { + return 2; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111) + { + return 3; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111) + { + return 4; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + return 5; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + return 6; + } + + if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) + { + return 7; + } + + return 8; + } + + private int GetByteCount30Bits(uint value) + { + + if (value <= 0b0011_1111) + { + return 1; + } + + if (value <= 0b0011_1111_1111_1111) + { + return 2; + } + + if (value <= 0b0011_1111_1111_1111_1111_1111) + { + return 3; + } + + return 4; + } + + private int GetByteCount15Bits(ushort value) + { + + if (value <= 0b0111_1111) + { + return 1; + } + + return 2; + } + + private unsafe ulong Get61BitEncodedValue(byte* data) + { + ulong returnValue = 0; + byte* ptr = ((byte*) &returnValue); + int numBytes = (data[0] & 0b111) + 1; + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + case 3: + *(ushort*) ptr = *(ushort*)data; + *(ptr+2) = *(data+2); + break; + case 4: + *(uint*) ptr = *(uint*)data; + break; + case 5: + *(uint*) ptr = *(uint*)data; + *(ptr+4) = *(data+4); + break; + case 6: + *(uint*) ptr = *(uint*)data; + *(ushort*) (ptr+4) = *(ushort*)(data+4); + break; + case 7: + *(uint*) ptr = *(uint*)data; + *(ushort*) (ptr+4) = *(ushort*)(data+4); + *(ptr+6) = *(data+6); + break; + case 8: + *(ulong*) ptr = *(ulong*)data; + break; + } + + return returnValue >> 3; + } + + private unsafe uint Get30BitEncodedValue(byte* data) + { + uint returnValue = 0; + byte* ptr = ((byte*) &returnValue); + int numBytes = (data[0] & 0b11) + 1; + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + case 3: + *(ushort*) ptr = *(ushort*)data; + *(ptr+2) = *(data+2); + break; + case 4: + *(uint*) ptr = *(uint*)data; + break; + } + + return returnValue >> 2; + } + + private unsafe ushort Get15BitEncodedValue(byte* data) + { + ushort returnValue = 0; + byte* ptr = ((byte*) &returnValue); + int numBytes = (data[0] & 0b1) + 1; + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + } + + return (ushort)(returnValue >> 1); + } + + [Test] + public unsafe void TestBitPacking61BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(8); + ulong value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b111); + Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + + for (var i = 0; i < 61; ++i) + { + value = 1UL << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(value)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + + for (var j = 0; j < 8; ++j) + { + value = (1UL << i) | (1UL << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(value)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + } + } + + Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1UL << 61); }); + } + } + + [Test] + public unsafe void TestBitPacking60BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(8); + long value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b111); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + + for (var i = 0; i < 61; ++i) + { + value = 1U << i; + ulong zzvalue = Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + + value = -value; + zzvalue = Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + + for (var j = 0; j < 8; ++j) + { + value = (1U << i) | (1U << j); + zzvalue = Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + + value = -value; + zzvalue = Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + } + } + + Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1UL << 61); }); + } + } + + [Test] + public unsafe void TestBitPacking30BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(4); + uint value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + + for (var i = 0; i < 30; ++i) + { + value = 1U << i; + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(value)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + + for (var j = 0; j < 8; ++j) + { + value = (1U << i) | (1U << j); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(value)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + } + } + + Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1U << 30); }); + } + } + + [Test] + public unsafe void TestBitPacking29BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(4); + int value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + + for (var i = 0; i < 29; ++i) + { + value = 1 << i; + uint zzvalue = (uint)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + + value = -value; + zzvalue = (uint)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + + for (var j = 0; j < 8; ++j) + { + value = (1 << i) | (1 << j); + zzvalue = (uint)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + + value = -value; + zzvalue = (uint)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + } + } + } + } + + [Test] + public unsafe void TestBitPacking15BitsUnsigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(2); + ushort value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + + for (var i = 0; i < 15; ++i) + { + value = (ushort)(1U << i); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(value)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + + for (var j = 0; j < 8; ++j) + { + value = (ushort)((1U << i) | (1U << j)); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(value)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + } + } + + Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, (ushort)(1U << 15)); }); + } + } + [Test] + public unsafe void TestBitPacking14BitsSigned() + { + FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(2); + short value = 0; + BytePacker.WriteValueBitPacked(ref writer, value); + byte* asByte = writer.GetUnsafePtr(); + Assert.AreEqual(1, writer.Position); + Assert.AreEqual(0, asByte[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + + for (var i = 0; i < 14; ++i) + { + value = (short)(1 << i); + ushort zzvalue = (ushort)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + + value = (short)-value; + zzvalue = (ushort)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + + for (var j = 0; j < 8; ++j) + { + value = (short)((1 << i) | (1 << j)); + zzvalue = (ushort)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + + value = (short)-value; + zzvalue = (ushort)Arithmetic.ZigZagEncode(value); + writer.Seek(0); + writer.Truncate(); + BytePacker.WriteValueBitPacked(ref writer, value); + Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + } + } + } + } + } +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs.meta new file mode 100644 index 0000000000..4c07179b82 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b50db056cd7443b4eb2e00b603d4c15c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs new file mode 100644 index 0000000000..e3daefee58 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -0,0 +1,2123 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.Internal; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Multiplayer.Netcode; +using UnityEngine; +using UnityEngine.SceneManagement; +using Random = System.Random; + +namespace Unity.Netcode.EditorTests +{ + public class FastBufferWriterTests + { + #region Test Types + private enum ByteEnum : byte + { + A, + B, + C + }; + private enum SByteEnum : sbyte + { + A, + B, + C + }; + private enum ShortEnum : short + { + A, + B, + C + }; + private enum UShortEnum : ushort + { + A, + B, + C + }; + private enum IntEnum : int + { + A, + B, + C + }; + private enum UIntEnum : uint + { + A, + B, + C + }; + private enum LongEnum : long + { + A, + B, + C + }; + private enum ULongEnum : ulong + { + A, + B, + C + }; + + private struct TestStruct + { + public byte a; + public short b; + public ushort c; + public int d; + public uint e; + public long f; + public ulong g; + public bool h; + public char i; + public float j; + public double k; + } + + public enum WriteType + { + WriteDirect, + WriteSafe, + WriteAsObject + } + #endregion + + #region Common Checks + private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage="") + { + Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); + writer.WriteValue((byte)0x80); + Assert.AreEqual(writeSize+1, writer.Position, failMessage); + Assert.AreEqual(writeSize+1, writer.Length, failMessage); + writer.WriteValue((byte)0xFF); + Assert.AreEqual(writeSize+2, writer.Position, failMessage); + Assert.AreEqual(writeSize+2, writer.Length, failMessage); + } + + private void VerifyCheckBytes(NativeArray underlyingArray, int writeSize, string failMessage = "") + { + Assert.AreEqual(0x80, underlyingArray[writeSize], failMessage); + Assert.AreEqual(0xFF, underlyingArray[writeSize+1], failMessage); + } + + private unsafe void VerifyBytewiseEquality(T value, NativeArray underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T: unmanaged + { + byte* asBytePointer = (byte*) &value; + for (var i = 0; i < size; ++i) + { + Assert.AreEqual(asBytePointer[i+valueOffset], underlyingArray[i+bufferOffset], failMessage); + } + } + + private unsafe void VerifyTypedEquality(T value, NativeArray underlyingArray) where T: unmanaged + { + T* checkValue = (T*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(value, *checkValue); + } + + private void VerifyPositionAndLength(ref FastBufferWriter writer, int position, string failMessage = "") + { + Assert.AreEqual(position, writer.Position, failMessage); + Assert.AreEqual(position, writer.Length, failMessage); + } + + private void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + { + NativeArray underlyingArray = writer.GetNativeArray(); + + VerifyPositionAndLength(ref writer, writeSize, failMessage); + + WriteCheckBytes(ref writer, writeSize, failMessage); + + VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, writeSize, failMessage); + + VerifyCheckBytes(underlyingArray, writeSize, failMessage); + + VerifyTypedEquality(valueToTest, underlyingArray); + } + #endregion + + #region Generic Checks + private unsafe void RunTypeTest(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + + var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; + + writer.WriteValue(valueToTest); + + CommonChecks(ref writer, valueToTest, writeSize, failMessage); + } + } + private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; + + writer.WriteValueSafe(valueToTest); + + CommonChecks(ref writer, valueToTest, writeSize, failMessage); + } + } + + private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + var failMessage = $"RunObjectTypeTest failed with type {typeof(T)} and value {valueToTest}"; + writer.WriteObject(valueToTest); + + CommonChecks(ref writer, valueToTest, writeSize, failMessage); + } + } + + private unsafe void VerifyArrayEquality(T[] value, NativeArray underlyingArray, int offset) where T: unmanaged + { + int* sizeValue = (int*)((byte*)underlyingArray.GetUnsafePtr() + offset); + Assert.AreEqual(value.Length, *sizeValue); + + fixed (T* asTPointer = value) + { + T* underlyingTArray = (T*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int) + offset); + for (var i = 0; i < value.Length; ++i) + { + Assert.AreEqual(asTPointer[i], underlyingTArray[i]); + } + } + } + + private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + + writer.WriteValue(valueToTest); + VerifyPositionAndLength(ref writer, writeSize); + + WriteCheckBytes(ref writer, writeSize); + + VerifyArrayEquality(valueToTest, underlyingArray, 0); + + VerifyCheckBytes(underlyingArray, writeSize); + } + } + + private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + + writer.WriteValueSafe(valueToTest); + VerifyPositionAndLength(ref writer, writeSize); + + WriteCheckBytes(ref writer, writeSize); + + VerifyArrayEquality(valueToTest, underlyingArray, 0); + + VerifyCheckBytes(underlyingArray, writeSize); + } + } + + private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + + writer.WriteObject(valueToTest); + VerifyPositionAndLength(ref writer, writeSize + sizeof(byte)); + + WriteCheckBytes(ref writer, writeSize + sizeof(byte)); + + VerifyArrayEquality(valueToTest, underlyingArray, sizeof(byte)); + + VerifyCheckBytes(underlyingArray, writeSize + sizeof(byte)); + } + } + #endregion + + #region Helpers + private TestStruct GetTestStruct() + { + var random = new Random(); + + var testStruct = new TestStruct + { + a = (byte) random.Next(), + b = (short) random.Next(), + c = (ushort) random.Next(), + d = (int) random.Next(), + e = (uint) random.Next(), + f = ((long) random.Next() << 32) + random.Next(), + g = ((ulong) random.Next() << 32) + (ulong) random.Next(), + h = true, + i = '\u263a', + j = (float) random.NextDouble(), + k = random.NextDouble(), + }; + + return testStruct; + } + #endregion + + #region Tests + [Test] + public void TestWritingBasicTypes( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte b = (byte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(b); + } + else + { + RunObjectTypeTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte sb = (sbyte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(sb); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(sb); + } + else + { + RunObjectTypeTest(sb); + } + } + else if (testType == typeof(short)) + { + short s = (short)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(s); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(s); + } + else + { + RunObjectTypeTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort us = (ushort)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(us); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(us); + } + else + { + RunObjectTypeTest(us); + } + } + else if (testType == typeof(int)) + { + int i = (int)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(i); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(i); + } + else + { + RunObjectTypeTest(i); + } + } + else if (testType == typeof(uint)) + { + uint ui = (uint)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ui); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(ui); + } + else + { + RunObjectTypeTest(ui); + } + } + else if (testType == typeof(long)) + { + long l = ((long)random.Next() << 32) + random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(l); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(l); + } + else + { + RunObjectTypeTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ul); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(ul); + } + else + { + RunObjectTypeTest(ul); + } + } + else if (testType == typeof(bool)) + { + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(true); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(true); + } + else + { + RunObjectTypeTest(true); + } + } + else if (testType == typeof(char)) + { + char c = 'a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(c); + } + else + { + RunObjectTypeTest(c); + } + + c = '\u263a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(c); + } + else + { + RunObjectTypeTest(c); + } + } + else if (testType == typeof(float)) + { + float f = (float)random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(f); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(f); + } + else + { + RunObjectTypeTest(f); + } + } + else if (testType == typeof(double)) + { + double d = random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(d); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(d); + } + else + { + RunObjectTypeTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum e = ByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum e = SByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum e = ShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum e = UShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum e = IntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum e = UIntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum e = LongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum e = ULongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray)) + { + var v = new Ray( + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray2D)) + { + var v = new Ray2D( + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + [Test] + public void TestWritingBasicArrays( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte[] b = { + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(b); + } + else + { + RunObjectTypeArrayTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte[] sb = { + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(sb); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(sb); + } + else + { + RunObjectTypeArrayTest(sb); + } + } + else if (testType == typeof(short)) + { + short[] s = { + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(s); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(s); + } + else + { + RunObjectTypeArrayTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort[] us = { + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(us); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(us); + } + else + { + RunObjectTypeArrayTest(us); + } + } + else if (testType == typeof(int)) + { + int[] i = { + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(i); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(i); + } + else + { + RunObjectTypeArrayTest(i); + } + } + else if (testType == typeof(uint)) + { + uint[] ui = { + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(ui); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(ui); + } + else + { + RunObjectTypeArrayTest(ui); + } + } + else if (testType == typeof(long)) + { + long[] l = { + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(l); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(l); + } + else + { + RunObjectTypeArrayTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong[] ul = { + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(ul); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(ul); + } + else + { + RunObjectTypeArrayTest(ul); + } + } + else if (testType == typeof(bool)) + { + bool[] b = { + true, + false, + true, + true, + false, + false, + true, + false, + true + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(b); + } + else + { + RunObjectTypeArrayTest(b); + } + } + else if (testType == typeof(char)) + { + char[] c = { + 'a', + '\u263a', + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(c); + } + else + { + RunObjectTypeArrayTest(c); + } + } + else if (testType == typeof(float)) + { + float[] f = { + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(f); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(f); + } + else + { + RunObjectTypeArrayTest(f); + } + } + else if (testType == typeof(double)) + { + double[] d = { + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(d); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(d); + } + else + { + RunObjectTypeArrayTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum[] e = { + ByteEnum.C, + ByteEnum.A, + ByteEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum[] e = { + SByteEnum.C, + SByteEnum.A, + SByteEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum[] e = { + ShortEnum.C, + ShortEnum.A, + ShortEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum[] e = { + UShortEnum.C, + UShortEnum.A, + UShortEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum[] e = { + IntEnum.C, + IntEnum.A, + IntEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum[] e = { + UIntEnum.C, + UIntEnum.A, + UIntEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum[] e = { + LongEnum.C, + LongEnum.A, + LongEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum[] e = { + ULongEnum.C, + ULongEnum.A, + ULongEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new[] + { + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new[] + { + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new[] + { + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new[] + { + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new[] + { + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new[] + { + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Ray)) + { + var v = new[] + { + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Ray2D)) + { + var v = new[] + { + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + [Test] + public void TestWritingStruct() + { + RunTypeTest(GetTestStruct()); + } + + [Test] + public void TestWritingStructSafe() + { + RunTypeTestSafe(GetTestStruct()); + } + + [Test] + public void TestWritingStructAsObjectWithRegisteredTypeTableSerializer() + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct) obj); + }; + try + { + RunObjectTypeTest(GetTestStruct()); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + } + } + + [Test] + public void TestWritingStructArray() + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunTypeArrayTest(arr); + } + + [Test] + public void TestWritingStructArraySafe() + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunTypeArrayTestSafe(arr); + } + + [Test] + public void TestWritingStructArrayAsObjectWithRegisteredTypeTableSerializer() + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct) obj); + }; + try + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunObjectTypeArrayTest(arr); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + } + } + + [Test] + public unsafe void TestWritingString() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest); + + VerifyPositionAndLength(ref writer, serializedValueSize); + WriteCheckBytes(ref writer, serializedValueSize); + + int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int)); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize); + } + } + + [Test] + public unsafe void TestWritingStringSafe() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteValueSafe(valueToTest); + + VerifyPositionAndLength(ref writer, serializedValueSize); + WriteCheckBytes(ref writer, serializedValueSize); + + int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int)); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize); + } + } + + [Test] + public unsafe void TestWritingStringAsObject() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteObject(valueToTest); + + VerifyPositionAndLength(ref writer, serializedValueSize + sizeof(byte)); + WriteCheckBytes(ref writer, serializedValueSize + sizeof(byte)); + + int* sizeValue = (int*) ((byte*)underlyingArray.GetUnsafePtr() + sizeof(byte)); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int) + sizeof(byte)); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize + sizeof(byte)); + } + } + + [Test] + public unsafe void TestWritingOneByteString() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest, true); + + VerifyPositionAndLength(ref writer, serializedValueSize); + WriteCheckBytes(ref writer, serializedValueSize); + + int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + byte* underlyingByteArray = (byte*) underlyingArray.GetUnsafePtr() + sizeof(int); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize); + } + } + + [Test] + public unsafe void TestWritingOneByteStringSafe() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteValueSafe(valueToTest, true); + + VerifyPositionAndLength(ref writer, serializedValueSize); + WriteCheckBytes(ref writer, serializedValueSize); + + int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) + { + byte* underlyingByteArray = (byte*) underlyingArray.GetUnsafePtr() + sizeof(int); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); + } + } + + VerifyCheckBytes(underlyingArray, serializedValueSize); + } + } + + [Test] + public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) + { + var random = new Random(); + var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count); + + var failMessage = $"TestWritingPartialValues failed with value {valueToTest}"; + VerifyPositionAndLength(ref writer, count, failMessage); + WriteCheckBytes(ref writer, count, failMessage); + VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, count, failMessage); + VerifyCheckBytes(underlyingArray, count, failMessage); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + ulong* checkValue = (ulong*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual(valueToTest & mask, *checkValue & mask, failMessage); + } + } + + [Test] + public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + { + var random = new Random(); + var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count, 2); + var failMessage = $"TestWritingPartialValuesWithOffsets failed with value {valueToTest}"; + + VerifyPositionAndLength(ref writer, count, failMessage); + WriteCheckBytes(ref writer, count, failMessage); + VerifyBytewiseEquality(valueToTest, underlyingArray, 2, 0, count, failMessage); + VerifyCheckBytes(underlyingArray, count, failMessage); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + ulong* checkValue = (ulong*) underlyingArray.GetUnsafePtr(); + Assert.AreEqual((valueToTest >> 16) & mask, *checkValue & mask); + } + } + + [Test] + public void TestToArray() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(requiredSize); + writer.WriteValue(testStruct); + var array = writer.ToArray(); + var underlyingArray = writer.GetNativeArray(); + for(var i = 0; i < array.Length; ++i) + { + Assert.AreEqual(array[i], underlyingArray[i]); + } + } + } + + [Test] + public unsafe void TestGetUnsafePtr() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(requiredSize); + writer.WriteValue(testStruct); + var ptr = writer.GetUnsafePtr(); + var underlyingArrayPtr = writer.GetNativeArray().GetUnsafePtr(); + Assert.IsTrue(underlyingArrayPtr == ptr); + + var ptrAtPosition = writer.GetUnsafePtrAtCurrentPosition(); + Assert.IsTrue((byte*)underlyingArrayPtr + writer.Position == ptrAtPosition); + } + } + + [Test] + public void TestThrowingIfBoundsCheckingSkipped() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + Assert.Throws(() => { writer.WriteByte(1); }); + var bytes = new byte[] {0, 1, 2}; + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); + int i = 1; + Assert.Throws(() => { writer.WriteValue(i); }); + Assert.Throws(() => { writer.WriteValue(bytes); }); + Assert.Throws(() => { writer.WriteValue(""); }); + + writer.VerifyCanWrite(sizeof(int) - 1); + Assert.Throws(() => { writer.WriteValue(i); }); + writer.WriteByte(1); + writer.WriteByte(2); + writer.WriteByte(3); + Assert.Throws(() => { writer.WriteByte(4); }); + } + } + + [Test] + public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(100); + var bytes = new byte[] {0, 1, 2}; + int i = 1; + using (var context = writer.EnterBitwiseContext()) + { + Assert.Throws(() => { writer.WriteByte(1); }); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); + Assert.Throws(() => { writer.WriteValue(i); }); + Assert.Throws(() => { writer.WriteValue(bytes); }); + Assert.Throws(() => { writer.WriteValue(""); }); + + Assert.Throws(() => { writer.WriteByteSafe(1); }); + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); + Assert.Throws(() => { writer.WriteValueSafe(i); }); + Assert.Throws(() => { writer.WriteValueSafe(bytes); }); + Assert.Throws(() => { writer.WriteValueSafe(""); }); + } + writer.WriteByte(1); + writer.WriteBytes(bytes, bytes.Length); + writer.WriteValue(i); + writer.WriteValue(bytes); + writer.WriteValue(""); + + writer.WriteByteSafe(1); + writer.WriteBytesSafe(bytes, bytes.Length); + writer.WriteValueSafe(i); + writer.WriteValueSafe(bytes); + writer.WriteValueSafe(""); + } + } + + [Test] + public void TestVerifyCanWriteIsRelativeToPositionAndNotAllowedWritePosition() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.VerifyCanWrite(100); + writer.WriteByte(1); + writer.VerifyCanWrite(1); + writer.WriteByte(1); + Assert.Throws(() => { writer.WriteByte(1); }); + } + } + + [Test] + public void TestSeeking() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.Seek(5); + writer.WriteByteSafe(0); + Assert.AreEqual(writer.Position, 6); + Assert.AreEqual(writer.Length, 6); + + writer.Seek(0); + writer.WriteByteSafe(1); + Assert.AreEqual(writer.Position, 1); + Assert.AreEqual(writer.Length, 6); + + writer.Seek(10); + Assert.AreEqual(writer.Position, 10); + Assert.AreEqual(writer.Length, 10); + + writer.Seek(2); + writer.WriteByteSafe(2); + + writer.Seek(1); + writer.WriteByteSafe(3); + + writer.Seek(4); + writer.WriteByteSafe(4); + + writer.Seek(3); + writer.WriteByteSafe(5); + + Assert.AreEqual(writer.Position, 4); + Assert.AreEqual(writer.Length, 10); + + var expected = new byte[] {1, 3, 2, 5, 4, 0}; + var underlyingArray = writer.GetNativeArray(); + for (var i = 0; i < expected.Length; ++i) + { + Assert.AreEqual(expected[i], underlyingArray[i]); + } + } + } + + [Test] + public void TestTruncate() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.Seek(10); + Assert.AreEqual(writer.Position, 10); + Assert.AreEqual(writer.Length, 10); + + writer.Seek(5); + Assert.AreEqual(writer.Position, 5); + Assert.AreEqual(writer.Length, 10); + + writer.Truncate(8); + Assert.AreEqual(writer.Position, 5); + Assert.AreEqual(writer.Length, 8); + + writer.Truncate(); + Assert.AreEqual(writer.Position, 5); + Assert.AreEqual(writer.Length, 5); + } + } + + [Test] + public void TestCapacity() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + Assert.AreEqual(100, writer.Capacity); + writer.Dispose(); + } + + [Test] + public void TestGrowth() + { + var writer = new FastBufferWriter(150, Allocator.Temp); + var growingWriter = new FastBufferWriter(150, Allocator.Temp, 500); + Assert.AreEqual(150, writer.Capacity); + using (writer) + using (growingWriter) + { + var testStruct = GetTestStruct(); + writer.VerifyCanWriteValue(testStruct); + writer.WriteValue(testStruct); + growingWriter.VerifyCanWriteValue(testStruct); + growingWriter.WriteValue(testStruct); + + // Seek to exactly where the write would cross the buffer boundary + writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + + // First writer isn't allowed to grow because it didn't specify a maxSize + Assert.IsFalse(writer.VerifyCanWriteValue(testStruct)); + Assert.Throws(() => writer.WriteValue(testStruct)); + + // Second writer is allowed to grow + Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); + + // First writer shouldn't have grown + Assert.AreEqual(150, writer.Capacity); + Assert.AreEqual(150, writer.GetNativeArray().Length); + // First growth doubles the size + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(300, growingWriter.GetNativeArray().Length); + growingWriter.WriteValue(testStruct); + + // Write right up to the very end of the buffer, verify it doesn't grow + growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); + Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(300, growingWriter.GetNativeArray().Length); + growingWriter.WriteValue(testStruct); + + // Go to the end of the buffer and grow again + growingWriter.Seek(300); + Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); + growingWriter.WriteValue(testStruct); + + // Second growth caps it at maxSize + Assert.AreEqual(500, growingWriter.Capacity); + Assert.AreEqual(500, growingWriter.GetNativeArray().Length); + + VerifyBytewiseEquality(testStruct, writer.GetNativeArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + // Verify the growth properly copied the existing data + VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 150-FastBufferWriter.GetWriteSize(testStruct)+1, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 300-FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); + } + } + + [Test] + public void TestNetworkBehavior() + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); + + writer.WriteValue(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, sizeof(ulong), sizeof(ushort)); + } + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + [Test] + public void TestNetworkObject() + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); + + writer.WriteValue(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + } + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + [Test] + public void TestGameObject() + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); + + writer.WriteValue(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + } + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs.meta new file mode 100644 index 0000000000..b549311462 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1cef42b60935e29469ed1404fb30ba2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef b/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef index 35dcb1ce8d..2888eed780 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef @@ -3,12 +3,23 @@ "rootNamespace": "Unity.Netcode.EditorTests", "references": [ "Unity.Netcode.Runtime", - "Unity.Netcode.Editor" - ], - "optionalUnityReferences": [ - "TestAssemblies" + "Unity.Netcode.Editor", + "UnityEngine.TestRunner", + "UnityEditor.TestRunner" ], "includePlatforms": [ "Editor" - ] + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [ + "nunit.framework.dll" + ], + "autoReferenced": false, + "defineConstraints": [ + "UNITY_INCLUDE_TESTS" + ], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/testproject/Assets/StreamingAssets/BuildInfo.json b/testproject/Assets/StreamingAssets/BuildInfo.json new file mode 100644 index 0000000000..14f9fcbcd2 --- /dev/null +++ b/testproject/Assets/StreamingAssets/BuildInfo.json @@ -0,0 +1 @@ +{"BuildPath":"C:\\Users\\jaedyn.draper\\repos\\mlapi\\testproject\\Builds\\MultiprocessTests\\MultiprocessTestPlayer","IsDebug":false} \ No newline at end of file diff --git a/testproject/Assets/StreamingAssets/BuildInfo.json.meta b/testproject/Assets/StreamingAssets/BuildInfo.json.meta new file mode 100644 index 0000000000..5af63d794e --- /dev/null +++ b/testproject/Assets/StreamingAssets/BuildInfo.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1c16680acd9943944a6d4ae05644bfd5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From c36b3af5d449ec6b421dc0cc79a5593c659594f0 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 16 Aug 2021 19:19:35 -0500 Subject: [PATCH 02/29] A few additional tests to cover the last missed cases I can think of (aside from INetworkSerializable, which isn't ready to be tested yet due to lack of BufferSerializer) --- .../Runtime/Serialization/FastBufferWriter.cs | 13 +- .../Serialization/FastBufferWriterTests.cs | 197 ++++++++++++------ 2 files changed, 139 insertions(+), 71 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index c6b88f3b60..f77d6eba14 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -295,7 +295,7 @@ public void WriteObject(object value, bool isNullable = false) throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } - WriteValueSafe(networkObject.NetworkObjectId); + WriteValueSafe(networkObject); return; } if (value is NetworkObject) @@ -305,7 +305,7 @@ public void WriteObject(object value, bool isNullable = false) throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); } - WriteValueSafe(((NetworkObject)value).NetworkObjectId); + WriteValueSafe((NetworkObject)value); return; } if (value is NetworkBehaviour) @@ -314,13 +314,8 @@ public void WriteObject(object value, bool isNullable = false) { throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); } - - if (!VerifyCanWrite(sizeof(ulong) * 2)) - { - throw new OverflowException("Writing past the end of the buffer"); - } - WriteValue(((NetworkBehaviour)value).NetworkObjectId); - WriteValue(((NetworkBehaviour)value).NetworkBehaviourId); + + WriteValueSafe((NetworkBehaviour)value); return; } if (value is INetworkSerializable) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index e3daefee58..1685f70d66 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -254,6 +254,7 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + // Extra byte for WriteObject adding isNull flag FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); using(writer) { @@ -262,6 +263,7 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); + Assert.AreEqual(0, writer.GetNativeArray()[0]); VerifyPositionAndLength(ref writer, writeSize + sizeof(byte)); WriteCheckBytes(ref writer, writeSize + sizeof(byte)); @@ -1994,8 +1996,9 @@ public void TestGrowth() } } - [Test] - public void TestNetworkBehavior() + private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, + NetworkObject networkObject); + private void RunGameObjectTest(GameObjectTestDelegate testCode) { var obj = new GameObject("Object"); var networkBehaviour = obj.AddComponent(); @@ -2016,106 +2019,176 @@ public void TestNetworkBehavior() networkManager.StartHost(); try + { + testCode(obj, networkBehaviour, networkObject); + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + [Test] + public void TestNetworkBehavior() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => { var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); - + writer.WriteValue(networkBehaviour); - + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, sizeof(ulong), sizeof(ushort)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + sizeof(ulong), sizeof(ushort)); } - } - finally - { - GameObject.DestroyImmediate(obj); - networkManager.StopHost(); - } + }); } [Test] public void TestNetworkObject() { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkObject.NetworkManagerOwner = networkManager; - - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, - // Set transport - NetworkTransport = obj.AddComponent() - }; - - networkManager.StartHost(); - - try + RunGameObjectTest((obj, networkBehaviour, networkObject) => { var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); - + writer.WriteValue(networkObject); - + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); } - } - finally - { - GameObject.DestroyImmediate(obj); - networkManager.StopHost(); - } + }); } [Test] public void TestGameObject() { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkObject.NetworkManagerOwner = networkManager; + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() + writer.WriteValue(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + } + }); + } + + [Test] + public void TestNetworkBehaviorSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, - // Set transport - NetworkTransport = obj.AddComponent() - }; + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(networkBehaviour); - networkManager.StartHost(); + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + sizeof(ulong), sizeof(ushort)); + } + }); + } - try + [Test] + public void TestNetworkObjectSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + } + }); + } + + [Test] + public void TestGameObjectSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => { var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); - - writer.WriteValue(obj); - + writer.WriteValueSafe(obj); + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); } - } - finally + }); + } + + [Test] + public void TestNetworkBehaviorAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => { - GameObject.DestroyImmediate(obj); - networkManager.StopHost(); - } + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); + Assert.AreEqual(0, writer.GetNativeArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + sizeof(ulong)+1, sizeof(ushort)); + } + }); + } + + [Test] + public void TestNetworkObjectAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); + Assert.AreEqual(0, writer.GetNativeArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + } + }); + } + + [Test] + public void TestGameObjectAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); + Assert.AreEqual(0, writer.GetNativeArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + } + }); } #endregion From 5ac5ef984308bb4c4d7d0c2affa0742ab4394470 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 18 Aug 2021 17:05:41 -0500 Subject: [PATCH 03/29] FastBufferReader + tests --- .../Runtime/Serialization/BitReader.cs | 216 ++ .../Runtime/Serialization/BitReader.cs.meta | 11 + .../Runtime/Serialization/BitWriter.cs | 55 +- .../Runtime/Serialization/BytePacker.cs | 86 +- .../Runtime/Serialization/ByteUnpacker.cs | 624 +++++ .../Serialization/ByteUnpacker.cs.meta | 11 + .../Runtime/Serialization/FastBufferReader.cs | 836 +++++++ .../Serialization/FastBufferReader.cs.meta | 11 + .../Runtime/Serialization/FastBufferWriter.cs | 152 +- .../Serialization/SerializationTypeTable.cs | 338 +++ .../Editor/Serialization/BitReaderTests.cs | 325 +++ .../Serialization/BitReaderTests.cs.meta | 11 + .../Editor/Serialization/BitWriterTests.cs | 129 + .../Editor/Serialization/BytePackerTests.cs | 320 +-- .../Serialization/FastBufferReaderTests.cs | 2137 +++++++++++++++++ .../FastBufferReaderTests.cs.meta | 11 + .../Serialization/FastBufferWriterTests.cs | 11 +- 17 files changed, 4991 insertions(+), 293 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs new file mode 100644 index 0000000000..ef383d0adf --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -0,0 +1,216 @@ +using System; +using System.Runtime.CompilerServices; +using Mono.Cecil; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Netcode; + +namespace Unity.Multiplayer.Netcode +{ + public ref struct BitReader + { + private unsafe FastBufferReader.InternalData* m_InternalData; + private unsafe byte* m_BufferPointer; + private int m_Position; + private const int BITS_PER_BYTE = 8; + + + internal unsafe BitReader(ref FastBufferReader.InternalData internalData) + { + fixed (FastBufferReader.InternalData* internalDataPtr = &internalData) + { + m_InternalData = internalDataPtr; + } + + m_BufferPointer = internalData.BufferPointer + internalData.Position; + m_Position = internalData.Position; + m_InternalData->BitPosition = 0; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData->AllowedBitwiseReadMark = (m_InternalData->AllowedReadMark - m_InternalData->Position) * BITS_PER_BYTE; +#endif + } + + public unsafe void Dispose() + { + var bytesWritten = m_InternalData->BitPosition >> 3; + if (!m_InternalData->BitAligned()) + { + // Accounting for the partial read + ++bytesWritten; + } + + m_InternalData->CommitBitwiseReads(bytesWritten); + } + + /// + /// Read a certain amount of bits from the stream. + /// + /// Value to store bits into. + /// Amount of bits to read + public unsafe void ReadBits(out ulong value, int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (bitCount > 64) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits from a 64-bit value!"); + } + + if (bitCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); + } + + int checkPos = (m_InternalData->BitPosition + bitCount); + if (checkPos > m_InternalData->AllowedBitwiseReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + } +#endif + ulong val = 0; + + int wholeBytes = bitCount / BITS_PER_BYTE; + byte* asBytes = (byte*) &val; + if (m_InternalData->BitAligned()) + { + if (wholeBytes != 0) + { + ReadPartialValue(out val, wholeBytes); + } + } + else + { + for (var i = 0; i < wholeBytes; ++i) + { + ReadMisaligned(out asBytes[i]); + } + } + + val |= (ulong)ReadByteBits(bitCount & 7) << (bitCount & ~7); + value = val; + } + + /// + /// Read bits from stream. + /// + /// Value to store bits into. + /// Amount of bits to read. + public unsafe void ReadBits(out byte value, int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + int checkPos = (m_InternalData->BitPosition + bitCount); + if (checkPos > m_InternalData->AllowedBitwiseReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + } +#endif + value = ReadByteBits(bitCount); + } + + /// + /// Read a single bit from the buffer + /// + /// Out value of the bit. True represents 1, False represents 0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBit(out bool bit) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + int checkPos = (m_InternalData->BitPosition + 1); + if (checkPos > m_InternalData->AllowedBitwiseReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + } +#endif + + int offset = m_InternalData->BitPosition & 7; + int pos = m_InternalData->BitPosition >> 3; + bit = (m_BufferPointer[pos] & (1 << offset)) != 0; + ++m_InternalData->BitPosition; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + { + // Switch statement to read small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + T val = new T(); + byte* ptr = ((byte*) &val) + offsetBytes; + byte* bufferPointer = m_BufferPointer + m_Position; + switch (bytesToRead) + { + case 1: + ptr[0] = *bufferPointer; + break; + case 2: + *(ushort*) ptr = *(ushort*)bufferPointer; + break; + case 3: + *(ushort*) ptr = *(ushort*)bufferPointer; + *(ptr+2) = *(bufferPointer+2); + break; + case 4: + *(uint*) ptr = *(uint*)bufferPointer; + break; + case 5: + *(uint*) ptr = *(uint*)bufferPointer; + *(ptr+4) = *(bufferPointer+4); + break; + case 6: + *(uint*) ptr = *(uint*)bufferPointer; + *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); + break; + case 7: + *(uint*) ptr = *(uint*)ptr; + *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); + *(ptr+6) = *(bufferPointer+6); + break; + case 8: + *(ulong*) ptr = *(ulong*)bufferPointer; + break; + default: + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); + break; + } + + m_InternalData->BitPosition += bytesToRead * BITS_PER_BYTE; + value = val; + } + + /// + /// Read a certain amount of bits from the stream. + /// + /// How many bits to read. Minimum 0, maximum 64. + /// The bits that were read + private byte ReadByteBits(int bitCount) + { + if (bitCount > 8) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 8 bits into an 8-bit value!"); + } + + if (bitCount < 0) + { + throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); + } + + int result = 0; + var convert = new ByteBool(); + for (int i = 0; i < bitCount; ++i) + { + ReadBit(out bool bit); + result |= convert.Collapse(bit) << i; + } + + return (byte)result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe void ReadMisaligned(out byte value) + { + int off = (int)(m_InternalData->BitPosition & 7); + int pos = m_InternalData->BitPosition >> 3; + int shift1 = 8 - off; + + value = (byte)((m_BufferPointer[(int)pos] >> shift1) | (m_BufferPointer[(int)(m_InternalData->BitPosition += 8) >> 3] << shift1)); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs.meta new file mode 100644 index 0000000000..9b0d159683 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 72e2d94a96ca96a4fb2921df9adc2fdf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 8b28d3d80d..f60fabc26d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -10,13 +10,8 @@ public ref struct BitWriter private unsafe FastBufferWriter.InternalData* m_InternalData; private unsafe byte* m_BufferPointer; private int m_Position; - private int m_BitPosition; private const int BITS_PER_BYTE = 8; - /// - /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. - /// - public bool BitAligned { get => (m_BitPosition & 7) == 0; } internal unsafe BitWriter(ref FastBufferWriter.InternalData internalData) { @@ -27,13 +22,16 @@ internal unsafe BitWriter(ref FastBufferWriter.InternalData internalData) m_BufferPointer = internalData.BufferPointer + internalData.Position; m_Position = internalData.Position; - m_BitPosition = 0; + m_InternalData->BitPosition = 0; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData->AllowedBitwiseWriteMark = (m_InternalData->AllowedWriteMark - m_InternalData->Position) * BITS_PER_BYTE; +#endif } public unsafe void Dispose() { - var bytesWritten = m_BitPosition >> 3; - if (!BitAligned) + var bytesWritten = m_InternalData->BitPosition >> 3; + if (!m_InternalData->BitAligned()) { // Accounting for the partial write ++bytesWritten; @@ -60,18 +58,21 @@ public unsafe void WriteBits(ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); } - int checkPos = (m_BitPosition + bitCount) >> 3; - if (checkPos > m_InternalData->AllowedWriteMark) + int checkPos = (m_InternalData->BitPosition + bitCount); + if (checkPos > m_InternalData->AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &value; - if (BitAligned) + if (m_InternalData->BitAligned()) { - WritePartialValue(value, wholeBytes); + if (wholeBytes != 0) + { + WritePartialValue(value, wholeBytes); + } } else { @@ -95,10 +96,10 @@ public unsafe void WriteBits(ulong value, int bitCount) public unsafe void WriteBits(byte value, int bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_BitPosition + bitCount) >> 3; - if (checkPos >= m_InternalData->AllowedWriteMark) + int checkPos = (m_InternalData->BitPosition + bitCount); + if (checkPos > m_InternalData->AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif @@ -112,19 +113,20 @@ public unsafe void WriteBits(byte value, int bitCount) /// Write a single bit to the buffer /// /// Value of the bit. True represents 1, False represents 0 + [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBit(bool bit) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_BitPosition + 1) >> 3; - if (checkPos >= m_InternalData->AllowedWriteMark) + int checkPos = (m_InternalData->BitPosition + 1); + if (checkPos > m_InternalData->AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif - int offset = m_BitPosition & 7; - int pos = m_BitPosition >> 3; - ++m_BitPosition; + int offset = m_InternalData->BitPosition & 7; + int pos = m_InternalData->BitPosition >> 3; + ++m_InternalData->BitPosition; m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset))); } @@ -173,18 +175,19 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy break; } - m_BitPosition += bytesToWrite * BITS_PER_BYTE; + m_InternalData->BitPosition += bytesToWrite * BITS_PER_BYTE; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void WriteMisaligned(byte value) { - int off = (int)(m_BitPosition & 7); - int pos = m_BitPosition >> 3; + int off = (int)(m_InternalData->BitPosition & 7); + int pos = m_InternalData->BitPosition >> 3; int shift1 = 8 - off; m_BufferPointer[pos + 1] = (byte)((m_BufferPointer[pos + 1] & (0xFF << off)) | (value >> shift1)); m_BufferPointer[pos] = (byte)((m_BufferPointer[pos] & (0xFF >> shift1)) | (value << off)); - m_BitPosition += 8; + m_InternalData->BitPosition += 8; } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index bf458eee20..f23531c73a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -138,7 +138,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteValuePacked(T value) where T: unmanaged => writer.WriteValue(value); + public void WriteValuePacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteValuePacked(ref FastBufferWriter writer, ref TEnum value) where TEnum : unmanaged, Enum @@ -189,7 +189,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByte(value); + public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByteSafe(value); /// /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. @@ -199,7 +199,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteValuePacked(ref FastBufferWriter writer, bool value) => writer.WriteValue(value); + public static void WriteValuePacked(ref FastBufferWriter writer, bool value) => writer.WriteValueSafe(value); /// @@ -387,7 +387,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteValueBitPacked(T value) where T: unmanaged => writer.WriteValue(value); + public void WriteValueBitPacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort) Arithmetic.ZigZagEncode(value)); @@ -400,10 +400,18 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value if (value <= 0b0111_1111) { + if (!writer.VerifyCanWriteInternal(1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(value << 1)); return; } + if (!writer.VerifyCanWriteInternal(2)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((ushort)((value << 1) | 0b1)); } @@ -418,22 +426,38 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) if (value <= 0b0011_1111) { + if (!writer.VerifyCanWriteInternal(1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(value << 2)); return; } if (value <= 0b0011_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(2)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((ushort)((value << 2) | 0b01)); return; } if (value <= 0b0011_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(3)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue(((value << 2) | 0b10), 3); return; } + if (!writer.VerifyCanWriteInternal(4)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue(((value << 2) | 0b11)); } @@ -448,46 +472,78 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) if (value <= 0b0001_1111) { + if (!writer.VerifyCanWriteInternal(1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(value << 3)); return; } if (value <= 0b0001_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(2)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((ushort)((value << 3) | 0b001)); return; } if (value <= 0b0001_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(3)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue((value << 3) | 0b010, 3); return; } if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(4)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((uint)((value << 3) | 0b011)); return; } if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(5)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue((value << 3) | 0b100, 5); return; } if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(6)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue((value << 3) | 0b101, 6); return; } if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) { + if (!writer.VerifyCanWriteInternal(7)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WritePartialValue((value << 3) | 0b110, 7); return; } + if (!writer.VerifyCanWriteInternal(8)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteValue((value << 3) | 0b111); } #endif @@ -498,16 +554,21 @@ private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) { if (value <= 240) { - writer.WriteByte((byte)value); + writer.WriteByteSafe((byte)value); return; } if (value <= 2287) { - writer.WriteByte((byte)(((value - 240) >> 8) + 241)); - writer.WriteByte((byte)(value - 240)); + writer.WriteByteSafe((byte)(((value - 240) >> 8) + 241)); + writer.WriteByteSafe((byte)(value - 240)); return; } var writeBytes = BitCounter.GetUsedByteCount(value); + + if (!writer.VerifyCanWriteInternal(writeBytes+1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); } @@ -519,16 +580,21 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) { if (value <= 240) { - writer.WriteByte((byte)value); + writer.WriteByteSafe((byte)value); return; } if (value <= 2287) { - writer.WriteByte((byte)(((value - 240) >> 8) + 241)); - writer.WriteByte((byte)(value - 240)); + writer.WriteByteSafe((byte)(((value - 240) >> 8) + 241)); + writer.WriteByteSafe((byte)(value - 240)); return; } var writeBytes = BitCounter.GetUsedByteCount(value); + + if (!writer.VerifyCanWriteInternal(writeBytes+1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs new file mode 100644 index 0000000000..f63a843954 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -0,0 +1,624 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class ByteUnpacker + { + #region Managed TypePacking + /// + /// Writes a boxed object in a packed format + /// Named differently from other ReadValuePacked methods to avoid accidental boxing + /// + /// The object to write + public static void ReadObjectPacked(ref FastBufferReader reader, out object value, Type type, bool isNullable = false) + { +#if UNITY_NETCODE_DEBUG_NO_PACKING + reader.ReadObject(out value, type, isNullable); + return; +#endif + if (isNullable || type.IsNullable()) + { + reader.ReadValueSafe(out bool isNull); + + if (isNull) + { + value = null; + return; + } + } + + var hasDeserializer = SerializationTypeTable.DeserializersPacked.TryGetValue(type, out var deserializer); + if (hasDeserializer) + { + deserializer(ref reader, out value); + return; + } + + if (type.IsArray && type.HasElementType) + { + ReadValuePacked(ref reader, out int length); + + var arr = Array.CreateInstance(type.GetElementType(), length); + + for (int i = 0; i < length; i++) + { + ReadObjectPacked(ref reader, out object item, type.GetElementType()); + arr.SetValue(item, i); + } + + value = arr; + return; + } + + if (type.IsEnum) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + ReadValuePacked(ref reader, out byte boolVal); + value = Enum.ToObject(type, boolVal != 0); + return; + case TypeCode.Char: + ReadValuePacked(ref reader, out char charVal); + value = Enum.ToObject(type, charVal); + return; + case TypeCode.SByte: + ReadValuePacked(ref reader, out byte sbyteVal); + value = Enum.ToObject(type, sbyteVal); + return; + case TypeCode.Byte: + ReadValuePacked(ref reader, out byte byteVal); + value = Enum.ToObject(type, byteVal); + return; + case TypeCode.Int16: + ReadValuePacked(ref reader, out short shortVal); + value = Enum.ToObject(type, shortVal); + return; + case TypeCode.UInt16: + ReadValuePacked(ref reader, out ushort ushortVal); + value = Enum.ToObject(type, ushortVal); + return; + case TypeCode.Int32: + ReadValuePacked(ref reader, out int intVal); + value = Enum.ToObject(type, intVal); + return; + case TypeCode.UInt32: + ReadValuePacked(ref reader, out uint uintVal); + value = Enum.ToObject(type, uintVal); + return; + case TypeCode.Int64: + ReadValuePacked(ref reader, out long longVal); + value = Enum.ToObject(type, longVal); + return; + case TypeCode.UInt64: + ReadValuePacked(ref reader, out ulong ulongVal); + value = Enum.ToObject(type, ulongVal); + return; + } + } + + if (type == typeof(GameObject)) + { + reader.ReadValueSafe(out GameObject go); + value = go; + return; + } + + if (type == typeof(NetworkObject)) + { + reader.ReadValueSafe(out NetworkObject no); + value = no; + return; + } + + if (typeof(NetworkBehaviour).IsAssignableFrom(type)) + { + reader.ReadValueSafe(out NetworkBehaviour nb); + value = nb; + return; + } + /*if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + }*/ + + throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); + } + #endregion + + #region Unmanaged Type Packing + +#if UNITY_NETCODE_DEBUG_NO_PACKING + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ReadValuePacked(ref FastBufferReader reader, out T value) where T: unmanaged => reader.ReadValueSafe(out value); +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void ReadValuePacked(ref FastBufferReader reader, out TEnum value) where TEnum : unmanaged, Enum + { + switch (sizeof(TEnum)) + { + case sizeof(int): + ReadValuePacked(ref reader, out int asInt); + value = *(TEnum*)&asInt; + break; + case sizeof(byte): + ReadValuePacked(ref reader, out byte asByte); + value = *(TEnum*)&asByte; + break; + case sizeof(short): + ReadValuePacked(ref reader, out short asShort); + value = *(TEnum*)&asShort; + break; + case sizeof(long): + ReadValuePacked(ref reader, out long asLong); + value = *(TEnum*)&asLong; + break; + default: + throw new InvalidOperationException("Enum is a size that cannot exist?!"); + } + } + + /// + /// Write single-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out float value) + { + ReadUInt32Packed(ref reader, out uint asUInt); + value = ToSingle(asUInt); + } + + /// + /// Write double-precision floating point value to the stream as a varint + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out double value) + { + ReadUInt64Packed(ref reader, out ulong asULong); + value = ToDouble(asULong); + } + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out byte value) => reader.ReadByte(out value); + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out bool value) => reader.ReadValue(out value); + + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out short value) + { + ReadUInt32Packed(ref reader, out uint readValue); + value = (short)Arithmetic.ZigZagDecode(readValue); + } + + /// + /// Write an unsigned short (UInt16) as a varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out ushort value) + { + ReadUInt32Packed(ref reader, out uint readValue); + value = (ushort)readValue; + } + + /// + /// Write a two-byte character as a varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out char c) + { + ReadUInt32Packed(ref reader, out uint readValue); + c = (char)readValue; + } + + /// + /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out int value) + { + ReadUInt32Packed(ref reader, out uint readValue); + value = (int)Arithmetic.ZigZagDecode(readValue); + } + + /// + /// Write an unsigned int (UInt32) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out uint value) => ReadUInt32Packed(ref reader, out value); + + /// + /// Write an unsigned long (UInt64) to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out ulong value) => ReadUInt64Packed(ref reader, out value); + + /// + /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out long value) + { + ReadUInt64Packed(ref reader, out ulong readValue); + value = Arithmetic.ZigZagDecode(readValue); + } + + /// + /// Convenience method that writes two packed Vector3 from the ray to the stream + /// + /// Ray to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Ray ray) + { + ReadValuePacked(ref reader, out Vector3 origin); + ReadValuePacked(ref reader, out Vector3 direction); + ray = new Ray(origin, direction); + } + + /// + /// Convenience method that writes two packed Vector2 from the ray to the stream + /// + /// Ray2D to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Ray2D ray2d) + { + ReadValuePacked(ref reader, out Vector2 origin); + ReadValuePacked(ref reader, out Vector2 direction); + ray2d = new Ray2D(origin, direction); + } + + /// + /// Convenience method that writes four varint floats from the color to the stream + /// + /// Color to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Color color) + { + color = new Color(); + ReadValuePacked(ref reader, out color.r); + ReadValuePacked(ref reader, out color.g); + ReadValuePacked(ref reader, out color.b); + ReadValuePacked(ref reader, out color.a); + } + + /// + /// Convenience method that writes four varint floats from the color to the stream + /// + /// Color to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Color32 color) + { + color = new Color32(); + ReadValuePacked(ref reader, out color.r); + ReadValuePacked(ref reader, out color.g); + ReadValuePacked(ref reader, out color.b); + ReadValuePacked(ref reader, out color.a); + } + + /// + /// Convenience method that writes two varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Vector2 vector2) + { + vector2 = new Vector2(); + ReadValuePacked(ref reader, out vector2.x); + ReadValuePacked(ref reader, out vector2.y); + } + + /// + /// Convenience method that writes three varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Vector3 vector3) + { + vector3 = new Vector3(); + ReadValuePacked(ref reader, out vector3.x); + ReadValuePacked(ref reader, out vector3.y); + ReadValuePacked(ref reader, out vector3.z); + } + + /// + /// Convenience method that writes four varint floats from the vector to the stream + /// + /// Vector to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Vector4 vector4) + { + vector4 = new Vector4(); + ReadValuePacked(ref reader, out vector4.x); + ReadValuePacked(ref reader, out vector4.y); + ReadValuePacked(ref reader, out vector4.z); + ReadValuePacked(ref reader, out vector4.w); + } + + /// + /// Writes the rotation to the stream. + /// + /// Rotation to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out Quaternion rotation) + { + rotation = new Quaternion(); + ReadValuePacked(ref reader, out rotation.x); + ReadValuePacked(ref reader, out rotation.y); + ReadValuePacked(ref reader, out rotation.z); + + // numerical precision issues can make the remainder very slightly negative. + // In this case, use 0 for w as, otherwise, w would be NaN. + float remainder = 1f - Mathf.Pow(rotation.x, 2) - Mathf.Pow(rotation.y, 2) - Mathf.Pow(rotation.z, 2); + rotation.w = (remainder > 0f) ? Mathf.Sqrt(remainder) : 0.0f; + } + + /// + /// Writes a string in a packed format + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void ReadValuePacked(ref FastBufferReader reader, out string s) + { + ReadValuePacked(ref reader, out uint length); + s = "".PadRight((int)length); + int target = s.Length; + fixed (char* c = s) + { + for (int i = 0; i < target; ++i) + { + ReadValuePacked(ref reader, out c[i]); + } + } + } +#endif + #endregion + + #region Bit Packing + +#if UNITY_NETCODE_DEBUG_NO_PACKING + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ReadValueBitPacked(ref FastBufferReader reader, T value) where T: unmanaged => reader.ReadValueSafe(out value); +#else + public static void ReadValueBitPacked(ref FastBufferReader reader, out short value) + { + ReadValueBitPacked(ref reader, out ushort readValue); + value = (short)Arithmetic.ZigZagDecode(readValue); + } + + public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ushort value) + { + ushort returnValue = 0; + byte* ptr = ((byte*) &returnValue); + byte* data = reader.GetUnsafePtrAtCurrentPosition(); + int numBytes = (data[0] & 0b1) + 1; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.MarkBytesRead(numBytes); + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + default: + throw new InvalidOperationException("Could not read bit-packed value: impossible byte count"); + } + + value = (ushort)(returnValue >> 1); + } + + public static void ReadValueBitPacked(ref FastBufferReader reader, out int value) + { + ReadValueBitPacked(ref reader, out uint readValue); + value = (int)Arithmetic.ZigZagDecode(readValue); + } + public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out uint value) + { + uint returnValue = 0; + byte* ptr = ((byte*) &returnValue); + byte* data = reader.GetUnsafePtrAtCurrentPosition(); + int numBytes = (data[0] & 0b11) + 1; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.MarkBytesRead(numBytes); + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + case 3: + *(ushort*) ptr = *(ushort*)data; + *(ptr+2) = *(data+2); + break; + case 4: + *(uint*) ptr = *(uint*)data; + break; + } + + value = returnValue >> 2; + } + + public static void ReadValueBitPacked(ref FastBufferReader reader, out long value) + { + ReadValueBitPacked(ref reader, out ulong readValue); + value = Arithmetic.ZigZagDecode(readValue); + } + public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ulong value) + { + ulong returnValue = 0; + byte* ptr = ((byte*) &returnValue); + byte* data = reader.GetUnsafePtrAtCurrentPosition(); + int numBytes = (data[0] & 0b111) + 1; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.MarkBytesRead(numBytes); + switch (numBytes) + { + case 1: + *ptr = *data; + break; + case 2: + *(ushort*) ptr = *(ushort*)data; + break; + case 3: + *(ushort*) ptr = *(ushort*)data; + *(ptr+2) = *(data+2); + break; + case 4: + *(uint*) ptr = *(uint*)data; + break; + case 5: + *(uint*) ptr = *(uint*)data; + *(ptr+4) = *(data+4); + break; + case 6: + *(uint*) ptr = *(uint*)data; + *(ushort*) (ptr+4) = *(ushort*)(data+4); + break; + case 7: + *(uint*) ptr = *(uint*)data; + *(ushort*) (ptr+4) = *(ushort*)(data+4); + *(ptr+6) = *(data+6); + break; + case 8: + *(ulong*) ptr = *(ulong*)data; + break; + } + + value = returnValue >> 3; + } +#endif + #endregion + + #region Private Methods + private static void ReadUInt64Packed(ref FastBufferReader reader, out ulong value) + { + reader.ReadByteSafe(out byte firstByte); + if (firstByte <= 240) + { + value = firstByte; + return; + } + + if (firstByte <= 248) + { + reader.ReadByteSafe(out byte secondByte); + value = 240UL + ((firstByte - 241UL) << 8) + secondByte; + return; + } + + var numBytes = firstByte - 247; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.ReadPartialValue(out value, numBytes); + } + + private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value) + { + reader.ReadByteSafe(out byte firstByte); + if (firstByte <= 240) + { + value = firstByte; + return; + } + + if (firstByte <= 248) + { + reader.ReadByteSafe(out byte secondByte); + value = 240U + ((firstByte - 241U) << 8) + secondByte; + return; + } + + var numBytes = firstByte - 247; + if (!reader.VerifyCanReadInternal(numBytes)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + reader.ReadPartialValue(out value, numBytes); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint ToUint(T value) where T : unmanaged + { + uint* asUint = (uint*) &value; + return *asUint; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong ToUlong(T value) where T : unmanaged + { + ulong* asUlong = (ulong*) &value; + return *asUlong; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe float ToSingle(T value) where T : unmanaged + { + float* asFloat = (float*) &value; + return *asFloat; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe double ToDouble(T value) where T : unmanaged + { + double* asDouble = (double*) &value; + return *asDouble; + } + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs.meta new file mode 100644 index 0000000000..f3784bcdb4 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 73484532f9cd8a7418b6a7ac770df851 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs new file mode 100644 index 0000000000..a39af22973 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -0,0 +1,836 @@ +using System; +using System.Runtime.CompilerServices; +using System.Text; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public struct FastBufferReader + { + private NativeArray m_buffer; + + internal struct InternalData + { + public unsafe byte* BufferPointer; + public int Position; + public int Length; + public int BitPosition; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + public int AllowedReadMark; + public int AllowedBitwiseReadMark; + public bool InBitwiseContext; +#endif + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool BitAligned() + { + return (BitPosition & 7) == 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CommitBitwiseReads(int amount) + { + Position += amount; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + InBitwiseContext = false; +#endif + } + } + + private InternalData m_InternalData; + + public unsafe FastBufferReader(NativeArray buffer, int offset = 0, int length = -1) + { + m_buffer = buffer; + m_InternalData = new InternalData + { + BufferPointer = (byte*) m_buffer.GetUnsafePtr(), + Position = offset, + Length = length == -1 ? buffer.Length : length, +#if DEVELOPMENT_BUILD || UNITY_EDITOR + AllowedReadMark = 0, + InBitwiseContext = false, +#endif + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Seek(int where) + { + m_InternalData.Position = where; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void MarkBytesRead(int amount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + amount > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + } +#endif + m_InternalData.Position += amount; + } + + public unsafe BitReader EnterBitwiseContext() + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.InBitwiseContext = true; +#endif + return new BitReader(ref m_InternalData); + } + + public int Position => m_InternalData.Position; + public int Length => m_InternalData.Length; + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool VerifyCanReadInternal(int bytes) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + if (m_InternalData.Position + bytes > m_InternalData.Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.Position + bytes > m_InternalData.AllowedReadMark) + { + m_InternalData.AllowedReadMark = m_InternalData.Position + bytes; + } +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool VerifyCanRead(int bytes) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + if (m_InternalData.Position + bytes > m_InternalData.Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedReadMark = m_InternalData.Position + bytes; +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + int len = sizeof(T); + if (m_InternalData.Position + len > m_InternalData.Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedReadMark = m_InternalData.Position + len; +#endif + return true; + } + public bool VerifyCanReadBits(int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (!m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bitwise mode while not in a bitwise context."); + } +#endif + var newBitPosition = m_InternalData.BitPosition + bitCount; + var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; + if ((newBitPosition & 7) != 0) + { + // Accounting for the partial read + ++totalBytesWrittenInBitwiseContext; + } + + if (m_InternalData.Position + totalBytesWrittenInBitwiseContext > m_InternalData.Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedBitwiseReadMark = newBitPosition; +#endif + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public NativeArray GetNativeArray() + { + return m_buffer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public byte[] ToArray() + { + return m_buffer.ToArray(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe byte* GetUnsafePtr() + { + return m_InternalData.BufferPointer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe byte* GetUnsafePtrAtCurrentPosition() + { + return m_InternalData.BufferPointer + m_InternalData.Position; + } + + /// + /// Reads a boxed object in a standard format + /// Named differently from other ReadValue methods to avoid accidental boxing + /// + /// The object to read + /// The type to be read + /// Should a null value be encoded? Any type for which type.IsNullable() returns true will always encode it. + public void ReadObject(out object value, Type type, bool isNullable = false) + { + if (isNullable || type.IsNullable()) + { + ReadValueSafe(out bool isNull); + + if (isNull) + { + value = null; + return; + } + } + + var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); + if (hasDeserializer) + { + deserializer(ref this, out value); + return; + } + + if (type.IsArray && type.HasElementType) + { + ReadValueSafe(out int length); + + var arr = Array.CreateInstance(type.GetElementType(), length); + + for (int i = 0; i < length; i++) + { + ReadObject(out object item, type.GetElementType()); + arr.SetValue(item, i); + } + + value = arr; + return; + } + + if (type.IsEnum) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + ReadValueSafe(out byte boolVal); + value = Enum.ToObject(type, boolVal != 0); + return; + case TypeCode.Char: + ReadValueSafe(out char charVal); + value = Enum.ToObject(type, charVal); + return; + case TypeCode.SByte: + ReadValueSafe(out sbyte sbyteVal); + value = Enum.ToObject(type, sbyteVal); + return; + case TypeCode.Byte: + ReadValueSafe(out byte byteVal); + value = Enum.ToObject(type, byteVal); + return; + case TypeCode.Int16: + ReadValueSafe(out short shortVal); + value = Enum.ToObject(type, shortVal); + return; + case TypeCode.UInt16: + ReadValueSafe(out ushort ushortVal); + value = Enum.ToObject(type, ushortVal); + return; + case TypeCode.Int32: + ReadValueSafe(out int intVal); + value = Enum.ToObject(type, intVal); + return; + case TypeCode.UInt32: + ReadValueSafe(out uint uintVal); + value = Enum.ToObject(type, uintVal); + return; + case TypeCode.Int64: + ReadValueSafe(out long longVal); + value = Enum.ToObject(type, longVal); + return; + case TypeCode.UInt64: + ReadValueSafe(out ulong ulongVal); + value = Enum.ToObject(type, ulongVal); + return; + } + } + + if (type == typeof(GameObject)) + { + ReadValueSafe(out GameObject go); + value = go; + return; + } + + if (type == typeof(NetworkObject)) + { + ReadValueSafe(out NetworkObject no); + value = no; + return; + } + + if (typeof(NetworkBehaviour).IsAssignableFrom(type)) + { + ReadValueSafe(out NetworkBehaviour nb); + value = nb; + return; + } + /*if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + }*/ + + throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); + } + + /*public void ReadValue(ref T value) where T : INetworkSerializable + { + // TODO + }*/ + + public void ReadValue(out GameObject value) + { + ReadValue(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.gameObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValueSafe(out GameObject value) + { + ReadValueSafe(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.gameObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValue(out NetworkObject value) + { + ReadValue(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValueSafe(out NetworkObject value) + { + ReadValueSafe(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValue(out NetworkBehaviour value) + { + ReadValue(out ulong networkObjectId); + ReadValue(out ushort networkBehaviourId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + public void ReadValueSafe(out NetworkBehaviour value) + { + ReadValueSafe(out ulong networkObjectId); + ReadValueSafe(out ushort networkBehaviourId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Reads a string + /// NOTE: ALLOCATES + /// + /// Stores the read string + /// Whether or not to use one byte per character. This will only allow ASCII + public unsafe void ReadValue(out string s, bool oneByteChars = false) + { + ReadValue(out uint length); + s = "".PadRight((int)length); + int target = s.Length; + fixed (char* native = s) + { + if (oneByteChars) + { + for (int i = 0; i < target; ++i) + { + if (oneByteChars) + { + ReadByte(out byte b); + native[i] = (char) b; + } + } + } + else + { + ReadBytes((byte*) native, target * sizeof(char)); + } + } + } + + /// + /// Reads a string. + /// NOTE: ALLOCATES + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Stores the read string + /// Whether or not to use one byte per character. This will only allow ASCII + public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(sizeof(uint))) + { + throw new OverflowException("Reading past the end of the buffer"); + } + + ReadValue(out uint length); + + if (!VerifyCanReadInternal((int)length * (oneByteChars ? 1 : sizeof(char)))) + { + throw new OverflowException("Reading past the end of the buffer"); + } + s = "".PadRight((int)length); + int target = s.Length; + fixed (char* native = s) + { + if (oneByteChars) + { + for (int i = 0; i < target; ++i) + { + if (oneByteChars) + { + ReadByte(out byte b); + native[i] = (char) b; + } + } + } + else + { + ReadBytes((byte*) native, target * sizeof(char)); + } + } + } + + /// + /// Writes an unmanaged array + /// NOTE: ALLOCATES + /// + /// Stores the read array + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValue(out T[] array) where T: unmanaged + { + ReadValue(out int sizeInTs); + int sizeInBytes = sizeInTs * sizeof(T); + array = new T[sizeInTs]; + fixed (T* native = array) + { + byte* bytes = (byte*)(native); + ReadBytes(bytes, sizeInBytes); + } + } + + /// + /// Reads an unmanaged array + /// NOTE: ALLOCATES + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Stores the read array + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValueSafe(out T[] array) where T: unmanaged + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(sizeof(int))) + { + throw new OverflowException("Writing past the end of the buffer"); + } + ReadValue(out int sizeInTs); + int sizeInBytes = sizeInTs * sizeof(T); + if (!VerifyCanReadInternal(sizeInBytes)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + array = new T[sizeInTs]; + fixed (T* native = array) + { + byte* bytes = (byte*)(native); + ReadBytes(bytes, sizeInBytes); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + { + // Switch statement to read small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + bytesToRead > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + } +#endif + + T val = new T(); + byte* ptr = ((byte*) &val) + offsetBytes; + byte* bufferPointer = m_InternalData.BufferPointer + m_InternalData.Position; + switch (bytesToRead) + { + case 1: + ptr[0] = *bufferPointer; + break; + case 2: + *(ushort*) ptr = *(ushort*)bufferPointer; + break; + case 3: + *(ushort*) ptr = *(ushort*)bufferPointer; + *(ptr+2) = *(bufferPointer+2); + break; + case 4: + *(uint*) ptr = *(uint*)bufferPointer; + break; + case 5: + *(uint*) ptr = *(uint*)bufferPointer; + *(ptr+4) = *(bufferPointer+4); + break; + case 6: + *(uint*) ptr = *(uint*)bufferPointer; + *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); + break; + case 7: + *(uint*) ptr = *(uint*)bufferPointer; + *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); + *(ptr+6) = *(bufferPointer+6); + break; + case 8: + *(ulong*) ptr = *(ulong*)bufferPointer; + break; + default: + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); + break; + } + + m_InternalData.Position += bytesToRead; + value = val; + } + + /// + /// Read a byte to the stream. + /// + /// Stores the read value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadByte(out byte value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + 1 > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + } +#endif + value = m_InternalData.BufferPointer[m_InternalData.Position++]; + } + + /// + /// Read a byte to the stream. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Stores the read value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadByteSafe(out byte value) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(1)) + { + throw new OverflowException("Reading past the end of the buffer"); + } + value = m_InternalData.BufferPointer[m_InternalData.Position++]; + } + + /// + /// Read multiple bytes to the stream + /// + /// Pointer to the destination buffer + /// Number of bytes to read - MUST BE <= BUFFER SIZE + /// Offset of the byte buffer to store into + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBytes(byte* value, int size, int offset = 0) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + size > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + } +#endif + UnsafeUtility.MemCpy(value + offset, (m_InternalData.BufferPointer + m_InternalData.Position), size); + m_InternalData.Position += size; + } + + /// + /// Read multiple bytes to the stream + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Pointer to the destination buffer + /// Number of bytes to read - MUST BE <= BUFFER SIZE + /// Offset of the byte buffer to store into + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(size)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + UnsafeUtility.MemCpy(value + offset, (m_InternalData.BufferPointer + m_InternalData.Position), size); + m_InternalData.Position += size; + } + + /// + /// Read multiple bytes from the stream + /// + /// Pointer to the destination buffer + /// Number of bytes to read - MUST BE <= BUFFER SIZE + /// Offset of the byte buffer to store into + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBytes(ref byte[] value, int size, int offset = 0) + { + fixed (byte* ptr = value) + { + ReadBytes(ptr, size, offset); + } + } + + /// + /// Read multiple bytes from the stream + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// Pointer to the destination buffer + /// Number of bytes to read - MUST BE <= BUFFER SIZE + /// Offset of the byte buffer to store into + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadBytesSafe(ref byte[] value, int size, int offset = 0) + { + fixed (byte* ptr = value) + { + ReadBytesSafe(ptr, size, offset); + } + } + + /// + /// Read a value of any unmanaged type to the buffer. + /// It will be copied from the buffer exactly as it existed in memory on the writing end. + /// + /// The read value + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValue(out T value) where T : unmanaged + { + int len = sizeof(T); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } + if (m_InternalData.Position + len > m_InternalData.AllowedReadMark) + { + throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + } +#endif + + T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + value = *pointer; + m_InternalData.Position += len; + } + + /// + /// Read a value of any unmanaged type to the buffer. + /// It will be copied from the buffer exactly as it existed in memory on the writing end. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// The read value + /// Any unmanaged type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void ReadValueSafe(out T value) where T : unmanaged + { + int len = sizeof(T); + +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferReader in bytewise mode while in a bitwise context."); + } +#endif + + if (!VerifyCanReadInternal(len)) + { + throw new OverflowException("Writing past the end of the buffer"); + } + + T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + value = *pointer; + m_InternalData.Position += len; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs.meta new file mode 100644 index 0000000000..3667f738aa --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5479786a4b1f57648a0fe56bd37a823b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index f77d6eba14..1c3e5e002f 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -19,10 +19,23 @@ internal struct InternalData public int Capacity; public int MaxCapacity; public Allocator Allocator; + public int BitPosition; #if DEVELOPMENT_BUILD || UNITY_EDITOR public int AllowedWriteMark; + public int AllowedBitwiseWriteMark; public bool InBitwiseContext; #endif + + + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool BitAligned() + { + return (BitPosition & 7) == 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CommitBitwiseWrites(int amount) { @@ -154,6 +167,36 @@ public bool VerifyCanWrite(int bytes) return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool VerifyCanWriteInternal(int bytes) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bytewise mode while in a bitwise context."); + } +#endif + if (m_InternalData.Position + bytes > m_InternalData.Capacity) + { + if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + { + Grow(); + } + else + { + return false; + } + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (m_InternalData.Position + bytes > m_InternalData.AllowedWriteMark) + { + m_InternalData.AllowedWriteMark = m_InternalData.Position + bytes; + } +#endif + return true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged { @@ -181,6 +224,40 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged #endif return true; } + + public bool VerifyCanWriteBits(int bitCount) + { +#if DEVELOPMENT_BUILD || UNITY_EDITOR + if (!m_InternalData.InBitwiseContext) + { + throw new InvalidOperationException( + "Cannot use BufferWriter in bitwise mode while not in a bitwise context."); + } +#endif + var newBitPosition = m_InternalData.BitPosition + bitCount; + var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; + if ((newBitPosition & 7) != 0) + { + // Accounting for the partial write + ++totalBytesWrittenInBitwiseContext; + } + + if (m_InternalData.Position + totalBytesWrittenInBitwiseContext > m_InternalData.Capacity) + { + if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + { + Grow(); + } + else + { + return false; + } + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InternalData.AllowedBitwiseWriteMark = newBitPosition; +#endif + return true; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public NativeArray GetNativeArray() @@ -284,37 +361,16 @@ public void WriteObject(object value, bool isNullable = false) } if (value is GameObject) { - var networkObject = ((GameObject)value).GetComponent(); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); - } - - WriteValueSafe(networkObject); + WriteValueSafe((GameObject)value); return; } if (value is NetworkObject) { - if (!((NetworkObject)value).IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((NetworkObject)value).gameObject.name}"); - } - WriteValueSafe((NetworkObject)value); return; } if (value is NetworkBehaviour) { - if (!((NetworkBehaviour)value).HasNetworkObject || !((NetworkBehaviour)value).NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); - } - WriteValueSafe((NetworkBehaviour)value); return; } @@ -324,7 +380,7 @@ public void WriteObject(object value, bool isNullable = false) return; } - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } public void WriteValue(T value) where T : INetworkSerializable @@ -337,12 +393,12 @@ public void WriteValue(GameObject value) var networkObject = ((GameObject)value).GetComponent(); if (networkObject == null) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } WriteValue(networkObject.NetworkObjectId); @@ -353,12 +409,12 @@ public void WriteValueSafe(GameObject value) var networkObject = ((GameObject)value).GetComponent(); if (networkObject == null) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); } WriteValueSafe(networkObject.NetworkObjectId); @@ -373,7 +429,7 @@ public void WriteValue(in NetworkObject value) { if (!value.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); } WriteValue(value.NetworkObjectId); @@ -383,7 +439,7 @@ public void WriteValueSafe(NetworkObject value) { if (!value.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); } WriteValueSafe(value.NetworkObjectId); } @@ -397,7 +453,7 @@ public void WriteValue(NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); } WriteValue(value.NetworkObjectId); @@ -408,10 +464,10 @@ public void WriteValueSafe(NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); } - if (!VerifyCanWrite(sizeof(ulong) + sizeof(ushort))) + if (!VerifyCanWriteInternal(sizeof(ulong) + sizeof(ushort))) { throw new OverflowException("Writing past the end of the buffer"); } @@ -424,7 +480,6 @@ public static int GetWriteSize(NetworkBehaviour value) return sizeof(ulong) + sizeof(ushort); } - // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays /// /// Writes a string /// @@ -453,7 +508,6 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) } } - // As it turns out, strings cannot be treated as char arrays, since strings use pointers to store data rather than C# arrays /// /// Writes a string /// @@ -471,7 +525,7 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) int sizeInBytes = GetWriteSize(s, oneByteChars); - if (!VerifyCanWrite(sizeInBytes)) + if (!VerifyCanWriteInternal(sizeInBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -542,7 +596,7 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); - if (!VerifyCanWrite(sizeInBytes + sizeof(int))) + if (!VerifyCanWriteInternal(sizeInBytes + sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); } @@ -658,7 +712,7 @@ public unsafe void WriteByteSafe(byte value) } #endif - if (!VerifyCanWrite(1)) + if (!VerifyCanWriteInternal(1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -704,7 +758,7 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) } #endif - if (!VerifyCanWrite(size)) + if (!VerifyCanWriteInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -811,7 +865,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged } #endif - if (!VerifyCanWrite(len)) + if (!VerifyCanWriteInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -826,5 +880,25 @@ public static unsafe int GetWriteSize(in T value) where T : unmanaged { return sizeof(T); } + + public static unsafe int GetWriteSize() where T : unmanaged + { + return sizeof(T); + } + + public static int GetNetworkObjectWriteSize() + { + return sizeof(ulong); + } + + public static int GetGameObjectWriteSize() + { + return sizeof(ulong); + } + + public static int GetNetworkBehaviourWriteSize() + { + return sizeof(ulong) + sizeof(ushort); + } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs index 8ef5bdb1ed..2d714c2642 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -7,6 +7,7 @@ namespace Unity.Multiplayer.Netcode public static class SerializationTypeTable { public delegate void Serialize(ref FastBufferWriter writer, object value); + public delegate void Deserialize(ref FastBufferReader reader, out object value); public static Dictionary Serializers = new Dictionary { @@ -65,6 +66,228 @@ public static class SerializationTypeTable [typeof(bool[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool[])value), }; + + public static Dictionary Deserializers = new Dictionary + { + [typeof(byte)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadByteSafe(out byte tmp); + value = tmp; + }, + [typeof(sbyte)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadByteSafe(out byte tmp); + value = (sbyte)tmp; + }, + + [typeof(ushort)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out ushort tmp); + value = tmp; + }, + [typeof(short)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out short tmp); + value = tmp; + }, + [typeof(uint)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out uint tmp); + value = tmp; + }, + [typeof(int)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out int tmp); + value = tmp; + }, + [typeof(ulong)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out ulong tmp); + value = tmp; + }, + [typeof(long)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out long tmp); + value = tmp; + }, + + [typeof(float)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out float tmp); + value = tmp; + }, + [typeof(double)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out double tmp); + value = tmp; + }, + + [typeof(string)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out string tmp); + value = tmp; + }, + + [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector2 tmp); + value = tmp; + }, + [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector3 tmp); + value = tmp; + }, + [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector4 tmp); + value = tmp; + }, + [typeof(Color)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Color tmp); + value = tmp; + }, + [typeof(Color32)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Color32 tmp); + value = tmp; + }, + [typeof(Ray)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Ray tmp); + value = tmp; + }, + [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Ray2D tmp); + value = tmp; + }, + [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Quaternion tmp); + value = tmp; + }, + + [typeof(char)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out char tmp); + value = tmp; + }, + + [typeof(bool)] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out bool tmp); + value = tmp; + }, + + + [typeof(byte[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out byte[] tmp); + value = tmp; + }, + [typeof(sbyte[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out sbyte[] tmp); + value = tmp; + }, + + [typeof(ushort[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out ushort[] tmp); + value = tmp; + }, + [typeof(short[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out short[] tmp); + value = tmp; + }, + [typeof(uint[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out uint[] tmp); + value = tmp; + }, + [typeof(int[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out int[] tmp); + value = tmp; + }, + [typeof(ulong[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out ulong[] tmp); + value = tmp; + }, + [typeof(long[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out long[] tmp); + value = tmp; + }, + + [typeof(float[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out float[] tmp); + value = tmp; + }, + [typeof(double[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out double[] tmp); + value = tmp; + }, + + [typeof(Vector2[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector2[] tmp); + value = tmp; + }, + [typeof(Vector3[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector3[] tmp); + value = tmp; + }, + [typeof(Vector4[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Vector4[] tmp); + value = tmp; + }, + [typeof(Color[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Color[] tmp); + value = tmp; + }, + [typeof(Color32[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Color32[] tmp); + value = tmp; + }, + [typeof(Ray[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Ray[] tmp); + value = tmp; + }, + [typeof(Ray2D[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Ray2D[] tmp); + value = tmp; + }, + [typeof(Quaternion[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out Quaternion[] tmp); + value = tmp; + }, + + [typeof(char[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out char[] tmp); + value = tmp; + }, + + [typeof(bool[])] = (ref FastBufferReader reader, out object value) => + { + reader.ReadValueSafe(out bool[] tmp); + value = tmp; + }, + }; public static Dictionary SerializersPacked = new Dictionary { @@ -96,5 +319,120 @@ public static class SerializationTypeTable [typeof(bool)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (bool)value), }; + + public static Dictionary DeserializersPacked = new Dictionary + { + [typeof(byte)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out byte tmp); + value = tmp; + }, + [typeof(sbyte)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out byte tmp); + value = (sbyte)tmp; + }, + + [typeof(ushort)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out ushort tmp); + value = tmp; + }, + [typeof(short)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out short tmp); + value = tmp; + }, + [typeof(uint)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out uint tmp); + value = tmp; + }, + [typeof(int)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out int tmp); + value = tmp; + }, + [typeof(ulong)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out ulong tmp); + value = tmp; + }, + [typeof(long)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out long tmp); + value = tmp; + }, + + [typeof(float)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out float tmp); + value = tmp; + }, + [typeof(double)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out double tmp); + value = tmp; + }, + + [typeof(string)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out string tmp); + value = tmp; + }, + + [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Vector2 tmp); + value = tmp; + }, + [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Vector3 tmp); + value = tmp; + }, + [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Vector4 tmp); + value = tmp; + }, + [typeof(Color)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Color tmp); + value = tmp; + }, + [typeof(Color32)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Color32 tmp); + value = tmp; + }, + [typeof(Ray)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Ray tmp); + value = tmp; + }, + [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Ray2D tmp); + value = tmp; + }, + [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out Quaternion tmp); + value = tmp; + }, + + [typeof(char)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out char tmp); + value = tmp; + }, + + [typeof(bool)] = (ref FastBufferReader reader, out object value) => + { + ByteUnpacker.ReadValuePacked(ref reader, out bool tmp); + value = tmp; + }, + }; } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs new file mode 100644 index 0000000000..4034de6648 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -0,0 +1,325 @@ +using System; +using NUnit.Framework; +using Unity.Collections; +using Unity.Multiplayer.Netcode; + +namespace Unity.Netcode.EditorTests +{ + public class BitReaderTests + { + [Test] + public void TestReadingOneBit() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBit(true); + + bitWriter.WriteBit(true); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(false); + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + } + + writer.WriteByte(0b11111111); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + bool b; + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + } + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); + } + } + [Test] + public unsafe void TestVerifyCanReadBits() + { + FastBufferReader reader = new FastBufferReader(new NativeArray(4, Allocator.Temp)); + using (reader.GetNativeArray()) + { + int* asInt = (int*) reader.GetUnsafePtr(); + *asInt = 0b11111111_00001010_10101011; + + using (var bitReader = reader.EnterBitwiseContext()) + { + Assert.Throws(() => reader.VerifyCanRead(1)); + Assert.Throws(() => reader.VerifyCanReadValue(1)); + Assert.IsTrue(reader.VerifyCanReadBits(1)); + bitReader.ReadBit(out bool b); + Assert.IsTrue(b); + + // Can't use Assert.Throws() because ref struct BitWriter can't be captured in a lambda + try + { + bitReader.ReadBit(out b); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(reader.VerifyCanReadBits(3)); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + byte byteVal; + try + { + bitReader.ReadBits(out byteVal, 4); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + + try + { + bitReader.ReadBits(out byteVal, 1); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(reader.VerifyCanReadBits(3)); + + try + { + bitReader.ReadBits(out byteVal, 4); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(reader.VerifyCanReadBits(4)); + bitReader.ReadBits(out byteVal, 3); + Assert.AreEqual(0b010, byteVal); + + Assert.IsTrue(reader.VerifyCanReadBits(5)); + + bitReader.ReadBits(out byteVal, 5); + Assert.AreEqual(0b10101, byteVal); + } + + Assert.AreEqual(2, reader.Position); + + Assert.IsTrue(reader.VerifyCanRead(1)); + reader.ReadByte(out byte nextByte); + Assert.AreEqual(0b11111111, nextByte); + + Assert.IsTrue(reader.VerifyCanRead(1)); + reader.ReadByte(out nextByte); + Assert.AreEqual(0b00000000, nextByte); + + Assert.IsFalse(reader.VerifyCanRead(1)); + using (var bitReader = reader.EnterBitwiseContext()) + { + Assert.IsFalse(reader.VerifyCanReadBits(1)); + } + } + } + + [Test] + public void TestReadingMultipleBits() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111, 1); + bitWriter.WriteBits(0b11111111, 1); + bitWriter.WriteBits(0b11111110, 2); + bitWriter.WriteBits(0b11111000, 4); + bitWriter.WriteBits(0b11111010, 4); + } + writer.WriteByte(0b11111111); + + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + byte b; + bitReader.ReadBits(out b, 1); + Assert.AreEqual(0b1, b); + + bitReader.ReadBits(out b, 1); + Assert.AreEqual(0b1, b); + + bitReader.ReadBits(out b, 2); + Assert.AreEqual(0b10, b); + + bitReader.ReadBits(out b, 4); + Assert.AreEqual(0b1000, b); + + bitReader.ReadBits(out b, 4); + Assert.AreEqual(0b1010, b); + } + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); + } + } + + [Test] + public void TestReadingMultipleBitsToLongs() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(3)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(0b11111111UL, 1); + bitWriter.WriteBits(0b11111111UL, 1); + bitWriter.WriteBits(0b11111110UL, 2); + bitWriter.WriteBits(0b11111000UL, 4); + bitWriter.WriteBits(0b11111010UL, 4); + } + + writer.WriteByte(0b11111111); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + ulong ul; + bitReader.ReadBits(out ul, 1); + Assert.AreEqual(0b1, ul); + + bitReader.ReadBits(out ul, 1); + Assert.AreEqual(0b1, ul); + + bitReader.ReadBits(out ul, 2); + Assert.AreEqual(0b10, ul); + + bitReader.ReadBits(out ul, 4); + Assert.AreEqual(0b1000, ul); + + bitReader.ReadBits(out ul, 4); + Assert.AreEqual(0b1010, ul); + } + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); + } + } + + [Test] + public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() + { + FastBufferReader reader = new FastBufferReader(new NativeArray(4, Allocator.Temp)); + using (reader.GetNativeArray()) + { + int* asInt = (int*) reader.GetUnsafePtr(); + *asInt = 0b11111111_00001010_10101011; + + Assert.Throws(() => + { + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBit(out bool b); + } + }); + + Assert.Throws(() => + { + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBits(out byte b, 1); + } + }); + + Assert.Throws(() => + { + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBits(out ulong ul, 1); + } + }); + + Assert.AreEqual(0, reader.Position); + + Assert.Throws(() => + { + Assert.IsTrue(reader.VerifyCanRead(1)); + using (var bitReader = reader.EnterBitwiseContext()) + { + ulong ul; + try + { + bitReader.ReadBits(out ul, 4); + bitReader.ReadBits(out ul, 4); + } + catch (OverflowException e) + { + Assert.Fail("Overflow exception was thrown too early."); + throw; + } + bitReader.ReadBits(out ul, 4); + } + }); + + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs.meta new file mode 100644 index 0000000000..0dc0f36136 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67df11865abcd5843a4e142cf6bbd901 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index d4a41ae2ea..b03fb84d1d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -50,6 +50,114 @@ public unsafe void TestWritingOneBit() Assert.AreEqual(0b11111111_00001010_10001011, *asInt); } } + [Test] + public unsafe void TestVerifyCanWriteBits() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + int* asInt = (int*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asInt); + + using (var bitWriter = writer.EnterBitwiseContext()) + { + Assert.Throws(() => writer.VerifyCanWrite(1)); + Assert.Throws(() => writer.VerifyCanWriteValue(1)); + Assert.IsTrue(writer.VerifyCanWriteBits(1)); + bitWriter.WriteBit(true); + Assert.AreEqual(0b1, *asInt); + + // Can't use Assert.Throws() because ref struct BitWriter can't be captured in a lambda + try + { + bitWriter.WriteBit(true); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(writer.VerifyCanWriteBits(3)); + bitWriter.WriteBit(true); + Assert.AreEqual(0b11, *asInt); + + bitWriter.WriteBit(false); + bitWriter.WriteBit(true); + Assert.AreEqual(0b1011, *asInt); + + try + { + bitWriter.WriteBits(0b11111111, 4); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + + try + { + bitWriter.WriteBits(0b11111111, 1); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(writer.VerifyCanWriteBits(3)); + + try + { + bitWriter.WriteBits(0b11111111, 4); + } + catch (OverflowException e) + { + // Should get called here. + } + catch (Exception e) + { + throw e; + } + Assert.IsTrue(writer.VerifyCanWriteBits(4)); + + bitWriter.WriteBits(0b11111010, 3); + + Assert.AreEqual(0b00101011, *asInt); + + Assert.IsTrue(writer.VerifyCanWriteBits(5)); + + bitWriter.WriteBits(0b11110101, 5); + Assert.AreEqual(0b1010_10101011, *asInt); + } + + Assert.AreEqual(2, writer.Position); + Assert.AreEqual(0b1010_10101011, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(1)); + writer.WriteByte(0b11111111); + Assert.AreEqual(0b11111111_00001010_10101011, *asInt); + + Assert.IsTrue(writer.VerifyCanWrite(1)); + writer.WriteByte(0b00000000); + Assert.AreEqual(0b11111111_00001010_10101011, *asInt); + + Assert.IsFalse(writer.VerifyCanWrite(1)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + Assert.IsFalse(writer.VerifyCanWriteBits(1)); + } + } + } [Test] public unsafe void TestWritingMultipleBits() @@ -172,6 +280,27 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() writer.WriteByteSafe(0b11111111); Assert.AreEqual(0b11111111, *asInt); + + + Assert.Throws(() => + { + Assert.IsTrue(writer.VerifyCanWrite(1)); + using (var bitWriter = writer.EnterBitwiseContext()) + { + try + { + bitWriter.WriteBits(0b11111111UL, 4); + bitWriter.WriteBits(0b11111111UL, 4); + } + catch (OverflowException e) + { + Assert.Fail("Overflow exception was thrown too early."); + throw; + } + bitWriter.WriteBits(0b11111111UL, 1); + } + }); + } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 4fc34219fa..3a31a778b7 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -1,9 +1,7 @@ using System; using NUnit.Framework; using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; using Unity.Multiplayer.Netcode; -using UnityEngine; namespace Unity.Netcode.EditorTests { @@ -26,26 +24,10 @@ private void CheckUnsignedPackedSize64(ref FastBufferWriter writer, ulong value) } } - private unsafe void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value) + private void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value) { - byte* asBytes = writer.GetUnsafePtr(); - ulong readValue; - if (asBytes[0] <= 240) - { - Assert.AreEqual(asBytes[0], value); - return; - } - - if (asBytes[0] <= 248) - { - readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; - Assert.AreEqual(readValue, value); - return; - } - - var numBytes = asBytes[0] - 247; - readValue = 0; - UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + var reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out ulong readValue); Assert.AreEqual(readValue, value); } @@ -66,26 +48,10 @@ private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) } } - private unsafe void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) + private void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) { - byte* asBytes = writer.GetUnsafePtr(); - ulong readValue; - if (asBytes[0] <= 240) - { - Assert.AreEqual(asBytes[0], value); - return; - } - - if (asBytes[0] <= 248) - { - readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; - Assert.AreEqual(readValue, value); - return; - } - - var numBytes = asBytes[0] - 247; - readValue = 0; - UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); + var reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out uint readValue); Assert.AreEqual(readValue, value); } @@ -107,27 +73,11 @@ private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) } } - private unsafe void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) + private void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) { - byte* asBytes = writer.GetUnsafePtr(); - ulong readValue; - if (asBytes[0] <= 240) - { - Assert.AreEqual(Arithmetic.ZigZagDecode(asBytes[0]), value); - return; - } - - if (asBytes[0] <= 248) - { - readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; - Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); - return; - } - - var numBytes = asBytes[0] - 247; - readValue = 0; - UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); - Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + var reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out long readValue); + Assert.AreEqual(readValue, value); } private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) @@ -148,27 +98,11 @@ private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) } } - private unsafe void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) + private void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) { - byte* asBytes = writer.GetUnsafePtr(); - ulong readValue; - if (asBytes[0] <= 240) - { - Assert.AreEqual(Arithmetic.ZigZagDecode(asBytes[0]), value); - return; - } - - if (asBytes[0] <= 248) - { - readValue = 240UL + ((asBytes[0] - 241UL) << 8) + asBytes[1]; - Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); - return; - } - - var numBytes = asBytes[0] - 247; - readValue = 0; - UnsafeUtility.MemCpy(&readValue, asBytes + 1, numBytes); - Assert.AreEqual(Arithmetic.ZigZagDecode(readValue), value); + var reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out int readValue); + Assert.AreEqual(readValue, value); } [Test] @@ -400,92 +334,50 @@ private int GetByteCount15Bits(ushort value) return 2; } - private unsafe ulong Get61BitEncodedValue(byte* data) + private ulong Get61BitEncodedValue(NativeArray data) { - ulong returnValue = 0; - byte* ptr = ((byte*) &returnValue); - int numBytes = (data[0] & 0b111) + 1; - switch (numBytes) - { - case 1: - *ptr = *data; - break; - case 2: - *(ushort*) ptr = *(ushort*)data; - break; - case 3: - *(ushort*) ptr = *(ushort*)data; - *(ptr+2) = *(data+2); - break; - case 4: - *(uint*) ptr = *(uint*)data; - break; - case 5: - *(uint*) ptr = *(uint*)data; - *(ptr+4) = *(data+4); - break; - case 6: - *(uint*) ptr = *(uint*)data; - *(ushort*) (ptr+4) = *(ushort*)(data+4); - break; - case 7: - *(uint*) ptr = *(uint*)data; - *(ushort*) (ptr+4) = *(ushort*)(data+4); - *(ptr+6) = *(data+6); - break; - case 8: - *(ulong*) ptr = *(ulong*)data; - break; - } - - return returnValue >> 3; + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out ulong value); + return value; } - private unsafe uint Get30BitEncodedValue(byte* data) + private long Get60BitSignedEncodedValue(NativeArray data) { - uint returnValue = 0; - byte* ptr = ((byte*) &returnValue); - int numBytes = (data[0] & 0b11) + 1; - switch (numBytes) - { - case 1: - *ptr = *data; - break; - case 2: - *(ushort*) ptr = *(ushort*)data; - break; - case 3: - *(ushort*) ptr = *(ushort*)data; - *(ptr+2) = *(data+2); - break; - case 4: - *(uint*) ptr = *(uint*)data; - break; - } - - return returnValue >> 2; + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out long value); + return value; } - private unsafe ushort Get15BitEncodedValue(byte* data) + private uint Get30BitEncodedValue(NativeArray data) { - ushort returnValue = 0; - byte* ptr = ((byte*) &returnValue); - int numBytes = (data[0] & 0b1) + 1; - switch (numBytes) - { - case 1: - *ptr = *data; - break; - case 2: - *(ushort*) ptr = *(ushort*)data; - break; - } + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out uint value); + return value; + } + + private int Get29BitSignedEncodedValue(NativeArray data) + { + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out int value); + return value; + } - return (ushort)(returnValue >> 1); + private ushort Get15BitEncodedValue(NativeArray data) + { + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out ushort value); + return value; + } + + private short Get14BitSignedEncodedValue(NativeArray data) + { + FastBufferReader reader = new FastBufferReader(data); + ByteUnpacker.ReadValueBitPacked(ref reader, out short value); + return value; } [Test] - public unsafe void TestBitPacking61BitsUnsigned() + public void TestBitPacking61BitsUnsigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -494,10 +386,10 @@ public unsafe void TestBitPacking61BitsUnsigned() writer.VerifyCanWrite(8); ulong value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b111); - Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b111); + Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); for (var i = 0; i < 61; ++i) { @@ -506,8 +398,8 @@ public unsafe void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount61Bits(value)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -516,8 +408,8 @@ public unsafe void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get61BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount61Bits(value)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); } } @@ -526,7 +418,7 @@ public unsafe void TestBitPacking61BitsUnsigned() } [Test] - public unsafe void TestBitPacking60BitsSigned() + public void TestBitPacking60BitsSigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -535,10 +427,10 @@ public unsafe void TestBitPacking60BitsSigned() writer.VerifyCanWrite(8); long value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b111); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(0, nativeArray[0] & 0b111); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); for (var i = 0; i < 61; ++i) { @@ -548,8 +440,8 @@ public unsafe void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); value = -value; zzvalue = Arithmetic.ZigZagEncode(value); @@ -557,8 +449,8 @@ public unsafe void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -568,8 +460,8 @@ public unsafe void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); value = -value; zzvalue = Arithmetic.ZigZagEncode(value); @@ -577,8 +469,8 @@ public unsafe void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, asByte[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get61BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); } } @@ -587,7 +479,7 @@ public unsafe void TestBitPacking60BitsSigned() } [Test] - public unsafe void TestBitPacking30BitsUnsigned() + public void TestBitPacking30BitsUnsigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -596,10 +488,10 @@ public unsafe void TestBitPacking30BitsUnsigned() writer.VerifyCanWrite(4); uint value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b11); - Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); for (var i = 0; i < 30; ++i) { @@ -608,8 +500,8 @@ public unsafe void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount30Bits(value)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -618,8 +510,8 @@ public unsafe void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount30Bits(value)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); } } @@ -628,7 +520,7 @@ public unsafe void TestBitPacking30BitsUnsigned() } [Test] - public unsafe void TestBitPacking29BitsSigned() + public void TestBitPacking29BitsSigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -637,10 +529,10 @@ public unsafe void TestBitPacking29BitsSigned() writer.VerifyCanWrite(4); int value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b11); - Assert.AreEqual(value, Get30BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); for (var i = 0; i < 29; ++i) { @@ -650,8 +542,8 @@ public unsafe void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); value = -value; zzvalue = (uint)Arithmetic.ZigZagEncode(value); @@ -659,8 +551,8 @@ public unsafe void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -670,8 +562,8 @@ public unsafe void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); value = -value; zzvalue = (uint)Arithmetic.ZigZagEncode(value); @@ -679,15 +571,15 @@ public unsafe void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, asByte[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get30BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); } } } } [Test] - public unsafe void TestBitPacking15BitsUnsigned() + public void TestBitPacking15BitsUnsigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -696,10 +588,10 @@ public unsafe void TestBitPacking15BitsUnsigned() writer.VerifyCanWrite(2); ushort value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b1); - Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); for (var i = 0; i < 15; ++i) { @@ -708,8 +600,8 @@ public unsafe void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount15Bits(value)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -718,8 +610,8 @@ public unsafe void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + Assert.AreEqual(GetByteCount15Bits(value)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); } } @@ -727,7 +619,7 @@ public unsafe void TestBitPacking15BitsUnsigned() } } [Test] - public unsafe void TestBitPacking14BitsSigned() + public void TestBitPacking14BitsSigned() { FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); @@ -736,10 +628,10 @@ public unsafe void TestBitPacking14BitsSigned() writer.VerifyCanWrite(2); short value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - byte* asByte = writer.GetUnsafePtr(); + NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, asByte[0] & 0b1); - Assert.AreEqual(value, Get15BitEncodedValue(asByte)); + Assert.AreEqual(0, nativeArray[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); for (var i = 0; i < 14; ++i) { @@ -749,8 +641,8 @@ public unsafe void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); value = (short)-value; zzvalue = (ushort)Arithmetic.ZigZagEncode(value); @@ -758,8 +650,8 @@ public unsafe void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); for (var j = 0; j < 8; ++j) { @@ -769,8 +661,8 @@ public unsafe void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); value = (short)-value; zzvalue = (ushort)Arithmetic.ZigZagEncode(value); @@ -778,8 +670,8 @@ public unsafe void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, asByte[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Arithmetic.ZigZagDecode(Get15BitEncodedValue(asByte))); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs new file mode 100644 index 0000000000..97f51b5468 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -0,0 +1,2137 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Framework.Internal; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Multiplayer.Netcode; +using UnityEngine; +using UnityEngine.SceneManagement; +using Random = System.Random; + +namespace Unity.Netcode.EditorTests +{ + public class FastBufferReaderTests + { + #region Test Types + private enum ByteEnum : byte + { + A, + B, + C + }; + private enum SByteEnum : sbyte + { + A, + B, + C + }; + private enum ShortEnum : short + { + A, + B, + C + }; + private enum UShortEnum : ushort + { + A, + B, + C + }; + private enum IntEnum : int + { + A, + B, + C + }; + private enum UIntEnum : uint + { + A, + B, + C + }; + private enum LongEnum : long + { + A, + B, + C + }; + private enum ULongEnum : ulong + { + A, + B, + C + }; + + private struct TestStruct + { + public byte a; + public short b; + public ushort c; + public int d; + public uint e; + public long f; + public ulong g; + public bool h; + public char i; + public float j; + public double k; + } + + public enum WriteType + { + WriteDirect, + WriteSafe, + WriteAsObject + } + #endregion + + #region Common Checks + private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage="") + { + Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); + writer.WriteValue((byte)0x80); + Assert.AreEqual(writeSize+1, writer.Position, failMessage); + Assert.AreEqual(writeSize+1, writer.Length, failMessage); + writer.WriteValue((byte)0xFF); + Assert.AreEqual(writeSize+2, writer.Position, failMessage); + Assert.AreEqual(writeSize+2, writer.Length, failMessage); + } + + private void VerifyCheckBytes(ref FastBufferReader reader, int checkPosition, string failMessage = "") + { + reader.Seek(checkPosition); + reader.VerifyCanRead(2); + + reader.ReadByte(out byte value); + Assert.AreEqual(0x80, value, failMessage); + reader.ReadByte(out value); + Assert.AreEqual(0xFF, value, failMessage); + } + + private void VerifyPositionAndLength(ref FastBufferReader reader, int length, string failMessage = "") + { + Assert.AreEqual(0, reader.Position, failMessage); + Assert.AreEqual(length, reader.Length, failMessage); + } + + private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + { + NativeArray underlyingArray = writer.GetNativeArray(); + + WriteCheckBytes(ref writer, writeSize, failMessage); + + FastBufferReader reader = new FastBufferReader(underlyingArray); + + VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); + + VerifyCheckBytes(ref reader, writeSize, failMessage); + + reader.Seek(0); + + return reader; + } + #endregion + + #region Generic Checks + private unsafe void RunTypeTest(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + Assert.AreEqual(sizeof(T), writeSize); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + + var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; + + writer.WriteValue(valueToTest); + + var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); + + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetWriteSize())); + reader.ReadValue(out T result); + Assert.AreEqual(valueToTest, result); + } + } + private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; + + writer.WriteValueSafe(valueToTest); + + + var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); + + reader.ReadValueSafe(out T result); + Assert.AreEqual(valueToTest, result); + } + } + + private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using(writer) + { + Assert.AreEqual(sizeof(T), writeSize); + + var failMessage = $"RunObjectTypeTest failed with type {typeof(T)} and value {valueToTest}"; + writer.WriteObject(valueToTest); + + var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); + + reader.ReadObject(out object result, typeof(T)); + Assert.AreEqual(valueToTest, result); + } + } + + private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) where T: unmanaged + { + Assert.AreEqual(value.Length, compareValue.Length); + + for (var i = 0; i < value.Length; ++i) + { + Assert.AreEqual(value[i], compareValue[i]); + } + } + + private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + + writer.WriteValue(valueToTest); + + WriteCheckBytes(ref writer, writeSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + Assert.IsTrue(reader.VerifyCanRead(writeSize)); + reader.ReadValue(out T[] result); + VerifyArrayEquality(valueToTest, result, 0); + + VerifyCheckBytes(ref reader, writeSize); + } + } + + private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + + writer.WriteValueSafe(valueToTest); + + WriteCheckBytes(ref writer, writeSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadValueSafe(out T[] result); + VerifyArrayEquality(valueToTest, result, 0); + + VerifyCheckBytes(ref reader, writeSize); + } + } + + private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged + { + var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + // Extra byte for WriteObject adding isNull flag + FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); + + writer.WriteObject(valueToTest); + + WriteCheckBytes(ref writer, writeSize + 1); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadObject(out object result, typeof(T[])); + VerifyArrayEquality(valueToTest, (T[])result, 0); + + VerifyCheckBytes(ref reader, writeSize + 1); + } + } + #endregion + + #region Helpers + private TestStruct GetTestStruct() + { + var random = new Random(); + + var testStruct = new TestStruct + { + a = (byte) random.Next(), + b = (short) random.Next(), + c = (ushort) random.Next(), + d = (int) random.Next(), + e = (uint) random.Next(), + f = ((long) random.Next() << 32) + random.Next(), + g = ((ulong) random.Next() << 32) + (ulong) random.Next(), + h = true, + i = '\u263a', + j = (float) random.NextDouble(), + k = random.NextDouble(), + }; + + return testStruct; + } + #endregion + + #region Tests + [Test] + public void TestReadingBasicTypes( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte b = (byte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(b); + } + else + { + RunObjectTypeTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte sb = (sbyte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(sb); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(sb); + } + else + { + RunObjectTypeTest(sb); + } + } + else if (testType == typeof(short)) + { + short s = (short)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(s); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(s); + } + else + { + RunObjectTypeTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort us = (ushort)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(us); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(us); + } + else + { + RunObjectTypeTest(us); + } + } + else if (testType == typeof(int)) + { + int i = (int)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(i); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(i); + } + else + { + RunObjectTypeTest(i); + } + } + else if (testType == typeof(uint)) + { + uint ui = (uint)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ui); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(ui); + } + else + { + RunObjectTypeTest(ui); + } + } + else if (testType == typeof(long)) + { + long l = ((long)random.Next() << 32) + random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(l); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(l); + } + else + { + RunObjectTypeTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ul); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(ul); + } + else + { + RunObjectTypeTest(ul); + } + } + else if (testType == typeof(bool)) + { + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(true); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(true); + } + else + { + RunObjectTypeTest(true); + } + } + else if (testType == typeof(char)) + { + char c = 'a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(c); + } + else + { + RunObjectTypeTest(c); + } + + c = '\u263a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(c); + } + else + { + RunObjectTypeTest(c); + } + } + else if (testType == typeof(float)) + { + float f = (float)random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(f); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(f); + } + else + { + RunObjectTypeTest(f); + } + } + else if (testType == typeof(double)) + { + double d = random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(d); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(d); + } + else + { + RunObjectTypeTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum e = ByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum e = SByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum e = ShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum e = UShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum e = IntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum e = UIntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum e = LongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum e = ULongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray)) + { + var v = new Ray( + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray2D)) + { + var v = new Ray2D( + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeTestSafe(v); + } + else + { + RunObjectTypeTest(v); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + [Test] + public void TestReadingBasicArrays( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte[] b = { + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(b); + } + else + { + RunObjectTypeArrayTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte[] sb = { + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(sb); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(sb); + } + else + { + RunObjectTypeArrayTest(sb); + } + } + else if (testType == typeof(short)) + { + short[] s = { + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(s); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(s); + } + else + { + RunObjectTypeArrayTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort[] us = { + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(us); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(us); + } + else + { + RunObjectTypeArrayTest(us); + } + } + else if (testType == typeof(int)) + { + int[] i = { + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(i); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(i); + } + else + { + RunObjectTypeArrayTest(i); + } + } + else if (testType == typeof(uint)) + { + uint[] ui = { + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(ui); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(ui); + } + else + { + RunObjectTypeArrayTest(ui); + } + } + else if (testType == typeof(long)) + { + long[] l = { + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(l); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(l); + } + else + { + RunObjectTypeArrayTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong[] ul = { + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(ul); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(ul); + } + else + { + RunObjectTypeArrayTest(ul); + } + } + else if (testType == typeof(bool)) + { + bool[] b = { + true, + false, + true, + true, + false, + false, + true, + false, + true + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(b); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(b); + } + else + { + RunObjectTypeArrayTest(b); + } + } + else if (testType == typeof(char)) + { + char[] c = { + 'a', + '\u263a', + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(c); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(c); + } + else + { + RunObjectTypeArrayTest(c); + } + } + else if (testType == typeof(float)) + { + float[] f = { + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(f); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(f); + } + else + { + RunObjectTypeArrayTest(f); + } + } + else if (testType == typeof(double)) + { + double[] d = { + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble() + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(d); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(d); + } + else + { + RunObjectTypeArrayTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum[] e = { + ByteEnum.C, + ByteEnum.A, + ByteEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum[] e = { + SByteEnum.C, + SByteEnum.A, + SByteEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum[] e = { + ShortEnum.C, + ShortEnum.A, + ShortEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum[] e = { + UShortEnum.C, + UShortEnum.A, + UShortEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum[] e = { + IntEnum.C, + IntEnum.A, + IntEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum[] e = { + UIntEnum.C, + UIntEnum.A, + UIntEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum[] e = { + LongEnum.C, + LongEnum.A, + LongEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum[] e = { + ULongEnum.C, + ULongEnum.A, + ULongEnum.B + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(e); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(e); + } + else + { + RunObjectTypeArrayTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new[] + { + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new[] + { + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new[] + { + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }; + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new[] + { + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new[] + { + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new[] + { + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Ray)) + { + var v = new[] + { + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else if (testType == typeof(Ray2D)) + { + var v = new[] + { + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + }; + + if (writeType == WriteType.WriteDirect) + { + RunTypeArrayTest(v); + } + else if (writeType == WriteType.WriteSafe) + { + RunTypeArrayTestSafe(v); + } + else + { + RunObjectTypeArrayTest(v); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + [Test] + public void TestReadingStruct() + { + RunTypeTest(GetTestStruct()); + } + + [Test] + public void TestReadingStructSafe() + { + RunTypeTestSafe(GetTestStruct()); + } + + [Test] + public void TestReadingStructAsObjectWithRegisteredTypeTableSerializer() + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct) obj); + }; + SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => + { + reader.ReadValueSafe(out TestStruct value); + obj = value; + }; + try + { + RunObjectTypeTest(GetTestStruct()); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + } + } + + [Test] + public void TestReadingStructArray() + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunTypeArrayTest(arr); + } + + [Test] + public void TestReadingStructArraySafe() + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunTypeArrayTestSafe(arr); + } + + [Test] + public void TestReadingStructArrayAsObjectWithRegisteredTypeTableSerializer() + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct) obj); + }; + SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => + { + reader.ReadValueSafe(out TestStruct value); + obj = value; + }; + try + { + TestStruct[] arr = { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }; + RunObjectTypeArrayTest(arr); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + } + } + + [Test] + public void TestReadingString() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest); + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out string result); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); + } + } + + [Test] + public void TestReadingStringSafe() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteValueSafe(valueToTest); + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadValueSafe(out string result); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); + } + } + + [Test] + public void TestReadingStringAsObject() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteObject(valueToTest); + + WriteCheckBytes(ref writer, serializedValueSize+1); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadObject(out object result, typeof(string)); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize+1); + } + } + + [Test] + public void TestReadingOneByteString() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest, true); + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out string result, true); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); + } + } + + [Test] + public void TestReadingOneByteStringSafe() + { + string valueToTest = "Hello, I am a test string!"; + + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); + FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using(writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + writer.WriteValueSafe(valueToTest, true); + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity); + + reader.ReadValueSafe(out string result, true); + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); + } + } + + [Test] + public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) + { + var random = new Random(); + var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count); + + var failMessage = $"TestReadingPartialValues failed with value {valueToTest}"; + WriteCheckBytes(ref writer, count, failMessage); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); + Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + reader.ReadPartialValue(out ulong result, count); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); + } + } + + [Test] + public unsafe void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + { + var random = new Random(); + var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + + using (writer) + { + NativeArray underlyingArray = writer.GetNativeArray(); + + Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count, 2); + var failMessage = $"TestReadingPartialValuesWithOffsets failed with value {valueToTest}"; + WriteCheckBytes(ref writer, count, failMessage); + + var reader = new FastBufferReader(writer.GetNativeArray()); + VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); + Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + mask <<= 16; + + reader.ReadPartialValue(out ulong result, count, 2); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); + } + } + + [Test] + public void TestToArray() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(requiredSize); + writer.WriteValue(testStruct); + + var reader = new FastBufferReader(writer.GetNativeArray()); + var array = reader.ToArray(); + var underlyingArray = writer.GetNativeArray(); + for(var i = 0; i < array.Length; ++i) + { + Assert.AreEqual(array[i], underlyingArray[i]); + } + } + } + + [Test] + public unsafe void TestGetUnsafePtr() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(requiredSize); + writer.WriteValue(testStruct); + var reader = new FastBufferReader(writer.GetNativeArray()); + var ptr = reader.GetUnsafePtr(); + var underlyingArrayPtr = writer.GetNativeArray().GetUnsafePtr(); + Assert.IsTrue(underlyingArrayPtr == ptr); + + var ptrAtPosition = writer.GetUnsafePtrAtCurrentPosition(); + Assert.IsTrue((byte*)underlyingArrayPtr + writer.Position == ptrAtPosition); + } + } + + [Test] + public void TestThrowingIfBoundsCheckingSkipped() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + var emptyReader = new FastBufferReader(new NativeArray(100, Allocator.Temp), 0, 0); + + using (emptyReader.GetNativeArray()) + using (writer) + { + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); + var bytes = new byte[] {0, 1, 2}; + Assert.Throws(() => { emptyReader.ReadBytes(ref bytes, bytes.Length); }); + int i = 1; + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); + Assert.Throws(() => { emptyReader.ReadValue(out bytes); }); + Assert.Throws(() => { emptyReader.ReadValue(out string s); }); + + writer.VerifyCanWrite(sizeof(int) - 1); + writer.WriteByte(1); + writer.WriteByte(2); + writer.WriteByte(3); + var reader = new FastBufferReader(writer.GetNativeArray(), 0, writer.Length); + Assert.Throws(() => { reader.ReadValue(out int i); }); + Assert.Throws(() => { reader.ReadValue(out byte b); }); + Assert.IsTrue(reader.VerifyCanRead(3)); + reader.ReadByte(out byte b); + reader.ReadByte(out b); + reader.ReadByte(out b); + Assert.Throws(() => { reader.ReadValue(out byte b); }); + } + } + + [Test] + public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + writer.VerifyCanWrite(100); + var bytes = new byte[] {0, 1, 2}; + int i = 1; + writer.WriteByte(1); + writer.WriteBytes(bytes, bytes.Length); + writer.WriteValue(i); + writer.WriteValue(bytes); + writer.WriteValue(""); + + writer.WriteByteSafe(1); + writer.WriteBytesSafe(bytes, bytes.Length); + writer.WriteValueSafe(i); + writer.WriteValueSafe(bytes); + writer.WriteValueSafe(""); + + var reader = new FastBufferReader(writer.GetNativeArray(), 0, writer.Length); + Assert.IsTrue(reader.VerifyCanRead(writer.Length)); + using (var context = reader.EnterBitwiseContext()) + { + Assert.Throws(() => { reader.ReadByte(out byte b); }); + Assert.Throws(() => { reader.ReadBytes(ref bytes, bytes.Length); }); + Assert.Throws(() => { reader.ReadValue(out i); }); + Assert.Throws(() => { reader.ReadValue(out bytes); }); + Assert.Throws(() => { reader.ReadValue(out string s); }); + + Assert.Throws(() => { reader.ReadByteSafe(out byte b); }); + Assert.Throws(() => { reader.ReadBytesSafe(ref bytes, bytes.Length); }); + Assert.Throws(() => { reader.ReadValueSafe(out i); }); + Assert.Throws(() => { reader.ReadValueSafe(out bytes); }); + Assert.Throws(() => { reader.ReadValueSafe(out string s); }); + } + } + } + + [Test] + public void TestVerifyCanReadIsRelativeToPositionAndNotAllowedReadPosition() + { + var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp)); + using (reader.GetNativeArray()) + { + reader.VerifyCanRead(100); + reader.ReadByte(out byte b); + reader.VerifyCanRead(1); + reader.ReadByte(out b); + Assert.Throws(() => { reader.ReadByte(out b); }); + } + } + + [Test] + public void TestSeeking() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.WriteByteSafe(1); + writer.WriteByteSafe(3); + writer.WriteByteSafe(2); + writer.WriteByteSafe(5); + writer.WriteByteSafe(4); + writer.WriteByteSafe(0); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.Seek(5); + reader.ReadByteSafe(out byte b); + Assert.AreEqual(reader.Position, 6); + Assert.AreEqual(reader.Length, 100); + Assert.AreEqual(0, b); + + reader.Seek(0); + reader.ReadByteSafe(out b); + Assert.AreEqual(reader.Position, 1); + Assert.AreEqual(reader.Length, 100); + Assert.AreEqual(1, b); + + reader.Seek(10); + Assert.AreEqual(reader.Position, 10); + Assert.AreEqual(reader.Length, 100); + + reader.Seek(2); + reader.ReadByteSafe(out b); + Assert.AreEqual(2, b); + + reader.Seek(1); + reader.ReadByteSafe(out b); + Assert.AreEqual(3, b); + + reader.Seek(4); + reader.ReadByteSafe(out b); + Assert.AreEqual(4, b); + + reader.Seek(3); + reader.ReadByteSafe(out b); + Assert.AreEqual(5, b); + + Assert.AreEqual(reader.Position, 4); + Assert.AreEqual(reader.Length, 100); + } + } + + private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, + NetworkObject networkObject); + private void RunGameObjectTest(GameObjectTestDelegate testCode) + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkManager.SetSingleton(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + testCode(obj, networkBehaviour, networkObject); + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + + [Test] + public void TestNetworkBehaviour() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); + + writer.WriteValue(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkBehaviourWriteSize())); + reader.ReadValue(out NetworkBehaviour result); + Assert.AreSame(result, networkBehaviour); + + } + }); + } + + [Test] + public void TestNetworkObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); + + writer.WriteValue(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkObjectWriteSize())); + reader.ReadValue(out NetworkObject result); + Assert.AreSame(result, networkObject); + } + }); + } + + [Test] + public void TestGameObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + using (writer) + { + Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); + + writer.WriteValue(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetGameObjectWriteSize())); + reader.ReadValue(out GameObject result); + Assert.AreSame(result, obj); + } + }); + } + + [Test] + public void TestNetworkBehaviourSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadValueSafe(out NetworkBehaviour result); + Assert.AreSame(result, networkBehaviour); + } + }); + } + + [Test] + public void TestNetworkObjectSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadValueSafe(out NetworkObject result); + Assert.AreSame(result, networkObject); + } + }); + } + + [Test] + public void TestGameObjectSafe() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + using (writer) + { + writer.WriteValueSafe(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadValueSafe(out GameObject result); + Assert.AreSame(result, obj); + } + }); + } + + [Test] + public void TestNetworkBehaviourAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(networkBehaviour); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadObject(out object result, typeof(NetworkBehaviour)); + Assert.AreSame(result, networkBehaviour); + } + }); + } + + [Test] + public void TestNetworkObjectAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(networkObject); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadObject(out object result, typeof(NetworkObject)); + Assert.AreSame(result, networkObject); + } + }); + } + + [Test] + public void TestGameObjectAsObject() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + // +1 for extra isNull added by WriteObject + var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj) + 1, Allocator.Temp); + using (writer) + { + writer.WriteObject(obj); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); + + var reader = new FastBufferReader(writer.GetNativeArray()); + reader.ReadObject(out object result, typeof(GameObject)); + Assert.AreSame(result, obj); + } + }); + } + + #endregion + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs.meta new file mode 100644 index 0000000000..52af796039 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2881f8138b479c34389b76687e5307ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 1685f70d66..d1776840ed 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -145,11 +145,14 @@ private void CommonChecks(ref FastBufferWriter writer, T valueToTest, int wri private unsafe void RunTypeTest(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); + var alternateWriteSize = FastBufferWriter.GetWriteSize(); + Assert.AreEqual(sizeof(T), writeSize); + Assert.AreEqual(sizeof(T), alternateWriteSize); + FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using(writer) { - Assert.AreEqual(sizeof(T), writeSize); Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -2030,7 +2033,7 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) } [Test] - public void TestNetworkBehavior() + public void TestNetworkBehaviour() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { @@ -2086,7 +2089,7 @@ public void TestGameObject() } [Test] - public void TestNetworkBehaviorSafe() + public void TestNetworkBehaviourSafe() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { @@ -2136,7 +2139,7 @@ public void TestGameObjectSafe() } [Test] - public void TestNetworkBehaviorAsObject() + public void TestNetworkBehaviourAsObject() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { From 47767371ca5592548d8847144d3fc6ba4bc370a7 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 19 Aug 2021 13:18:17 -0500 Subject: [PATCH 04/29] - More tests - Streamlined the implementations of bit-packing uints and ulongs. --- .../Runtime/Serialization/BytePacker.cs | 160 ++--- .../Runtime/Serialization/ByteUnpacker.cs | 26 +- .../Runtime/Serialization/FastBufferReader.cs | 2 +- .../Runtime/Serialization/FastBufferWriter.cs | 2 +- .../Editor/Serialization/BytePackerTests.cs | 573 +++++++++++++++++- .../Serialization/FastBufferReaderTests.cs | 12 + .../Serialization/FastBufferWriterTests.cs | 11 + 7 files changed, 644 insertions(+), 142 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index f23531c73a..3976032578 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -141,7 +141,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, public void WriteValuePacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteValuePacked(ref FastBufferWriter writer, ref TEnum value) where TEnum : unmanaged, Enum + public static unsafe void WriteValuePacked(ref FastBufferWriter writer, TEnum value) where TEnum : unmanaged, Enum { TEnum enumValue = value; switch (sizeof(TEnum)) @@ -191,6 +191,16 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByteSafe(value); + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void WriteValuePacked(ref FastBufferWriter writer, sbyte value) => writer.WriteByteSafe((byte)value); + /// /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. /// WARNING: If the value you're writing is > 2287, this will use MORE space @@ -351,18 +361,10 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Vector4 vector4 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Quaternion rotation) { - if (Mathf.Sign(rotation.w) < 0) - { - WriteValuePacked(ref writer, -rotation.x); - WriteValuePacked(ref writer, -rotation.y); - WriteValuePacked(ref writer, -rotation.z); - } - else - { - WriteValuePacked(ref writer, rotation.x); - WriteValuePacked(ref writer, rotation.y); - WriteValuePacked(ref writer, rotation.z); - } + WriteValuePacked(ref writer, rotation.x); + WriteValuePacked(ref writer, rotation.y); + WriteValuePacked(ref writer, rotation.z); + WriteValuePacked(ref writer, rotation.w); } /// @@ -393,16 +395,18 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value) { +#if DEVELOPMENT_BUILD || UNITY_EDITOR if (value >= 0b1000_0000_0000_0000) { throw new ArgumentException("BitPacked ushorts must be <= 15 bits"); } +#endif if (value <= 0b0111_1111) { if (!writer.VerifyCanWriteInternal(1)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } writer.WriteByte((byte)(value << 1)); return; @@ -410,7 +414,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value if (!writer.VerifyCanWriteInternal(2)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } writer.WriteValue((ushort)((value << 1) | 0b1)); } @@ -419,132 +423,38 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) { +#if DEVELOPMENT_BUILD || UNITY_EDITOR if (value >= 0b0100_0000_0000_0000_0000_0000_0000_0000) { throw new ArgumentException("BitPacked uints must be <= 30 bits"); } - - if (value <= 0b0011_1111) - { - if (!writer.VerifyCanWriteInternal(1)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteByte((byte)(value << 2)); - return; - } - - if (value <= 0b0011_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(2)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteValue((ushort)((value << 2) | 0b01)); - return; - } - - if (value <= 0b0011_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(3)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue(((value << 2) | 0b10), 3); - return; - } - - if (!writer.VerifyCanWriteInternal(4)) +#endif + value <<= 2; + var numBytes = BitCounter.GetUsedByteCount(value); + if (!writer.VerifyCanWriteInternal(numBytes)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } - writer.WriteValue(((value << 2) | 0b11)); + writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes); } public static void WriteValueBitPacked(ref FastBufferWriter writer, long value) => WriteValueBitPacked(ref writer, (ulong) Arithmetic.ZigZagEncode(value)); public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) { +#if DEVELOPMENT_BUILD || UNITY_EDITOR if (value >= 0b0010_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000) { throw new ArgumentException("BitPacked ulongs must be <= 61 bits"); } - - if (value <= 0b0001_1111) - { - if (!writer.VerifyCanWriteInternal(1)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteByte((byte)(value << 3)); - return; - } - - if (value <= 0b0001_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(2)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteValue((ushort)((value << 3) | 0b001)); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(3)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue((value << 3) | 0b010, 3); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(4)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WriteValue((uint)((value << 3) | 0b011)); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(5)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue((value << 3) | 0b100, 5); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(6)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue((value << 3) | 0b101, 6); - return; - } - - if (value <= 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) - { - if (!writer.VerifyCanWriteInternal(7)) - { - throw new OverflowException("Reading past the end of the buffer"); - } - writer.WritePartialValue((value << 3) | 0b110, 7); - return; - } - - if (!writer.VerifyCanWriteInternal(8)) +#endif + value <<= 3; + var numBytes = BitCounter.GetUsedByteCount(value); + if (!writer.VerifyCanWriteInternal(numBytes)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } - writer.WriteValue((value << 3) | 0b111); + writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes); } #endif #endregion @@ -567,7 +477,7 @@ private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) if (!writer.VerifyCanWriteInternal(writeBytes+1)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); @@ -593,7 +503,7 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) if (!writer.VerifyCanWriteInternal(writeBytes+1)) { - throw new OverflowException("Reading past the end of the buffer"); + throw new OverflowException("Writing past the end of the buffer"); } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index f63a843954..5ac013acec 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -193,8 +193,22 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ReadValuePacked(ref FastBufferReader reader, out byte value) => reader.ReadByte(out value); - + public static void ReadValuePacked(ref FastBufferReader reader, out byte value) => reader.ReadByteSafe(out value); + + /// + /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// WARNING: If the value you're writing is > 2287, this will use MORE space + /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. + /// Only use this if you're certain your value will be small. + /// + /// Value to write + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ReadValuePacked(ref FastBufferReader reader, out sbyte value) + { + reader.ReadByteSafe(out byte byteVal); + value = (sbyte) byteVal; + } + /// /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. /// WARNING: If the value you're writing is > 2287, this will use MORE space @@ -203,7 +217,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ReadValuePacked(ref FastBufferReader reader, out bool value) => reader.ReadValue(out value); + public static void ReadValuePacked(ref FastBufferReader reader, out bool value) => reader.ReadValueSafe(out value); /// @@ -386,11 +400,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Quaternion r ReadValuePacked(ref reader, out rotation.x); ReadValuePacked(ref reader, out rotation.y); ReadValuePacked(ref reader, out rotation.z); - - // numerical precision issues can make the remainder very slightly negative. - // In this case, use 0 for w as, otherwise, w would be NaN. - float remainder = 1f - Mathf.Pow(rotation.x, 2) - Mathf.Pow(rotation.y, 2) - Mathf.Pow(rotation.z, 2); - rotation.w = (remainder > 0f) ? Mathf.Sqrt(remainder) : 0.0f; + ReadValuePacked(ref reader, out rotation.w); } /// diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index a39af22973..be07e4980b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -42,7 +42,7 @@ internal void CommitBitwiseReads(int amount) } } - private InternalData m_InternalData; + internal InternalData m_InternalData; public unsafe FastBufferReader(NativeArray buffer, int offset = 0, int length = -1) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 1c3e5e002f..0658676296 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -46,7 +46,7 @@ internal void CommitBitwiseWrites(int amount) } } - private InternalData m_InternalData; + internal InternalData m_InternalData; public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 3a31a778b7..d11744c2b0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -1,15 +1,100 @@ using System; +using System.Linq; +using System.Reflection; using NUnit.Framework; using Unity.Collections; using Unity.Multiplayer.Netcode; +using UnityEngine; +using Random = System.Random; namespace Unity.Netcode.EditorTests { public class BytePackerTests { + #region Test Types + + private enum ByteEnum : byte + { + A, + B, + C + } + + private enum SByteEnum : sbyte + { + A, + B, + C + } + + private enum ShortEnum : short + { + A, + B, + C + } + + private enum UShortEnum : ushort + { + A, + B, + C + } + + private enum IntEnum + { + A, + B, + C + } + + private enum UIntEnum : uint + { + A, + B, + C + } + + private enum LongEnum : long + { + A, + B, + C + } + + private enum ULongEnum : ulong + { + A, + B, + C + } + + private struct TestStruct + { + public byte a; + public short b; + public ushort c; + public int d; + public uint e; + public long f; + public ulong g; + public bool h; + public char i; + public float j; + public double k; + } + + public enum WriteType + { + WriteDirect, + WriteAsObject + } + + #endregion + private void CheckUnsignedPackedSize64(ref FastBufferWriter writer, ulong value) { - + if (value <= 240) { Assert.AreEqual(1, writer.Position); @@ -30,10 +115,10 @@ private void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value ByteUnpacker.ReadValuePacked(ref reader, out ulong readValue); Assert.AreEqual(readValue, value); } - + private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) { - + if (value <= 240) { Assert.AreEqual(1, writer.Position); @@ -54,11 +139,11 @@ private void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) ByteUnpacker.ReadValuePacked(ref reader, out uint readValue); Assert.AreEqual(readValue, value); } - + private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) { ulong asUlong = Arithmetic.ZigZagEncode(value); - + if (asUlong <= 240) { Assert.AreEqual(1, writer.Position); @@ -79,11 +164,11 @@ private void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) ByteUnpacker.ReadValuePacked(ref reader, out long readValue); Assert.AreEqual(readValue, value); } - + private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) { ulong asUlong = Arithmetic.ZigZagEncode(value); - + if (asUlong <= 240) { Assert.AreEqual(1, writer.Position); @@ -105,6 +190,59 @@ private void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) Assert.AreEqual(readValue, value); } + private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T: unmanaged + { + byte* asBytePointer = (byte*) &value; + byte* otherBytePointer = (byte*) &otherValue; + for (var i = 0; i < sizeof(T); ++i) + { + Assert.AreEqual(asBytePointer[i], otherBytePointer[i]); + } + } + + private unsafe void RunTypeTest(T value) where T : unmanaged + { + FastBufferWriter writer = new FastBufferWriter(sizeof(T)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteValuePacked(ref writer, (dynamic)value); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + + T outVal = new T(); + MethodInfo method; + if (value is Enum) + { + method = typeof(ByteUnpacker).GetMethods().Single(x => x.Name == "ReadValuePacked" && x.IsGenericMethodDefinition) + .MakeGenericMethod(typeof(T)); + } + else + { + method = typeof (ByteUnpacker).GetMethod("ReadValuePacked", new[] {typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType()}); + } + object[] args = {reader, outVal}; + method.Invoke(null, args); + outVal = (T) args[1]; + Assert.AreEqual(value, outVal); + VerifyBytewiseEquality(value, outVal); + } + } + + private unsafe void RunObjectTypeTest(T value) where T : unmanaged + { + FastBufferWriter writer = new FastBufferWriter(sizeof(T)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteObjectPacked(ref writer, value); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); + Assert.AreEqual(value, outVal); + VerifyBytewiseEquality(value, (T)outVal); + } + } + + + [Test] public void TestPacking64BitsUnsigned() { @@ -676,5 +814,426 @@ public void TestBitPacking14BitsSigned() } } } + + [Test] + public void TestPackingBasicTypes( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + Type testType, + [Values] WriteType writeType) + { + var random = new Random(); + + if (testType == typeof(byte)) + { + byte b = (byte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(b); + } + else + { + RunObjectTypeTest(b); + } + } + else if (testType == typeof(sbyte)) + { + sbyte sb = (sbyte) random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(sb); + } + else + { + RunObjectTypeTest(sb); + } + } + else if (testType == typeof(short)) + { + short s = (short)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(s); + } + else + { + RunObjectTypeTest(s); + } + } + else if (testType == typeof(ushort)) + { + ushort us = (ushort)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(us); + } + else + { + RunObjectTypeTest(us); + } + } + else if (testType == typeof(int)) + { + int i = random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(i); + } + else + { + RunObjectTypeTest(i); + } + } + else if (testType == typeof(uint)) + { + uint ui = (uint)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ui); + } + else + { + RunObjectTypeTest(ui); + } + } + else if (testType == typeof(long)) + { + long l = ((long)random.Next() << 32) + random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(l); + } + else + { + RunObjectTypeTest(l); + } + } + else if (testType == typeof(ulong)) + { + ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(ul); + } + else + { + RunObjectTypeTest(ul); + } + } + else if (testType == typeof(bool)) + { + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(true); + } + else + { + RunObjectTypeTest(true); + } + } + else if (testType == typeof(char)) + { + char c = 'a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else + { + RunObjectTypeTest(c); + } + + c = '\u263a'; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(c); + } + else + { + RunObjectTypeTest(c); + } + } + else if (testType == typeof(float)) + { + float f = (float)random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(f); + } + else + { + RunObjectTypeTest(f); + } + } + else if (testType == typeof(double)) + { + double d = random.NextDouble(); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(d); + } + else + { + RunObjectTypeTest(d); + } + } + else if (testType == typeof(ByteEnum)) + { + ByteEnum e = ByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(SByteEnum)) + { + SByteEnum e = SByteEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ShortEnum)) + { + ShortEnum e = ShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UShortEnum)) + { + UShortEnum e = UShortEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(IntEnum)) + { + IntEnum e = IntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(UIntEnum)) + { + UIntEnum e = UIntEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(LongEnum)) + { + LongEnum e = LongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(ULongEnum)) + { + ULongEnum e = ULongEnum.C; + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(e); + } + else + { + RunObjectTypeTest(e); + } + } + else if (testType == typeof(Vector2)) + { + var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector3)) + { + var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Vector4)) + { + var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Quaternion)) + { + var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color)) + { + var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Color32)) + { + var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); + if (writeType == WriteType.WriteDirect) + { + RunTypeTest(v); + } + else + { + RunObjectTypeTest(v); + } + } + else if (testType == typeof(Ray)) + { + // Rays need special handling on the equality checks because the constructor normalizes direction + // Which can cause slight variations in the result + var v = new Ray( + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + unsafe + { + FastBufferWriter writer = new FastBufferWriter(sizeof(Ray)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteValuePacked(ref writer, v); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out Ray outVal); + Assert.AreEqual(v.origin, outVal.origin); + Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); + Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + Assert.AreEqual(v.direction.z, outVal.direction.z, 0.00001); + } + } + } + else + { + unsafe + { + FastBufferWriter writer = new FastBufferWriter(sizeof(Ray)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteObjectPacked(ref writer, v); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); + Assert.AreEqual(v.origin, ((Ray)outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray)outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray)outVal).direction.y, 0.00001); + Assert.AreEqual(v.direction.z, ((Ray)outVal).direction.z, 0.00001); + } + } + } + } + else if (testType == typeof(Ray2D)) + { + // Rays need special handling on the equality checks because the constructor normalizes direction + // Which can cause slight variations in the result + var v = new Ray2D( + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble())); + if (writeType == WriteType.WriteDirect) + { + unsafe + { + FastBufferWriter writer = new FastBufferWriter(sizeof(Ray2D)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteValuePacked(ref writer, v); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadValuePacked(ref reader, out Ray2D outVal); + Assert.AreEqual(v.origin, outVal.origin); + Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); + Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + } + } + } + else + { + unsafe + { + FastBufferWriter writer = new FastBufferWriter(sizeof(Ray2D)*2, Allocator.Temp); + using (writer) + { + BytePacker.WriteObjectPacked(ref writer, v); + FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); + Assert.AreEqual(v.origin, ((Ray2D)outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray2D)outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray2D)outVal).direction.y, 0.00001); + } + } + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 97f51b5468..49e423d04f 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -2132,6 +2132,18 @@ public void TestGameObjectAsObject() }); } + [Test] + public void TestVerifyInternalDoesntReduceAllowedWritePoint() + { + var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp)); + using (reader.GetNativeArray()) + { + reader.VerifyCanRead(25); + reader.VerifyCanReadInternal(5); + Assert.AreEqual(reader.m_InternalData.AllowedReadMark, 25); + } + } + #endregion } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index d1776840ed..2291627605 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -2194,6 +2194,17 @@ public void TestGameObjectAsObject() }); } + [Test] + public void TestVerifyInternalDoesntReduceAllowedWritePoint() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.VerifyCanWrite(25); + writer.VerifyCanWriteInternal(5); + Assert.AreEqual(writer.m_InternalData.AllowedWriteMark, 25); + } + } #endregion } } \ No newline at end of file From f24515097f994ce69a8ce1395b697d25d4fa3400 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 19 Aug 2021 17:13:27 -0500 Subject: [PATCH 05/29] - Removed NativeArray from FastBufferReader and FastBufferWriter, replacing with byte* allocated directly through UnsafeUtility.Malloc() (same function used under the hood by NativeArray). This is required in order to be able to store pointers to FastBufferReader and FastBufferWriter in order to be able to wrap them with BufferSerializer - NativeArray contains a managed variable in it that disallows taking a pointer to it. And since FBW and FBR are structs, pointers are the only way to wrap them "by reference" in another struct - ref fields aren't allowed even inside ref structs. --- .../Runtime/Serialization/BitReader.cs | 82 ++- .../Runtime/Serialization/BitWriter.cs | 94 +++- .../Runtime/Serialization/FastBufferReader.cs | 268 +++++----- .../Runtime/Serialization/FastBufferWriter.cs | 262 ++++------ .../Editor/Serialization/BitReaderTests.cs | 180 ++++--- .../Editor/Serialization/BitWriterTests.cs | 12 +- .../Editor/Serialization/BytePackerTests.cs | 309 ++++++----- .../Serialization/FastBufferReaderTests.cs | 490 ++++++++++-------- .../Serialization/FastBufferWriterTests.cs | 148 +++--- 9 files changed, 978 insertions(+), 867 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index ef383d0adf..a194e228ad 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -8,37 +8,69 @@ namespace Unity.Multiplayer.Netcode { public ref struct BitReader { - private unsafe FastBufferReader.InternalData* m_InternalData; + private unsafe FastBufferReader* m_Reader; private unsafe byte* m_BufferPointer; private int m_Position; private const int BITS_PER_BYTE = 8; + private int m_BitPosition; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + private int m_AllowedBitwiseReadMark; +#endif + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool BitAligned() + { + return (m_BitPosition & 7) == 0; + } - internal unsafe BitReader(ref FastBufferReader.InternalData internalData) + internal unsafe BitReader(ref FastBufferReader reader) { - fixed (FastBufferReader.InternalData* internalDataPtr = &internalData) + fixed (FastBufferReader* readerPtr = &reader) { - m_InternalData = internalDataPtr; + m_Reader = readerPtr; } - m_BufferPointer = internalData.BufferPointer + internalData.Position; - m_Position = internalData.Position; - m_InternalData->BitPosition = 0; + m_BufferPointer = m_Reader->m_BufferPointer + m_Reader->Position; + m_Position = m_Reader->Position; + m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData->AllowedBitwiseReadMark = (m_InternalData->AllowedReadMark - m_InternalData->Position) * BITS_PER_BYTE; + m_AllowedBitwiseReadMark = (m_Reader->m_AllowedReadMark - m_Position) * BITS_PER_BYTE; #endif } public unsafe void Dispose() { - var bytesWritten = m_InternalData->BitPosition >> 3; - if (!m_InternalData->BitAligned()) + var bytesWritten = m_BitPosition >> 3; + if (!BitAligned()) { // Accounting for the partial read ++bytesWritten; } - m_InternalData->CommitBitwiseReads(bytesWritten); + m_Reader->CommitBitwiseReads(bytesWritten); + } + + public unsafe bool VerifyCanReadBits(int bitCount) + { + var newBitPosition = m_BitPosition + bitCount; + var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; + if ((newBitPosition & 7) != 0) + { + // Accounting for the partial read + ++totalBytesWrittenInBitwiseContext; + } + + if (m_Reader->m_Position + totalBytesWrittenInBitwiseContext > m_Reader->m_Length) + { + return false; + } +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_AllowedBitwiseReadMark = newBitPosition; +#endif + return true; } /// @@ -59,8 +91,8 @@ public unsafe void ReadBits(out ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); } - int checkPos = (m_InternalData->BitPosition + bitCount); - if (checkPos > m_InternalData->AllowedBitwiseReadMark) + int checkPos = (m_BitPosition + bitCount); + if (checkPos > m_AllowedBitwiseReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); } @@ -69,7 +101,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &val; - if (m_InternalData->BitAligned()) + if (BitAligned()) { if (wholeBytes != 0) { @@ -96,8 +128,8 @@ public unsafe void ReadBits(out ulong value, int bitCount) public unsafe void ReadBits(out byte value, int bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_InternalData->BitPosition + bitCount); - if (checkPos > m_InternalData->AllowedBitwiseReadMark) + int checkPos = (m_BitPosition + bitCount); + if (checkPos > m_AllowedBitwiseReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); } @@ -113,17 +145,17 @@ public unsafe void ReadBits(out byte value, int bitCount) public unsafe void ReadBit(out bool bit) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_InternalData->BitPosition + 1); - if (checkPos > m_InternalData->AllowedBitwiseReadMark) + int checkPos = (m_BitPosition + 1); + if (checkPos > m_AllowedBitwiseReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); } #endif - int offset = m_InternalData->BitPosition & 7; - int pos = m_InternalData->BitPosition >> 3; + int offset = m_BitPosition & 7; + int pos = m_BitPosition >> 3; bit = (m_BufferPointer[pos] & (1 << offset)) != 0; - ++m_InternalData->BitPosition; + ++m_BitPosition; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -171,7 +203,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB break; } - m_InternalData->BitPosition += bytesToRead * BITS_PER_BYTE; + m_BitPosition += bytesToRead * BITS_PER_BYTE; value = val; } @@ -206,11 +238,11 @@ private byte ReadByteBits(int bitCount) [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void ReadMisaligned(out byte value) { - int off = (int)(m_InternalData->BitPosition & 7); - int pos = m_InternalData->BitPosition >> 3; + int off = (int)(m_BitPosition & 7); + int pos = m_BitPosition >> 3; int shift1 = 8 - off; - value = (byte)((m_BufferPointer[(int)pos] >> shift1) | (m_BufferPointer[(int)(m_InternalData->BitPosition += 8) >> 3] << shift1)); + value = (byte)((m_BufferPointer[(int)pos] >> shift1) | (m_BufferPointer[(int)(m_BitPosition += 8) >> 3] << shift1)); } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index f60fabc26d..b941d36f39 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,43 +1,83 @@ using System; using System.Runtime.CompilerServices; -using Mono.Cecil; using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { public ref struct BitWriter { - private unsafe FastBufferWriter.InternalData* m_InternalData; + private unsafe FastBufferWriter* m_Writer; private unsafe byte* m_BufferPointer; private int m_Position; + internal int m_BitPosition; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + internal int m_AllowedBitwiseWriteMark; +#endif private const int BITS_PER_BYTE = 8; + /// + /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool BitAligned() + { + return (m_BitPosition & 7) == 0; + } + - internal unsafe BitWriter(ref FastBufferWriter.InternalData internalData) + internal unsafe BitWriter(ref FastBufferWriter writer) { - fixed (FastBufferWriter.InternalData* internalDataPtr = &internalData) + fixed (FastBufferWriter* internalDataPtr = &writer) { - m_InternalData = internalDataPtr; + m_Writer = internalDataPtr; } - m_BufferPointer = internalData.BufferPointer + internalData.Position; - m_Position = internalData.Position; - m_InternalData->BitPosition = 0; + m_BufferPointer = writer.m_BufferPointer + writer.m_Position; + m_Position = writer.m_Position; + m_BitPosition = 0; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_AllowedBitwiseWriteMark = (m_Writer->m_AllowedWriteMark - m_Writer->Position) * BITS_PER_BYTE; +#endif + } + + public unsafe bool VerifyCanWriteBits(int bitCount) + { + var newBitPosition = m_BitPosition + bitCount; + var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; + if ((newBitPosition & 7) != 0) + { + // Accounting for the partial write + ++totalBytesWrittenInBitwiseContext; + } + + if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer->m_Capacity) + { + if (m_Writer->m_Capacity < m_Writer->m_MaxCapacity) + { + m_Writer->Grow(); + m_BufferPointer = m_Writer->m_BufferPointer; + } + else + { + return false; + } + } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData->AllowedBitwiseWriteMark = (m_InternalData->AllowedWriteMark - m_InternalData->Position) * BITS_PER_BYTE; + m_AllowedBitwiseWriteMark = newBitPosition; #endif + return true; } public unsafe void Dispose() { - var bytesWritten = m_InternalData->BitPosition >> 3; - if (!m_InternalData->BitAligned()) + var bytesWritten = m_BitPosition >> 3; + if (!BitAligned()) { // Accounting for the partial write ++bytesWritten; } - m_InternalData->CommitBitwiseWrites(bytesWritten); + m_Writer->CommitBitwiseWrites(bytesWritten); } /// @@ -58,8 +98,8 @@ public unsafe void WriteBits(ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); } - int checkPos = (m_InternalData->BitPosition + bitCount); - if (checkPos > m_InternalData->AllowedBitwiseWriteMark) + int checkPos = (m_BitPosition + bitCount); + if (checkPos > m_AllowedBitwiseWriteMark) { throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } @@ -67,7 +107,7 @@ public unsafe void WriteBits(ulong value, int bitCount) int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &value; - if (m_InternalData->BitAligned()) + if (BitAligned()) { if (wholeBytes != 0) { @@ -93,11 +133,11 @@ public unsafe void WriteBits(ulong value, int bitCount) /// /// Value to get bits from. /// Amount of bits to write. - public unsafe void WriteBits(byte value, int bitCount) + public void WriteBits(byte value, int bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_InternalData->BitPosition + bitCount); - if (checkPos > m_InternalData->AllowedBitwiseWriteMark) + int checkPos = (m_BitPosition + bitCount); + if (checkPos > m_AllowedBitwiseWriteMark) { throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } @@ -117,16 +157,16 @@ public unsafe void WriteBits(byte value, int bitCount) public unsafe void WriteBit(bool bit) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_InternalData->BitPosition + 1); - if (checkPos > m_InternalData->AllowedBitwiseWriteMark) + int checkPos = (m_BitPosition + 1); + if (checkPos > m_AllowedBitwiseWriteMark) { throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif - int offset = m_InternalData->BitPosition & 7; - int pos = m_InternalData->BitPosition >> 3; - ++m_InternalData->BitPosition; + int offset = m_BitPosition & 7; + int pos = m_BitPosition >> 3; + ++m_BitPosition; m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset))); } @@ -175,19 +215,19 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy break; } - m_InternalData->BitPosition += bytesToWrite * BITS_PER_BYTE; + m_BitPosition += bytesToWrite * BITS_PER_BYTE; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void WriteMisaligned(byte value) { - int off = (int)(m_InternalData->BitPosition & 7); - int pos = m_InternalData->BitPosition >> 3; + int off = m_BitPosition & 7; + int pos = m_BitPosition >> 3; int shift1 = 8 - off; m_BufferPointer[pos + 1] = (byte)((m_BufferPointer[pos + 1] & (0xFF << off)) | (value >> shift1)); m_BufferPointer[pos] = (byte)((m_BufferPointer[pos] & (0xFF >> shift1)) | (value << off)); - m_InternalData->BitPosition += 8; + m_BitPosition += 8; } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index be07e4980b..a46f6967a1 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.CompilerServices; -using System.Text; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; @@ -8,110 +7,160 @@ namespace Unity.Multiplayer.Netcode { - public struct FastBufferReader + public struct FastBufferReader : IDisposable { - private NativeArray m_buffer; + internal unsafe byte* m_BufferPointer; + internal int m_Position; + internal int m_Length; + internal Allocator m_Allocator; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + internal int m_AllowedReadMark; + internal bool m_InBitwiseContext; +#endif + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CommitBitwiseReads(int amount) + { + m_Position += amount; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_InBitwiseContext = false; +#endif + } - internal struct InternalData + public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, int length = -1, int offset = 0) { - public unsafe byte* BufferPointer; - public int Position; - public int Length; - public int BitPosition; + m_Length = Math.Max(1, length == -1 ? buffer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr()+offset, m_Length); + m_BufferPointer = (byte*)bufferPtr; + m_Position = offset; + m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - public int AllowedReadMark; - public int AllowedBitwiseReadMark; - public bool InBitwiseContext; + m_AllowedReadMark = 0; + m_InBitwiseContext = false; #endif - /// - /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool BitAligned() + } + + public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, int length = -1, int offset = 0) + { + m_Length = Math.Max(1, length == -1 ? buffer.Count : length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + fixed (byte* data = buffer.Array) { - return (BitPosition & 7) == 0; + UnsafeUtility.MemCpy(bufferPtr, data+offset, m_Length); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CommitBitwiseReads(int amount) - { - Position += amount; + m_BufferPointer = (byte*) bufferPtr; + m_Position = 0; + m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - InBitwiseContext = false; + m_AllowedReadMark = 0; + m_InBitwiseContext = false; #endif - } } - - internal InternalData m_InternalData; - - public unsafe FastBufferReader(NativeArray buffer, int offset = 0, int length = -1) + + public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = -1, int offset = 0) { - m_buffer = buffer; - m_InternalData = new InternalData + m_Length = Math.Max(1, length == -1 ? buffer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + fixed (byte* data = buffer) { - BufferPointer = (byte*) m_buffer.GetUnsafePtr(), - Position = offset, - Length = length == -1 ? buffer.Length : length, + UnsafeUtility.MemCpy(bufferPtr, data+offset, m_Length); + } + m_BufferPointer = (byte*) bufferPtr; + m_Position = 0; + m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - AllowedReadMark = 0, - InBitwiseContext = false, + m_AllowedReadMark = 0; + m_InBitwiseContext = false; #endif - }; + } + + public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0) + { + m_Length = Math.Max(1, length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, buffer + offset, m_Length); + m_BufferPointer = (byte*) bufferPtr; + m_Position = 0; + m_Allocator = allocator; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_AllowedReadMark = 0; + m_InBitwiseContext = false; +#endif + } + + public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, int length = -1, int offset = 0) + { + m_Length = Math.Max(1, length == -1 ? writer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, writer.GetUnsafePtr() + offset, m_Length); + m_BufferPointer = (byte*) bufferPtr; + m_Position = 0; + m_Allocator = allocator; +#if DEVELOPMENT_BUILD || UNITY_EDITOR + m_AllowedReadMark = 0; + m_InBitwiseContext = false; +#endif + } + + public unsafe void Dispose() + { + UnsafeUtility.Free(m_BufferPointer, m_Allocator); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Seek(int where) { - m_InternalData.Position = where; + m_Position = Math.Min(Length, where); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void MarkBytesRead(int amount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + amount > m_InternalData.AllowedReadMark) + if (m_Position + amount > m_AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - m_InternalData.Position += amount; + m_Position += amount; } - public unsafe BitReader EnterBitwiseContext() + public BitReader EnterBitwiseContext() { #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.InBitwiseContext = true; + m_InBitwiseContext = true; #endif - return new BitReader(ref m_InternalData); + return new BitReader(ref this); } - public int Position => m_InternalData.Position; - public int Length => m_InternalData.Length; + public int Position => m_Position; + public int Length => m_Length; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool VerifyCanReadInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_InternalData.Position + bytes > m_InternalData.Length) + if (m_Position + bytes > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.Position + bytes > m_InternalData.AllowedReadMark) + if (m_Position + bytes > m_AllowedReadMark) { - m_InternalData.AllowedReadMark = m_InternalData.Position + bytes; + m_AllowedReadMark = m_Position + bytes; } #endif return true; @@ -121,18 +170,18 @@ internal bool VerifyCanReadInternal(int bytes) public bool VerifyCanRead(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_InternalData.Position + bytes > m_InternalData.Length) + if (m_Position + bytes > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedReadMark = m_InternalData.Position + bytes; + m_AllowedReadMark = m_Position + bytes; #endif return true; } @@ -141,71 +190,44 @@ public bool VerifyCanRead(int bytes) public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif int len = sizeof(T); - if (m_InternalData.Position + len > m_InternalData.Length) - { - return false; - } -#if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedReadMark = m_InternalData.Position + len; -#endif - return true; - } - public bool VerifyCanReadBits(int bitCount) - { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (!m_InternalData.InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferReader in bitwise mode while not in a bitwise context."); - } -#endif - var newBitPosition = m_InternalData.BitPosition + bitCount; - var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; - if ((newBitPosition & 7) != 0) - { - // Accounting for the partial read - ++totalBytesWrittenInBitwiseContext; - } - - if (m_InternalData.Position + totalBytesWrittenInBitwiseContext > m_InternalData.Length) + if (m_Position + len > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedBitwiseReadMark = newBitPosition; + m_AllowedReadMark = m_Position + len; #endif return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NativeArray GetNativeArray() + public unsafe byte[] ToArray() { - return m_buffer; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte[] ToArray() - { - return m_buffer.ToArray(); + byte[] ret = new byte[Length]; + fixed (byte* b = ret) + { + UnsafeUtility.MemCpy(b, m_BufferPointer, Length); + } + return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { - return m_InternalData.BufferPointer; + return m_BufferPointer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { - return m_InternalData.BufferPointer + m_InternalData.Position; + return m_BufferPointer + m_Position; } /// @@ -459,11 +481,8 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - if (oneByteChars) - { - ReadByte(out byte b); - native[i] = (char) b; - } + ReadByte(out byte b); + native[i] = (char) b; } } else @@ -485,7 +504,7 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -511,11 +530,8 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - if (oneByteChars) - { - ReadByte(out byte b); - native[i] = (char) b; - } + ReadByte(out byte b); + native[i] = (char) b; } } else @@ -555,7 +571,7 @@ public unsafe void ReadValue(out T[] array) where T: unmanaged public unsafe void ReadValueSafe(out T[] array) where T: unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -588,12 +604,12 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB // in all builds - editor, mono, and ILCPP #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + bytesToRead > m_InternalData.AllowedReadMark) + if (m_Position + bytesToRead > m_AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } @@ -601,7 +617,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; - byte* bufferPointer = m_InternalData.BufferPointer + m_InternalData.Position; + byte* bufferPointer = m_BufferPointer + m_Position; switch (bytesToRead) { case 1: @@ -638,7 +654,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB break; } - m_InternalData.Position += bytesToRead; + m_Position += bytesToRead; value = val; } @@ -650,17 +666,17 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB public unsafe void ReadByte(out byte value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + 1 > m_InternalData.AllowedReadMark) + if (m_Position + 1 > m_AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - value = m_InternalData.BufferPointer[m_InternalData.Position++]; + value = m_BufferPointer[m_Position++]; } /// @@ -674,7 +690,7 @@ public unsafe void ReadByte(out byte value) public unsafe void ReadByteSafe(out byte value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -685,7 +701,7 @@ public unsafe void ReadByteSafe(out byte value) { throw new OverflowException("Reading past the end of the buffer"); } - value = m_InternalData.BufferPointer[m_InternalData.Position++]; + value = m_BufferPointer[m_Position++]; } /// @@ -698,18 +714,18 @@ public unsafe void ReadByteSafe(out byte value) public unsafe void ReadBytes(byte* value, int size, int offset = 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + size > m_InternalData.AllowedReadMark) + if (m_Position + size > m_AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - UnsafeUtility.MemCpy(value + offset, (m_InternalData.BufferPointer + m_InternalData.Position), size); - m_InternalData.Position += size; + UnsafeUtility.MemCpy(value + offset, (m_BufferPointer + m_Position), size); + m_Position += size; } /// @@ -725,7 +741,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -736,8 +752,8 @@ public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) { throw new OverflowException("Writing past the end of the buffer"); } - UnsafeUtility.MemCpy(value + offset, (m_InternalData.BufferPointer + m_InternalData.Position), size); - m_InternalData.Position += size; + UnsafeUtility.MemCpy(value + offset, (m_BufferPointer + m_Position), size); + m_Position += size; } /// @@ -785,20 +801,20 @@ public unsafe void ReadValue(out T value) where T : unmanaged int len = sizeof(T); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + len > m_InternalData.AllowedReadMark) + if (m_Position + len > m_AllowedReadMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + T* pointer = (T*)(m_BufferPointer+m_Position); value = *pointer; - m_InternalData.Position += len; + m_Position += len; } /// @@ -816,7 +832,7 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged int len = sizeof(T); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); @@ -828,9 +844,9 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged throw new OverflowException("Writing past the end of the buffer"); } - T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + T* pointer = (T*)(m_BufferPointer+m_Position); value = *pointer; - m_InternalData.Position += len; + m_Position += len; } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 0658676296..6d3df83614 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -9,71 +9,48 @@ namespace Unity.Multiplayer.Netcode { public struct FastBufferWriter : IDisposable { - private NativeArray m_buffer; - - internal struct InternalData - { - public unsafe byte* BufferPointer; - public int Position; - public int Length; - public int Capacity; - public int MaxCapacity; - public Allocator Allocator; - public int BitPosition; + internal unsafe byte* m_BufferPointer; + internal int m_Position; + internal int m_Length; + internal int m_Capacity; + internal int m_MaxCapacity; + internal Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - public int AllowedWriteMark; - public int AllowedBitwiseWriteMark; - public bool InBitwiseContext; + internal int m_AllowedWriteMark; + internal bool m_InBitwiseContext; #endif - - /// - /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool BitAligned() - { - return (BitPosition & 7) == 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CommitBitwiseWrites(int amount) - { - Position += amount; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CommitBitwiseWrites(int amount) + { + m_Position += amount; #if DEVELOPMENT_BUILD || UNITY_EDITOR - InBitwiseContext = false; + m_InBitwiseContext = false; #endif - } } - internal InternalData m_InternalData; - public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) { + void* buffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR - var buffer = new NativeArray(size, allocator, NativeArrayOptions.ClearMemory); -#else - var buffer = new NativeArray(size, allocator, NativeArrayOptions.UninitializedMemory); + UnsafeUtility.MemSet(buffer, 0, size); #endif - m_buffer = buffer; - m_InternalData = new InternalData - { - BufferPointer = (byte*) m_buffer.GetUnsafePtr(), - Position = 0, - Length = 0, - Capacity = buffer.Length, - Allocator = allocator, - MaxCapacity = maxSize == -1 ? size : maxSize, + m_BufferPointer = (byte*)buffer; + m_Position = 0; + m_Length = 0; + m_Capacity = size; + m_Allocator = allocator; + m_MaxCapacity = maxSize == -1 ? size : maxSize; #if DEVELOPMENT_BUILD || UNITY_EDITOR - AllowedWriteMark = 0, - InBitwiseContext = false, + m_AllowedWriteMark = 0; + m_InBitwiseContext = false; #endif - }; } - public void Dispose() + public unsafe void Dispose() { - m_buffer.Dispose(); + UnsafeUtility.Free(m_BufferPointer, m_Allocator); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -89,11 +66,12 @@ public void Seek(int where) // because position increases, and if we seek backward, length remembers // the position it was in. // Seeking forward will not update the length. - if (m_InternalData.Position > m_InternalData.Length && where < m_InternalData.Position) + where = Math.Min(where, m_Capacity); + if (m_Position > m_Length && where < m_Position) { - m_InternalData.Length = m_InternalData.Position; + m_Length = m_Position; } - m_InternalData.Position = where; + m_Position = where; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -104,55 +82,54 @@ public void Truncate(int where = -1) where = Position; } - if (m_InternalData.Position > where) + if (m_Position > where) { - m_InternalData.Position = where; + m_Position = where; } - if(m_InternalData.Length > where) + if(m_Length > where) { - m_InternalData.Length = where; + m_Length = where; } } - public unsafe BitWriter EnterBitwiseContext() + public BitWriter EnterBitwiseContext() { #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.InBitwiseContext = true; + m_InBitwiseContext = true; #endif - return new BitWriter(ref m_InternalData); + return new BitWriter(ref this); } - public int Position => m_InternalData.Position; - public int Capacity => m_InternalData.Capacity; - public int Length => m_InternalData.Position > m_InternalData.Length ? m_InternalData.Position : m_InternalData.Length; + public int Position => m_Position; + public int Capacity => m_Capacity; + public int Length => m_Position > m_Length ? m_Position : m_Length; - private unsafe void Grow() + internal unsafe void Grow() { + var newSize = Math.Min(m_Capacity * 2, m_MaxCapacity); + void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR - var buffer = new NativeArray(Math.Min(m_InternalData.Capacity * 2, m_InternalData.MaxCapacity), m_InternalData.Allocator, NativeArrayOptions.ClearMemory); -#else - var buffer = new NativeArray(m_InternalData.Capacity * 2, m_InternalData.Allocator, NativeArrayOptions.UninitializedMemory); + UnsafeUtility.MemSet(buffer, 0, newSize); #endif - UnsafeUtility.MemCpy(buffer.GetUnsafePtr(), m_InternalData.BufferPointer, Length); - m_buffer.Dispose(); - m_buffer = buffer; - m_InternalData.BufferPointer = (byte*) m_buffer.GetUnsafePtr(); - m_InternalData.Capacity = buffer.Length; + UnsafeUtility.MemCpy(buffer, m_BufferPointer, Length); + UnsafeUtility.Free(m_BufferPointer, m_Allocator); + m_BufferPointer = (byte*)buffer; + m_Capacity = newSize; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool VerifyCanWrite(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_InternalData.Position + bytes > m_InternalData.Capacity) + if (m_Position + bytes > m_Capacity) { - if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + if (m_Capacity < m_MaxCapacity) { Grow(); } @@ -162,7 +139,7 @@ public bool VerifyCanWrite(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedWriteMark = m_InternalData.Position + bytes; + m_AllowedWriteMark = m_Position + bytes; #endif return true; } @@ -171,15 +148,15 @@ public bool VerifyCanWrite(int bytes) public bool VerifyCanWriteInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_InternalData.Position + bytes > m_InternalData.Capacity) + if (m_Position + bytes > m_Capacity) { - if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + if (m_Capacity < m_MaxCapacity) { Grow(); } @@ -189,9 +166,9 @@ public bool VerifyCanWriteInternal(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.Position + bytes > m_InternalData.AllowedWriteMark) + if (m_Position + bytes > m_AllowedWriteMark) { - m_InternalData.AllowedWriteMark = m_InternalData.Position + bytes; + m_AllowedWriteMark = m_Position + bytes; } #endif return true; @@ -201,16 +178,16 @@ public bool VerifyCanWriteInternal(int bytes) public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif int len = sizeof(T); - if (m_InternalData.Position + len > m_InternalData.Capacity) + if (m_Position + len > m_Capacity) { - if (m_InternalData.Capacity < m_InternalData.MaxCapacity) + if (m_Capacity < m_MaxCapacity) { Grow(); } @@ -220,67 +197,32 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedWriteMark = m_InternalData.Position + len; + m_AllowedWriteMark = m_Position + len; #endif return true; } - - public bool VerifyCanWriteBits(int bitCount) - { -#if DEVELOPMENT_BUILD || UNITY_EDITOR - if (!m_InternalData.InBitwiseContext) - { - throw new InvalidOperationException( - "Cannot use BufferWriter in bitwise mode while not in a bitwise context."); - } -#endif - var newBitPosition = m_InternalData.BitPosition + bitCount; - var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; - if ((newBitPosition & 7) != 0) - { - // Accounting for the partial write - ++totalBytesWrittenInBitwiseContext; - } - - if (m_InternalData.Position + totalBytesWrittenInBitwiseContext > m_InternalData.Capacity) - { - if (m_InternalData.Capacity < m_InternalData.MaxCapacity) - { - Grow(); - } - else - { - return false; - } - } -#if DEVELOPMENT_BUILD || UNITY_EDITOR - m_InternalData.AllowedBitwiseWriteMark = newBitPosition; -#endif - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public NativeArray GetNativeArray() - { - return m_buffer; - } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte[] ToArray() + public unsafe byte[] ToArray() { - return m_buffer.ToArray(); + byte[] ret = new byte[Length]; + fixed (byte* b = ret) + { + UnsafeUtility.MemCpy(b, m_BufferPointer, Length); + } + return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { - return m_InternalData.BufferPointer; + return m_BufferPointer; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { - return m_InternalData.BufferPointer + m_InternalData.Position; + return m_BufferPointer + m_Position; } /// @@ -493,10 +435,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - if (oneByteChars) - { - WriteByte((byte) s[i]); - } + WriteByte((byte) s[i]); } } else @@ -516,7 +455,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) public unsafe void WriteValueSafe(string s, bool oneByteChars = false) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -536,10 +475,7 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - if (oneByteChars) - { - WriteByte((byte) s[i]); - } + WriteByte((byte) s[i]); } } else @@ -586,7 +522,7 @@ public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) wher public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) where T: unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -624,19 +560,19 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt // in all builds - editor, mono, and ILCPP #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + bytesToWrite > m_InternalData.AllowedWriteMark) + if (m_Position + bytesToWrite > m_AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif byte* ptr = ((byte*) &value) + offsetBytes; - byte* bufferPointer = m_InternalData.BufferPointer + m_InternalData.Position; + byte* bufferPointer = m_BufferPointer + m_Position; switch (bytesToWrite) { case 1: @@ -673,7 +609,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt break; } - m_InternalData.Position += bytesToWrite; + m_Position += bytesToWrite; } /// @@ -684,17 +620,17 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt public unsafe void WriteByte(byte value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + 1 > m_InternalData.AllowedWriteMark) + if (m_Position + 1 > m_AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - m_InternalData.BufferPointer[m_InternalData.Position++] = value; + m_BufferPointer[m_Position++] = value; } /// @@ -705,7 +641,7 @@ public unsafe void WriteByte(byte value) public unsafe void WriteByteSafe(byte value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -716,7 +652,7 @@ public unsafe void WriteByteSafe(byte value) { throw new OverflowException("Writing past the end of the buffer"); } - m_InternalData.BufferPointer[m_InternalData.Position++] = value; + m_BufferPointer[m_Position++] = value; } /// @@ -728,18 +664,18 @@ public unsafe void WriteByteSafe(byte value) public unsafe void WriteBytes(byte* value, int size, int offset = 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + size > m_InternalData.AllowedWriteMark) + if (m_Position + size > m_AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - UnsafeUtility.MemCpy((m_InternalData.BufferPointer + m_InternalData.Position), value + offset, size); - m_InternalData.Position += size; + UnsafeUtility.MemCpy((m_BufferPointer + m_Position), value + offset, size); + m_Position += size; } /// @@ -751,7 +687,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -762,8 +698,8 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) { throw new OverflowException("Writing past the end of the buffer"); } - UnsafeUtility.MemCpy((m_InternalData.BufferPointer + m_InternalData.Position), value + offset, size); - m_InternalData.Position += size; + UnsafeUtility.MemCpy((m_BufferPointer + m_Position), value + offset, size); + m_Position += size; } /// @@ -803,7 +739,7 @@ public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyTo(FastBufferWriter other) { - other.WriteBytes(m_InternalData.BufferPointer, m_InternalData.Position); + other.WriteBytes(m_BufferPointer, m_Position); } /// @@ -815,7 +751,7 @@ public unsafe void CopyTo(FastBufferWriter other) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyFrom(FastBufferWriter other) { - WriteBytes(other.m_InternalData.BufferPointer, other.m_InternalData.Position); + WriteBytes(other.m_BufferPointer, other.m_Position); } /// @@ -830,20 +766,20 @@ public unsafe void WriteValue(in T value) where T : unmanaged int len = sizeof(T); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_InternalData.Position + len > m_InternalData.AllowedWriteMark) + if (m_Position + len > m_AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + T* pointer = (T*)(m_BufferPointer+m_Position); *pointer = value; - m_InternalData.Position += len; + m_Position += len; } /// @@ -858,7 +794,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged int len = sizeof(T); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_InternalData.InBitwiseContext) + if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); @@ -870,9 +806,9 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged throw new OverflowException("Writing past the end of the buffer"); } - T* pointer = (T*)(m_InternalData.BufferPointer+m_InternalData.Position); + T* pointer = (T*)(m_BufferPointer+m_Position); *pointer = value; - m_InternalData.Position += len; + m_Position += len; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index 4034de6648..a395fa43b0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -36,47 +36,53 @@ public void TestReadingOneBit() writer.WriteByte(0b11111111); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(3)); - using (var bitReader = reader.EnterBitwiseContext()) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - bool b; - bitReader.ReadBit(out b); - Assert.IsTrue(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); - - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); - - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); - bitReader.ReadBit(out b); - Assert.IsFalse(b); - bitReader.ReadBit(out b); - Assert.IsTrue(b); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + bool b; + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + bitReader.ReadBit(out b); + Assert.IsFalse(b); + bitReader.ReadBit(out b); + Assert.IsTrue(b); + } + + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); } - reader.ReadByte(out byte lastByte); - Assert.AreEqual(0b11111111, lastByte); } } [Test] public unsafe void TestVerifyCanReadBits() { - FastBufferReader reader = new FastBufferReader(new NativeArray(4, Allocator.Temp)); - using (reader.GetNativeArray()) + var nativeArray = new NativeArray(4, Allocator.Temp); + FastBufferReader reader = new FastBufferReader(nativeArray, Allocator.Temp); + nativeArray.Dispose(); + using (reader) { int* asInt = (int*) reader.GetUnsafePtr(); *asInt = 0b11111111_00001010_10101011; @@ -85,7 +91,7 @@ public unsafe void TestVerifyCanReadBits() { Assert.Throws(() => reader.VerifyCanRead(1)); Assert.Throws(() => reader.VerifyCanReadValue(1)); - Assert.IsTrue(reader.VerifyCanReadBits(1)); + Assert.IsTrue(bitReader.VerifyCanReadBits(1)); bitReader.ReadBit(out bool b); Assert.IsTrue(b); @@ -102,7 +108,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(reader.VerifyCanReadBits(3)); + Assert.IsTrue(bitReader.VerifyCanReadBits(3)); bitReader.ReadBit(out b); Assert.IsTrue(b); bitReader.ReadBit(out b); @@ -136,7 +142,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(reader.VerifyCanReadBits(3)); + Assert.IsTrue(bitReader.VerifyCanReadBits(3)); try { @@ -150,11 +156,11 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(reader.VerifyCanReadBits(4)); + Assert.IsTrue(bitReader.VerifyCanReadBits(4)); bitReader.ReadBits(out byteVal, 3); Assert.AreEqual(0b010, byteVal); - Assert.IsTrue(reader.VerifyCanReadBits(5)); + Assert.IsTrue(bitReader.VerifyCanReadBits(5)); bitReader.ReadBits(out byteVal, 5); Assert.AreEqual(0b10101, byteVal); @@ -173,7 +179,7 @@ public unsafe void TestVerifyCanReadBits() Assert.IsFalse(reader.VerifyCanRead(1)); using (var bitReader = reader.EnterBitwiseContext()) { - Assert.IsFalse(reader.VerifyCanReadBits(1)); + Assert.IsFalse(bitReader.VerifyCanReadBits(1)); } } } @@ -196,28 +202,32 @@ public void TestReadingMultipleBits() writer.WriteByte(0b11111111); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(3)); - using (var bitReader = reader.EnterBitwiseContext()) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - byte b; - bitReader.ReadBits(out b, 1); - Assert.AreEqual(0b1, b); - - bitReader.ReadBits(out b, 1); - Assert.AreEqual(0b1, b); - - bitReader.ReadBits(out b, 2); - Assert.AreEqual(0b10, b); - - bitReader.ReadBits(out b, 4); - Assert.AreEqual(0b1000, b); - - bitReader.ReadBits(out b, 4); - Assert.AreEqual(0b1010, b); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + byte b; + bitReader.ReadBits(out b, 1); + Assert.AreEqual(0b1, b); + + bitReader.ReadBits(out b, 1); + Assert.AreEqual(0b1, b); + + bitReader.ReadBits(out b, 2); + Assert.AreEqual(0b10, b); + + bitReader.ReadBits(out b, 4); + Assert.AreEqual(0b1000, b); + + bitReader.ReadBits(out b, 4); + Assert.AreEqual(0b1010, b); + } + + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); } - reader.ReadByte(out byte lastByte); - Assert.AreEqual(0b11111111, lastByte); } } @@ -239,36 +249,42 @@ public void TestReadingMultipleBitsToLongs() writer.WriteByte(0b11111111); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(3)); - using (var bitReader = reader.EnterBitwiseContext()) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - ulong ul; - bitReader.ReadBits(out ul, 1); - Assert.AreEqual(0b1, ul); - - bitReader.ReadBits(out ul, 1); - Assert.AreEqual(0b1, ul); - - bitReader.ReadBits(out ul, 2); - Assert.AreEqual(0b10, ul); - - bitReader.ReadBits(out ul, 4); - Assert.AreEqual(0b1000, ul); - - bitReader.ReadBits(out ul, 4); - Assert.AreEqual(0b1010, ul); + Assert.IsTrue(reader.VerifyCanRead(3)); + using (var bitReader = reader.EnterBitwiseContext()) + { + ulong ul; + bitReader.ReadBits(out ul, 1); + Assert.AreEqual(0b1, ul); + + bitReader.ReadBits(out ul, 1); + Assert.AreEqual(0b1, ul); + + bitReader.ReadBits(out ul, 2); + Assert.AreEqual(0b10, ul); + + bitReader.ReadBits(out ul, 4); + Assert.AreEqual(0b1000, ul); + + bitReader.ReadBits(out ul, 4); + Assert.AreEqual(0b1010, ul); + } + + reader.ReadByte(out byte lastByte); + Assert.AreEqual(0b11111111, lastByte); } - reader.ReadByte(out byte lastByte); - Assert.AreEqual(0b11111111, lastByte); } } [Test] public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() { - FastBufferReader reader = new FastBufferReader(new NativeArray(4, Allocator.Temp)); - using (reader.GetNativeArray()) + var nativeArray = new NativeArray(4, Allocator.Temp); + FastBufferReader reader = new FastBufferReader(nativeArray, Allocator.Temp); + nativeArray.Dispose(); + using (reader) { int* asInt = (int*) reader.GetUnsafePtr(); *asInt = 0b11111111_00001010_10101011; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index b03fb84d1d..7614242e93 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -64,7 +64,7 @@ public unsafe void TestVerifyCanWriteBits() { Assert.Throws(() => writer.VerifyCanWrite(1)); Assert.Throws(() => writer.VerifyCanWriteValue(1)); - Assert.IsTrue(writer.VerifyCanWriteBits(1)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(1)); bitWriter.WriteBit(true); Assert.AreEqual(0b1, *asInt); @@ -81,7 +81,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(writer.VerifyCanWriteBits(3)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); bitWriter.WriteBit(true); Assert.AreEqual(0b11, *asInt); @@ -114,7 +114,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(writer.VerifyCanWriteBits(3)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); try { @@ -128,13 +128,13 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(writer.VerifyCanWriteBits(4)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(4)); bitWriter.WriteBits(0b11111010, 3); Assert.AreEqual(0b00101011, *asInt); - Assert.IsTrue(writer.VerifyCanWriteBits(5)); + Assert.IsTrue(bitWriter.VerifyCanWriteBits(5)); bitWriter.WriteBits(0b11110101, 5); Assert.AreEqual(0b1010_10101011, *asInt); @@ -154,7 +154,7 @@ public unsafe void TestVerifyCanWriteBits() Assert.IsFalse(writer.VerifyCanWrite(1)); using (var bitWriter = writer.EnterBitwiseContext()) { - Assert.IsFalse(writer.VerifyCanWriteBits(1)); + Assert.IsFalse(bitWriter.VerifyCanWriteBits(1)); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index d11744c2b0..9ae500584c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -111,9 +111,12 @@ private void CheckUnsignedPackedSize64(ref FastBufferWriter writer, ulong value) private void CheckUnsignedPackedValue64(ref FastBufferWriter writer, ulong value) { - var reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out ulong readValue); - Assert.AreEqual(readValue, value); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out ulong readValue); + Assert.AreEqual(readValue, value); + } } private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) @@ -135,9 +138,12 @@ private void CheckUnsignedPackedSize32(ref FastBufferWriter writer, uint value) private void CheckUnsignedPackedValue32(ref FastBufferWriter writer, uint value) { - var reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out uint readValue); - Assert.AreEqual(readValue, value); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out uint readValue); + Assert.AreEqual(readValue, value); + } } private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) @@ -160,9 +166,12 @@ private void CheckSignedPackedSize64(ref FastBufferWriter writer, long value) private void CheckSignedPackedValue64(ref FastBufferWriter writer, long value) { - var reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out long readValue); - Assert.AreEqual(readValue, value); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out long readValue); + Assert.AreEqual(readValue, value); + } } private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) @@ -185,9 +194,12 @@ private void CheckSignedPackedSize32(ref FastBufferWriter writer, int value) private void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) { - var reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out int readValue); - Assert.AreEqual(readValue, value); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out int readValue); + Assert.AreEqual(readValue, value); + } } private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T: unmanaged @@ -206,24 +218,30 @@ private unsafe void RunTypeTest(T value) where T : unmanaged using (writer) { BytePacker.WriteValuePacked(ref writer, (dynamic)value); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - - T outVal = new T(); - MethodInfo method; - if (value is Enum) - { - method = typeof(ByteUnpacker).GetMethods().Single(x => x.Name == "ReadValuePacked" && x.IsGenericMethodDefinition) - .MakeGenericMethod(typeof(T)); - } - else + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - method = typeof (ByteUnpacker).GetMethod("ReadValuePacked", new[] {typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType()}); + + T outVal = new T(); + MethodInfo method; + if (value is Enum) + { + method = typeof(ByteUnpacker).GetMethods().Single(x => + x.Name == "ReadValuePacked" && x.IsGenericMethodDefinition) + .MakeGenericMethod(typeof(T)); + } + else + { + method = typeof(ByteUnpacker).GetMethod("ReadValuePacked", + new[] {typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType()}); + } + + object[] args = {reader, outVal}; + method.Invoke(null, args); + outVal = (T) args[1]; + Assert.AreEqual(value, outVal); + VerifyBytewiseEquality(value, outVal); } - object[] args = {reader, outVal}; - method.Invoke(null, args); - outVal = (T) args[1]; - Assert.AreEqual(value, outVal); - VerifyBytewiseEquality(value, outVal); } } @@ -233,11 +251,14 @@ private unsafe void RunObjectTypeTest(T value) where T : unmanaged using (writer) { BytePacker.WriteObjectPacked(ref writer, value); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); - Assert.AreEqual(value, outVal); - VerifyBytewiseEquality(value, (T)outVal); + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); + Assert.AreEqual(value, outVal); + VerifyBytewiseEquality(value, (T) outVal); + } } } @@ -472,46 +493,64 @@ private int GetByteCount15Bits(ushort value) return 2; } - private ulong Get61BitEncodedValue(NativeArray data) + private ulong Get61BitEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out ulong value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out ulong value); + return value; + } } - private long Get60BitSignedEncodedValue(NativeArray data) + private long Get60BitSignedEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out long value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out long value); + return value; + } } - private uint Get30BitEncodedValue(NativeArray data) + private uint Get30BitEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out uint value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out uint value); + return value; + } } - private int Get29BitSignedEncodedValue(NativeArray data) + private int Get29BitSignedEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out int value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out int value); + return value; + } } - private ushort Get15BitEncodedValue(NativeArray data) + private ushort Get15BitEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out ushort value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out ushort value); + return value; + } } - private short Get14BitSignedEncodedValue(NativeArray data) + private short Get14BitSignedEncodedValue(ref FastBufferWriter writer) { - FastBufferReader reader = new FastBufferReader(data); - ByteUnpacker.ReadValueBitPacked(ref reader, out short value); - return value; + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValueBitPacked(ref reader, out short value); + return value; + } } [Test] @@ -524,10 +563,9 @@ public void TestBitPacking61BitsUnsigned() writer.VerifyCanWrite(8); ulong value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b111); - Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b111); + Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); for (var i = 0; i < 61; ++i) { @@ -536,8 +574,8 @@ public void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(value)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -546,8 +584,8 @@ public void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get61BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(value)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); } } @@ -565,10 +603,9 @@ public void TestBitPacking60BitsSigned() writer.VerifyCanWrite(8); long value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b111); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b111); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); for (var i = 0; i < 61; ++i) { @@ -578,8 +615,8 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); value = -value; zzvalue = Arithmetic.ZigZagEncode(value); @@ -587,8 +624,8 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -598,8 +635,8 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); value = -value; zzvalue = Arithmetic.ZigZagEncode(value); @@ -607,8 +644,8 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, nativeArray[0] & 0b111, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get60BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); } } @@ -626,10 +663,9 @@ public void TestBitPacking30BitsUnsigned() writer.VerifyCanWrite(4); uint value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b11); - Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); for (var i = 0; i < 30; ++i) { @@ -638,8 +674,8 @@ public void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(value)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -648,8 +684,8 @@ public void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(value)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); } } @@ -667,10 +703,9 @@ public void TestBitPacking29BitsSigned() writer.VerifyCanWrite(4); int value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b11); - Assert.AreEqual(value, Get30BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b11); + Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); for (var i = 0; i < 29; ++i) { @@ -680,8 +715,8 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); value = -value; zzvalue = (uint)Arithmetic.ZigZagEncode(value); @@ -689,8 +724,8 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -700,8 +735,8 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); value = -value; zzvalue = (uint)Arithmetic.ZigZagEncode(value); @@ -709,8 +744,8 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, nativeArray[0] & 0b11, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get29BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); } } } @@ -726,10 +761,9 @@ public void TestBitPacking15BitsUnsigned() writer.VerifyCanWrite(2); ushort value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b1); - Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); for (var i = 0; i < 15; ++i) { @@ -738,8 +772,8 @@ public void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(value)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -748,8 +782,8 @@ public void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(value)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); } } @@ -766,10 +800,9 @@ public void TestBitPacking14BitsSigned() writer.VerifyCanWrite(2); short value = 0; BytePacker.WriteValueBitPacked(ref writer, value); - NativeArray nativeArray = writer.GetNativeArray(); Assert.AreEqual(1, writer.Position); - Assert.AreEqual(0, nativeArray[0] & 0b1); - Assert.AreEqual(value, Get15BitEncodedValue(nativeArray)); + Assert.AreEqual(0, writer.ToArray()[0] & 0b1); + Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); for (var i = 0; i < 14; ++i) { @@ -779,8 +812,8 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); value = (short)-value; zzvalue = (ushort)Arithmetic.ZigZagEncode(value); @@ -788,8 +821,8 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i})"); - Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); for (var j = 0; j < 8; ++j) { @@ -799,8 +832,8 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); value = (short)-value; zzvalue = (ushort)Arithmetic.ZigZagEncode(value); @@ -808,8 +841,8 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, nativeArray[0] & 0b1, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(value, Get14BitSignedEncodedValue(nativeArray)); + Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); } } } @@ -1163,12 +1196,15 @@ public void TestPackingBasicTypes( using (writer) { BytePacker.WriteValuePacked(ref writer, v); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out Ray outVal); - Assert.AreEqual(v.origin, outVal.origin); - Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); - Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); - Assert.AreEqual(v.direction.z, outVal.direction.z, 0.00001); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out Ray outVal); + Assert.AreEqual(v.origin, outVal.origin); + Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); + Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + Assert.AreEqual(v.direction.z, outVal.direction.z, 0.00001); + } } } } @@ -1180,12 +1216,15 @@ public void TestPackingBasicTypes( using (writer) { BytePacker.WriteObjectPacked(ref writer, v); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); - Assert.AreEqual(v.origin, ((Ray)outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray)outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray)outVal).direction.y, 0.00001); - Assert.AreEqual(v.direction.z, ((Ray)outVal).direction.z, 0.00001); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); + Assert.AreEqual(v.origin, ((Ray) outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray) outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray) outVal).direction.y, 0.00001); + Assert.AreEqual(v.direction.z, ((Ray) outVal).direction.z, 0.00001); + } } } } @@ -1205,11 +1244,14 @@ public void TestPackingBasicTypes( using (writer) { BytePacker.WriteValuePacked(ref writer, v); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadValuePacked(ref reader, out Ray2D outVal); - Assert.AreEqual(v.origin, outVal.origin); - Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); - Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadValuePacked(ref reader, out Ray2D outVal); + Assert.AreEqual(v.origin, outVal.origin); + Assert.AreEqual(v.direction.x, outVal.direction.x, 0.00001); + Assert.AreEqual(v.direction.y, outVal.direction.y, 0.00001); + } } } } @@ -1221,11 +1263,14 @@ public void TestPackingBasicTypes( using (writer) { BytePacker.WriteObjectPacked(ref writer, v); - FastBufferReader reader = new FastBufferReader(writer.GetNativeArray()); - ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); - Assert.AreEqual(v.origin, ((Ray2D)outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray2D)outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray2D)outVal).direction.y, 0.00001); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); + Assert.AreEqual(v.origin, ((Ray2D) outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray2D) outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray2D) outVal).direction.y, 0.00001); + } } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 49e423d04f..9cb416300c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -117,13 +117,11 @@ private void VerifyPositionAndLength(ref FastBufferReader reader, int length, st private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged { - NativeArray underlyingArray = writer.GetNativeArray(); - WriteCheckBytes(ref writer, writeSize, failMessage); - FastBufferReader reader = new FastBufferReader(underlyingArray); + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); - VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); + VerifyPositionAndLength(ref reader, writer.Length, failMessage); VerifyCheckBytes(ref reader, writeSize, failMessage); @@ -150,9 +148,12 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetWriteSize())); - reader.ReadValue(out T result); - Assert.AreEqual(valueToTest, result); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetWriteSize())); + reader.ReadValue(out T result); + Assert.AreEqual(valueToTest, result); + } } } private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged @@ -170,9 +171,12 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); - - reader.ReadValueSafe(out T result); - Assert.AreEqual(valueToTest, result); + + using (reader) + { + reader.ReadValueSafe(out T result); + Assert.AreEqual(valueToTest, result); + } } } @@ -189,9 +193,12 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged writer.WriteObject(valueToTest); var reader = CommonChecks(ref writer, valueToTest, writeSize, failMessage); - - reader.ReadObject(out object result, typeof(T)); - Assert.AreEqual(valueToTest, result); + + using (reader) + { + reader.ReadObject(out object result, typeof(T)); + Assert.AreEqual(valueToTest, result); + } } } @@ -211,8 +218,6 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -220,14 +225,17 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged WriteCheckBytes(ref writer, writeSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); - Assert.IsTrue(reader.VerifyCanRead(writeSize)); - reader.ReadValue(out T[] result); - VerifyArrayEquality(valueToTest, result, 0); + Assert.IsTrue(reader.VerifyCanRead(writeSize)); + reader.ReadValue(out T[] result); + VerifyArrayEquality(valueToTest, result, 0); - VerifyCheckBytes(ref reader, writeSize); + VerifyCheckBytes(ref reader, writeSize); + } } } @@ -237,21 +245,22 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteValueSafe(valueToTest); WriteCheckBytes(ref writer, writeSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadValueSafe(out T[] result); - VerifyArrayEquality(valueToTest, result, 0); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadValueSafe(out T[] result); + VerifyArrayEquality(valueToTest, result, 0); - VerifyCheckBytes(ref reader, writeSize); + VerifyCheckBytes(ref reader, writeSize); + } } } @@ -262,21 +271,22 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); WriteCheckBytes(ref writer, writeSize + 1); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadObject(out object result, typeof(T[])); - VerifyArrayEquality(valueToTest, (T[])result, 0); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadObject(out object result, typeof(T[])); + VerifyArrayEquality(valueToTest, (T[]) result, 0); - VerifyCheckBytes(ref reader, writeSize + 1); + VerifyCheckBytes(ref reader, writeSize + 1); + } } } #endregion @@ -1547,21 +1557,22 @@ public void TestReadingString() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); WriteCheckBytes(ref writer, serializedValueSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); - reader.ReadValue(out string result); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out string result); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize); + VerifyCheckBytes(ref reader, serializedValueSize); + } } } @@ -1575,19 +1586,20 @@ public void TestReadingStringSafe() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - writer.WriteValueSafe(valueToTest); WriteCheckBytes(ref writer, serializedValueSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadValueSafe(out string result); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadValueSafe(out string result); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize); + VerifyCheckBytes(ref reader, serializedValueSize); + } } } @@ -1601,19 +1613,20 @@ public void TestReadingStringAsObject() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - writer.WriteObject(valueToTest); WriteCheckBytes(ref writer, serializedValueSize+1); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadObject(out object result, typeof(string)); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadObject(out object result, typeof(string)); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize+1); + VerifyCheckBytes(ref reader, serializedValueSize + 1); + } } } @@ -1626,21 +1639,22 @@ public void TestReadingOneByteString() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest, true); WriteCheckBytes(ref writer, serializedValueSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); - reader.ReadValue(out string result, true); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out string result, true); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize); + VerifyCheckBytes(ref reader, serializedValueSize); + } } } @@ -1653,19 +1667,20 @@ public void TestReadingOneByteStringSafe() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - writer.WriteValueSafe(valueToTest, true); WriteCheckBytes(ref writer, serializedValueSize); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity); - - reader.ReadValueSafe(out string result, true); - Assert.AreEqual(valueToTest, result); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + VerifyPositionAndLength(ref reader, writer.Length); + + reader.ReadValueSafe(out string result, true); + Assert.AreEqual(valueToTest, result); - VerifyCheckBytes(ref reader, serializedValueSize); + VerifyCheckBytes(ref reader, serializedValueSize); + } } } @@ -1683,24 +1698,27 @@ public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulo var failMessage = $"TestReadingPartialValues failed with value {valueToTest}"; WriteCheckBytes(ref writer, count, failMessage); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); - Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - - ulong mask = 0; - for (var i = 0; i < count; ++i) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - mask = (mask << 8) | 0b11111111; + VerifyPositionAndLength(ref reader, writer.Length, failMessage); + Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + reader.ReadPartialValue(out ulong result, count); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); } - - reader.ReadPartialValue(out ulong result, count); - Assert.AreEqual(valueToTest & mask, result & mask, failMessage); - VerifyCheckBytes(ref reader, count, failMessage); } } [Test] - public unsafe void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + public void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) { var random = new Random(); var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); @@ -1708,55 +1726,34 @@ public unsafe void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); writer.WritePartialValue(valueToTest, count, 2); var failMessage = $"TestReadingPartialValuesWithOffsets failed with value {valueToTest}"; WriteCheckBytes(ref writer, count, failMessage); - var reader = new FastBufferReader(writer.GetNativeArray()); - VerifyPositionAndLength(ref reader, writer.Capacity, failMessage); - Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - - ulong mask = 0; - for (var i = 0; i < count; ++i) + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - mask = (mask << 8) | 0b11111111; - } + VerifyPositionAndLength(ref reader, writer.Length, failMessage); + Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - mask <<= 16; - - reader.ReadPartialValue(out ulong result, count, 2); - Assert.AreEqual(valueToTest & mask, result & mask, failMessage); - VerifyCheckBytes(ref reader, count, failMessage); - } - } - - [Test] - public void TestToArray() - { - var testStruct = GetTestStruct(); - var requiredSize = FastBufferWriter.GetWriteSize(testStruct); - var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } - using (writer) - { - writer.VerifyCanWrite(requiredSize); - writer.WriteValue(testStruct); + mask <<= 16; - var reader = new FastBufferReader(writer.GetNativeArray()); - var array = reader.ToArray(); - var underlyingArray = writer.GetNativeArray(); - for(var i = 0; i < array.Length; ++i) - { - Assert.AreEqual(array[i], underlyingArray[i]); + reader.ReadPartialValue(out ulong result, count, 2); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); } } } [Test] - public unsafe void TestGetUnsafePtr() + public unsafe void TestToArray() { var testStruct = GetTestStruct(); var requiredSize = FastBufferWriter.GetWriteSize(testStruct); @@ -1766,13 +1763,17 @@ public unsafe void TestGetUnsafePtr() { writer.VerifyCanWrite(requiredSize); writer.WriteValue(testStruct); - var reader = new FastBufferReader(writer.GetNativeArray()); - var ptr = reader.GetUnsafePtr(); - var underlyingArrayPtr = writer.GetNativeArray().GetUnsafePtr(); - Assert.IsTrue(underlyingArrayPtr == ptr); - var ptrAtPosition = writer.GetUnsafePtrAtCurrentPosition(); - Assert.IsTrue((byte*)underlyingArrayPtr + writer.Position == ptrAtPosition); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + var array = reader.ToArray(); + var underlyingArray = writer.GetUnsafePtr(); + for (var i = 0; i < array.Length; ++i) + { + Assert.AreEqual(array[i], underlyingArray[i]); + } + } } } @@ -1780,9 +1781,11 @@ public unsafe void TestGetUnsafePtr() public void TestThrowingIfBoundsCheckingSkipped() { var writer = new FastBufferWriter(100, Allocator.Temp); - var emptyReader = new FastBufferReader(new NativeArray(100, Allocator.Temp), 0, 0); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp, 0); + nativeArray.Dispose(); - using (emptyReader.GetNativeArray()) + using (emptyReader) using (writer) { Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); @@ -1797,14 +1800,17 @@ public void TestThrowingIfBoundsCheckingSkipped() writer.WriteByte(1); writer.WriteByte(2); writer.WriteByte(3); - var reader = new FastBufferReader(writer.GetNativeArray(), 0, writer.Length); - Assert.Throws(() => { reader.ReadValue(out int i); }); - Assert.Throws(() => { reader.ReadValue(out byte b); }); - Assert.IsTrue(reader.VerifyCanRead(3)); - reader.ReadByte(out byte b); - reader.ReadByte(out b); - reader.ReadByte(out b); - Assert.Throws(() => { reader.ReadValue(out byte b); }); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.Throws(() => { reader.ReadValue(out int i); }); + Assert.Throws(() => { reader.ReadValue(out byte b); }); + Assert.IsTrue(reader.VerifyCanRead(3)); + reader.ReadByte(out byte b); + reader.ReadByte(out b); + reader.ReadByte(out b); + Assert.Throws(() => { reader.ReadValue(out byte b); }); + } } } @@ -1830,21 +1836,27 @@ public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() writer.WriteValueSafe(bytes); writer.WriteValueSafe(""); - var reader = new FastBufferReader(writer.GetNativeArray(), 0, writer.Length); - Assert.IsTrue(reader.VerifyCanRead(writer.Length)); - using (var context = reader.EnterBitwiseContext()) - { - Assert.Throws(() => { reader.ReadByte(out byte b); }); - Assert.Throws(() => { reader.ReadBytes(ref bytes, bytes.Length); }); - Assert.Throws(() => { reader.ReadValue(out i); }); - Assert.Throws(() => { reader.ReadValue(out bytes); }); - Assert.Throws(() => { reader.ReadValue(out string s); }); - - Assert.Throws(() => { reader.ReadByteSafe(out byte b); }); - Assert.Throws(() => { reader.ReadBytesSafe(ref bytes, bytes.Length); }); - Assert.Throws(() => { reader.ReadValueSafe(out i); }); - Assert.Throws(() => { reader.ReadValueSafe(out bytes); }); - Assert.Throws(() => { reader.ReadValueSafe(out string s); }); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(writer.Length)); + using (var context = reader.EnterBitwiseContext()) + { + Assert.Throws(() => { reader.ReadByte(out byte b); }); + Assert.Throws(() => { reader.ReadBytes(ref bytes, bytes.Length); }); + Assert.Throws(() => { reader.ReadValue(out i); }); + Assert.Throws(() => { reader.ReadValue(out bytes); }); + Assert.Throws(() => { reader.ReadValue(out string s); }); + + Assert.Throws(() => { reader.ReadByteSafe(out byte b); }); + Assert.Throws(() => + { + reader.ReadBytesSafe(ref bytes, bytes.Length); + }); + Assert.Throws(() => { reader.ReadValueSafe(out i); }); + Assert.Throws(() => { reader.ReadValueSafe(out bytes); }); + Assert.Throws(() => { reader.ReadValueSafe(out string s); }); + } } } } @@ -1852,8 +1864,10 @@ public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() [Test] public void TestVerifyCanReadIsRelativeToPositionAndNotAllowedReadPosition() { - var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp)); - using (reader.GetNativeArray()) + var nativeArray = new NativeArray(100, Allocator.Temp); + var reader = new FastBufferReader(nativeArray, Allocator.Temp, 100); + nativeArray.Dispose(); + using (reader) { reader.VerifyCanRead(100); reader.ReadByte(out byte b); @@ -1876,41 +1890,44 @@ public void TestSeeking() writer.WriteByteSafe(4); writer.WriteByteSafe(0); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.Seek(5); - reader.ReadByteSafe(out byte b); - Assert.AreEqual(reader.Position, 6); - Assert.AreEqual(reader.Length, 100); - Assert.AreEqual(0, b); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + reader.Seek(5); + reader.ReadByteSafe(out byte b); + Assert.AreEqual(reader.Position, 6); + Assert.AreEqual(reader.Length, writer.Length); + Assert.AreEqual(0, b); - reader.Seek(0); - reader.ReadByteSafe(out b); - Assert.AreEqual(reader.Position, 1); - Assert.AreEqual(reader.Length, 100); - Assert.AreEqual(1, b); + reader.Seek(0); + reader.ReadByteSafe(out b); + Assert.AreEqual(reader.Position, 1); + Assert.AreEqual(reader.Length, writer.Length); + Assert.AreEqual(1, b); - reader.Seek(10); - Assert.AreEqual(reader.Position, 10); - Assert.AreEqual(reader.Length, 100); + reader.Seek(10); + Assert.AreEqual(reader.Position, writer.Length); + Assert.AreEqual(reader.Length, writer.Length); - reader.Seek(2); - reader.ReadByteSafe(out b); - Assert.AreEqual(2, b); + reader.Seek(2); + reader.ReadByteSafe(out b); + Assert.AreEqual(2, b); - reader.Seek(1); - reader.ReadByteSafe(out b); - Assert.AreEqual(3, b); + reader.Seek(1); + reader.ReadByteSafe(out b); + Assert.AreEqual(3, b); - reader.Seek(4); - reader.ReadByteSafe(out b); - Assert.AreEqual(4, b); + reader.Seek(4); + reader.ReadByteSafe(out b); + Assert.AreEqual(4, b); - reader.Seek(3); - reader.ReadByteSafe(out b); - Assert.AreEqual(5, b); + reader.Seek(3); + reader.ReadByteSafe(out b); + Assert.AreEqual(5, b); - Assert.AreEqual(reader.Position, 4); - Assert.AreEqual(reader.Length, 100); + Assert.AreEqual(reader.Position, 4); + Assert.AreEqual(reader.Length, writer.Length); + } } } @@ -1935,7 +1952,7 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) NetworkTransport = obj.AddComponent() }; - networkManager.StartHost(); + networkManager.StartServer(); try { @@ -1944,7 +1961,7 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) finally { GameObject.DestroyImmediate(obj); - networkManager.StopHost(); + networkManager.StopServer(); } } @@ -1962,11 +1979,14 @@ public void TestNetworkBehaviour() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkBehaviourWriteSize())); - reader.ReadValue(out NetworkBehaviour result); - Assert.AreSame(result, networkBehaviour); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkBehaviourWriteSize())); + reader.ReadValue(out NetworkBehaviour result); + Assert.AreSame(result, networkBehaviour); + } + } }); } @@ -1985,10 +2005,13 @@ public void TestNetworkObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkObjectWriteSize())); - reader.ReadValue(out NetworkObject result); - Assert.AreSame(result, networkObject); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkObjectWriteSize())); + reader.ReadValue(out NetworkObject result); + Assert.AreSame(result, networkObject); + } } }); } @@ -2007,10 +2030,13 @@ public void TestGameObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetGameObjectWriteSize())); - reader.ReadValue(out GameObject result); - Assert.AreSame(result, obj); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetGameObjectWriteSize())); + reader.ReadValue(out GameObject result); + Assert.AreSame(result, obj); + } } }); } @@ -2027,9 +2053,12 @@ public void TestNetworkBehaviourSafe() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadValueSafe(out NetworkBehaviour result); - Assert.AreSame(result, networkBehaviour); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + reader.ReadValueSafe(out NetworkBehaviour result); + Assert.AreSame(result, networkBehaviour); + } } }); } @@ -2046,9 +2075,12 @@ public void TestNetworkObjectSafe() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadValueSafe(out NetworkObject result); - Assert.AreSame(result, networkObject); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + reader.ReadValueSafe(out NetworkObject result); + Assert.AreSame(result, networkObject); + } } }); } @@ -2065,9 +2097,12 @@ public void TestGameObjectSafe() Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadValueSafe(out GameObject result); - Assert.AreSame(result, obj); + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + reader.ReadValueSafe(out GameObject result); + Assert.AreSame(result, obj); + } } }); } @@ -2085,9 +2120,12 @@ public void TestNetworkBehaviourAsObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadObject(out object result, typeof(NetworkBehaviour)); - Assert.AreSame(result, networkBehaviour); + var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); + using (reader) + { + reader.ReadObject(out object result, typeof(NetworkBehaviour)); + Assert.AreSame(result, networkBehaviour); + } } }); } @@ -2105,9 +2143,12 @@ public void TestNetworkObjectAsObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadObject(out object result, typeof(NetworkObject)); - Assert.AreSame(result, networkObject); + var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); + using (reader) + { + reader.ReadObject(out object result, typeof(NetworkObject)); + Assert.AreSame(result, networkObject); + } } }); } @@ -2125,9 +2166,12 @@ public void TestGameObjectAsObject() Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); - var reader = new FastBufferReader(writer.GetNativeArray()); - reader.ReadObject(out object result, typeof(GameObject)); - Assert.AreSame(result, obj); + var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); + using (reader) + { + reader.ReadObject(out object result, typeof(GameObject)); + Assert.AreSame(result, obj); + } } }); } @@ -2135,12 +2179,12 @@ public void TestGameObjectAsObject() [Test] public void TestVerifyInternalDoesntReduceAllowedWritePoint() { - var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp)); - using (reader.GetNativeArray()) + var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp), Allocator.Temp); + using (reader) { reader.VerifyCanRead(25); reader.VerifyCanReadInternal(5); - Assert.AreEqual(reader.m_InternalData.AllowedReadMark, 25); + Assert.AreEqual(reader.m_AllowedReadMark, 25); } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 2291627605..5401a4e5f5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -98,13 +98,13 @@ private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string Assert.AreEqual(writeSize+2, writer.Length, failMessage); } - private void VerifyCheckBytes(NativeArray underlyingArray, int writeSize, string failMessage = "") + private void VerifyCheckBytes(byte[] underlyingArray, int writeSize, string failMessage = "") { Assert.AreEqual(0x80, underlyingArray[writeSize], failMessage); Assert.AreEqual(0xFF, underlyingArray[writeSize+1], failMessage); } - private unsafe void VerifyBytewiseEquality(T value, NativeArray underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T: unmanaged + private unsafe void VerifyBytewiseEquality(T value, byte[] underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T: unmanaged { byte* asBytePointer = (byte*) &value; for (var i = 0; i < size; ++i) @@ -113,9 +113,9 @@ private unsafe void VerifyBytewiseEquality(T value, NativeArray underly } } - private unsafe void VerifyTypedEquality(T value, NativeArray underlyingArray) where T: unmanaged + private unsafe void VerifyTypedEquality(T value, byte* unsafePtr) where T: unmanaged { - T* checkValue = (T*) underlyingArray.GetUnsafePtr(); + T* checkValue = (T*) unsafePtr; Assert.AreEqual(value, *checkValue); } @@ -125,19 +125,20 @@ private void VerifyPositionAndLength(ref FastBufferWriter writer, int position, Assert.AreEqual(position, writer.Length, failMessage); } - private void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged { - NativeArray underlyingArray = writer.GetNativeArray(); VerifyPositionAndLength(ref writer, writeSize, failMessage); WriteCheckBytes(ref writer, writeSize, failMessage); + var underlyingArray = writer.ToArray(); + VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, writeSize, failMessage); VerifyCheckBytes(underlyingArray, writeSize, failMessage); - VerifyTypedEquality(valueToTest, underlyingArray); + VerifyTypedEquality(valueToTest, writer.GetUnsafePtr()); } #endregion @@ -196,14 +197,14 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged } } - private unsafe void VerifyArrayEquality(T[] value, NativeArray underlyingArray, int offset) where T: unmanaged + private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T: unmanaged { - int* sizeValue = (int*)((byte*)underlyingArray.GetUnsafePtr() + offset); + int* sizeValue = (int*)(unsafePtr + offset); Assert.AreEqual(value.Length, *sizeValue); fixed (T* asTPointer = value) { - T* underlyingTArray = (T*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int) + offset); + T* underlyingTArray = (T*) (unsafePtr + sizeof(int) + offset); for (var i = 0; i < value.Length; ++i) { Assert.AreEqual(asTPointer[i], underlyingTArray[i]); @@ -217,7 +218,6 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -227,8 +227,9 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged WriteCheckBytes(ref writer, writeSize); - VerifyArrayEquality(valueToTest, underlyingArray, 0); + VerifyArrayEquality(valueToTest, writer.GetUnsafePtr(), 0); + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, writeSize); } } @@ -239,7 +240,6 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); @@ -248,8 +248,9 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged WriteCheckBytes(ref writer, writeSize); - VerifyArrayEquality(valueToTest, underlyingArray, 0); + VerifyArrayEquality(valueToTest, writer.GetUnsafePtr(), 0); + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, writeSize); } } @@ -261,18 +262,18 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); - Assert.AreEqual(0, writer.GetNativeArray()[0]); + Assert.AreEqual(0, writer.ToArray()[0]); VerifyPositionAndLength(ref writer, writeSize + sizeof(byte)); WriteCheckBytes(ref writer, writeSize + sizeof(byte)); - VerifyArrayEquality(valueToTest, underlyingArray, sizeof(byte)); + VerifyArrayEquality(valueToTest, writer.GetUnsafePtr(), sizeof(byte)); + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, writeSize + sizeof(byte)); } } @@ -1532,7 +1533,6 @@ public unsafe void TestWritingString() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); @@ -1540,18 +1540,19 @@ public unsafe void TestWritingString() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + int* sizeValue = (int*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int)); + char* underlyingCharArray = (char*) (writer.GetUnsafePtr() + sizeof(int)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize); } } @@ -1566,25 +1567,25 @@ public unsafe void TestWritingStringSafe() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); writer.WriteValueSafe(valueToTest); VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + int* sizeValue = (int*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int)); + char* underlyingCharArray = (char*) ((byte*) writer.GetUnsafePtr() + sizeof(int)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize); } } @@ -1599,25 +1600,25 @@ public unsafe void TestWritingStringAsObject() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); writer.WriteObject(valueToTest); VerifyPositionAndLength(ref writer, serializedValueSize + sizeof(byte)); WriteCheckBytes(ref writer, serializedValueSize + sizeof(byte)); - int* sizeValue = (int*) ((byte*)underlyingArray.GetUnsafePtr() + sizeof(byte)); + int* sizeValue = (int*) (writer.GetUnsafePtr() + sizeof(byte)); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) ((byte*) underlyingArray.GetUnsafePtr() + sizeof(int) + sizeof(byte)); + char* underlyingCharArray = (char*) (writer.GetUnsafePtr() + sizeof(int) + sizeof(byte)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize + sizeof(byte)); } } @@ -1631,7 +1632,6 @@ public unsafe void TestWritingOneByteString() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest, true); @@ -1639,18 +1639,19 @@ public unsafe void TestWritingOneByteString() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + int* sizeValue = (int*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - byte* underlyingByteArray = (byte*) underlyingArray.GetUnsafePtr() + sizeof(int); + byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize); } } @@ -1664,25 +1665,25 @@ public unsafe void TestWritingOneByteStringSafe() FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using(writer) { - NativeArray underlyingArray = writer.GetNativeArray(); writer.WriteValueSafe(valueToTest, true); VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) underlyingArray.GetUnsafePtr(); + int* sizeValue = (int*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - byte* underlyingByteArray = (byte*) underlyingArray.GetUnsafePtr() + sizeof(int); + byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); } } + var underlyingArray = writer.ToArray(); VerifyCheckBytes(underlyingArray, serializedValueSize); } } @@ -1695,7 +1696,6 @@ public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulo FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); writer.WritePartialValue(valueToTest, count); @@ -1703,6 +1703,7 @@ public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulo var failMessage = $"TestWritingPartialValues failed with value {valueToTest}"; VerifyPositionAndLength(ref writer, count, failMessage); WriteCheckBytes(ref writer, count, failMessage); + var underlyingArray = writer.ToArray(); VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, count, failMessage); VerifyCheckBytes(underlyingArray, count, failMessage); @@ -1712,7 +1713,7 @@ public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulo mask = (mask << 8) | 0b11111111; } - ulong* checkValue = (ulong*) underlyingArray.GetUnsafePtr(); + ulong* checkValue = (ulong*) writer.GetUnsafePtr(); Assert.AreEqual(valueToTest & mask, *checkValue & mask, failMessage); } } @@ -1726,7 +1727,6 @@ public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, using (writer) { - NativeArray underlyingArray = writer.GetNativeArray(); Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); writer.WritePartialValue(valueToTest, count, 2); @@ -1734,6 +1734,7 @@ public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, VerifyPositionAndLength(ref writer, count, failMessage); WriteCheckBytes(ref writer, count, failMessage); + var underlyingArray = writer.ToArray(); VerifyBytewiseEquality(valueToTest, underlyingArray, 2, 0, count, failMessage); VerifyCheckBytes(underlyingArray, count, failMessage); @@ -1743,7 +1744,7 @@ public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, mask = (mask << 8) | 0b11111111; } - ulong* checkValue = (ulong*) underlyingArray.GetUnsafePtr(); + ulong* checkValue = (ulong*) writer.GetUnsafePtr(); Assert.AreEqual((valueToTest >> 16) & mask, *checkValue & mask); } } @@ -1760,7 +1761,7 @@ public void TestToArray() writer.VerifyCanWrite(requiredSize); writer.WriteValue(testStruct); var array = writer.ToArray(); - var underlyingArray = writer.GetNativeArray(); + var underlyingArray = writer.ToArray(); for(var i = 0; i < array.Length; ++i) { Assert.AreEqual(array[i], underlyingArray[i]); @@ -1768,26 +1769,6 @@ public void TestToArray() } } - [Test] - public unsafe void TestGetUnsafePtr() - { - var testStruct = GetTestStruct(); - var requiredSize = FastBufferWriter.GetWriteSize(testStruct); - var writer = new FastBufferWriter(requiredSize, Allocator.Temp); - - using (writer) - { - writer.VerifyCanWrite(requiredSize); - writer.WriteValue(testStruct); - var ptr = writer.GetUnsafePtr(); - var underlyingArrayPtr = writer.GetNativeArray().GetUnsafePtr(); - Assert.IsTrue(underlyingArrayPtr == ptr); - - var ptrAtPosition = writer.GetUnsafePtrAtCurrentPosition(); - Assert.IsTrue((byte*)underlyingArrayPtr + writer.Position == ptrAtPosition); - } - } - [Test] public void TestThrowingIfBoundsCheckingSkipped() { @@ -1900,7 +1881,7 @@ public void TestSeeking() Assert.AreEqual(writer.Length, 10); var expected = new byte[] {1, 3, 2, 5, 4, 0}; - var underlyingArray = writer.GetNativeArray(); + var underlyingArray = writer.ToArray(); for (var i = 0; i < expected.Length; ++i) { Assert.AreEqual(expected[i], underlyingArray[i]); @@ -1958,6 +1939,7 @@ public void TestGrowth() // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + var preGrowthLength = writer.Position; // First writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.VerifyCanWriteValue(testStruct)); @@ -1968,17 +1950,17 @@ public void TestGrowth() // First writer shouldn't have grown Assert.AreEqual(150, writer.Capacity); - Assert.AreEqual(150, writer.GetNativeArray().Length); + Assert.AreEqual(preGrowthLength, writer.ToArray().Length); // First growth doubles the size Assert.AreEqual(300, growingWriter.Capacity); - Assert.AreEqual(300, growingWriter.GetNativeArray().Length); + Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); growingWriter.WriteValue(testStruct); // Write right up to the very end of the buffer, verify it doesn't grow growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); Assert.AreEqual(300, growingWriter.Capacity); - Assert.AreEqual(300, growingWriter.GetNativeArray().Length); + Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); growingWriter.WriteValue(testStruct); // Go to the end of the buffer and grow again @@ -1988,14 +1970,14 @@ public void TestGrowth() // Second growth caps it at maxSize Assert.AreEqual(500, growingWriter.Capacity); - Assert.AreEqual(500, growingWriter.GetNativeArray().Length); + Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - VerifyBytewiseEquality(testStruct, writer.GetNativeArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, writer.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); // Verify the growth properly copied the existing data - VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 150-FastBufferWriter.GetWriteSize(testStruct)+1, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 300-FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.GetNativeArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150-FastBufferWriter.GetWriteSize(testStruct)+1, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300-FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); } } @@ -2045,8 +2027,8 @@ public void TestNetworkBehaviour() writer.WriteValue(networkBehaviour); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, sizeof(ulong), sizeof(ushort)); } }); @@ -2065,7 +2047,7 @@ public void TestNetworkObject() writer.WriteValue(networkObject); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); } }); } @@ -2083,7 +2065,7 @@ public void TestGameObject() writer.WriteValue(obj); Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); } }); } @@ -2099,8 +2081,8 @@ public void TestNetworkBehaviourSafe() writer.WriteValueSafe(networkBehaviour); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, sizeof(ulong), sizeof(ushort)); } }); @@ -2117,7 +2099,7 @@ public void TestNetworkObjectSafe() writer.WriteValueSafe(networkObject); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); } }); } @@ -2133,7 +2115,7 @@ public void TestGameObjectSafe() writer.WriteValueSafe(obj); Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 0, sizeof(ulong)); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); } }); } @@ -2150,9 +2132,9 @@ public void TestNetworkBehaviourAsObject() writer.WriteObject(networkBehaviour); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); - Assert.AreEqual(0, writer.GetNativeArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.GetNativeArray(), 0, + Assert.AreEqual(0, writer.ToArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); + VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, sizeof(ulong)+1, sizeof(ushort)); } }); @@ -2170,8 +2152,8 @@ public void TestNetworkObjectAsObject() writer.WriteObject(networkObject); Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); - Assert.AreEqual(0, writer.GetNativeArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(0, writer.ToArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); } }); } @@ -2188,8 +2170,8 @@ public void TestGameObjectAsObject() writer.WriteObject(obj); Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); - Assert.AreEqual(0, writer.GetNativeArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.GetNativeArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(0, writer.ToArray()[0]); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); } }); } @@ -2202,7 +2184,7 @@ public void TestVerifyInternalDoesntReduceAllowedWritePoint() { writer.VerifyCanWrite(25); writer.VerifyCanWriteInternal(5); - Assert.AreEqual(writer.m_InternalData.AllowedWriteMark, 25); + Assert.AreEqual(writer.m_AllowedWriteMark, 25); } } #endregion From fc944c48f3907919a75ccf95c70fbc0e6e01a1b9 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 19 Aug 2021 17:40:17 -0500 Subject: [PATCH 06/29] Added utility ref struct "Ref" to more generically support wrapping values in other ref structs in a capture-by-reference style, updated BitReader and BitWriter to use that. Also aggressively inlining properties in FBW and FBR. --- .../Runtime/Serialization/BitReader.cs | 27 ++++++++--------- .../Runtime/Serialization/BitWriter.cs | 30 ++++++++----------- .../Runtime/Serialization/FastBufferReader.cs | 15 ++++++++-- .../Runtime/Serialization/FastBufferWriter.cs | 21 +++++++++++-- .../Runtime/Utility.meta | 3 ++ .../Runtime/Utility/Ref.cs | 23 ++++++++++++++ .../Runtime/Utility/Ref.cs.meta | 11 +++++++ 7 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index a194e228ad..e4ed71af02 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -8,7 +8,7 @@ namespace Unity.Multiplayer.Netcode { public ref struct BitReader { - private unsafe FastBufferReader* m_Reader; + private Ref m_Reader; private unsafe byte* m_BufferPointer; private int m_Position; private const int BITS_PER_BYTE = 8; @@ -20,37 +20,34 @@ public ref struct BitReader /// /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool BitAligned() + public bool BitAligned { - return (m_BitPosition & 7) == 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (m_BitPosition & 7) == 0; } internal unsafe BitReader(ref FastBufferReader reader) { - fixed (FastBufferReader* readerPtr = &reader) - { - m_Reader = readerPtr; - } + m_Reader = new Ref(ref reader); - m_BufferPointer = m_Reader->m_BufferPointer + m_Reader->Position; - m_Position = m_Reader->Position; + m_BufferPointer = m_Reader.Value.m_BufferPointer + m_Reader.Value.Position; + m_Position = m_Reader.Value.Position; m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseReadMark = (m_Reader->m_AllowedReadMark - m_Position) * BITS_PER_BYTE; + m_AllowedBitwiseReadMark = (m_Reader.Value.m_AllowedReadMark - m_Position) * BITS_PER_BYTE; #endif } public unsafe void Dispose() { var bytesWritten = m_BitPosition >> 3; - if (!BitAligned()) + if (!BitAligned) { // Accounting for the partial read ++bytesWritten; } - m_Reader->CommitBitwiseReads(bytesWritten); + m_Reader.Value.CommitBitwiseReads(bytesWritten); } public unsafe bool VerifyCanReadBits(int bitCount) @@ -63,7 +60,7 @@ public unsafe bool VerifyCanReadBits(int bitCount) ++totalBytesWrittenInBitwiseContext; } - if (m_Reader->m_Position + totalBytesWrittenInBitwiseContext > m_Reader->m_Length) + if (m_Reader.Value.m_Position + totalBytesWrittenInBitwiseContext > m_Reader.Value.m_Length) { return false; } @@ -101,7 +98,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &val; - if (BitAligned()) + if (BitAligned) { if (wholeBytes != 0) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index b941d36f39..03658b0b2d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -6,7 +6,7 @@ namespace Unity.Multiplayer.Netcode { public ref struct BitWriter { - private unsafe FastBufferWriter* m_Writer; + private Ref m_Writer; private unsafe byte* m_BufferPointer; private int m_Position; internal int m_BitPosition; @@ -18,25 +18,21 @@ public ref struct BitWriter /// /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool BitAligned() + public bool BitAligned { - return (m_BitPosition & 7) == 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (m_BitPosition & 7) == 0; } internal unsafe BitWriter(ref FastBufferWriter writer) { - fixed (FastBufferWriter* internalDataPtr = &writer) - { - m_Writer = internalDataPtr; - } - + m_Writer = new Ref(ref writer); m_BufferPointer = writer.m_BufferPointer + writer.m_Position; m_Position = writer.m_Position; m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseWriteMark = (m_Writer->m_AllowedWriteMark - m_Writer->Position) * BITS_PER_BYTE; + m_AllowedBitwiseWriteMark = (m_Writer.Value.m_AllowedWriteMark - m_Writer.Value.Position) * BITS_PER_BYTE; #endif } @@ -50,12 +46,12 @@ public unsafe bool VerifyCanWriteBits(int bitCount) ++totalBytesWrittenInBitwiseContext; } - if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer->m_Capacity) + if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.m_Capacity) { - if (m_Writer->m_Capacity < m_Writer->m_MaxCapacity) + if (m_Writer.Value.m_Capacity < m_Writer.Value.m_MaxCapacity) { - m_Writer->Grow(); - m_BufferPointer = m_Writer->m_BufferPointer; + m_Writer.Value.Grow(); + m_BufferPointer = m_Writer.Value.m_BufferPointer + m_Writer.Value.m_Position; } else { @@ -71,13 +67,13 @@ public unsafe bool VerifyCanWriteBits(int bitCount) public unsafe void Dispose() { var bytesWritten = m_BitPosition >> 3; - if (!BitAligned()) + if (!BitAligned) { // Accounting for the partial write ++bytesWritten; } - m_Writer->CommitBitwiseWrites(bytesWritten); + m_Writer.Value.CommitBitwiseWrites(bytesWritten); } /// @@ -107,7 +103,7 @@ public unsafe void WriteBits(ulong value, int bitCount) int wholeBytes = bitCount / BITS_PER_BYTE; byte* asBytes = (byte*) &value; - if (BitAligned()) + if (BitAligned) { if (wholeBytes != 0) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index a46f6967a1..053f5ce4e0 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -138,9 +138,18 @@ public BitReader EnterBitwiseContext() #endif return new BitReader(ref this); } - - public int Position => m_Position; - public int Length => m_Length; + + public int Position + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position; + } + + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Length; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 6d3df83614..73152a5045 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -100,9 +100,24 @@ public BitWriter EnterBitwiseContext() return new BitWriter(ref this); } - public int Position => m_Position; - public int Capacity => m_Capacity; - public int Length => m_Position > m_Length ? m_Position : m_Length; + + public int Position + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position; + } + + public int Capacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Capacity; + } + + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position > m_Length ? m_Position : m_Length; + } internal unsafe void Grow() { diff --git a/com.unity.netcode.gameobjects/Runtime/Utility.meta b/com.unity.netcode.gameobjects/Runtime/Utility.meta new file mode 100644 index 0000000000..a71098491b --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 07815748c1ee48a69d6981ec990575ed +timeCreated: 1629412677 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs new file mode 100644 index 0000000000..f58938a7e7 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; + +namespace Unity.Multiplayer.Netcode +{ + public ref struct Ref where T: unmanaged + { + private unsafe T* m_Value; + + public unsafe Ref(ref T value) + { + fixed (T* ptr = &value) + { + m_Value = ptr; + } + } + + public unsafe ref T Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref *m_Value; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs.meta new file mode 100644 index 0000000000..56afe0844f --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00adc41a9c8699349a50dba23dbd46a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From cb95862194f8cca0e5355439f7c1a5b5ce46056a Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 23 Aug 2021 17:29:12 -0500 Subject: [PATCH 07/29] BufferSerializer and tests. --- .../Runtime/Serialization/BitReader.cs | 2 +- .../Runtime/Serialization/BitWriter.cs | 4 +- .../Runtime/Serialization/BufferSerializer.cs | 105 +++ .../Serialization/BufferSerializer.cs.meta | 11 + .../Serialization/BufferSerializerReader.cs | 119 ++++ .../BufferSerializerReader.cs.meta | 11 + .../Serialization/BufferSerializerWriter.cs | 119 ++++ .../BufferSerializerWriter.cs.meta | 11 + .../Runtime/Serialization/FastBufferReader.cs | 6 +- .../Runtime/Serialization/FastBufferWriter.cs | 2 +- .../Serialization/IBufferSerializer.cs | 42 ++ .../Serialization/IBufferSerializer.cs.meta | 11 + .../Runtime/Utility/Ref.cs | 4 +- .../Runtime/Utility/RefArray.cs | 92 +++ .../Runtime/Utility/RefArray.cs.meta | 11 + .../Editor/Serialization/BitReaderTests.cs | 27 + .../Editor/Serialization/BitWriterTests.cs | 26 + .../Serialization/BufferSerializerTests.cs | 662 ++++++++++++++++++ .../BufferSerializerTests.cs.meta | 11 + 19 files changed, 1268 insertions(+), 8 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index e4ed71af02..5cda1a6bf6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -188,7 +188,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); break; case 7: - *(uint*) ptr = *(uint*)ptr; + *(uint*) ptr = *(uint*)bufferPointer; *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); *(ptr+6) = *(bufferPointer+6); break; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 03658b0b2d..1af6025a90 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -195,11 +195,11 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy *(bufferPointer+4) = *(ptr+4); break; case 6: - *(uint*) bufferPointer = *(uint*) &ptr; + *(uint*) bufferPointer = *(uint*)ptr; *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); break; case 7: - *(uint*) bufferPointer = *(uint*) &value; + *(uint*) bufferPointer = *(uint*)ptr; *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); *(bufferPointer+6) = *(ptr+6); break; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs new file mode 100644 index 0000000000..5e409704e9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -0,0 +1,105 @@ +using System; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.Serialization +{ + public ref struct BufferSerializer where T : IBufferSerializerImplementation + { + private T m_Implementation; + + public bool IsReader => m_Implementation.IsReader; + public bool IsWriter => m_Implementation.IsWriter; + + public BufferSerializer(T implementation) + { + m_Implementation = implementation; + } + + public ref FastBufferReader GetFastBufferReader() + { + return ref m_Implementation.GetFastBufferReader(); + } + public ref FastBufferWriter GetFastBufferWriter() + { + return ref m_Implementation.GetFastBufferWriter(); + } + + public void SerializeValue(ref object value, Type type, bool isNullable = false) + { + m_Implementation.SerializeValue(ref value, type, isNullable); + } + public void SerializeValue(ref INetworkSerializable value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref GameObject value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref NetworkObject value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref NetworkBehaviour value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref string s, bool oneByteChars = false) + { + m_Implementation.SerializeValue(ref s, oneByteChars); + } + public void SerializeValue(ref T[] array) where T : unmanaged + { + m_Implementation.SerializeValue(ref array); + } + public void SerializeValue(ref byte value) + { + m_Implementation.SerializeValue(ref value); + } + public void SerializeValue(ref T value) where T : unmanaged + { + m_Implementation.SerializeValue(ref value); + } + + // Has to have a different name to avoid conflicting with "where T: unmananged" + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + { + m_Implementation.SerializeNetworkSerializable(ref value); + } + + public bool PreCheck(int amount) + { + return m_Implementation.PreCheck(amount); + } + + public void SerializeValuePreChecked(ref GameObject value) + { + m_Implementation.SerializeValuePreChecked(ref value); + } + public void SerializeValuePreChecked(ref NetworkObject value) + { + m_Implementation.SerializeValuePreChecked(ref value); + } + public void SerializeValuePreChecked(ref NetworkBehaviour value) + { + m_Implementation.SerializeValuePreChecked(ref value); + } + public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) + { + m_Implementation.SerializeValuePreChecked(ref s, oneByteChars); + } + public void SerializeValuePreChecked(ref T[] array) where T : unmanaged + { + m_Implementation.SerializeValuePreChecked(ref array); + } + public void SerializeValuePreChecked(ref byte value) + { + m_Implementation.SerializeValuePreChecked(ref value); + } + public void SerializeValuePreChecked(ref T value) where T : unmanaged + { + m_Implementation.SerializeValuePreChecked(ref value); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs.meta new file mode 100644 index 0000000000..fe70dbe5be --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fca519b9bc3b32e4f9bd7cd138d690af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs new file mode 100644 index 0000000000..7016806da9 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -0,0 +1,119 @@ +using System; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.Serialization +{ + public struct BufferSerializerReader : IBufferSerializerImplementation + { + private Ref m_Reader; + + public BufferSerializerReader(ref FastBufferReader reader) + { + m_Reader = new Ref(ref reader); + } + + public bool IsReader => true; + public bool IsWriter => false; + + public ref FastBufferReader GetFastBufferReader() + { + return ref m_Reader.Value; + } + + public ref FastBufferWriter GetFastBufferWriter() + { + throw new InvalidOperationException("Cannot retrieve a FastBufferWriter from a serializer where IsWriter = false"); + } + + public void SerializeValue(ref object value, Type type, bool isNullable = false) + { + m_Reader.Value.ReadObject(out value, type, isNullable); + } + + public void SerializeValue(ref INetworkSerializable value) + { + m_Reader.Value.ReadNetworkSerializable(out value); + } + + public void SerializeValue(ref GameObject value) + { + m_Reader.Value.ReadValueSafe(out value); + } + + public void SerializeValue(ref NetworkObject value) + { + m_Reader.Value.ReadValueSafe(out value); + } + + public void SerializeValue(ref NetworkBehaviour value) + { + m_Reader.Value.ReadValueSafe(out value); + } + + public void SerializeValue(ref string s, bool oneByteChars = false) + { + m_Reader.Value.ReadValueSafe(out s, oneByteChars); + } + + public void SerializeValue(ref T[] array) where T : unmanaged + { + m_Reader.Value.ReadValueSafe(out array); + } + + public void SerializeValue(ref byte value) + { + m_Reader.Value.ReadByteSafe(out value); + } + + public void SerializeValue(ref T value) where T : unmanaged + { + m_Reader.Value.ReadValueSafe(out value); + } + + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + { + m_Reader.Value.ReadNetworkSerializable(out value); + } + + public bool PreCheck(int amount) + { + return m_Reader.Value.VerifyCanRead(amount); + } + + public void SerializeValuePreChecked(ref GameObject value) + { + m_Reader.Value.ReadValue(out value); + } + + public void SerializeValuePreChecked(ref NetworkObject value) + { + m_Reader.Value.ReadValue(out value); + } + + public void SerializeValuePreChecked(ref NetworkBehaviour value) + { + m_Reader.Value.ReadValue(out value); + } + + public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) + { + m_Reader.Value.ReadValue(out s, oneByteChars); + } + + public void SerializeValuePreChecked(ref T[] array) where T : unmanaged + { + m_Reader.Value.ReadValue(out array); + } + + public void SerializeValuePreChecked(ref byte value) + { + m_Reader.Value.ReadValue(out value); + } + + public void SerializeValuePreChecked(ref T value) where T : unmanaged + { + m_Reader.Value.ReadValue(out value); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs.meta new file mode 100644 index 0000000000..ed94cf148a --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 70dd17b6c14f7cd43ba5380d01cf91ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs new file mode 100644 index 0000000000..2b16b718fa --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -0,0 +1,119 @@ +using System; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.Serialization +{ + public struct BufferSerializerWriter : IBufferSerializerImplementation + { + private Ref m_Writer; + + public BufferSerializerWriter(ref FastBufferWriter writer) + { + m_Writer = new Ref(ref writer); + } + + public bool IsReader => false; + public bool IsWriter => true; + + public ref FastBufferReader GetFastBufferReader() + { + throw new InvalidOperationException("Cannot retrieve a FastBufferReader from a serializer where IsReader = false"); + } + + public ref FastBufferWriter GetFastBufferWriter() + { + return ref m_Writer.Value; + } + + public void SerializeValue(ref object value, Type type, bool isNullable = false) + { + m_Writer.Value.WriteObject(value, isNullable); + } + + public void SerializeValue(ref INetworkSerializable value) + { + m_Writer.Value.WriteNetworkSerializable(value); + } + + public void SerializeValue(ref GameObject value) + { + m_Writer.Value.WriteValueSafe(value); + } + + public void SerializeValue(ref NetworkObject value) + { + m_Writer.Value.WriteValueSafe(value); + } + + public void SerializeValue(ref NetworkBehaviour value) + { + m_Writer.Value.WriteValueSafe(value); + } + + public void SerializeValue(ref string s, bool oneByteChars = false) + { + m_Writer.Value.WriteValueSafe(s, oneByteChars); + } + + public void SerializeValue(ref T[] array) where T : unmanaged + { + m_Writer.Value.WriteValueSafe(array); + } + + public void SerializeValue(ref byte value) + { + m_Writer.Value.WriteByteSafe(value); + } + + public void SerializeValue(ref T value) where T : unmanaged + { + m_Writer.Value.WriteValueSafe(value); + } + + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + { + m_Writer.Value.WriteNetworkSerializable(value); + } + + public bool PreCheck(int amount) + { + return m_Writer.Value.VerifyCanWrite(amount); + } + + public void SerializeValuePreChecked(ref GameObject value) + { + m_Writer.Value.WriteValue(value); + } + + public void SerializeValuePreChecked(ref NetworkObject value) + { + m_Writer.Value.WriteValue(value); + } + + public void SerializeValuePreChecked(ref NetworkBehaviour value) + { + m_Writer.Value.WriteValue(value); + } + + public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) + { + m_Writer.Value.WriteValue(s, oneByteChars); + } + + public void SerializeValuePreChecked(ref T[] array) where T : unmanaged + { + m_Writer.Value.WriteValue(array); + } + + public void SerializeValuePreChecked(ref byte value) + { + m_Writer.Value.WriteByte(value); + } + + public void SerializeValuePreChecked(ref T value) where T : unmanaged + { + m_Writer.Value.WriteValue(value); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs.meta new file mode 100644 index 0000000000..183a7bafad --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c05ed8e3061e62147a012cc01a64b5a5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 053f5ce4e0..ff9254f9a8 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -358,10 +358,10 @@ public void ReadObject(out object value, Type type, bool isNullable = false) throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); } - /*public void ReadValue(ref T value) where T : INetworkSerializable + public void ReadNetworkSerializable(out T value) where T : INetworkSerializable { - // TODO - }*/ + throw new NotImplementedException(); + } public void ReadValue(out GameObject value) { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 73152a5045..508dc1d95e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -340,7 +340,7 @@ public void WriteObject(object value, bool isNullable = false) throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } - public void WriteValue(T value) where T : INetworkSerializable + public void WriteNetworkSerializable(in T value) where T : INetworkSerializable { // TODO } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs new file mode 100644 index 0000000000..f421088482 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs @@ -0,0 +1,42 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Multiplayer.Netcode; +using UnityEngine; + +namespace Unity.Netcode.Serialization +{ + public interface IBufferSerializerImplementation + { + public bool IsReader { get; } + public bool IsWriter { get; } + + public ref FastBufferReader GetFastBufferReader(); + public ref FastBufferWriter GetFastBufferWriter(); + + public void SerializeValue(ref object value, Type type, bool isNullable = false); + public void SerializeValue(ref INetworkSerializable value); + public void SerializeValue(ref GameObject value); + public void SerializeValue(ref NetworkObject value); + public void SerializeValue(ref NetworkBehaviour value); + public void SerializeValue(ref string s, bool oneByteChars = false); + public void SerializeValue(ref T[] array) where T : unmanaged; + public void SerializeValue(ref byte value); + public void SerializeValue(ref T value) where T : unmanaged; + + // Has to have a different name to avoid conflicting with "where T: unmananged" + // Using SerializeValue(INetworkSerializable) will result in boxing on struct INetworkSerializables + // So this is provided as an alternative to avoid boxing allocations. + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable; + + public bool PreCheck(int amount); + public void SerializeValuePreChecked(ref GameObject value); + public void SerializeValuePreChecked(ref NetworkObject value); + public void SerializeValuePreChecked(ref NetworkBehaviour value); + public void SerializeValuePreChecked(ref string s, bool oneByteChars = false); + public void SerializeValuePreChecked(ref T[] array) where T : unmanaged; + public void SerializeValuePreChecked(ref byte value); + public void SerializeValuePreChecked(ref T value) where T : unmanaged; + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta new file mode 100644 index 0000000000..6dd5e01fcc --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e09b1183d63526341acdacfebdad750d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs index f58938a7e7..877ce2ca52 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -2,7 +2,7 @@ namespace Unity.Multiplayer.Netcode { - public ref struct Ref where T: unmanaged + public struct Ref where T: unmanaged { private unsafe T* m_Value; @@ -14,6 +14,8 @@ public unsafe Ref(ref T value) } } + public unsafe bool IsSet => m_Value != null; + public unsafe ref T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs new file mode 100644 index 0000000000..623a87c008 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Unity.Multiplayer.Netcode +{ + public ref struct RefArray where T: unmanaged + { + public struct RefArrayImplementation : IReadOnlyList + where T : unmanaged + { + internal unsafe T* m_Value; + internal int m_Length; + + internal unsafe RefArrayImplementation(T* ptr, int length) + { + m_Value = ptr; + m_Length = length; + } + + public unsafe ref T Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref *m_Value; + } + + public struct Enumerator : IEnumerator, IEnumerator, IDisposable + { + private RefArrayImplementation m_array; + private int m_Index; + + public Enumerator(ref RefArrayImplementation array) + { + this.m_array = array; + this.m_Index = -1; + } + + public void Dispose() + { + } + + public bool MoveNext() + { + ++this.m_Index; + return this.m_Index < this.m_array.Length; + } + + public void Reset() => this.m_Index = -1; + + public T Current => this.m_array[this.m_Index]; + + object IEnumerator.Current => (object) this.Current; + } + + public RefArrayImplementation.Enumerator GetEnumerator() => + new RefArrayImplementation.Enumerator(ref this); + + IEnumerator IEnumerable.GetEnumerator() => + (IEnumerator) new RefArrayImplementation.Enumerator(ref this); + + IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator(); + + public int Count => m_Length; + public int Length => m_Length; + + public unsafe T this[int index] + { + get => m_Value[index]; + set => m_Value[index] = value; + } + } + + internal RefArrayImplementation m_Value; + + public unsafe RefArray(T* ptr, int length) + { + m_Value = new RefArrayImplementation(ptr, length); + } + + public unsafe ref RefArrayImplementation Value + { + get + { + fixed (RefArrayImplementation* ptr = &m_Value) + { + return ref *ptr; + } + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta new file mode 100644 index 0000000000..9e30a4c74c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1422b1128ad62024d9e8dac38e009aad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index a395fa43b0..c8fa6a5c4a 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -278,6 +278,33 @@ public void TestReadingMultipleBitsToLongs() } } + [Test] + public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) + { + ulong value = 0xFFFFFFFFFFFFFFFF; + FastBufferReader reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong)); + using (reader) + { + ulong* asUlong = (ulong*) reader.GetUnsafePtr(); + + Assert.AreEqual(value, *asUlong); + var mask = 0UL; + for (var i = 0; i < numBits; ++i) + { + mask |= (1UL << i); + } + + ulong readValue; + + Assert.IsTrue(reader.VerifyCanRead(sizeof(ulong))); + using (var bitReader = reader.EnterBitwiseContext()) + { + bitReader.ReadBits(out readValue, numBits); + } + Assert.AreEqual(value & mask, readValue); + } + } + [Test] public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index 7614242e93..d794bb6cb9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -233,6 +233,32 @@ public unsafe void TestWritingMultipleBitsFromLongs() } } + [Test] + public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) + { + FastBufferWriter writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); + using (writer) + { + ulong* asUlong = (ulong*) writer.GetUnsafePtr(); + + Assert.AreEqual(0, *asUlong); + var mask = 0UL; + for (var i = 0; i < numBits; ++i) + { + mask |= (1UL << i); + } + + ulong value = 0xFFFFFFFFFFFFFFFF; + + Assert.IsTrue(writer.VerifyCanWrite(sizeof(ulong))); + using (var bitWriter = writer.EnterBitwiseContext()) + { + bitWriter.WriteBits(value, numBits); + } + Assert.AreEqual(value & mask, *asUlong); + } + } + [Test] public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs new file mode 100644 index 0000000000..6ea498b5dd --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -0,0 +1,662 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Unity.Collections; +using Unity.Multiplayer.Netcode; +using Unity.Netcode.Serialization; +using UnityEngine; +using UnityEngine.SceneManagement; +using Random = System.Random; + +namespace Unity.Netcode.EditorTests +{ + public class BufferSerializerTests + { + [Test] + public void TestIsReaderIsWriter() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + Assert.IsFalse(serializer.IsReader); + Assert.IsTrue(serializer.IsWriter); + } + byte[] readBuffer = new byte[4]; + FastBufferReader reader = new FastBufferReader(readBuffer, Allocator.Temp); + using (reader) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + Assert.IsTrue(serializer.IsReader); + Assert.IsFalse(serializer.IsWriter); + } + } + [Test] + public unsafe void TestGetUnderlyingStructs() + { + FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + ref FastBufferWriter underlyingWriter = ref serializer.GetFastBufferWriter(); + fixed (FastBufferWriter* ptr = &underlyingWriter) + { + Assert.IsTrue(ptr == &writer); + } + // Can't use Assert.Throws() because ref structs can't be passed into lambdas. + try + { + serializer.GetFastBufferReader(); + } + catch (InvalidOperationException) + { + // pass + } + + } + byte[] readBuffer = new byte[4]; + FastBufferReader reader = new FastBufferReader(readBuffer, Allocator.Temp); + using (reader) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + ref FastBufferReader underlyingReader = ref serializer.GetFastBufferReader(); + fixed (FastBufferReader* ptr = &underlyingReader) + { + Assert.IsTrue(ptr == &reader); + } + // Can't use Assert.Throws() because ref structs can't be passed into lambdas. + try + { + serializer.GetFastBufferWriter(); + } + catch (InvalidOperationException) + { + // pass + } + } + } + + // Not reimplementing the entire suite of all value tests for BufferSerializer since they're already tested + // for the underlying structures. These are just basic tests to make sure the correct underlying functions + // are being called. + [Test] + public void TestSerializingObjects() + { + Random random = new Random(); + int value = random.Next(); + object asObj = value; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref asObj, typeof(int)); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + object readValue = 0; + deserializer.SerializeValue(ref readValue, typeof(int)); + + Assert.AreEqual(value, readValue); + } + } + } + + [Test] + public void TestSerializingValues() + { + Random random = new Random(); + int value = random.Next(); + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + int readValue = 0; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingBytes() + { + Random random = new Random(); + byte value = (byte)random.Next(); + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + byte readValue = 0; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingArrays() + { + Random random = new Random(); + int[] value = {random.Next(), random.Next(), random.Next()}; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + int[] readValue = null; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingStrings([Values] bool oneBytChars) + { + string value = "I am a test string"; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref value, oneBytChars); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + string readValue = null; + deserializer.SerializeValue(ref readValue, oneBytChars); + + Assert.AreEqual(value, readValue); + } + } + } + + + [Test] + public void TestSerializingValuesPreChecked() + { + Random random = new Random(); + int value = random.Next(); + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref value); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + serializer.SerializeValuePreChecked(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + int readValue = 0; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingBytesPreChecked() + { + Random random = new Random(); + byte value = (byte)random.Next(); + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref value); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + serializer.SerializeValuePreChecked(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + byte readValue = 0; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingArraysPreChecked() + { + Random random = new Random(); + int[] value = {random.Next(), random.Next(), random.Next()}; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref value); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + serializer.SerializeValuePreChecked(ref value); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + int[] readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(value, readValue); + } + } + } + [Test] + public void TestSerializingStringsPreChecked([Values] bool oneBytChars) + { + string value = "I am a test string"; + + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref value, oneBytChars); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars))); + serializer.SerializeValuePreChecked(ref value, oneBytChars); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + string readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue, oneBytChars); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars))); + deserializer.SerializeValuePreChecked(ref readValue, oneBytChars); + + Assert.AreEqual(value, readValue); + } + } + } + /* + public void SerializeValue(ref object value, Type type, bool isNullable = false); + public void SerializeValue(ref INetworkSerializable value); + public void SerializeValue(ref GameObject value); + public void SerializeValue(ref NetworkObject value); + public void SerializeValue(ref NetworkBehaviour value); + + public bool PreCheck(int amount); + public void SerializeValuePreChecked(ref GameObject value); + public void SerializeValuePreChecked(ref NetworkObject value); + public void SerializeValuePreChecked(ref NetworkBehaviour value);*/ + + + private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, + NetworkObject networkObject); + private void RunGameObjectTest(GameObjectTestDelegate testCode) + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkManager.SetSingleton(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartServer(); + + try + { + testCode(obj, networkBehaviour, networkObject); + } + finally + { + GameObject.DestroyImmediate(obj); + networkManager.StopServer(); + } + } + + [Test] + public void TestSerializingGameObjects() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref obj); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + GameObject readValue = null; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(obj, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingNetworkObjects() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref networkObject); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + NetworkObject readValue = null; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(networkObject, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingNetworkBehaviours() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + serializer.SerializeValue(ref networkBehaviour); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + NetworkBehaviour readValue = null; + deserializer.SerializeValue(ref readValue); + + Assert.AreEqual(networkBehaviour, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingGameObjectsPreChecked() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref obj); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(obj))); + serializer.SerializeValuePreChecked(ref obj); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + GameObject readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(obj, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingNetworkObjectsPreChecked() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref networkObject); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkObject))); + serializer.SerializeValuePreChecked(ref networkObject); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + NetworkObject readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(networkObject, readValue); + } + } + } + ); + } + + [Test] + public void TestSerializingNetworkBehavioursPreChecked() + { + RunGameObjectTest((obj, networkBehaviour, networkObject) => + { + FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + BufferSerializer serializer = + new BufferSerializer(new BufferSerializerWriter(ref writer)); + try + { + serializer.SerializeValuePreChecked(ref networkBehaviour); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkBehaviour))); + serializer.SerializeValuePreChecked(ref networkBehaviour); + + FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) + { + BufferSerializer deserializer = + new BufferSerializer(new BufferSerializerReader(ref reader)); + NetworkBehaviour readValue = null; + try + { + deserializer.SerializeValuePreChecked(ref readValue); + } + catch (OverflowException e) + { + // Pass + } + + Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + deserializer.SerializeValuePreChecked(ref readValue); + + Assert.AreEqual(networkBehaviour, readValue); + } + } + } + ); + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs.meta new file mode 100644 index 0000000000..cb62c7e2d2 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0cf899c97866c76498b71585a61a8142 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From a3e0abcc119beeb707558409b2eca71175b96d2a Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 23 Aug 2021 17:29:51 -0500 Subject: [PATCH 08/29] Removed unnecessary comment. --- .../Editor/Serialization/BufferSerializerTests.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 6ea498b5dd..4c6f967c00 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -390,18 +390,6 @@ public void TestSerializingStringsPreChecked([Values] bool oneBytChars) } } } - /* - public void SerializeValue(ref object value, Type type, bool isNullable = false); - public void SerializeValue(ref INetworkSerializable value); - public void SerializeValue(ref GameObject value); - public void SerializeValue(ref NetworkObject value); - public void SerializeValue(ref NetworkBehaviour value); - - public bool PreCheck(int amount); - public void SerializeValuePreChecked(ref GameObject value); - public void SerializeValuePreChecked(ref NetworkObject value); - public void SerializeValuePreChecked(ref NetworkBehaviour value);*/ - private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, NetworkObject networkObject); From ea31f101610867af216d209d8734cbbc31d3aeae Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Mon, 23 Aug 2021 22:24:33 -0500 Subject: [PATCH 09/29] XMLDocs + cleanup for PR --- .../Runtime/Serialization/BitCounter.cs | 88 ++-- .../Runtime/Serialization/BitReader.cs | 78 +-- .../Runtime/Serialization/BitWriter.cs | 91 ++-- .../Runtime/Serialization/BufferSerializer.cs | 226 ++++++++- .../Serialization/BufferSerializerReader.cs | 2 +- .../Serialization/BufferSerializerWriter.cs | 2 +- .../Runtime/Serialization/BytePacker.cs | 173 +++++-- .../Runtime/Serialization/ByteUnpacker.cs | 187 +++++--- .../Runtime/Serialization/BytewiseUtility.cs | 57 +++ ...alizer.cs.meta => BytewiseUtility.cs.meta} | 2 +- .../Runtime/Serialization/FastBufferReader.cs | 263 ++++++++--- .../Runtime/Serialization/FastBufferWriter.cs | 445 ++++++++++++------ ....cs => IBufferSerializerImplementation.cs} | 0 .../IBufferSerializerImplementation.cs.meta | 11 + .../Serialization/SerializationTypeTable.cs | 11 + 15 files changed, 1154 insertions(+), 482 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs rename com.unity.netcode.gameobjects/Runtime/Serialization/{IBufferSerializer.cs.meta => BytewiseUtility.cs.meta} (83%) rename com.unity.netcode.gameobjects/Runtime/Serialization/{IBufferSerializer.cs => IBufferSerializerImplementation.cs} (100%) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs index a357077bb5..44c09dc8cf 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs @@ -53,54 +53,74 @@ public static class BitCounter 14+1, 27+1, 9+1, 5+1, 26+1, 8+1, 25+1, 24+1, }; + /// + /// Get the minimum number of bytes required to represent the given value + /// + /// The value + /// The number of bytes required [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetUsedByteCount(uint b) + public static int GetUsedByteCount(uint value) { - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b = b & ~(b >> 1); - return k_deBruijnTableBytes32[b*k_DeBruijnMagic32 >> 27]; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value = value & ~(value >> 1); + return k_deBruijnTableBytes32[value*k_DeBruijnMagic32 >> 27]; } + /// + /// Get the minimum number of bytes required to represent the given value + /// + /// The value + /// The number of bytes required [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetUsedByteCount(ulong b) + public static int GetUsedByteCount(ulong value) { - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b |= b >> 32; - b = b & ~(b >> 1); - return k_deBruijnTableBytes64[b*k_DeBruijnMagic64 >> 58]; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + value = value & ~(value >> 1); + return k_deBruijnTableBytes64[value*k_DeBruijnMagic64 >> 58]; } + /// + /// Get the minimum number of bits required to represent the given value + /// + /// The value + /// The number of bits required [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetUsedBitCount(uint b) + public static int GetUsedBitCount(uint value) { - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b = b & ~(b >> 1); - return k_deBruijnTableBits32[b*k_DeBruijnMagic32 >> 27]; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value = value & ~(value >> 1); + return k_deBruijnTableBits32[value*k_DeBruijnMagic32 >> 27]; } + /// + /// Get the minimum number of bits required to represent the given value + /// + /// The value + /// The number of bits required [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetUsedBitCount(ulong b) + public static int GetUsedBitCount(ulong value) { - b |= b >> 1; - b |= b >> 2; - b |= b >> 4; - b |= b >> 8; - b |= b >> 16; - b |= b >> 32; - b = b & ~(b >> 1); - return k_deBruijnTableBits64[b*k_DeBruijnMagic64 >> 58]; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + value = value & ~(value >> 1); + return k_deBruijnTableBits64[value*k_DeBruijnMagic64 >> 58]; } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 5cda1a6bf6..7c564c6d0a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,16 +1,19 @@ using System; using System.Runtime.CompilerServices; -using Mono.Cecil; -using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; namespace Unity.Multiplayer.Netcode { + /// + /// Helper class for doing bitwise reads for a FastBufferReader. + /// Ensures all bitwise reads end on proper byte alignment so FastBufferReader doesn't have to be concerned + /// with misaligned reads. + /// public ref struct BitReader { private Ref m_Reader; - private unsafe byte* m_BufferPointer; - private int m_Position; + private readonly unsafe byte* m_BufferPointer; + private readonly int m_Position; private const int BITS_PER_BYTE = 8; private int m_BitPosition; #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -38,7 +41,10 @@ internal unsafe BitReader(ref FastBufferReader reader) #endif } - public unsafe void Dispose() + /// + /// Pads the read bit count to byte alignment and commits the read back to the reader + /// + public void Dispose() { var bytesWritten = m_BitPosition >> 3; if (!BitAligned) @@ -50,7 +56,15 @@ public unsafe void Dispose() m_Reader.Value.CommitBitwiseReads(bytesWritten); } - public unsafe bool VerifyCanReadBits(int bitCount) + /// + /// Verifies the requested bit count can be read from the buffer. + /// This exists as a separate method to allow multiple bit reads to be bounds checked with a single call. + /// If it returns false, you may not read, and in editor and development builds, attempting to do so will + /// throw an exception. In release builds, attempting to do so will read junk memory. + /// + /// Number of bits you want to read, in total + /// True if you can read, false if that would exceed buffer bounds + public bool VerifyCanReadBits(int bitCount) { var newBitPosition = m_BitPosition + bitCount; var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; @@ -122,7 +136,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) /// /// Value to store bits into. /// Amount of bits to read. - public unsafe void ReadBits(out byte value, int bitCount) + public void ReadBits(out byte value, int bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR int checkPos = (m_BitPosition + bitCount); @@ -156,59 +170,17 @@ public unsafe void ReadBit(out bool bit) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + private unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged { - // Switch statement to read small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - switch (bytesToRead) - { - case 1: - ptr[0] = *bufferPointer; - break; - case 2: - *(ushort*) ptr = *(ushort*)bufferPointer; - break; - case 3: - *(ushort*) ptr = *(ushort*)bufferPointer; - *(ptr+2) = *(bufferPointer+2); - break; - case 4: - *(uint*) ptr = *(uint*)bufferPointer; - break; - case 5: - *(uint*) ptr = *(uint*)bufferPointer; - *(ptr+4) = *(bufferPointer+4); - break; - case 6: - *(uint*) ptr = *(uint*)bufferPointer; - *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); - break; - case 7: - *(uint*) ptr = *(uint*)bufferPointer; - *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); - *(ptr+6) = *(bufferPointer+6); - break; - case 8: - *(ulong*) ptr = *(ulong*)bufferPointer; - break; - default: - UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); - break; - } + BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); m_BitPosition += bytesToRead * BITS_PER_BYTE; value = val; } - /// - /// Read a certain amount of bits from the stream. - /// - /// How many bits to read. Minimum 0, maximum 64. - /// The bits that were read private byte ReadByteBits(int bitCount) { if (bitCount > 8) @@ -235,11 +207,11 @@ private byte ReadByteBits(int bitCount) [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void ReadMisaligned(out byte value) { - int off = (int)(m_BitPosition & 7); + int off = m_BitPosition & 7; int pos = m_BitPosition >> 3; int shift1 = 8 - off; - value = (byte)((m_BufferPointer[(int)pos] >> shift1) | (m_BufferPointer[(int)(m_BitPosition += 8) >> 3] << shift1)); + value = (byte)((m_BufferPointer[pos] >> shift1) | (m_BufferPointer[(m_BitPosition += 8) >> 3] << shift1)); } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 1af6025a90..5bbf095641 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,17 +1,21 @@ using System; using System.Runtime.CompilerServices; -using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { + /// + /// Helper class for doing bitwise writes for a FastBufferWriter. + /// Ensures all bitwise writes end on proper byte alignment so FastBufferWriter doesn't have to be concerned + /// with misaligned writes. + /// public ref struct BitWriter { private Ref m_Writer; private unsafe byte* m_BufferPointer; - private int m_Position; - internal int m_BitPosition; + private readonly int m_Position; + private int m_BitPosition; #if DEVELOPMENT_BUILD || UNITY_EDITOR - internal int m_AllowedBitwiseWriteMark; + private int m_AllowedBitwiseWriteMark; #endif private const int BITS_PER_BYTE = 8; @@ -23,8 +27,7 @@ public bool BitAligned [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (m_BitPosition & 7) == 0; } - - + internal unsafe BitWriter(ref FastBufferWriter writer) { m_Writer = new Ref(ref writer); @@ -35,7 +38,31 @@ internal unsafe BitWriter(ref FastBufferWriter writer) m_AllowedBitwiseWriteMark = (m_Writer.Value.m_AllowedWriteMark - m_Writer.Value.Position) * BITS_PER_BYTE; #endif } + + /// + /// Pads the written bit count to byte alignment and commits the write back to the writer + /// + public void Dispose() + { + var bytesWritten = m_BitPosition >> 3; + if (!BitAligned) + { + // Accounting for the partial write + ++bytesWritten; + } + + m_Writer.Value.CommitBitwiseWrites(bytesWritten); + } + /// + /// Verifies the requested bit count can be written to the buffer. + /// This exists as a separate method to allow multiple bit writes to be bounds checked with a single call. + /// If it returns false, you may not write, and in editor and development builds, attempting to do so will + /// throw an exception. In release builds, attempting to do so will write to random memory addresses and cause + /// Bad Things(TM). + /// + /// Number of bits you want to write, in total + /// True if you can write, false if that would exceed buffer bounds public unsafe bool VerifyCanWriteBits(int bitCount) { var newBitPosition = m_BitPosition + bitCount; @@ -64,18 +91,6 @@ public unsafe bool VerifyCanWriteBits(int bitCount) return true; } - public unsafe void Dispose() - { - var bytesWritten = m_BitPosition >> 3; - if (!BitAligned) - { - // Accounting for the partial write - ++bytesWritten; - } - - m_Writer.Value.CommitBitwiseWrites(bytesWritten); - } - /// /// Write s certain amount of bits to the stream. /// @@ -169,47 +184,9 @@ public unsafe void WriteBit(bool bit) [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged { - // Switch statement to write small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - switch (bytesToWrite) - { - case 1: - bufferPointer[0] = *ptr; - break; - case 2: - *(ushort*) bufferPointer = *(ushort*)ptr; - break; - case 3: - *(ushort*) bufferPointer = *(ushort*)ptr; - *(bufferPointer+2) = *(ptr+2); - break; - case 4: - *(uint*) bufferPointer = *(uint*)ptr; - break; - case 5: - *(uint*) bufferPointer = *(uint*)ptr; - *(bufferPointer+4) = *(ptr+4); - break; - case 6: - *(uint*) bufferPointer = *(uint*)ptr; - *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); - break; - case 7: - *(uint*) bufferPointer = *(uint*)ptr; - *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); - *(bufferPointer+6) = *(ptr+6); - break; - case 8: - *(ulong*) bufferPointer = *(ulong*)ptr; - break; - default: - UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); - break; - } + BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); m_BitPosition += bytesToWrite * BITS_PER_BYTE; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index 5e409704e9..cfe11aed19 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -4,99 +4,307 @@ namespace Unity.Netcode.Serialization { - public ref struct BufferSerializer where T : IBufferSerializerImplementation + /// + /// Two-way serializer wrapping FastBufferReader or FastBufferWriter. + /// + /// Implemented as a ref struct for two reasons: + /// 1. The BufferSerializer cannot outlive the FBR/FBW it wraps or using it will cause a crash + /// 2. The BufferSerializer must always be passed by reference and can't be copied + /// + /// Ref structs help enforce both of those rules: they can't out live the stack context in which they were + /// created, and they're always passed by reference no matter what. + /// + /// BufferSerializer doesn't wrapp FastBufferReader or FastBufferWriter directly because it can't. + /// ref structs can't implement interfaces, and in order to be able to have two different implementations with + /// the same interface (which allows us to avoid an "if(IsReader)" on every call), the thing directly wrapping + /// the struct has to implement an interface. So IBufferSerializerImplementation exists as the interface, + /// which is implemented by a normal struct, while the ref struct wraps the normal one to enforce the two above + /// requirements. (Allowing direct access to the IBufferSerializerImplementation struct would allow dangerous + /// things to happen because the struct's lifetime could outlive the Reader/Writer's.) + /// + /// The implementation struct + public ref struct BufferSerializer where TImplementation : IBufferSerializerImplementation { - private T m_Implementation; + private TImplementation m_Implementation; + /// + /// Check if the contained implementation is a reader + /// public bool IsReader => m_Implementation.IsReader; + + /// + /// Check if the contained implementation is a writer + /// public bool IsWriter => m_Implementation.IsWriter; - public BufferSerializer(T implementation) + public BufferSerializer(TImplementation implementation) { m_Implementation = implementation; } + /// + /// Retrieves the FastBufferReader instance. Only valid if IsReader = true, throws + /// InvalidOperationException otherwise. + /// + /// Reader instance public ref FastBufferReader GetFastBufferReader() { return ref m_Implementation.GetFastBufferReader(); } + + /// + /// Retrieves the FastBufferWriter instance. Only valid if IsWriter = true, throws + /// InvalidOperationException otherwise. + /// + /// Writer instance public ref FastBufferWriter GetFastBufferWriter() { return ref m_Implementation.GetFastBufferWriter(); } + /// + /// Serialize an object value. + /// Note: Will ALWAYS cause allocations when reading. + /// This function is also much slower than the others as it has to figure out how to serialize + /// the object using runtime reflection. + /// It's recommended not to use this unless you have no choice. + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize + /// Type to deserialize to when reading + /// + /// If true, will force an isNull byte to be written. + /// Some types will write this byte regardless. + /// public void SerializeValue(ref object value, Type type, bool isNullable = false) { m_Implementation.SerializeValue(ref value, type, isNullable); } + + /// + /// Serialize an INetworkSerializable + /// If your INetworkSerializable is implemented by a struct, as opposed to a class, use this + /// function instead of SerializeValue. SerializeValue will incur a boxing allocation, + /// SerializeNetworkSerializable will not. + /// + /// A definition of SerializeValue that doesn't allocate can't be created because C# + /// doesn't allow overriding generics based solely on the constraint, so this would conflict + /// with WriteValue<T>(ref T value) where T: unmanaged + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize + public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable + { + m_Implementation.SerializeNetworkSerializable(ref value); + } + + /// + /// Serialize an INetworkSerializable + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref INetworkSerializable value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize a GameObject + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref GameObject value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize a NetworkObject + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref NetworkObject value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize a NetworkBehaviour + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref NetworkBehaviour value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize a string. + /// + /// Note: Will ALWAYS allocate a new string when reading. + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize + /// + /// If true, will truncate each char to one byte. + /// This is slower than two-byte chars, but uses less bandwidth. + /// public void SerializeValue(ref string s, bool oneByteChars = false) { m_Implementation.SerializeValue(ref s, oneByteChars); } + + /// + /// Serialize an array value. + /// + /// Note: Will ALWAYS allocate a new array when reading. + /// If you have a statically-sized array that you know is large enough, it's recommended to + /// serialize the size yourself and iterate serializing array members. + /// + /// (This is because C# doesn't allow setting an array's length value, so deserializing + /// into an existing array of larger size would result in an array that doesn't have as many values + /// as its Length indicates it should.) + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref T[] array) where T : unmanaged { m_Implementation.SerializeValue(ref array); } + + /// + /// Serialize a single byte + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref byte value) { m_Implementation.SerializeValue(ref value); } + + /// + /// Serialize an unmanaged type. Supports basic value types as well as structs. + /// The provided type will be copied to/from the buffer as it exists in memory. + /// + /// Throws OverflowException if the end of the buffer has been reached. + /// Write buffers will grow up to the maximum allowable message size before throwing OverflowException. + /// + /// Value to serialize public void SerializeValue(ref T value) where T : unmanaged { m_Implementation.SerializeValue(ref value); } - - // Has to have a different name to avoid conflicting with "where T: unmananged" - public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable - { - m_Implementation.SerializeNetworkSerializable(ref value); - } + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be writing multiple fields back-to-back and you know the total size, + /// you can call PreCheck() once on the total size, and then follow it with calls to + /// SerializeValuePreChecked() for faster serialization. Write buffers will grow during PreCheck() + /// if needed. + /// + /// PreChecked serialization operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using PreCheck(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using PreCheck is to avoid bounds checking in the following + /// operations in release builds. + /// + /// Number of bytes you plan to read or write + /// True if the read/write can proceed, false otherwise. public bool PreCheck(int amount) { return m_Implementation.PreCheck(amount); } + /// + /// Serialize a GameObject + /// + /// Value to serialize public void SerializeValuePreChecked(ref GameObject value) { m_Implementation.SerializeValuePreChecked(ref value); } + + /// + /// Serialize a NetworkObject + /// + /// Value to serialize public void SerializeValuePreChecked(ref NetworkObject value) { m_Implementation.SerializeValuePreChecked(ref value); } + + /// + /// Serialize a NetworkBehaviour + /// + /// Value to serialize public void SerializeValuePreChecked(ref NetworkBehaviour value) { m_Implementation.SerializeValuePreChecked(ref value); } + + /// + /// Serialize a string. + /// + /// Note: Will ALWAYS allocate a new string when reading. + /// + /// Value to serialize + /// + /// If true, will truncate each char to one byte. + /// This is slower than two-byte chars, but uses less bandwidth. + /// public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) { m_Implementation.SerializeValuePreChecked(ref s, oneByteChars); } + + /// + /// Serialize an array value. + /// + /// Note: Will ALWAYS allocate a new array when reading. + /// If you have a statically-sized array that you know is large enough, it's recommended to + /// serialize the size yourself and iterate serializing array members. + /// + /// (This is because C# doesn't allow setting an array's length value, so deserializing + /// into an existing array of larger size would result in an array that doesn't have as many values + /// as its Length indicates it should.) + /// + /// Value to serialize public void SerializeValuePreChecked(ref T[] array) where T : unmanaged { m_Implementation.SerializeValuePreChecked(ref array); } + + /// + /// Serialize a single byte + /// + /// Value to serialize public void SerializeValuePreChecked(ref byte value) { m_Implementation.SerializeValuePreChecked(ref value); } + + /// + /// Serialize an unmanaged type. Supports basic value types as well as structs. + /// The provided type will be copied to/from the buffer as it exists in memory. + /// + /// Value to serialize public void SerializeValuePreChecked(ref T value) where T : unmanaged { m_Implementation.SerializeValuePreChecked(ref value); diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index 7016806da9..e809cec933 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -4,7 +4,7 @@ namespace Unity.Netcode.Serialization { - public struct BufferSerializerReader : IBufferSerializerImplementation + internal struct BufferSerializerReader : IBufferSerializerImplementation { private Ref m_Reader; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index 2b16b718fa..916c0f51ef 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -4,7 +4,7 @@ namespace Unity.Netcode.Serialization { - public struct BufferSerializerWriter : IBufferSerializerImplementation + internal struct BufferSerializerWriter : IBufferSerializerImplementation { private Ref m_Writer; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index 3976032578..f935ad400d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -5,14 +5,24 @@ namespace Unity.Multiplayer.Netcode { + /// + /// Utility class for packing values in serialization. + /// public static class BytePacker { #region Managed TypePacking + /// /// Writes a boxed object in a packed format - /// Named differently from other WriteValuePacked methods to avoid accidental boxing + /// Named differently from other WriteValuePacked methods to avoid accidental boxing. + /// Don't use this method unless you have no other choice. /// + /// Writer to write to /// The object to write + /// + /// If true, an extra byte will be written to indicate whether or not the value is null. + /// Some types will always write this. + /// public static void WriteObjectPacked(ref FastBufferWriter writer, object value, bool isNullable = false) { #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -21,7 +31,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, #endif if (isNullable || value.GetType().IsNullable()) { - bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); + bool isNull = value == null || (value is UnityEngine.Object o && o == null); WriteValuePacked(ref writer, isNull); @@ -140,6 +150,12 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValuePacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else + /// + /// Write a packed enum value. + /// + /// The writer to write to + /// The value to write + /// An enum type [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void WriteValuePacked(ref FastBufferWriter writer, TEnum value) where TEnum : unmanaged, Enum { @@ -162,8 +178,9 @@ public static unsafe void WriteValuePacked(ref FastBufferWriter writer, T } /// - /// Write single-precision floating point value to the stream as a varint + /// Write single-precision floating point value to the buffer as a varint /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, float value) @@ -172,8 +189,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, float value) } /// - /// Write double-precision floating point value to the stream as a varint + /// Write double-precision floating point value to the buffer as a varint /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, double value) @@ -182,97 +200,99 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) } /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Write a byte to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByteSafe(value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Write a signed byte to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, sbyte value) => writer.WriteByteSafe((byte)value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Write a bool to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, bool value) => writer.WriteValueSafe(value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. + /// Write a signed short (Int16) as a ZigZag encoded varint to the buffer. /// WARNING: If the value you're writing is > 2287, this will use MORE space /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. /// Only use this if you're certain your value will be small. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, short value) => WriteUInt32Packed(ref writer, (ushort)Arithmetic.ZigZagEncode(value)); /// - /// Write an unsigned short (UInt16) as a varint to the stream. + /// Write an unsigned short (UInt16) as a varint to the buffer. /// WARNING: If the value you're writing is > 2287, this will use MORE space /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. /// Only use this if you're certain your value will be small. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, ushort value) => WriteUInt32Packed(ref writer, value); /// - /// Write a two-byte character as a varint to the stream. + /// Write a two-byte character as a varint to the buffer. /// WARNING: If the value you're writing is > 2287, this will use MORE space /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. /// Only use this if you're certain your value will be small. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, char c) => WriteUInt32Packed(ref writer, c); /// - /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// Write a signed int (Int32) as a ZigZag encoded varint to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, int value) => WriteUInt32Packed(ref writer, (uint)Arithmetic.ZigZagEncode(value)); /// - /// Write an unsigned int (UInt32) to the stream. + /// Write an unsigned int (UInt32) to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, uint value) => WriteUInt32Packed(ref writer, value); /// - /// Write an unsigned long (UInt64) to the stream. + /// Write an unsigned long (UInt64) to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, ulong value) => WriteUInt64Packed(ref writer, value); /// - /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// Write a signed long (Int64) as a ZigZag encoded varint to the buffer. /// + /// The writer to write to /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteValuePacked(ref FastBufferWriter writer, long value) => WriteUInt64Packed(ref writer, (ulong)Arithmetic.ZigZagEncode(value)); + public static void WriteValuePacked(ref FastBufferWriter writer, long value) => WriteUInt64Packed(ref writer, Arithmetic.ZigZagEncode(value)); /// - /// Convenience method that writes two packed Vector3 from the ray to the stream + /// Convenience method that writes two packed Vector3 from the ray to the buffer /// + /// The writer to write to /// Ray to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Ray ray) @@ -282,8 +302,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Ray ray) } /// - /// Convenience method that writes two packed Vector2 from the ray to the stream + /// Convenience method that writes two packed Vector2 from the ray to the buffer /// + /// The writer to write to /// Ray2D to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Ray2D ray2d) @@ -293,8 +314,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Ray2D ray2d) } /// - /// Convenience method that writes four varint floats from the color to the stream + /// Convenience method that writes four varint floats from the color to the buffer /// + /// The writer to write to /// Color to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Color color) @@ -306,8 +328,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Color color) } /// - /// Convenience method that writes four varint floats from the color to the stream + /// Convenience method that writes four varint floats from the color to the buffer /// + /// The writer to write to /// Color to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Color32 color) @@ -319,8 +342,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Color32 color) } /// - /// Convenience method that writes two varint floats from the vector to the stream + /// Convenience method that writes two varint floats from the vector to the buffer /// + /// The writer to write to /// Vector to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Vector2 vector2) @@ -330,8 +354,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Vector2 vector2 } /// - /// Convenience method that writes three varint floats from the vector to the stream + /// Convenience method that writes three varint floats from the vector to the buffer /// + /// The writer to write to /// Vector to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Vector3 vector3) @@ -342,8 +367,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Vector3 vector3 } /// - /// Convenience method that writes four varint floats from the vector to the stream + /// Convenience method that writes four varint floats from the vector to the buffer /// + /// The writer to write to /// Vector to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Vector4 vector4) @@ -355,8 +381,9 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Vector4 vector4 } /// - /// Writes the rotation to the stream. + /// Writes the rotation to the buffer. /// + /// The writer to write to /// Rotation to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, Quaternion rotation) @@ -370,7 +397,8 @@ public static void WriteValuePacked(ref FastBufferWriter writer, Quaternion rota /// /// Writes a string in a packed format /// - /// + /// The writer to write to + /// The value to pack [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, string s) { @@ -391,8 +419,29 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteValueBitPacked(ref FastBufferWriter writer, T value) where T: unmanaged => writer.WriteValueSafe(value); #else + /// + /// Writes a 14-bit signed short to the buffer in a bit-encoded packed format. + /// The first bit indicates whether the value is 1 byte or 2. + /// The sign bit takes up another bit. + /// That leaves 14 bits for the value. + /// A value greater than 2^14-1 or less than -2^14 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its two + /// most significant bits after zig-zag encoding. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort) Arithmetic.ZigZagEncode(value)); + /// + /// Writes a 15-bit unsigned short to the buffer in a bit-encoded packed format. + /// The first bit indicates whether the value is 1 byte or 2. + /// That leaves 15 bits for the value. + /// A value greater than 2^15-1 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its + /// most significant bit. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -419,8 +468,29 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value writer.WriteValue((ushort)((value << 1) | 0b1)); } + /// + /// Writes a 29-bit signed int to the buffer in a bit-encoded packed format. + /// The first two bits indicate whether the value is 1, 2, 3, or 4 bytes. + /// The sign bit takes up another bit. + /// That leaves 29 bits for the value. + /// A value greater than 2^29-1 or less than -2^29 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its three + /// most significant bits after zig-zag encoding. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, int value) => WriteValueBitPacked(ref writer, (uint) Arithmetic.ZigZagEncode(value)); + /// + /// Writes a 30-bit unsigned int to the buffer in a bit-encoded packed format. + /// The first two bits indicate whether the value is 1, 2, 3, or 4 bytes. + /// That leaves 30 bits for the value. + /// A value greater than 2^30-1 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its two + /// most significant bits. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -438,8 +508,29 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) writer.WritePartialValue(value | (uint)(numBytes - 1), numBytes); } - public static void WriteValueBitPacked(ref FastBufferWriter writer, long value) => WriteValueBitPacked(ref writer, (ulong) Arithmetic.ZigZagEncode(value)); + /// + /// Writes a 60-bit signed long to the buffer in a bit-encoded packed format. + /// The first three bits indicate whether the value is 1, 2, 3, 4, 5, 6, 7, or 8 bytes. + /// The sign bit takes up another bit. + /// That leaves 60 bits for the value. + /// A value greater than 2^60-1 or less than -2^60 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its four + /// most significant bits after zig-zag encoding. + /// + /// The writer to write to + /// The value to pack + public static void WriteValueBitPacked(ref FastBufferWriter writer, long value) => WriteValueBitPacked(ref writer, Arithmetic.ZigZagEncode(value)); + /// + /// Writes a 61-bit unsigned long to the buffer in a bit-encoded packed format. + /// The first three bits indicate whether the value is 1, 2, 3, 4, 5, 6, 7, or 8 bytes. + /// That leaves 31 bits for the value. + /// A value greater than 2^61-1 will throw an exception in editor and development builds. + /// In release builds builds the exception is not thrown and the value is truncated by losing its three + /// most significant bits. + /// + /// The writer to write to + /// The value to pack public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -522,20 +613,6 @@ private static unsafe ulong ToUlong(T value) where T : unmanaged ulong* asUlong = (ulong*) &value; return *asUlong; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe float ToSingle(T value) where T : unmanaged - { - float* asFloat = (float*) &value; - return *asFloat; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe double ToDouble(T value) where T : unmanaged - { - double* asDouble = (double*) &value; - return *asDouble; - } #endregion } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index 5ac013acec..dc827b40b6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -8,11 +8,19 @@ namespace Unity.Multiplayer.Netcode public static class ByteUnpacker { #region Managed TypePacking + /// - /// Writes a boxed object in a packed format + /// Reads a boxed object in a packed format /// Named differently from other ReadValuePacked methods to avoid accidental boxing + /// Don't use this method unless you have no other choice. /// - /// The object to write + /// The reader to read from + /// The object to read + /// The type of the object to read (i.e., typeof(int)) + /// + /// If true, reads a byte indicating whether or not the object is null. + /// Should match the way the object was written. + /// public static void ReadObjectPacked(ref FastBufferReader reader, out object value, Type type, bool isNullable = false) { #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -162,11 +170,12 @@ public static unsafe void ReadValuePacked(ref FastBufferReader reader, ou throw new InvalidOperationException("Enum is a size that cannot exist?!"); } } - + /// - /// Write single-precision floating point value to the stream as a varint + /// Read single-precision floating point value from the stream as a varint /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out float value) { @@ -175,9 +184,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out float value) } /// - /// Write double-precision floating point value to the stream as a varint + /// Read double-precision floating point value from the stream as a varint /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out double value) { @@ -186,22 +196,18 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value } /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read a byte from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out byte value) => reader.ReadByteSafe(out value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read a signed byte from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out sbyte value) { @@ -210,23 +216,19 @@ public static void ReadValuePacked(ref FastBufferReader reader, out sbyte value) } /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read a boolean from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out bool value) => reader.ReadValueSafe(out value); /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read an usigned short (Int16) as a varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out short value) { @@ -235,12 +237,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out short value) } /// - /// Write an unsigned short (UInt16) as a varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read an unsigned short (UInt16) as a varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out ushort value) { @@ -249,12 +249,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out ushort value } /// - /// Write a two-byte character as a varint to the stream. - /// WARNING: If the value you're writing is > 2287, this will use MORE space - /// (3 bytes instead of 2), and if your value is > 240 you'll get no savings at all. - /// Only use this if you're certain your value will be small. + /// Read a two-byte character as a varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out char c) { @@ -263,9 +261,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out char c) } /// - /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. + /// Read a signed int (Int32) as a ZigZag encoded varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out int value) { @@ -274,23 +273,26 @@ public static void ReadValuePacked(ref FastBufferReader reader, out int value) } /// - /// Write an unsigned int (UInt32) to the stream. + /// Read an unsigned int (UInt32) from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out uint value) => ReadUInt32Packed(ref reader, out value); /// - /// Write an unsigned long (UInt64) to the stream. + /// Read an unsigned long (UInt64) from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out ulong value) => ReadUInt64Packed(ref reader, out value); /// - /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. + /// Read a signed long (Int64) as a ZigZag encoded varint from the stream. /// - /// Value to write + /// The reader to read from + /// Value to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out long value) { @@ -299,9 +301,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out long value) } /// - /// Convenience method that writes two packed Vector3 from the ray to the stream + /// Convenience method that reads two packed Vector3 from the ray from the stream /// - /// Ray to write + /// The reader to read from + /// Ray to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Ray ray) { @@ -311,9 +314,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Ray ray) } /// - /// Convenience method that writes two packed Vector2 from the ray to the stream + /// Convenience method that reads two packed Vector2 from the ray from the stream /// - /// Ray2D to write + /// The reader to read from + /// Ray2D to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Ray2D ray2d) { @@ -323,9 +327,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Ray2D ray2d) } /// - /// Convenience method that writes four varint floats from the color to the stream + /// Convenience method that reads four varint floats from the color from the stream /// - /// Color to write + /// The reader to read from + /// Color to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Color color) { @@ -337,9 +342,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Color color) } /// - /// Convenience method that writes four varint floats from the color to the stream + /// Convenience method that reads four varint floats from the color from the stream /// - /// Color to write + /// The reader to read from + /// Color to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Color32 color) { @@ -351,9 +357,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Color32 colo } /// - /// Convenience method that writes two varint floats from the vector to the stream + /// Convenience method that reads two varint floats from the vector from the stream /// - /// Vector to write + /// The reader to read from + /// Vector to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Vector2 vector2) { @@ -363,9 +370,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Vector2 vect } /// - /// Convenience method that writes three varint floats from the vector to the stream + /// Convenience method that reads three varint floats from the vector from the stream /// - /// Vector to write + /// The reader to read from + /// Vector to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Vector3 vector3) { @@ -376,9 +384,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Vector3 vect } /// - /// Convenience method that writes four varint floats from the vector to the stream + /// Convenience method that reads four varint floats from the vector from the stream /// - /// Vector to write + /// The reader to read from + /// Vector to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Vector4 vector4) { @@ -390,9 +399,10 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Vector4 vect } /// - /// Writes the rotation to the stream. + /// Reads the rotation from the stream. /// - /// Rotation to write + /// The reader to read from + /// Rotation to read [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ReadValuePacked(ref FastBufferReader reader, out Quaternion rotation) { @@ -404,8 +414,9 @@ public static void ReadValuePacked(ref FastBufferReader reader, out Quaternion r } /// - /// Writes a string in a packed format + /// Reads a string in a packed format /// + /// The reader to read from /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void ReadValuePacked(ref FastBufferReader reader, out string s) @@ -431,12 +442,24 @@ public static unsafe void ReadValuePacked(ref FastBufferReader reader, out strin [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadValueBitPacked(ref FastBufferReader reader, T value) where T: unmanaged => reader.ReadValueSafe(out value); #else + /// + /// Read a bit-packed 14-bit signed short from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static void ReadValueBitPacked(ref FastBufferReader reader, out short value) { ReadValueBitPacked(ref reader, out ushort readValue); value = (short)Arithmetic.ZigZagDecode(readValue); } + /// + /// Read a bit-packed 15-bit unsigned short from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ushort value) { ushort returnValue = 0; @@ -463,11 +486,24 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out us value = (ushort)(returnValue >> 1); } + /// + /// Read a bit-packed 29-bit signed int from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static void ReadValueBitPacked(ref FastBufferReader reader, out int value) { ReadValueBitPacked(ref reader, out uint readValue); value = (int)Arithmetic.ZigZagDecode(readValue); } + + /// + /// Read a bit-packed 30-bit unsigned int from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out uint value) { uint returnValue = 0; @@ -499,11 +535,24 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ui value = returnValue >> 2; } + /// + /// Read a bit-packed 60-bit signed long from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static void ReadValueBitPacked(ref FastBufferReader reader, out long value) { ReadValueBitPacked(ref reader, out ulong readValue); value = Arithmetic.ZigZagDecode(readValue); } + + /// + /// Read a bit-packed 61-bit signed long from the stream. + /// See BytePacker.cs for a description of the format. + /// + /// The reader to read from + /// The value to read public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ulong value) { ulong returnValue = 0; @@ -602,20 +651,6 @@ private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value reader.ReadPartialValue(out value, numBytes); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe uint ToUint(T value) where T : unmanaged - { - uint* asUint = (uint*) &value; - return *asUint; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe ulong ToUlong(T value) where T : unmanaged - { - ulong* asUlong = (ulong*) &value; - return *asUlong; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe float ToSingle(T value) where T : unmanaged { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs new file mode 100644 index 0000000000..de1e617069 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -0,0 +1,57 @@ +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Multiplayer.Netcode +{ + public static class BytewiseUtility + { + /// + /// Helper function optimized for quickly copying small numbers of bytes. + /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 + /// Slower for amount > 8 + /// + /// Pointer to the source value + /// Pointer to the destination value + /// Number of bytes to copy + public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) + { + // Switch statement to write small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + switch (amount) + { + case 1: + dest[0] = *source; + break; + case 2: + *(ushort*) dest = *(ushort*)source; + break; + case 3: + *(ushort*) dest = *(ushort*)source; + *(dest+2) = *(source+2); + break; + case 4: + *(uint*) dest = *(uint*)source; + break; + case 5: + *(uint*) dest = *(uint*)source; + *(dest+4) = *(source+4); + break; + case 6: + *(uint*) dest = *(uint*)source; + *(ushort*) (dest+4) = *(ushort*)(source+4); + break; + case 7: + *(uint*) dest = *(uint*)source; + *(ushort*) (dest+4) = *(ushort*)(source+4); + *(dest+6) = *(source+6); + break; + case 8: + *(ulong*) dest = *(ulong*)source; + break; + default: + UnsafeUtility.MemCpy(dest, source, amount); + break; + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta similarity index 83% rename from com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta rename to com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta index 6dd5e01fcc..12bebf3d01 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs.meta +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e09b1183d63526341acdacfebdad750d +guid: 5df078ced492f8c45966997ceda09c8f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index ff9254f9a8..ac571ad9c3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -9,14 +9,32 @@ namespace Unity.Multiplayer.Netcode { public struct FastBufferReader : IDisposable { - internal unsafe byte* m_BufferPointer; + internal readonly unsafe byte* m_BufferPointer; internal int m_Position; - internal int m_Length; - internal Allocator m_Allocator; + internal readonly int m_Length; + private readonly Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR internal int m_AllowedReadMark; - internal bool m_InBitwiseContext; + private bool m_InBitwiseContext; #endif + + /// + /// Get the current read position + /// + public int Position + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position; + } + + /// + /// Get the total length of the buffer + /// + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Length; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CommitBitwiseReads(int amount) @@ -41,9 +59,18 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in #endif } + /// + /// Create a FastBufferReader from an ArraySegment. + /// A new buffer will be created using the given allocator and the value will be copied in. + /// FastBufferReader will then own the data. + /// + /// The buffer to copy from + /// The allocator to use + /// The number of bytes to copy (all if this is -1) + /// The offset of the buffer to start copying from public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? buffer.Count : length); + m_Length = Math.Max(1, length == -1 ? (buffer.Count - offset) : length); void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); fixed (byte* data = buffer.Array) { @@ -58,9 +85,18 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i #endif } + /// + /// Create a FastBufferReader from an existing byte array. + /// A new buffer will be created using the given allocator and the value will be copied in. + /// FastBufferReader will then own the data. + /// + /// The buffer to copy from + /// The allocator to use + /// The number of bytes to copy (all if this is -1) + /// The offset of the buffer to start copying from public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? buffer.Length : length); + m_Length = Math.Max(1, length == -1 ? (buffer.Length - offset) : length); void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); fixed (byte* data = buffer) { @@ -75,6 +111,15 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = #endif } + /// + /// Create a FastBufferReader from an existing byte buffer. + /// A new buffer will be created using the given allocator and the value will be copied in. + /// FastBufferReader will then own the data. + /// + /// The buffer to copy from + /// The allocator to use + /// The number of bytes to copy + /// The offset of the buffer to start copying from public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0) { m_Length = Math.Max(1, length); @@ -89,6 +134,15 @@ public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, in #endif } + /// + /// Create a FastBufferReader from a FastBufferWriter. + /// A new buffer will be created using the given allocator and the value will be copied in. + /// FastBufferReader will then own the data. + /// + /// The writer to copy from + /// The allocator to use + /// The number of bytes to copy (all if this is -1) + /// The offset of the buffer to start copying from public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, int length = -1, int offset = 0) { m_Length = Math.Max(1, length == -1 ? writer.Length : length); @@ -103,17 +157,30 @@ public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, #endif } + /// + /// Frees the allocated buffer + /// public unsafe void Dispose() { UnsafeUtility.Free(m_BufferPointer, m_Allocator); } + /// + /// Move the read position in the stream + /// + /// Absolute value to move the position to, truncated to Length [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Seek(int where) { m_Position = Math.Min(Length, where); } + /// + /// Mark that some bytes are going to be read via GetUnsafePtr(). + /// + /// Amount that will be read + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void MarkBytesRead(int amount) { @@ -131,6 +198,12 @@ internal void MarkBytesRead(int amount) m_Position += amount; } + /// + /// Retrieve a BitReader to be able to perform bitwise operations on the buffer. + /// No bytewise operations can be performed on the buffer until bitReader.Dispose() has been called. + /// At the end of the operation, FastBufferReader will remain byte-aligned. + /// + /// A BitReader public BitReader EnterBitwiseContext() { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -139,21 +212,22 @@ public BitReader EnterBitwiseContext() return new BitReader(ref this); } - public int Position - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position; - } - - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Length; - } - - + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be reading multiple fields back-to-back and you know the total size, + /// you can call VerifyCanRead() once on the total size, and then follow it with calls to + /// ReadValue() instead of ReadValueSafe() for faster serialization. + /// + /// Unsafe read operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using VerifyCanRead(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using VerifyCanRead is to avoid bounds checking in the following + /// operations in release builds. + /// + /// Amount of bytes to read + /// True if the read is allowed, false otherwise + /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool VerifyCanReadInternal(int bytes) + public bool VerifyCanRead(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -167,16 +241,27 @@ internal bool VerifyCanReadInternal(int bytes) return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_Position + bytes > m_AllowedReadMark) - { - m_AllowedReadMark = m_Position + bytes; - } + m_AllowedReadMark = m_Position + bytes; #endif return true; } + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be reading multiple fields back-to-back and you know the total size, + /// you can call VerifyCanRead() once on the total size, and then follow it with calls to + /// ReadValue() instead of ReadValueSafe() for faster serialization. + /// + /// Unsafe read operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using VerifyCanRead(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using VerifyCanRead is to avoid bounds checking in the following + /// operations in release builds. + /// + /// The value you want to read + /// True if the read is allowed, false otherwise + /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanRead(int bytes) + public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -185,18 +270,26 @@ public bool VerifyCanRead(int bytes) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Length) + int len = sizeof(T); + if (m_Position + len > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = m_Position + bytes; + m_AllowedReadMark = m_Position + len; #endif return true; } + /// + /// Internal version of VerifyCanRead. + /// Differs from VerifyCanRead only in that it won't ever move the AllowedReadMark backward. + /// + /// + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged + internal bool VerifyCanReadInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -205,17 +298,24 @@ public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - int len = sizeof(T); - if (m_Position + len > m_Length) + if (m_Position + bytes > m_Length) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = m_Position + len; + if (m_Position + bytes > m_AllowedReadMark) + { + m_AllowedReadMark = m_Position + bytes; + } #endif return true; } + /// + /// Returns an array representation of the underlying byte buffer. + /// !!Allocates a new array!! + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte[] ToArray() { @@ -227,12 +327,20 @@ public unsafe byte[] ToArray() return ret; } + /// + /// Gets a direct pointer to the underlying buffer + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { return m_BufferPointer; } + /// + /// Gets a direct pointer to the underlying buffer at the current read position + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { @@ -245,7 +353,10 @@ public unsafe byte[] ToArray() /// /// The object to read /// The type to be read - /// Should a null value be encoded? Any type for which type.IsNullable() returns true will always encode it. + /// + /// If true, reads a byte indicating whether or not the object is null. + /// Should match the way the object was written. + /// public void ReadObject(out object value, Type type, bool isNullable = false) { if (isNullable || type.IsNullable()) @@ -358,11 +469,21 @@ public void ReadObject(out object value, Type type, bool isNullable = false) throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); } + /// + /// Read an INetworkSerializable + /// + /// INetworkSerializable instance + /// + /// public void ReadNetworkSerializable(out T value) where T : INetworkSerializable { throw new NotImplementedException(); } + /// + /// Read a GameObject + /// + /// value to read public void ReadValue(out GameObject value) { ReadValue(out ulong networkObjectId); @@ -381,6 +502,13 @@ public void ReadValue(out GameObject value) value = null; } + /// + /// Read a GameObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// value to read public void ReadValueSafe(out GameObject value) { ReadValueSafe(out ulong networkObjectId); @@ -399,6 +527,10 @@ public void ReadValueSafe(out GameObject value) value = null; } + /// + /// Read a NetworkObject + /// + /// value to read public void ReadValue(out NetworkObject value) { ReadValue(out ulong networkObjectId); @@ -417,6 +549,13 @@ public void ReadValue(out NetworkObject value) value = null; } + /// + /// Read a NetworkObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// value to read public void ReadValueSafe(out NetworkObject value) { ReadValueSafe(out ulong networkObjectId); @@ -435,6 +574,10 @@ public void ReadValueSafe(out NetworkObject value) value = null; } + /// + /// Read a NetworkBehaviour + /// + /// value to read public void ReadValue(out NetworkBehaviour value) { ReadValue(out ulong networkObjectId); @@ -454,6 +597,13 @@ public void ReadValue(out NetworkBehaviour value) value = null; } + /// + /// Read a NetworkBehaviour + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling VerifyCanRead. + /// + /// value to read public void ReadValueSafe(out NetworkBehaviour value) { ReadValueSafe(out ulong networkObjectId); @@ -605,13 +755,18 @@ public unsafe void ReadValueSafe(out T[] array) where T: unmanaged } } + /// + /// Read a partial value. The value is zero-initialized and then the specified number of bytes is read into it. + /// + /// Value to read + /// Number of bytes + /// Offset into the value to write the bytes + /// + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged { - // Switch statement to read small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -627,41 +782,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - switch (bytesToRead) - { - case 1: - ptr[0] = *bufferPointer; - break; - case 2: - *(ushort*) ptr = *(ushort*)bufferPointer; - break; - case 3: - *(ushort*) ptr = *(ushort*)bufferPointer; - *(ptr+2) = *(bufferPointer+2); - break; - case 4: - *(uint*) ptr = *(uint*)bufferPointer; - break; - case 5: - *(uint*) ptr = *(uint*)bufferPointer; - *(ptr+4) = *(bufferPointer+4); - break; - case 6: - *(uint*) ptr = *(uint*)bufferPointer; - *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); - break; - case 7: - *(uint*) ptr = *(uint*)bufferPointer; - *(ushort*) (ptr+4) = *(ushort*)(bufferPointer+4); - *(ptr+6) = *(bufferPointer+6); - break; - case 8: - *(ulong*) ptr = *(ulong*)bufferPointer; - break; - default: - UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); - break; - } + BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); m_Position += bytesToRead; value = val; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 508dc1d95e..d08dbdad17 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -11,14 +11,50 @@ public struct FastBufferWriter : IDisposable { internal unsafe byte* m_BufferPointer; internal int m_Position; - internal int m_Length; + private int m_Length; internal int m_Capacity; - internal int m_MaxCapacity; - internal Allocator m_Allocator; + internal readonly int m_MaxCapacity; + private readonly Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR internal int m_AllowedWriteMark; - internal bool m_InBitwiseContext; + private bool m_InBitwiseContext; #endif + + /// + /// The current write position + /// + public int Position + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position; + } + + /// + /// The current total buffer size + /// + public int Capacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Capacity; + } + + /// + /// The maximum possible total buffer size + /// + public int MaxCapacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_MaxCapacity; + } + + /// + /// The total amount of bytes that have been written to the stream + /// + public int Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => m_Position > m_Length ? m_Position : m_Length; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -30,6 +66,12 @@ internal void CommitBitwiseWrites(int amount) #endif } + /// + /// Create a FastBufferWriter. + /// + /// Size of the buffer to create + /// Allocator to use in creating it + /// Maximum size the buffer can grow to. If less than size, buffer cannot grow. public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) { void* buffer = UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), allocator); @@ -41,18 +83,26 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) m_Length = 0; m_Capacity = size; m_Allocator = allocator; - m_MaxCapacity = maxSize == -1 ? size : maxSize; + m_MaxCapacity = maxSize < size ? size : maxSize; #if DEVELOPMENT_BUILD || UNITY_EDITOR m_AllowedWriteMark = 0; m_InBitwiseContext = false; #endif } + /// + /// Frees the allocated buffer + /// public unsafe void Dispose() { UnsafeUtility.Free(m_BufferPointer, m_Allocator); } + /// + /// Move the write position in the stream. + /// Note that moving forward past the current length will extend the buffer's Length value even if you don't write. + /// + /// Absolute value to move the position to, truncated to Capacity [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Seek(int where) { @@ -74,6 +124,11 @@ public void Seek(int where) m_Position = where; } + /// + /// Truncate the stream by setting Length to the specified value. + /// If Position is greater than the specified value, it will be moved as well. + /// + /// The value to truncate to. If -1, the current position will be used. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Truncate(int where = -1) { @@ -92,6 +147,12 @@ public void Truncate(int where = -1) } } + /// + /// Retrieve a BitWriter to be able to perform bitwise operations on the buffer. + /// No bytewise operations can be performed on the buffer until bitWriter.Dispose() has been called. + /// At the end of the operation, FastBufferWriter will remain byte-aligned. + /// + /// A BitWriter public BitWriter EnterBitwiseContext() { #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -99,25 +160,6 @@ public BitWriter EnterBitwiseContext() #endif return new BitWriter(ref this); } - - - public int Position - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position; - } - - public int Capacity - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Capacity; - } - - public int Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position > m_Length ? m_Position : m_Length; - } internal unsafe void Grow() { @@ -132,6 +174,20 @@ internal unsafe void Grow() m_Capacity = newSize; } + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be writing multiple fields back-to-back and you know the total size, + /// you can call VerifyCanWrite() once on the total size, and then follow it with calls to + /// WriteValue() instead of WriteValueSafe() for faster serialization. + /// + /// Unsafe write operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using VerifyCanWrite(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using VerifyCanWrite is to avoid bounds checking in the following + /// operations in release builds. + /// + /// Amount of bytes to write + /// True if the write is allowed, false otherwise + /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool VerifyCanWrite(int bytes) { @@ -158,9 +214,23 @@ public bool VerifyCanWrite(int bytes) #endif return true; } - + + /// + /// Allows faster serialization by batching bounds checking. + /// When you know you will be writing multiple fields back-to-back and you know the total size, + /// you can call VerifyCanWrite() once on the total size, and then follow it with calls to + /// WriteValue() instead of WriteValueSafe() for faster serialization. + /// + /// Unsafe write operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using VerifyCanWrite(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using VerifyCanWrite is to avoid bounds checking in the following + /// operations in release builds. + /// + /// The value you want to write + /// True if the write is allowed, false otherwise + /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanWriteInternal(int bytes) + public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -169,7 +239,8 @@ public bool VerifyCanWriteInternal(int bytes) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Capacity) + int len = sizeof(T); + if (m_Position + len > m_Capacity) { if (m_Capacity < m_MaxCapacity) { @@ -181,16 +252,20 @@ public bool VerifyCanWriteInternal(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_Position + bytes > m_AllowedWriteMark) - { - m_AllowedWriteMark = m_Position + bytes; - } + m_AllowedWriteMark = m_Position + len; #endif return true; } - + + /// + /// Internal version of VerifyCanWrite. + /// Differs from VerifyCanWrite only in that it won't ever move the AllowedWriteMark backward. + /// + /// + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged + public bool VerifyCanWriteInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -199,8 +274,7 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - int len = sizeof(T); - if (m_Position + len > m_Capacity) + if (m_Position + bytes > m_Capacity) { if (m_Capacity < m_MaxCapacity) { @@ -212,11 +286,19 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedWriteMark = m_Position + len; + if (m_Position + bytes > m_AllowedWriteMark) + { + m_AllowedWriteMark = m_Position + bytes; + } #endif return true; } + /// + /// Returns an array representation of the underlying byte buffer. + /// !!Allocates a new array!! + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte[] ToArray() { @@ -228,12 +310,20 @@ public unsafe byte[] ToArray() return ret; } + /// + /// Gets a direct pointer to the underlying buffer + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { return m_BufferPointer; } + /// + /// Gets a direct pointer to the underlying buffer at the current read position + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { @@ -245,11 +335,15 @@ public unsafe byte[] ToArray() /// Named differently from other WriteValue methods to avoid accidental boxing /// /// The object to write + /// + /// If true, an extra byte will be written to indicate whether or not the value is null. + /// Some types will always write this. + /// public void WriteObject(object value, bool isNullable = false) { if (isNullable || value.GetType().IsNullable()) { - bool isNull = value == null || (value is UnityEngine.Object && ((UnityEngine.Object)value) == null); + bool isNull = value == null || (value is UnityEngine.Object o && o == null); WriteValueSafe(isNull); @@ -340,48 +434,102 @@ public void WriteObject(object value, bool isNullable = false) throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } + /// + /// Write an INetworkSerializable + /// + /// The value to write + /// public void WriteNetworkSerializable(in T value) where T : INetworkSerializable { // TODO } - + + /// + /// Get the required amount of space to write a GameObject + /// + /// + /// + public static int GetWriteSize(GameObject value) + { + return sizeof(ulong); + } + + /// + /// Get the required amount of space to write a GameObject + /// + /// + public static int GetGameObjectWriteSize() + { + return sizeof(ulong); + } + + /// + /// Write a GameObject + /// + /// The value to write public void WriteValue(GameObject value) { - var networkObject = ((GameObject)value).GetComponent(); + var networkObject = (value).GetComponent(); if (networkObject == null) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); } WriteValue(networkObject.NetworkObjectId); } + /// + /// Write a GameObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. + /// + /// The value to write public void WriteValueSafe(GameObject value) { - var networkObject = ((GameObject)value).GetComponent(); + var networkObject = (value).GetComponent(); if (networkObject == null) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); } if (!networkObject.IsSpawned) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {((GameObject)value).name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); } WriteValueSafe(networkObject.NetworkObjectId); } - public static int GetWriteSize(GameObject value) + /// + /// Get the required size to write a NetworkObject + /// + /// + /// + public static int GetWriteSize(NetworkObject value) + { + return sizeof(ulong); + } + + /// + /// Get the required size to write a NetworkObject + /// + /// + public static int GetNetworkObjectWriteSize() { return sizeof(ulong); } + + /// + /// Write a NetworkObject + /// + /// The value to write public void WriteValue(in NetworkObject value) { if (!value.IsSpawned) @@ -392,6 +540,13 @@ public void WriteValue(in NetworkObject value) WriteValue(value.NetworkObjectId); } + /// + /// Write a NetworkObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. + /// + /// The value to write public void WriteValueSafe(NetworkObject value) { if (!value.IsSpawned) @@ -401,27 +556,56 @@ public void WriteValueSafe(NetworkObject value) WriteValueSafe(value.NetworkObjectId); } - public static int GetWriteSize(NetworkObject value) + /// + /// Get the required size to write a NetworkBehaviour + /// + /// + /// + public static int GetWriteSize(NetworkBehaviour value) { - return sizeof(ulong); + return sizeof(ulong) + sizeof(ushort); + } + + + /// + /// Get the required size to write a NetworkBehaviour + /// + /// + public static int GetNetworkBehaviourWriteSize() + { + return sizeof(ulong) + sizeof(ushort); } + + /// + /// Write a NetworkBehaviour + /// + /// The value to write public void WriteValue(NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); } WriteValue(value.NetworkObjectId); WriteValue(value.NetworkBehaviourId); } + /// + /// Write a NetworkBehaviour + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. + /// + /// The value to write + /// + /// public void WriteValueSafe(NetworkBehaviour value) { if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {((NetworkBehaviour)value).gameObject.name}"); + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); } if (!VerifyCanWriteInternal(sizeof(ulong) + sizeof(ushort))) @@ -431,10 +615,17 @@ public void WriteValueSafe(NetworkBehaviour value) WriteValue(value.NetworkObjectId); WriteValue(value.NetworkBehaviourId); } - - public static int GetWriteSize(NetworkBehaviour value) + + /// + /// Get the required size to write a string + /// + /// The string to write + /// Whether or not to use one byte per character. This will only allow ASCII + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetWriteSize(string s, bool oneByteChars = false) { - return sizeof(ulong) + sizeof(ushort); + return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); } /// @@ -464,6 +655,9 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) /// /// Writes a string + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// The string to write /// Whether or not to use one byte per character. This will only allow ASCII @@ -502,10 +696,20 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) } } + /// + /// Get the required size to write an unmanaged array + /// + /// The array to write + /// The amount of elements to write + /// Where in the array to start + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetWriteSize(string s, bool oneByteChars = false) + public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T: unmanaged { - return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); + int sizeInTs = count != -1 ? count : array.Length - offset; + int sizeInBytes = sizeInTs * sizeof(T); + return sizeof(int) + sizeInBytes; } /// @@ -529,6 +733,9 @@ public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) wher /// /// Writes an unmanaged array + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// The array to write /// The amount of elements to write @@ -558,22 +765,19 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) WriteBytes(bytes, sizeInBytes); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T: unmanaged - { - int sizeInTs = count != -1 ? count : array.Length - offset; - int sizeInBytes = sizeInTs * sizeof(T); - return sizeof(int) + sizeInBytes; - } - + + /// + /// Write a partial value. The specified number of bytes is written from the value and the rest is ignored. + /// + /// Value to write + /// Number of bytes + /// Offset into the value to begin reading the bytes + /// + /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged { - // Switch statement to write small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -588,41 +792,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - switch (bytesToWrite) - { - case 1: - bufferPointer[0] = *ptr; - break; - case 2: - *(ushort*) bufferPointer = *(ushort*)ptr; - break; - case 3: - *(ushort*) bufferPointer = *(ushort*)ptr; - *(bufferPointer+2) = *(ptr+2); - break; - case 4: - *(uint*) bufferPointer = *(uint*)ptr; - break; - case 5: - *(uint*) bufferPointer = *(uint*)ptr; - *(bufferPointer+4) = *(ptr+4); - break; - case 6: - *(uint*) bufferPointer = *(uint*)ptr; - *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); - break; - case 7: - *(uint*) bufferPointer = *(uint*)ptr; - *(ushort*) (bufferPointer+4) = *(ushort*)(ptr+4); - *(bufferPointer+6) = *(ptr+6); - break; - case 8: - *(ulong*) bufferPointer = *(ulong*)ptr; - break; - default: - UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); - break; - } + BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); m_Position += bytesToWrite; } @@ -650,6 +820,9 @@ public unsafe void WriteByte(byte value) /// /// Write a byte to the stream. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -669,12 +842,13 @@ public unsafe void WriteByteSafe(byte value) } m_BufferPointer[m_Position++] = value; } - + /// /// Write multiple bytes to the stream /// /// Value to write /// Number of bytes to write + /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytes(byte* value, int size, int offset = 0) { @@ -695,9 +869,13 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) /// /// Write multiple bytes to the stream + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// Value to write /// Number of bytes to write + /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) { @@ -722,6 +900,7 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) /// /// Value to write /// Number of bytes to write + /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytes(byte[] value, int size, int offset = 0) { @@ -733,9 +912,13 @@ public unsafe void WriteBytes(byte[] value, int size, int offset = 0) /// /// Write multiple bytes to the stream + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// Value to write /// Number of bytes to write + /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) { @@ -768,9 +951,32 @@ public unsafe void CopyFrom(FastBufferWriter other) { WriteBytes(other.m_BufferPointer, other.m_Position); } + + /// + /// Get the size required to write an unmanaged value + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe int GetWriteSize(in T value) where T : unmanaged + { + return sizeof(T); + } + + /// + /// Get the size required to write an unmanaged value of type T + /// + /// + /// + /// + public static unsafe int GetWriteSize() where T : unmanaged + { + return sizeof(T); + } /// - /// Write a value of any unmanaged type to the buffer. + /// Write a value of any unmanaged type (including unmanaged structs) to the buffer. /// It will be copied into the buffer exactly as it exists in memory. /// /// The value to copy @@ -798,8 +1004,11 @@ public unsafe void WriteValue(in T value) where T : unmanaged } /// - /// Write a value of any unmanaged type to the buffer. + /// Write a value of any unmanaged type (including unmanaged structs) to the buffer. /// It will be copied into the buffer exactly as it exists in memory. + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling VerifyCanWrite. /// /// The value to copy /// Any unmanaged type @@ -825,31 +1034,5 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged *pointer = value; m_Position += len; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetWriteSize(in T value) where T : unmanaged - { - return sizeof(T); - } - - public static unsafe int GetWriteSize() where T : unmanaged - { - return sizeof(T); - } - - public static int GetNetworkObjectWriteSize() - { - return sizeof(ulong); - } - - public static int GetGameObjectWriteSize() - { - return sizeof(ulong); - } - - public static int GetNetworkBehaviourWriteSize() - { - return sizeof(ulong) + sizeof(ushort); - } } } \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs similarity index 100% rename from com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializer.cs rename to com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta new file mode 100644 index 0000000000..f5c741d324 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9220f80eab4051e4d9b9504ef840eba4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs index 2d714c2642..28cc2ad1bb 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -4,6 +4,17 @@ namespace Unity.Multiplayer.Netcode { + /// + /// Registry for telling FastBufferWriter and FastBufferReader how to read types when passed to + /// WriteObject and ReadObject, as well as telling BytePacker and ByteUnpacker how to do it when passed to + /// WriteObjectPacked and ReadObjectPacked. + /// + /// These object-based serialization functions shouldn't be used if at all possible, but if they're required, + /// and you need to serialize a type that's not natively supported, you can register it with the dictionaries here: + /// + /// Serializers and Deserializers for FastBufferWriter and FasteBufferReader + /// SerializersPacked and DeserializersPacked for BytePacker and ByteUnpacker + /// public static class SerializationTypeTable { public delegate void Serialize(ref FastBufferWriter writer, object value); From 63309e2c1b6a483c3740ac078cc1b8835a575d3d Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 13:42:39 -0500 Subject: [PATCH 10/29] Replaced possibly unaligned memory access with UnsafeUtility.MemCpy... it's a little slower, but apparently some platforms won't support unaligned memory access (e.g., WEBGL, ARM processors) and there's no compile time way to detect ARM processors since the bytecode is not processor-dependent... the cost of the runtime detection would be more expensive than the cost of just doing the memcpy. --- .../Runtime/Serialization/BitReader.cs | 3 +- .../Runtime/Serialization/BitWriter.cs | 3 +- .../Runtime/Serialization/BytewiseUtility.cs | 57 ------------------- .../Serialization/BytewiseUtility.cs.meta | 11 ---- .../Runtime/Serialization/FastBufferReader.cs | 15 +++-- .../Runtime/Serialization/FastBufferWriter.cs | 10 ++-- 6 files changed, 20 insertions(+), 79 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 7c564c6d0a..c784922a34 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; namespace Unity.Multiplayer.Netcode @@ -175,7 +176,7 @@ private unsafe void ReadPartialValue(out T value, int bytesToRead, int offset T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); m_BitPosition += bytesToRead * BITS_PER_BYTE; value = val; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 5bbf095641..e103dce473 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { @@ -186,7 +187,7 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy { byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); m_BitPosition += bytesToWrite * BITS_PER_BYTE; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs deleted file mode 100644 index de1e617069..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Unity.Collections.LowLevel.Unsafe; - -namespace Unity.Multiplayer.Netcode -{ - public static class BytewiseUtility - { - /// - /// Helper function optimized for quickly copying small numbers of bytes. - /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 - /// Slower for amount > 8 - /// - /// Pointer to the source value - /// Pointer to the destination value - /// Number of bytes to copy - public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) - { - // Switch statement to write small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - switch (amount) - { - case 1: - dest[0] = *source; - break; - case 2: - *(ushort*) dest = *(ushort*)source; - break; - case 3: - *(ushort*) dest = *(ushort*)source; - *(dest+2) = *(source+2); - break; - case 4: - *(uint*) dest = *(uint*)source; - break; - case 5: - *(uint*) dest = *(uint*)source; - *(dest+4) = *(source+4); - break; - case 6: - *(uint*) dest = *(uint*)source; - *(ushort*) (dest+4) = *(ushort*)(source+4); - break; - case 7: - *(uint*) dest = *(uint*)source; - *(ushort*) (dest+4) = *(ushort*)(source+4); - *(dest+6) = *(source+6); - break; - case 8: - *(ulong*) dest = *(ulong*)source; - break; - default: - UnsafeUtility.MemCpy(dest, source, amount); - break; - } - } - } -} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta deleted file mode 100644 index 12bebf3d01..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5df078ced492f8c45966997ceda09c8f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index ac571ad9c3..4a25be7d29 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -782,7 +782,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); m_Position += bytesToRead; value = val; @@ -942,8 +942,10 @@ public unsafe void ReadValue(out T value) where T : unmanaged } #endif - T* pointer = (T*)(m_BufferPointer+m_Position); - value = *pointer; + fixed (T* ptr = &value) + { + UnsafeUtility.MemCpy(ptr, m_BufferPointer+m_Position, len); + } m_Position += len; } @@ -974,8 +976,11 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged throw new OverflowException("Writing past the end of the buffer"); } - T* pointer = (T*)(m_BufferPointer+m_Position); - value = *pointer; + + fixed (T* ptr = &value) + { + UnsafeUtility.MemCpy(ptr, m_BufferPointer+m_Position, len); + } m_Position += len; } } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index d08dbdad17..84e2abfc6b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -792,7 +792,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); m_Position += bytesToWrite; } @@ -1029,9 +1029,11 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged { throw new OverflowException("Writing past the end of the buffer"); } - - T* pointer = (T*)(m_BufferPointer+m_Position); - *pointer = value; + + fixed (T* ptr = &value) + { + UnsafeUtility.MemCpy(m_BufferPointer+m_Position, ptr, len); + } m_Position += len; } } From e9b93056aab3b57c18e694d263a45976b5b26b4c Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:00:18 -0500 Subject: [PATCH 11/29] Resurrected BytewiseUtil.FastCopyBytes as a faster alternative to UnsafeUtility.MemCpy for small values, while still supporting unaligned access. --- .../Runtime/Serialization/BitReader.cs | 2 +- .../Runtime/Serialization/BitWriter.cs | 2 +- .../Runtime/Serialization/ByteUnpacker.cs | 55 ++++++++++--- .../Runtime/Serialization/BytewiseUtility.cs | 80 +++++++++++++++++++ .../Serialization/BytewiseUtility.cs.meta | 11 +++ .../Runtime/Serialization/FastBufferReader.cs | 4 +- .../Runtime/Serialization/FastBufferWriter.cs | 8 +- .../Editor/Serialization/BitCounterTests.cs | 5 +- 8 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index c784922a34..60f84c8b74 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -176,7 +176,7 @@ private unsafe void ReadPartialValue(out T value, int bytesToRead, int offset T val = new T(); byte* ptr = ((byte*) &val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); + BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); m_BitPosition += bytesToRead * BITS_PER_BYTE; value = val; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index e103dce473..db70356262 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -187,7 +187,7 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy { byte* ptr = ((byte*) &value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); + BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); m_BitPosition += bytesToWrite * BITS_PER_BYTE; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index dc827b40b6..a5601dfd60 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -477,7 +477,8 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out us *ptr = *data; break; case 2: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); break; default: throw new InvalidOperationException("Could not read bit-packed value: impossible byte count"); @@ -521,14 +522,19 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ui *ptr = *data; break; case 2: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); break; case 3: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); *(ptr+2) = *(data+2); break; case 4: - *(uint*) ptr = *(uint*)data; + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); break; } @@ -570,30 +576,53 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ul *ptr = *data; break; case 2: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); break; case 3: - *(ushort*) ptr = *(ushort*)data; + *ptr = *data; + *(ptr+1) = *(data+1); *(ptr+2) = *(data+2); break; case 4: - *(uint*) ptr = *(uint*)data; + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); break; case 5: - *(uint*) ptr = *(uint*)data; + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); *(ptr+4) = *(data+4); break; case 6: - *(uint*) ptr = *(uint*)data; - *(ushort*) (ptr+4) = *(ushort*)(data+4); + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); + *(ptr+4) = *(data+4); + *(ptr+5) = *(data+5); break; case 7: - *(uint*) ptr = *(uint*)data; - *(ushort*) (ptr+4) = *(ushort*)(data+4); + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); + *(ptr+4) = *(data+4); + *(ptr+5) = *(data+5); *(ptr+6) = *(data+6); break; case 8: - *(ulong*) ptr = *(ulong*)data; + *ptr = *data; + *(ptr+1) = *(data+1); + *(ptr+2) = *(data+2); + *(ptr+3) = *(data+3); + *(ptr+4) = *(data+4); + *(ptr+5) = *(data+5); + *(ptr+6) = *(data+6); + *(ptr+7) = *(data+7); break; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs new file mode 100644 index 0000000000..1cfda38e3c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -0,0 +1,80 @@ +using Unity.Collections.LowLevel.Unsafe; + +namespace Unity.Multiplayer.Netcode +{ + public static class BytewiseUtility + { + /// + /// Helper function optimized for quickly copying small numbers of bytes. + /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 + /// Slower for amount > 8 + /// + /// Pointer to the source value + /// Pointer to the destination value + /// Number of bytes to copy + public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) + { + // Switch statement to write small values with assignments + // is considerably faster than calling UnsafeUtility.MemCpy + // in all builds - editor, mono, and ILCPP + switch (amount) + { + case 1: + *dest = *source; + break; + case 2: + *dest = *source; + *(dest + 1) = *(source + 1); + break; + case 3: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + break; + case 4: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + break; + case 5: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + *(dest + 4) = *(source + 4); + break; + case 6: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + *(dest + 4) = *(source + 4); + *(dest + 5) = *(source + 5); + break; + case 7: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + *(dest + 4) = *(source + 4); + *(dest + 5) = *(source + 5); + *(dest + 6) = *(source + 6); + break; + case 8: + *dest = *source; + *(dest + 1) = *(source + 1); + *(dest + 2) = *(source + 2); + *(dest + 3) = *(source + 3); + *(dest + 4) = *(source + 4); + *(dest + 5) = *(source + 5); + *(dest + 6) = *(source + 6); + *(dest + 7) = *(source + 7); + break; + default: + UnsafeUtility.MemCpy(dest, source, amount); + break; + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta new file mode 100644 index 0000000000..12bebf3d01 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5df078ced492f8c45966997ceda09c8f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 4a25be7d29..6501441f71 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -944,7 +944,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged fixed (T* ptr = &value) { - UnsafeUtility.MemCpy(ptr, m_BufferPointer+m_Position, len); + BytewiseUtility.FastCopyBytes((byte*)ptr, m_BufferPointer+m_Position, len); } m_Position += len; } @@ -979,7 +979,7 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged fixed (T* ptr = &value) { - UnsafeUtility.MemCpy(ptr, m_BufferPointer+m_Position, len); + BytewiseUtility.FastCopyBytes((byte*)ptr, m_BufferPointer+m_Position, len); } m_Position += len; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 84e2abfc6b..55e1ba0b6b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -998,8 +998,10 @@ public unsafe void WriteValue(in T value) where T : unmanaged } #endif - T* pointer = (T*)(m_BufferPointer+m_Position); - *pointer = value; + fixed (T* ptr = &value) + { + BytewiseUtility.FastCopyBytes(m_BufferPointer+m_Position, (byte*)ptr, len); + } m_Position += len; } @@ -1032,7 +1034,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged fixed (T* ptr = &value) { - UnsafeUtility.MemCpy(m_BufferPointer+m_Position, ptr, len); + BytewiseUtility.FastCopyBytes(m_BufferPointer+m_Position, (byte*)ptr, len); } m_Position += len; } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 12331d6ff5..b3d216b7ca 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,5 +1,8 @@ -using NUnit.Framework; +using System.Diagnostics; +using NUnit.Framework; +using Unity.Collections.LowLevel.Unsafe; using Unity.Multiplayer.Netcode; +using Debug = UnityEngine.Debug; namespace Unity.Netcode.EditorTests { From aa56440dfc19059c4bc55bdc65f452ecd283aa0f Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:19:44 -0500 Subject: [PATCH 12/29] Reverting an accidental change. --- .../Tests/Editor/Serialization/BitCounterTests.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index b3d216b7ca..12331d6ff5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,8 +1,5 @@ -using System.Diagnostics; -using NUnit.Framework; -using Unity.Collections.LowLevel.Unsafe; +using NUnit.Framework; using Unity.Multiplayer.Netcode; -using Debug = UnityEngine.Debug; namespace Unity.Netcode.EditorTests { From fabb3b8b65734dc4dc83fbaed92abd5be21082ff Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:35:19 -0500 Subject: [PATCH 13/29] Removed files that got accidentally duplicated from before the rename. --- .../Runtime/Serialization.meta | 8 - .../Runtime/Serialization/FastBufferWriter.cs | 254 ------------------ .../Serialization/FastBufferWriter.cs.meta | 11 - 3 files changed, 273 deletions(-) delete mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization.meta delete mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs delete mode 100644 com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization.meta b/com.unity.multiplayer.mlapi/Runtime/Serialization.meta deleted file mode 100644 index 7ec99bd274..0000000000 --- a/com.unity.multiplayer.mlapi/Runtime/Serialization.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 4fd73dafe8658fb4cb3c87f77e783073 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs deleted file mode 100644 index ddf56fcd51..0000000000 --- a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System.Runtime.CompilerServices; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Netcode; - -namespace Unity.Multiplayer.Netcode -{ - public struct FastBufferWriter - { - private NativeArray m_buffer; - private readonly unsafe byte* m_bufferPointer; - private int m_position; - - public unsafe FastBufferWriter(NativeArray buffer, int position = 0) - { - m_buffer = buffer; - m_bufferPointer = (byte*) m_buffer.GetUnsafePtr(); - m_position = position; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Seek(int where) - { - m_position = where; - } - - public int Position => m_position; - - public NativeArray GetNativeArray() - { - return m_buffer; - } - - public byte[] ToArray() - { - return m_buffer.ToArray(); - } - - public unsafe byte* GetUnsafePtr() - { - return m_bufferPointer; - } - - public unsafe byte* GetUnsafePtrAtCurrentPosition() - { - return m_bufferPointer + m_position; - } - - /// - /// Write single-precision floating point value to the stream as a varint - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteSinglePacked(float value) - { - WriteUInt32Packed(ToUint(value)); - } - - /// - /// Write double-precision floating point value to the stream as a varint - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteDoublePacked(double value) - { - WriteUInt64Packed(ToUlong(value)); - } - - /// - /// Write a signed short (Int16) as a ZigZag encoded varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt16Packed(short value) => WriteInt64Packed(value); - - /// - /// Write an unsigned short (UInt16) as a varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt16Packed(ushort value) => WriteUInt64Packed(value); - - /// - /// Write a two-byte character as a varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteCharPacked(char c) => WriteUInt16Packed(c); - - /// - /// Write a signed int (Int32) as a ZigZag encoded varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt32Packed(int value) => WriteInt64Packed(value); - - /// - /// Write an unsigned int (UInt32) as a varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUInt32Packed(uint value) => WriteUInt64Packed(value); - - /// - /// Write a signed long (Int64) as a ZigZag encoded varint to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt64Packed(long value) => WriteUInt64Packed(Arithmetic.ZigZagEncode(value)); - - /// - /// Write an unsigned long (UInt64) as a varint to the stream. - /// - /// Value to write - public void WriteUInt64Packed(ulong value) - { - if (value <= 240) - { - WriteULongByte(value); - } - else if (value <= 2287) - { - WriteULongByte(((value - 240) >> 8) + 241); - WriteULongByte(value - 240); - } - else if (value <= 67823) - { - WriteByte(249); - WriteULongByte((value - 2288) >> 8); - WriteULongByte(value - 2288); - } - else - { - ulong header = 255; - ulong match = 0x00FF_FFFF_FFFF_FFFFUL; - while (value <= match) - { - --header; - match >>= 8; - } - - WriteULongByte(header); - int max = (int)(header - 247); - for (int i = 0; i < max; ++i) - { - WriteULongByte(value >> (i << 3)); - } - } - } - - /// - /// Write a byte (in an int format) to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteIntByte(int value) => WriteByte((byte)value); - - /// - /// Write a byte (in a ulong format) to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteULongByte(ulong byteValue) => WriteByte((byte)byteValue); - - /// - /// Write a byte to the stream. - /// - /// Value to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteByte(byte value) - { - m_bufferPointer[m_position++] = value; - } - - /// - /// Write multiple bytes to the stream - /// - /// Value to write - /// Number of bytes to write - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteBytes(byte* value, int size) - { - UnsafeUtility.MemCpy((m_bufferPointer + m_position), value, size); - m_position += size; - } - - /// - /// Copy the contents of this writer into another writer. - /// The contents will be copied from the beginning of this writer to its current position. - /// They will be copied to the other writer starting at the other writer's current position. - /// - /// Writer to copy to - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyTo(FastBufferWriter other) - { - other.WriteBytes(m_bufferPointer, m_position); - } - - /// - /// Copy the contents of another writer into this writer. - /// The contents will be copied from the beginning of the other writer to its current position. - /// They will be copied to this writer starting at this writer's current position. - /// - /// Writer to copy to - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void CopyFrom(FastBufferWriter other) - { - WriteBytes(other.m_bufferPointer, other.m_position); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe uint ToUint(T value) where T : unmanaged - { - uint* asUint = (uint*) &value; - return *asUint; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe ulong ToUlong(T value) where T : unmanaged - { - ulong* asUlong = (ulong*) &value; - return *asUlong; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe float ToSingle(T value) where T : unmanaged - { - float* asFloat = (float*) &value; - return *asFloat; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe double ToDouble(T value) where T : unmanaged - { - double* asDouble = (double*) &value; - return *asDouble; - } - - /// - /// Write a value of any unmanaged type to the buffer. - /// It will be copied into the buffer exactly as it exists in memory. - /// - /// The value to copy - /// Any unmanaged type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteValue(in T value) where T : unmanaged - { - int len = sizeof(T); - T* pointer = (T*)(m_bufferPointer+m_position); - *pointer = value; - m_position += len; - } - } -} \ No newline at end of file diff --git a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta b/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta deleted file mode 100644 index 6a6eb1001b..0000000000 --- a/com.unity.multiplayer.mlapi/Runtime/Serialization/FastBufferWriter.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b6cac99a38cd00a41a020d7f46dfb0f4 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From 3a2a50b837d7bfd7faa502f87ccca8921e126e63 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:57:19 -0500 Subject: [PATCH 14/29] Standards fixes --- .../Runtime/Serialization/BitCounter.cs | 34 +-- .../Runtime/Serialization/BitReader.cs | 40 +-- .../Runtime/Serialization/BitWriter.cs | 49 ++-- .../Runtime/Serialization/BufferSerializer.cs | 38 +-- .../Serialization/BufferSerializerReader.cs | 10 +- .../Serialization/BufferSerializerWriter.cs | 8 +- .../Runtime/Serialization/BytePacker.cs | 46 ++-- .../Runtime/Serialization/ByteUnpacker.cs | 112 ++++----- .../Runtime/Serialization/BytewiseUtility.cs | 4 +- .../Runtime/Serialization/FastBufferReader.cs | 234 +++++++++--------- .../Runtime/Serialization/FastBufferWriter.cs | 214 ++++++++-------- .../IBufferSerializerImplementation.cs | 9 +- .../Serialization/SerializationTypeTable.cs | 172 ++++++------- .../Runtime/Utility/Ref.cs | 6 +- .../Runtime/Utility/RefArray.cs | 38 +-- .../Editor/Serialization/BitCounterTests.cs | 16 +- .../Editor/Serialization/BitReaderTests.cs | 68 ++--- .../Editor/Serialization/BitWriterTests.cs | 118 ++++----- .../Serialization/BufferSerializerTests.cs | 212 ++++++++-------- .../Editor/Serialization/BytePackerTests.cs | 187 +++++++------- .../Serialization/FastBufferReaderTests.cs | 173 +++++++------ .../Serialization/FastBufferWriterTests.cs | 233 +++++++++-------- .../Tests/Runtime/NetworkSceneManagerTests.cs | 12 +- 23 files changed, 1006 insertions(+), 1027 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs index 44c09dc8cf..fad7dcf093 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs @@ -1,4 +1,4 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; namespace Unity.Multiplayer.Netcode { @@ -11,7 +11,7 @@ public static class BitCounter private const uint k_DeBruijnMagic32 = 0x06EB14F9; // We're counting bytes, not bits, so these have all had the operation x/8 + 1 applied - private static readonly int[] k_deBruijnTableBytes64 = + private static readonly int[] k_DeBruijnTableBytes64 = { 0/8+1, 1/8+1, 17/8+1, 2/8+1, 18/8+1, 50/8+1, 3/8+1, 57/8+1, 47/8+1, 19/8+1, 22/8+1, 51/8+1, 29/8+1, 4/8+1, 33/8+1, 58/8+1, @@ -22,18 +22,18 @@ public static class BitCounter 62/8+1, 55/8+1, 45/8+1, 31/8+1, 13/8+1, 39/8+1, 36/8+1, 6/8+1, 61/8+1, 44/8+1, 12/8+1, 35/8+1, 60/8+1, 11/8+1, 10/8+1, 9/8+1, }; - - private static readonly int[] k_deBruijnTableBytes32 = + + private static readonly int[] k_DeBruijnTableBytes32 = { - 0/8+1, 1/8+1, 16/8+1, 2/8+1, 29/8+1, 17/8+1, 3/8+1, 22/8+1, + 0/8+1, 1/8+1, 16/8+1, 2/8+1, 29/8+1, 17/8+1, 3/8+1, 22/8+1, 30/8+1, 20/8+1, 18/8+1, 11/8+1, 13/8+1, 4/8+1, 7/8+1, 23/8+1, - 31/8+1, 15/8+1, 28/8+1, 21/8+1, 19/8+1, 10/8+1, 12/8+1, 6/8+1, + 31/8+1, 15/8+1, 28/8+1, 21/8+1, 19/8+1, 10/8+1, 12/8+1, 6/8+1, 14/8+1, 27/8+1, 9/8+1, 5/8+1, 26/8+1, 8/8+1, 25/8+1, 24/8+1, }; - + // And here we're counting the number of set bits, not the position of the highest set, // so these still have +1 applied - unfortunately 0 and 1 both return the same value. - private static readonly int[] k_deBruijnTableBits64 = + private static readonly int[] k_DeBruijnTableBits64 = { 0+1, 1+1, 17+1, 2+1, 18+1, 50+1, 3+1, 57+1, 47+1, 19+1, 22+1, 51+1, 29+1, 4+1, 33+1, 58+1, @@ -44,12 +44,12 @@ public static class BitCounter 62+1, 55+1, 45+1, 31+1, 13+1, 39+1, 36+1, 6+1, 61+1, 44+1, 12+1, 35+1, 60+1, 11+1, 10+1, 9+1, }; - - private static readonly int[] k_deBruijnTableBits32 = + + private static readonly int[] k_DeBruijnTableBits32 = { - 0+1, 1+1, 16+1, 2+1, 29+1, 17+1, 3+1, 22+1, + 0+1, 1+1, 16+1, 2+1, 29+1, 17+1, 3+1, 22+1, 30+1, 20+1, 18+1, 11+1, 13+1, 4+1, 7+1, 23+1, - 31+1, 15+1, 28+1, 21+1, 19+1, 10+1, 12+1, 6+1, + 31+1, 15+1, 28+1, 21+1, 19+1, 10+1, 12+1, 6+1, 14+1, 27+1, 9+1, 5+1, 26+1, 8+1, 25+1, 24+1, }; @@ -67,7 +67,7 @@ public static int GetUsedByteCount(uint value) value |= value >> 8; value |= value >> 16; value = value & ~(value >> 1); - return k_deBruijnTableBytes32[value*k_DeBruijnMagic32 >> 27]; + return k_DeBruijnTableBytes32[value * k_DeBruijnMagic32 >> 27]; } /// @@ -85,7 +85,7 @@ public static int GetUsedByteCount(ulong value) value |= value >> 16; value |= value >> 32; value = value & ~(value >> 1); - return k_deBruijnTableBytes64[value*k_DeBruijnMagic64 >> 58]; + return k_DeBruijnTableBytes64[value * k_DeBruijnMagic64 >> 58]; } /// @@ -102,7 +102,7 @@ public static int GetUsedBitCount(uint value) value |= value >> 8; value |= value >> 16; value = value & ~(value >> 1); - return k_deBruijnTableBits32[value*k_DeBruijnMagic32 >> 27]; + return k_DeBruijnTableBits32[value * k_DeBruijnMagic32 >> 27]; } /// @@ -120,7 +120,7 @@ public static int GetUsedBitCount(ulong value) value |= value >> 16; value |= value >> 32; value = value & ~(value >> 1); - return k_deBruijnTableBits64[value*k_DeBruijnMagic64 >> 58]; + return k_DeBruijnTableBits64[value * k_DeBruijnMagic64 >> 58]; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 60f84c8b74..01ddbc6b91 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Runtime.CompilerServices; -using Unity.Collections.LowLevel.Unsafe; using Unity.Netcode; namespace Unity.Multiplayer.Netcode @@ -15,12 +14,13 @@ public ref struct BitReader private Ref m_Reader; private readonly unsafe byte* m_BufferPointer; private readonly int m_Position; - private const int BITS_PER_BYTE = 8; private int m_BitPosition; #if DEVELOPMENT_BUILD || UNITY_EDITOR private int m_AllowedBitwiseReadMark; #endif + private const int k_BitsPerByte = 8; + /// /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. /// @@ -34,11 +34,11 @@ internal unsafe BitReader(ref FastBufferReader reader) { m_Reader = new Ref(ref reader); - m_BufferPointer = m_Reader.Value.m_BufferPointer + m_Reader.Value.Position; + m_BufferPointer = m_Reader.Value.BufferPointer + m_Reader.Value.Position; m_Position = m_Reader.Value.Position; m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseReadMark = (m_Reader.Value.m_AllowedReadMark - m_Position) * BITS_PER_BYTE; + m_AllowedBitwiseReadMark = (m_Reader.Value.AllowedReadMark - m_Position) * k_BitsPerByte; #endif } @@ -75,7 +75,7 @@ public bool VerifyCanReadBits(int bitCount) ++totalBytesWrittenInBitwiseContext; } - if (m_Reader.Value.m_Position + totalBytesWrittenInBitwiseContext > m_Reader.Value.m_Length) + if (m_Reader.Value.PositionInternal + totalBytesWrittenInBitwiseContext > m_Reader.Value.LengthInternal) { return false; } @@ -102,7 +102,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) { throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); } - + int checkPos = (m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { @@ -111,8 +111,8 @@ public unsafe void ReadBits(out ulong value, int bitCount) #endif ulong val = 0; - int wholeBytes = bitCount / BITS_PER_BYTE; - byte* asBytes = (byte*) &val; + int wholeBytes = bitCount / k_BitsPerByte; + byte* asBytes = (byte*)&val; if (BitAligned) { if (wholeBytes != 0) @@ -127,11 +127,11 @@ public unsafe void ReadBits(out ulong value, int bitCount) ReadMisaligned(out asBytes[i]); } } - + val |= (ulong)ReadByteBits(bitCount & 7) << (bitCount & ~7); value = val; } - + /// /// Read bits from stream. /// @@ -163,25 +163,25 @@ public unsafe void ReadBit(out bool bit) throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); } #endif - + int offset = m_BitPosition & 7; int pos = m_BitPosition >> 3; bit = (m_BufferPointer[pos] & (1 << offset)) != 0; ++m_BitPosition; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + private unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T : unmanaged { - T val = new T(); - byte* ptr = ((byte*) &val) + offsetBytes; + var val = new T(); + byte* ptr = ((byte*)&val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); - m_BitPosition += bytesToRead * BITS_PER_BYTE; + m_BitPosition += bytesToRead * k_BitsPerByte; value = val; } - + private byte ReadByteBits(int bitCount) { if (bitCount > 8) @@ -211,8 +211,8 @@ private unsafe void ReadMisaligned(out byte value) int off = m_BitPosition & 7; int pos = m_BitPosition >> 3; int shift1 = 8 - off; - + value = (byte)((m_BufferPointer[pos] >> shift1) | (m_BufferPointer[(m_BitPosition += 8) >> 3] << shift1)); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index db70356262..903b279f76 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Runtime.CompilerServices; -using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { @@ -18,7 +17,7 @@ public ref struct BitWriter #if DEVELOPMENT_BUILD || UNITY_EDITOR private int m_AllowedBitwiseWriteMark; #endif - private const int BITS_PER_BYTE = 8; + private const int k_BitsPerByte = 8; /// /// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary. @@ -28,15 +27,15 @@ public bool BitAligned [MethodImpl(MethodImplOptions.AggressiveInlining)] get => (m_BitPosition & 7) == 0; } - + internal unsafe BitWriter(ref FastBufferWriter writer) { m_Writer = new Ref(ref writer); - m_BufferPointer = writer.m_BufferPointer + writer.m_Position; - m_Position = writer.m_Position; + m_BufferPointer = writer.BufferPointer + writer.PositionInternal; + m_Position = writer.PositionInternal; m_BitPosition = 0; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseWriteMark = (m_Writer.Value.m_AllowedWriteMark - m_Writer.Value.Position) * BITS_PER_BYTE; + m_AllowedBitwiseWriteMark = (m_Writer.Value.AllowedWriteMark - m_Writer.Value.Position) * k_BitsPerByte; #endif } @@ -54,7 +53,7 @@ public void Dispose() m_Writer.Value.CommitBitwiseWrites(bytesWritten); } - + /// /// Verifies the requested bit count can be written to the buffer. /// This exists as a separate method to allow multiple bit writes to be bounds checked with a single call. @@ -74,12 +73,12 @@ public unsafe bool VerifyCanWriteBits(int bitCount) ++totalBytesWrittenInBitwiseContext; } - if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.m_Capacity) + if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.CapacityInternal) { - if (m_Writer.Value.m_Capacity < m_Writer.Value.m_MaxCapacity) + if (m_Writer.Value.CapacityInternal < m_Writer.Value.MaxCapacityInternal) { m_Writer.Value.Grow(); - m_BufferPointer = m_Writer.Value.m_BufferPointer + m_Writer.Value.m_Position; + m_BufferPointer = m_Writer.Value.BufferPointer + m_Writer.Value.PositionInternal; } else { @@ -109,7 +108,7 @@ public unsafe void WriteBits(ulong value, int bitCount) { throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); } - + int checkPos = (m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { @@ -117,8 +116,8 @@ public unsafe void WriteBits(ulong value, int bitCount) } #endif - int wholeBytes = bitCount / BITS_PER_BYTE; - byte* asBytes = (byte*) &value; + int wholeBytes = bitCount / k_BitsPerByte; + byte* asBytes = (byte*)&value; if (BitAligned) { if (wholeBytes != 0) @@ -133,13 +132,13 @@ public unsafe void WriteBits(ulong value, int bitCount) WriteMisaligned(asBytes[i]); } } - - for (var count = wholeBytes * BITS_PER_BYTE; count < bitCount; ++count) + + for (var count = wholeBytes * k_BitsPerByte; count < bitCount; ++count) { WriteBit((value & (1UL << count)) != 0); } } - + /// /// Write bits to stream. /// @@ -154,7 +153,7 @@ public void WriteBits(byte value, int bitCount) throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif - + for (int i = 0; i < bitCount; ++i) { WriteBit(((value >> i) & 1) != 0); @@ -175,23 +174,23 @@ public unsafe void WriteBit(bool bit) throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); } #endif - + int offset = m_BitPosition & 7; int pos = m_BitPosition >> 3; ++m_BitPosition; m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset))); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged + private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T : unmanaged { - byte* ptr = ((byte*) &value) + offsetBytes; + byte* ptr = ((byte*)&value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); - m_BitPosition += bytesToWrite * BITS_PER_BYTE; + m_BitPosition += bytesToWrite * k_BitsPerByte; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] private unsafe void WriteMisaligned(byte value) { @@ -204,4 +203,4 @@ private unsafe void WriteMisaligned(byte value) m_BitPosition += 8; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index cfe11aed19..bba0c112c9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -1,4 +1,4 @@ -using System; +using System; using Unity.Multiplayer.Netcode; using UnityEngine; @@ -26,12 +26,12 @@ namespace Unity.Netcode.Serialization public ref struct BufferSerializer where TImplementation : IBufferSerializerImplementation { private TImplementation m_Implementation; - + /// /// Check if the contained implementation is a reader /// public bool IsReader => m_Implementation.IsReader; - + /// /// Check if the contained implementation is a writer /// @@ -51,7 +51,7 @@ public ref FastBufferReader GetFastBufferReader() { return ref m_Implementation.GetFastBufferReader(); } - + /// /// Retrieves the FastBufferWriter instance. Only valid if IsWriter = true, throws /// InvalidOperationException otherwise. @@ -82,7 +82,7 @@ public void SerializeValue(ref object value, Type type, bool isNullable = false) { m_Implementation.SerializeValue(ref value, type, isNullable); } - + /// /// Serialize an INetworkSerializable /// If your INetworkSerializable is implemented by a struct, as opposed to a class, use this @@ -101,7 +101,7 @@ public void SerializeNetworkSerializable(ref T value) where T : INetworkSeria { m_Implementation.SerializeNetworkSerializable(ref value); } - + /// /// Serialize an INetworkSerializable /// @@ -113,7 +113,7 @@ public void SerializeValue(ref INetworkSerializable value) { m_Implementation.SerializeValue(ref value); } - + /// /// Serialize a GameObject /// @@ -125,7 +125,7 @@ public void SerializeValue(ref GameObject value) { m_Implementation.SerializeValue(ref value); } - + /// /// Serialize a NetworkObject /// @@ -137,7 +137,7 @@ public void SerializeValue(ref NetworkObject value) { m_Implementation.SerializeValue(ref value); } - + /// /// Serialize a NetworkBehaviour /// @@ -167,7 +167,7 @@ public void SerializeValue(ref string s, bool oneByteChars = false) { m_Implementation.SerializeValue(ref s, oneByteChars); } - + /// /// Serialize an array value. /// @@ -187,7 +187,7 @@ public void SerializeValue(ref T[] array) where T : unmanaged { m_Implementation.SerializeValue(ref array); } - + /// /// Serialize a single byte /// @@ -199,7 +199,7 @@ public void SerializeValue(ref byte value) { m_Implementation.SerializeValue(ref value); } - + /// /// Serialize an unmanaged type. Supports basic value types as well as structs. /// The provided type will be copied to/from the buffer as it exists in memory. @@ -240,7 +240,7 @@ public void SerializeValuePreChecked(ref GameObject value) { m_Implementation.SerializeValuePreChecked(ref value); } - + /// /// Serialize a NetworkObject /// @@ -249,7 +249,7 @@ public void SerializeValuePreChecked(ref NetworkObject value) { m_Implementation.SerializeValuePreChecked(ref value); } - + /// /// Serialize a NetworkBehaviour /// @@ -258,7 +258,7 @@ public void SerializeValuePreChecked(ref NetworkBehaviour value) { m_Implementation.SerializeValuePreChecked(ref value); } - + /// /// Serialize a string. /// @@ -273,7 +273,7 @@ public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) { m_Implementation.SerializeValuePreChecked(ref s, oneByteChars); } - + /// /// Serialize an array value. /// @@ -290,7 +290,7 @@ public void SerializeValuePreChecked(ref T[] array) where T : unmanaged { m_Implementation.SerializeValuePreChecked(ref array); } - + /// /// Serialize a single byte /// @@ -299,7 +299,7 @@ public void SerializeValuePreChecked(ref byte value) { m_Implementation.SerializeValuePreChecked(ref value); } - + /// /// Serialize an unmanaged type. Supports basic value types as well as structs. /// The provided type will be copied to/from the buffer as it exists in memory. @@ -310,4 +310,4 @@ public void SerializeValuePreChecked(ref T value) where T : unmanaged m_Implementation.SerializeValuePreChecked(ref value); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index e809cec933..146adc6cff 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using Unity.Multiplayer.Netcode; using UnityEngine; @@ -7,12 +7,12 @@ namespace Unity.Netcode.Serialization internal struct BufferSerializerReader : IBufferSerializerImplementation { private Ref m_Reader; - + public BufferSerializerReader(ref FastBufferReader reader) { m_Reader = new Ref(ref reader); } - + public bool IsReader => true; public bool IsWriter => false; @@ -20,7 +20,7 @@ public ref FastBufferReader GetFastBufferReader() { return ref m_Reader.Value; } - + public ref FastBufferWriter GetFastBufferWriter() { throw new InvalidOperationException("Cannot retrieve a FastBufferWriter from a serializer where IsWriter = false"); @@ -116,4 +116,4 @@ public void SerializeValuePreChecked(ref T value) where T : unmanaged m_Reader.Value.ReadValue(out value); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index 916c0f51ef..8349ea0d3c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -1,4 +1,4 @@ -using System; +using System; using Unity.Multiplayer.Netcode; using UnityEngine; @@ -12,10 +12,10 @@ public BufferSerializerWriter(ref FastBufferWriter writer) { m_Writer = new Ref(ref writer); } - + public bool IsReader => false; public bool IsWriter => true; - + public ref FastBufferReader GetFastBufferReader() { throw new InvalidOperationException("Cannot retrieve a FastBufferReader from a serializer where IsReader = false"); @@ -116,4 +116,4 @@ public void SerializeValuePreChecked(ref T value) where T : unmanaged m_Writer.Value.WriteValue(value); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index f935ad400d..c0e8672179 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using Unity.Netcode; using UnityEngine; @@ -40,7 +40,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, return; } } - + var type = value.GetType(); var hasSerializer = SerializationTypeTable.SerializersPacked.TryGetValue(type, out var serializer); if (hasSerializer) @@ -48,7 +48,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, serializer(ref writer, value); return; } - + if (value is Array array) { WriteValuePacked(ref writer, array.Length); @@ -58,7 +58,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, WriteObjectPacked(ref writer, array.GetValue(i)); } } - + if (value.GetType().IsEnum) { switch (Convert.GetTypeCode(value)) @@ -142,9 +142,9 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, throw new ArgumentException($"{nameof(NetworkWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); } #endregion - + #region Unmanaged Type Packing - + #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -176,7 +176,7 @@ public static unsafe void WriteValuePacked(ref FastBufferWriter writer, T break; } } - + /// /// Write single-precision floating point value to the buffer as a varint /// @@ -198,7 +198,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) { WriteUInt64Packed(ref writer, ToUlong(value)); } - + /// /// Write a byte to the buffer. /// @@ -206,7 +206,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, byte value) => writer.WriteByteSafe(value); - + /// /// Write a signed byte to the buffer. /// @@ -214,7 +214,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, double value) /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void WriteValuePacked(ref FastBufferWriter writer, sbyte value) => writer.WriteByteSafe((byte)value); - + /// /// Write a bool to the buffer. /// @@ -411,7 +411,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) } #endif #endregion - + #region Bit Packing #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -430,7 +430,7 @@ public static void WriteValuePacked(ref FastBufferWriter writer, string s) /// /// The writer to write to /// The value to pack - public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort) Arithmetic.ZigZagEncode(value)); + public static void WriteValueBitPacked(ref FastBufferWriter writer, short value) => WriteValueBitPacked(ref writer, (ushort)Arithmetic.ZigZagEncode(value)); /// /// Writes a 15-bit unsigned short to the buffer in a bit-encoded packed format. @@ -450,7 +450,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value throw new ArgumentException("BitPacked ushorts must be <= 15 bits"); } #endif - + if (value <= 0b0111_1111) { if (!writer.VerifyCanWriteInternal(1)) @@ -460,7 +460,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value writer.WriteByte((byte)(value << 1)); return; } - + if (!writer.VerifyCanWriteInternal(2)) { throw new OverflowException("Writing past the end of the buffer"); @@ -479,7 +479,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value /// /// The writer to write to /// The value to pack - public static void WriteValueBitPacked(ref FastBufferWriter writer, int value) => WriteValueBitPacked(ref writer, (uint) Arithmetic.ZigZagEncode(value)); + public static void WriteValueBitPacked(ref FastBufferWriter writer, int value) => WriteValueBitPacked(ref writer, (uint)Arithmetic.ZigZagEncode(value)); /// /// Writes a 30-bit unsigned int to the buffer in a bit-encoded packed format. @@ -565,15 +565,15 @@ private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) return; } var writeBytes = BitCounter.GetUsedByteCount(value); - - if (!writer.VerifyCanWriteInternal(writeBytes+1)) + + if (!writer.VerifyCanWriteInternal(writeBytes + 1)) { throw new OverflowException("Writing past the end of the buffer"); } writer.WriteByte((byte)(247 + writeBytes)); writer.WritePartialValue(value, writeBytes); } - + // Looks like the same code as WriteUInt64Packed? // It's actually different because it will call the more efficient 32-bit version // of BytewiseUtility.GetUsedByteCount(). @@ -591,8 +591,8 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) return; } var writeBytes = BitCounter.GetUsedByteCount(value); - - if (!writer.VerifyCanWriteInternal(writeBytes+1)) + + if (!writer.VerifyCanWriteInternal(writeBytes + 1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -603,16 +603,16 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe uint ToUint(T value) where T : unmanaged { - uint* asUint = (uint*) &value; + uint* asUint = (uint*)&value; return *asUint; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe ulong ToUlong(T value) where T : unmanaged { - ulong* asUlong = (ulong*) &value; + ulong* asUlong = (ulong*)&value; return *asUlong; } #endregion } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index a5601dfd60..bc5edc3baf 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using Unity.Netcode; using UnityEngine; @@ -37,14 +37,14 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu return; } } - + var hasDeserializer = SerializationTypeTable.DeserializersPacked.TryGetValue(type, out var deserializer); if (hasDeserializer) { deserializer(ref reader, out value); return; } - + if (type.IsArray && type.HasElementType) { ReadValuePacked(ref reader, out int length); @@ -60,7 +60,7 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu value = arr; return; } - + if (type.IsEnum) { switch (Type.GetTypeCode(type)) @@ -107,7 +107,7 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu return; } } - + if (type == typeof(GameObject)) { reader.ReadValueSafe(out GameObject go); @@ -137,9 +137,9 @@ public static void ReadObjectPacked(ref FastBufferReader reader, out object valu throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); } #endregion - + #region Unmanaged Type Packing - + #if UNITY_NETCODE_DEBUG_NO_PACKING [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -194,7 +194,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value ReadUInt64Packed(ref reader, out ulong asULong); value = ToDouble(asULong); } - + /// /// Read a byte from the stream. /// @@ -212,7 +212,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out double value public static void ReadValuePacked(ref FastBufferReader reader, out sbyte value) { reader.ReadByteSafe(out byte byteVal); - value = (sbyte) byteVal; + value = (sbyte)byteVal; } /// @@ -271,7 +271,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out int value) ReadUInt32Packed(ref reader, out uint readValue); value = (int)Arithmetic.ZigZagDecode(readValue); } - + /// /// Read an unsigned int (UInt32) from the stream. /// @@ -299,7 +299,7 @@ public static void ReadValuePacked(ref FastBufferReader reader, out long value) ReadUInt64Packed(ref reader, out ulong readValue); value = Arithmetic.ZigZagDecode(readValue); } - + /// /// Convenience method that reads two packed Vector3 from the ray from the stream /// @@ -434,7 +434,7 @@ public static unsafe void ReadValuePacked(ref FastBufferReader reader, out strin } #endif #endregion - + #region Bit Packing #if UNITY_NETCODE_DEBUG_NO_PACKING @@ -463,7 +463,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out short val public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ushort value) { ushort returnValue = 0; - byte* ptr = ((byte*) &returnValue); + byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b1) + 1; if (!reader.VerifyCanReadInternal(numBytes)) @@ -478,7 +478,7 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out us break; case 2: *ptr = *data; - *(ptr+1) = *(data+1); + *(ptr + 1) = *(data + 1); break; default: throw new InvalidOperationException("Could not read bit-packed value: impossible byte count"); @@ -498,7 +498,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out int value ReadValueBitPacked(ref reader, out uint readValue); value = (int)Arithmetic.ZigZagDecode(readValue); } - + /// /// Read a bit-packed 30-bit unsigned int from the stream. /// See BytePacker.cs for a description of the format. @@ -508,7 +508,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out int value public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out uint value) { uint returnValue = 0; - byte* ptr = ((byte*) &returnValue); + byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b11) + 1; if (!reader.VerifyCanReadInternal(numBytes)) @@ -523,18 +523,18 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ui break; case 2: *ptr = *data; - *(ptr+1) = *(data+1); + *(ptr + 1) = *(data + 1); break; case 3: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); break; case 4: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); break; } @@ -552,7 +552,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out long valu ReadValueBitPacked(ref reader, out ulong readValue); value = Arithmetic.ZigZagDecode(readValue); } - + /// /// Read a bit-packed 61-bit signed long from the stream. /// See BytePacker.cs for a description of the format. @@ -562,7 +562,7 @@ public static void ReadValueBitPacked(ref FastBufferReader reader, out long valu public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ulong value) { ulong returnValue = 0; - byte* ptr = ((byte*) &returnValue); + byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b111) + 1; if (!reader.VerifyCanReadInternal(numBytes)) @@ -577,52 +577,52 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ul break; case 2: *ptr = *data; - *(ptr+1) = *(data+1); + *(ptr + 1) = *(data + 1); break; case 3: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); break; case 4: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); break; case 5: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); - *(ptr+4) = *(data+4); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); + *(ptr + 4) = *(data + 4); break; case 6: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); - *(ptr+4) = *(data+4); - *(ptr+5) = *(data+5); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); + *(ptr + 4) = *(data + 4); + *(ptr + 5) = *(data + 5); break; case 7: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); - *(ptr+4) = *(data+4); - *(ptr+5) = *(data+5); - *(ptr+6) = *(data+6); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); + *(ptr + 4) = *(data + 4); + *(ptr + 5) = *(data + 5); + *(ptr + 6) = *(data + 6); break; case 8: *ptr = *data; - *(ptr+1) = *(data+1); - *(ptr+2) = *(data+2); - *(ptr+3) = *(data+3); - *(ptr+4) = *(data+4); - *(ptr+5) = *(data+5); - *(ptr+6) = *(data+6); - *(ptr+7) = *(data+7); + *(ptr + 1) = *(data + 1); + *(ptr + 2) = *(data + 2); + *(ptr + 3) = *(data + 3); + *(ptr + 4) = *(data + 4); + *(ptr + 5) = *(data + 5); + *(ptr + 6) = *(data + 6); + *(ptr + 7) = *(data + 7); break; } @@ -655,7 +655,7 @@ private static void ReadUInt64Packed(ref FastBufferReader reader, out ulong valu } reader.ReadPartialValue(out value, numBytes); } - + private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value) { reader.ReadByteSafe(out byte firstByte); @@ -683,16 +683,16 @@ private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe float ToSingle(T value) where T : unmanaged { - float* asFloat = (float*) &value; + float* asFloat = (float*)&value; return *asFloat; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe double ToDouble(T value) where T : unmanaged { - double* asDouble = (double*) &value; + double* asDouble = (double*)&value; return *asDouble; } #endregion } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs index 1cfda38e3c..36394addad 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -1,4 +1,4 @@ -using Unity.Collections.LowLevel.Unsafe; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Multiplayer.Netcode { @@ -77,4 +77,4 @@ public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 6501441f71..34336fed81 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -9,12 +9,12 @@ namespace Unity.Multiplayer.Netcode { public struct FastBufferReader : IDisposable { - internal readonly unsafe byte* m_BufferPointer; - internal int m_Position; - internal readonly int m_Length; + internal readonly unsafe byte* BufferPointer; + internal int PositionInternal; + internal readonly int LengthInternal; private readonly Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - internal int m_AllowedReadMark; + internal int AllowedReadMark; private bool m_InBitwiseContext; #endif @@ -24,7 +24,7 @@ public struct FastBufferReader : IDisposable public int Position { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position; + get => PositionInternal; } /// @@ -33,13 +33,13 @@ public int Position public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Length; + get => LengthInternal; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CommitBitwiseReads(int amount) { - m_Position += amount; + PositionInternal += amount; #if DEVELOPMENT_BUILD || UNITY_EDITOR m_InBitwiseContext = false; #endif @@ -47,18 +47,18 @@ internal void CommitBitwiseReads(int amount) public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? buffer.Length : length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr()+offset, m_Length); - m_BufferPointer = (byte*)bufferPtr; - m_Position = offset; + LengthInternal = Math.Max(1, length == -1 ? buffer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr() + offset, LengthInternal); + BufferPointer = (byte*)bufferPtr; + PositionInternal = offset; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } - + /// /// Create a FastBufferReader from an ArraySegment. /// A new buffer will be created using the given allocator and the value will be copied in. @@ -70,21 +70,21 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in /// The offset of the buffer to start copying from public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? (buffer.Count - offset) : length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + LengthInternal = Math.Max(1, length == -1 ? (buffer.Count - offset) : length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); fixed (byte* data = buffer.Array) { - UnsafeUtility.MemCpy(bufferPtr, data+offset, m_Length); + UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); } - m_BufferPointer = (byte*) bufferPtr; - m_Position = 0; + BufferPointer = (byte*)bufferPtr; + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } - + /// /// Create a FastBufferReader from an existing byte array. /// A new buffer will be created using the given allocator and the value will be copied in. @@ -96,21 +96,21 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i /// The offset of the buffer to start copying from public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? (buffer.Length - offset) : length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); + LengthInternal = Math.Max(1, length == -1 ? (buffer.Length - offset) : length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); fixed (byte* data = buffer) { - UnsafeUtility.MemCpy(bufferPtr, data+offset, m_Length); + UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); } - m_BufferPointer = (byte*) bufferPtr; - m_Position = 0; + BufferPointer = (byte*)bufferPtr; + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } - + /// /// Create a FastBufferReader from an existing byte buffer. /// A new buffer will be created using the given allocator and the value will be copied in. @@ -122,18 +122,18 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = /// The offset of the buffer to start copying from public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0) { - m_Length = Math.Max(1, length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, buffer + offset, m_Length); - m_BufferPointer = (byte*) bufferPtr; - m_Position = 0; + LengthInternal = Math.Max(1, length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, buffer + offset, LengthInternal); + BufferPointer = (byte*)bufferPtr; + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } - + /// /// Create a FastBufferReader from a FastBufferWriter. /// A new buffer will be created using the given allocator and the value will be copied in. @@ -145,14 +145,14 @@ public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, in /// The offset of the buffer to start copying from public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, int length = -1, int offset = 0) { - m_Length = Math.Max(1, length == -1 ? writer.Length : length); - void* bufferPtr = UnsafeUtility.Malloc(m_Length, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, writer.GetUnsafePtr() + offset, m_Length); - m_BufferPointer = (byte*) bufferPtr; - m_Position = 0; + LengthInternal = Math.Max(1, length == -1 ? writer.Length : length); + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, writer.GetUnsafePtr() + offset, LengthInternal); + BufferPointer = (byte*)bufferPtr; + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = 0; + AllowedReadMark = 0; m_InBitwiseContext = false; #endif } @@ -162,7 +162,7 @@ public unsafe FastBufferReader(ref FastBufferWriter writer, Allocator allocator, /// public unsafe void Dispose() { - UnsafeUtility.Free(m_BufferPointer, m_Allocator); + UnsafeUtility.Free(BufferPointer, m_Allocator); } /// @@ -172,7 +172,7 @@ public unsafe void Dispose() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Seek(int where) { - m_Position = Math.Min(Length, where); + PositionInternal = Math.Min(Length, where); } /// @@ -190,12 +190,12 @@ internal void MarkBytesRead(int amount) throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + amount > m_AllowedReadMark) + if (PositionInternal + amount > AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - m_Position += amount; + PositionInternal += amount; } /// @@ -236,12 +236,12 @@ public bool VerifyCanRead(int bytes) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Length) + if (PositionInternal + bytes > LengthInternal) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = m_Position + bytes; + AllowedReadMark = PositionInternal + bytes; #endif return true; } @@ -271,12 +271,12 @@ public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged } #endif int len = sizeof(T); - if (m_Position + len > m_Length) + if (PositionInternal + len > LengthInternal) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedReadMark = m_Position + len; + AllowedReadMark = PositionInternal + len; #endif return true; } @@ -298,14 +298,14 @@ internal bool VerifyCanReadInternal(int bytes) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Length) + if (PositionInternal + bytes > LengthInternal) { return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_Position + bytes > m_AllowedReadMark) + if (PositionInternal + bytes > AllowedReadMark) { - m_AllowedReadMark = m_Position + bytes; + AllowedReadMark = PositionInternal + bytes; } #endif return true; @@ -322,7 +322,7 @@ public unsafe byte[] ToArray() byte[] ret = new byte[Length]; fixed (byte* b = ret) { - UnsafeUtility.MemCpy(b, m_BufferPointer, Length); + UnsafeUtility.MemCpy(b, BufferPointer, Length); } return ret; } @@ -334,7 +334,7 @@ public unsafe byte[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { - return m_BufferPointer; + return BufferPointer; } /// @@ -344,7 +344,7 @@ public unsafe byte[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { - return m_BufferPointer + m_Position; + return BufferPointer + PositionInternal; } /// @@ -369,14 +369,14 @@ public void ReadObject(out object value, Type type, bool isNullable = false) return; } } - + var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); if (hasDeserializer) { deserializer(ref this, out value); return; } - + if (type.IsArray && type.HasElementType) { ReadValueSafe(out int length); @@ -392,7 +392,7 @@ public void ReadObject(out object value, Type type, bool isNullable = false) value = arr; return; } - + if (type.IsEnum) { switch (Type.GetTypeCode(type)) @@ -439,7 +439,7 @@ public void ReadObject(out object value, Type type, bool isNullable = false) return; } } - + if (type == typeof(GameObject)) { ReadValueSafe(out GameObject go); @@ -487,7 +487,7 @@ public void ReadNetworkSerializable(out T value) where T : INetworkSerializab public void ReadValue(out GameObject value) { ReadValue(out ulong networkObjectId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject.gameObject; @@ -501,7 +501,7 @@ public void ReadValue(out GameObject value) value = null; } - + /// /// Read a GameObject /// @@ -512,7 +512,7 @@ public void ReadValue(out GameObject value) public void ReadValueSafe(out GameObject value) { ReadValueSafe(out ulong networkObjectId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject.gameObject; @@ -534,7 +534,7 @@ public void ReadValueSafe(out GameObject value) public void ReadValue(out NetworkObject value) { ReadValue(out ulong networkObjectId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject; @@ -559,7 +559,7 @@ public void ReadValue(out NetworkObject value) public void ReadValueSafe(out NetworkObject value) { ReadValueSafe(out ulong networkObjectId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject; @@ -582,7 +582,7 @@ public void ReadValue(out NetworkBehaviour value) { ReadValue(out ulong networkObjectId); ReadValue(out ushort networkBehaviourId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); @@ -608,7 +608,7 @@ public void ReadValueSafe(out NetworkBehaviour value) { ReadValueSafe(out ulong networkObjectId); ReadValueSafe(out ushort networkBehaviourId); - + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) { value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); @@ -622,7 +622,7 @@ public void ReadValueSafe(out NetworkBehaviour value) value = null; } - + /// /// Reads a string /// NOTE: ALLOCATES @@ -640,13 +640,13 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - ReadByte(out byte b); - native[i] = (char) b; + ReadByte(out byte b); + native[i] = (char)b; } } else { - ReadBytes((byte*) native, target * sizeof(char)); + ReadBytes((byte*)native, target * sizeof(char)); } } } @@ -669,14 +669,14 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(sizeof(uint))) { throw new OverflowException("Reading past the end of the buffer"); } - + ReadValue(out uint length); - + if (!VerifyCanReadInternal((int)length * (oneByteChars ? 1 : sizeof(char)))) { throw new OverflowException("Reading past the end of the buffer"); @@ -689,13 +689,13 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - ReadByte(out byte b); - native[i] = (char) b; + ReadByte(out byte b); + native[i] = (char)b; } } else { - ReadBytes((byte*) native, target * sizeof(char)); + ReadBytes((byte*)native, target * sizeof(char)); } } } @@ -706,7 +706,7 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) /// /// Stores the read array [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadValue(out T[] array) where T: unmanaged + public unsafe void ReadValue(out T[] array) where T : unmanaged { ReadValue(out int sizeInTs); int sizeInBytes = sizeInTs * sizeof(T); @@ -727,7 +727,7 @@ public unsafe void ReadValue(out T[] array) where T: unmanaged /// /// Stores the read array [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadValueSafe(out T[] array) where T: unmanaged + public unsafe void ReadValueSafe(out T[] array) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -736,7 +736,7 @@ public unsafe void ReadValueSafe(out T[] array) where T: unmanaged "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); @@ -754,7 +754,7 @@ public unsafe void ReadValueSafe(out T[] array) where T: unmanaged ReadBytes(bytes, sizeInBytes); } } - + /// /// Read a partial value. The value is zero-initialized and then the specified number of bytes is read into it. /// @@ -765,7 +765,7 @@ public unsafe void ReadValueSafe(out T[] array) where T: unmanaged /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T: unmanaged + public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetBytes = 0) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -773,18 +773,18 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + bytesToRead > m_AllowedReadMark) + if (PositionInternal + bytesToRead > AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - T val = new T(); - byte* ptr = ((byte*) &val) + offsetBytes; - byte* bufferPointer = m_BufferPointer + m_Position; + var val = new T(); + byte* ptr = ((byte*)&val) + offsetBytes; + byte* bufferPointer = BufferPointer + PositionInternal; UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); - m_Position += bytesToRead; + PositionInternal += bytesToRead; value = val; } @@ -794,19 +794,19 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB /// Stores the read value [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadByte(out byte value) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + 1 > m_AllowedReadMark) + if (PositionInternal + 1 > AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - value = m_BufferPointer[m_Position++]; + value = BufferPointer[PositionInternal++]; } /// @@ -818,7 +818,7 @@ public unsafe void ReadByte(out byte value) /// Stores the read value [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadByteSafe(out byte value) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -826,14 +826,14 @@ public unsafe void ReadByteSafe(out byte value) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(1)) { throw new OverflowException("Reading past the end of the buffer"); } - value = m_BufferPointer[m_Position++]; + value = BufferPointer[PositionInternal++]; } - + /// /// Read multiple bytes to the stream /// @@ -842,22 +842,22 @@ public unsafe void ReadByteSafe(out byte value) /// Offset of the byte buffer to store into [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadBytes(byte* value, int size, int offset = 0) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + size > m_AllowedReadMark) + if (PositionInternal + size > AllowedReadMark) { throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); } #endif - UnsafeUtility.MemCpy(value + offset, (m_BufferPointer + m_Position), size); - m_Position += size; + UnsafeUtility.MemCpy(value + offset, (BufferPointer + PositionInternal), size); + PositionInternal += size; } - + /// /// Read multiple bytes to the stream /// @@ -869,7 +869,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) /// Offset of the byte buffer to store into [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -877,15 +877,15 @@ public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } - UnsafeUtility.MemCpy(value + offset, (m_BufferPointer + m_Position), size); - m_Position += size; + UnsafeUtility.MemCpy(value + offset, (BufferPointer + PositionInternal), size); + PositionInternal += size; } - + /// /// Read multiple bytes from the stream /// @@ -900,7 +900,7 @@ public unsafe void ReadBytes(ref byte[] value, int size, int offset = 0) ReadBytes(ptr, size, offset); } } - + /// /// Read multiple bytes from the stream /// @@ -929,26 +929,26 @@ public unsafe void ReadBytesSafe(ref byte[] value, int size, int offset = 0) public unsafe void ReadValue(out T value) where T : unmanaged { int len = sizeof(T); - + #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferReader in bytewise mode while in a bitwise context."); } - if (m_Position + len > m_AllowedReadMark) + if (PositionInternal + len > AllowedReadMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - + fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes((byte*)ptr, m_BufferPointer+m_Position, len); + BytewiseUtility.FastCopyBytes((byte*)ptr, BufferPointer + PositionInternal, len); } - m_Position += len; + PositionInternal += len; } - + /// /// Read a value of any unmanaged type to the buffer. /// It will be copied from the buffer exactly as it existed in memory on the writing end. @@ -962,7 +962,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged public unsafe void ReadValueSafe(out T value) where T : unmanaged { int len = sizeof(T); - + #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -970,18 +970,18 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged "Cannot use BufferReader in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanReadInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); } - + fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes((byte*)ptr, m_BufferPointer+m_Position, len); + BytewiseUtility.FastCopyBytes((byte*)ptr, BufferPointer + PositionInternal, len); } - m_Position += len; + PositionInternal += len; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 55e1ba0b6b..91ac86ba91 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -9,24 +9,24 @@ namespace Unity.Multiplayer.Netcode { public struct FastBufferWriter : IDisposable { - internal unsafe byte* m_BufferPointer; - internal int m_Position; + internal unsafe byte* BufferPointer; + internal int PositionInternal; private int m_Length; - internal int m_Capacity; - internal readonly int m_MaxCapacity; + internal int CapacityInternal; + internal readonly int MaxCapacityInternal; private readonly Allocator m_Allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR - internal int m_AllowedWriteMark; + internal int AllowedWriteMark; private bool m_InBitwiseContext; #endif - + /// /// The current write position /// public int Position { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position; + get => PositionInternal; } /// @@ -35,7 +35,7 @@ public int Position public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Capacity; + get => CapacityInternal; } /// @@ -44,7 +44,7 @@ public int Capacity public int MaxCapacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_MaxCapacity; + get => MaxCapacityInternal; } /// @@ -53,14 +53,14 @@ public int MaxCapacity public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => m_Position > m_Length ? m_Position : m_Length; + get => PositionInternal > m_Length ? PositionInternal : m_Length; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CommitBitwiseWrites(int amount) { - m_Position += amount; + PositionInternal += amount; #if DEVELOPMENT_BUILD || UNITY_EDITOR m_InBitwiseContext = false; #endif @@ -78,14 +78,14 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, size); #endif - m_BufferPointer = (byte*)buffer; - m_Position = 0; + BufferPointer = (byte*)buffer; + PositionInternal = 0; m_Length = 0; - m_Capacity = size; + CapacityInternal = size; m_Allocator = allocator; - m_MaxCapacity = maxSize < size ? size : maxSize; + MaxCapacityInternal = maxSize < size ? size : maxSize; #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedWriteMark = 0; + AllowedWriteMark = 0; m_InBitwiseContext = false; #endif } @@ -95,7 +95,7 @@ public unsafe FastBufferWriter(int size, Allocator allocator, int maxSize = -1) /// public unsafe void Dispose() { - UnsafeUtility.Free(m_BufferPointer, m_Allocator); + UnsafeUtility.Free(BufferPointer, m_Allocator); } /// @@ -116,12 +116,12 @@ public void Seek(int where) // because position increases, and if we seek backward, length remembers // the position it was in. // Seeking forward will not update the length. - where = Math.Min(where, m_Capacity); - if (m_Position > m_Length && where < m_Position) + where = Math.Min(where, CapacityInternal); + if (PositionInternal > m_Length && where < PositionInternal) { - m_Length = m_Position; + m_Length = PositionInternal; } - m_Position = where; + PositionInternal = where; } /// @@ -137,11 +137,11 @@ public void Truncate(int where = -1) where = Position; } - if (m_Position > where) + if (PositionInternal > where) { - m_Position = where; + PositionInternal = where; } - if(m_Length > where) + if (m_Length > where) { m_Length = where; } @@ -163,15 +163,15 @@ public BitWriter EnterBitwiseContext() internal unsafe void Grow() { - var newSize = Math.Min(m_Capacity * 2, m_MaxCapacity); + var newSize = Math.Min(CapacityInternal * 2, MaxCapacityInternal); void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, newSize); #endif - UnsafeUtility.MemCpy(buffer, m_BufferPointer, Length); - UnsafeUtility.Free(m_BufferPointer, m_Allocator); - m_BufferPointer = (byte*)buffer; - m_Capacity = newSize; + UnsafeUtility.MemCpy(buffer, BufferPointer, Length); + UnsafeUtility.Free(BufferPointer, m_Allocator); + BufferPointer = (byte*)buffer; + CapacityInternal = newSize; } /// @@ -198,9 +198,9 @@ public bool VerifyCanWrite(int bytes) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Capacity) + if (PositionInternal + bytes > CapacityInternal) { - if (m_Capacity < m_MaxCapacity) + if (CapacityInternal < MaxCapacityInternal) { Grow(); } @@ -210,11 +210,11 @@ public bool VerifyCanWrite(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedWriteMark = m_Position + bytes; + AllowedWriteMark = PositionInternal + bytes; #endif return true; } - + /// /// Allows faster serialization by batching bounds checking. /// When you know you will be writing multiple fields back-to-back and you know the total size, @@ -240,9 +240,9 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } #endif int len = sizeof(T); - if (m_Position + len > m_Capacity) + if (PositionInternal + len > CapacityInternal) { - if (m_Capacity < m_MaxCapacity) + if (CapacityInternal < MaxCapacityInternal) { Grow(); } @@ -252,11 +252,11 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedWriteMark = m_Position + len; + AllowedWriteMark = PositionInternal + len; #endif return true; } - + /// /// Internal version of VerifyCanWrite. /// Differs from VerifyCanWrite only in that it won't ever move the AllowedWriteMark backward. @@ -274,9 +274,9 @@ public bool VerifyCanWriteInternal(int bytes) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - if (m_Position + bytes > m_Capacity) + if (PositionInternal + bytes > CapacityInternal) { - if (m_Capacity < m_MaxCapacity) + if (CapacityInternal < MaxCapacityInternal) { Grow(); } @@ -286,9 +286,9 @@ public bool VerifyCanWriteInternal(int bytes) } } #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (m_Position + bytes > m_AllowedWriteMark) + if (PositionInternal + bytes > AllowedWriteMark) { - m_AllowedWriteMark = m_Position + bytes; + AllowedWriteMark = PositionInternal + bytes; } #endif return true; @@ -305,7 +305,7 @@ public unsafe byte[] ToArray() byte[] ret = new byte[Length]; fixed (byte* b = ret) { - UnsafeUtility.MemCpy(b, m_BufferPointer, Length); + UnsafeUtility.MemCpy(b, BufferPointer, Length); } return ret; } @@ -317,7 +317,7 @@ public unsafe byte[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtr() { - return m_BufferPointer; + return BufferPointer; } /// @@ -327,7 +327,7 @@ public unsafe byte[] ToArray() [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe byte* GetUnsafePtrAtCurrentPosition() { - return m_BufferPointer + m_Position; + return BufferPointer + PositionInternal; } /// @@ -352,7 +352,7 @@ public void WriteObject(object value, bool isNullable = false) return; } } - + var type = value.GetType(); var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); if (hasSerializer) @@ -360,7 +360,7 @@ public void WriteObject(object value, bool isNullable = false) serializer(ref this, value); return; } - + if (value is Array array) { WriteValueSafe(array.Length); @@ -372,7 +372,7 @@ public void WriteObject(object value, bool isNullable = false) return; } - + if (value.GetType().IsEnum) { switch (Convert.GetTypeCode(value)) @@ -443,7 +443,7 @@ public void WriteNetworkSerializable(in T value) where T : INetworkSerializab { // TODO } - + /// /// Get the required amount of space to write a GameObject /// @@ -453,7 +453,7 @@ public static int GetWriteSize(GameObject value) { return sizeof(ulong); } - + /// /// Get the required amount of space to write a GameObject /// @@ -462,7 +462,7 @@ public static int GetGameObjectWriteSize() { return sizeof(ulong); } - + /// /// Write a GameObject /// @@ -482,7 +482,7 @@ public void WriteValue(GameObject value) WriteValue(networkObject.NetworkObjectId); } - + /// /// Write a GameObject /// @@ -505,7 +505,7 @@ public void WriteValueSafe(GameObject value) WriteValueSafe(networkObject.NetworkObjectId); } - + /// /// Get the required size to write a NetworkObject /// @@ -515,7 +515,7 @@ public static int GetWriteSize(NetworkObject value) { return sizeof(ulong); } - + /// /// Get the required size to write a NetworkObject /// @@ -555,7 +555,7 @@ public void WriteValueSafe(NetworkObject value) } WriteValueSafe(value.NetworkObjectId); } - + /// /// Get the required size to write a NetworkBehaviour /// @@ -565,8 +565,8 @@ public static int GetWriteSize(NetworkBehaviour value) { return sizeof(ulong) + sizeof(ushort); } - - + + /// /// Get the required size to write a NetworkBehaviour /// @@ -627,7 +627,7 @@ public static int GetWriteSize(string s, bool oneByteChars = false) { return sizeof(int) + s.Length * (oneByteChars ? sizeof(byte) : sizeof(char)); } - + /// /// Writes a string /// @@ -641,7 +641,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) { for (int i = 0; i < target; ++i) { - WriteByte((byte) s[i]); + WriteByte((byte)s[i]); } } else @@ -670,21 +670,21 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + int sizeInBytes = GetWriteSize(s, oneByteChars); - + if (!VerifyCanWriteInternal(sizeInBytes)) { throw new OverflowException("Writing past the end of the buffer"); } - + WriteValue((uint)s.Length); int target = s.Length; if (oneByteChars) { for (int i = 0; i < target; ++i) { - WriteByte((byte) s[i]); + WriteByte((byte)s[i]); } } else @@ -705,7 +705,7 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T: unmanaged + public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = 0) where T : unmanaged { int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); @@ -719,7 +719,7 @@ public static unsafe int GetWriteSize(T[] array, int count = -1, int offset = /// The amount of elements to write /// Where in the array to start [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) where T: unmanaged + public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) where T : unmanaged { int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); @@ -741,7 +741,7 @@ public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) wher /// The amount of elements to write /// Where in the array to start [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) where T: unmanaged + public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -750,10 +750,10 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); - + if (!VerifyCanWriteInternal(sizeInBytes + sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); @@ -765,7 +765,7 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) WriteBytes(bytes, sizeInBytes); } } - + /// /// Write a partial value. The specified number of bytes is written from the value and the rest is ignored. /// @@ -776,7 +776,7 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T: unmanaged + public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBytes = 0) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -784,17 +784,17 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_Position + bytesToWrite > m_AllowedWriteMark) + if (PositionInternal + bytesToWrite > AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - byte* ptr = ((byte*) &value) + offsetBytes; - byte* bufferPointer = m_BufferPointer + m_Position; + byte* ptr = ((byte*)&value) + offsetBytes; + byte* bufferPointer = BufferPointer + PositionInternal; UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); - m_Position += bytesToWrite; + PositionInternal += bytesToWrite; } /// @@ -803,19 +803,19 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteByte(byte value) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_Position + 1 > m_AllowedWriteMark) + if (PositionInternal + 1 > AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - m_BufferPointer[m_Position++] = value; + BufferPointer[PositionInternal++] = value; } /// @@ -827,7 +827,7 @@ public unsafe void WriteByte(byte value) /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteByteSafe(byte value) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -835,12 +835,12 @@ public unsafe void WriteByteSafe(byte value) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanWriteInternal(1)) { throw new OverflowException("Writing past the end of the buffer"); } - m_BufferPointer[m_Position++] = value; + BufferPointer[PositionInternal++] = value; } /// @@ -851,22 +851,22 @@ public unsafe void WriteByteSafe(byte value) /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytes(byte* value, int size, int offset = 0) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_Position + size > m_AllowedWriteMark) + if (PositionInternal + size > AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - UnsafeUtility.MemCpy((m_BufferPointer + m_Position), value + offset, size); - m_Position += size; + UnsafeUtility.MemCpy((BufferPointer + PositionInternal), value + offset, size); + PositionInternal += size; } - + /// /// Write multiple bytes to the stream /// @@ -878,7 +878,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) /// Offset into the buffer to begin writing [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) - { + { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -886,15 +886,15 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanWriteInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } - UnsafeUtility.MemCpy((m_BufferPointer + m_Position), value + offset, size); - m_Position += size; + UnsafeUtility.MemCpy((BufferPointer + PositionInternal), value + offset, size); + PositionInternal += size; } - + /// /// Write multiple bytes to the stream /// @@ -909,7 +909,7 @@ public unsafe void WriteBytes(byte[] value, int size, int offset = 0) WriteBytes(ptr, size, offset); } } - + /// /// Write multiple bytes to the stream /// @@ -927,7 +927,7 @@ public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) WriteBytesSafe(ptr, size, offset); } } - + /// /// Copy the contents of this writer into another writer. /// The contents will be copied from the beginning of this writer to its current position. @@ -937,9 +937,9 @@ public unsafe void WriteBytesSafe(byte[] value, int size, int offset = 0) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyTo(FastBufferWriter other) { - other.WriteBytes(m_BufferPointer, m_Position); + other.WriteBytes(BufferPointer, PositionInternal); } - + /// /// Copy the contents of another writer into this writer. /// The contents will be copied from the beginning of the other writer to its current position. @@ -949,9 +949,9 @@ public unsafe void CopyTo(FastBufferWriter other) [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe void CopyFrom(FastBufferWriter other) { - WriteBytes(other.m_BufferPointer, other.m_Position); + WriteBytes(other.BufferPointer, other.PositionInternal); } - + /// /// Get the size required to write an unmanaged value /// @@ -985,26 +985,26 @@ public static unsafe int GetWriteSize() where T : unmanaged public unsafe void WriteValue(in T value) where T : unmanaged { int len = sizeof(T); - + #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { throw new InvalidOperationException( "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } - if (m_Position + len > m_AllowedWriteMark) + if (PositionInternal + len > AllowedWriteMark) { throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); } #endif - + fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes(m_BufferPointer+m_Position, (byte*)ptr, len); + BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)ptr, len); } - m_Position += len; + PositionInternal += len; } - + /// /// Write a value of any unmanaged type (including unmanaged structs) to the buffer. /// It will be copied into the buffer exactly as it exists in memory. @@ -1018,7 +1018,7 @@ public unsafe void WriteValue(in T value) where T : unmanaged public unsafe void WriteValueSafe(in T value) where T : unmanaged { int len = sizeof(T); - + #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) { @@ -1026,7 +1026,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged "Cannot use BufferWriter in bytewise mode while in a bitwise context."); } #endif - + if (!VerifyCanWriteInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); @@ -1034,9 +1034,9 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes(m_BufferPointer+m_Position, (byte*)ptr, len); + BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)ptr, len); } - m_Position += len; + PositionInternal += len; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs index f421088482..9d04ce6120 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs @@ -1,7 +1,4 @@ -using System; -using System.Runtime.CompilerServices; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; +using System; using Unity.Multiplayer.Netcode; using UnityEngine; @@ -24,7 +21,7 @@ public interface IBufferSerializerImplementation public void SerializeValue(ref T[] array) where T : unmanaged; public void SerializeValue(ref byte value); public void SerializeValue(ref T value) where T : unmanaged; - + // Has to have a different name to avoid conflicting with "where T: unmananged" // Using SerializeValue(INetworkSerializable) will result in boxing on struct INetworkSerializables // So this is provided as an alternative to avoid boxing allocations. @@ -39,4 +36,4 @@ public interface IBufferSerializerImplementation public void SerializeValuePreChecked(ref byte value); public void SerializeValuePreChecked(ref T value) where T : unmanaged; } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs index 28cc2ad1bb..c3e497281e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using UnityEngine; @@ -34,9 +34,9 @@ public static class SerializationTypeTable [typeof(float)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((float)value), [typeof(double)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((double)value), - + [typeof(string)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((string)value), - + [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector2)value), [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector3)value), [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Vector4)value), @@ -45,12 +45,12 @@ public static class SerializationTypeTable [typeof(Ray)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray)value), [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D)value), [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion)value), - + [typeof(char)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char)value), - + [typeof(bool)] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool)value), - - + + [typeof(byte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((byte[])value), [typeof(sbyte[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((sbyte[])value), @@ -72,12 +72,12 @@ public static class SerializationTypeTable [typeof(Ray[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray[])value), [typeof(Ray2D[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Ray2D[])value), [typeof(Quaternion[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((Quaternion[])value), - + [typeof(char[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((char[])value), - + [typeof(bool[])] = (ref FastBufferWriter writer, object value) => writer.WriteValueSafe((bool[])value), }; - + public static Dictionary Deserializers = new Dictionary { [typeof(byte)] = (ref FastBufferReader reader, out object value) => @@ -91,209 +91,209 @@ public static class SerializationTypeTable value = (sbyte)tmp; }, - [typeof(ushort)] = (ref FastBufferReader reader, out object value) => + [typeof(ushort)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out ushort tmp); value = tmp; }, - [typeof(short)] = (ref FastBufferReader reader, out object value) => + [typeof(short)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out short tmp); value = tmp; }, - [typeof(uint)] = (ref FastBufferReader reader, out object value) => + [typeof(uint)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out uint tmp); value = tmp; }, - [typeof(int)] = (ref FastBufferReader reader, out object value) => + [typeof(int)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out int tmp); value = tmp; }, - [typeof(ulong)] = (ref FastBufferReader reader, out object value) => + [typeof(ulong)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out ulong tmp); value = tmp; }, - [typeof(long)] = (ref FastBufferReader reader, out object value) => + [typeof(long)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out long tmp); value = tmp; }, - [typeof(float)] = (ref FastBufferReader reader, out object value) => + [typeof(float)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out float tmp); value = tmp; }, - [typeof(double)] = (ref FastBufferReader reader, out object value) => + [typeof(double)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out double tmp); value = tmp; }, - - [typeof(string)] = (ref FastBufferReader reader, out object value) => + + [typeof(string)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out string tmp); value = tmp; }, - - [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => + + [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector2 tmp); value = tmp; }, - [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => + [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector3 tmp); value = tmp; }, - [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => + [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector4 tmp); value = tmp; }, - [typeof(Color)] = (ref FastBufferReader reader, out object value) => + [typeof(Color)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Color tmp); value = tmp; }, - [typeof(Color32)] = (ref FastBufferReader reader, out object value) => + [typeof(Color32)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Color32 tmp); value = tmp; }, - [typeof(Ray)] = (ref FastBufferReader reader, out object value) => + [typeof(Ray)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Ray tmp); value = tmp; }, - [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => + [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Ray2D tmp); value = tmp; }, - [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => + [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Quaternion tmp); value = tmp; }, - - [typeof(char)] = (ref FastBufferReader reader, out object value) => + + [typeof(char)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out char tmp); value = tmp; }, - - [typeof(bool)] = (ref FastBufferReader reader, out object value) => + + [typeof(bool)] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out bool tmp); value = tmp; }, - - - [typeof(byte[])] = (ref FastBufferReader reader, out object value) => + + + [typeof(byte[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out byte[] tmp); value = tmp; }, - [typeof(sbyte[])] = (ref FastBufferReader reader, out object value) => + [typeof(sbyte[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out sbyte[] tmp); value = tmp; }, - [typeof(ushort[])] = (ref FastBufferReader reader, out object value) => + [typeof(ushort[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out ushort[] tmp); value = tmp; }, - [typeof(short[])] = (ref FastBufferReader reader, out object value) => + [typeof(short[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out short[] tmp); value = tmp; }, - [typeof(uint[])] = (ref FastBufferReader reader, out object value) => + [typeof(uint[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out uint[] tmp); value = tmp; }, - [typeof(int[])] = (ref FastBufferReader reader, out object value) => + [typeof(int[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out int[] tmp); value = tmp; }, - [typeof(ulong[])] = (ref FastBufferReader reader, out object value) => + [typeof(ulong[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out ulong[] tmp); value = tmp; }, - [typeof(long[])] = (ref FastBufferReader reader, out object value) => + [typeof(long[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out long[] tmp); value = tmp; }, - [typeof(float[])] = (ref FastBufferReader reader, out object value) => + [typeof(float[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out float[] tmp); value = tmp; }, - [typeof(double[])] = (ref FastBufferReader reader, out object value) => + [typeof(double[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out double[] tmp); value = tmp; }, - [typeof(Vector2[])] = (ref FastBufferReader reader, out object value) => + [typeof(Vector2[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector2[] tmp); value = tmp; }, - [typeof(Vector3[])] = (ref FastBufferReader reader, out object value) => + [typeof(Vector3[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector3[] tmp); value = tmp; }, - [typeof(Vector4[])] = (ref FastBufferReader reader, out object value) => + [typeof(Vector4[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Vector4[] tmp); value = tmp; }, - [typeof(Color[])] = (ref FastBufferReader reader, out object value) => + [typeof(Color[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Color[] tmp); value = tmp; }, - [typeof(Color32[])] = (ref FastBufferReader reader, out object value) => + [typeof(Color32[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Color32[] tmp); value = tmp; }, - [typeof(Ray[])] = (ref FastBufferReader reader, out object value) => + [typeof(Ray[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Ray[] tmp); value = tmp; }, - [typeof(Ray2D[])] = (ref FastBufferReader reader, out object value) => + [typeof(Ray2D[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Ray2D[] tmp); value = tmp; }, - [typeof(Quaternion[])] = (ref FastBufferReader reader, out object value) => + [typeof(Quaternion[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out Quaternion[] tmp); value = tmp; }, - - [typeof(char[])] = (ref FastBufferReader reader, out object value) => + + [typeof(char[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out char[] tmp); value = tmp; }, - - [typeof(bool[])] = (ref FastBufferReader reader, out object value) => + + [typeof(bool[])] = (ref FastBufferReader reader, out object value) => { reader.ReadValueSafe(out bool[] tmp); value = tmp; @@ -314,9 +314,9 @@ public static class SerializationTypeTable [typeof(float)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (float)value), [typeof(double)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (double)value), - + [typeof(string)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (string)value), - + [typeof(Vector2)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector2)value), [typeof(Vector3)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector3)value), [typeof(Vector4)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Vector4)value), @@ -325,12 +325,12 @@ public static class SerializationTypeTable [typeof(Ray)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray)value), [typeof(Ray2D)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Ray2D)value), [typeof(Quaternion)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (Quaternion)value), - + [typeof(char)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (char)value), - + [typeof(bool)] = (ref FastBufferWriter writer, object value) => BytePacker.WriteValuePacked(ref writer, (bool)value), }; - + public static Dictionary DeserializersPacked = new Dictionary { [typeof(byte)] = (ref FastBufferReader reader, out object value) => @@ -344,106 +344,106 @@ public static class SerializationTypeTable value = (sbyte)tmp; }, - [typeof(ushort)] = (ref FastBufferReader reader, out object value) => + [typeof(ushort)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out ushort tmp); value = tmp; }, - [typeof(short)] = (ref FastBufferReader reader, out object value) => + [typeof(short)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out short tmp); value = tmp; }, - [typeof(uint)] = (ref FastBufferReader reader, out object value) => + [typeof(uint)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out uint tmp); value = tmp; }, - [typeof(int)] = (ref FastBufferReader reader, out object value) => + [typeof(int)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out int tmp); value = tmp; }, - [typeof(ulong)] = (ref FastBufferReader reader, out object value) => + [typeof(ulong)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out ulong tmp); value = tmp; }, - [typeof(long)] = (ref FastBufferReader reader, out object value) => + [typeof(long)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out long tmp); value = tmp; }, - [typeof(float)] = (ref FastBufferReader reader, out object value) => + [typeof(float)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out float tmp); value = tmp; }, - [typeof(double)] = (ref FastBufferReader reader, out object value) => + [typeof(double)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out double tmp); value = tmp; }, - - [typeof(string)] = (ref FastBufferReader reader, out object value) => + + [typeof(string)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out string tmp); value = tmp; }, - - [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => + + [typeof(Vector2)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Vector2 tmp); value = tmp; }, - [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => + [typeof(Vector3)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Vector3 tmp); value = tmp; }, - [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => + [typeof(Vector4)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Vector4 tmp); value = tmp; }, - [typeof(Color)] = (ref FastBufferReader reader, out object value) => + [typeof(Color)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Color tmp); value = tmp; }, - [typeof(Color32)] = (ref FastBufferReader reader, out object value) => + [typeof(Color32)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Color32 tmp); value = tmp; }, - [typeof(Ray)] = (ref FastBufferReader reader, out object value) => + [typeof(Ray)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Ray tmp); value = tmp; }, - [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => + [typeof(Ray2D)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Ray2D tmp); value = tmp; }, - [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => + [typeof(Quaternion)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out Quaternion tmp); value = tmp; }, - - [typeof(char)] = (ref FastBufferReader reader, out object value) => + + [typeof(char)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out char tmp); value = tmp; }, - - [typeof(bool)] = (ref FastBufferReader reader, out object value) => + + [typeof(bool)] = (ref FastBufferReader reader, out object value) => { ByteUnpacker.ReadValuePacked(ref reader, out bool tmp); value = tmp; }, }; } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs index 877ce2ca52..62ae287a3a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -1,8 +1,8 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; namespace Unity.Multiplayer.Netcode { - public struct Ref where T: unmanaged + public struct Ref where T : unmanaged { private unsafe T* m_Value; @@ -22,4 +22,4 @@ public unsafe ref T Value get => ref *m_Value; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs index 623a87c008..bac5669ad9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs @@ -1,17 +1,17 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace Unity.Multiplayer.Netcode { - public ref struct RefArray where T: unmanaged + public ref struct RefArray where T : unmanaged { public struct RefArrayImplementation : IReadOnlyList where T : unmanaged { - internal unsafe T* m_Value; - internal int m_Length; + private unsafe T* m_Value; + private int m_Length; internal unsafe RefArrayImplementation(T* ptr, int length) { @@ -27,13 +27,13 @@ public unsafe ref T Value public struct Enumerator : IEnumerator, IEnumerator, IDisposable { - private RefArrayImplementation m_array; + private RefArrayImplementation m_Array; private int m_Index; public Enumerator(ref RefArrayImplementation array) { - this.m_array = array; - this.m_Index = -1; + m_Array = array; + m_Index = -1; } public void Dispose() @@ -42,24 +42,24 @@ public void Dispose() public bool MoveNext() { - ++this.m_Index; - return this.m_Index < this.m_array.Length; + ++m_Index; + return m_Index < m_Array.Length; } - public void Reset() => this.m_Index = -1; + public void Reset() => m_Index = -1; - public T Current => this.m_array[this.m_Index]; + public T Current => m_Array[m_Index]; - object IEnumerator.Current => (object) this.Current; + object IEnumerator.Current => (object)Current; } - public RefArrayImplementation.Enumerator GetEnumerator() => - new RefArrayImplementation.Enumerator(ref this); + public Enumerator GetEnumerator() => + new Enumerator(ref this); IEnumerator IEnumerable.GetEnumerator() => - (IEnumerator) new RefArrayImplementation.Enumerator(ref this); + (IEnumerator)new Enumerator(ref this); - IEnumerator IEnumerable.GetEnumerator() => (IEnumerator) this.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)GetEnumerator(); public int Count => m_Length; public int Length => m_Length; @@ -71,8 +71,8 @@ public unsafe T this[int index] } } - internal RefArrayImplementation m_Value; - + private RefArrayImplementation m_Value; + public unsafe RefArray(T* ptr, int length) { m_Value = new RefArrayImplementation(ptr, length); @@ -89,4 +89,4 @@ public unsafe ref RefArrayImplementation Value } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 12331d6ff5..8584391af1 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using Unity.Multiplayer.Netcode; namespace Unity.Netcode.EditorTests @@ -15,10 +15,10 @@ public void TestBitCounter64Bits() for (int i = 0; i < 64; ++i) { value = 1UL << i; - Assert.AreEqual(i+1, BitCounter.GetUsedBitCount(value)); + Assert.AreEqual(i + 1, BitCounter.GetUsedBitCount(value)); } } - + [Test] public void TestBitCounter32Bits() { @@ -29,7 +29,7 @@ public void TestBitCounter32Bits() for (int i = 0; i < 32; ++i) { value = 1U << i; - Assert.AreEqual(i+1, BitCounter.GetUsedBitCount(value)); + Assert.AreEqual(i + 1, BitCounter.GetUsedBitCount(value)); } } [Test] @@ -42,10 +42,10 @@ public void TestByteCounter64Bits() for (int i = 0; i < 64; ++i) { value = 1UL << i; - Assert.AreEqual(i/8+1, BitCounter.GetUsedByteCount(value)); + Assert.AreEqual(i / 8 + 1, BitCounter.GetUsedByteCount(value)); } } - + [Test] public void TestByteCounter32Bits() { @@ -56,8 +56,8 @@ public void TestByteCounter32Bits() for (int i = 0; i < 32; ++i) { value = 1U << i; - Assert.AreEqual(i/8+1, BitCounter.GetUsedByteCount(value)); + Assert.AreEqual(i / 8 + 1, BitCounter.GetUsedByteCount(value)); } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index c8fa6a5c4a..a63f84c48d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using NUnit.Framework; using Unity.Collections; using Unity.Multiplayer.Netcode; @@ -10,30 +10,30 @@ public class BitReaderTests [Test] public void TestReadingOneBit() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBit(true); - + bitWriter.WriteBit(true); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); - + bitWriter.WriteBit(false); bitWriter.WriteBit(false); bitWriter.WriteBit(false); bitWriter.WriteBit(true); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); bitWriter.WriteBit(false); bitWriter.WriteBit(true); } - + writer.WriteByte(0b11111111); var reader = new FastBufferReader(ref writer, Allocator.Temp); @@ -80,11 +80,11 @@ public void TestReadingOneBit() public unsafe void TestVerifyCanReadBits() { var nativeArray = new NativeArray(4, Allocator.Temp); - FastBufferReader reader = new FastBufferReader(nativeArray, Allocator.Temp); + var reader = new FastBufferReader(nativeArray, Allocator.Temp); nativeArray.Dispose(); using (reader) { - int* asInt = (int*) reader.GetUnsafePtr(); + int* asInt = (int*)reader.GetUnsafePtr(); *asInt = 0b11111111_00001010_10101011; using (var bitReader = reader.EnterBitwiseContext()) @@ -129,7 +129,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - + try { bitReader.ReadBits(out byteVal, 1); @@ -143,7 +143,7 @@ public unsafe void TestVerifyCanReadBits() throw e; } Assert.IsTrue(bitReader.VerifyCanReadBits(3)); - + try { bitReader.ReadBits(out byteVal, 4); @@ -159,19 +159,19 @@ public unsafe void TestVerifyCanReadBits() Assert.IsTrue(bitReader.VerifyCanReadBits(4)); bitReader.ReadBits(out byteVal, 3); Assert.AreEqual(0b010, byteVal); - + Assert.IsTrue(bitReader.VerifyCanReadBits(5)); - + bitReader.ReadBits(out byteVal, 5); Assert.AreEqual(0b10101, byteVal); } - + Assert.AreEqual(2, reader.Position); - + Assert.IsTrue(reader.VerifyCanRead(1)); reader.ReadByte(out byte nextByte); Assert.AreEqual(0b11111111, nextByte); - + Assert.IsTrue(reader.VerifyCanRead(1)); reader.ReadByte(out nextByte); Assert.AreEqual(0b00000000, nextByte); @@ -183,11 +183,11 @@ public unsafe void TestVerifyCanReadBits() } } } - + [Test] public void TestReadingMultipleBits() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -200,7 +200,7 @@ public void TestReadingMultipleBits() bitWriter.WriteBits(0b11111010, 4); } writer.WriteByte(0b11111111); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) @@ -230,11 +230,11 @@ public void TestReadingMultipleBits() } } } - + [Test] public void TestReadingMultipleBitsToLongs() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -246,9 +246,9 @@ public void TestReadingMultipleBitsToLongs() bitWriter.WriteBits(0b11111000UL, 4); bitWriter.WriteBits(0b11111010UL, 4); } - + writer.WriteByte(0b11111111); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { @@ -277,16 +277,16 @@ public void TestReadingMultipleBitsToLongs() } } } - + [Test] public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) { ulong value = 0xFFFFFFFFFFFFFFFF; - FastBufferReader reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong)); + var reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong)); using (reader) { - ulong* asUlong = (ulong*) reader.GetUnsafePtr(); - + ulong* asUlong = (ulong*)reader.GetUnsafePtr(); + Assert.AreEqual(value, *asUlong); var mask = 0UL; for (var i = 0; i < numBits; ++i) @@ -304,16 +304,16 @@ public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) Assert.AreEqual(value & mask, readValue); } } - + [Test] public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() { var nativeArray = new NativeArray(4, Allocator.Temp); - FastBufferReader reader = new FastBufferReader(nativeArray, Allocator.Temp); + var reader = new FastBufferReader(nativeArray, Allocator.Temp); nativeArray.Dispose(); using (reader) { - int* asInt = (int*) reader.GetUnsafePtr(); + int* asInt = (int*)reader.GetUnsafePtr(); *asInt = 0b11111111_00001010_10101011; Assert.Throws(() => @@ -323,7 +323,7 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() bitReader.ReadBit(out bool b); } }); - + Assert.Throws(() => { using (var bitReader = reader.EnterBitwiseContext()) @@ -331,7 +331,7 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() bitReader.ReadBits(out byte b, 1); } }); - + Assert.Throws(() => { using (var bitReader = reader.EnterBitwiseContext()) @@ -339,7 +339,7 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() bitReader.ReadBits(out ulong ul, 1); } }); - + Assert.AreEqual(0, reader.Position); Assert.Throws(() => @@ -365,4 +365,4 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index d794bb6cb9..2459147dd6 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using NUnit.Framework; using Unity.Collections; using Unity.Multiplayer.Netcode; @@ -10,11 +10,11 @@ public class BitWriterTests [Test] public unsafe void TestWritingOneBit() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -22,30 +22,30 @@ public unsafe void TestWritingOneBit() { bitWriter.WriteBit(true); Assert.AreEqual(0b1, *asInt); - + bitWriter.WriteBit(true); Assert.AreEqual(0b11, *asInt); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); Assert.AreEqual(0b1011, *asInt); - + bitWriter.WriteBit(false); bitWriter.WriteBit(false); bitWriter.WriteBit(false); bitWriter.WriteBit(true); Assert.AreEqual(0b10001011, *asInt); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); bitWriter.WriteBit(false); bitWriter.WriteBit(true); Assert.AreEqual(0b1010_10001011, *asInt); } - + Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10001011, *asInt); - + writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10001011, *asInt); } @@ -53,11 +53,11 @@ public unsafe void TestWritingOneBit() [Test] public unsafe void TestVerifyCanWriteBits() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); using (var bitWriter = writer.EnterBitwiseContext()) @@ -84,11 +84,11 @@ public unsafe void TestVerifyCanWriteBits() Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); bitWriter.WriteBit(true); Assert.AreEqual(0b11, *asInt); - + bitWriter.WriteBit(false); bitWriter.WriteBit(true); Assert.AreEqual(0b1011, *asInt); - + try { bitWriter.WriteBits(0b11111111, 4); @@ -101,7 +101,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - + try { bitWriter.WriteBits(0b11111111, 1); @@ -115,7 +115,7 @@ public unsafe void TestVerifyCanWriteBits() throw e; } Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); - + try { bitWriter.WriteBits(0b11111111, 4); @@ -129,24 +129,24 @@ public unsafe void TestVerifyCanWriteBits() throw e; } Assert.IsTrue(bitWriter.VerifyCanWriteBits(4)); - + bitWriter.WriteBits(0b11111010, 3); - + Assert.AreEqual(0b00101011, *asInt); Assert.IsTrue(bitWriter.VerifyCanWriteBits(5)); - + bitWriter.WriteBits(0b11110101, 5); Assert.AreEqual(0b1010_10101011, *asInt); } - + Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10101011, *asInt); - + Assert.IsTrue(writer.VerifyCanWrite(1)); writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10101011, *asInt); - + Assert.IsTrue(writer.VerifyCanWrite(1)); writer.WriteByte(0b00000000); Assert.AreEqual(0b11111111_00001010_10101011, *asInt); @@ -158,15 +158,15 @@ public unsafe void TestVerifyCanWriteBits() } } } - + [Test] public unsafe void TestWritingMultipleBits() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -174,36 +174,36 @@ public unsafe void TestWritingMultipleBits() { bitWriter.WriteBits(0b11111111, 1); Assert.AreEqual(0b1, *asInt); - + bitWriter.WriteBits(0b11111111, 1); Assert.AreEqual(0b11, *asInt); - + bitWriter.WriteBits(0b11111110, 2); Assert.AreEqual(0b1011, *asInt); - + bitWriter.WriteBits(0b11111000, 4); Assert.AreEqual(0b10001011, *asInt); - + bitWriter.WriteBits(0b11111010, 4); Assert.AreEqual(0b1010_10001011, *asInt); } - + Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10001011, *asInt); - + writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10001011, *asInt); } } - + [Test] public unsafe void TestWritingMultipleBitsFromLongs() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); Assert.IsTrue(writer.VerifyCanWrite(3)); @@ -211,36 +211,36 @@ public unsafe void TestWritingMultipleBitsFromLongs() { bitWriter.WriteBits(0b11111111UL, 1); Assert.AreEqual(0b1, *asInt); - + bitWriter.WriteBits(0b11111111UL, 1); Assert.AreEqual(0b11, *asInt); - + bitWriter.WriteBits(0b11111110UL, 2); Assert.AreEqual(0b1011, *asInt); - + bitWriter.WriteBits(0b11111000UL, 4); Assert.AreEqual(0b10001011, *asInt); - + bitWriter.WriteBits(0b11111010UL, 4); Assert.AreEqual(0b1010_10001011, *asInt); } - + Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10001011, *asInt); - + writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10001011, *asInt); } } - + [Test] public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) { - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); + var writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); using (writer) { - ulong* asUlong = (ulong*) writer.GetUnsafePtr(); - + ulong* asUlong = (ulong*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asUlong); var mask = 0UL; for (var i = 0; i < numBits; ++i) @@ -258,15 +258,15 @@ public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) Assert.AreEqual(value & mask, *asUlong); } } - + [Test] public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - int* asInt = (int*) writer.GetUnsafePtr(); - + int* asInt = (int*)writer.GetUnsafePtr(); + Assert.AreEqual(0, *asInt); Assert.Throws(() => @@ -276,7 +276,7 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() bitWriter.WriteBit(true); } }); - + Assert.Throws(() => { using (var bitWriter = writer.EnterBitwiseContext()) @@ -284,7 +284,7 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() bitWriter.WriteBit(false); } }); - + Assert.Throws(() => { using (var bitWriter = writer.EnterBitwiseContext()) @@ -292,7 +292,7 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() bitWriter.WriteBits(0b11111111, 1); } }); - + Assert.Throws(() => { using (var bitWriter = writer.EnterBitwiseContext()) @@ -300,14 +300,14 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() bitWriter.WriteBits(0b11111111UL, 1); } }); - + Assert.AreEqual(0, writer.Position); Assert.AreEqual(0, *asInt); - + writer.WriteByteSafe(0b11111111); Assert.AreEqual(0b11111111, *asInt); - - + + Assert.Throws(() => { Assert.IsTrue(writer.VerifyCanWrite(1)); @@ -330,4 +330,4 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 4c6f967c00..a1bf9b53c8 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; @@ -15,19 +15,19 @@ public class BufferSerializerTests [Test] public void TestIsReaderIsWriter() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); Assert.IsFalse(serializer.IsReader); Assert.IsTrue(serializer.IsWriter); } byte[] readBuffer = new byte[4]; - FastBufferReader reader = new FastBufferReader(readBuffer, Allocator.Temp); + var reader = new FastBufferReader(readBuffer, Allocator.Temp); using (reader) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerReader(ref reader)); Assert.IsTrue(serializer.IsReader); Assert.IsFalse(serializer.IsWriter); @@ -36,10 +36,10 @@ public void TestIsReaderIsWriter() [Test] public unsafe void TestGetUnderlyingStructs() { - FastBufferWriter writer = new FastBufferWriter(4, Allocator.Temp); + var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); ref FastBufferWriter underlyingWriter = ref serializer.GetFastBufferWriter(); fixed (FastBufferWriter* ptr = &underlyingWriter) @@ -58,10 +58,10 @@ public unsafe void TestGetUnderlyingStructs() } byte[] readBuffer = new byte[4]; - FastBufferReader reader = new FastBufferReader(readBuffer, Allocator.Temp); + var reader = new FastBufferReader(readBuffer, Allocator.Temp); using (reader) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerReader(ref reader)); ref FastBufferReader underlyingReader = ref serializer.GetFastBufferReader(); fixed (FastBufferReader* ptr = &underlyingReader) @@ -79,58 +79,58 @@ public unsafe void TestGetUnderlyingStructs() } } } - + // Not reimplementing the entire suite of all value tests for BufferSerializer since they're already tested // for the underlying structures. These are just basic tests to make sure the correct underlying functions // are being called. [Test] public void TestSerializingObjects() { - Random random = new Random(); + var random = new Random(); int value = random.Next(); object asObj = value; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref asObj, typeof(int)); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); object readValue = 0; deserializer.SerializeValue(ref readValue, typeof(int)); - + Assert.AreEqual(value, readValue); } } } - + [Test] public void TestSerializingValues() { - Random random = new Random(); + var random = new Random(); int value = random.Next(); - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); int readValue = 0; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -138,24 +138,24 @@ public void TestSerializingValues() [Test] public void TestSerializingBytes() { - Random random = new Random(); + var random = new Random(); byte value = (byte)random.Next(); - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); byte readValue = 0; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -163,24 +163,24 @@ public void TestSerializingBytes() [Test] public void TestSerializingArrays() { - Random random = new Random(); - int[] value = {random.Next(), random.Next(), random.Next()}; + var random = new Random(); + int[] value = { random.Next(), random.Next(), random.Next() }; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); int[] readValue = null; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -190,37 +190,37 @@ public void TestSerializingStrings([Values] bool oneBytChars) { string value = "I am a test string"; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref value, oneBytChars); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); string readValue = null; deserializer.SerializeValue(ref readValue, oneBytChars); - + Assert.AreEqual(value, readValue); } } } - - + + [Test] public void TestSerializingValuesPreChecked() { - Random random = new Random(); + var random = new Random(); int value = random.Next(); - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -234,10 +234,10 @@ public void TestSerializingValuesPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); serializer.SerializeValuePreChecked(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); int readValue = 0; try @@ -251,7 +251,7 @@ public void TestSerializingValuesPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -259,13 +259,13 @@ public void TestSerializingValuesPreChecked() [Test] public void TestSerializingBytesPreChecked() { - Random random = new Random(); + var random = new Random(); byte value = (byte)random.Next(); - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -279,10 +279,10 @@ public void TestSerializingBytesPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); serializer.SerializeValuePreChecked(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); byte readValue = 0; try @@ -296,7 +296,7 @@ public void TestSerializingBytesPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -304,13 +304,13 @@ public void TestSerializingBytesPreChecked() [Test] public void TestSerializingArraysPreChecked() { - Random random = new Random(); - int[] value = {random.Next(), random.Next(), random.Next()}; + var random = new Random(); + int[] value = { random.Next(), random.Next(), random.Next() }; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -324,10 +324,10 @@ public void TestSerializingArraysPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value))); serializer.SerializeValuePreChecked(ref value); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); int[] readValue = null; try @@ -341,7 +341,7 @@ public void TestSerializingArraysPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(value, readValue); } } @@ -351,10 +351,10 @@ public void TestSerializingStringsPreChecked([Values] bool oneBytChars) { string value = "I am a test string"; - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -368,10 +368,10 @@ public void TestSerializingStringsPreChecked([Values] bool oneBytChars) Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars))); serializer.SerializeValuePreChecked(ref value, oneBytChars); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); string readValue = null; try @@ -385,12 +385,12 @@ public void TestSerializingStringsPreChecked([Values] bool oneBytChars) Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(value, oneBytChars))); deserializer.SerializeValuePreChecked(ref readValue, oneBytChars); - + Assert.AreEqual(value, readValue); } } } - + private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, NetworkObject networkObject); private void RunGameObjectTest(GameObjectTestDelegate testCode) @@ -420,101 +420,101 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) } finally { - GameObject.DestroyImmediate(obj); + UnityEngine.Object.DestroyImmediate(obj); networkManager.StopServer(); } } - + [Test] public void TestSerializingGameObjects() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref obj); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); GameObject readValue = null; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(obj, readValue); } } } ); } - + [Test] public void TestSerializingNetworkObjects() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref networkObject); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); NetworkObject readValue = null; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(networkObject, readValue); } } } ); } - + [Test] public void TestSerializingNetworkBehaviours() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); serializer.SerializeValue(ref networkBehaviour); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); NetworkBehaviour readValue = null; deserializer.SerializeValue(ref readValue); - + Assert.AreEqual(networkBehaviour, readValue); } } } ); } - + [Test] public void TestSerializingGameObjectsPreChecked() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -528,10 +528,10 @@ public void TestSerializingGameObjectsPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(obj))); serializer.SerializeValuePreChecked(ref obj); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); GameObject readValue = null; try @@ -545,23 +545,23 @@ public void TestSerializingGameObjectsPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(obj, readValue); } } } ); } - + [Test] public void TestSerializingNetworkObjectsPreChecked() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -575,10 +575,10 @@ public void TestSerializingNetworkObjectsPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkObject))); serializer.SerializeValuePreChecked(ref networkObject); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); NetworkObject readValue = null; try @@ -592,23 +592,23 @@ public void TestSerializingNetworkObjectsPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(networkObject, readValue); } } } ); } - + [Test] public void TestSerializingNetworkBehavioursPreChecked() { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - FastBufferWriter writer = new FastBufferWriter(100, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - BufferSerializer serializer = + var serializer = new BufferSerializer(new BufferSerializerWriter(ref writer)); try { @@ -622,10 +622,10 @@ public void TestSerializingNetworkBehavioursPreChecked() Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkBehaviour))); serializer.SerializeValuePreChecked(ref networkBehaviour); - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - BufferSerializer deserializer = + var deserializer = new BufferSerializer(new BufferSerializerReader(ref reader)); NetworkBehaviour readValue = null; try @@ -639,7 +639,7 @@ public void TestSerializingNetworkBehavioursPreChecked() Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); - + Assert.AreEqual(networkBehaviour, readValue); } } @@ -647,4 +647,4 @@ public void TestSerializingNetworkBehavioursPreChecked() ); } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 9ae500584c..2fcc0f4e28 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reflection; using NUnit.Framework; @@ -69,21 +69,6 @@ private enum ULongEnum : ulong C } - private struct TestStruct - { - public byte a; - public short b; - public ushort c; - public int d; - public uint e; - public long f; - public ulong g; - public bool h; - public char i; - public float j; - public double k; - } - public enum WriteType { WriteDirect, @@ -201,11 +186,11 @@ private void CheckSignedPackedValue32(ref FastBufferWriter writer, int value) Assert.AreEqual(readValue, value); } } - - private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T: unmanaged + + private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T : unmanaged { - byte* asBytePointer = (byte*) &value; - byte* otherBytePointer = (byte*) &otherValue; + byte* asBytePointer = (byte*)&value; + byte* otherBytePointer = (byte*)&otherValue; for (var i = 0; i < sizeof(T); ++i) { Assert.AreEqual(asBytePointer[i], otherBytePointer[i]); @@ -214,7 +199,7 @@ private unsafe void VerifyBytewiseEquality(T value, T otherValue) where T: un private unsafe void RunTypeTest(T value) where T : unmanaged { - FastBufferWriter writer = new FastBufferWriter(sizeof(T)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(T) * 2, Allocator.Temp); using (writer) { BytePacker.WriteValuePacked(ref writer, (dynamic)value); @@ -222,7 +207,7 @@ private unsafe void RunTypeTest(T value) where T : unmanaged using (reader) { - T outVal = new T(); + var outVal = new T(); MethodInfo method; if (value is Enum) { @@ -233,12 +218,12 @@ private unsafe void RunTypeTest(T value) where T : unmanaged else { method = typeof(ByteUnpacker).GetMethod("ReadValuePacked", - new[] {typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType()}); + new[] { typeof(FastBufferReader).MakeByRefType(), typeof(T).MakeByRefType() }); } - object[] args = {reader, outVal}; + object[] args = { reader, outVal }; method.Invoke(null, args); - outVal = (T) args[1]; + outVal = (T)args[1]; Assert.AreEqual(value, outVal); VerifyBytewiseEquality(value, outVal); } @@ -247,7 +232,7 @@ private unsafe void RunTypeTest(T value) where T : unmanaged private unsafe void RunObjectTypeTest(T value) where T : unmanaged { - FastBufferWriter writer = new FastBufferWriter(sizeof(T)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(T) * 2, Allocator.Temp); using (writer) { BytePacker.WriteObjectPacked(ref writer, value); @@ -257,17 +242,17 @@ private unsafe void RunObjectTypeTest(T value) where T : unmanaged ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(T)); Assert.AreEqual(value, outVal); - VerifyBytewiseEquality(value, (T) outVal); + VerifyBytewiseEquality(value, (T)outVal); } } } - - - + + + [Test] public void TestPacking64BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -296,11 +281,11 @@ public void TestPacking64BitsUnsigned() } } } - + [Test] public void TestPacking32BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -329,11 +314,11 @@ public void TestPacking32BitsUnsigned() } } } - + [Test] public void TestPacking64BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -350,7 +335,7 @@ public void TestPacking64BitsSigned() BytePacker.WriteValuePacked(ref writer, value); CheckSignedPackedSize64(ref writer, value); CheckSignedPackedValue64(ref writer, value); - + writer.Seek(0); writer.Truncate(); BytePacker.WriteValuePacked(ref writer, -value); @@ -364,7 +349,7 @@ public void TestPacking64BitsSigned() BytePacker.WriteValuePacked(ref writer, value); CheckSignedPackedSize64(ref writer, value); CheckSignedPackedValue64(ref writer, value); - + writer.Seek(0); writer.Truncate(); BytePacker.WriteValuePacked(ref writer, -value); @@ -374,11 +359,11 @@ public void TestPacking64BitsSigned() } } } - + [Test] public void TestPacking32BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -395,7 +380,7 @@ public void TestPacking32BitsSigned() BytePacker.WriteValuePacked(ref writer, value); CheckSignedPackedSize32(ref writer, value); CheckSignedPackedValue32(ref writer, value); - + writer.Seek(0); writer.Truncate(); BytePacker.WriteValuePacked(ref writer, -value); @@ -409,7 +394,7 @@ public void TestPacking32BitsSigned() BytePacker.WriteValuePacked(ref writer, value); CheckSignedPackedSize32(ref writer, value); CheckSignedPackedValue32(ref writer, value); - + writer.Seek(0); writer.Truncate(); BytePacker.WriteValuePacked(ref writer, -value); @@ -422,7 +407,7 @@ public void TestPacking32BitsSigned() private int GetByteCount61Bits(ulong value) { - + if (value <= 0b0001_1111) { return 1; @@ -463,7 +448,7 @@ private int GetByteCount61Bits(ulong value) private int GetByteCount30Bits(uint value) { - + if (value <= 0b0011_1111) { return 1; @@ -484,7 +469,7 @@ private int GetByteCount30Bits(uint value) private int GetByteCount15Bits(ushort value) { - + if (value <= 0b0111_1111) { return 1; @@ -552,11 +537,11 @@ private short Get14BitSignedEncodedValue(ref FastBufferWriter writer) return value; } } - + [Test] public void TestBitPacking61BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -566,7 +551,7 @@ public void TestBitPacking61BitsUnsigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b111); Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); - + for (var i = 0; i < 61; ++i) { value = 1UL << i; @@ -574,9 +559,9 @@ public void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(value) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (1UL << i) | (1UL << j); @@ -584,7 +569,7 @@ public void TestBitPacking61BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(value)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(value) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get61BitEncodedValue(ref writer)); } } @@ -592,11 +577,11 @@ public void TestBitPacking61BitsUnsigned() Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1UL << 61); }); } } - + [Test] public void TestBitPacking60BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -606,7 +591,7 @@ public void TestBitPacking60BitsSigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b111); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); - + for (var i = 0; i < 61; ++i) { value = 1U << i; @@ -615,7 +600,7 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); value = -value; @@ -624,9 +609,9 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (1U << i) | (1U << j); @@ -635,7 +620,7 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); value = -value; @@ -644,7 +629,7 @@ public void TestBitPacking60BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount61Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount61Bits(zzvalue)-1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount61Bits(zzvalue) - 1, writer.ToArray()[0] & 0b111, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get60BitSignedEncodedValue(ref writer)); } } @@ -652,11 +637,11 @@ public void TestBitPacking60BitsSigned() Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1UL << 61); }); } } - + [Test] public void TestBitPacking30BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -666,7 +651,7 @@ public void TestBitPacking30BitsUnsigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b11); Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); - + for (var i = 0; i < 30; ++i) { value = 1U << i; @@ -674,9 +659,9 @@ public void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(value) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (1U << i) | (1U << j); @@ -684,7 +669,7 @@ public void TestBitPacking30BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(value)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(value) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); } } @@ -692,11 +677,11 @@ public void TestBitPacking30BitsUnsigned() Assert.Throws(() => { BytePacker.WriteValueBitPacked(ref writer, 1U << 30); }); } } - + [Test] public void TestBitPacking29BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -706,7 +691,7 @@ public void TestBitPacking29BitsSigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b11); Assert.AreEqual(value, Get30BitEncodedValue(ref writer)); - + for (var i = 0; i < 29; ++i) { value = 1 << i; @@ -715,7 +700,7 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); value = -value; @@ -724,9 +709,9 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (1 << i) | (1 << j); @@ -735,7 +720,7 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); value = -value; @@ -744,17 +729,17 @@ public void TestBitPacking29BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount30Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount30Bits(zzvalue)-1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount30Bits(zzvalue) - 1, writer.ToArray()[0] & 0b11, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get29BitSignedEncodedValue(ref writer)); } } } } - + [Test] public void TestBitPacking15BitsUnsigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -764,7 +749,7 @@ public void TestBitPacking15BitsUnsigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b1); Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); - + for (var i = 0; i < 15; ++i) { value = (ushort)(1U << i); @@ -772,9 +757,9 @@ public void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(value) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (ushort)((1U << i) | (1U << j)); @@ -782,7 +767,7 @@ public void TestBitPacking15BitsUnsigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(value), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(value)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(value) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); } } @@ -793,7 +778,7 @@ public void TestBitPacking15BitsUnsigned() [Test] public void TestBitPacking14BitsSigned() { - FastBufferWriter writer = new FastBufferWriter(9, Allocator.Temp); + var writer = new FastBufferWriter(9, Allocator.Temp); using (writer) { @@ -803,7 +788,7 @@ public void TestBitPacking14BitsSigned() Assert.AreEqual(1, writer.Position); Assert.AreEqual(0, writer.ToArray()[0] & 0b1); Assert.AreEqual(value, Get15BitEncodedValue(ref writer)); - + for (var i = 0; i < 14; ++i) { value = (short)(1 << i); @@ -812,7 +797,7 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); value = (short)-value; @@ -821,9 +806,9 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i})"); Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); - + for (var j = 0; j < 8; ++j) { value = (short)((1 << i) | (1 << j)); @@ -832,7 +817,7 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); value = (short)-value; @@ -841,13 +826,13 @@ public void TestBitPacking14BitsSigned() writer.Truncate(); BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(GetByteCount15Bits(zzvalue), writer.Position, $"Failed on {value} ({i}, {j})"); - Assert.AreEqual(GetByteCount15Bits(zzvalue)-1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); + Assert.AreEqual(GetByteCount15Bits(zzvalue) - 1, writer.ToArray()[0] & 0b1, $"Failed on {value} ({i}, {j})"); Assert.AreEqual(value, Get14BitSignedEncodedValue(ref writer)); } } } } - + [Test] public void TestPackingBasicTypes( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), @@ -862,7 +847,7 @@ public void TestPackingBasicTypes( if (testType == typeof(byte)) { - byte b = (byte) random.Next(); + byte b = (byte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(b); @@ -874,7 +859,7 @@ public void TestPackingBasicTypes( } else if (testType == typeof(sbyte)) { - sbyte sb = (sbyte) random.Next(); + sbyte sb = (sbyte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(sb); @@ -1186,13 +1171,13 @@ public void TestPackingBasicTypes( // Rays need special handling on the equality checks because the constructor normalizes direction // Which can cause slight variations in the result var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { unsafe { - FastBufferWriter writer = new FastBufferWriter(sizeof(Ray)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(Ray) * 2, Allocator.Temp); using (writer) { BytePacker.WriteValuePacked(ref writer, v); @@ -1212,7 +1197,7 @@ public void TestPackingBasicTypes( { unsafe { - FastBufferWriter writer = new FastBufferWriter(sizeof(Ray)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(Ray) * 2, Allocator.Temp); using (writer) { BytePacker.WriteObjectPacked(ref writer, v); @@ -1220,10 +1205,10 @@ public void TestPackingBasicTypes( using (reader) { ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray)); - Assert.AreEqual(v.origin, ((Ray) outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray) outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray) outVal).direction.y, 0.00001); - Assert.AreEqual(v.direction.z, ((Ray) outVal).direction.z, 0.00001); + Assert.AreEqual(v.origin, ((Ray)outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray)outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray)outVal).direction.y, 0.00001); + Assert.AreEqual(v.direction.z, ((Ray)outVal).direction.z, 0.00001); } } } @@ -1234,13 +1219,13 @@ public void TestPackingBasicTypes( // Rays need special handling on the equality checks because the constructor normalizes direction // Which can cause slight variations in the result var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), new Vector2((float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { unsafe { - FastBufferWriter writer = new FastBufferWriter(sizeof(Ray2D)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(Ray2D) * 2, Allocator.Temp); using (writer) { BytePacker.WriteValuePacked(ref writer, v); @@ -1259,7 +1244,7 @@ public void TestPackingBasicTypes( { unsafe { - FastBufferWriter writer = new FastBufferWriter(sizeof(Ray2D)*2, Allocator.Temp); + var writer = new FastBufferWriter(sizeof(Ray2D) * 2, Allocator.Temp); using (writer) { BytePacker.WriteObjectPacked(ref writer, v); @@ -1267,9 +1252,9 @@ public void TestPackingBasicTypes( using (reader) { ByteUnpacker.ReadObjectPacked(ref reader, out object outVal, typeof(Ray2D)); - Assert.AreEqual(v.origin, ((Ray2D) outVal).origin); - Assert.AreEqual(v.direction.x, ((Ray2D) outVal).direction.x, 0.00001); - Assert.AreEqual(v.direction.y, ((Ray2D) outVal).direction.y, 0.00001); + Assert.AreEqual(v.origin, ((Ray2D)outVal).origin); + Assert.AreEqual(v.direction.x, ((Ray2D)outVal).direction.x, 0.00001); + Assert.AreEqual(v.direction.y, ((Ray2D)outVal).direction.y, 0.00001); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 9cb416300c..e4c78be467 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; using Unity.Multiplayer.Netcode; using UnityEngine; using UnityEngine.SceneManagement; @@ -62,22 +61,22 @@ private enum ULongEnum : ulong B, C }; - + private struct TestStruct { - public byte a; - public short b; - public ushort c; - public int d; - public uint e; - public long f; - public ulong g; - public bool h; - public char i; - public float j; - public double k; + public byte A; + public short B; + public ushort C; + public int D; + public uint E; + public long F; + public ulong G; + public bool H; + public char I; + public float J; + public double K; } - + public enum WriteType { WriteDirect, @@ -87,58 +86,58 @@ public enum WriteType #endregion #region Common Checks - private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage="") + private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage = "") { Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); writer.WriteValue((byte)0x80); - Assert.AreEqual(writeSize+1, writer.Position, failMessage); - Assert.AreEqual(writeSize+1, writer.Length, failMessage); + Assert.AreEqual(writeSize + 1, writer.Position, failMessage); + Assert.AreEqual(writeSize + 1, writer.Length, failMessage); writer.WriteValue((byte)0xFF); - Assert.AreEqual(writeSize+2, writer.Position, failMessage); - Assert.AreEqual(writeSize+2, writer.Length, failMessage); + Assert.AreEqual(writeSize + 2, writer.Position, failMessage); + Assert.AreEqual(writeSize + 2, writer.Length, failMessage); } private void VerifyCheckBytes(ref FastBufferReader reader, int checkPosition, string failMessage = "") { reader.Seek(checkPosition); reader.VerifyCanRead(2); - + reader.ReadByte(out byte value); Assert.AreEqual(0x80, value, failMessage); reader.ReadByte(out value); Assert.AreEqual(0xFF, value, failMessage); } - + private void VerifyPositionAndLength(ref FastBufferReader reader, int length, string failMessage = "") { Assert.AreEqual(0, reader.Position, failMessage); Assert.AreEqual(length, reader.Length, failMessage); } - private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T : unmanaged { WriteCheckBytes(ref writer, writeSize, failMessage); - - FastBufferReader reader = new FastBufferReader(ref writer, Allocator.Temp); - + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + VerifyPositionAndLength(ref reader, writer.Length, failMessage); VerifyCheckBytes(ref reader, writeSize, failMessage); - + reader.Seek(0); return reader; } #endregion - + #region Generic Checks private unsafe void RunTypeTest(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); Assert.AreEqual(sizeof(T), writeSize); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -159,9 +158,9 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.AreEqual(sizeof(T), writeSize); @@ -179,13 +178,13 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged } } } - + private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.AreEqual(sizeof(T), writeSize); @@ -202,7 +201,7 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged } } - private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) where T: unmanaged + private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) where T : unmanaged { Assert.AreEqual(value.Length, compareValue.Length); @@ -215,7 +214,7 @@ private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) whe private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); @@ -242,7 +241,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); @@ -250,7 +249,7 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged writer.WriteValueSafe(valueToTest); WriteCheckBytes(ref writer, writeSize); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { @@ -268,22 +267,22 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); // Extra byte for WriteObject adding isNull flag - FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); + using (writer) { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); WriteCheckBytes(ref writer, writeSize + 1); - + var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { VerifyPositionAndLength(ref reader, writer.Length); reader.ReadObject(out object result, typeof(T[])); - VerifyArrayEquality(valueToTest, (T[]) result, 0); + VerifyArrayEquality(valueToTest, (T[])result, 0); VerifyCheckBytes(ref reader, writeSize + 1); } @@ -298,19 +297,19 @@ private TestStruct GetTestStruct() var testStruct = new TestStruct { - a = (byte) random.Next(), - b = (short) random.Next(), - c = (ushort) random.Next(), - d = (int) random.Next(), - e = (uint) random.Next(), - f = ((long) random.Next() << 32) + random.Next(), - g = ((ulong) random.Next() << 32) + (ulong) random.Next(), - h = true, - i = '\u263a', - j = (float) random.NextDouble(), - k = random.NextDouble(), + A = (byte)random.Next(), + B = (short)random.Next(), + C = (ushort)random.Next(), + D = (int)random.Next(), + E = (uint)random.Next(), + F = ((long)random.Next() << 32) + random.Next(), + G = ((ulong)random.Next() << 32) + (ulong)random.Next(), + H = true, + I = '\u263a', + J = (float)random.NextDouble(), + K = random.NextDouble(), }; - + return testStruct; } #endregion @@ -330,7 +329,7 @@ public void TestReadingBasicTypes( if (testType == typeof(byte)) { - byte b = (byte) random.Next(); + byte b = (byte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(b); @@ -346,7 +345,7 @@ public void TestReadingBasicTypes( } else if (testType == typeof(sbyte)) { - sbyte sb = (sbyte) random.Next(); + sbyte sb = (sbyte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(sb); @@ -760,7 +759,7 @@ public void TestReadingBasicTypes( else if (testType == typeof(Ray)) { var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { @@ -778,7 +777,7 @@ public void TestReadingBasicTypes( else if (testType == typeof(Ray2D)) { var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), new Vector2((float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { @@ -1479,7 +1478,7 @@ public void TestReadingStructAsObjectWithRegisteredTypeTableSerializer() { SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => { - writer.WriteValueSafe((TestStruct) obj); + writer.WriteValueSafe((TestStruct)obj); }; SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => { @@ -1524,7 +1523,7 @@ public void TestReadingStructArrayAsObjectWithRegisteredTypeTableSerializer() { SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => { - writer.WriteValueSafe((TestStruct) obj); + writer.WriteValueSafe((TestStruct)obj); }; SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => { @@ -1554,8 +1553,8 @@ public void TestReadingString() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); @@ -1583,8 +1582,8 @@ public void TestReadingStringSafe() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { writer.WriteValueSafe(valueToTest); @@ -1610,12 +1609,12 @@ public void TestReadingStringAsObject() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using (writer) { writer.WriteObject(valueToTest); - WriteCheckBytes(ref writer, serializedValueSize+1); + WriteCheckBytes(ref writer, serializedValueSize + 1); var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) @@ -1636,8 +1635,8 @@ public void TestReadingOneByteString() string valueToTest = "Hello, I am a test string!"; var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest, true); @@ -1664,8 +1663,8 @@ public void TestReadingOneByteStringSafe() string valueToTest = "Hello, I am a test string!"; var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { writer.WriteValueSafe(valueToTest, true); @@ -1688,8 +1687,8 @@ public void TestReadingOneByteStringSafe() public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) { var random = new Random(); - var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); using (writer) { Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); @@ -1718,12 +1717,12 @@ public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulo } [Test] - public void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + public void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong) - 2)] int count) { var random = new Random(); - var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); - + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); @@ -1789,7 +1788,7 @@ public void TestThrowingIfBoundsCheckingSkipped() using (writer) { Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); - var bytes = new byte[] {0, 1, 2}; + var bytes = new byte[] { 0, 1, 2 }; Assert.Throws(() => { emptyReader.ReadBytes(ref bytes, bytes.Length); }); int i = 1; Assert.Throws(() => { emptyReader.ReadValue(out int i); }); @@ -1822,14 +1821,14 @@ public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() using (writer) { writer.VerifyCanWrite(100); - var bytes = new byte[] {0, 1, 2}; + var bytes = new byte[] { 0, 1, 2 }; int i = 1; writer.WriteByte(1); writer.WriteBytes(bytes, bytes.Length); writer.WriteValue(i); writer.WriteValue(bytes); writer.WriteValue(""); - + writer.WriteByteSafe(1); writer.WriteBytesSafe(bytes, bytes.Length); writer.WriteValueSafe(i); @@ -1960,11 +1959,11 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) } finally { - GameObject.DestroyImmediate(obj); + UnityEngine.Object.DestroyImmediate(obj); networkManager.StopServer(); } } - + [Test] public void TestNetworkBehaviour() { @@ -2040,7 +2039,7 @@ public void TestGameObject() } }); } - + [Test] public void TestNetworkBehaviourSafe() { @@ -2106,7 +2105,7 @@ public void TestGameObjectSafe() } }); } - + [Test] public void TestNetworkBehaviourAsObject() { @@ -2184,10 +2183,10 @@ public void TestVerifyInternalDoesntReduceAllowedWritePoint() { reader.VerifyCanRead(25); reader.VerifyCanReadInternal(5); - Assert.AreEqual(reader.m_AllowedReadMark, 25); + Assert.AreEqual(reader.AllowedReadMark, 25); } } #endregion } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 5401a4e5f5..8fbe632431 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; using Unity.Multiplayer.Netcode; using UnityEngine; using UnityEngine.SceneManagement; @@ -62,22 +61,22 @@ private enum ULongEnum : ulong B, C }; - + private struct TestStruct { - public byte a; - public short b; - public ushort c; - public int d; - public uint e; - public long f; - public ulong g; - public bool h; - public char i; - public float j; - public double k; + public byte A; + public short B; + public ushort C; + public int D; + public uint E; + public long F; + public ulong G; + public bool H; + public char I; + public float J; + public double K; } - + public enum WriteType { WriteDirect, @@ -87,35 +86,35 @@ public enum WriteType #endregion #region Common Checks - private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage="") + private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage = "") { Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); writer.WriteValue((byte)0x80); - Assert.AreEqual(writeSize+1, writer.Position, failMessage); - Assert.AreEqual(writeSize+1, writer.Length, failMessage); + Assert.AreEqual(writeSize + 1, writer.Position, failMessage); + Assert.AreEqual(writeSize + 1, writer.Length, failMessage); writer.WriteValue((byte)0xFF); - Assert.AreEqual(writeSize+2, writer.Position, failMessage); - Assert.AreEqual(writeSize+2, writer.Length, failMessage); + Assert.AreEqual(writeSize + 2, writer.Position, failMessage); + Assert.AreEqual(writeSize + 2, writer.Length, failMessage); } private void VerifyCheckBytes(byte[] underlyingArray, int writeSize, string failMessage = "") { Assert.AreEqual(0x80, underlyingArray[writeSize], failMessage); - Assert.AreEqual(0xFF, underlyingArray[writeSize+1], failMessage); + Assert.AreEqual(0xFF, underlyingArray[writeSize + 1], failMessage); } - private unsafe void VerifyBytewiseEquality(T value, byte[] underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T: unmanaged + private unsafe void VerifyBytewiseEquality(T value, byte[] underlyingArray, int valueOffset, int bufferOffset, int size, string failMessage = "") where T : unmanaged { - byte* asBytePointer = (byte*) &value; + byte* asBytePointer = (byte*)&value; for (var i = 0; i < size; ++i) { - Assert.AreEqual(asBytePointer[i+valueOffset], underlyingArray[i+bufferOffset], failMessage); + Assert.AreEqual(asBytePointer[i + valueOffset], underlyingArray[i + bufferOffset], failMessage); } } - private unsafe void VerifyTypedEquality(T value, byte* unsafePtr) where T: unmanaged + private unsafe void VerifyTypedEquality(T value, byte* unsafePtr) where T : unmanaged { - T* checkValue = (T*) unsafePtr; + var checkValue = (T*)unsafePtr; Assert.AreEqual(value, *checkValue); } @@ -125,15 +124,15 @@ private void VerifyPositionAndLength(ref FastBufferWriter writer, int position, Assert.AreEqual(position, writer.Length, failMessage); } - private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T: unmanaged + private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, int writeSize, string failMessage = "") where T : unmanaged { - + VerifyPositionAndLength(ref writer, writeSize, failMessage); - + WriteCheckBytes(ref writer, writeSize, failMessage); - + var underlyingArray = writer.ToArray(); - + VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, writeSize, failMessage); VerifyCheckBytes(underlyingArray, writeSize, failMessage); @@ -141,7 +140,7 @@ private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, VerifyTypedEquality(valueToTest, writer.GetUnsafePtr()); } #endregion - + #region Generic Checks private unsafe void RunTypeTest(T valueToTest) where T : unmanaged { @@ -149,10 +148,10 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged var alternateWriteSize = FastBufferWriter.GetWriteSize(); Assert.AreEqual(sizeof(T), writeSize); Assert.AreEqual(sizeof(T), alternateWriteSize); - - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); @@ -167,9 +166,9 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.AreEqual(sizeof(T), writeSize); @@ -180,13 +179,13 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged CommonChecks(ref writer, valueToTest, writeSize, failMessage); } } - + private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); - using(writer) + using (writer) { Assert.AreEqual(sizeof(T), writeSize); @@ -197,14 +196,14 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged } } - private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T: unmanaged + private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T : unmanaged { int* sizeValue = (int*)(unsafePtr + offset); Assert.AreEqual(value.Length, *sizeValue); fixed (T* asTPointer = value) { - T* underlyingTArray = (T*) (unsafePtr + sizeof(int) + offset); + var underlyingTArray = (T*)(unsafePtr + sizeof(int) + offset); for (var i = 0; i < value.Length; ++i) { Assert.AreEqual(asTPointer[i], underlyingTArray[i]); @@ -215,7 +214,7 @@ private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offse private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { @@ -237,7 +236,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); + var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { @@ -259,10 +258,10 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); // Extra byte for WriteObject adding isNull flag - FastBufferWriter writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(writeSize + 3, Allocator.Temp); + using (writer) { - + Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); writer.WriteObject(valueToTest); @@ -286,19 +285,19 @@ private TestStruct GetTestStruct() var testStruct = new TestStruct { - a = (byte) random.Next(), - b = (short) random.Next(), - c = (ushort) random.Next(), - d = (int) random.Next(), - e = (uint) random.Next(), - f = ((long) random.Next() << 32) + random.Next(), - g = ((ulong) random.Next() << 32) + (ulong) random.Next(), - h = true, - i = '\u263a', - j = (float) random.NextDouble(), - k = random.NextDouble(), + A = (byte)random.Next(), + B = (short)random.Next(), + C = (ushort)random.Next(), + D = (int)random.Next(), + E = (uint)random.Next(), + F = ((long)random.Next() << 32) + random.Next(), + G = ((ulong)random.Next() << 32) + (ulong)random.Next(), + H = true, + I = '\u263a', + J = (float)random.NextDouble(), + K = random.NextDouble(), }; - + return testStruct; } #endregion @@ -318,7 +317,7 @@ public void TestWritingBasicTypes( if (testType == typeof(byte)) { - byte b = (byte) random.Next(); + byte b = (byte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(b); @@ -334,7 +333,7 @@ public void TestWritingBasicTypes( } else if (testType == typeof(sbyte)) { - sbyte sb = (sbyte) random.Next(); + sbyte sb = (sbyte)random.Next(); if (writeType == WriteType.WriteDirect) { RunTypeTest(sb); @@ -748,7 +747,7 @@ public void TestWritingBasicTypes( else if (testType == typeof(Ray)) { var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { @@ -766,7 +765,7 @@ public void TestWritingBasicTypes( else if (testType == typeof(Ray2D)) { var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), new Vector2((float)random.NextDouble(), (float)random.NextDouble())); if (writeType == WriteType.WriteDirect) { @@ -1467,7 +1466,7 @@ public void TestWritingStructAsObjectWithRegisteredTypeTableSerializer() { SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => { - writer.WriteValueSafe((TestStruct) obj); + writer.WriteValueSafe((TestStruct)obj); }; try { @@ -1506,7 +1505,7 @@ public void TestWritingStructArrayAsObjectWithRegisteredTypeTableSerializer() { SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => { - writer.WriteValueSafe((TestStruct) obj); + writer.WriteValueSafe((TestStruct)obj); }; try { @@ -1530,8 +1529,8 @@ public unsafe void TestWritingString() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); @@ -1540,12 +1539,12 @@ public unsafe void TestWritingString() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) writer.GetUnsafePtr(); + int* sizeValue = (int*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) (writer.GetUnsafePtr() + sizeof(int)); + char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); @@ -1564,8 +1563,8 @@ public unsafe void TestWritingStringSafe() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { writer.WriteValueSafe(valueToTest); @@ -1573,12 +1572,12 @@ public unsafe void TestWritingStringSafe() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) writer.GetUnsafePtr(); + int* sizeValue = (int*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) ((byte*) writer.GetUnsafePtr() + sizeof(int)); + char* underlyingCharArray = (char*)((byte*)writer.GetUnsafePtr() + sizeof(int)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); @@ -1597,8 +1596,8 @@ public unsafe void TestWritingStringAsObject() var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using (writer) { writer.WriteObject(valueToTest); @@ -1606,12 +1605,12 @@ public unsafe void TestWritingStringAsObject() VerifyPositionAndLength(ref writer, serializedValueSize + sizeof(byte)); WriteCheckBytes(ref writer, serializedValueSize + sizeof(byte)); - int* sizeValue = (int*) (writer.GetUnsafePtr() + sizeof(byte)); + int* sizeValue = (int*)(writer.GetUnsafePtr() + sizeof(byte)); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - char* underlyingCharArray = (char*) (writer.GetUnsafePtr() + sizeof(int) + sizeof(byte)); + char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int) + sizeof(byte)); for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); @@ -1629,8 +1628,8 @@ public unsafe void TestWritingOneByteString() string valueToTest = "Hello, I am a test string!"; var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); @@ -1639,7 +1638,7 @@ public unsafe void TestWritingOneByteString() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) writer.GetUnsafePtr(); + int* sizeValue = (int*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) @@ -1647,7 +1646,7 @@ public unsafe void TestWritingOneByteString() byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); for (var i = 0; i < valueToTest.Length; ++i) { - Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); + Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); } } @@ -1662,8 +1661,8 @@ public unsafe void TestWritingOneByteStringSafe() string valueToTest = "Hello, I am a test string!"; var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - FastBufferWriter writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using(writer) + var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); + using (writer) { writer.WriteValueSafe(valueToTest, true); @@ -1671,7 +1670,7 @@ public unsafe void TestWritingOneByteStringSafe() VerifyPositionAndLength(ref writer, serializedValueSize); WriteCheckBytes(ref writer, serializedValueSize); - int* sizeValue = (int*) writer.GetUnsafePtr(); + int* sizeValue = (int*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) @@ -1679,7 +1678,7 @@ public unsafe void TestWritingOneByteStringSafe() byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); for (var i = 0; i < valueToTest.Length; ++i) { - Assert.AreEqual((byte) asCharPointer[i], underlyingByteArray[i]); + Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); } } @@ -1692,8 +1691,8 @@ public unsafe void TestWritingOneByteStringSafe() public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) { var random = new Random(); - var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); using (writer) { @@ -1713,18 +1712,18 @@ public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulo mask = (mask << 8) | 0b11111111; } - ulong* checkValue = (ulong*) writer.GetUnsafePtr(); + ulong* checkValue = (ulong*)writer.GetUnsafePtr(); Assert.AreEqual(valueToTest & mask, *checkValue & mask, failMessage); } } [Test] - public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong)-2)] int count) + public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong) - 2)] int count) { var random = new Random(); - var valueToTest = ((ulong) random.Next() << 32) + (ulong)random.Next(); - FastBufferWriter writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); - + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) { @@ -1744,7 +1743,7 @@ public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, mask = (mask << 8) | 0b11111111; } - ulong* checkValue = (ulong*) writer.GetUnsafePtr(); + ulong* checkValue = (ulong*)writer.GetUnsafePtr(); Assert.AreEqual((valueToTest >> 16) & mask, *checkValue & mask); } } @@ -1762,7 +1761,7 @@ public void TestToArray() writer.WriteValue(testStruct); var array = writer.ToArray(); var underlyingArray = writer.ToArray(); - for(var i = 0; i < array.Length; ++i) + for (var i = 0; i < array.Length; ++i) { Assert.AreEqual(array[i], underlyingArray[i]); } @@ -1777,7 +1776,7 @@ public void TestThrowingIfBoundsCheckingSkipped() using (writer) { Assert.Throws(() => { writer.WriteByte(1); }); - var bytes = new byte[] {0, 1, 2}; + var bytes = new byte[] { 0, 1, 2 }; Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); int i = 1; Assert.Throws(() => { writer.WriteValue(i); }); @@ -1801,7 +1800,7 @@ public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() using (writer) { writer.VerifyCanWrite(100); - var bytes = new byte[] {0, 1, 2}; + var bytes = new byte[] { 0, 1, 2 }; int i = 1; using (var context = writer.EnterBitwiseContext()) { @@ -1810,7 +1809,7 @@ public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() Assert.Throws(() => { writer.WriteValue(i); }); Assert.Throws(() => { writer.WriteValue(bytes); }); Assert.Throws(() => { writer.WriteValue(""); }); - + Assert.Throws(() => { writer.WriteByteSafe(1); }); Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); Assert.Throws(() => { writer.WriteValueSafe(i); }); @@ -1822,7 +1821,7 @@ public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() writer.WriteValue(i); writer.WriteValue(bytes); writer.WriteValue(""); - + writer.WriteByteSafe(1); writer.WriteBytesSafe(bytes, bytes.Length); writer.WriteValueSafe(i); @@ -1880,7 +1879,7 @@ public void TestSeeking() Assert.AreEqual(writer.Position, 4); Assert.AreEqual(writer.Length, 10); - var expected = new byte[] {1, 3, 2, 5, 4, 0}; + var expected = new byte[] { 1, 3, 2, 5, 4, 0 }; var underlyingArray = writer.ToArray(); for (var i = 0; i < expected.Length; ++i) { @@ -1902,7 +1901,7 @@ public void TestTruncate() writer.Seek(5); Assert.AreEqual(writer.Position, 5); Assert.AreEqual(writer.Length, 10); - + writer.Truncate(8); Assert.AreEqual(writer.Position, 5); Assert.AreEqual(writer.Length, 8); @@ -1927,7 +1926,7 @@ public void TestGrowth() var writer = new FastBufferWriter(150, Allocator.Temp); var growingWriter = new FastBufferWriter(150, Allocator.Temp, 500); Assert.AreEqual(150, writer.Capacity); - using (writer) + using (writer) using (growingWriter) { var testStruct = GetTestStruct(); @@ -1935,7 +1934,7 @@ public void TestGrowth() writer.WriteValue(testStruct); growingWriter.VerifyCanWriteValue(testStruct); growingWriter.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); @@ -1944,10 +1943,10 @@ public void TestGrowth() // First writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.VerifyCanWriteValue(testStruct)); Assert.Throws(() => writer.WriteValue(testStruct)); - + // Second writer is allowed to grow Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); - + // First writer shouldn't have grown Assert.AreEqual(150, writer.Capacity); Assert.AreEqual(preGrowthLength, writer.ToArray().Length); @@ -1955,28 +1954,28 @@ public void TestGrowth() Assert.AreEqual(300, growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); growingWriter.WriteValue(testStruct); - + // Write right up to the very end of the buffer, verify it doesn't grow growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); Assert.AreEqual(300, growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); growingWriter.WriteValue(testStruct); - + // Go to the end of the buffer and grow again growingWriter.Seek(300); Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); growingWriter.WriteValue(testStruct); - + // Second growth caps it at maxSize Assert.AreEqual(500, growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - + VerifyBytewiseEquality(testStruct, writer.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); // Verify the growth properly copied the existing data VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150-FastBufferWriter.GetWriteSize(testStruct)+1, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300-FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150 - FastBufferWriter.GetWriteSize(testStruct) + 1, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300 - FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); } } @@ -2009,11 +2008,11 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) } finally { - GameObject.DestroyImmediate(obj); + UnityEngine.Object.DestroyImmediate(obj); networkManager.StopHost(); } } - + [Test] public void TestNetworkBehaviour() { @@ -2069,7 +2068,7 @@ public void TestGameObject() } }); } - + [Test] public void TestNetworkBehaviourSafe() { @@ -2119,7 +2118,7 @@ public void TestGameObjectSafe() } }); } - + [Test] public void TestNetworkBehaviourAsObject() { @@ -2135,7 +2134,7 @@ public void TestNetworkBehaviourAsObject() Assert.AreEqual(0, writer.ToArray()[0]); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong)+1, sizeof(ushort)); + sizeof(ulong) + 1, sizeof(ushort)); } }); } @@ -2184,9 +2183,9 @@ public void TestVerifyInternalDoesntReduceAllowedWritePoint() { writer.VerifyCanWrite(25); writer.VerifyCanWriteInternal(5); - Assert.AreEqual(writer.m_AllowedWriteMark, 25); + Assert.AreEqual(writer.AllowedWriteMark, 25); } } #endregion } -} \ No newline at end of file +} diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs index 8bfae869b2..508d5a341b 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs @@ -11,7 +11,7 @@ namespace TestProject.RuntimeTests { - public class NetworkSceneManagerTests: BaseMultiInstanceTest + public class NetworkSceneManagerTests : BaseMultiInstanceTest { protected override int NbClients => 9; @@ -224,15 +224,15 @@ private void SetClientWaitDone(List clients) private bool ContainsAllClients(List clients) { // First, make sure we have the expected client count - if(clients.Count != m_ShouldWaitList.Count) + if (clients.Count != m_ShouldWaitList.Count) { return false; } // Next, make sure we have all client identifiers - foreach(var sceneTestInfo in m_ShouldWaitList) + foreach (var sceneTestInfo in m_ShouldWaitList) { - if(!clients.Contains(sceneTestInfo.ClientId)) + if (!clients.Contains(sceneTestInfo.ClientId)) { return false; } @@ -248,12 +248,12 @@ private bool ContainsAllClients(List clients) /// private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) { - switch(sceneEvent.SceneEventType) + switch (sceneEvent.SceneEventType) { case SceneEventData.SceneEventTypes.S2C_Load: case SceneEventData.SceneEventTypes.S2C_Unload: { - Assert.AreEqual(sceneEvent.SceneName,m_CurrentSceneName); + Assert.AreEqual(sceneEvent.SceneName, m_CurrentSceneName); Assert.IsTrue(ContainsClient(sceneEvent.ClientId)); Assert.IsNotNull(sceneEvent.AsyncOperation); break; From 58db1bd514a04490ae165fb3c041c2765c5629c9 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:59:12 -0500 Subject: [PATCH 15/29] Removed accidentally added files. --- testproject/Assets/StreamingAssets/BuildInfo.json | 1 - testproject/Assets/StreamingAssets/BuildInfo.json.meta | 7 ------- 2 files changed, 8 deletions(-) delete mode 100644 testproject/Assets/StreamingAssets/BuildInfo.json delete mode 100644 testproject/Assets/StreamingAssets/BuildInfo.json.meta diff --git a/testproject/Assets/StreamingAssets/BuildInfo.json b/testproject/Assets/StreamingAssets/BuildInfo.json deleted file mode 100644 index 14f9fcbcd2..0000000000 --- a/testproject/Assets/StreamingAssets/BuildInfo.json +++ /dev/null @@ -1 +0,0 @@ -{"BuildPath":"C:\\Users\\jaedyn.draper\\repos\\mlapi\\testproject\\Builds\\MultiprocessTests\\MultiprocessTestPlayer","IsDebug":false} \ No newline at end of file diff --git a/testproject/Assets/StreamingAssets/BuildInfo.json.meta b/testproject/Assets/StreamingAssets/BuildInfo.json.meta deleted file mode 100644 index 5af63d794e..0000000000 --- a/testproject/Assets/StreamingAssets/BuildInfo.json.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 1c16680acd9943944a6d4ae05644bfd5 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From c8273398e9e37024fbf5aeafbc5b505feb86de52 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 24 Aug 2021 16:59:46 -0500 Subject: [PATCH 16/29] Added BuildInfo.json to the .gitignore so I stop accidentally checking it in. --- testproject/Assets/StreamingAssets/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 testproject/Assets/StreamingAssets/.gitignore diff --git a/testproject/Assets/StreamingAssets/.gitignore b/testproject/Assets/StreamingAssets/.gitignore new file mode 100644 index 0000000000..3b64b774a1 --- /dev/null +++ b/testproject/Assets/StreamingAssets/.gitignore @@ -0,0 +1,2 @@ +/BuildInfo.json +/BuildInfo.json.meta From f8bfb2f80e71d211ff07d5872d0c03d6ba5f9b02 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 25 Aug 2021 20:21:12 -0500 Subject: [PATCH 17/29] Addressed most of the review feedback. Still need to do a little more restructuring of some of the other tests. --- .../Runtime/Serialization/BitReader.cs | 24 +- .../Runtime/Serialization/BitWriter.cs | 38 +- .../Serialization/BufferSerializerReader.cs | 2 +- .../Serialization/BufferSerializerWriter.cs | 2 +- .../Runtime/Serialization/BytePacker.cs | 14 +- .../Runtime/Serialization/ByteUnpacker.cs | 10 +- .../Runtime/Serialization/FastBufferReader.cs | 334 +-- .../FastBufferReaderExtensions.cs | 287 +++ .../FastBufferReaderExtensions.cs.meta | 11 + .../Runtime/Serialization/FastBufferWriter.cs | 341 +-- .../FastBufferWriterExtensions.cs | 298 +++ .../FastBufferWriterExtensions.cs.meta | 11 + .../IBufferSerializerImplementation.cs | 44 +- .../BaseFastBufferReaderWriterTest.cs | 669 +++++ .../BaseFastBufferReaderWriterTest.cs.meta | 3 + .../Editor/Serialization/BitCounterTests.cs | 75 +- .../Editor/Serialization/BitReaderTests.cs | 44 +- .../Editor/Serialization/BitWriterTests.cs | 38 +- .../Serialization/BufferSerializerTests.cs | 12 +- .../Editor/Serialization/BytePackerTests.cs | 20 +- .../Serialization/FastBufferReaderTests.cs | 2225 ++++------------ .../Serialization/FastBufferWriterTests.cs | 2275 +++++------------ testproject/.gitignore | 2 + .../Tests/Runtime/NetworkSceneManagerTests.cs | 12 +- 24 files changed, 2695 insertions(+), 4096 deletions(-) create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs create mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs create mode 100644 com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 01ddbc6b91..3540ea73fd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -65,7 +65,7 @@ public void Dispose() /// /// Number of bits you want to read, in total /// True if you can read, false if that would exceed buffer bounds - public bool VerifyCanReadBits(int bitCount) + public bool TryBeginReadBits(uint bitCount) { var newBitPosition = m_BitPosition + bitCount; var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; @@ -80,7 +80,7 @@ public bool VerifyCanReadBits(int bitCount) return false; } #if DEVELOPMENT_BUILD || UNITY_EDITOR - m_AllowedBitwiseReadMark = newBitPosition; + m_AllowedBitwiseReadMark = (int)newBitPosition; #endif return true; } @@ -90,7 +90,7 @@ public bool VerifyCanReadBits(int bitCount) /// /// Value to store bits into. /// Amount of bits to read - public unsafe void ReadBits(out ulong value, int bitCount) + public unsafe void ReadBits(out ulong value, uint bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (bitCount > 64) @@ -103,15 +103,15 @@ public unsafe void ReadBits(out ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!"); } - int checkPos = (m_BitPosition + bitCount); + int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); } #endif ulong val = 0; - int wholeBytes = bitCount / k_BitsPerByte; + int wholeBytes = (int)bitCount / k_BitsPerByte; byte* asBytes = (byte*)&val; if (BitAligned) { @@ -128,7 +128,7 @@ public unsafe void ReadBits(out ulong value, int bitCount) } } - val |= (ulong)ReadByteBits(bitCount & 7) << (bitCount & ~7); + val |= (ulong)ReadByteBits((int)bitCount & 7) << ((int)bitCount & ~7); value = val; } @@ -137,16 +137,16 @@ public unsafe void ReadBits(out ulong value, int bitCount) /// /// Value to store bits into. /// Amount of bits to read. - public void ReadBits(out byte value, int bitCount) + public void ReadBits(out byte value, uint bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_BitPosition + bitCount); + int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); } #endif - value = ReadByteBits(bitCount); + value = ReadByteBits((int)bitCount); } /// @@ -160,7 +160,7 @@ public unsafe void ReadBit(out bool bit) int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanReadBits()"); + throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 903b279f76..3183f6ce33 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -55,15 +55,20 @@ public void Dispose() } /// - /// Verifies the requested bit count can be written to the buffer. - /// This exists as a separate method to allow multiple bit writes to be bounds checked with a single call. - /// If it returns false, you may not write, and in editor and development builds, attempting to do so will - /// throw an exception. In release builds, attempting to do so will write to random memory addresses and cause - /// Bad Things(TM). + /// Allows faster serialization by batching bounds checking. + /// When you know you will be writing multiple fields back-to-back and you know the total size, + /// you can call TryBeginWriteBits() once on the total size, and then follow it with calls to + /// WriteBit() or WriteBits(). + /// + /// Bitwise write operations will throw OverflowException in editor and development builds if you + /// go past the point you've marked using TryBeginWriteBits(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following + /// operations in release builds. Instead, attempting to write past the marked position in release builds + /// will write to random memory and cause undefined behavior, likely including instability and crashes. /// /// Number of bits you want to write, in total /// True if you can write, false if that would exceed buffer bounds - public unsafe bool VerifyCanWriteBits(int bitCount) + public unsafe bool TryBeginWriteBits(int bitCount) { var newBitPosition = m_BitPosition + bitCount; var totalBytesWrittenInBitwiseContext = newBitPosition >> 3; @@ -96,7 +101,7 @@ public unsafe bool VerifyCanWriteBits(int bitCount) /// /// Value to get bits from. /// Amount of bits to write - public unsafe void WriteBits(ulong value, int bitCount) + public unsafe void WriteBits(ulong value, uint bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (bitCount > 64) @@ -104,19 +109,14 @@ public unsafe void WriteBits(ulong value, int bitCount) throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write more than 64 bits from a 64-bit value!"); } - if (bitCount < 0) - { - throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot write fewer than 0 bits!"); - } - - int checkPos = (m_BitPosition + bitCount); + int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); } #endif - int wholeBytes = bitCount / k_BitsPerByte; + int wholeBytes = (int)bitCount / k_BitsPerByte; byte* asBytes = (byte*)&value; if (BitAligned) { @@ -144,13 +144,13 @@ public unsafe void WriteBits(ulong value, int bitCount) /// /// Value to get bits from. /// Amount of bits to write. - public void WriteBits(byte value, int bitCount) + public void WriteBits(byte value, uint bitCount) { #if DEVELOPMENT_BUILD || UNITY_EDITOR - int checkPos = (m_BitPosition + bitCount); + int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); } #endif @@ -171,7 +171,7 @@ public unsafe void WriteBit(bool bit) int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.VerifyCanWriteBits()"); + throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index 146adc6cff..495212dfa9 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -78,7 +78,7 @@ public void SerializeNetworkSerializable(ref T value) where T : INetworkSeria public bool PreCheck(int amount) { - return m_Reader.Value.VerifyCanRead(amount); + return m_Reader.Value.TryBeginRead(amount); } public void SerializeValuePreChecked(ref GameObject value) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index 8349ea0d3c..f6f63b95c2 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -78,7 +78,7 @@ public void SerializeNetworkSerializable(ref T value) where T : INetworkSeria public bool PreCheck(int amount) { - return m_Writer.Value.VerifyCanWrite(amount); + return m_Writer.Value.TryBeginWrite(amount); } public void SerializeValuePreChecked(ref GameObject value) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index c0e8672179..39f031d220 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -98,7 +98,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value, } if (value is GameObject) { - var networkObject = ((GameObject)value).GetComponent(); + ((GameObject)value).TryGetComponent(out var networkObject); if (networkObject == null) { throw new ArgumentException($"{nameof(NetworkWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {((GameObject)value).name}"); @@ -453,7 +453,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value if (value <= 0b0111_1111) { - if (!writer.VerifyCanWriteInternal(1)) + if (!writer.TryBeginWriteInternal(1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -461,7 +461,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ushort value return; } - if (!writer.VerifyCanWriteInternal(2)) + if (!writer.TryBeginWriteInternal(2)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -501,7 +501,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, uint value) #endif value <<= 2; var numBytes = BitCounter.GetUsedByteCount(value); - if (!writer.VerifyCanWriteInternal(numBytes)) + if (!writer.TryBeginWriteInternal(numBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -541,7 +541,7 @@ public static void WriteValueBitPacked(ref FastBufferWriter writer, ulong value) #endif value <<= 3; var numBytes = BitCounter.GetUsedByteCount(value); - if (!writer.VerifyCanWriteInternal(numBytes)) + if (!writer.TryBeginWriteInternal(numBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -566,7 +566,7 @@ private static void WriteUInt64Packed(ref FastBufferWriter writer, ulong value) } var writeBytes = BitCounter.GetUsedByteCount(value); - if (!writer.VerifyCanWriteInternal(writeBytes + 1)) + if (!writer.TryBeginWriteInternal(writeBytes + 1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -592,7 +592,7 @@ private static void WriteUInt32Packed(ref FastBufferWriter writer, uint value) } var writeBytes = BitCounter.GetUsedByteCount(value); - if (!writer.VerifyCanWriteInternal(writeBytes + 1)) + if (!writer.TryBeginWriteInternal(writeBytes + 1)) { throw new OverflowException("Writing past the end of the buffer"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index bc5edc3baf..b83bca1062 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -466,7 +466,7 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out us byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b1) + 1; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -511,7 +511,7 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ui byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b11) + 1; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -565,7 +565,7 @@ public static unsafe void ReadValueBitPacked(ref FastBufferReader reader, out ul byte* ptr = ((byte*)&returnValue); byte* data = reader.GetUnsafePtrAtCurrentPosition(); int numBytes = (data[0] & 0b111) + 1; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -649,7 +649,7 @@ private static void ReadUInt64Packed(ref FastBufferReader reader, out ulong valu } var numBytes = firstByte - 247; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -673,7 +673,7 @@ private static void ReadUInt32Packed(ref FastBufferReader reader, out uint value } var numBytes = firstByte - 247; - if (!reader.VerifyCanReadInternal(numBytes)) + if (!reader.TryBeginReadInternal(numBytes)) { throw new OverflowException("Reading past the end of the buffer"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 34336fed81..31493bc2e6 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -192,7 +192,7 @@ internal void MarkBytesRead(int amount) } if (PositionInternal + amount > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif PositionInternal += amount; @@ -215,19 +215,19 @@ public BitReader EnterBitwiseContext() /// /// Allows faster serialization by batching bounds checking. /// When you know you will be reading multiple fields back-to-back and you know the total size, - /// you can call VerifyCanRead() once on the total size, and then follow it with calls to + /// you can call TryBeginRead() once on the total size, and then follow it with calls to /// ReadValue() instead of ReadValueSafe() for faster serialization. /// /// Unsafe read operations will throw OverflowException in editor and development builds if you - /// go past the point you've marked using VerifyCanRead(). In release builds, OverflowException will not be thrown - /// for performance reasons, since the point of using VerifyCanRead is to avoid bounds checking in the following + /// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following /// operations in release builds. /// /// Amount of bytes to read /// True if the read is allowed, false otherwise /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanRead(int bytes) + public bool TryBeginRead(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -249,19 +249,19 @@ public bool VerifyCanRead(int bytes) /// /// Allows faster serialization by batching bounds checking. /// When you know you will be reading multiple fields back-to-back and you know the total size, - /// you can call VerifyCanRead() once on the total size, and then follow it with calls to + /// you can call TryBeginRead() once on the total size, and then follow it with calls to /// ReadValue() instead of ReadValueSafe() for faster serialization. /// /// Unsafe read operations will throw OverflowException in editor and development builds if you - /// go past the point you've marked using VerifyCanRead(). In release builds, OverflowException will not be thrown - /// for performance reasons, since the point of using VerifyCanRead is to avoid bounds checking in the following + /// go past the point you've marked using TryBeginRead(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginRead is to avoid bounds checking in the following /// operations in release builds. /// /// The value you want to read /// True if the read is allowed, false otherwise /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged + public unsafe bool TryBeginReadValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -282,14 +282,14 @@ public unsafe bool VerifyCanReadValue(in T value) where T : unmanaged } /// - /// Internal version of VerifyCanRead. - /// Differs from VerifyCanRead only in that it won't ever move the AllowedReadMark backward. + /// Internal version of TryBeginRead. + /// Differs from TryBeginRead only in that it won't ever move the AllowedReadMark backward. /// /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool VerifyCanReadInternal(int bytes) + internal bool TryBeginReadInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -347,282 +347,6 @@ public unsafe byte[] ToArray() return BufferPointer + PositionInternal; } - /// - /// Reads a boxed object in a standard format - /// Named differently from other ReadValue methods to avoid accidental boxing - /// - /// The object to read - /// The type to be read - /// - /// If true, reads a byte indicating whether or not the object is null. - /// Should match the way the object was written. - /// - public void ReadObject(out object value, Type type, bool isNullable = false) - { - if (isNullable || type.IsNullable()) - { - ReadValueSafe(out bool isNull); - - if (isNull) - { - value = null; - return; - } - } - - var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); - if (hasDeserializer) - { - deserializer(ref this, out value); - return; - } - - if (type.IsArray && type.HasElementType) - { - ReadValueSafe(out int length); - - var arr = Array.CreateInstance(type.GetElementType(), length); - - for (int i = 0; i < length; i++) - { - ReadObject(out object item, type.GetElementType()); - arr.SetValue(item, i); - } - - value = arr; - return; - } - - if (type.IsEnum) - { - switch (Type.GetTypeCode(type)) - { - case TypeCode.Boolean: - ReadValueSafe(out byte boolVal); - value = Enum.ToObject(type, boolVal != 0); - return; - case TypeCode.Char: - ReadValueSafe(out char charVal); - value = Enum.ToObject(type, charVal); - return; - case TypeCode.SByte: - ReadValueSafe(out sbyte sbyteVal); - value = Enum.ToObject(type, sbyteVal); - return; - case TypeCode.Byte: - ReadValueSafe(out byte byteVal); - value = Enum.ToObject(type, byteVal); - return; - case TypeCode.Int16: - ReadValueSafe(out short shortVal); - value = Enum.ToObject(type, shortVal); - return; - case TypeCode.UInt16: - ReadValueSafe(out ushort ushortVal); - value = Enum.ToObject(type, ushortVal); - return; - case TypeCode.Int32: - ReadValueSafe(out int intVal); - value = Enum.ToObject(type, intVal); - return; - case TypeCode.UInt32: - ReadValueSafe(out uint uintVal); - value = Enum.ToObject(type, uintVal); - return; - case TypeCode.Int64: - ReadValueSafe(out long longVal); - value = Enum.ToObject(type, longVal); - return; - case TypeCode.UInt64: - ReadValueSafe(out ulong ulongVal); - value = Enum.ToObject(type, ulongVal); - return; - } - } - - if (type == typeof(GameObject)) - { - ReadValueSafe(out GameObject go); - value = go; - return; - } - - if (type == typeof(NetworkObject)) - { - ReadValueSafe(out NetworkObject no); - value = no; - return; - } - - if (typeof(NetworkBehaviour).IsAssignableFrom(type)) - { - ReadValueSafe(out NetworkBehaviour nb); - value = nb; - return; - } - /*if (value is INetworkSerializable) - { - //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - }*/ - - throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); - } - - /// - /// Read an INetworkSerializable - /// - /// INetworkSerializable instance - /// - /// - public void ReadNetworkSerializable(out T value) where T : INetworkSerializable - { - throw new NotImplementedException(); - } - - /// - /// Read a GameObject - /// - /// value to read - public void ReadValue(out GameObject value) - { - ReadValue(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.gameObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a GameObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. - /// - /// value to read - public void ReadValueSafe(out GameObject value) - { - ReadValueSafe(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.gameObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkObject - /// - /// value to read - public void ReadValue(out NetworkObject value) - { - ReadValue(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. - /// - /// value to read - public void ReadValueSafe(out NetworkObject value) - { - ReadValueSafe(out ulong networkObjectId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject; - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkBehaviour - /// - /// value to read - public void ReadValue(out NetworkBehaviour value) - { - ReadValue(out ulong networkObjectId); - ReadValue(out ushort networkBehaviourId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - - /// - /// Read a NetworkBehaviour - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. - /// - /// value to read - public void ReadValueSafe(out NetworkBehaviour value) - { - ReadValueSafe(out ulong networkObjectId); - ReadValueSafe(out ushort networkBehaviourId); - - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) - { - value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); - return; - } - - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); - } - - value = null; - } - /// /// Reads a string /// NOTE: ALLOCATES @@ -656,7 +380,7 @@ public unsafe void ReadValue(out string s, bool oneByteChars = false) /// NOTE: ALLOCATES /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Stores the read string /// Whether or not to use one byte per character. This will only allow ASCII @@ -670,14 +394,14 @@ public unsafe void ReadValueSafe(out string s, bool oneByteChars = false) } #endif - if (!VerifyCanReadInternal(sizeof(uint))) + if (!TryBeginReadInternal(sizeof(uint))) { throw new OverflowException("Reading past the end of the buffer"); } ReadValue(out uint length); - if (!VerifyCanReadInternal((int)length * (oneByteChars ? 1 : sizeof(char)))) + if (!TryBeginReadInternal((int)length * (oneByteChars ? 1 : sizeof(char)))) { throw new OverflowException("Reading past the end of the buffer"); } @@ -723,7 +447,7 @@ public unsafe void ReadValue(out T[] array) where T : unmanaged /// NOTE: ALLOCATES /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Stores the read array [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -737,13 +461,13 @@ public unsafe void ReadValueSafe(out T[] array) where T : unmanaged } #endif - if (!VerifyCanReadInternal(sizeof(int))) + if (!TryBeginReadInternal(sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); } ReadValue(out int sizeInTs); int sizeInBytes = sizeInTs * sizeof(T); - if (!VerifyCanReadInternal(sizeInBytes)) + if (!TryBeginReadInternal(sizeInBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -775,7 +499,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB } if (PositionInternal + bytesToRead > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif @@ -803,7 +527,7 @@ public unsafe void ReadByte(out byte value) } if (PositionInternal + 1 > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif value = BufferPointer[PositionInternal++]; @@ -813,7 +537,7 @@ public unsafe void ReadByte(out byte value) /// Read a byte to the stream. /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Stores the read value [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -827,7 +551,7 @@ public unsafe void ReadByteSafe(out byte value) } #endif - if (!VerifyCanReadInternal(1)) + if (!TryBeginReadInternal(1)) { throw new OverflowException("Reading past the end of the buffer"); } @@ -851,7 +575,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) } if (PositionInternal + size > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling VerifyCanRead()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif UnsafeUtility.MemCpy(value + offset, (BufferPointer + PositionInternal), size); @@ -862,7 +586,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) /// Read multiple bytes to the stream /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Pointer to the destination buffer /// Number of bytes to read - MUST BE <= BUFFER SIZE @@ -878,7 +602,7 @@ public unsafe void ReadBytesSafe(byte* value, int size, int offset = 0) } #endif - if (!VerifyCanReadInternal(size)) + if (!TryBeginReadInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -905,7 +629,7 @@ public unsafe void ReadBytes(ref byte[] value, int size, int offset = 0) /// Read multiple bytes from the stream /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// Pointer to the destination buffer /// Number of bytes to read - MUST BE <= BUFFER SIZE @@ -938,7 +662,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged } if (PositionInternal + len > AllowedReadMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif @@ -954,7 +678,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged /// It will be copied from the buffer exactly as it existed in memory on the writing end. /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple reads at once by calling VerifyCanRead. + /// for multiple reads at once by calling TryBeginRead. /// /// The read value /// Any unmanaged type @@ -971,7 +695,7 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged } #endif - if (!VerifyCanReadInternal(len)) + if (!TryBeginReadInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs new file mode 100644 index 0000000000..21d7daf745 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -0,0 +1,287 @@ + +using System; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class FastBufferReaderExtensions + { + + /// + /// Reads a boxed object in a standard format + /// Named differently from other ReadValue methods to avoid accidental boxing + /// + /// The object to read + /// The type to be read + /// + /// If true, reads a byte indicating whether or not the object is null. + /// Should match the way the object was written. + /// + public static void ReadObject(this ref FastBufferReader reader, out object value, Type type, bool isNullable = false) + { + if (isNullable || type.IsNullable()) + { + reader.ReadValueSafe(out bool isNull); + + if (isNull) + { + value = null; + return; + } + } + + var hasDeserializer = SerializationTypeTable.Deserializers.TryGetValue(type, out var deserializer); + if (hasDeserializer) + { + deserializer(ref reader, out value); + return; + } + + if (type.IsArray && type.HasElementType) + { + reader.ReadValueSafe(out int length); + + var arr = Array.CreateInstance(type.GetElementType(), length); + + for (int i = 0; i < length; i++) + { + reader.ReadObject(out object item, type.GetElementType()); + arr.SetValue(item, i); + } + + value = arr; + return; + } + + if (type.IsEnum) + { + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + reader.ReadValueSafe(out byte boolVal); + value = Enum.ToObject(type, boolVal != 0); + return; + case TypeCode.Char: + reader.ReadValueSafe(out char charVal); + value = Enum.ToObject(type, charVal); + return; + case TypeCode.SByte: + reader.ReadValueSafe(out sbyte sbyteVal); + value = Enum.ToObject(type, sbyteVal); + return; + case TypeCode.Byte: + reader.ReadValueSafe(out byte byteVal); + value = Enum.ToObject(type, byteVal); + return; + case TypeCode.Int16: + reader.ReadValueSafe(out short shortVal); + value = Enum.ToObject(type, shortVal); + return; + case TypeCode.UInt16: + reader.ReadValueSafe(out ushort ushortVal); + value = Enum.ToObject(type, ushortVal); + return; + case TypeCode.Int32: + reader.ReadValueSafe(out int intVal); + value = Enum.ToObject(type, intVal); + return; + case TypeCode.UInt32: + reader.ReadValueSafe(out uint uintVal); + value = Enum.ToObject(type, uintVal); + return; + case TypeCode.Int64: + reader.ReadValueSafe(out long longVal); + value = Enum.ToObject(type, longVal); + return; + case TypeCode.UInt64: + reader.ReadValueSafe(out ulong ulongVal); + value = Enum.ToObject(type, ulongVal); + return; + } + } + + if (type == typeof(GameObject)) + { + reader.ReadValueSafe(out GameObject go); + value = go; + return; + } + + if (type == typeof(NetworkObject)) + { + reader.ReadValueSafe(out NetworkObject no); + value = no; + return; + } + + if (typeof(NetworkBehaviour).IsAssignableFrom(type)) + { + reader.ReadValueSafe(out NetworkBehaviour nb); + value = nb; + return; + } + /*if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + }*/ + + throw new ArgumentException($"{nameof(FastBufferReader)} cannot read type {type.Name} - it does not implement {nameof(INetworkSerializable)}"); + } + + /// + /// Read an INetworkSerializable + /// + /// INetworkSerializable instance + /// + /// + public static void ReadNetworkSerializable(this ref FastBufferReader reader, out T value) where T : INetworkSerializable + { + throw new NotImplementedException(); + } + + /// + /// Read a GameObject + /// + /// value to read + public static void ReadValue(this ref FastBufferReader reader, out GameObject value) + { + reader.ReadValue(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.gameObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a GameObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out GameObject value) + { + reader.ReadValueSafe(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.gameObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a NetworkObject + /// + /// value to read + public static void ReadValue(this ref FastBufferReader reader, out NetworkObject value) + { + reader.ReadValue(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a NetworkObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkObject value) + { + reader.ReadValueSafe(out ulong networkObjectId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject; + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(GameObject)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a NetworkBehaviour + /// + /// value to read + public static void ReadValue(this ref FastBufferReader reader, out NetworkBehaviour value) + { + reader.ReadValue(out ulong networkObjectId); + reader.ReadValue(out ushort networkBehaviourId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + + /// + /// Read a NetworkBehaviour + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple reads at once by calling TryBeginRead. + /// + /// value to read + public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBehaviour value) + { + reader.ReadValueSafe(out ulong networkObjectId); + reader.ReadValueSafe(out ushort networkBehaviourId); + + if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out NetworkObject networkObject)) + { + value = networkObject.GetNetworkBehaviourAtOrderIndex(networkBehaviourId); + return; + } + + if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) + { + NetworkLog.LogWarning($"{nameof(FastBufferReader)} cannot find the {nameof(NetworkBehaviour)} sent in the {nameof(NetworkSpawnManager.SpawnedObjects)} list, it may have been destroyed. {nameof(networkObjectId)}: {networkObjectId}"); + } + + value = null; + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta new file mode 100644 index 0000000000..d00798303c --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4dc2c9158967ec847895c0d9653283fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 91ac86ba91..22fea00ced 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -177,19 +177,19 @@ internal unsafe void Grow() /// /// Allows faster serialization by batching bounds checking. /// When you know you will be writing multiple fields back-to-back and you know the total size, - /// you can call VerifyCanWrite() once on the total size, and then follow it with calls to + /// you can call TryBeginWrite() once on the total size, and then follow it with calls to /// WriteValue() instead of WriteValueSafe() for faster serialization. /// /// Unsafe write operations will throw OverflowException in editor and development builds if you - /// go past the point you've marked using VerifyCanWrite(). In release builds, OverflowException will not be thrown - /// for performance reasons, since the point of using VerifyCanWrite is to avoid bounds checking in the following + /// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following /// operations in release builds. /// /// Amount of bytes to write /// True if the write is allowed, false otherwise /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanWrite(int bytes) + public bool TryBeginWrite(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -218,19 +218,20 @@ public bool VerifyCanWrite(int bytes) /// /// Allows faster serialization by batching bounds checking. /// When you know you will be writing multiple fields back-to-back and you know the total size, - /// you can call VerifyCanWrite() once on the total size, and then follow it with calls to + /// you can call TryBeginWrite() once on the total size, and then follow it with calls to /// WriteValue() instead of WriteValueSafe() for faster serialization. /// /// Unsafe write operations will throw OverflowException in editor and development builds if you - /// go past the point you've marked using VerifyCanWrite(). In release builds, OverflowException will not be thrown - /// for performance reasons, since the point of using VerifyCanWrite is to avoid bounds checking in the following - /// operations in release builds. + /// go past the point you've marked using TryBeginWrite(). In release builds, OverflowException will not be thrown + /// for performance reasons, since the point of using TryBeginWrite is to avoid bounds checking in the following + /// operations in release builds. Instead, attempting to write past the marked position in release builds + /// will write to random memory and cause undefined behavior, likely including instability and crashes. /// /// The value you want to write /// True if the write is allowed, false otherwise /// If called while in a bitwise context [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged + public unsafe bool TryBeginWriteValue(in T value) where T : unmanaged { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -258,14 +259,14 @@ public unsafe bool VerifyCanWriteValue(in T value) where T : unmanaged } /// - /// Internal version of VerifyCanWrite. - /// Differs from VerifyCanWrite only in that it won't ever move the AllowedWriteMark backward. + /// Internal version of TryBeginWrite. + /// Differs from TryBeginWrite only in that it won't ever move the AllowedWriteMark backward. /// /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool VerifyCanWriteInternal(int bytes) + public bool TryBeginWriteInternal(int bytes) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_InBitwiseContext) @@ -330,292 +331,6 @@ public unsafe byte[] ToArray() return BufferPointer + PositionInternal; } - /// - /// Writes a boxed object in a standard format - /// Named differently from other WriteValue methods to avoid accidental boxing - /// - /// The object to write - /// - /// If true, an extra byte will be written to indicate whether or not the value is null. - /// Some types will always write this. - /// - public void WriteObject(object value, bool isNullable = false) - { - if (isNullable || value.GetType().IsNullable()) - { - bool isNull = value == null || (value is UnityEngine.Object o && o == null); - - WriteValueSafe(isNull); - - if (isNull) - { - return; - } - } - - var type = value.GetType(); - var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); - if (hasSerializer) - { - serializer(ref this, value); - return; - } - - if (value is Array array) - { - WriteValueSafe(array.Length); - - for (int i = 0; i < array.Length; i++) - { - WriteObject(array.GetValue(i)); - } - - return; - } - - if (value.GetType().IsEnum) - { - switch (Convert.GetTypeCode(value)) - { - case TypeCode.Boolean: - WriteValueSafe((byte)value); - break; - case TypeCode.Char: - WriteValueSafe((char)value); - break; - case TypeCode.SByte: - WriteValueSafe((sbyte)value); - break; - case TypeCode.Byte: - WriteValueSafe((byte)value); - break; - case TypeCode.Int16: - WriteValueSafe((short)value); - break; - case TypeCode.UInt16: - WriteValueSafe((ushort)value); - break; - case TypeCode.Int32: - WriteValueSafe((int)value); - break; - case TypeCode.UInt32: - WriteValueSafe((uint)value); - break; - case TypeCode.Int64: - WriteValueSafe((long)value); - break; - case TypeCode.UInt64: - WriteValueSafe((ulong)value); - break; - } - return; - } - if (value is GameObject) - { - WriteValueSafe((GameObject)value); - return; - } - if (value is NetworkObject) - { - WriteValueSafe((NetworkObject)value); - return; - } - if (value is NetworkBehaviour) - { - WriteValueSafe((NetworkBehaviour)value); - return; - } - if (value is INetworkSerializable) - { - //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); - return; - } - - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); - } - - /// - /// Write an INetworkSerializable - /// - /// The value to write - /// - public void WriteNetworkSerializable(in T value) where T : INetworkSerializable - { - // TODO - } - - /// - /// Get the required amount of space to write a GameObject - /// - /// - /// - public static int GetWriteSize(GameObject value) - { - return sizeof(ulong); - } - - /// - /// Get the required amount of space to write a GameObject - /// - /// - public static int GetGameObjectWriteSize() - { - return sizeof(ulong); - } - - /// - /// Write a GameObject - /// - /// The value to write - public void WriteValue(GameObject value) - { - var networkObject = (value).GetComponent(); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); - } - - WriteValue(networkObject.NetworkObjectId); - } - - /// - /// Write a GameObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. - /// - /// The value to write - public void WriteValueSafe(GameObject value) - { - var networkObject = (value).GetComponent(); - if (networkObject == null) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); - } - - if (!networkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); - } - - WriteValueSafe(networkObject.NetworkObjectId); - } - - /// - /// Get the required size to write a NetworkObject - /// - /// - /// - public static int GetWriteSize(NetworkObject value) - { - return sizeof(ulong); - } - - /// - /// Get the required size to write a NetworkObject - /// - /// - public static int GetNetworkObjectWriteSize() - { - return sizeof(ulong); - } - - - /// - /// Write a NetworkObject - /// - /// The value to write - public void WriteValue(in NetworkObject value) - { - if (!value.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); - } - - WriteValue(value.NetworkObjectId); - } - - /// - /// Write a NetworkObject - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. - /// - /// The value to write - public void WriteValueSafe(NetworkObject value) - { - if (!value.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); - } - WriteValueSafe(value.NetworkObjectId); - } - - /// - /// Get the required size to write a NetworkBehaviour - /// - /// - /// - public static int GetWriteSize(NetworkBehaviour value) - { - return sizeof(ulong) + sizeof(ushort); - } - - - /// - /// Get the required size to write a NetworkBehaviour - /// - /// - public static int GetNetworkBehaviourWriteSize() - { - return sizeof(ulong) + sizeof(ushort); - } - - - /// - /// Write a NetworkBehaviour - /// - /// The value to write - public void WriteValue(NetworkBehaviour value) - { - if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); - } - - WriteValue(value.NetworkObjectId); - WriteValue(value.NetworkBehaviourId); - } - - /// - /// Write a NetworkBehaviour - /// - /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. - /// - /// The value to write - /// - /// - public void WriteValueSafe(NetworkBehaviour value) - { - if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) - { - throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); - } - - if (!VerifyCanWriteInternal(sizeof(ulong) + sizeof(ushort))) - { - throw new OverflowException("Writing past the end of the buffer"); - } - WriteValue(value.NetworkObjectId); - WriteValue(value.NetworkBehaviourId); - } - /// /// Get the required size to write a string /// @@ -657,7 +372,7 @@ public unsafe void WriteValue(string s, bool oneByteChars = false) /// Writes a string /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// The string to write /// Whether or not to use one byte per character. This will only allow ASCII @@ -673,7 +388,7 @@ public unsafe void WriteValueSafe(string s, bool oneByteChars = false) int sizeInBytes = GetWriteSize(s, oneByteChars); - if (!VerifyCanWriteInternal(sizeInBytes)) + if (!TryBeginWriteInternal(sizeInBytes)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -735,7 +450,7 @@ public unsafe void WriteValue(T[] array, int count = -1, int offset = 0) wher /// Writes an unmanaged array /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// The array to write /// The amount of elements to write @@ -754,7 +469,7 @@ public unsafe void WriteValueSafe(T[] array, int count = -1, int offset = 0) int sizeInTs = count != -1 ? count : array.Length - offset; int sizeInBytes = sizeInTs * sizeof(T); - if (!VerifyCanWriteInternal(sizeInBytes + sizeof(int))) + if (!TryBeginWriteInternal(sizeInBytes + sizeof(int))) { throw new OverflowException("Writing past the end of the buffer"); } @@ -786,7 +501,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt } if (PositionInternal + bytesToWrite > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif @@ -812,7 +527,7 @@ public unsafe void WriteByte(byte value) } if (PositionInternal + 1 > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif BufferPointer[PositionInternal++] = value; @@ -822,7 +537,7 @@ public unsafe void WriteByte(byte value) /// Write a byte to the stream. /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// Value to write [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -836,7 +551,7 @@ public unsafe void WriteByteSafe(byte value) } #endif - if (!VerifyCanWriteInternal(1)) + if (!TryBeginWriteInternal(1)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -860,7 +575,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) } if (PositionInternal + size > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif UnsafeUtility.MemCpy((BufferPointer + PositionInternal), value + offset, size); @@ -871,7 +586,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) /// Write multiple bytes to the stream /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// Value to write /// Number of bytes to write @@ -887,7 +602,7 @@ public unsafe void WriteBytesSafe(byte* value, int size, int offset = 0) } #endif - if (!VerifyCanWriteInternal(size)) + if (!TryBeginWriteInternal(size)) { throw new OverflowException("Writing past the end of the buffer"); } @@ -914,7 +629,7 @@ public unsafe void WriteBytes(byte[] value, int size, int offset = 0) /// Write multiple bytes to the stream /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// Value to write /// Number of bytes to write @@ -994,7 +709,7 @@ public unsafe void WriteValue(in T value) where T : unmanaged } if (PositionInternal + len > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling VerifyCanWrite()"); + throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); } #endif @@ -1010,7 +725,7 @@ public unsafe void WriteValue(in T value) where T : unmanaged /// It will be copied into the buffer exactly as it exists in memory. /// /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking - /// for multiple writes at once by calling VerifyCanWrite. + /// for multiple writes at once by calling TryBeginWrite. /// /// The value to copy /// Any unmanaged type @@ -1027,7 +742,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged } #endif - if (!VerifyCanWriteInternal(len)) + if (!TryBeginWriteInternal(len)) { throw new OverflowException("Writing past the end of the buffer"); } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs new file mode 100644 index 0000000000..3fea8ee5c0 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs @@ -0,0 +1,298 @@ + +using System; +using Unity.Netcode; +using UnityEngine; + +namespace Unity.Multiplayer.Netcode +{ + public static class FastBufferWriterExtensions + { + + /// + /// Writes a boxed object in a standard format + /// Named differently from other WriteValue methods to avoid accidental boxing + /// + /// The object to write + /// + /// If true, an extra byte will be written to indicate whether or not the value is null. + /// Some types will always write this. + /// + public static void WriteObject(this ref FastBufferWriter writer, object value, bool isNullable = false) + { + if (isNullable || value.GetType().IsNullable()) + { + bool isNull = value == null || (value is UnityEngine.Object o && o == null); + + writer.WriteValueSafe(isNull); + + if (isNull) + { + return; + } + } + + var type = value.GetType(); + var hasSerializer = SerializationTypeTable.Serializers.TryGetValue(type, out var serializer); + if (hasSerializer) + { + serializer(ref writer, value); + return; + } + + if (value is Array array) + { + writer.WriteValueSafe(array.Length); + + for (int i = 0; i < array.Length; i++) + { + writer.WriteObject(array.GetValue(i)); + } + + return; + } + + if (value.GetType().IsEnum) + { + switch (Convert.GetTypeCode(value)) + { + case TypeCode.Boolean: + writer.WriteValueSafe((byte)value); + break; + case TypeCode.Char: + writer.WriteValueSafe((char)value); + break; + case TypeCode.SByte: + writer.WriteValueSafe((sbyte)value); + break; + case TypeCode.Byte: + writer.WriteValueSafe((byte)value); + break; + case TypeCode.Int16: + writer.WriteValueSafe((short)value); + break; + case TypeCode.UInt16: + writer.WriteValueSafe((ushort)value); + break; + case TypeCode.Int32: + writer.WriteValueSafe((int)value); + break; + case TypeCode.UInt32: + writer.WriteValueSafe((uint)value); + break; + case TypeCode.Int64: + writer.WriteValueSafe((long)value); + break; + case TypeCode.UInt64: + writer.WriteValueSafe((ulong)value); + break; + } + return; + } + if (value is GameObject) + { + writer.WriteValueSafe((GameObject)value); + return; + } + if (value is NetworkObject) + { + writer.WriteValueSafe((NetworkObject)value); + return; + } + if (value is NetworkBehaviour) + { + writer.WriteValueSafe((NetworkBehaviour)value); + return; + } + if (value is INetworkSerializable) + { + //TODO ((INetworkSerializable)value).NetworkSerialize(new NetworkSerializer(this)); + return; + } + + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write type {value.GetType().Name} - it does not implement {nameof(INetworkSerializable)}"); + } + + /// + /// Write an INetworkSerializable + /// + /// The value to write + /// + public static void WriteNetworkSerializable(this ref FastBufferWriter writer, in T value) where T : INetworkSerializable + { + // TODO + } + + /// + /// Get the required amount of space to write a GameObject + /// + /// + /// + public static int GetWriteSize(GameObject value) + { + return sizeof(ulong); + } + + /// + /// Get the required amount of space to write a GameObject + /// + /// + public static int GetGameObjectWriteSize() + { + return sizeof(ulong); + } + + /// + /// Write a GameObject + /// + /// The value to write + public static void WriteValue(this ref FastBufferWriter writer, GameObject value) + { + value.TryGetComponent(out var networkObject); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); + } + + writer.WriteValue(networkObject.NetworkObjectId); + } + + /// + /// Write a GameObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + public static void WriteValueSafe(this ref FastBufferWriter writer, GameObject value) + { + value.TryGetComponent(out var networkObject); + if (networkObject == null) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(GameObject)} types that does not has a {nameof(NetworkObject)} component attached. {nameof(GameObject)}: {(value).name}"); + } + + if (!networkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {(value).name}"); + } + + writer.WriteValueSafe(networkObject.NetworkObjectId); + } + + /// + /// Get the required size to write a NetworkObject + /// + /// + /// + public static int GetWriteSize(NetworkObject value) + { + return sizeof(ulong); + } + + /// + /// Get the required size to write a NetworkObject + /// + /// + public static int GetNetworkObjectWriteSize() + { + return sizeof(ulong); + } + + + /// + /// Write a NetworkObject + /// + /// The value to write + public static void WriteValue(this ref FastBufferWriter writer, in NetworkObject value) + { + if (!value.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + } + + writer.WriteValue(value.NetworkObjectId); + } + + /// + /// Write a NetworkObject + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkObject value) + { + if (!value.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkObject)} types that are not spawned. {nameof(GameObject)}: {value.name}"); + } + writer.WriteValueSafe(value.NetworkObjectId); + } + + /// + /// Get the required size to write a NetworkBehaviour + /// + /// + /// + public static int GetWriteSize(NetworkBehaviour value) + { + return sizeof(ulong) + sizeof(ushort); + } + + + /// + /// Get the required size to write a NetworkBehaviour + /// + /// + public static int GetNetworkBehaviourWriteSize() + { + return sizeof(ulong) + sizeof(ushort); + } + + + /// + /// Write a NetworkBehaviour + /// + /// The value to write + public static void WriteValue(this ref FastBufferWriter writer, NetworkBehaviour value) + { + if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); + } + + writer.WriteValue(value.NetworkObjectId); + writer.WriteValue(value.NetworkBehaviourId); + } + + /// + /// Write a NetworkBehaviour + /// + /// "Safe" version - automatically performs bounds checking. Less efficient than bounds checking + /// for multiple writes at once by calling TryBeginWrite. + /// + /// The value to write + /// + /// + public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehaviour value) + { + if (!value.HasNetworkObject || !value.NetworkObject.IsSpawned) + { + throw new ArgumentException($"{nameof(FastBufferWriter)} cannot write {nameof(NetworkBehaviour)} types that are not spawned. {nameof(GameObject)}: {(value).gameObject.name}"); + } + + if (!writer.TryBeginWriteInternal(sizeof(ulong) + sizeof(ushort))) + { + throw new OverflowException("Writing past the end of the buffer"); + } + writer.WriteValue(value.NetworkObjectId); + writer.WriteValue(value.NetworkBehaviourId); + } + + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta new file mode 100644 index 0000000000..b96c1f3b21 --- /dev/null +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1a9dd964797964b4a99e03706516a8a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs index 9d04ce6120..7d181d3f11 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs @@ -6,34 +6,34 @@ namespace Unity.Netcode.Serialization { public interface IBufferSerializerImplementation { - public bool IsReader { get; } - public bool IsWriter { get; } + bool IsReader { get; } + bool IsWriter { get; } - public ref FastBufferReader GetFastBufferReader(); - public ref FastBufferWriter GetFastBufferWriter(); + ref FastBufferReader GetFastBufferReader(); + ref FastBufferWriter GetFastBufferWriter(); - public void SerializeValue(ref object value, Type type, bool isNullable = false); - public void SerializeValue(ref INetworkSerializable value); - public void SerializeValue(ref GameObject value); - public void SerializeValue(ref NetworkObject value); - public void SerializeValue(ref NetworkBehaviour value); - public void SerializeValue(ref string s, bool oneByteChars = false); - public void SerializeValue(ref T[] array) where T : unmanaged; - public void SerializeValue(ref byte value); - public void SerializeValue(ref T value) where T : unmanaged; + void SerializeValue(ref object value, Type type, bool isNullable = false); + void SerializeValue(ref INetworkSerializable value); + void SerializeValue(ref GameObject value); + void SerializeValue(ref NetworkObject value); + void SerializeValue(ref NetworkBehaviour value); + void SerializeValue(ref string s, bool oneByteChars = false); + void SerializeValue(ref T[] array) where T : unmanaged; + void SerializeValue(ref byte value); + void SerializeValue(ref T value) where T : unmanaged; // Has to have a different name to avoid conflicting with "where T: unmananged" // Using SerializeValue(INetworkSerializable) will result in boxing on struct INetworkSerializables // So this is provided as an alternative to avoid boxing allocations. - public void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable; + void SerializeNetworkSerializable(ref T value) where T : INetworkSerializable; - public bool PreCheck(int amount); - public void SerializeValuePreChecked(ref GameObject value); - public void SerializeValuePreChecked(ref NetworkObject value); - public void SerializeValuePreChecked(ref NetworkBehaviour value); - public void SerializeValuePreChecked(ref string s, bool oneByteChars = false); - public void SerializeValuePreChecked(ref T[] array) where T : unmanaged; - public void SerializeValuePreChecked(ref byte value); - public void SerializeValuePreChecked(ref T value) where T : unmanaged; + bool PreCheck(int amount); + void SerializeValuePreChecked(ref GameObject value); + void SerializeValuePreChecked(ref NetworkObject value); + void SerializeValuePreChecked(ref NetworkBehaviour value); + void SerializeValuePreChecked(ref string s, bool oneByteChars = false); + void SerializeValuePreChecked(ref T[] array) where T : unmanaged; + void SerializeValuePreChecked(ref byte value); + void SerializeValuePreChecked(ref T value) where T : unmanaged; } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs new file mode 100644 index 0000000000..e2d22d75b3 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -0,0 +1,669 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Unity.Multiplayer.Netcode; +using Unity.Netcode.EditorTests; +using UnityEngine; +using UnityEngine.SceneManagement; +using Random = System.Random; + +namespace Unity.Netcode +{ + public abstract class BaseFastBufferReaderWriterTest + { + + #region Test Types + protected enum ByteEnum : byte + { + A, + B, + C + }; + protected enum SByteEnum : sbyte + { + A, + B, + C + }; + protected enum ShortEnum : short + { + A, + B, + C + }; + protected enum UShortEnum : ushort + { + A, + B, + C + }; + protected enum IntEnum : int + { + A, + B, + C + }; + protected enum UIntEnum : uint + { + A, + B, + C + }; + protected enum LongEnum : long + { + A, + B, + C + }; + protected enum ULongEnum : ulong + { + A, + B, + C + }; + + protected struct TestStruct + { + public byte A; + public short B; + public ushort C; + public int D; + public uint E; + public long F; + public ulong G; + public bool H; + public char I; + public float J; + public double K; + } + + public enum WriteType + { + WriteDirect, + WriteSafe, + WriteAsObject + } + #endregion + + + protected abstract void RunTypeTest(T valueToTest) where T : unmanaged; + + protected abstract void RunTypeTestSafe(T valueToTest) where T : unmanaged; + + protected abstract void RunObjectTypeTest(T valueToTest) where T : unmanaged; + + protected abstract void RunTypeArrayTest(T[] valueToTest) where T : unmanaged; + + protected abstract void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged; + + protected abstract void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged; + + #region Helpers + protected TestStruct GetTestStruct() + { + var random = new Random(); + + var testStruct = new TestStruct + { + A = (byte)random.Next(), + B = (short)random.Next(), + C = (ushort)random.Next(), + D = (int)random.Next(), + E = (uint)random.Next(), + F = ((long)random.Next() << 32) + random.Next(), + G = ((ulong)random.Next() << 32) + (ulong)random.Next(), + H = true, + I = '\u263a', + J = (float)random.NextDouble(), + K = random.NextDouble(), + }; + + return testStruct; + } + + protected delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, + NetworkObject networkObject); + protected void RunGameObjectTest(GameObjectTestDelegate testCode) + { + var obj = new GameObject("Object"); + var networkBehaviour = obj.AddComponent(); + var networkObject = obj.AddComponent(); + // Create networkManager component + var networkManager = obj.AddComponent(); + networkManager.SetSingleton(); + networkObject.NetworkManagerOwner = networkManager; + + // Set the NetworkConfig + networkManager.NetworkConfig = new NetworkConfig() + { + // Set the current scene to prevent unexpected log messages which would trigger a failure + RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, + // Set transport + NetworkTransport = obj.AddComponent() + }; + + networkManager.StartHost(); + + try + { + testCode(obj, networkBehaviour, networkObject); + } + finally + { + UnityEngine.Object.DestroyImmediate(obj); + networkManager.StopHost(); + } + } + #endregion + + public void BaseTypeTest(Type testType, WriteType writeType) + { + var random = new Random(); + + void RunTypeTestLocal(T val, WriteType wt) where T : unmanaged + { + switch (wt) + { + case WriteType.WriteDirect: + RunTypeTest(val); + break; + case WriteType.WriteSafe: + RunTypeTestSafe(val); + break; + default: + RunObjectTypeTest(val); + break; + } + } + + if (testType == typeof(byte)) + { + RunTypeTestLocal((byte)random.Next(), writeType); + } + else if (testType == typeof(sbyte)) + { + RunTypeTestLocal((sbyte)random.Next(), writeType); + } + else if (testType == typeof(short)) + { + RunTypeTestLocal((short)random.Next(), writeType); + } + else if (testType == typeof(ushort)) + { + RunTypeTestLocal((ushort)random.Next(), writeType); + } + else if (testType == typeof(int)) + { + RunTypeTestLocal((int)random.Next(), writeType); + } + else if (testType == typeof(uint)) + { + RunTypeTestLocal((uint)random.Next(), writeType); + } + else if (testType == typeof(long)) + { + RunTypeTestLocal(((long)random.Next() << 32) + random.Next(), writeType); + } + else if (testType == typeof(ulong)) + { + RunTypeTestLocal(((ulong)random.Next() << 32) + (ulong)random.Next(), writeType); + } + else if (testType == typeof(bool)) + { + RunTypeTestLocal(true, writeType); + } + else if (testType == typeof(char)) + { + RunTypeTestLocal('a', writeType); + RunTypeTestLocal('\u263a', writeType); + } + else if (testType == typeof(float)) + { + RunTypeTestLocal((float)random.NextDouble(), writeType); + } + else if (testType == typeof(double)) + { + RunTypeTestLocal(random.NextDouble(), writeType); + } + else if (testType == typeof(ByteEnum)) + { + RunTypeTestLocal(ByteEnum.C, writeType); + } + else if (testType == typeof(SByteEnum)) + { + RunTypeTestLocal(SByteEnum.C, writeType); + } + else if (testType == typeof(ShortEnum)) + { + RunTypeTestLocal(ShortEnum.C, writeType); + } + else if (testType == typeof(UShortEnum)) + { + RunTypeTestLocal(UShortEnum.C, writeType); + } + else if (testType == typeof(IntEnum)) + { + RunTypeTestLocal(IntEnum.C, writeType); + } + else if (testType == typeof(UIntEnum)) + { + RunTypeTestLocal(UIntEnum.C, writeType); + } + else if (testType == typeof(LongEnum)) + { + RunTypeTestLocal(LongEnum.C, writeType); + } + else if (testType == typeof(ULongEnum)) + { + RunTypeTestLocal(ULongEnum.C, writeType); + } + else if (testType == typeof(Vector2)) + { + RunTypeTestLocal(new Vector2((float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Vector3)) + { + RunTypeTestLocal(new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Vector4)) + { + RunTypeTestLocal(new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Quaternion)) + { + RunTypeTestLocal(new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Color)) + { + RunTypeTestLocal(new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), writeType); + } + else if (testType == typeof(Color32)) + { + RunTypeTestLocal(new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()), writeType); + } + else if (testType == typeof(Ray)) + { + RunTypeTestLocal(new Ray( + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), + new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())), writeType); + } + else if (testType == typeof(Ray2D)) + { + RunTypeTestLocal(new Ray2D( + new Vector2((float)random.NextDouble(), (float)random.NextDouble()), + new Vector2((float)random.NextDouble(), (float)random.NextDouble())), writeType); + } + else if (testType == typeof(TestStruct)) + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct)obj); + }; + SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => + { + reader.ReadValueSafe(out TestStruct value); + obj = value; + }; + try + { + RunTypeTestLocal(GetTestStruct(), writeType); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + + public void BaseArrayTypeTest(Type testType, WriteType writeType) + { + var random = new Random(); + void RunTypeTestLocal(T[] val, WriteType wt) where T : unmanaged + { + switch (wt) + { + case WriteType.WriteDirect: + RunTypeArrayTest(val); + break; + case WriteType.WriteSafe: + RunTypeArrayTestSafe(val); + break; + default: + RunObjectTypeArrayTest(val); + break; + } + } + + if (testType == typeof(byte)) + { + RunTypeTestLocal(new[]{ + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next(), + (byte) random.Next() + }, writeType); + } + else if (testType == typeof(sbyte)) + { + RunTypeTestLocal(new[]{ + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next(), + (sbyte) random.Next() + }, writeType); + } + else if (testType == typeof(short)) + { + RunTypeTestLocal(new[]{ + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next(), + (short) random.Next() + }, writeType); + } + else if (testType == typeof(ushort)) + { + RunTypeTestLocal(new[]{ + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next(), + (ushort) random.Next() + }, writeType); + } + else if (testType == typeof(int)) + { + RunTypeTestLocal(new[]{ + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next(), + random.Next() + }, writeType); + } + else if (testType == typeof(uint)) + { + RunTypeTestLocal(new[]{ + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next(), + (uint) random.Next() + }, writeType); + } + else if (testType == typeof(long)) + { + RunTypeTestLocal(new[]{ + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next(), + ((long)random.Next() << 32) + (long)random.Next() + }, writeType); + } + else if (testType == typeof(ulong)) + { + RunTypeTestLocal(new[]{ + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next(), + ((ulong)random.Next() << 32) + (ulong)random.Next() + }, writeType); + } + else if (testType == typeof(bool)) + { + RunTypeTestLocal(new[]{ + true, + false, + true, + true, + false, + false, + true, + false, + true + }, writeType); + } + else if (testType == typeof(char)) + { + RunTypeTestLocal(new[]{ + 'a', + '\u263a' + }, writeType); + } + else if (testType == typeof(float)) + { + RunTypeTestLocal(new[]{ + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble(), + (float)random.NextDouble() + }, writeType); + } + else if (testType == typeof(double)) + { + RunTypeTestLocal(new[]{ + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble(), + random.NextDouble() + }, writeType); + } + else if (testType == typeof(ByteEnum)) + { + RunTypeTestLocal(new[]{ + ByteEnum.C, + ByteEnum.A, + ByteEnum.B + }, writeType); + } + else if (testType == typeof(SByteEnum)) + { + RunTypeTestLocal(new[]{ + SByteEnum.C, + SByteEnum.A, + SByteEnum.B + }, writeType); + } + else if (testType == typeof(ShortEnum)) + { + RunTypeTestLocal(new[]{ + ShortEnum.C, + ShortEnum.A, + ShortEnum.B + }, writeType); + } + else if (testType == typeof(UShortEnum)) + { + RunTypeTestLocal(new[]{ + UShortEnum.C, + UShortEnum.A, + UShortEnum.B + }, writeType); + } + else if (testType == typeof(IntEnum)) + { + RunTypeTestLocal(new[]{ + IntEnum.C, + IntEnum.A, + IntEnum.B + }, writeType); + } + else if (testType == typeof(UIntEnum)) + { + RunTypeTestLocal(new[]{ + UIntEnum.C, + UIntEnum.A, + UIntEnum.B + }, writeType); + } + else if (testType == typeof(LongEnum)) + { + RunTypeTestLocal(new[]{ + LongEnum.C, + LongEnum.A, + LongEnum.B + }, writeType); + } + else if (testType == typeof(ULongEnum)) + { + RunTypeTestLocal(new[]{ + ULongEnum.C, + ULongEnum.A, + ULongEnum.B + }, writeType); + } + else if (testType == typeof(Vector2)) + { + RunTypeTestLocal(new[]{ + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Vector3)) + { + RunTypeTestLocal(new[]{ + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Vector4)) + { + RunTypeTestLocal(new[]{ + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Quaternion)) + { + RunTypeTestLocal(new[]{ + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble(), (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Color)) + { + RunTypeTestLocal(new[]{ + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + }, writeType); + } + else if (testType == typeof(Color32)) + { + RunTypeTestLocal(new[]{ + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), + }, writeType); + } + else if (testType == typeof(Ray)) + { + RunTypeTestLocal(new[]{ + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + new Ray( + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble()), + new Vector3((float) random.NextDouble(), (float) random.NextDouble(), + (float) random.NextDouble())), + }, writeType); + } + else if (testType == typeof(Ray2D)) + { + RunTypeTestLocal(new[]{ + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + new Ray2D( + new Vector2((float) random.NextDouble(), (float) random.NextDouble()), + new Vector2((float) random.NextDouble(), (float) random.NextDouble())), + }, writeType); + } + else if (testType == typeof(TestStruct)) + { + SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => + { + writer.WriteValueSafe((TestStruct)obj); + }; + SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => + { + reader.ReadValueSafe(out TestStruct value); + obj = value; + }; + try + { + RunTypeTestLocal(new[] { + GetTestStruct(), + GetTestStruct(), + GetTestStruct(), + }, writeType); + } + finally + { + SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + } + } + else + { + Assert.Fail("No type handler was provided for this type in the test!"); + } + } + } +} \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs.meta b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs.meta new file mode 100644 index 0000000000..f0b683d274 --- /dev/null +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 573b1f36caed496a9c6e0eaa788d0c29 +timeCreated: 1629917174 \ No newline at end of file diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 8584391af1..96d68f8c67 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -6,57 +6,66 @@ namespace Unity.Netcode.EditorTests public class BitCounterTests { [Test] - public void TestBitCounter64Bits() + public void WhenCountingUsedBitsIn64BitValue_ResultMatchesHighBitSetPlusOne([Range(0, 63)] int highBit) { - ulong value = 0; - // 0 is a special case. All values are considered at least 1 bit. - Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); - - for (int i = 0; i < 64; ++i) + if (highBit == 0) + { + ulong value = 0; + // 0 is a special case. All values are considered at least 1 bit. + Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); + } + else { - value = 1UL << i; - Assert.AreEqual(i + 1, BitCounter.GetUsedBitCount(value)); + ulong value = 1UL << highBit; + Assert.AreEqual(highBit + 1, BitCounter.GetUsedBitCount(value)); } } [Test] - public void TestBitCounter32Bits() + public void WhenCountingUsedBitsIn32BitValue_ResultMatchesHighBitSetPlusOne([Range(0, 31)] int highBit) { - uint value = 0; - // 0 is a special case. All values are considered at least 1 bit. - Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); - - for (int i = 0; i < 32; ++i) + if (highBit == 0) { - value = 1U << i; - Assert.AreEqual(i + 1, BitCounter.GetUsedBitCount(value)); + uint value = 0; + // 0 is a special case. All values are considered at least 1 bit. + Assert.AreEqual(1, BitCounter.GetUsedBitCount(value)); + } + else + { + uint value = 1U << highBit; + Assert.AreEqual(highBit + 1, BitCounter.GetUsedBitCount(value)); } } + [Test] - public void TestByteCounter64Bits() + public void WhenCountingUsedBytesIn64BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 63)] int highBit) { - ulong value = 0; - // 0 is a special case. All values are considered at least 1 byte. - Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); - - for (int i = 0; i < 64; ++i) + if (highBit == 0) { - value = 1UL << i; - Assert.AreEqual(i / 8 + 1, BitCounter.GetUsedByteCount(value)); + ulong value = 0; + // 0 is a special case. All values are considered at least 1 byte. + Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); + } + else + { + ulong value = 1UL << highBit; + Assert.AreEqual(highBit/8 + 1, BitCounter.GetUsedByteCount(value)); } } - + [Test] - public void TestByteCounter32Bits() + public void WhenCountingUsedBytesIn32BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 31)] int highBit) { - uint value = 0; - // 0 is a special case. All values are considered at least 1 byte. - Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); - - for (int i = 0; i < 32; ++i) + if (highBit == 0) + { + uint value = 0; + // 0 is a special case. All values are considered at least 1 byte. + Assert.AreEqual(1, BitCounter.GetUsedByteCount(value)); + } + else { - value = 1U << i; - Assert.AreEqual(i / 8 + 1, BitCounter.GetUsedByteCount(value)); + uint value = 1U << highBit; + Assert.AreEqual(highBit/8 + 1, BitCounter.GetUsedByteCount(value)); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index a63f84c48d..533c6bf963 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -13,7 +13,7 @@ public void TestReadingOneBit() var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBit(true); @@ -39,7 +39,7 @@ public void TestReadingOneBit() var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - Assert.IsTrue(reader.VerifyCanRead(3)); + Assert.IsTrue(reader.TryBeginRead(3)); using (var bitReader = reader.EnterBitwiseContext()) { bool b; @@ -77,7 +77,7 @@ public void TestReadingOneBit() } } [Test] - public unsafe void TestVerifyCanReadBits() + public unsafe void TestTryBeginReadBits() { var nativeArray = new NativeArray(4, Allocator.Temp); var reader = new FastBufferReader(nativeArray, Allocator.Temp); @@ -89,9 +89,9 @@ public unsafe void TestVerifyCanReadBits() using (var bitReader = reader.EnterBitwiseContext()) { - Assert.Throws(() => reader.VerifyCanRead(1)); - Assert.Throws(() => reader.VerifyCanReadValue(1)); - Assert.IsTrue(bitReader.VerifyCanReadBits(1)); + Assert.Throws(() => reader.TryBeginRead(1)); + Assert.Throws(() => reader.TryBeginReadValue(1)); + Assert.IsTrue(bitReader.TryBeginReadBits(1)); bitReader.ReadBit(out bool b); Assert.IsTrue(b); @@ -108,7 +108,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(bitReader.VerifyCanReadBits(3)); + Assert.IsTrue(bitReader.TryBeginReadBits(3)); bitReader.ReadBit(out b); Assert.IsTrue(b); bitReader.ReadBit(out b); @@ -142,7 +142,7 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(bitReader.VerifyCanReadBits(3)); + Assert.IsTrue(bitReader.TryBeginReadBits(3)); try { @@ -156,11 +156,11 @@ public unsafe void TestVerifyCanReadBits() { throw e; } - Assert.IsTrue(bitReader.VerifyCanReadBits(4)); + Assert.IsTrue(bitReader.TryBeginReadBits(4)); bitReader.ReadBits(out byteVal, 3); Assert.AreEqual(0b010, byteVal); - Assert.IsTrue(bitReader.VerifyCanReadBits(5)); + Assert.IsTrue(bitReader.TryBeginReadBits(5)); bitReader.ReadBits(out byteVal, 5); Assert.AreEqual(0b10101, byteVal); @@ -168,18 +168,18 @@ public unsafe void TestVerifyCanReadBits() Assert.AreEqual(2, reader.Position); - Assert.IsTrue(reader.VerifyCanRead(1)); + Assert.IsTrue(reader.TryBeginRead(1)); reader.ReadByte(out byte nextByte); Assert.AreEqual(0b11111111, nextByte); - Assert.IsTrue(reader.VerifyCanRead(1)); + Assert.IsTrue(reader.TryBeginRead(1)); reader.ReadByte(out nextByte); Assert.AreEqual(0b00000000, nextByte); - Assert.IsFalse(reader.VerifyCanRead(1)); + Assert.IsFalse(reader.TryBeginRead(1)); using (var bitReader = reader.EnterBitwiseContext()) { - Assert.IsFalse(bitReader.VerifyCanReadBits(1)); + Assert.IsFalse(bitReader.TryBeginReadBits(1)); } } } @@ -190,7 +190,7 @@ public void TestReadingMultipleBits() var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(0b11111111, 1); @@ -205,7 +205,7 @@ public void TestReadingMultipleBits() var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - Assert.IsTrue(reader.VerifyCanRead(3)); + Assert.IsTrue(reader.TryBeginRead(3)); using (var bitReader = reader.EnterBitwiseContext()) { byte b; @@ -237,7 +237,7 @@ public void TestReadingMultipleBitsToLongs() var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(0b11111111UL, 1); @@ -252,7 +252,7 @@ public void TestReadingMultipleBitsToLongs() var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - Assert.IsTrue(reader.VerifyCanRead(3)); + Assert.IsTrue(reader.TryBeginRead(3)); using (var bitReader = reader.EnterBitwiseContext()) { ulong ul; @@ -279,7 +279,7 @@ public void TestReadingMultipleBitsToLongs() } [Test] - public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) + public unsafe void TestReadingMultipleBytesToLongs([Range(1U, 64U)] uint numBits) { ulong value = 0xFFFFFFFFFFFFFFFF; var reader = new FastBufferReader((byte*)&value, Allocator.Temp, sizeof(ulong)); @@ -296,7 +296,7 @@ public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) ulong readValue; - Assert.IsTrue(reader.VerifyCanRead(sizeof(ulong))); + Assert.IsTrue(reader.TryBeginRead(sizeof(ulong))); using (var bitReader = reader.EnterBitwiseContext()) { bitReader.ReadBits(out readValue, numBits); @@ -306,7 +306,7 @@ public unsafe void TestReadingMultipleBytesToLongs([Range(1, 64)] int numBits) } [Test] - public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() + public unsafe void TestReadingBitsThrowsIfTryBeginReadNotCalled() { var nativeArray = new NativeArray(4, Allocator.Temp); var reader = new FastBufferReader(nativeArray, Allocator.Temp); @@ -344,7 +344,7 @@ public unsafe void TestReadingBitsThrowsIfVerifyCanReadNotCalled() Assert.Throws(() => { - Assert.IsTrue(reader.VerifyCanRead(1)); + Assert.IsTrue(reader.TryBeginRead(1)); using (var bitReader = reader.EnterBitwiseContext()) { ulong ul; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index 2459147dd6..612fcb1f2f 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -17,7 +17,7 @@ public unsafe void TestWritingOneBit() Assert.AreEqual(0, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBit(true); @@ -51,7 +51,7 @@ public unsafe void TestWritingOneBit() } } [Test] - public unsafe void TestVerifyCanWriteBits() + public unsafe void TestTryBeginWriteBits() { var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) @@ -62,9 +62,9 @@ public unsafe void TestVerifyCanWriteBits() using (var bitWriter = writer.EnterBitwiseContext()) { - Assert.Throws(() => writer.VerifyCanWrite(1)); - Assert.Throws(() => writer.VerifyCanWriteValue(1)); - Assert.IsTrue(bitWriter.VerifyCanWriteBits(1)); + Assert.Throws(() => writer.TryBeginWrite(1)); + Assert.Throws(() => writer.TryBeginWriteValue(1)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(1)); bitWriter.WriteBit(true); Assert.AreEqual(0b1, *asInt); @@ -81,7 +81,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(3)); bitWriter.WriteBit(true); Assert.AreEqual(0b11, *asInt); @@ -114,7 +114,7 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(bitWriter.VerifyCanWriteBits(3)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(3)); try { @@ -128,13 +128,13 @@ public unsafe void TestVerifyCanWriteBits() { throw e; } - Assert.IsTrue(bitWriter.VerifyCanWriteBits(4)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(4)); bitWriter.WriteBits(0b11111010, 3); Assert.AreEqual(0b00101011, *asInt); - Assert.IsTrue(bitWriter.VerifyCanWriteBits(5)); + Assert.IsTrue(bitWriter.TryBeginWriteBits(5)); bitWriter.WriteBits(0b11110101, 5); Assert.AreEqual(0b1010_10101011, *asInt); @@ -143,18 +143,18 @@ public unsafe void TestVerifyCanWriteBits() Assert.AreEqual(2, writer.Position); Assert.AreEqual(0b1010_10101011, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(1)); + Assert.IsTrue(writer.TryBeginWrite(1)); writer.WriteByte(0b11111111); Assert.AreEqual(0b11111111_00001010_10101011, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(1)); + Assert.IsTrue(writer.TryBeginWrite(1)); writer.WriteByte(0b00000000); Assert.AreEqual(0b11111111_00001010_10101011, *asInt); - Assert.IsFalse(writer.VerifyCanWrite(1)); + Assert.IsFalse(writer.TryBeginWrite(1)); using (var bitWriter = writer.EnterBitwiseContext()) { - Assert.IsFalse(bitWriter.VerifyCanWriteBits(1)); + Assert.IsFalse(bitWriter.TryBeginWriteBits(1)); } } } @@ -169,7 +169,7 @@ public unsafe void TestWritingMultipleBits() Assert.AreEqual(0, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(0b11111111, 1); @@ -206,7 +206,7 @@ public unsafe void TestWritingMultipleBitsFromLongs() Assert.AreEqual(0, *asInt); - Assert.IsTrue(writer.VerifyCanWrite(3)); + Assert.IsTrue(writer.TryBeginWrite(3)); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(0b11111111UL, 1); @@ -234,7 +234,7 @@ public unsafe void TestWritingMultipleBitsFromLongs() } [Test] - public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) + public unsafe void TestWritingMultipleBytesFromLongs([Range(1U, 64U)] uint numBits) { var writer = new FastBufferWriter(sizeof(ulong), Allocator.Temp); using (writer) @@ -250,7 +250,7 @@ public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) ulong value = 0xFFFFFFFFFFFFFFFF; - Assert.IsTrue(writer.VerifyCanWrite(sizeof(ulong))); + Assert.IsTrue(writer.TryBeginWrite(sizeof(ulong))); using (var bitWriter = writer.EnterBitwiseContext()) { bitWriter.WriteBits(value, numBits); @@ -260,7 +260,7 @@ public unsafe void TestWritingMultipleBytesFromLongs([Range(1, 64)] int numBits) } [Test] - public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() + public unsafe void TestWritingBitsThrowsIfTryBeginWriteNotCalled() { var writer = new FastBufferWriter(4, Allocator.Temp); using (writer) @@ -310,7 +310,7 @@ public unsafe void TestWritingBitsThrowsIfVerifyCanWriteNotCalled() Assert.Throws(() => { - Assert.IsTrue(writer.VerifyCanWrite(1)); + Assert.IsTrue(writer.TryBeginWrite(1)); using (var bitWriter = writer.EnterBitwiseContext()) { try diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index a1bf9b53c8..aa24d4e030 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -525,7 +525,7 @@ public void TestSerializingGameObjectsPreChecked() // Pass } - Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(obj))); + Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(obj))); serializer.SerializeValuePreChecked(ref obj); var reader = new FastBufferReader(ref writer, Allocator.Temp); @@ -543,7 +543,7 @@ public void TestSerializingGameObjectsPreChecked() // Pass } - Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); Assert.AreEqual(obj, readValue); @@ -572,7 +572,7 @@ public void TestSerializingNetworkObjectsPreChecked() // Pass } - Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkObject))); + Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(networkObject))); serializer.SerializeValuePreChecked(ref networkObject); var reader = new FastBufferReader(ref writer, Allocator.Temp); @@ -590,7 +590,7 @@ public void TestSerializingNetworkObjectsPreChecked() // Pass } - Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); Assert.AreEqual(networkObject, readValue); @@ -619,7 +619,7 @@ public void TestSerializingNetworkBehavioursPreChecked() // Pass } - Assert.IsTrue(serializer.PreCheck(FastBufferWriter.GetWriteSize(networkBehaviour))); + Assert.IsTrue(serializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); serializer.SerializeValuePreChecked(ref networkBehaviour); var reader = new FastBufferReader(ref writer, Allocator.Temp); @@ -637,7 +637,7 @@ public void TestSerializingNetworkBehavioursPreChecked() // Pass } - Assert.IsTrue(deserializer.PreCheck(FastBufferWriter.GetWriteSize(readValue))); + Assert.IsTrue(deserializer.PreCheck(FastBufferWriterExtensions.GetWriteSize(readValue))); deserializer.SerializeValuePreChecked(ref readValue); Assert.AreEqual(networkBehaviour, readValue); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 2fcc0f4e28..0ece584a51 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -256,7 +256,7 @@ public void TestPacking64BitsUnsigned() using (writer) { - writer.VerifyCanWrite(9); + writer.TryBeginWrite(9); ulong value = 0; BytePacker.WriteValuePacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -289,7 +289,7 @@ public void TestPacking32BitsUnsigned() using (writer) { - writer.VerifyCanWrite(9); + writer.TryBeginWrite(9); uint value = 0; BytePacker.WriteValuePacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -322,7 +322,7 @@ public void TestPacking64BitsSigned() using (writer) { - writer.VerifyCanWrite(9); + writer.TryBeginWrite(9); long value = 0; BytePacker.WriteValuePacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -367,7 +367,7 @@ public void TestPacking32BitsSigned() using (writer) { - writer.VerifyCanWrite(5); + writer.TryBeginWrite(5); int value = 0; BytePacker.WriteValuePacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -545,7 +545,7 @@ public void TestBitPacking61BitsUnsigned() using (writer) { - writer.VerifyCanWrite(8); + writer.TryBeginWrite(8); ulong value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -585,7 +585,7 @@ public void TestBitPacking60BitsSigned() using (writer) { - writer.VerifyCanWrite(8); + writer.TryBeginWrite(8); long value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -645,7 +645,7 @@ public void TestBitPacking30BitsUnsigned() using (writer) { - writer.VerifyCanWrite(4); + writer.TryBeginWrite(4); uint value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -685,7 +685,7 @@ public void TestBitPacking29BitsSigned() using (writer) { - writer.VerifyCanWrite(4); + writer.TryBeginWrite(4); int value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -743,7 +743,7 @@ public void TestBitPacking15BitsUnsigned() using (writer) { - writer.VerifyCanWrite(2); + writer.TryBeginWrite(2); ushort value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); @@ -782,7 +782,7 @@ public void TestBitPacking14BitsSigned() using (writer) { - writer.VerifyCanWrite(2); + writer.TryBeginWrite(2); short value = 0; BytePacker.WriteValueBitPacked(ref writer, value); Assert.AreEqual(1, writer.Position); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index e4c78be467..cf0817f507 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -10,85 +10,12 @@ namespace Unity.Netcode.EditorTests { - public class FastBufferReaderTests + public class FastBufferReaderTests : BaseFastBufferReaderWriterTest { - #region Test Types - private enum ByteEnum : byte - { - A, - B, - C - }; - private enum SByteEnum : sbyte - { - A, - B, - C - }; - private enum ShortEnum : short - { - A, - B, - C - }; - private enum UShortEnum : ushort - { - A, - B, - C - }; - private enum IntEnum : int - { - A, - B, - C - }; - private enum UIntEnum : uint - { - A, - B, - C - }; - private enum LongEnum : long - { - A, - B, - C - }; - private enum ULongEnum : ulong - { - A, - B, - C - }; - - private struct TestStruct - { - public byte A; - public short B; - public ushort C; - public int D; - public uint E; - public long F; - public ulong G; - public bool H; - public char I; - public float J; - public double K; - } - - public enum WriteType - { - WriteDirect, - WriteSafe, - WriteAsObject - } - #endregion - #region Common Checks private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage = "") { - Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(2), "Writer denied write permission"); writer.WriteValue((byte)0x80); Assert.AreEqual(writeSize + 1, writer.Position, failMessage); Assert.AreEqual(writeSize + 1, writer.Length, failMessage); @@ -100,7 +27,7 @@ private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string private void VerifyCheckBytes(ref FastBufferReader reader, int checkPosition, string failMessage = "") { reader.Seek(checkPosition); - reader.VerifyCanRead(2); + reader.TryBeginRead(2); reader.ReadByte(out byte value); Assert.AreEqual(0x80, value, failMessage); @@ -131,7 +58,7 @@ private FastBufferReader CommonChecks(ref FastBufferWriter writer, T valueToT #endregion #region Generic Checks - private unsafe void RunTypeTest(T valueToTest) where T : unmanaged + protected override unsafe void RunTypeTest(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); Assert.AreEqual(sizeof(T), writeSize); @@ -139,7 +66,7 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission"); var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; @@ -149,13 +76,13 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged using (reader) { - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetWriteSize())); + Assert.IsTrue(reader.TryBeginRead(FastBufferWriter.GetWriteSize())); reader.ReadValue(out T result); Assert.AreEqual(valueToTest, result); } } } - private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged + protected override unsafe void RunTypeTestSafe(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -179,7 +106,7 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged } } - private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged + protected override unsafe void RunObjectTypeTest(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -211,14 +138,14 @@ private void VerifyArrayEquality(T[] value, T[] compareValue, int offset) whe } } - private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged + protected override unsafe void RunTypeArrayTest(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); using (writer) { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); - Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); @@ -229,7 +156,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged { VerifyPositionAndLength(ref reader, writer.Length); - Assert.IsTrue(reader.VerifyCanRead(writeSize)); + Assert.IsTrue(reader.TryBeginRead(writeSize)); reader.ReadValue(out T[] result); VerifyArrayEquality(valueToTest, result, 0); @@ -238,7 +165,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged } } - private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged + protected override unsafe void RunTypeArrayTestSafe(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -263,7 +190,7 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged } } - private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged + protected override unsafe void RunObjectTypeArrayTest(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); // Extra byte for WriteObject adding isNull flag @@ -290,1594 +217,630 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag } #endregion - #region Helpers - private TestStruct GetTestStruct() + #region Tests + [Test] + public void GivenFastBufferWriterContainingValue_WhenReadingUnmanagedType_ValueMatchesWhatWasWritten( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))] + Type testType, + [Values] WriteType writeType) { - var random = new Random(); - - var testStruct = new TestStruct - { - A = (byte)random.Next(), - B = (short)random.Next(), - C = (ushort)random.Next(), - D = (int)random.Next(), - E = (uint)random.Next(), - F = ((long)random.Next() << 32) + random.Next(), - G = ((ulong)random.Next() << 32) + (ulong)random.Next(), - H = true, - I = '\u263a', - J = (float)random.NextDouble(), - K = random.NextDouble(), - }; - - return testStruct; + BaseTypeTest(testType, writeType); } - #endregion - #region Tests [Test] - public void TestReadingBasicTypes( + public void GivenFastBufferWriterContainingValue_WhenReadingArrayOfUnmanagedElementType_ValueMatchesWhatWasWritten( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), - typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))] Type testType, [Values] WriteType writeType) { - var random = new Random(); + BaseArrayTypeTest(testType, writeType); + } + + [TestCase(false, WriteType.WriteDirect)] + [TestCase(false, WriteType.WriteSafe)] + [TestCase(false, WriteType.WriteAsObject)] + [TestCase(true, WriteType.WriteDirect)] + [TestCase(true, WriteType.WriteSafe)] + public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesWhatWasWritten(bool oneByteChars, WriteType writeType) + { + string valueToTest = "Hello, I am a test string!"; - if (testType == typeof(byte)) + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, oneByteChars); + + var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using (writer) { - byte b = (byte)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(b); - } - else if (writeType == WriteType.WriteSafe) + switch (writeType) { - RunTypeTestSafe(b); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest, oneByteChars); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(valueToTest, oneByteChars); + break; + case WriteType.WriteAsObject: + writer.WriteObject(valueToTest); + serializedValueSize += 1; + break; } - else + + WriteCheckBytes(ref writer, serializedValueSize); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - RunObjectTypeTest(b); + VerifyPositionAndLength(ref reader, writer.Length); + + string result = null; + switch (writeType) + { + case WriteType.WriteDirect: + Assert.IsTrue(reader.TryBeginRead(serializedValueSize + 2), "Reader denied read permission"); + reader.ReadValue(out result, oneByteChars); + break; + case WriteType.WriteSafe: + reader.ReadValueSafe(out result, oneByteChars); + break; + case WriteType.WriteAsObject: + reader.ReadObject(out object resultObj, typeof(string), oneByteChars); + result = (string) resultObj; + break; + } + Assert.AreEqual(valueToTest, result); + + VerifyCheckBytes(ref reader, serializedValueSize); } } - else if (testType == typeof(sbyte)) + } + + + [TestCase(1, 0)] + [TestCase(2, 0)] + [TestCase(3, 0)] + [TestCase(4, 0)] + [TestCase(5, 0)] + [TestCase(6, 0)] + [TestCase(7, 0)] + [TestCase(8, 0)] + + [TestCase(1, 1)] + [TestCase(2, 1)] + [TestCase(3, 1)] + [TestCase(4, 1)] + [TestCase(5, 1)] + [TestCase(6, 1)] + [TestCase(7, 1)] + + [TestCase(1, 2)] + [TestCase(2, 2)] + [TestCase(3, 2)] + [TestCase(4, 2)] + [TestCase(5, 2)] + [TestCase(6, 2)] + + [TestCase(1, 3)] + [TestCase(2, 3)] + [TestCase(3, 3)] + [TestCase(4, 3)] + [TestCase(5, 3)] + + [TestCase(1, 4)] + [TestCase(2, 4)] + [TestCase(3, 4)] + [TestCase(4, 4)] + + [TestCase(1, 5)] + [TestCase(2, 5)] + [TestCase(3, 5)] + + [TestCase(1, 6)] + [TestCase(2, 6)] + + [TestCase(1, 7)] + public void GivenFastBufferWriterContainingValue_WhenReadingPartialValue_ValueMatchesWhatWasWritten(int count, int offset) + { + var random = new Random(); + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) { - sbyte sb = (sbyte)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(sb); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(sb); - } - else + Assert.IsTrue(writer.TryBeginWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count, offset); + + var failMessage = $"TestReadingPartialValues failed with value {valueToTest}"; + WriteCheckBytes(ref writer, count, failMessage); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - RunObjectTypeTest(sb); + VerifyPositionAndLength(ref reader, writer.Length, failMessage); + Assert.IsTrue(reader.TryBeginRead(count + 2), "Reader denied read permission"); + + ulong mask = 0; + for (var i = 0; i < count; ++i) + { + mask = (mask << 8) | 0b11111111; + } + + mask <<= (offset * 8); + + reader.ReadPartialValue(out ulong result, count, offset); + Assert.AreEqual(valueToTest & mask, result & mask, failMessage); + VerifyCheckBytes(ref reader, count, failMessage); } } - else if (testType == typeof(short)) + } + + + [Test] + public unsafe void GivenFastBufferReaderInitializedFromFastBufferWriterContainingValue_WhenCallingToArray_ReturnedArrayMatchesContentOfWriter() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) { - short s = (short)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(s); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(s); - } - else + writer.TryBeginWrite(requiredSize); + writer.WriteValue(testStruct); + + var reader = new FastBufferReader(ref writer, Allocator.Temp); + using (reader) { - RunObjectTypeTest(s); + var array = reader.ToArray(); + var underlyingArray = writer.GetUnsafePtr(); + for (var i = 0; i < array.Length; ++i) + { + Assert.AreEqual(array[i], underlyingArray[i]); + } } } - else if (testType == typeof(ushort)) + } + + + [Test] + public void WhenCallingReadByteWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - ushort us = (ushort)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(us); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(us); - } - else - { - RunObjectTypeTest(us); - } + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); } - else if (testType == typeof(int)) + } + + [Test] + public void WhenCallingReadBytesWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + byte[] b = {0, 1, 2}; + using (emptyReader) { - int i = (int)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(i); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(i); - } - else - { - RunObjectTypeTest(i); - } + Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); } - else if (testType == typeof(uint)) + } + + [Test] + public void WhenCallingReadValueWithUnmanagedTypeWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - uint ui = (uint)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(ui); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(ui); - } - else - { - RunObjectTypeTest(ui); - } + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); } - else if (testType == typeof(long)) + } + + [Test] + public void WhenCallingReadValueWithByteArrayWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - long l = ((long)random.Next() << 32) + random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(l); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(l); - } - else - { - RunObjectTypeTest(l); - } + Assert.Throws(() => { emptyReader.ReadValue(out byte[] b); }); } - else if (testType == typeof(ulong)) + } + + [Test] + public void WhenCallingReadValueWithStringWithoutCallingTryBeingReadFirst_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(ul); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(ul); - } - else - { - RunObjectTypeTest(ul); - } + Assert.Throws(() => { emptyReader.ReadValue(out string s); }); } - else if (testType == typeof(bool)) + } + + [Test] + public void WhenCallingReadValueAfterCallingTryBeginWriteWithTooFewBytes_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(true); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(true); - } - else - { - RunObjectTypeTest(true); - } + emptyReader.TryBeginRead(sizeof(int) - 1); + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); } - else if (testType == typeof(char)) + } + + [Test] + public void WhenCallingReadBytePastBoundaryMarkedByTryBeginWrite_OverflowExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - char c = 'a'; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(c); - } - else - { - RunObjectTypeTest(c); - } + emptyReader.TryBeginRead(sizeof(int) - 1); + emptyReader.ReadByte(out byte b); + emptyReader.ReadByte(out b); + emptyReader.ReadByte(out b); + Assert.Throws(() => { emptyReader.ReadByte(out b); }); + } + } - c = '\u263a'; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(c); - } - else + [Test] + public void WhenCallingReadByteDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) + { + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeTest(c); + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); } } - else if (testType == typeof(float)) + } + + [Test] + public void WhenCallingReadBytesDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - float f = (float)random.NextDouble(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(f); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(f); - } - else + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeTest(f); + byte[] b = {0, 1, 2}; + Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); } } - else if (testType == typeof(double)) + } + + [Test] + public void WhenCallingReadValueWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - double d = random.NextDouble(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(d); - } - else if (writeType == WriteType.WriteSafe) + using (var context = emptyReader.EnterBitwiseContext()) { - RunTypeTestSafe(d); - } - else - { - RunObjectTypeTest(d); + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); } } - else if (testType == typeof(ByteEnum)) + } + + [Test] + public void WhenCallingReadValueWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - ByteEnum e = ByteEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeTest(e); + Assert.Throws(() => { emptyReader.ReadValue(out byte[] b); }); } } - else if (testType == typeof(SByteEnum)) + } + + [Test] + public void WhenCallingReadValueWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - SByteEnum e = SByteEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeTest(e); + Assert.Throws(() => { emptyReader.ReadValue(out string s); }); } } - else if (testType == typeof(ShortEnum)) - { - ShortEnum e = ShortEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(UShortEnum)) - { - UShortEnum e = UShortEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(IntEnum)) - { - IntEnum e = IntEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(UIntEnum)) - { - UIntEnum e = UIntEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(LongEnum)) - { - LongEnum e = LongEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(ULongEnum)) - { - ULongEnum e = ULongEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(Vector2)) - { - var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Vector3)) - { - var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Vector4)) - { - var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Quaternion)) - { - var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Color)) - { - var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Color32)) - { - var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Ray)) - { - var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Ray2D)) - { - var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), - new Vector2((float)random.NextDouble(), (float)random.NextDouble())); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else - { - Assert.Fail("No type handler was provided for this type in the test!"); - } } [Test] - public void TestReadingBasicArrays( - [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), - typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), - typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), - typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), - typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] - Type testType, - [Values] WriteType writeType) + public void WhenCallingReadByteSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - var random = new Random(); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - if (testType == typeof(byte)) - { - byte[] b = { - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(b); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(b); - } - else - { - RunObjectTypeArrayTest(b); - } - } - else if (testType == typeof(sbyte)) - { - sbyte[] sb = { - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(sb); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(sb); - } - else - { - RunObjectTypeArrayTest(sb); - } - } - else if (testType == typeof(short)) - { - short[] s = { - (short) random.Next(), - (short) random.Next(), - (short) random.Next(), - (short) random.Next(), - (short) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(s); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(s); - } - else - { - RunObjectTypeArrayTest(s); - } - } - else if (testType == typeof(ushort)) - { - ushort[] us = { - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(us); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(us); - } - else - { - RunObjectTypeArrayTest(us); - } - } - else if (testType == typeof(int)) - { - int[] i = { - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(i); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(i); - } - else - { - RunObjectTypeArrayTest(i); - } - } - else if (testType == typeof(uint)) - { - uint[] ui = { - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(ui); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(ui); - } - else - { - RunObjectTypeArrayTest(ui); - } - } - else if (testType == typeof(long)) - { - long[] l = { - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(l); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(l); - } - else - { - RunObjectTypeArrayTest(l); - } - } - else if (testType == typeof(ulong)) - { - ulong[] ul = { - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(ul); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(ul); - } - else - { - RunObjectTypeArrayTest(ul); - } - } - else if (testType == typeof(bool)) - { - bool[] b = { - true, - false, - true, - true, - false, - false, - true, - false, - true - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(b); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(b); - } - else - { - RunObjectTypeArrayTest(b); - } - } - else if (testType == typeof(char)) - { - char[] c = { - 'a', - '\u263a', - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(c); - } - else - { - RunObjectTypeArrayTest(c); - } - } - else if (testType == typeof(float)) - { - float[] f = { - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(f); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(f); - } - else - { - RunObjectTypeArrayTest(f); - } - } - else if (testType == typeof(double)) - { - double[] d = { - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(d); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(d); - } - else - { - RunObjectTypeArrayTest(d); - } - } - else if (testType == typeof(ByteEnum)) - { - ByteEnum[] e = { - ByteEnum.C, - ByteEnum.A, - ByteEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(SByteEnum)) - { - SByteEnum[] e = { - SByteEnum.C, - SByteEnum.A, - SByteEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(ShortEnum)) - { - ShortEnum[] e = { - ShortEnum.C, - ShortEnum.A, - ShortEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(UShortEnum)) - { - UShortEnum[] e = { - UShortEnum.C, - UShortEnum.A, - UShortEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(IntEnum)) - { - IntEnum[] e = { - IntEnum.C, - IntEnum.A, - IntEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(UIntEnum)) - { - UIntEnum[] e = { - UIntEnum.C, - UIntEnum.A, - UIntEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(LongEnum)) - { - LongEnum[] e = { - LongEnum.C, - LongEnum.A, - LongEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(ULongEnum)) - { - ULongEnum[] e = { - ULongEnum.C, - ULongEnum.A, - ULongEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(Vector2)) - { - var v = new[] - { - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Vector3)) - { - var v = new[] - { - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Vector4)) - { - var v = new[] - { - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Quaternion)) - { - var v = new[] - { - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Color)) + using (emptyReader) { - var v = new[] - { - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - }; - - if (writeType == WriteType.WriteDirect) + using (var context = emptyReader.EnterBitwiseContext()) { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); + Assert.Throws(() => { emptyReader.ReadByteSafe(out byte b); }); } } - else if (testType == typeof(Color32)) - { - var v = new[] - { - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - }; + } - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Ray)) - { - var v = new[] - { - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Ray2D)) + [Test] + public void WhenCallingReadBytesSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - var v = new[] - { - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else + using (var context = emptyReader.EnterBitwiseContext()) { - RunObjectTypeArrayTest(v); + byte[] b = {0, 1, 2}; + Assert.Throws(() => { emptyReader.ReadBytesSafe(ref b, 3); }); } } - else - { - Assert.Fail("No type handler was provided for this type in the test!"); - } } [Test] - public void TestReadingStruct() + public void WhenCallingReadValueSafeWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - RunTypeTest(GetTestStruct()); - } - - [Test] - public void TestReadingStructSafe() - { - RunTypeTestSafe(GetTestStruct()); - } + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - [Test] - public void TestReadingStructAsObjectWithRegisteredTypeTableSerializer() - { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => - { - reader.ReadValueSafe(out TestStruct value); - obj = value; - }; - try - { - RunObjectTypeTest(GetTestStruct()); - } - finally + using (emptyReader) { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); - SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + using (var context = emptyReader.EnterBitwiseContext()) + { + Assert.Throws(() => { emptyReader.ReadValueSafe(out int i); }); + } } } [Test] - public void TestReadingStructArray() + public void WhenCallingReadValueSafeWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunTypeArrayTest(arr); - } - - [Test] - public void TestReadingStructArraySafe() - { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunTypeArrayTestSafe(arr); - } + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - [Test] - public void TestReadingStructArrayAsObjectWithRegisteredTypeTableSerializer() - { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - SerializationTypeTable.Deserializers[typeof(TestStruct)] = (ref FastBufferReader reader, out object obj) => - { - reader.ReadValueSafe(out TestStruct value); - obj = value; - }; - try - { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunObjectTypeArrayTest(arr); - } - finally + using (emptyReader) { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); - SerializationTypeTable.Deserializers.Remove(typeof(TestStruct)); + using (var context = emptyReader.EnterBitwiseContext()) + { + Assert.Throws(() => { emptyReader.ReadValueSafe(out byte[] b); }); + } } } [Test] - public void TestReadingString() + public void WhenCallingReadValueSafeWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using (writer) + using (emptyReader) { - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); - writer.WriteValue(valueToTest); - - WriteCheckBytes(ref writer, serializedValueSize); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length); - - Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); - reader.ReadValue(out string result); - Assert.AreEqual(valueToTest, result); - - VerifyCheckBytes(ref reader, serializedValueSize); + Assert.Throws(() => { emptyReader.ReadValueSafe(out string s); }); } } } - + [Test] - public void TestReadingStringSafe() + public void WhenCallingReadByteAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); - - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(valueToTest); - - WriteCheckBytes(ref writer, serializedValueSize); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) - { - VerifyPositionAndLength(ref reader, writer.Length); - - reader.ReadValueSafe(out string result); - Assert.AreEqual(valueToTest, result); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - VerifyCheckBytes(ref reader, serializedValueSize); + using (emptyReader) + { + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) + { + context.ReadBit(out bool theBit); } + emptyReader.ReadByte(out byte theByte); } } [Test] - public void TestReadingStringAsObject() + public void WhenCallingReadBytesAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); - using (writer) + using (emptyReader) { - writer.WriteObject(valueToTest); - - WriteCheckBytes(ref writer, serializedValueSize + 1); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length); - - reader.ReadObject(out object result, typeof(string)); - Assert.AreEqual(valueToTest, result); - - VerifyCheckBytes(ref reader, serializedValueSize + 1); + context.ReadBit(out bool theBit); } + + byte[] theBytes = {0, 1, 2}; + emptyReader.ReadBytes(ref theBytes, 3); } } [Test] - public void TestReadingOneByteString() + public void WhenCallingReadValueWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using (writer) + using (emptyReader) { - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); - writer.WriteValue(valueToTest, true); - - WriteCheckBytes(ref writer, serializedValueSize); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length); - - Assert.IsTrue(reader.VerifyCanRead(serializedValueSize + 2), "Reader denied read permission"); - reader.ReadValue(out string result, true); - Assert.AreEqual(valueToTest, result); - - VerifyCheckBytes(ref reader, serializedValueSize); + context.ReadBit(out bool theBit); } + emptyReader.ReadValue(out int i); } } [Test] - public void TestReadingOneByteStringSafe() + public void WhenCallingReadValueWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); - using (writer) + using (emptyReader) { - writer.WriteValueSafe(valueToTest, true); - - WriteCheckBytes(ref writer, serializedValueSize); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length); - - reader.ReadValueSafe(out string result, true); - Assert.AreEqual(valueToTest, result); - - VerifyCheckBytes(ref reader, serializedValueSize); + context.ReadBit(out bool theBit); } + emptyReader.ReadValue(out byte[] theBytes); } } [Test] - public unsafe void TestReadingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) + public void WhenCallingReadValueWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var random = new Random(); - var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); - var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); - writer.WritePartialValue(valueToTest, count); - - var failMessage = $"TestReadingPartialValues failed with value {valueToTest}"; - WriteCheckBytes(ref writer, count, failMessage); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + using (emptyReader) + { + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length, failMessage); - Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - - ulong mask = 0; - for (var i = 0; i < count; ++i) - { - mask = (mask << 8) | 0b11111111; - } - - reader.ReadPartialValue(out ulong result, count); - Assert.AreEqual(valueToTest & mask, result & mask, failMessage); - VerifyCheckBytes(ref reader, count, failMessage); + context.ReadBit(out bool theBit); } + emptyReader.ReadValue(out string s); } } - + [Test] - public void TestReadingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong) - 2)] int count) + public void WhenCallingReadByteSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var random = new Random(); - var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); - var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - using (writer) + using (emptyReader) { - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); - writer.WritePartialValue(valueToTest, count, 2); - var failMessage = $"TestReadingPartialValuesWithOffsets failed with value {valueToTest}"; - WriteCheckBytes(ref writer, count, failMessage); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - VerifyPositionAndLength(ref reader, writer.Length, failMessage); - Assert.IsTrue(reader.VerifyCanRead(count + 2), "Reader denied read permission"); - - ulong mask = 0; - for (var i = 0; i < count; ++i) - { - mask = (mask << 8) | 0b11111111; - } - - mask <<= 16; - - reader.ReadPartialValue(out ulong result, count, 2); - Assert.AreEqual(valueToTest & mask, result & mask, failMessage); - VerifyCheckBytes(ref reader, count, failMessage); + context.ReadBit(out bool theBit); } + emptyReader.ReadByteSafe(out byte theByte); } } [Test] - public unsafe void TestToArray() + public void WhenCallingReadBytesSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var testStruct = GetTestStruct(); - var requiredSize = FastBufferWriter.GetWriteSize(testStruct); - var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - using (writer) + using (emptyReader) { - writer.VerifyCanWrite(requiredSize); - writer.WriteValue(testStruct); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - var array = reader.ToArray(); - var underlyingArray = writer.GetUnsafePtr(); - for (var i = 0; i < array.Length; ++i) - { - Assert.AreEqual(array[i], underlyingArray[i]); - } + context.ReadBit(out bool theBit); } + + byte[] theBytes = {0, 1, 2}; + emptyReader.ReadBytesSafe(ref theBytes, 3); } } [Test] - public void TestThrowingIfBoundsCheckingSkipped() + public void WhenCallingReadValueSafeWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var writer = new FastBufferWriter(100, Allocator.Temp); var nativeArray = new NativeArray(100, Allocator.Temp); - var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp, 0); - nativeArray.Dispose(); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); using (emptyReader) - using (writer) { - Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); - var bytes = new byte[] { 0, 1, 2 }; - Assert.Throws(() => { emptyReader.ReadBytes(ref bytes, bytes.Length); }); - int i = 1; - Assert.Throws(() => { emptyReader.ReadValue(out int i); }); - Assert.Throws(() => { emptyReader.ReadValue(out bytes); }); - Assert.Throws(() => { emptyReader.ReadValue(out string s); }); - - writer.VerifyCanWrite(sizeof(int) - 1); - writer.WriteByte(1); - writer.WriteByte(2); - writer.WriteByte(3); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - Assert.Throws(() => { reader.ReadValue(out int i); }); - Assert.Throws(() => { reader.ReadValue(out byte b); }); - Assert.IsTrue(reader.VerifyCanRead(3)); - reader.ReadByte(out byte b); - reader.ReadByte(out b); - reader.ReadByte(out b); - Assert.Throws(() => { reader.ReadValue(out byte b); }); + context.ReadBit(out bool theBit); } + emptyReader.ReadValueSafe(out int i); } } [Test] - public void TestThrowingIfDoingBytewiseReadsDuringBitwiseContext() + public void WhenCallingReadValueSafeWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var writer = new FastBufferWriter(100, Allocator.Temp); + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - using (writer) + using (emptyReader) { - writer.VerifyCanWrite(100); - var bytes = new byte[] { 0, 1, 2 }; - int i = 1; - writer.WriteByte(1); - writer.WriteBytes(bytes, bytes.Length); - writer.WriteValue(i); - writer.WriteValue(bytes); - writer.WriteValue(""); + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) + { + context.ReadBit(out bool theBit); + } + emptyReader.ReadValueSafe(out byte[] theBytes); + } + } - writer.WriteByteSafe(1); - writer.WriteBytesSafe(bytes, bytes.Length); - writer.WriteValueSafe(i); - writer.WriteValueSafe(bytes); - writer.WriteValueSafe(""); + [Test] + public void WhenCallingReadValueSafeWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() + { + var nativeArray = new NativeArray(100, Allocator.Temp); + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + using (emptyReader) + { + emptyReader.TryBeginRead(100); + using (var context = emptyReader.EnterBitwiseContext()) { - Assert.IsTrue(reader.VerifyCanRead(writer.Length)); - using (var context = reader.EnterBitwiseContext()) - { - Assert.Throws(() => { reader.ReadByte(out byte b); }); - Assert.Throws(() => { reader.ReadBytes(ref bytes, bytes.Length); }); - Assert.Throws(() => { reader.ReadValue(out i); }); - Assert.Throws(() => { reader.ReadValue(out bytes); }); - Assert.Throws(() => { reader.ReadValue(out string s); }); - - Assert.Throws(() => { reader.ReadByteSafe(out byte b); }); - Assert.Throws(() => - { - reader.ReadBytesSafe(ref bytes, bytes.Length); - }); - Assert.Throws(() => { reader.ReadValueSafe(out i); }); - Assert.Throws(() => { reader.ReadValueSafe(out bytes); }); - Assert.Throws(() => { reader.ReadValueSafe(out string s); }); - } + context.ReadBit(out bool theBit); } + emptyReader.ReadValueSafe(out string s); } } [Test] - public void TestVerifyCanReadIsRelativeToPositionAndNotAllowedReadPosition() + public void WhenCallingTryBeginRead_TheAllowedReadPositionIsMarkedRelativeToCurrentPosition() { var nativeArray = new NativeArray(100, Allocator.Temp); - var reader = new FastBufferReader(nativeArray, Allocator.Temp, 100); - nativeArray.Dispose(); - using (reader) + var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); + + using (emptyReader) { - reader.VerifyCanRead(100); - reader.ReadByte(out byte b); - reader.VerifyCanRead(1); - reader.ReadByte(out b); - Assert.Throws(() => { reader.ReadByte(out b); }); + emptyReader.TryBeginRead(100); + emptyReader.ReadByte(out byte b); + emptyReader.TryBeginRead(1); + emptyReader.ReadByte(out b); + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); } } [Test] - public void TestSeeking() + public void WhenReadingAfterSeeking_TheNewReadComesFromTheCorrectPosition() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) @@ -1930,85 +893,47 @@ public void TestSeeking() } } - private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, - NetworkObject networkObject); - private void RunGameObjectTest(GameObjectTestDelegate testCode) - { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkManager.SetSingleton(); - networkObject.NetworkManagerOwner = networkManager; - - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, - // Set transport - NetworkTransport = obj.AddComponent() - }; - - networkManager.StartServer(); - - try - { - testCode(obj, networkBehaviour, networkObject); - } - finally - { - UnityEngine.Object.DestroyImmediate(obj); - networkManager.StopServer(); - } - } - [Test] - public void TestNetworkBehaviour() + public void GivenFastBufferWriterWithNetworkObjectWritten_WhenReadingNetworkObject_TheSameObjectIsRetrieved([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject)+1, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); - - writer.WriteValue(networkBehaviour); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + switch (writeType) { - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkBehaviourWriteSize())); - reader.ReadValue(out NetworkBehaviour result); - Assert.AreSame(result, networkBehaviour); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkObject))); + writer.WriteValue(networkObject); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(networkObject); + break; + case WriteType.WriteAsObject: + writer.WriteObject(networkObject); + break; } - } - }); - } - - [Test] - public void TestNetworkObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); - - writer.WriteValue(networkObject); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetNetworkObjectWriteSize())); - reader.ReadValue(out NetworkObject result); + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetNetworkObjectWriteSize())); + NetworkObject result = null; + switch (writeType) + { + case WriteType.WriteDirect: + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(networkObject))); + reader.ReadValue(out result); + break; + case WriteType.WriteSafe: + reader.ReadValueSafe(out result); + break; + case WriteType.WriteAsObject: + reader.ReadObject(out object resultObj, typeof(NetworkObject)); + result = (NetworkObject) resultObj; + break; + } Assert.AreSame(result, networkObject); } } @@ -2016,113 +941,93 @@ public void TestNetworkObject() } [Test] - public void TestGameObject() + public void GivenFastBufferWriterWithGameObjectWritten_WhenReadingGameObject_TheSameObjectIsRetrieved([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj)+1, Allocator.Temp); using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); - - writer.WriteValue(obj); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + switch (writeType) { - Assert.IsTrue(reader.VerifyCanRead(FastBufferWriter.GetGameObjectWriteSize())); - reader.ReadValue(out GameObject result); - Assert.AreSame(result, obj); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(obj))); + writer.WriteValue(obj); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(obj); + break; + case WriteType.WriteAsObject: + writer.WriteObject(obj); + break; } - } - }); - } - - [Test] - public void TestNetworkBehaviourSafe() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(networkBehaviour); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - reader.ReadValueSafe(out NetworkBehaviour result); - Assert.AreSame(result, networkBehaviour); + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetGameObjectWriteSize())); + GameObject result = null; + switch (writeType) + { + case WriteType.WriteDirect: + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(obj))); + reader.ReadValue(out result); + break; + case WriteType.WriteSafe: + reader.ReadValueSafe(out result); + break; + case WriteType.WriteAsObject: + reader.ReadObject(out object resultObj, typeof(GameObject)); + result = (GameObject) resultObj; + break; + } + Assert.AreSame(result, obj); } } }); } [Test] - public void TestNetworkObjectSafe() + public void GivenFastBufferWriterWithNetworkBehaviourWritten_WhenReadingNetworkBehaviour_TheSameObjectIsRetrieved([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+1, Allocator.Temp); using (writer) { - writer.WriteValueSafe(networkObject); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp); - using (reader) + switch (writeType) { - reader.ReadValueSafe(out NetworkObject result); - Assert.AreSame(result, networkObject); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); + writer.WriteValue(networkBehaviour); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(networkBehaviour); + break; + case WriteType.WriteAsObject: + writer.WriteObject(networkBehaviour); + break; } - } - }); - } - - [Test] - public void TestGameObjectSafe() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(obj); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); var reader = new FastBufferReader(ref writer, Allocator.Temp); using (reader) { - reader.ReadValueSafe(out GameObject result); - Assert.AreSame(result, obj); - } - } - }); - } - - [Test] - public void TestNetworkBehaviourAsObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); - using (writer) - { - writer.WriteObject(networkBehaviour); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); - using (reader) - { - reader.ReadObject(out object result, typeof(NetworkBehaviour)); + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetNetworkBehaviourWriteSize())); + NetworkBehaviour result = null; + switch (writeType) + { + case WriteType.WriteDirect: + Assert.IsTrue(reader.TryBeginRead(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); + reader.ReadValue(out result); + break; + case WriteType.WriteSafe: + reader.ReadValueSafe(out result); + break; + case WriteType.WriteAsObject: + reader.ReadObject(out object resultObj, typeof(NetworkBehaviour)); + result = (NetworkBehaviour) resultObj; + break; + } Assert.AreSame(result, networkBehaviour); } } @@ -2130,59 +1035,13 @@ public void TestNetworkBehaviourAsObject() } [Test] - public void TestNetworkObjectAsObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject) + 1, Allocator.Temp); - using (writer) - { - writer.WriteObject(networkObject); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); - using (reader) - { - reader.ReadObject(out object result, typeof(NetworkObject)); - Assert.AreSame(result, networkObject); - } - } - }); - } - - [Test] - public void TestGameObjectAsObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj) + 1, Allocator.Temp); - using (writer) - { - writer.WriteObject(obj); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); - - var reader = new FastBufferReader(ref writer, Allocator.Temp, writer.Length); - using (reader) - { - reader.ReadObject(out object result, typeof(GameObject)); - Assert.AreSame(result, obj); - } - } - }); - } - - [Test] - public void TestVerifyInternalDoesntReduceAllowedWritePoint() + public void WhenCallingTryBeginReadInternal_AllowedReadPositionDoesNotMoveBackward() { var reader = new FastBufferReader(new NativeArray(100, Allocator.Temp), Allocator.Temp); using (reader) { - reader.VerifyCanRead(25); - reader.VerifyCanReadInternal(5); + reader.TryBeginRead(25); + reader.TryBeginReadInternal(5); Assert.AreEqual(reader.AllowedReadMark, 25); } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 8fbe632431..37e93eb59d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1,94 +1,19 @@ using System; -using System.Collections.Generic; using NUnit.Framework; -using NUnit.Framework.Internal; using Unity.Collections; using Unity.Multiplayer.Netcode; using UnityEngine; -using UnityEngine.SceneManagement; using Random = System.Random; namespace Unity.Netcode.EditorTests { - public class FastBufferWriterTests + public class FastBufferWriterTests : BaseFastBufferReaderWriterTest { - #region Test Types - private enum ByteEnum : byte - { - A, - B, - C - }; - private enum SByteEnum : sbyte - { - A, - B, - C - }; - private enum ShortEnum : short - { - A, - B, - C - }; - private enum UShortEnum : ushort - { - A, - B, - C - }; - private enum IntEnum : int - { - A, - B, - C - }; - private enum UIntEnum : uint - { - A, - B, - C - }; - private enum LongEnum : long - { - A, - B, - C - }; - private enum ULongEnum : ulong - { - A, - B, - C - }; - - private struct TestStruct - { - public byte A; - public short B; - public ushort C; - public int D; - public uint E; - public long F; - public ulong G; - public bool H; - public char I; - public float J; - public double K; - } - - public enum WriteType - { - WriteDirect, - WriteSafe, - WriteAsObject - } - #endregion #region Common Checks private void WriteCheckBytes(ref FastBufferWriter writer, int writeSize, string failMessage = "") { - Assert.IsTrue(writer.VerifyCanWrite(2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(2), "Writer denied write permission"); writer.WriteValue((byte)0x80); Assert.AreEqual(writeSize + 1, writer.Position, failMessage); Assert.AreEqual(writeSize + 1, writer.Length, failMessage); @@ -142,7 +67,7 @@ private unsafe void CommonChecks(ref FastBufferWriter writer, T valueToTest, #endregion #region Generic Checks - private unsafe void RunTypeTest(T valueToTest) where T : unmanaged + protected override unsafe void RunTypeTest(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var alternateWriteSize = FastBufferWriter.GetWriteSize(); @@ -154,7 +79,7 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged using (writer) { - Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission"); var failMessage = $"RunTypeTest failed with type {typeof(T)} and value {valueToTest}"; @@ -163,7 +88,7 @@ private unsafe void RunTypeTest(T valueToTest) where T : unmanaged CommonChecks(ref writer, valueToTest, writeSize, failMessage); } } - private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged + protected override unsafe void RunTypeTestSafe(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -180,7 +105,7 @@ private unsafe void RunTypeTestSafe(T valueToTest) where T : unmanaged } } - private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged + protected override unsafe void RunObjectTypeTest(T valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -196,7 +121,7 @@ private unsafe void RunObjectTypeTest(T valueToTest) where T : unmanaged } } - private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T : unmanaged + private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T: unmanaged { int* sizeValue = (int*)(unsafePtr + offset); Assert.AreEqual(value.Length, *sizeValue); @@ -211,7 +136,7 @@ private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offse } } - private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged + protected override unsafe void RunTypeArrayTest(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -219,7 +144,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged { Assert.AreEqual(sizeof(int) + sizeof(T) * valueToTest.Length, writeSize); - Assert.IsTrue(writer.VerifyCanWrite(writeSize + 2), "Writer denied write permission"); + Assert.IsTrue(writer.TryBeginWrite(writeSize + 2), "Writer denied write permission"); writer.WriteValue(valueToTest); VerifyPositionAndLength(ref writer, writeSize); @@ -233,7 +158,7 @@ private unsafe void RunTypeArrayTest(T[] valueToTest) where T : unmanaged } } - private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged + protected override unsafe void RunTypeArrayTestSafe(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); var writer = new FastBufferWriter(writeSize + 2, Allocator.Temp); @@ -254,7 +179,7 @@ private unsafe void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged } } - private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged + protected override unsafe void RunObjectTypeArrayTest(T[] valueToTest) { var writeSize = FastBufferWriter.GetWriteSize(valueToTest); // Extra byte for WriteObject adding isNull flag @@ -278,1574 +203,620 @@ private unsafe void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanag } #endregion - #region Helpers - private TestStruct GetTestStruct() - { - var random = new Random(); - var testStruct = new TestStruct - { - A = (byte)random.Next(), - B = (short)random.Next(), - C = (ushort)random.Next(), - D = (int)random.Next(), - E = (uint)random.Next(), - F = ((long)random.Next() << 32) + random.Next(), - G = ((ulong)random.Next() << 32) + (ulong)random.Next(), - H = true, - I = '\u263a', - J = (float)random.NextDouble(), - K = random.NextDouble(), - }; - - return testStruct; + #region Tests + [Test, Description("Tests ")] + public void WhenWritingUnmanagedType_ValueIsWrittenCorrectly( + [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), + typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), + typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), + typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))] + Type testType, + [Values] WriteType writeType) + { + BaseTypeTest(testType, writeType); } - #endregion - #region Tests [Test] - public void TestWritingBasicTypes( + public void WhenWritingArrayOfUnmanagedElementType_ArrayIsWrittenCorrectly( [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), - typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] + typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D), typeof(TestStruct))] Type testType, [Values] WriteType writeType) { - var random = new Random(); + BaseArrayTypeTest(testType, writeType); + } + + [TestCase(false, WriteType.WriteDirect)] + [TestCase(false, WriteType.WriteSafe)] + [TestCase(false, WriteType.WriteAsObject)] + [TestCase(true, WriteType.WriteDirect)] + [TestCase(true, WriteType.WriteSafe)] + public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, WriteType writeType) + { + string valueToTest = "Hello, I am a test string!"; - if (testType == typeof(byte)) + var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, oneByteChars); + + var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); + using (writer) { - byte b = (byte)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(b); - } - else if (writeType == WriteType.WriteSafe) + var offset = 0; + switch (writeType) { - RunTypeTestSafe(b); + case WriteType.WriteDirect: + Assert.IsTrue(writer.TryBeginWrite(serializedValueSize + 2), "Writer denied write permission"); + writer.WriteValue(valueToTest, oneByteChars); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(valueToTest, oneByteChars); + break; + case WriteType.WriteAsObject: + writer.WriteObject(valueToTest); + // account for isNull byte + offset = sizeof(byte); + break; + } - else + + VerifyPositionAndLength(ref writer, serializedValueSize+offset); + WriteCheckBytes(ref writer, serializedValueSize+offset); + + int* sizeValue = (int*)(writer.GetUnsafePtr() + offset); + Assert.AreEqual(valueToTest.Length, *sizeValue); + + fixed (char* asCharPointer = valueToTest) { - RunObjectTypeTest(b); + if(oneByteChars) + { + byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int) + offset; + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); + } + + } + else + { + char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int) + offset); + for (var i = 0; i < valueToTest.Length; ++i) + { + Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); + } + } } + + var underlyingArray = writer.ToArray(); + VerifyCheckBytes(underlyingArray, serializedValueSize+offset); } - else if (testType == typeof(sbyte)) + } + + [TestCase(1, 0)] + [TestCase(2, 0)] + [TestCase(3, 0)] + [TestCase(4, 0)] + [TestCase(5, 0)] + [TestCase(6, 0)] + [TestCase(7, 0)] + [TestCase(8, 0)] + + [TestCase(1, 1)] + [TestCase(2, 1)] + [TestCase(3, 1)] + [TestCase(4, 1)] + [TestCase(5, 1)] + [TestCase(6, 1)] + [TestCase(7, 1)] + + [TestCase(1, 2)] + [TestCase(2, 2)] + [TestCase(3, 2)] + [TestCase(4, 2)] + [TestCase(5, 2)] + [TestCase(6, 2)] + + [TestCase(1, 3)] + [TestCase(2, 3)] + [TestCase(3, 3)] + [TestCase(4, 3)] + [TestCase(5, 3)] + + [TestCase(1, 4)] + [TestCase(2, 4)] + [TestCase(3, 4)] + [TestCase(4, 4)] + + [TestCase(1, 5)] + [TestCase(2, 5)] + [TestCase(3, 5)] + + [TestCase(1, 6)] + [TestCase(2, 6)] + + [TestCase(1, 7)] + public unsafe void WhenWritingPartialValueWithCountAndOffset_ValueIsWrittenCorrectly(int count, int offset) + { + var random = new Random(); + var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); + var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + using (writer) { - sbyte sb = (sbyte)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(sb); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(sb); - } - else + + Assert.IsTrue(writer.TryBeginWrite(count + 2), "Writer denied write permission"); + writer.WritePartialValue(valueToTest, count, offset); + + var failMessage = $"TestWritingPartialValues failed with value {valueToTest}"; + VerifyPositionAndLength(ref writer, count, failMessage); + WriteCheckBytes(ref writer, count, failMessage); + var underlyingArray = writer.ToArray(); + VerifyBytewiseEquality(valueToTest, underlyingArray, offset, 0, count, failMessage); + VerifyCheckBytes(underlyingArray, count, failMessage); + + ulong mask = 0; + for (var i = 0; i < count; ++i) { - RunObjectTypeTest(sb); + mask = (mask << 8) | 0b11111111; } + + ulong* checkValue = (ulong*)writer.GetUnsafePtr(); + Assert.AreEqual((valueToTest >> (offset * 8)) & mask, *checkValue & mask); } - else if (testType == typeof(short)) + } + + [Test] + public void WhenCallingToArray_ReturnedArrayContainsCorrectData() + { + var testStruct = GetTestStruct(); + var requiredSize = FastBufferWriter.GetWriteSize(testStruct); + var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + + using (writer) { - short s = (short)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(s); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(s); - } - else + writer.TryBeginWrite(requiredSize); + writer.WriteValue(testStruct); + var array = writer.ToArray(); + var underlyingArray = writer.ToArray(); + for (var i = 0; i < array.Length; ++i) { - RunObjectTypeTest(s); + Assert.AreEqual(array[i], underlyingArray[i]); } } - else if (testType == typeof(ushort)) + } + + [Test] + public void WhenCallingWriteByteWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - ushort us = (ushort)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(us); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(us); - } - else - { - RunObjectTypeTest(us); - } + Assert.Throws(() => { writer.WriteByte(1); }); } - else if (testType == typeof(int)) + } + + [Test] + public void WhenCallingWriteBytesWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - int i = (int)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(i); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(i); - } - else - { - RunObjectTypeTest(i); - } + var bytes = new byte[] { 0, 1, 2 }; + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } - else if (testType == typeof(uint)) + } + + [Test] + public void WhenCallingWriteValueWithUnmanagedTypeWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - uint ui = (uint)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(ui); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(ui); - } - else - { - RunObjectTypeTest(ui); - } + int i = 1; + Assert.Throws(() => { writer.WriteValue(i); }); } - else if (testType == typeof(long)) + } + + [Test] + public void WhenCallingWriteValueWithByteArrayWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - long l = ((long)random.Next() << 32) + random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(l); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(l); - } - else - { - RunObjectTypeTest(l); - } + var bytes = new byte[] { 0, 1, 2 }; + Assert.Throws(() => { writer.WriteValue(bytes); }); } - else if (testType == typeof(ulong)) + } + + [Test] + public void WhenCallingWriteValueWithStringWithoutCallingTryBeingWriteFirst_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - ulong ul = ((ulong)random.Next() << 32) + (ulong)random.Next(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(ul); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(ul); - } - else - { - RunObjectTypeTest(ul); - } + Assert.Throws(() => { writer.WriteValue(""); }); } - else if (testType == typeof(bool)) + } + + [Test] + public void WhenCallingWriteValueAfterCallingTryBeginWriteWithTooFewBytes_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(true); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(true); - } - else - { - RunObjectTypeTest(true); - } + int i = 0; + writer.TryBeginWrite(sizeof(int) - 1); + Assert.Throws(() => { writer.WriteValue(i); }); } - else if (testType == typeof(char)) - { - char c = 'a'; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(c); - } - else - { - RunObjectTypeTest(c); - } + } - c = '\u263a'; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(c); - } - else - { - RunObjectTypeTest(c); - } + [Test] + public void WhenCallingWriteBytePastBoundaryMarkedByTryBeginWrite_OverflowExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + writer.TryBeginWrite(sizeof(int) - 1); + writer.WriteByte(1); + writer.WriteByte(2); + writer.WriteByte(3); + Assert.Throws(() => { writer.WriteByte(4); }); } - else if (testType == typeof(float)) + } + + [Test] + public void WhenCallingWriteByteDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - float f = (float)random.NextDouble(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(f); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(f); - } - else + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeTest(f); + Assert.Throws(() => { writer.WriteByte(1); }); } } - else if (testType == typeof(double)) + } + + [Test] + public void WhenCallingWriteBytesDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - double d = random.NextDouble(); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(d); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(d); - } - else + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeTest(d); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } } - else if (testType == typeof(ByteEnum)) + } + + [Test] + public void WhenCallingWriteValueWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - ByteEnum e = ByteEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else + writer.TryBeginWrite(100); + int i = 1; + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeTest(e); + Assert.Throws(() => { writer.WriteValue(i); }); } } - else if (testType == typeof(SByteEnum)) + } + + [Test] + public void WhenCallingWriteValueWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - SByteEnum e = SByteEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeTest(e); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } } - else if (testType == typeof(ShortEnum)) + } + + [Test] + public void WhenCallingWriteValueWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - ShortEnum e = ShortEnum.C; - if (writeType == WriteType.WriteDirect) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + int i = 1; + using (var context = writer.EnterBitwiseContext()) { - RunTypeTest(e); + Assert.Throws(() => { writer.WriteValue(""); }); } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(UShortEnum)) - { - UShortEnum e = UShortEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(IntEnum)) - { - IntEnum e = IntEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(UIntEnum)) - { - UIntEnum e = UIntEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(LongEnum)) - { - LongEnum e = LongEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(ULongEnum)) - { - ULongEnum e = ULongEnum.C; - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(e); - } - else - { - RunObjectTypeTest(e); - } - } - else if (testType == typeof(Vector2)) - { - var v = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Vector3)) - { - var v = new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Vector4)) - { - var v = new Vector4((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Quaternion)) - { - var v = new Quaternion((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Color)) - { - var v = new Color((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Color32)) - { - var v = new Color32((byte)random.Next(), (byte)random.Next(), (byte)random.Next(), (byte)random.Next()); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Ray)) - { - var v = new Ray( - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()), - new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble())); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else if (testType == typeof(Ray2D)) - { - var v = new Ray2D( - new Vector2((float)random.NextDouble(), (float)random.NextDouble()), - new Vector2((float)random.NextDouble(), (float)random.NextDouble())); - if (writeType == WriteType.WriteDirect) - { - RunTypeTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeTestSafe(v); - } - else - { - RunObjectTypeTest(v); - } - } - else - { - Assert.Fail("No type handler was provided for this type in the test!"); } } [Test] - public void TestWritingBasicArrays( - [Values(typeof(byte), typeof(sbyte), typeof(short), typeof(ushort), typeof(int), typeof(uint), - typeof(long), typeof(ulong), typeof(bool), typeof(char), typeof(float), typeof(double), - typeof(ByteEnum), typeof(SByteEnum), typeof(ShortEnum), typeof(UShortEnum), typeof(IntEnum), - typeof(UIntEnum), typeof(LongEnum), typeof(ULongEnum), typeof(Vector2), typeof(Vector3), typeof(Vector4), - typeof(Quaternion), typeof(Color), typeof(Color32), typeof(Ray), typeof(Ray2D))] - Type testType, - [Values] WriteType writeType) + public void WhenCallingWriteByteSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - var random = new Random(); + var writer = new FastBufferWriter(100, Allocator.Temp); - if (testType == typeof(byte)) - { - byte[] b = { - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next(), - (byte) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(b); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(b); - } - else - { - RunObjectTypeArrayTest(b); - } - } - else if (testType == typeof(sbyte)) - { - sbyte[] sb = { - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next(), - (sbyte) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(sb); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(sb); - } - else - { - RunObjectTypeArrayTest(sb); - } - } - else if (testType == typeof(short)) - { - short[] s = { - (short) random.Next(), - (short) random.Next(), - (short) random.Next(), - (short) random.Next(), - (short) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(s); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(s); - } - else - { - RunObjectTypeArrayTest(s); - } - } - else if (testType == typeof(ushort)) - { - ushort[] us = { - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next(), - (ushort) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(us); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(us); - } - else - { - RunObjectTypeArrayTest(us); - } - } - else if (testType == typeof(int)) - { - int[] i = { - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next(), - random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(i); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(i); - } - else - { - RunObjectTypeArrayTest(i); - } - } - else if (testType == typeof(uint)) - { - uint[] ui = { - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next(), - (uint) random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(ui); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(ui); - } - else - { - RunObjectTypeArrayTest(ui); - } - } - else if (testType == typeof(long)) - { - long[] l = { - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next(), - ((long)random.Next() << 32) + (long)random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(l); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(l); - } - else - { - RunObjectTypeArrayTest(l); - } - } - else if (testType == typeof(ulong)) - { - ulong[] ul = { - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next(), - ((ulong)random.Next() << 32) + (ulong)random.Next() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(ul); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(ul); - } - else - { - RunObjectTypeArrayTest(ul); - } - } - else if (testType == typeof(bool)) - { - bool[] b = { - true, - false, - true, - true, - false, - false, - true, - false, - true - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(b); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(b); - } - else - { - RunObjectTypeArrayTest(b); - } - } - else if (testType == typeof(char)) - { - char[] c = { - 'a', - '\u263a', - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(c); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(c); - } - else - { - RunObjectTypeArrayTest(c); - } - } - else if (testType == typeof(float)) - { - float[] f = { - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble(), - (float)random.NextDouble() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(f); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(f); - } - else - { - RunObjectTypeArrayTest(f); - } - } - else if (testType == typeof(double)) - { - double[] d = { - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble(), - random.NextDouble() - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(d); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(d); - } - else - { - RunObjectTypeArrayTest(d); - } - } - else if (testType == typeof(ByteEnum)) - { - ByteEnum[] e = { - ByteEnum.C, - ByteEnum.A, - ByteEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(SByteEnum)) - { - SByteEnum[] e = { - SByteEnum.C, - SByteEnum.A, - SByteEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(ShortEnum)) - { - ShortEnum[] e = { - ShortEnum.C, - ShortEnum.A, - ShortEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(UShortEnum)) - { - UShortEnum[] e = { - UShortEnum.C, - UShortEnum.A, - UShortEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(IntEnum)) - { - IntEnum[] e = { - IntEnum.C, - IntEnum.A, - IntEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(UIntEnum)) - { - UIntEnum[] e = { - UIntEnum.C, - UIntEnum.A, - UIntEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(LongEnum)) - { - LongEnum[] e = { - LongEnum.C, - LongEnum.A, - LongEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(ULongEnum)) - { - ULongEnum[] e = { - ULongEnum.C, - ULongEnum.A, - ULongEnum.B - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(e); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(e); - } - else - { - RunObjectTypeArrayTest(e); - } - } - else if (testType == typeof(Vector2)) - { - var v = new[] - { - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Vector3)) - { - var v = new[] - { - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Vector4)) - { - var v = new[] - { - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector4((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - }; - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Quaternion)) - { - var v = new[] - { - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - new Quaternion((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble(), (float) random.NextDouble()), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Color)) + using (writer) { - var v = new[] - { - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Color((float) random.NextDouble(), (float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeArrayTest(v); + Assert.Throws(() => { writer.WriteByteSafe(1); }); } } - else if (testType == typeof(Color32)) - { - var v = new[] - { - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - new Color32((byte) random.Next(), (byte) random.Next(), (byte) random.Next(), (byte) random.Next()), - }; + } - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Ray)) - { - var v = new[] - { - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - new Ray( - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble()), - new Vector3((float) random.NextDouble(), (float) random.NextDouble(), - (float) random.NextDouble())), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else - { - RunObjectTypeArrayTest(v); - } - } - else if (testType == typeof(Ray2D)) + [Test] + public void WhenCallingWriteBytesSafeDuringBitwiseContext_InvalidOperationExceptionIsThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) { - var v = new[] - { - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - new Ray2D( - new Vector2((float) random.NextDouble(), (float) random.NextDouble()), - new Vector2((float) random.NextDouble(), (float) random.NextDouble())), - }; - - if (writeType == WriteType.WriteDirect) - { - RunTypeArrayTest(v); - } - else if (writeType == WriteType.WriteSafe) - { - RunTypeArrayTestSafe(v); - } - else + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - RunObjectTypeArrayTest(v); + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); } } - else - { - Assert.Fail("No type handler was provided for this type in the test!"); - } - } - - [Test] - public void TestWritingStruct() - { - RunTypeTest(GetTestStruct()); } [Test] - public void TestWritingStructSafe() + public void WhenCallingWriteValueSafeWithUnmanagedTypeDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - RunTypeTestSafe(GetTestStruct()); - } + var writer = new FastBufferWriter(100, Allocator.Temp); - [Test] - public void TestWritingStructAsObjectWithRegisteredTypeTableSerializer() - { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - try - { - RunObjectTypeTest(GetTestStruct()); - } - finally + using (writer) { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + writer.TryBeginWrite(100); + int i = 1; + using (var context = writer.EnterBitwiseContext()) + { + Assert.Throws(() => { writer.WriteValueSafe(i); }); + } } } [Test] - public void TestWritingStructArray() + public void WhenCallingWriteValueSafeWithByteArrayDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunTypeArrayTest(arr); - } - - [Test] - public void TestWritingStructArraySafe() - { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunTypeArrayTestSafe(arr); - } + var writer = new FastBufferWriter(100, Allocator.Temp); - [Test] - public void TestWritingStructArrayAsObjectWithRegisteredTypeTableSerializer() - { - SerializationTypeTable.Serializers[typeof(TestStruct)] = (ref FastBufferWriter writer, object obj) => - { - writer.WriteValueSafe((TestStruct)obj); - }; - try - { - TestStruct[] arr = { - GetTestStruct(), - GetTestStruct(), - GetTestStruct(), - }; - RunObjectTypeArrayTest(arr); - } - finally + using (writer) { - SerializationTypeTable.Serializers.Remove(typeof(TestStruct)); + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) + { + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); + } } } [Test] - public unsafe void TestWritingString() + public void WhenCallingWriteValueSafeWithStringDuringBitwiseContext_InvalidOperationExceptionIsThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var writer = new FastBufferWriter(100, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using (writer) { - - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); - writer.WriteValue(valueToTest); - - VerifyPositionAndLength(ref writer, serializedValueSize); - WriteCheckBytes(ref writer, serializedValueSize); - - int* sizeValue = (int*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int)); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); - } + Assert.Throws(() => { writer.WriteValueSafe(""); }); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize); } } [Test] - public unsafe void TestWritingStringSafe() + public void WhenCallingWriteByteAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var writer = new FastBufferWriter(100, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using (writer) { - - writer.WriteValueSafe(valueToTest); - - VerifyPositionAndLength(ref writer, serializedValueSize); - WriteCheckBytes(ref writer, serializedValueSize); - - int* sizeValue = (int*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - char* underlyingCharArray = (char*)((byte*)writer.GetUnsafePtr() + sizeof(int)); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); - } + context.WriteBit(true); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize); + writer.WriteByte(1); } } [Test] - public unsafe void TestWritingStringAsObject() + public void WhenCallingWriteBytesAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; - - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest); + var writer = new FastBufferWriter(100, Allocator.Temp); - var writer = new FastBufferWriter(serializedValueSize + 3, Allocator.Temp); using (writer) { - - writer.WriteObject(valueToTest); - - VerifyPositionAndLength(ref writer, serializedValueSize + sizeof(byte)); - WriteCheckBytes(ref writer, serializedValueSize + sizeof(byte)); - - int* sizeValue = (int*)(writer.GetUnsafePtr() + sizeof(byte)); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - char* underlyingCharArray = (char*)(writer.GetUnsafePtr() + sizeof(int) + sizeof(byte)); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual(asCharPointer[i], underlyingCharArray[i]); - } + context.WriteBit(true); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize + sizeof(byte)); + writer.WriteBytes(bytes, bytes.Length); } } [Test] - public unsafe void TestWritingOneByteString() + public void WhenCallingWriteValueWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; + var writer = new FastBufferWriter(100, Allocator.Temp); - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using (writer) { - - Assert.IsTrue(writer.VerifyCanWrite(serializedValueSize + 2), "Writer denied write permission"); - writer.WriteValue(valueToTest, true); - - VerifyPositionAndLength(ref writer, serializedValueSize); - WriteCheckBytes(ref writer, serializedValueSize); - - int* sizeValue = (int*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + int i = 1; + using (var context = writer.EnterBitwiseContext()) { - byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); - } + context.WriteBit(true); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize); + writer.WriteValue(i); } } [Test] - public unsafe void TestWritingOneByteStringSafe() + public void WhenCallingWriteValueWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - string valueToTest = "Hello, I am a test string!"; + var writer = new FastBufferWriter(100, Allocator.Temp); - var serializedValueSize = FastBufferWriter.GetWriteSize(valueToTest, true); - var writer = new FastBufferWriter(serializedValueSize + 2, Allocator.Temp); using (writer) { - - writer.WriteValueSafe(valueToTest, true); - - VerifyPositionAndLength(ref writer, serializedValueSize); - WriteCheckBytes(ref writer, serializedValueSize); - - int* sizeValue = (int*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest.Length, *sizeValue); - - fixed (char* asCharPointer = valueToTest) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + int i = 1; + using (var context = writer.EnterBitwiseContext()) { - byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int); - for (var i = 0; i < valueToTest.Length; ++i) - { - Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); - } + context.WriteBit(true); } - - var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize); + writer.WriteValue(bytes); } } [Test] - public unsafe void TestWritingPartialValues([NUnit.Framework.Range(1, sizeof(ulong))] int count) + public void WhenCallingWriteValueWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var random = new Random(); - var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); - var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) { - - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); - writer.WritePartialValue(valueToTest, count); - - var failMessage = $"TestWritingPartialValues failed with value {valueToTest}"; - VerifyPositionAndLength(ref writer, count, failMessage); - WriteCheckBytes(ref writer, count, failMessage); - var underlyingArray = writer.ToArray(); - VerifyBytewiseEquality(valueToTest, underlyingArray, 0, 0, count, failMessage); - VerifyCheckBytes(underlyingArray, count, failMessage); - - ulong mask = 0; - for (var i = 0; i < count; ++i) + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - mask = (mask << 8) | 0b11111111; + context.WriteBit(true); } - - ulong* checkValue = (ulong*)writer.GetUnsafePtr(); - Assert.AreEqual(valueToTest & mask, *checkValue & mask, failMessage); + writer.WriteValue(""); } } [Test] - public unsafe void TestWritingPartialValuesWithOffsets([NUnit.Framework.Range(1, sizeof(ulong) - 2)] int count) + public void WhenCallingWriteByteSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var random = new Random(); - var valueToTest = ((ulong)random.Next() << 32) + (ulong)random.Next(); - var writer = new FastBufferWriter(sizeof(ulong) + 2, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - - Assert.IsTrue(writer.VerifyCanWrite(count + 2), "Writer denied write permission"); - writer.WritePartialValue(valueToTest, count, 2); - var failMessage = $"TestWritingPartialValuesWithOffsets failed with value {valueToTest}"; - - VerifyPositionAndLength(ref writer, count, failMessage); - WriteCheckBytes(ref writer, count, failMessage); - var underlyingArray = writer.ToArray(); - VerifyBytewiseEquality(valueToTest, underlyingArray, 2, 0, count, failMessage); - VerifyCheckBytes(underlyingArray, count, failMessage); - - ulong mask = 0; - for (var i = 0; i < count; ++i) + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) { - mask = (mask << 8) | 0b11111111; - } - - ulong* checkValue = (ulong*)writer.GetUnsafePtr(); - Assert.AreEqual((valueToTest >> 16) & mask, *checkValue & mask); + context.WriteBit(true); + } + writer.WriteByteSafe(1); } } [Test] - public void TestToArray() + public void WhenCallingWriteBytesSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { - var testStruct = GetTestStruct(); - var requiredSize = FastBufferWriter.GetWriteSize(testStruct); - var writer = new FastBufferWriter(requiredSize, Allocator.Temp); + var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - writer.VerifyCanWrite(requiredSize); - writer.WriteValue(testStruct); - var array = writer.ToArray(); - var underlyingArray = writer.ToArray(); - for (var i = 0; i < array.Length; ++i) + writer.TryBeginWrite(100); + var bytes = new byte[] { 0, 1, 2 }; + using (var context = writer.EnterBitwiseContext()) { - Assert.AreEqual(array[i], underlyingArray[i]); + context.WriteBit(true); } + writer.WriteBytesSafe(bytes, bytes.Length); } } [Test] - public void TestThrowingIfBoundsCheckingSkipped() + public void WhenCallingWriteValueSafeWithUnmanagedTypeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - Assert.Throws(() => { writer.WriteByte(1); }); + writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); int i = 1; - Assert.Throws(() => { writer.WriteValue(i); }); - Assert.Throws(() => { writer.WriteValue(bytes); }); - Assert.Throws(() => { writer.WriteValue(""); }); - - writer.VerifyCanWrite(sizeof(int) - 1); - Assert.Throws(() => { writer.WriteValue(i); }); - writer.WriteByte(1); - writer.WriteByte(2); - writer.WriteByte(3); - Assert.Throws(() => { writer.WriteByte(4); }); + using (var context = writer.EnterBitwiseContext()) + { + context.WriteBit(true); + } + writer.WriteValueSafe(i); } } [Test] - public void TestThrowingIfDoingBytewiseWritesDuringBitwiseContext() + public void WhenCallingWriteValueSafeWithByteArrayAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - writer.VerifyCanWrite(100); + writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; int i = 1; using (var context = writer.EnterBitwiseContext()) { - Assert.Throws(() => { writer.WriteByte(1); }); - Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); - Assert.Throws(() => { writer.WriteValue(i); }); - Assert.Throws(() => { writer.WriteValue(bytes); }); - Assert.Throws(() => { writer.WriteValue(""); }); - - Assert.Throws(() => { writer.WriteByteSafe(1); }); - Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); - Assert.Throws(() => { writer.WriteValueSafe(i); }); - Assert.Throws(() => { writer.WriteValueSafe(bytes); }); - Assert.Throws(() => { writer.WriteValueSafe(""); }); + context.WriteBit(true); } - writer.WriteByte(1); - writer.WriteBytes(bytes, bytes.Length); - writer.WriteValue(i); - writer.WriteValue(bytes); - writer.WriteValue(""); - - writer.WriteByteSafe(1); - writer.WriteBytesSafe(bytes, bytes.Length); - writer.WriteValueSafe(i); writer.WriteValueSafe(bytes); + } + } + + [Test] + public void WhenCallingWriteValueSafeWithStringAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + + using (writer) + { + writer.TryBeginWrite(100); + using (var context = writer.EnterBitwiseContext()) + { + context.WriteBit(true); + } writer.WriteValueSafe(""); } } [Test] - public void TestVerifyCanWriteIsRelativeToPositionAndNotAllowedWritePosition() + public void WhenCallingTryBeginWrite_TheAllowedWritePositionIsMarkedRelativeToCurrentPosition() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - writer.VerifyCanWrite(100); + writer.TryBeginWrite(100); writer.WriteByte(1); - writer.VerifyCanWrite(1); + writer.TryBeginWrite(1); writer.WriteByte(1); Assert.Throws(() => { writer.WriteByte(1); }); } } [Test] - public void TestSeeking() + public void WhenWritingAfterSeeking_TheNewWriteGoesToTheCorrectPosition() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) @@ -1853,16 +824,13 @@ public void TestSeeking() writer.Seek(5); writer.WriteByteSafe(0); Assert.AreEqual(writer.Position, 6); - Assert.AreEqual(writer.Length, 6); writer.Seek(0); writer.WriteByteSafe(1); Assert.AreEqual(writer.Position, 1); - Assert.AreEqual(writer.Length, 6); writer.Seek(10); Assert.AreEqual(writer.Position, 10); - Assert.AreEqual(writer.Length, 10); writer.Seek(2); writer.WriteByteSafe(2); @@ -1877,7 +845,6 @@ public void TestSeeking() writer.WriteByteSafe(5); Assert.AreEqual(writer.Position, 4); - Assert.AreEqual(writer.Length, 10); var expected = new byte[] { 1, 3, 2, 5, 4, 0 }; var underlyingArray = writer.ToArray(); @@ -1889,7 +856,35 @@ public void TestSeeking() } [Test] - public void TestTruncate() + public void WhenSeekingForward_LengthUpdatesToNewPosition() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + Assert.AreEqual(writer.Length, 0); + writer.Seek(5); + Assert.AreEqual(writer.Length, 5); + writer.Seek(10); + Assert.AreEqual(writer.Length, 10); + } + } + + [Test] + public void WhenSeekingBackward_LengthDoesNotChange() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + Assert.AreEqual(writer.Length, 0); + writer.Seek(5); + Assert.AreEqual(writer.Length, 5); + writer.Seek(0); + Assert.AreEqual(writer.Length, 5); + } + } + + [Test] + public void WhenTruncatingToSpecificPositionAheadOfWritePosition_LengthIsUpdatedAndPositionIsNot() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) @@ -1905,7 +900,36 @@ public void TestTruncate() writer.Truncate(8); Assert.AreEqual(writer.Position, 5); Assert.AreEqual(writer.Length, 8); + } + } + + [Test] + public void WhenTruncatingToSpecificPositionBehindWritePosition_BothLengthAndPositionAreUpdated() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.Seek(10); + Assert.AreEqual(writer.Position, 10); + Assert.AreEqual(writer.Length, 10); + + writer.Truncate(8); + Assert.AreEqual(writer.Position, 8); + Assert.AreEqual(writer.Length, 8); + } + } + + [Test] + public void WhenTruncatingToCurrentPosition_LengthIsUpdated() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + using (writer) + { + writer.Seek(10); + Assert.AreEqual(writer.Position, 10); + Assert.AreEqual(writer.Length, 10); + writer.Seek(5); writer.Truncate(); Assert.AreEqual(writer.Position, 5); Assert.AreEqual(writer.Length, 5); @@ -1913,276 +937,263 @@ public void TestTruncate() } [Test] - public void TestCapacity() + public void WhenCreatingNewFastBufferWriter_CapacityIsCorrect() { var writer = new FastBufferWriter(100, Allocator.Temp); Assert.AreEqual(100, writer.Capacity); writer.Dispose(); + + writer = new FastBufferWriter(200, Allocator.Temp); + Assert.AreEqual(200, writer.Capacity); + writer.Dispose(); } [Test] - public void TestGrowth() + public void WhenCreatingNewFastBufferWriter_MaxCapacityIsCorrect() + { + var writer = new FastBufferWriter(100, Allocator.Temp); + Assert.AreEqual(100, writer.MaxCapacity); + writer.Dispose(); + + writer = new FastBufferWriter(100, Allocator.Temp, 200); + Assert.AreEqual(200, writer.MaxCapacity); + writer.Dispose(); + } + + [Test] + public void WhenRequestingWritePastBoundsForNonGrowingWriter_TryBeginWriteReturnsFalse() { var writer = new FastBufferWriter(150, Allocator.Temp); - var growingWriter = new FastBufferWriter(150, Allocator.Temp, 500); - Assert.AreEqual(150, writer.Capacity); using (writer) - using (growingWriter) { var testStruct = GetTestStruct(); - writer.VerifyCanWriteValue(testStruct); + writer.TryBeginWriteValue(testStruct); writer.WriteValue(testStruct); - growingWriter.VerifyCanWriteValue(testStruct); - growingWriter.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - var preGrowthLength = writer.Position; - - // First writer isn't allowed to grow because it didn't specify a maxSize - Assert.IsFalse(writer.VerifyCanWriteValue(testStruct)); - Assert.Throws(() => writer.WriteValue(testStruct)); - - // Second writer is allowed to grow - Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); - - // First writer shouldn't have grown - Assert.AreEqual(150, writer.Capacity); - Assert.AreEqual(preGrowthLength, writer.ToArray().Length); - // First growth doubles the size - Assert.AreEqual(300, growingWriter.Capacity); - Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - growingWriter.WriteValue(testStruct); - - // Write right up to the very end of the buffer, verify it doesn't grow - growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); - Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); - Assert.AreEqual(300, growingWriter.Capacity); - Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - growingWriter.WriteValue(testStruct); - - // Go to the end of the buffer and grow again - growingWriter.Seek(300); - Assert.IsTrue(growingWriter.VerifyCanWriteValue(testStruct)); - growingWriter.WriteValue(testStruct); - - // Second growth caps it at maxSize - Assert.AreEqual(500, growingWriter.Capacity); - Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); - - VerifyBytewiseEquality(testStruct, writer.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); - // Verify the growth properly copied the existing data - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150 - FastBufferWriter.GetWriteSize(testStruct) + 1, FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300 - FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); - VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); + + // Writer isn't allowed to grow because it didn't specify a maxSize + Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); } } - private delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, - NetworkObject networkObject); - private void RunGameObjectTest(GameObjectTestDelegate testCode) + [Test] + public void WhenTryBeginWriteReturnsFalse_WritingThrowsOverflowException() { - var obj = new GameObject("Object"); - var networkBehaviour = obj.AddComponent(); - var networkObject = obj.AddComponent(); - // Create networkManager component - var networkManager = obj.AddComponent(); - networkObject.NetworkManagerOwner = networkManager; - - // Set the NetworkConfig - networkManager.NetworkConfig = new NetworkConfig() - { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, - // Set transport - NetworkTransport = obj.AddComponent() - }; - - networkManager.StartHost(); - - try - { - testCode(obj, networkBehaviour, networkObject); - } - finally + var writer = new FastBufferWriter(150, Allocator.Temp); + using (writer) { - UnityEngine.Object.DestroyImmediate(obj); - networkManager.StopHost(); + var testStruct = GetTestStruct(); + writer.TryBeginWriteValue(testStruct); + writer.WriteValue(testStruct); + + // Seek to exactly where the write would cross the buffer boundary + writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + + // Writer isn't allowed to grow because it didn't specify a maxSize + Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); + Assert.Throws(() => writer.WriteValue(testStruct)); } } [Test] - public void TestNetworkBehaviour() + public void WhenTryBeginWriteReturnsFalseAndOverflowExceptionIsThrown_DataIsNotAffected() { - RunGameObjectTest((obj, networkBehaviour, networkObject) => + var writer = new FastBufferWriter(150, Allocator.Temp); + using (writer) { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkBehaviour))); - - writer.WriteValue(networkBehaviour); - - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong), sizeof(ushort)); - } - }); + var testStruct = GetTestStruct(); + writer.TryBeginWriteValue(testStruct); + writer.WriteValue(testStruct); + + // Seek to exactly where the write would cross the buffer boundary + writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + + // Writer isn't allowed to grow because it didn't specify a maxSize + Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); + Assert.Throws(() => writer.WriteValue(testStruct)); + VerifyBytewiseEquality(testStruct, writer.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + } } [Test] - public void TestNetworkObject() + public void WhenRequestingWritePastBoundsForGrowingWriter_BufferGrowsWithoutLosingData() { - RunGameObjectTest((obj, networkBehaviour, networkObject) => + var growingWriter = new FastBufferWriter(150, Allocator.Temp, 500); + using (growingWriter) { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(networkObject))); - - writer.WriteValue(networkObject); + var testStruct = GetTestStruct(); + growingWriter.TryBeginWriteValue(testStruct); + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - } - }); - } + // Seek to exactly where the write would cross the buffer boundary + growingWriter.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - [Test] - public void TestGameObject() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); - using (writer) - { - Assert.IsTrue(writer.VerifyCanWrite(FastBufferWriter.GetWriteSize(obj))); + Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); - writer.WriteValue(obj); + // Growth doubles the size + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, 150 - FastBufferWriter.GetWriteSize(testStruct) + 1); + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - } - }); + // Verify the growth properly copied the existing data + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 150 - FastBufferWriter.GetWriteSize(testStruct) + 1, FastBufferWriter.GetWriteSize(testStruct)); + } } [Test] - public void TestNetworkBehaviourSafe() + public void WhenRequestingWriteExactlyAtBoundsForGrowingWriter_BufferDoesntGrow() { - RunGameObjectTest((obj, networkBehaviour, networkObject) => + var growingWriter = new FastBufferWriter(300, Allocator.Temp, 500); + using (growingWriter) { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(networkBehaviour); + var testStruct = GetTestStruct(); + growingWriter.TryBeginWriteValue(testStruct); + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong), sizeof(ushort)); - } - }); + growingWriter.Seek(300 - FastBufferWriter.GetWriteSize(testStruct)); + Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, growingWriter.ToArray().Length); + growingWriter.WriteValue(testStruct); + Assert.AreEqual(300, growingWriter.Position); + + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300 - FastBufferWriter.GetWriteSize(testStruct), FastBufferWriter.GetWriteSize(testStruct)); + } } [Test] - public void TestNetworkObjectSafe() + public void WhenBufferGrows_MaxCapacityIsNotExceeded() { - RunGameObjectTest((obj, networkBehaviour, networkObject) => + var growingWriter = new FastBufferWriter(300, Allocator.Temp, 500); + using (growingWriter) { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(networkObject); + var testStruct = GetTestStruct(); + growingWriter.TryBeginWriteValue(testStruct); + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - } - }); - } + growingWriter.Seek(300); + Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); - [Test] - public void TestGameObjectSafe() - { - RunGameObjectTest((obj, networkBehaviour, networkObject) => - { - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj), Allocator.Temp); - using (writer) - { - writer.WriteValueSafe(obj); + Assert.AreEqual(500, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, 300); + + growingWriter.WriteValue(testStruct); - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj), writer.Position); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 0, sizeof(ulong)); - } - }); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 300, FastBufferWriter.GetWriteSize(testStruct)); + } } [Test] - public void TestNetworkBehaviourAsObject() + public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+1, Allocator.Temp); using (writer) { - writer.WriteObject(networkBehaviour); + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); + + var offset = 0; + switch (writeType) + { + case WriteType.WriteDirect: + writer.WriteValue(networkBehaviour); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(networkBehaviour); + break; + case WriteType.WriteAsObject: + writer.WriteObject(networkBehaviour); + // account for isNull byte + offset = 1; + break; + } - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkBehaviour) + 1, writer.Position); - Assert.AreEqual(0, writer.ToArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+offset, writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, - sizeof(ulong) + 1, sizeof(ushort)); + sizeof(ulong) + offset, sizeof(ushort)); } }); } [Test] - public void TestNetworkObjectAsObject() + public void WhenWritingNetworkObject_NetworkObjectIdIsWritten([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(networkObject) + 1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject)+1, Allocator.Temp); using (writer) { - writer.WriteObject(networkObject); + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkObject))); + + var offset = 0; + switch (writeType) + { + case WriteType.WriteDirect: + writer.WriteValue(networkObject); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(networkObject); + break; + case WriteType.WriteAsObject: + writer.WriteObject(networkObject); + // account for isNull byte + offset = 1; + break; + } - Assert.AreEqual(FastBufferWriter.GetWriteSize(networkObject) + 1, writer.Position); - Assert.AreEqual(0, writer.ToArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkObject)+offset, writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); } }); } [Test] - public void TestGameObjectAsObject() + public void WhenWritingGameObject_NetworkObjectIdIsWritten([Values] WriteType writeType) { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - // +1 for extra isNull added by WriteObject - var writer = new FastBufferWriter(FastBufferWriter.GetWriteSize(obj) + 1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj)+1, Allocator.Temp); using (writer) { - writer.WriteObject(obj); + Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(obj))); + + var offset = 0; + switch (writeType) + { + case WriteType.WriteDirect: + writer.WriteValue(obj); + break; + case WriteType.WriteSafe: + writer.WriteValueSafe(obj); + break; + case WriteType.WriteAsObject: + writer.WriteObject(obj); + // account for isNull byte + offset = 1; + break; + } - Assert.AreEqual(FastBufferWriter.GetWriteSize(obj) + 1, writer.Position); - Assert.AreEqual(0, writer.ToArray()[0]); - VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, 1, sizeof(ulong)); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(obj)+offset, writer.Position); + VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); } }); } [Test] - public void TestVerifyInternalDoesntReduceAllowedWritePoint() + public void WhenCallingTryBeginWriteInternal_AllowedWritePositionDoesNotMoveBackward() { var writer = new FastBufferWriter(100, Allocator.Temp); using (writer) { - writer.VerifyCanWrite(25); - writer.VerifyCanWriteInternal(5); + writer.TryBeginWrite(25); + writer.TryBeginWriteInternal(5); Assert.AreEqual(writer.AllowedWriteMark, 25); } } diff --git a/testproject/.gitignore b/testproject/.gitignore index acbbe841e6..fbe0ca9b9e 100644 --- a/testproject/.gitignore +++ b/testproject/.gitignore @@ -69,5 +69,7 @@ crashlytics-build.properties # Temporary auto-generated Android Assets /[Aa]ssets/[Ss]treamingAssets/aa.meta /[Aa]ssets/[Ss]treamingAssets/aa/* +/[Aa]ssets/[Ss]treamingAssets/BuildInfo.json +/[Aa]ssets/[Ss]treamingAssets/BuildInfo.json.meta InitTestScene* diff --git a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs index 508d5a341b..8bfae869b2 100644 --- a/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs +++ b/testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs @@ -11,7 +11,7 @@ namespace TestProject.RuntimeTests { - public class NetworkSceneManagerTests : BaseMultiInstanceTest + public class NetworkSceneManagerTests: BaseMultiInstanceTest { protected override int NbClients => 9; @@ -224,15 +224,15 @@ private void SetClientWaitDone(List clients) private bool ContainsAllClients(List clients) { // First, make sure we have the expected client count - if (clients.Count != m_ShouldWaitList.Count) + if(clients.Count != m_ShouldWaitList.Count) { return false; } // Next, make sure we have all client identifiers - foreach (var sceneTestInfo in m_ShouldWaitList) + foreach(var sceneTestInfo in m_ShouldWaitList) { - if (!clients.Contains(sceneTestInfo.ClientId)) + if(!clients.Contains(sceneTestInfo.ClientId)) { return false; } @@ -248,12 +248,12 @@ private bool ContainsAllClients(List clients) /// private void SceneManager_OnSceneEvent(SceneEvent sceneEvent) { - switch (sceneEvent.SceneEventType) + switch(sceneEvent.SceneEventType) { case SceneEventData.SceneEventTypes.S2C_Load: case SceneEventData.SceneEventTypes.S2C_Unload: { - Assert.AreEqual(sceneEvent.SceneName, m_CurrentSceneName); + Assert.AreEqual(sceneEvent.SceneName,m_CurrentSceneName); Assert.IsTrue(ContainsClient(sceneEvent.ClientId)); Assert.IsNotNull(sceneEvent.AsyncOperation); break; From 9816b50b728b93ec6564f9e2b80f0d5a559b6c21 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 26 Aug 2021 11:14:50 -0500 Subject: [PATCH 18/29] standards.py --fix --- .../Serialization/FastBufferWriterTests.cs | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 37e93eb59d..af4adeb5f1 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -121,7 +121,7 @@ protected override unsafe void RunObjectTypeTest(T valueToTest) } } - private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T: unmanaged + private unsafe void VerifyArrayEquality(T[] value, byte* unsafePtr, int offset) where T : unmanaged { int* sizeValue = (int*)(unsafePtr + offset); Assert.AreEqual(value.Length, *sizeValue); @@ -260,25 +260,25 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, // account for isNull byte offset = sizeof(byte); break; - + } - VerifyPositionAndLength(ref writer, serializedValueSize+offset); - WriteCheckBytes(ref writer, serializedValueSize+offset); + VerifyPositionAndLength(ref writer, serializedValueSize + offset); + WriteCheckBytes(ref writer, serializedValueSize + offset); int* sizeValue = (int*)(writer.GetUnsafePtr() + offset); Assert.AreEqual(valueToTest.Length, *sizeValue); fixed (char* asCharPointer = valueToTest) { - if(oneByteChars) + if (oneByteChars) { byte* underlyingByteArray = writer.GetUnsafePtr() + sizeof(int) + offset; for (var i = 0; i < valueToTest.Length; ++i) { Assert.AreEqual((byte)asCharPointer[i], underlyingByteArray[i]); } - + } else { @@ -291,7 +291,7 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, } var underlyingArray = writer.ToArray(); - VerifyCheckBytes(underlyingArray, serializedValueSize+offset); + VerifyCheckBytes(underlyingArray, serializedValueSize + offset); } } @@ -303,7 +303,7 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, [TestCase(6, 0)] [TestCase(7, 0)] [TestCase(8, 0)] - + [TestCase(1, 1)] [TestCase(2, 1)] [TestCase(3, 1)] @@ -311,32 +311,32 @@ public unsafe void WhenWritingString_ValueIsWrittenCorrectly(bool oneByteChars, [TestCase(5, 1)] [TestCase(6, 1)] [TestCase(7, 1)] - + [TestCase(1, 2)] [TestCase(2, 2)] [TestCase(3, 2)] [TestCase(4, 2)] [TestCase(5, 2)] [TestCase(6, 2)] - + [TestCase(1, 3)] [TestCase(2, 3)] [TestCase(3, 3)] [TestCase(4, 3)] [TestCase(5, 3)] - + [TestCase(1, 4)] [TestCase(2, 4)] [TestCase(3, 4)] [TestCase(4, 4)] - + [TestCase(1, 5)] [TestCase(2, 5)] [TestCase(3, 5)] - + [TestCase(1, 6)] [TestCase(2, 6)] - + [TestCase(1, 7)] public unsafe void WhenWritingPartialValueWithCountAndOffset_ValueIsWrittenCorrectly(int count, int offset) { @@ -942,7 +942,7 @@ public void WhenCreatingNewFastBufferWriter_CapacityIsCorrect() var writer = new FastBufferWriter(100, Allocator.Temp); Assert.AreEqual(100, writer.Capacity); writer.Dispose(); - + writer = new FastBufferWriter(200, Allocator.Temp); Assert.AreEqual(200, writer.Capacity); writer.Dispose(); @@ -954,7 +954,7 @@ public void WhenCreatingNewFastBufferWriter_MaxCapacityIsCorrect() var writer = new FastBufferWriter(100, Allocator.Temp); Assert.AreEqual(100, writer.MaxCapacity); writer.Dispose(); - + writer = new FastBufferWriter(100, Allocator.Temp, 200); Assert.AreEqual(200, writer.MaxCapacity); writer.Dispose(); @@ -969,10 +969,10 @@ public void WhenRequestingWritePastBoundsForNonGrowingWriter_TryBeginWriteReturn var testStruct = GetTestStruct(); writer.TryBeginWriteValue(testStruct); writer.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - + // Writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); } @@ -987,10 +987,10 @@ public void WhenTryBeginWriteReturnsFalse_WritingThrowsOverflowException() var testStruct = GetTestStruct(); writer.TryBeginWriteValue(testStruct); writer.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - + // Writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); Assert.Throws(() => writer.WriteValue(testStruct)); @@ -1006,10 +1006,10 @@ public void WhenTryBeginWriteReturnsFalseAndOverflowExceptionIsThrown_DataIsNotA var testStruct = GetTestStruct(); writer.TryBeginWriteValue(testStruct); writer.WriteValue(testStruct); - + // Seek to exactly where the write would cross the buffer boundary writer.Seek(150 - FastBufferWriter.GetWriteSize(testStruct) + 1); - + // Writer isn't allowed to grow because it didn't specify a maxSize Assert.IsFalse(writer.TryBeginWriteValue(testStruct)); Assert.Throws(() => writer.WriteValue(testStruct)); @@ -1080,7 +1080,7 @@ public void WhenBufferGrows_MaxCapacityIsNotExceeded() Assert.AreEqual(500, growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, 300); - + growingWriter.WriteValue(testStruct); VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); @@ -1093,7 +1093,7 @@ public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); using (writer) { Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkBehaviour))); @@ -1114,7 +1114,7 @@ public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values break; } - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+offset, writer.Position); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + offset, writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); VerifyBytewiseEquality(networkBehaviour.NetworkBehaviourId, writer.ToArray(), 0, sizeof(ulong) + offset, sizeof(ushort)); @@ -1127,7 +1127,7 @@ public void WhenWritingNetworkObject_NetworkObjectIdIsWritten([Values] WriteType { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject) + 1, Allocator.Temp); using (writer) { Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(networkObject))); @@ -1148,7 +1148,7 @@ public void WhenWritingNetworkObject_NetworkObjectIdIsWritten([Values] WriteType break; } - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkObject)+offset, writer.Position); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(networkObject) + offset, writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); } }); @@ -1159,7 +1159,7 @@ public void WhenWritingGameObject_NetworkObjectIdIsWritten([Values] WriteType wr { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj) + 1, Allocator.Temp); using (writer) { Assert.IsTrue(writer.TryBeginWrite(FastBufferWriterExtensions.GetWriteSize(obj))); @@ -1180,7 +1180,7 @@ public void WhenWritingGameObject_NetworkObjectIdIsWritten([Values] WriteType wr break; } - Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(obj)+offset, writer.Position); + Assert.AreEqual(FastBufferWriterExtensions.GetWriteSize(obj) + offset, writer.Position); VerifyBytewiseEquality(networkObject.NetworkObjectId, writer.ToArray(), 0, offset, sizeof(ulong)); } }); From a576552feb61cef07abeda6448e1d75017cfc125 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 26 Aug 2021 13:20:58 -0500 Subject: [PATCH 19/29] standards.py --fix --- .../Runtime/Serialization/FastBufferReader.cs | 2 - .../FastBufferReaderExtensions.cs | 6 +-- .../Runtime/Serialization/FastBufferWriter.cs | 2 - .../FastBufferWriterExtensions.cs | 6 +-- .../BaseFastBufferReaderWriterTest.cs | 14 +++--- .../Editor/Serialization/BitCounterTests.cs | 8 ++-- .../Serialization/FastBufferReaderTests.cs | 44 +++++++++---------- 7 files changed, 38 insertions(+), 44 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 31493bc2e6..77adda8aab 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -2,8 +2,6 @@ using System.Runtime.CompilerServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using Unity.Netcode; -using UnityEngine; namespace Unity.Multiplayer.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs index 21d7daf745..43752c915e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -1,4 +1,4 @@ - + using System; using Unity.Netcode; using UnityEngine; @@ -7,7 +7,7 @@ namespace Unity.Multiplayer.Netcode { public static class FastBufferReaderExtensions { - + /// /// Reads a boxed object in a standard format /// Named differently from other ReadValue methods to avoid accidental boxing @@ -284,4 +284,4 @@ public static void ReadValueSafe(this ref FastBufferReader reader, out NetworkBe value = null; } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 22fea00ced..2504dedd55 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -2,8 +2,6 @@ using System.Runtime.CompilerServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using Unity.Netcode; -using UnityEngine; namespace Unity.Multiplayer.Netcode { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs index 3fea8ee5c0..c29f7f302a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs @@ -1,4 +1,4 @@ - + using System; using Unity.Netcode; using UnityEngine; @@ -7,7 +7,7 @@ namespace Unity.Multiplayer.Netcode { public static class FastBufferWriterExtensions { - + /// /// Writes a boxed object in a standard format /// Named differently from other WriteValue methods to avoid accidental boxing @@ -295,4 +295,4 @@ public static void WriteValueSafe(this ref FastBufferWriter writer, NetworkBehav } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index e2d22d75b3..315c24a7ae 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using NUnit.Framework; using Unity.Multiplayer.Netcode; @@ -11,7 +11,7 @@ namespace Unity.Netcode { public abstract class BaseFastBufferReaderWriterTest { - + #region Test Types protected enum ByteEnum : byte { @@ -97,7 +97,7 @@ public enum WriteType protected abstract void RunTypeArrayTestSafe(T[] valueToTest) where T : unmanaged; protected abstract void RunObjectTypeArrayTest(T[] valueToTest) where T : unmanaged; - + #region Helpers protected TestStruct GetTestStruct() { @@ -120,7 +120,7 @@ protected TestStruct GetTestStruct() return testStruct; } - + protected delegate void GameObjectTestDelegate(GameObject obj, NetworkBehaviour networkBehaviour, NetworkObject networkObject); protected void RunGameObjectTest(GameObjectTestDelegate testCode) @@ -155,11 +155,11 @@ protected void RunGameObjectTest(GameObjectTestDelegate testCode) } } #endregion - + public void BaseTypeTest(Type testType, WriteType writeType) { var random = new Random(); - + void RunTypeTestLocal(T val, WriteType wt) where T : unmanaged { switch (wt) @@ -666,4 +666,4 @@ void RunTypeTestLocal(T[] val, WriteType wt) where T : unmanaged } } } -} \ No newline at end of file +} diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 96d68f8c67..0aca9b397d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -36,7 +36,7 @@ public void WhenCountingUsedBitsIn32BitValue_ResultMatchesHighBitSetPlusOne([Ran Assert.AreEqual(highBit + 1, BitCounter.GetUsedBitCount(value)); } } - + [Test] public void WhenCountingUsedBytesIn64BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 63)] int highBit) { @@ -49,10 +49,10 @@ public void WhenCountingUsedBytesIn64BitValue_ResultMatchesHighBitSetOver8PlusOn else { ulong value = 1UL << highBit; - Assert.AreEqual(highBit/8 + 1, BitCounter.GetUsedByteCount(value)); + Assert.AreEqual(highBit / 8 + 1, BitCounter.GetUsedByteCount(value)); } } - + [Test] public void WhenCountingUsedBytesIn32BitValue_ResultMatchesHighBitSetOver8PlusOne([Range(0, 31)] int highBit) { @@ -65,7 +65,7 @@ public void WhenCountingUsedBytesIn32BitValue_ResultMatchesHighBitSetOver8PlusOn else { uint value = 1U << highBit; - Assert.AreEqual(highBit/8 + 1, BitCounter.GetUsedByteCount(value)); + Assert.AreEqual(highBit / 8 + 1, BitCounter.GetUsedByteCount(value)); } } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index cf0817f507..f794176d62 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; using NUnit.Framework; using NUnit.Framework.Internal; using Unity.Collections; using Unity.Multiplayer.Netcode; using UnityEngine; -using UnityEngine.SceneManagement; using Random = System.Random; namespace Unity.Netcode.EditorTests @@ -292,7 +290,7 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW break; case WriteType.WriteAsObject: reader.ReadObject(out object resultObj, typeof(string), oneByteChars); - result = (string) resultObj; + result = (string)resultObj; break; } Assert.AreEqual(valueToTest, result); @@ -311,7 +309,7 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW [TestCase(6, 0)] [TestCase(7, 0)] [TestCase(8, 0)] - + [TestCase(1, 1)] [TestCase(2, 1)] [TestCase(3, 1)] @@ -319,32 +317,32 @@ public void GivenFastBufferWriterContainingValue_WhenReadingString_ValueMatchesW [TestCase(5, 1)] [TestCase(6, 1)] [TestCase(7, 1)] - + [TestCase(1, 2)] [TestCase(2, 2)] [TestCase(3, 2)] [TestCase(4, 2)] [TestCase(5, 2)] [TestCase(6, 2)] - + [TestCase(1, 3)] [TestCase(2, 3)] [TestCase(3, 3)] [TestCase(4, 3)] [TestCase(5, 3)] - + [TestCase(1, 4)] [TestCase(2, 4)] [TestCase(3, 4)] [TestCase(4, 4)] - + [TestCase(1, 5)] [TestCase(2, 5)] [TestCase(3, 5)] - + [TestCase(1, 6)] [TestCase(2, 6)] - + [TestCase(1, 7)] public void GivenFastBufferWriterContainingValue_WhenReadingPartialValue_ValueMatchesWhatWasWritten(int count, int offset) { @@ -425,7 +423,7 @@ public void WhenCallingReadBytesWithoutCallingTryBeingReadFirst_OverflowExceptio var nativeArray = new NativeArray(100, Allocator.Temp); var emptyReader = new FastBufferReader(nativeArray, Allocator.Temp); - byte[] b = {0, 1, 2}; + byte[] b = { 0, 1, 2 }; using (emptyReader) { Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); @@ -522,7 +520,7 @@ public void WhenCallingReadBytesDuringBitwiseContext_InvalidOperationExceptionIs { using (var context = emptyReader.EnterBitwiseContext()) { - byte[] b = {0, 1, 2}; + byte[] b = { 0, 1, 2 }; Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); } } @@ -598,7 +596,7 @@ public void WhenCallingReadBytesSafeDuringBitwiseContext_InvalidOperationExcepti { using (var context = emptyReader.EnterBitwiseContext()) { - byte[] b = {0, 1, 2}; + byte[] b = { 0, 1, 2 }; Assert.Throws(() => { emptyReader.ReadBytesSafe(ref b, 3); }); } } @@ -648,7 +646,7 @@ public void WhenCallingReadValueSafeWithStringDuringBitwiseContext_InvalidOperat } } } - + [Test] public void WhenCallingReadByteAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { @@ -680,7 +678,7 @@ public void WhenCallingReadBytesAfterExitingBitwiseContext_InvalidOperationExcep context.ReadBit(out bool theBit); } - byte[] theBytes = {0, 1, 2}; + byte[] theBytes = { 0, 1, 2 }; emptyReader.ReadBytes(ref theBytes, 3); } } @@ -735,7 +733,7 @@ public void WhenCallingReadValueWithStringAfterExitingBitwiseContext_InvalidOper emptyReader.ReadValue(out string s); } } - + [Test] public void WhenCallingReadByteSafeAfterExitingBitwiseContext_InvalidOperationExceptionIsNotThrown() { @@ -767,7 +765,7 @@ public void WhenCallingReadBytesSafeAfterExitingBitwiseContext_InvalidOperationE context.ReadBit(out bool theBit); } - byte[] theBytes = {0, 1, 2}; + byte[] theBytes = { 0, 1, 2 }; emptyReader.ReadBytesSafe(ref theBytes, 3); } } @@ -898,7 +896,7 @@ public void GivenFastBufferWriterWithNetworkObjectWritten_WhenReadingNetworkObje { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkObject) + 1, Allocator.Temp); using (writer) { switch (writeType) @@ -931,7 +929,7 @@ public void GivenFastBufferWriterWithNetworkObjectWritten_WhenReadingNetworkObje break; case WriteType.WriteAsObject: reader.ReadObject(out object resultObj, typeof(NetworkObject)); - result = (NetworkObject) resultObj; + result = (NetworkObject)resultObj; break; } Assert.AreSame(result, networkObject); @@ -945,7 +943,7 @@ public void GivenFastBufferWriterWithGameObjectWritten_WhenReadingGameObject_The { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(obj) + 1, Allocator.Temp); using (writer) { switch (writeType) @@ -978,7 +976,7 @@ public void GivenFastBufferWriterWithGameObjectWritten_WhenReadingGameObject_The break; case WriteType.WriteAsObject: reader.ReadObject(out object resultObj, typeof(GameObject)); - result = (GameObject) resultObj; + result = (GameObject)resultObj; break; } Assert.AreSame(result, obj); @@ -992,7 +990,7 @@ public void GivenFastBufferWriterWithNetworkBehaviourWritten_WhenReadingNetworkB { RunGameObjectTest((obj, networkBehaviour, networkObject) => { - var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour)+1, Allocator.Temp); + var writer = new FastBufferWriter(FastBufferWriterExtensions.GetWriteSize(networkBehaviour) + 1, Allocator.Temp); using (writer) { switch (writeType) @@ -1025,7 +1023,7 @@ public void GivenFastBufferWriterWithNetworkBehaviourWritten_WhenReadingNetworkB break; case WriteType.WriteAsObject: reader.ReadObject(out object resultObj, typeof(NetworkBehaviour)); - result = (NetworkBehaviour) resultObj; + result = (NetworkBehaviour)resultObj; break; } Assert.AreSame(result, networkBehaviour); From 5193e39b95fb28eca7797b9effc73c44416b132f Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 26 Aug 2021 17:02:25 -0500 Subject: [PATCH 20/29] Fixed incorrect namespaces. --- .../Runtime/Serialization/BitCounter.cs | 2 +- .../Runtime/Serialization/BitReader.cs | 3 +-- .../Runtime/Serialization/BitWriter.cs | 2 +- .../Runtime/Serialization/BufferSerializer.cs | 3 +-- .../Runtime/Serialization/BufferSerializerReader.cs | 3 +-- .../Runtime/Serialization/BufferSerializerWriter.cs | 3 +-- .../Runtime/Serialization/BytePacker.cs | 3 +-- .../Runtime/Serialization/ByteUnpacker.cs | 3 +-- .../Runtime/Serialization/BytewiseUtility.cs | 2 +- .../Runtime/Serialization/FastBufferReader.cs | 2 +- .../Runtime/Serialization/FastBufferReaderExtensions.cs | 3 +-- .../Runtime/Serialization/FastBufferWriter.cs | 2 +- .../Runtime/Serialization/FastBufferWriterExtensions.cs | 3 +-- .../Runtime/Serialization/IBufferSerializerImplementation.cs | 3 +-- .../Runtime/Serialization/SerializationTypeTable.cs | 2 +- com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs | 2 +- com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs | 2 +- .../Editor/Serialization/BaseFastBufferReaderWriterTest.cs | 1 - .../Tests/Editor/Serialization/BitCounterTests.cs | 1 - .../Tests/Editor/Serialization/BitReaderTests.cs | 1 - .../Tests/Editor/Serialization/BitWriterTests.cs | 1 - .../Tests/Editor/Serialization/BufferSerializerTests.cs | 2 -- .../Tests/Editor/Serialization/BytePackerTests.cs | 1 - .../Tests/Editor/Serialization/FastBufferReaderTests.cs | 2 -- .../Tests/Editor/Serialization/FastBufferWriterTests.cs | 1 - 25 files changed, 17 insertions(+), 36 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs index fad7dcf093..4feb4477da 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitCounter.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class BitCounter { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 3540ea73fd..411152046d 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,8 +1,7 @@ using System; using System.Runtime.CompilerServices; -using Unity.Netcode; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { /// /// Helper class for doing bitwise reads for a FastBufferReader. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 3183f6ce33..76a7ecab2e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.CompilerServices; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { /// /// Helper class for doing bitwise writes for a FastBufferWriter. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index bba0c112c9..a9132cb5bd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -1,8 +1,7 @@ using System; -using Unity.Multiplayer.Netcode; using UnityEngine; -namespace Unity.Netcode.Serialization +namespace Unity.Netcode { /// /// Two-way serializer wrapping FastBufferReader or FastBufferWriter. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs index 495212dfa9..ae777d3735 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerReader.cs @@ -1,8 +1,7 @@ using System; -using Unity.Multiplayer.Netcode; using UnityEngine; -namespace Unity.Netcode.Serialization +namespace Unity.Netcode { internal struct BufferSerializerReader : IBufferSerializerImplementation { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs index f6f63b95c2..f3a345ea99 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializerWriter.cs @@ -1,8 +1,7 @@ using System; -using Unity.Multiplayer.Netcode; using UnityEngine; -namespace Unity.Netcode.Serialization +namespace Unity.Netcode { internal struct BufferSerializerWriter : IBufferSerializerImplementation { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs index 39f031d220..9eb5854e67 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytePacker.cs @@ -1,9 +1,8 @@ using System; using System.Runtime.CompilerServices; -using Unity.Netcode; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { /// /// Utility class for packing values in serialization. diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs index b83bca1062..857829e126 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/ByteUnpacker.cs @@ -1,9 +1,8 @@ using System; using System.Runtime.CompilerServices; -using Unity.Netcode; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class ByteUnpacker { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs index 36394addad..0732fca31e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs @@ -1,6 +1,6 @@ using Unity.Collections.LowLevel.Unsafe; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class BytewiseUtility { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 77adda8aab..070e209a38 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public struct FastBufferReader : IDisposable { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs index 43752c915e..02f567a9df 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReaderExtensions.cs @@ -1,9 +1,8 @@ using System; -using Unity.Netcode; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class FastBufferReaderExtensions { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 2504dedd55..4fb9e9a5a5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -3,7 +3,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public struct FastBufferWriter : IDisposable { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs index c29f7f302a..af269f811e 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriterExtensions.cs @@ -1,9 +1,8 @@ using System; -using Unity.Netcode; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public static class FastBufferWriterExtensions { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs index 7d181d3f11..d97954c243 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/IBufferSerializerImplementation.cs @@ -1,8 +1,7 @@ using System; -using Unity.Multiplayer.Netcode; using UnityEngine; -namespace Unity.Netcode.Serialization +namespace Unity.Netcode { public interface IBufferSerializerImplementation { diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs index c3e497281e..fb861c91d7 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/SerializationTypeTable.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using UnityEngine; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { /// /// Registry for telling FastBufferWriter and FastBufferReader how to read types when passed to diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs index 62ae287a3a..02572cbc46 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/Ref.cs @@ -1,6 +1,6 @@ using System.Runtime.CompilerServices; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public struct Ref where T : unmanaged { diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs index bac5669ad9..52d456e4a3 100644 --- a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs +++ b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Unity.Multiplayer.Netcode +namespace Unity.Netcode { public ref struct RefArray where T : unmanaged { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index 315c24a7ae..ae5e1aefa0 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using Unity.Multiplayer.Netcode; using Unity.Netcode.EditorTests; using UnityEngine; using UnityEngine.SceneManagement; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs index 0aca9b397d..5e0f4c8a12 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitCounterTests.cs @@ -1,5 +1,4 @@ using NUnit.Framework; -using Unity.Multiplayer.Netcode; namespace Unity.Netcode.EditorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index 533c6bf963..87b0154ee5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -1,7 +1,6 @@ using System; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; namespace Unity.Netcode.EditorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index 612fcb1f2f..cd51c356de 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -1,7 +1,6 @@ using System; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; namespace Unity.Netcode.EditorTests { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index aa24d4e030..0f050f6301 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; -using Unity.Netcode.Serialization; using UnityEngine; using UnityEngine.SceneManagement; using Random = System.Random; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs index 0ece584a51..1c2ff159e9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BytePackerTests.cs @@ -3,7 +3,6 @@ using System.Reflection; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; using UnityEngine; using Random = System.Random; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index f794176d62..52a9180c1e 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -1,8 +1,6 @@ using System; using NUnit.Framework; -using NUnit.Framework.Internal; using Unity.Collections; -using Unity.Multiplayer.Netcode; using UnityEngine; using Random = System.Random; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index af4adeb5f1..acdb2af153 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1,7 +1,6 @@ using System; using NUnit.Framework; using Unity.Collections; -using Unity.Multiplayer.Netcode; using UnityEngine; using Random = System.Random; From 119ee2f0e0715ddeeb646d67120ee7ee237a4081 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Thu, 26 Aug 2021 18:35:14 -0500 Subject: [PATCH 21/29] -Fixed a couple of issues where growing a FastBufferWriter wouldn't work correctly (requesting beyond MaxCapacity and requesting more than double current capacity) -Added support for FastBufferReader to be used in a mode that doesn't copy the input buffer --- .../Runtime/Serialization/BitWriter.cs | 6 +- .../Runtime/Serialization/FastBufferReader.cs | 61 ++++++++++++++----- .../Runtime/Serialization/FastBufferWriter.cs | 22 +++++-- .../Serialization/FastBufferWriterTests.cs | 31 ++++++++++ 4 files changed, 100 insertions(+), 20 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 76a7ecab2e..688210a9f5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -80,9 +80,13 @@ public unsafe bool TryBeginWriteBits(int bitCount) if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.CapacityInternal) { + if (m_Position + totalBytesWrittenInBitwiseContext > m_Writer.Value.MaxCapacityInternal) + { + return false; + } if (m_Writer.Value.CapacityInternal < m_Writer.Value.MaxCapacityInternal) { - m_Writer.Value.Grow(); + m_Writer.Value.Grow(totalBytesWrittenInBitwiseContext); m_BufferPointer = m_Writer.Value.BufferPointer + m_Writer.Value.PositionInternal; } else diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 070e209a38..9b9f7b7f2a 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -46,9 +46,16 @@ internal void CommitBitwiseReads(int amount) public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, int length = -1, int offset = 0) { LengthInternal = Math.Max(1, length == -1 ? buffer.Length : length); - void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr() + offset, LengthInternal); - BufferPointer = (byte*)bufferPtr; + if (allocator == Allocator.None) + { + BufferPointer = (byte*) buffer.GetUnsafePtr() + offset; + } + else + { + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, (byte*)buffer.GetUnsafePtr() + offset, LengthInternal); + BufferPointer = (byte*)bufferPtr; + } PositionInternal = offset; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -69,12 +76,21 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, int length = -1, int offset = 0) { LengthInternal = Math.Max(1, length == -1 ? (buffer.Count - offset) : length); - void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); - fixed (byte* data = buffer.Array) + if (allocator == Allocator.None) { - UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); + throw new NotSupportedException("Allocator.None cannot be used with managed source buffers."); } - BufferPointer = (byte*)bufferPtr; + else + { + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + fixed (byte* data = buffer.Array) + { + UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); + } + + BufferPointer = (byte*) bufferPtr; + } + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -95,12 +111,21 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = -1, int offset = 0) { LengthInternal = Math.Max(1, length == -1 ? (buffer.Length - offset) : length); - void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); - fixed (byte* data = buffer) + if (allocator == Allocator.None) { - UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); + throw new NotSupportedException("Allocator.None cannot be used with managed source buffers."); } - BufferPointer = (byte*)bufferPtr; + else + { + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + fixed (byte* data = buffer) + { + UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); + } + + BufferPointer = (byte*) bufferPtr; + } + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR @@ -121,9 +146,17 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, int offset = 0) { LengthInternal = Math.Max(1, length); - void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); - UnsafeUtility.MemCpy(bufferPtr, buffer + offset, LengthInternal); - BufferPointer = (byte*)bufferPtr; + if (allocator == Allocator.None) + { + BufferPointer = buffer + offset; + } + else + { + void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); + UnsafeUtility.MemCpy(bufferPtr, buffer + offset, LengthInternal); + BufferPointer = (byte*) bufferPtr; + } + PositionInternal = 0; m_Allocator = allocator; #if DEVELOPMENT_BUILD || UNITY_EDITOR diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 4fb9e9a5a5..b2e44dd0dd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -159,9 +159,9 @@ public BitWriter EnterBitwiseContext() return new BitWriter(ref this); } - internal unsafe void Grow() + internal unsafe void Grow(int additionalSizeRequired) { - var newSize = Math.Min(CapacityInternal * 2, MaxCapacityInternal); + var newSize = Math.Min(Math.Max(CapacityInternal * 2, (Position + additionalSizeRequired) * 2), MaxCapacityInternal); void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, newSize); @@ -198,9 +198,13 @@ public bool TryBeginWrite(int bytes) #endif if (PositionInternal + bytes > CapacityInternal) { + if (PositionInternal + bytes > MaxCapacityInternal) + { + return false; + } if (CapacityInternal < MaxCapacityInternal) { - Grow(); + Grow(bytes); } else { @@ -241,9 +245,13 @@ public unsafe bool TryBeginWriteValue(in T value) where T : unmanaged int len = sizeof(T); if (PositionInternal + len > CapacityInternal) { + if (PositionInternal + len > MaxCapacityInternal) + { + return false; + } if (CapacityInternal < MaxCapacityInternal) { - Grow(); + Grow(len); } else { @@ -275,9 +283,13 @@ public bool TryBeginWriteInternal(int bytes) #endif if (PositionInternal + bytes > CapacityInternal) { + if (PositionInternal + bytes > MaxCapacityInternal) + { + return false; + } if (CapacityInternal < MaxCapacityInternal) { - Grow(); + Grow(bytes); } else { diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index acdb2af153..b68de099f5 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1087,6 +1087,37 @@ public void WhenBufferGrows_MaxCapacityIsNotExceeded() } } + [Test] + public void WhenBufferGrowthRequiredIsMoreThanDouble_BufferGrowsEnoughToContainRequestedValue() + { + var growingWriter = new FastBufferWriter(1, Allocator.Temp, 500); + using (growingWriter) + { + var testStruct = GetTestStruct(); + Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); + + Assert.AreEqual(FastBufferWriter.GetWriteSize(testStruct)*2, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, 0); + + growingWriter.WriteValue(testStruct); + + VerifyBytewiseEquality(testStruct, growingWriter.ToArray(), 0, 0, FastBufferWriter.GetWriteSize(testStruct)); + } + } + + [Test] + public void WhenTryingToWritePastMaxCapacity_GrowthDoesNotOccurAndTryBeginWriteReturnsFalse() + { + var growingWriter = new FastBufferWriter(300, Allocator.Temp, 500); + using (growingWriter) + { + Assert.IsFalse(growingWriter.TryBeginWrite(501)); + + Assert.AreEqual(300, growingWriter.Capacity); + Assert.AreEqual(growingWriter.Position, 0); + } + } + [Test] public void WhenWritingNetworkBehaviour_ObjectIdAndBehaviourIdAreWritten([Values] WriteType writeType) { From 7fb2d536b38d16043d4dfbc9980824cdae585ebb Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Fri, 27 Aug 2021 10:46:22 -0500 Subject: [PATCH 22/29] Fix a test failure and better implementation of large growths --- .../Runtime/Serialization/FastBufferWriter.cs | 7 ++++++- .../Tests/Editor/Serialization/FastBufferWriterTests.cs | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index b2e44dd0dd..4a6a44092c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -161,7 +161,12 @@ public BitWriter EnterBitwiseContext() internal unsafe void Grow(int additionalSizeRequired) { - var newSize = Math.Min(Math.Max(CapacityInternal * 2, (Position + additionalSizeRequired) * 2), MaxCapacityInternal); + var desiredSize = CapacityInternal * 2; + while (desiredSize < Position + additionalSizeRequired) + { + desiredSize *= 2; + } + var newSize = Math.Min(desiredSize, MaxCapacityInternal); void* buffer = UnsafeUtility.Malloc(newSize, UnsafeUtility.AlignOf(), m_Allocator); #if DEVELOPMENT_BUILD || UNITY_EDITOR UnsafeUtility.MemSet(buffer, 0, newSize); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index b68de099f5..54dca7131a 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -1096,7 +1096,10 @@ public void WhenBufferGrowthRequiredIsMoreThanDouble_BufferGrowsEnoughToContainR var testStruct = GetTestStruct(); Assert.IsTrue(growingWriter.TryBeginWriteValue(testStruct)); - Assert.AreEqual(FastBufferWriter.GetWriteSize(testStruct)*2, growingWriter.Capacity); + // Buffer size doubles with each growth, so since we're starting with a size of 1, that means + // the resulting size should be the next power of 2 above the size of testStruct. + Assert.AreEqual(Math.Pow(2, Math.Ceiling(Mathf.Log(FastBufferWriter.GetWriteSize(testStruct), 2))), + growingWriter.Capacity); Assert.AreEqual(growingWriter.Position, 0); growingWriter.WriteValue(testStruct); From 8ee461051da34db6e5e7bd48ffee90f2a335e86b Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 31 Aug 2021 13:50:34 -0500 Subject: [PATCH 23/29] - Removed RefArray - Fixed incorrect text in a warning in FastBufferReader --- .../Runtime/Serialization/FastBufferReader.cs | 2 +- .../Runtime/Utility/RefArray.cs | 92 ------------------- 2 files changed, 1 insertion(+), 93 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 9b9f7b7f2a..88c36a1b61 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -693,7 +693,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged } if (PositionInternal + len > AllowedReadMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException("Attempted to read without first calling TryBeginRead()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs deleted file mode 100644 index 52d456e4a3..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace Unity.Netcode -{ - public ref struct RefArray where T : unmanaged - { - public struct RefArrayImplementation : IReadOnlyList - where T : unmanaged - { - private unsafe T* m_Value; - private int m_Length; - - internal unsafe RefArrayImplementation(T* ptr, int length) - { - m_Value = ptr; - m_Length = length; - } - - public unsafe ref T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref *m_Value; - } - - public struct Enumerator : IEnumerator, IEnumerator, IDisposable - { - private RefArrayImplementation m_Array; - private int m_Index; - - public Enumerator(ref RefArrayImplementation array) - { - m_Array = array; - m_Index = -1; - } - - public void Dispose() - { - } - - public bool MoveNext() - { - ++m_Index; - return m_Index < m_Array.Length; - } - - public void Reset() => m_Index = -1; - - public T Current => m_Array[m_Index]; - - object IEnumerator.Current => (object)Current; - } - - public Enumerator GetEnumerator() => - new Enumerator(ref this); - - IEnumerator IEnumerable.GetEnumerator() => - (IEnumerator)new Enumerator(ref this); - - IEnumerator IEnumerable.GetEnumerator() => (IEnumerator)GetEnumerator(); - - public int Count => m_Length; - public int Length => m_Length; - - public unsafe T this[int index] - { - get => m_Value[index]; - set => m_Value[index] = value; - } - } - - private RefArrayImplementation m_Value; - - public unsafe RefArray(T* ptr, int length) - { - m_Value = new RefArrayImplementation(ptr, length); - } - - public unsafe ref RefArrayImplementation Value - { - get - { - fixed (RefArrayImplementation* ptr = &m_Value) - { - return ref *ptr; - } - } - } - } -} From f4b7ea960a12445a251272ad10e779c7ce8e4bd6 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 31 Aug 2021 16:15:31 -0500 Subject: [PATCH 24/29] Removed RefArray meta file that stuck around. --- .../Runtime/Utility/RefArray.cs.meta | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta b/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta deleted file mode 100644 index 9e30a4c74c..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Utility/RefArray.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1422b1128ad62024d9e8dac38e009aad -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: From d983f07ba18fb3a9b9da74689441272429bbee2d Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 31 Aug 2021 16:19:24 -0500 Subject: [PATCH 25/29] Review feedback: Used nameof() instead of string literal. --- .../Runtime/Serialization/BitReader.cs | 6 +++--- .../Runtime/Serialization/BitWriter.cs | 6 +++--- .../Runtime/Serialization/FastBufferReader.cs | 8 ++++---- .../Runtime/Serialization/FastBufferWriter.cs | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 411152046d..172881e357 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -105,7 +105,7 @@ public unsafe void ReadBits(out ulong value, uint bitCount) int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginReadBits)}()"); } #endif ulong val = 0; @@ -142,7 +142,7 @@ public void ReadBits(out byte value, uint bitCount) int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginReadBits)}()"); } #endif value = ReadByteBits((int)bitCount); @@ -159,7 +159,7 @@ public unsafe void ReadBit(out bool bit) int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginReadBits()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginReadBits)}()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index 688210a9f5..b93ca2f686 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -116,7 +116,7 @@ public unsafe void WriteBits(ulong value, uint bitCount) int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWriteBits)}()"); } #endif @@ -154,7 +154,7 @@ public void WriteBits(byte value, uint bitCount) int checkPos = (int)(m_BitPosition + bitCount); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWriteBits)}()"); } #endif @@ -175,7 +175,7 @@ public unsafe void WriteBit(bool bit) int checkPos = (m_BitPosition + 1); if (checkPos > m_AllowedBitwiseWriteMark) { - throw new OverflowException("Attempted to write without first calling FastBufferWriter.TryBeginWriteBits()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWriteBits)}()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 88c36a1b61..202ea9cd17 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -530,7 +530,7 @@ public unsafe void ReadPartialValue(out T value, int bytesToRead, int offsetB } if (PositionInternal + bytesToRead > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginRead()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()"); } #endif @@ -558,7 +558,7 @@ public unsafe void ReadByte(out byte value) } if (PositionInternal + 1 > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginRead()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()"); } #endif value = BufferPointer[PositionInternal++]; @@ -606,7 +606,7 @@ public unsafe void ReadBytes(byte* value, int size, int offset = 0) } if (PositionInternal + size > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginRead()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()"); } #endif UnsafeUtility.MemCpy(value + offset, (BufferPointer + PositionInternal), size); @@ -693,7 +693,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged } if (PositionInternal + len > AllowedReadMark) { - throw new OverflowException("Attempted to read without first calling TryBeginRead()"); + throw new OverflowException($"Attempted to read without first calling {nameof(TryBeginRead)}()"); } #endif diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index 4a6a44092c..f79a27eccd 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -516,7 +516,7 @@ public unsafe void WritePartialValue(T value, int bytesToWrite, int offsetByt } if (PositionInternal + bytesToWrite > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()"); } #endif @@ -542,7 +542,7 @@ public unsafe void WriteByte(byte value) } if (PositionInternal + 1 > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()"); } #endif BufferPointer[PositionInternal++] = value; @@ -590,7 +590,7 @@ public unsafe void WriteBytes(byte* value, int size, int offset = 0) } if (PositionInternal + size > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()"); } #endif UnsafeUtility.MemCpy((BufferPointer + PositionInternal), value + offset, size); @@ -724,7 +724,7 @@ public unsafe void WriteValue(in T value) where T : unmanaged } if (PositionInternal + len > AllowedWriteMark) { - throw new OverflowException("Attempted to write without first calling TryBeginWrite()"); + throw new OverflowException($"Attempted to write without first calling {nameof(TryBeginWrite)}()"); } #endif From bead9f54a181d75b87d16ccbf7ce506a6dc4485a Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Tue, 7 Sep 2021 21:33:10 -0500 Subject: [PATCH 26/29] -Removed BytewiseUtility.FastCopyBytes -Added documentation on PreChecked() functions. --- .../Runtime/Serialization/BitReader.cs | 3 +- .../Runtime/Serialization/BitWriter.cs | 3 +- .../Runtime/Serialization/BufferSerializer.cs | 33 +++++++- .../Runtime/Serialization/BytewiseUtility.cs | 80 ------------------- .../Serialization/BytewiseUtility.cs.meta | 11 --- .../Runtime/Serialization/FastBufferReader.cs | 4 +- .../Runtime/Serialization/FastBufferWriter.cs | 4 +- 7 files changed, 40 insertions(+), 98 deletions(-) delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs delete mode 100644 com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs index 172881e357..bb97d0c95b 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Netcode { @@ -175,7 +176,7 @@ private unsafe void ReadPartialValue(out T value, int bytesToRead, int offset var val = new T(); byte* ptr = ((byte*)&val) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(ptr, bufferPointer, bytesToRead); + UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead); m_BitPosition += bytesToRead * k_BitsPerByte; value = val; diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs index b93ca2f686..3895c506fe 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.CompilerServices; +using Unity.Collections.LowLevel.Unsafe; namespace Unity.Netcode { @@ -190,7 +191,7 @@ private unsafe void WritePartialValue(T value, int bytesToWrite, int offsetBy { byte* ptr = ((byte*)&value) + offsetBytes; byte* bufferPointer = m_BufferPointer + m_Position; - BytewiseUtility.FastCopyBytes(bufferPointer, ptr, bytesToWrite); + UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite); m_BitPosition += bytesToWrite * k_BitsPerByte; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs index a9132cb5bd..acfecdd2d5 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/BufferSerializer.cs @@ -223,6 +223,9 @@ public void SerializeValue(ref T value) where T : unmanaged /// go past the point you've marked using PreCheck(). In release builds, OverflowException will not be thrown /// for performance reasons, since the point of using PreCheck is to avoid bounds checking in the following /// operations in release builds. + /// + /// To get the correct size to check for, use FastBufferWriter.GetWriteSize(value) or + /// FastBufferWriter.GetWriteSize<type>() /// /// Number of bytes you plan to read or write /// True if the read/write can proceed, false otherwise. @@ -232,7 +235,11 @@ public bool PreCheck(int amount) } /// - /// Serialize a GameObject + /// Serialize a GameObject. + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref GameObject value) @@ -242,6 +249,10 @@ public void SerializeValuePreChecked(ref GameObject value) /// /// Serialize a NetworkObject + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref NetworkObject value) @@ -251,6 +262,10 @@ public void SerializeValuePreChecked(ref NetworkObject value) /// /// Serialize a NetworkBehaviour + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref NetworkBehaviour value) @@ -262,6 +277,10 @@ public void SerializeValuePreChecked(ref NetworkBehaviour value) /// Serialize a string. /// /// Note: Will ALWAYS allocate a new string when reading. + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize /// @@ -283,6 +302,10 @@ public void SerializeValuePreChecked(ref string s, bool oneByteChars = false) /// (This is because C# doesn't allow setting an array's length value, so deserializing /// into an existing array of larger size would result in an array that doesn't have as many values /// as its Length indicates it should.) + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref T[] array) where T : unmanaged @@ -292,6 +315,10 @@ public void SerializeValuePreChecked(ref T[] array) where T : unmanaged /// /// Serialize a single byte + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref byte value) @@ -302,6 +329,10 @@ public void SerializeValuePreChecked(ref byte value) /// /// Serialize an unmanaged type. Supports basic value types as well as structs. /// The provided type will be copied to/from the buffer as it exists in memory. + /// + /// Using the PreChecked versions of these functions requires calling PreCheck() ahead of time, and they should only + /// be called if PreCheck() returns true. This is an efficiency option, as it allows you to PreCheck() multiple + /// serialization operations in one function call instead of having to do bounds checking on every call. /// /// Value to serialize public void SerializeValuePreChecked(ref T value) where T : unmanaged diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs deleted file mode 100644 index 0732fca31e..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Unity.Collections.LowLevel.Unsafe; - -namespace Unity.Netcode -{ - public static class BytewiseUtility - { - /// - /// Helper function optimized for quickly copying small numbers of bytes. - /// Faster than UnsafeUtil.Memcpy and other alternatives for amount <= 8 - /// Slower for amount > 8 - /// - /// Pointer to the source value - /// Pointer to the destination value - /// Number of bytes to copy - public static unsafe void FastCopyBytes(byte* dest, byte* source, int amount) - { - // Switch statement to write small values with assignments - // is considerably faster than calling UnsafeUtility.MemCpy - // in all builds - editor, mono, and ILCPP - switch (amount) - { - case 1: - *dest = *source; - break; - case 2: - *dest = *source; - *(dest + 1) = *(source + 1); - break; - case 3: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - break; - case 4: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - break; - case 5: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - *(dest + 4) = *(source + 4); - break; - case 6: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - *(dest + 4) = *(source + 4); - *(dest + 5) = *(source + 5); - break; - case 7: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - *(dest + 4) = *(source + 4); - *(dest + 5) = *(source + 5); - *(dest + 6) = *(source + 6); - break; - case 8: - *dest = *source; - *(dest + 1) = *(source + 1); - *(dest + 2) = *(source + 2); - *(dest + 3) = *(source + 3); - *(dest + 4) = *(source + 4); - *(dest + 5) = *(source + 5); - *(dest + 6) = *(source + 6); - *(dest + 7) = *(source + 7); - break; - default: - UnsafeUtility.MemCpy(dest, source, amount); - break; - } - } - } -} diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta b/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta deleted file mode 100644 index 12bebf3d01..0000000000 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/BytewiseUtility.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5df078ced492f8c45966997ceda09c8f -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 202ea9cd17..602c36c577 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -699,7 +699,7 @@ public unsafe void ReadValue(out T value) where T : unmanaged fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes((byte*)ptr, BufferPointer + PositionInternal, len); + UnsafeUtility.MemCpy((byte*)ptr, BufferPointer + PositionInternal, len); } PositionInternal += len; } @@ -734,7 +734,7 @@ public unsafe void ReadValueSafe(out T value) where T : unmanaged fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes((byte*)ptr, BufferPointer + PositionInternal, len); + UnsafeUtility.MemCpy((byte*)ptr, BufferPointer + PositionInternal, len); } PositionInternal += len; } diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs index f79a27eccd..8e7ec57933 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferWriter.cs @@ -730,7 +730,7 @@ public unsafe void WriteValue(in T value) where T : unmanaged fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)ptr, len); + UnsafeUtility.MemCpy(BufferPointer + PositionInternal, (byte*)ptr, len); } PositionInternal += len; } @@ -764,7 +764,7 @@ public unsafe void WriteValueSafe(in T value) where T : unmanaged fixed (T* ptr = &value) { - BytewiseUtility.FastCopyBytes(BufferPointer + PositionInternal, (byte*)ptr, len); + UnsafeUtility.MemCpy(BufferPointer + PositionInternal, (byte*)ptr, len); } PositionInternal += len; } From 5063260998f087c2f43f12db97f77849ac17cb97 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 18:45:22 -0500 Subject: [PATCH 27/29] removed .gitignore. --- testproject/Assets/StreamingAssets/.gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 testproject/Assets/StreamingAssets/.gitignore diff --git a/testproject/Assets/StreamingAssets/.gitignore b/testproject/Assets/StreamingAssets/.gitignore deleted file mode 100644 index 3b64b774a1..0000000000 --- a/testproject/Assets/StreamingAssets/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/BuildInfo.json -/BuildInfo.json.meta From 2f0179878bebce31ab63bae2a0a339df64603019 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 19:38:40 -0500 Subject: [PATCH 28/29] Fixed compile errors that somehow didn't happen on my machine until I looked at the files they were in? --- .../Editor/Serialization/BaseFastBufferReaderWriterTest.cs | 4 +--- .../Tests/Editor/Serialization/BufferSerializerTests.cs | 4 +--- .../Tests/Editor/com.unity.netcode.editortests.asmdef | 2 -- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index ae5e1aefa0..88f55a5551 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -135,8 +135,6 @@ protected void RunGameObjectTest(GameObjectTestDelegate testCode) // Set the NetworkConfig networkManager.NetworkConfig = new NetworkConfig() { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, // Set transport NetworkTransport = obj.AddComponent() }; @@ -150,7 +148,7 @@ protected void RunGameObjectTest(GameObjectTestDelegate testCode) finally { UnityEngine.Object.DestroyImmediate(obj); - networkManager.StopHost(); + networkManager.Shutdown(); } } #endregion diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 0f050f6301..876cc4ab12 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -404,8 +404,6 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) // Set the NetworkConfig networkManager.NetworkConfig = new NetworkConfig() { - // Set the current scene to prevent unexpected log messages which would trigger a failure - RegisteredScenes = new List() { SceneManager.GetActiveScene().name }, // Set transport NetworkTransport = obj.AddComponent() }; @@ -419,7 +417,7 @@ private void RunGameObjectTest(GameObjectTestDelegate testCode) finally { UnityEngine.Object.DestroyImmediate(obj); - networkManager.StopServer(); + networkManager.Shutdown(); } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef b/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef index d11759b336..a08074009c 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef @@ -4,8 +4,6 @@ "references": [ "Unity.Netcode.Runtime", "Unity.Netcode.Editor", - "UnityEngine.TestRunner", - "UnityEditor.TestRunner", "Unity.Multiplayer.MetricTypes", "Unity.Multiplayer.NetStats" ], From 3da5b38b0cbce52719f2be429d466eb8c607f2e5 Mon Sep 17 00:00:00 2001 From: Jaedyn Draper Date: Wed, 15 Sep 2021 20:13:58 -0500 Subject: [PATCH 29/29] standards.py --fix ... again ... despite no changes since the last success other than integrating develop ... --- .../Runtime/Serialization/FastBufferReader.cs | 10 +-- .../BaseFastBufferReaderWriterTest.cs | 2 - .../Editor/Serialization/BitReaderTests.cs | 40 +++++------- .../Editor/Serialization/BitWriterTests.cs | 46 ++++++------- .../Serialization/BufferSerializerTests.cs | 2 - .../Serialization/FastBufferReaderTests.cs | 64 +++++++------------ .../Serialization/FastBufferWriterTests.cs | 60 ++++++----------- 7 files changed, 81 insertions(+), 143 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs index 602c36c577..add6a0d487 100644 --- a/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs +++ b/com.unity.netcode.gameobjects/Runtime/Serialization/FastBufferReader.cs @@ -48,7 +48,7 @@ public unsafe FastBufferReader(NativeArray buffer, Allocator allocator, in LengthInternal = Math.Max(1, length == -1 ? buffer.Length : length); if (allocator == Allocator.None) { - BufferPointer = (byte*) buffer.GetUnsafePtr() + offset; + BufferPointer = (byte*)buffer.GetUnsafePtr() + offset; } else { @@ -88,7 +88,7 @@ public unsafe FastBufferReader(ArraySegment buffer, Allocator allocator, i UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); } - BufferPointer = (byte*) bufferPtr; + BufferPointer = (byte*)bufferPtr; } PositionInternal = 0; @@ -123,7 +123,7 @@ public unsafe FastBufferReader(byte[] buffer, Allocator allocator, int length = UnsafeUtility.MemCpy(bufferPtr, data + offset, LengthInternal); } - BufferPointer = (byte*) bufferPtr; + BufferPointer = (byte*)bufferPtr; } PositionInternal = 0; @@ -148,13 +148,13 @@ public unsafe FastBufferReader(byte* buffer, Allocator allocator, int length, in LengthInternal = Math.Max(1, length); if (allocator == Allocator.None) { - BufferPointer = buffer + offset; + BufferPointer = buffer + offset; } else { void* bufferPtr = UnsafeUtility.Malloc(LengthInternal, UnsafeUtility.AlignOf(), allocator); UnsafeUtility.MemCpy(bufferPtr, buffer + offset, LengthInternal); - BufferPointer = (byte*) bufferPtr; + BufferPointer = (byte*)bufferPtr; } PositionInternal = 0; diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs index 88f55a5551..fa067d73a6 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BaseFastBufferReaderWriterTest.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using NUnit.Framework; using Unity.Netcode.EditorTests; using UnityEngine; -using UnityEngine.SceneManagement; using Random = System.Random; namespace Unity.Netcode diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs index 87b0154ee5..e8c4cf3f29 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitReaderTests.cs @@ -317,26 +317,20 @@ public unsafe void TestReadingBitsThrowsIfTryBeginReadNotCalled() Assert.Throws(() => { - using (var bitReader = reader.EnterBitwiseContext()) - { - bitReader.ReadBit(out bool b); - } + using var bitReader = reader.EnterBitwiseContext(); + bitReader.ReadBit(out bool b); }); Assert.Throws(() => { - using (var bitReader = reader.EnterBitwiseContext()) - { - bitReader.ReadBits(out byte b, 1); - } + using var bitReader = reader.EnterBitwiseContext(); + bitReader.ReadBits(out byte b, 1); }); Assert.Throws(() => { - using (var bitReader = reader.EnterBitwiseContext()) - { - bitReader.ReadBits(out ulong ul, 1); - } + using var bitReader = reader.EnterBitwiseContext(); + bitReader.ReadBits(out ulong ul, 1); }); Assert.AreEqual(0, reader.Position); @@ -344,21 +338,19 @@ public unsafe void TestReadingBitsThrowsIfTryBeginReadNotCalled() Assert.Throws(() => { Assert.IsTrue(reader.TryBeginRead(1)); - using (var bitReader = reader.EnterBitwiseContext()) + using var bitReader = reader.EnterBitwiseContext(); + ulong ul; + try { - ulong ul; - try - { - bitReader.ReadBits(out ul, 4); - bitReader.ReadBits(out ul, 4); - } - catch (OverflowException e) - { - Assert.Fail("Overflow exception was thrown too early."); - throw; - } bitReader.ReadBits(out ul, 4); + bitReader.ReadBits(out ul, 4); + } + catch (OverflowException e) + { + Assert.Fail("Overflow exception was thrown too early."); + throw; } + bitReader.ReadBits(out ul, 4); }); } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs index cd51c356de..ab98d06da9 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BitWriterTests.cs @@ -270,34 +270,26 @@ public unsafe void TestWritingBitsThrowsIfTryBeginWriteNotCalled() Assert.Throws(() => { - using (var bitWriter = writer.EnterBitwiseContext()) - { - bitWriter.WriteBit(true); - } + using var bitWriter = writer.EnterBitwiseContext(); + bitWriter.WriteBit(true); }); Assert.Throws(() => { - using (var bitWriter = writer.EnterBitwiseContext()) - { - bitWriter.WriteBit(false); - } + using var bitWriter = writer.EnterBitwiseContext(); + bitWriter.WriteBit(false); }); Assert.Throws(() => { - using (var bitWriter = writer.EnterBitwiseContext()) - { - bitWriter.WriteBits(0b11111111, 1); - } + using var bitWriter = writer.EnterBitwiseContext(); + bitWriter.WriteBits(0b11111111, 1); }); Assert.Throws(() => { - using (var bitWriter = writer.EnterBitwiseContext()) - { - bitWriter.WriteBits(0b11111111UL, 1); - } + using var bitWriter = writer.EnterBitwiseContext(); + bitWriter.WriteBits(0b11111111UL, 1); }); Assert.AreEqual(0, writer.Position); @@ -310,20 +302,18 @@ public unsafe void TestWritingBitsThrowsIfTryBeginWriteNotCalled() Assert.Throws(() => { Assert.IsTrue(writer.TryBeginWrite(1)); - using (var bitWriter = writer.EnterBitwiseContext()) + using var bitWriter = writer.EnterBitwiseContext(); + try { - try - { - bitWriter.WriteBits(0b11111111UL, 4); - bitWriter.WriteBits(0b11111111UL, 4); - } - catch (OverflowException e) - { - Assert.Fail("Overflow exception was thrown too early."); - throw; - } - bitWriter.WriteBits(0b11111111UL, 1); + bitWriter.WriteBits(0b11111111UL, 4); + bitWriter.WriteBits(0b11111111UL, 4); } + catch (OverflowException e) + { + Assert.Fail("Overflow exception was thrown too early."); + throw; + } + bitWriter.WriteBits(0b11111111UL, 1); }); } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs index 876cc4ab12..1847dd0954 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/BufferSerializerTests.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; using UnityEngine; -using UnityEngine.SceneManagement; using Random = System.Random; namespace Unity.Netcode.EditorTests diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs index 52a9180c1e..0f3e36e2e8 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferReaderTests.cs @@ -501,10 +501,8 @@ public void WhenCallingReadByteDuringBitwiseContext_InvalidOperationExceptionIsT using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadByte(out byte b); }); } } @@ -516,11 +514,9 @@ public void WhenCallingReadBytesDuringBitwiseContext_InvalidOperationExceptionIs using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - byte[] b = { 0, 1, 2 }; - Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); - } + using var context = emptyReader.EnterBitwiseContext(); + byte[] b = { 0, 1, 2 }; + Assert.Throws(() => { emptyReader.ReadBytes(ref b, 3); }); } } @@ -532,10 +528,8 @@ public void WhenCallingReadValueWithUnmanagedTypeDuringBitwiseContext_InvalidOpe using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValue(out int i); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValue(out int i); }); } } @@ -547,10 +541,8 @@ public void WhenCallingReadValueWithByteArrayDuringBitwiseContext_InvalidOperati using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValue(out byte[] b); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValue(out byte[] b); }); } } @@ -562,10 +554,8 @@ public void WhenCallingReadValueWithStringDuringBitwiseContext_InvalidOperationE using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValue(out string s); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValue(out string s); }); } } @@ -577,10 +567,8 @@ public void WhenCallingReadByteSafeDuringBitwiseContext_InvalidOperationExceptio using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadByteSafe(out byte b); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadByteSafe(out byte b); }); } } @@ -592,11 +580,9 @@ public void WhenCallingReadBytesSafeDuringBitwiseContext_InvalidOperationExcepti using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - byte[] b = { 0, 1, 2 }; - Assert.Throws(() => { emptyReader.ReadBytesSafe(ref b, 3); }); - } + using var context = emptyReader.EnterBitwiseContext(); + byte[] b = { 0, 1, 2 }; + Assert.Throws(() => { emptyReader.ReadBytesSafe(ref b, 3); }); } } @@ -608,10 +594,8 @@ public void WhenCallingReadValueSafeWithUnmanagedTypeDuringBitwiseContext_Invali using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValueSafe(out int i); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValueSafe(out int i); }); } } @@ -623,10 +607,8 @@ public void WhenCallingReadValueSafeWithByteArrayDuringBitwiseContext_InvalidOpe using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValueSafe(out byte[] b); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValueSafe(out byte[] b); }); } } @@ -638,10 +620,8 @@ public void WhenCallingReadValueSafeWithStringDuringBitwiseContext_InvalidOperat using (emptyReader) { - using (var context = emptyReader.EnterBitwiseContext()) - { - Assert.Throws(() => { emptyReader.ReadValueSafe(out string s); }); - } + using var context = emptyReader.EnterBitwiseContext(); + Assert.Throws(() => { emptyReader.ReadValueSafe(out string s); }); } } diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs index 54dca7131a..f1d3897737 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Serialization/FastBufferWriterTests.cs @@ -480,10 +480,8 @@ public void WhenCallingWriteByteDuringBitwiseContext_InvalidOperationExceptionIs using (writer) { writer.TryBeginWrite(100); - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteByte(1); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteByte(1); }); } } @@ -496,10 +494,8 @@ public void WhenCallingWriteBytesDuringBitwiseContext_InvalidOperationExceptionI { writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } } @@ -512,10 +508,8 @@ public void WhenCallingWriteValueWithUnmanagedTypeDuringBitwiseContext_InvalidOp { writer.TryBeginWrite(100); int i = 1; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteValue(i); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteValue(i); }); } } @@ -528,10 +522,8 @@ public void WhenCallingWriteValueWithByteArrayDuringBitwiseContext_InvalidOperat { writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteBytes(bytes, bytes.Length); }); } } @@ -545,10 +537,8 @@ public void WhenCallingWriteValueWithStringDuringBitwiseContext_InvalidOperation writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; int i = 1; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteValue(""); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteValue(""); }); } } @@ -560,10 +550,8 @@ public void WhenCallingWriteByteSafeDuringBitwiseContext_InvalidOperationExcepti using (writer) { writer.TryBeginWrite(100); - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteByteSafe(1); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteByteSafe(1); }); } } @@ -576,10 +564,8 @@ public void WhenCallingWriteBytesSafeDuringBitwiseContext_InvalidOperationExcept { writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); } } @@ -592,10 +578,8 @@ public void WhenCallingWriteValueSafeWithUnmanagedTypeDuringBitwiseContext_Inval { writer.TryBeginWrite(100); int i = 1; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteValueSafe(i); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteValueSafe(i); }); } } @@ -608,10 +592,8 @@ public void WhenCallingWriteValueSafeWithByteArrayDuringBitwiseContext_InvalidOp { writer.TryBeginWrite(100); var bytes = new byte[] { 0, 1, 2 }; - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteBytesSafe(bytes, bytes.Length); }); } } @@ -623,10 +605,8 @@ public void WhenCallingWriteValueSafeWithStringDuringBitwiseContext_InvalidOpera using (writer) { writer.TryBeginWrite(100); - using (var context = writer.EnterBitwiseContext()) - { - Assert.Throws(() => { writer.WriteValueSafe(""); }); - } + using var context = writer.EnterBitwiseContext(); + Assert.Throws(() => { writer.WriteValueSafe(""); }); } }