Skip to content

Commit 7b6bcc6

Browse files
fix: NCCBUG-175: BitReader/BitWriter Reading/writing more than 8 bits was producing the wrong result (#2130)
* fix: NCCBUG-175: BitReader/BitWriter Reading/writing more than 8 bits was producing the wrong result * Standards + changelog Co-authored-by: Noel Stephens <[email protected]>
1 parent bc4f538 commit 7b6bcc6

File tree

6 files changed

+112
-13
lines changed

6 files changed

+112
-13
lines changed

com.unity.netcode.gameobjects/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
# Changelog
23

34
All notable changes to this project will be documented in this file.
@@ -16,6 +17,7 @@ Additional documentation and release notes are available at [Multiplayer Documen
1617

1718
### Fixed
1819

20+
- Fixed an issue where reading/writing more than 8 bits at a time with BitReader/BitWriter would write/read from the wrong place, returning and incorrect result. (#2130)
1921
- Fixed Owner-written NetworkVariable infinitely write themselves (#2109)
2022
- Fixed NetworkList issue that showed when inserting at the very end of a NetworkList (#2099)
2123
- Fixed issue where a client owner of a `NetworkVariable` with both owner read and write permissions would not update the server side when changed. (#2097)

com.unity.netcode.gameobjects/Runtime/Serialization/BitReader.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public ref struct BitReader
2121

2222
private const int k_BitsPerByte = 8;
2323

24+
private int BytePosition => m_BitPosition >> 3;
25+
2426
/// <summary>
2527
/// Whether or not the current BitPosition is evenly divisible by 8. I.e. whether or not the BitPosition is at a byte boundary.
2628
/// </summary>
@@ -98,11 +100,6 @@ public unsafe void ReadBits(out ulong value, uint bitCount)
98100
throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read more than 64 bits from a 64-bit value!");
99101
}
100102

101-
if (bitCount < 0)
102-
{
103-
throw new ArgumentOutOfRangeException(nameof(bitCount), "Cannot read fewer than 0 bits!");
104-
}
105-
106103
int checkPos = (int)(m_BitPosition + bitCount);
107104
if (checkPos > m_AllowedBitwiseReadMark)
108105
{
@@ -165,7 +162,7 @@ public unsafe void ReadBit(out bool bit)
165162
#endif
166163

167164
int offset = m_BitPosition & 7;
168-
int pos = m_BitPosition >> 3;
165+
int pos = BytePosition;
169166
bit = (m_BufferPointer[pos] & (1 << offset)) != 0;
170167
++m_BitPosition;
171168
}
@@ -175,7 +172,7 @@ private unsafe void ReadPartialValue<T>(out T value, int bytesToRead, int offset
175172
{
176173
var val = new T();
177174
byte* ptr = ((byte*)&val) + offsetBytes;
178-
byte* bufferPointer = m_BufferPointer + m_Position;
175+
byte* bufferPointer = m_BufferPointer + BytePosition;
179176
UnsafeUtility.MemCpy(ptr, bufferPointer, bytesToRead);
180177

181178
m_BitPosition += bytesToRead * k_BitsPerByte;

com.unity.netcode.gameobjects/Runtime/Serialization/BitWriter.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public bool BitAligned
2929
get => (m_BitPosition & 7) == 0;
3030
}
3131

32+
private int BytePosition => m_BitPosition >> 3;
33+
3234
internal unsafe BitWriter(FastBufferWriter writer)
3335
{
3436
m_Writer = writer;
@@ -181,7 +183,7 @@ public unsafe void WriteBit(bool bit)
181183
#endif
182184

183185
int offset = m_BitPosition & 7;
184-
int pos = m_BitPosition >> 3;
186+
int pos = BytePosition;
185187
++m_BitPosition;
186188
m_BufferPointer[pos] = (byte)(bit ? (m_BufferPointer[pos] & ~(1 << offset)) | (1 << offset) : (m_BufferPointer[pos] & ~(1 << offset)));
187189
}
@@ -190,7 +192,7 @@ public unsafe void WriteBit(bool bit)
190192
private unsafe void WritePartialValue<T>(T value, int bytesToWrite, int offsetBytes = 0) where T : unmanaged
191193
{
192194
byte* ptr = ((byte*)&value) + offsetBytes;
193-
byte* bufferPointer = m_BufferPointer + m_Position;
195+
byte* bufferPointer = m_BufferPointer + BytePosition;
194196
UnsafeUtility.MemCpy(bufferPointer, ptr, bytesToWrite);
195197

196198
m_BitPosition += bytesToWrite * k_BitsPerByte;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using NUnit.Framework;
2+
using Unity.Collections;
3+
4+
namespace Unity.Netcode.EditorTests
5+
{
6+
public class UserBitReaderAndBitWriterTests_NCCBUG175
7+
{
8+
9+
[Test]
10+
public void WhenBitwiseWritingMoreThan8Bits_ValuesAreCorrect()
11+
{
12+
using var writer = new FastBufferWriter(1024, Allocator.Temp);
13+
ulong inVal = 123456789;
14+
15+
for (int i = 0; i < 100; ++i)
16+
{
17+
writer.WriteValueSafe(i);
18+
}
19+
20+
using (var bitWriter = writer.EnterBitwiseContext())
21+
{
22+
for (int i = 0; i < 16; ++i)
23+
{
24+
Assert.IsTrue((bitWriter.TryBeginWriteBits(32)));
25+
bitWriter.WriteBits(inVal, 31);
26+
bitWriter.WriteBit(true);
27+
}
28+
}
29+
30+
using var reader = new FastBufferReader(writer, Allocator.Temp);
31+
32+
for (int i = 0; i < 100; ++i)
33+
{
34+
reader.ReadValueSafe(out int outVal);
35+
Assert.AreEqual(i, outVal);
36+
}
37+
38+
using var bitReader = reader.EnterBitwiseContext();
39+
for (int i = 0; i < 16; ++i)
40+
{
41+
Assert.IsTrue(bitReader.TryBeginReadBits(32));
42+
bitReader.ReadBits(out ulong outVal, 31);
43+
bitReader.ReadBit(out bool bit);
44+
Assert.AreEqual(inVal, outVal);
45+
Assert.AreEqual(true, bit);
46+
}
47+
}
48+
49+
[Test]
50+
public void WhenBitwiseReadingMoreThan8Bits_ValuesAreCorrect()
51+
{
52+
using var writer = new FastBufferWriter(1024, Allocator.Temp);
53+
ulong inVal = 123456789;
54+
55+
for (int i = 0; i < 100; ++i)
56+
{
57+
writer.WriteValueSafe(i);
58+
}
59+
60+
uint combined = (uint)inVal | (1u << 31);
61+
writer.WriteValueSafe(combined);
62+
writer.WriteValueSafe(combined);
63+
writer.WriteValueSafe(combined);
64+
65+
using var reader = new FastBufferReader(writer, Allocator.Temp);
66+
67+
for (int i = 0; i < 100; ++i)
68+
{
69+
reader.ReadValueSafe(out int outVal);
70+
Assert.AreEqual(i, outVal);
71+
}
72+
73+
using (var bitReader = reader.EnterBitwiseContext())
74+
{
75+
Assert.IsTrue(bitReader.TryBeginReadBits(32));
76+
bitReader.ReadBits(out ulong outVal, 31);
77+
bitReader.ReadBit(out bool bit);
78+
Assert.AreEqual(inVal, outVal);
79+
Assert.AreEqual(true, bit);
80+
81+
Assert.IsTrue(bitReader.TryBeginReadBits(32));
82+
bitReader.ReadBits(out outVal, 31);
83+
bitReader.ReadBit(out bit);
84+
Assert.AreEqual(inVal, outVal);
85+
Assert.AreEqual(true, bit);
86+
87+
Assert.IsTrue(bitReader.TryBeginReadBits(32));
88+
bitReader.ReadBits(out outVal, 31);
89+
bitReader.ReadBit(out bit);
90+
Assert.AreEqual(inVal, outVal);
91+
Assert.AreEqual(true, bit);
92+
}
93+
}
94+
}
95+
}

com.unity.netcode.gameobjects/Tests/Editor/Serialization/UserBitReaderAndBitWriterTests_NCCBUG175.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dotnet-tools/netcode.standards/Program.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ private static int Main(
3535
var procInfo = new ProcessStartInfo("dotnet");
3636

3737
procInfo.Arguments = check
38-
? $"format {file} whitespace --no-restore --verify-no-changes --verbosity {verbosity}"
39-
: $"format {file} whitespace --no-restore --verbosity {verbosity}";
38+
? $"format whitespace {file} --no-restore --verify-no-changes --verbosity {verbosity}"
39+
: $"format whitespace {file} --no-restore --verbosity {verbosity}";
4040
Console.WriteLine($"######## START -> {(check ? "check" : "fix")} whitespace issues");
4141
var whitespace = Process.Start(procInfo);
4242
whitespace.WaitForExit();
@@ -48,8 +48,8 @@ private static int Main(
4848
Console.WriteLine("######## SUCCEEDED -> no whitespace issues");
4949

5050
procInfo.Arguments = check
51-
? $"format {file} style --severity error --no-restore --verify-no-changes --verbosity {verbosity}"
52-
: $"format {file} style --severity error --no-restore --verbosity {verbosity}";
51+
? $"format style {file} --severity error --no-restore --verify-no-changes --verbosity {verbosity}"
52+
: $"format style {file} --severity error --no-restore --verbosity {verbosity}";
5353
Console.WriteLine($"######## START -> {(check ? "check" : "fix")} style/naming issues");
5454
var style = Process.Start(procInfo);
5555
style.WaitForExit();

0 commit comments

Comments
 (0)