Skip to content

feat: Fast buffer reader and fast buffer writer #1082

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Sep 16, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2fa0f6d
FastBufferWriter implemented and tested (still need to add and update…
ShadauxCat Aug 17, 2021
c36b3af
A few additional tests to cover the last missed cases I can think of …
ShadauxCat Aug 17, 2021
5ac5ef9
FastBufferReader + tests
ShadauxCat Aug 18, 2021
4776737
- More tests
ShadauxCat Aug 19, 2021
f245150
- Removed NativeArray from FastBufferReader and FastBufferWriter, rep…
ShadauxCat Aug 19, 2021
fc944c4
Added utility ref struct "Ref<T>" to more generically support wrappin…
ShadauxCat Aug 19, 2021
cb95862
BufferSerializer and tests.
ShadauxCat Aug 23, 2021
a3e0abc
Removed unnecessary comment.
ShadauxCat Aug 23, 2021
ea31f10
XMLDocs + cleanup for PR
ShadauxCat Aug 24, 2021
057c1fc
Merge branch 'develop' into feature/fast_buffer_reader_writer
ShadauxCat Aug 24, 2021
63309e2
Replaced possibly unaligned memory access with UnsafeUtility.MemCpy..…
ShadauxCat Aug 24, 2021
e9b9305
Resurrected BytewiseUtil.FastCopyBytes as a faster alternative to Uns…
ShadauxCat Aug 24, 2021
aa56440
Reverting an accidental change.
ShadauxCat Aug 24, 2021
fabb3b8
Removed files that got accidentally duplicated from before the rename.
ShadauxCat Aug 24, 2021
3a2a50b
Standards fixes
ShadauxCat Aug 24, 2021
58db1bd
Removed accidentally added files.
ShadauxCat Aug 24, 2021
c827339
Added BuildInfo.json to the .gitignore so I stop accidentally checkin…
ShadauxCat Aug 24, 2021
f8bfb2f
Addressed most of the review feedback. Still need to do a little more…
ShadauxCat Aug 26, 2021
9816b50
standards.py --fix
ShadauxCat Aug 26, 2021
a576552
standards.py --fix
ShadauxCat Aug 26, 2021
5193e39
Fixed incorrect namespaces.
ShadauxCat Aug 26, 2021
119ee2f
-Fixed a couple of issues where growing a FastBufferWriter wouldn't w…
ShadauxCat Aug 26, 2021
7fb2d53
Fix a test failure and better implementation of large growths
ShadauxCat Aug 27, 2021
8ee4610
- Removed RefArray
ShadauxCat Aug 31, 2021
f4b7ea9
Removed RefArray meta file that stuck around.
ShadauxCat Aug 31, 2021
d983f07
Review feedback: Used nameof() instead of string literal.
ShadauxCat Aug 31, 2021
bead9f5
-Removed BytewiseUtility.FastCopyBytes
ShadauxCat Sep 8, 2021
5063260
removed .gitignore.
ShadauxCat Sep 15, 2021
ade5d7c
Merge branch 'develop' into feature/fast_buffer_reader_writer
ShadauxCat Sep 15, 2021
2f01798
Fixed compile errors that somehow didn't happen on my machine until I…
ShadauxCat Sep 16, 2021
3da5b38
standards.py --fix ... again ... despite no changes since the last su…
ShadauxCat Sep 16, 2021
409d6fe
Merge branch 'develop' into feature/fast_buffer_reader_writer
ShadauxCat Sep 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Addressed most of the review feedback. Still need to do a little more…
… restructuring of some of the other tests.
  • Loading branch information
ShadauxCat committed Aug 26, 2021
commit f8bfb2f80e71d211ff07d5872d0c03d6ba5f9b02
24 changes: 12 additions & 12 deletions com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void Dispose()
/// </summary>
/// <param name="bitCount">Number of bits you want to read, in total</param>
/// <returns>True if you can read, false if that would exceed buffer bounds</returns>
public bool VerifyCanReadBits(int bitCount)
public bool TryBeginReadBits(uint bitCount)
{
var newBitPosition = m_BitPosition + bitCount;
var totalBytesWrittenInBitwiseContext = newBitPosition >> 3;
Expand All @@ -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;
}
Expand All @@ -90,7 +90,7 @@ public bool VerifyCanReadBits(int bitCount)
/// </summary>
/// <param name="value">Value to store bits into.</param>
/// <param name="bitCount">Amount of bits to read</param>
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)
Expand All @@ -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)
{
Expand All @@ -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;
}

Expand All @@ -137,16 +137,16 @@ public unsafe void ReadBits(out ulong value, int bitCount)
/// </summary>
/// <param name="value">Value to store bits into.</param>
/// <param name="bitCount">Amount of bits to read.</param>
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);
}

/// <summary>
Expand All @@ -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

Expand Down
38 changes: 19 additions & 19 deletions com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,20 @@ public void Dispose()
}

/// <summary>
/// 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.
/// </summary>
/// <param name="bitCount">Number of bits you want to write, in total</param>
/// <returns>True if you can write, false if that would exceed buffer bounds</returns>
public unsafe bool VerifyCanWriteBits(int bitCount)
public unsafe bool TryBeginWriteBits(int bitCount)
{
var newBitPosition = m_BitPosition + bitCount;
var totalBytesWrittenInBitwiseContext = newBitPosition >> 3;
Expand Down Expand Up @@ -96,27 +101,22 @@ public unsafe bool VerifyCanWriteBits(int bitCount)
/// </summary>
/// <param name="value">Value to get bits from.</param>
/// <param name="bitCount">Amount of bits to write</param>
public unsafe void WriteBits(ulong value, int bitCount)
public unsafe void WriteBits(ulong value, uint 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);
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)
{
Expand Down Expand Up @@ -144,13 +144,13 @@ public unsafe void WriteBits(ulong value, int bitCount)
/// </summary>
/// <param name="value">Value to get bits from.</param>
/// <param name="bitCount">Amount of bits to write.</param>
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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public void SerializeNetworkSerializable<T>(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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public void SerializeNetworkSerializable<T>(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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public static void WriteObjectPacked(ref FastBufferWriter writer, object value,
}
if (value is GameObject)
{
var networkObject = ((GameObject)value).GetComponent<NetworkObject>();
((GameObject)value).TryGetComponent<NetworkObject>(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}");
Expand Down Expand Up @@ -453,15 +453,15 @@ 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");
}
writer.WriteByte((byte)(value << 1));
return;
}

if (!writer.VerifyCanWriteInternal(2))
if (!writer.TryBeginWriteInternal(2))
{
throw new OverflowException("Writing past the end of the buffer");
}
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}
Expand All @@ -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");
}
Expand All @@ -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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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");
}
Expand All @@ -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");
}
Expand Down
Loading