Files
UnrealEngine/Engine/Plugins/Runtime/ReplicationSystemTestPlugin/Source/Private/Tests/Serialization/TestNetBitStreams.cpp
2025-05-18 13:04:45 +08:00

963 lines
29 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "NetworkAutomationTest.h"
#include "NetworkAutomationTestMacros.h"
#include "Iris/Serialization/NetBitStreamReader.h"
#include "Iris/Serialization/NetBitStreamWriter.h"
#include "Iris/Serialization/NetBitStreamUtil.h"
namespace UE::Net::Private
{
class FNetBitStreamTestWriteBuffer
{
public:
FNetBitStreamTestWriteBuffer()
{
Reset();
}
inline void* GetBuffer() { return &Buffer[0]; }
uint32 GetBufferCapacity() const { return sizeof(Buffer); }
private:
void Reset() {}
uint32 Buffer[16];
};
class FNetBitStreamWriterTest : public FNetworkAutomationTestSuiteFixture
{
protected:
FNetBitStreamTestWriteBuffer Buffer;
FNetBitStreamWriter Writer;
};
class FNetBitStreamReaderTest : public FNetworkAutomationTestSuiteFixture
{
protected:
FNetBitStreamTestWriteBuffer Buffer;
FNetBitStreamReader Reader;
};
class FNetBitStreamReaderWriterTest : public FNetworkAutomationTestSuiteFixture
{
protected:
FNetBitStreamTestWriteBuffer Buffer;
FNetBitStreamWriter Writer;
FNetBitStreamReader Reader;
};
class FNetBitStreamWriterSubstreamTest : public FNetBitStreamWriterTest
{
};
class FNetBitStreamReaderSubstreamTest : public FNetBitStreamReaderTest
{
};
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, TestInitState)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
const uint32 StartPos = Writer.GetPosBits();
UE_NET_ASSERT_EQ(StartPos, 0U);
const bool bIsOverflown = Writer.IsOverflown();
UE_NET_ASSERT_FALSE(bIsOverflown);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, CanSeek)
{
const uint32 SeekPositions[] = {0U, 47U, 11U};
const size_t TestCount = sizeof(SeekPositions) / sizeof(SeekPositions[0]);
for (size_t TestIt = 0; TestIt != TestCount; ++TestIt)
{
const uint32 SeekPosition = SeekPositions[TestIt];
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(SeekPosition);
const uint32 CurrentPosition = Writer.GetPosBits();
UE_NET_ASSERT_EQ(CurrentPosition, SeekPosition);
}
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, WriteZeroBitsAtEndDoesNotOverflow)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(Buffer.GetBufferCapacity()*8U);
Writer.WriteBits(0U, 0U);
UE_NET_ASSERT_FALSE(Writer.IsOverflown());
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, WriteBitsAtEndCausesOverflow)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(Buffer.GetBufferCapacity()*8U);
Writer.WriteBits(0U, 1U);
UE_NET_ASSERT_TRUE(Writer.IsOverflown());
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, SeekToValidPositionAfterOverflowClearsOverflow)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(Buffer.GetBufferCapacity()*8U);
Writer.WriteBits(0U, 1U);
UE_NET_EXPECT_TRUE(Writer.IsOverflown());
Writer.Seek(Buffer.GetBufferCapacity()*8U);
UE_NET_ASSERT_FALSE(Writer.IsOverflown());
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, InitBytesAfterOverflowClearsOverflow)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(Buffer.GetBufferCapacity() * 8U);
Writer.WriteBits(0U, 1U);
UE_NET_EXPECT_TRUE(Writer.IsOverflown());
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
UE_NET_ASSERT_FALSE(Writer.IsOverflown());
}
// FNetBitStreamReader tests
UE_NET_TEST_FIXTURE(FNetBitStreamReaderTest, TestInitState)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
const uint32 StartPos = Reader.GetPosBits();
UE_NET_ASSERT_EQ(StartPos, 0U);
const bool bIsOverflown = Reader.IsOverflown();
UE_NET_ASSERT_FALSE(bIsOverflown);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderTest, CanSeek)
{
const uint32 SeekPositions[] = { 0U, 75U, 12U };
const size_t TestCount = sizeof(SeekPositions)/sizeof(SeekPositions[0]);
for (size_t TestIt = 0; TestIt != TestCount; ++TestIt)
{
const uint32 SeekPosition = SeekPositions[TestIt];
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
Reader.Seek(SeekPosition);
const uint32 CurrentPosition = Reader.GetPosBits();
UE_NET_ASSERT_EQ(CurrentPosition, SeekPosition);
}
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderTest, ReadZeroBitsAtEndDoesNotOverflow)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8U);
Reader.Seek(Buffer.GetBufferCapacity()*8U);
Reader.ReadBits(0U);
UE_NET_ASSERT_FALSE(Reader.IsOverflown());
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderTest, ReadBitsAtEndCausesOverflow)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8U);
Reader.Seek(Buffer.GetBufferCapacity()*8U);
Reader.ReadBits(1U);
UE_NET_ASSERT_TRUE(Reader.IsOverflown());
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderTest, SeekToValidPositionAfterOverflowClearsOverflow)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8U);
Reader.Seek(Buffer.GetBufferCapacity()*8U);
Reader.ReadBits(1U);
UE_NET_EXPECT_TRUE(Reader.IsOverflown());
Reader.Seek(Buffer.GetBufferCapacity()*8U);
UE_NET_ASSERT_FALSE(Reader.IsOverflown());
}
// Combined reader/writer tests
UE_NET_TEST_FIXTURE(FNetBitStreamReaderWriterTest, WriteBitsAtOffset0)
{
const uint32 Sentinel = 0xC001C0DE;
for (uint32 BitCount = 0; BitCount <= 32; ++BitCount)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.WriteBits(~0U, BitCount);
Writer.WriteBits(Sentinel, sizeof(Sentinel)*8U);
Writer.CommitWrites();
Reader.InitBits(Buffer.GetBuffer(), Writer.GetPosBits());
const uint32 ReadValue = Reader.ReadBits(BitCount);
const uint32 ReadSentinel = Reader.ReadBits(sizeof(Sentinel)*8);
const uint32 ExpectedValue = uint32((uint64(1) << BitCount) - uint64(1));
UE_NET_ASSERT_EQ_MSG(ReadValue, ExpectedValue, FString::Printf(TEXT("Failed testing with %u bits"), BitCount));
UE_NET_ASSERT_EQ_MSG(ReadSentinel, Sentinel, FString::Printf(TEXT("Failed testing with %u bits"), BitCount));
}
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderWriterTest, WriteBitsAtArbitraryOffsets)
{
const uint32 Sentinel = 0xC001C0DE;
for (uint32 BitOffset = 32; BitOffset <= 64; ++BitOffset)
{
for (uint32 BitCount = 0; BitCount <= 32; ++BitCount)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(BitOffset);
Writer.WriteBits(~0U, BitCount);
Writer.WriteBits(Sentinel, sizeof(Sentinel)*8U);
Writer.CommitWrites();
Reader.InitBits(Buffer.GetBuffer(), Writer.GetPosBits());
Reader.Seek(BitOffset);
const uint32 ReadValue = Reader.ReadBits(BitCount);
const uint32 ReadSentinel = Reader.ReadBits(sizeof(Sentinel)*8);
const uint32 ExpectedValue = uint32((uint64(1) << BitCount) - uint64(1));
UE_NET_ASSERT_EQ_MSG(ReadValue, ExpectedValue, FString::Printf(TEXT("Failed testing with %u bits at offset %u"), BitCount, BitOffset));
UE_NET_ASSERT_EQ_MSG(ReadSentinel, Sentinel, FString::Printf(TEXT("Failed testing with %u bits at offset %u"), BitCount, BitOffset));
}
}
}
// Test writing X bits at offset 32 + Y to a stream and then write that stream to a second stream at offset 32 + Z
UE_NET_TEST_FIXTURE(FNetBitStreamReaderWriterTest, WriteStreamWithBitsWrittenAtArbitraryOffsets)
{
const uint32 ValuesAndBitCount[][2] =
{
{1U, 9U},
{47U, 17U},
{11U, 32U},
{777777U, 32U},
{25500U, 32U},
{311U, 32U},
{0xC001C0DE, 32U},
};
const uint32 Sentinel = 0xC0DEC0DE;
for (uint32 BitOffset0 = 32; BitOffset0 <= 64; ++BitOffset0)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(BitOffset0);
for (size_t ValueIt = 0, ValueEndIt = sizeof(ValuesAndBitCount)/sizeof(ValuesAndBitCount[0]); ValueIt != ValueEndIt; ++ValueIt)
{
const uint32 Value = ValuesAndBitCount[ValueIt][0];
const uint32 BitCount = ValuesAndBitCount[ValueIt][1];
Writer.WriteBits(Value, BitCount);
}
Writer.CommitWrites();
for (uint32 BitOffset1 = 32; BitOffset1 <= 64; ++BitOffset1)
{
FNetBitStreamTestWriteBuffer SecondBuffer;
FNetBitStreamWriter SecondWriter;
SecondWriter.InitBytes(SecondBuffer.GetBuffer(), SecondBuffer.GetBufferCapacity());
SecondWriter.Seek(BitOffset1);
SecondWriter.WriteBitStream(static_cast<const uint32*>(Buffer.GetBuffer()), BitOffset0, Writer.GetPosBits() - BitOffset0);
SecondWriter.WriteBits(Sentinel, sizeof(Sentinel)*8U);
SecondWriter.CommitWrites();
Reader.InitBits(SecondBuffer.GetBuffer(), SecondWriter.GetPosBits());
Reader.Seek(BitOffset1);
for (size_t ValueIt = 0, ValueEndIt = sizeof(ValuesAndBitCount)/sizeof(ValuesAndBitCount[0]); ValueIt != ValueEndIt; ++ValueIt)
{
const uint32 ExpectedValue = ValuesAndBitCount[ValueIt][0];
const uint32 BitCount = ValuesAndBitCount[ValueIt][1];
const uint32 ReadValue = Reader.ReadBits(BitCount);
UE_NET_ASSERT_EQ_MSG(ReadValue, ExpectedValue, FString::Printf(TEXT("Write stream with bits written at offset %u to stream at offset %u"), BitOffset0, BitOffset1));
}
const uint32 ReadSentinel = Reader.ReadBits(sizeof(Sentinel)*8U);
UE_NET_ASSERT_EQ_MSG(ReadSentinel, Sentinel, FString::Printf(TEXT("Write stream with bits written at offset %u to stream at offset %u"), BitOffset0, BitOffset1));
}
}
}
// Test writing X bits at offset 32 + Y to a stream and then write that stream to a second stream at offset 32 + Z.
// The resulting is then read from using ReadBitStream.
UE_NET_TEST_FIXTURE(FNetBitStreamReaderWriterTest, ReadStreamWithBitsWrittenAtArbitraryOffsets)
{
const uint32 ValuesAndBitCount[][2] =
{
{1U, 9U},
{47U, 17U},
{11U, 32U},
{777777U, 32U},
{25500U, 32U},
{311U, 32U},
{0xC001C0DE, 32U},
};
const uint32 Sentinel = 0xC0DEC0DE;
uint32 TotalBitCount = 0;
for (size_t ValueIt = 0, ValueEndIt = sizeof(ValuesAndBitCount)/sizeof(ValuesAndBitCount[0]); ValueIt != ValueEndIt; ++ValueIt)
{
TotalBitCount += ValuesAndBitCount[ValueIt][1];
}
for (uint32 BitOffset0 = 32; BitOffset0 <= 64; ++BitOffset0)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(BitOffset0);
for (size_t ValueIt = 0, ValueEndIt = sizeof(ValuesAndBitCount)/sizeof(ValuesAndBitCount[0]); ValueIt != ValueEndIt; ++ValueIt)
{
const uint32 Value = ValuesAndBitCount[ValueIt][0];
const uint32 BitCount = ValuesAndBitCount[ValueIt][1];
Writer.WriteBits(Value, BitCount);
}
Writer.CommitWrites();
for (uint32 BitOffset1 = 32; BitOffset1 <= 64; ++BitOffset1)
{
FNetBitStreamTestWriteBuffer SecondBuffer;
FNetBitStreamWriter SecondWriter;
SecondWriter.InitBytes(SecondBuffer.GetBuffer(), SecondBuffer.GetBufferCapacity());
SecondWriter.Seek(BitOffset1);
SecondWriter.WriteBitStream(static_cast<const uint32*>(Buffer.GetBuffer()), BitOffset0, Writer.GetPosBits() - BitOffset0);
SecondWriter.WriteBits(Sentinel, sizeof(Sentinel)*8U);
SecondWriter.CommitWrites();
Reader.InitBits(SecondBuffer.GetBuffer(), SecondWriter.GetPosBits());
Reader.Seek(BitOffset1);
uint32 ResultBuffer[sizeof(ValuesAndBitCount)/8];
Reader.ReadBitStream(ResultBuffer, TotalBitCount);
const uint32 ReadSentinel = Reader.ReadBits(sizeof(Sentinel)*8U);
UE_NET_ASSERT_EQ_MSG(ReadSentinel, Sentinel, FString::Printf(TEXT("Write stream with bits written at offset %u to stream at offset %u"), BitOffset0, BitOffset1));
Reader.InitBits(ResultBuffer, TotalBitCount);
for (size_t ValueIt = 0, ValueEndIt = sizeof(ValuesAndBitCount)/sizeof(ValuesAndBitCount[0]); ValueIt != ValueEndIt; ++ValueIt)
{
const uint32 ExpectedValue = ValuesAndBitCount[ValueIt][0];
const uint32 BitCount = ValuesAndBitCount[ValueIt][1];
const uint32 ReadValue = Reader.ReadBits(BitCount);
UE_NET_ASSERT_EQ_MSG(ReadValue, ExpectedValue, FString::Printf(TEXT("Write stream with bits written at offset %u to stream at offset %u"), BitOffset0, BitOffset1));
}
}
}
}
// Writer substream tests
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanCreateSubstream)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(1U);
FNetBitStreamWriter Substream = Writer.CreateSubstream(~0U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
UE_NET_ASSERT_EQ(Substream.GetPosBits(), 0U);
UE_NET_ASSERT_EQ(Substream.GetBitsLeft(), Writer.GetBitsLeft());
Writer.DiscardSubstream(Substream);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanCreateSmallSubstream)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(1U);
FNetBitStreamWriter Substream = Writer.CreateSubstream(3U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
UE_NET_ASSERT_EQ(Substream.GetPosBits(), 0U);
UE_NET_ASSERT_EQ(Substream.GetBitsLeft(), 3U);
Writer.DiscardSubstream(Substream);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanCreateEmptySubstream)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(1U);
FNetBitStreamWriter Substream = Writer.CreateSubstream(0U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
UE_NET_ASSERT_EQ(Substream.GetPosBits(), 0U);
UE_NET_ASSERT_EQ(Substream.GetBitsLeft(), 0U);
Writer.DiscardSubstream(Substream);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanDiscardSubstream)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
{
FNetBitStreamWriter Substream = Writer.CreateSubstream();
Substream.WriteBits(~0U, 32U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
Writer.DiscardSubstream(Substream);
}
UE_NET_ASSERT_EQ(Writer.GetPosBits(), 0U);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanCommitSubstream)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
constexpr uint32 SeekPos = 33;
constexpr uint32 WriteBitCount = 32;
Writer.Seek(SeekPos);
{
FNetBitStreamWriter Substream = Writer.CreateSubstream();
Substream.WriteBits(~0U, 32U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
Writer.CommitSubstream(Substream);
}
UE_NET_ASSERT_EQ(Writer.GetPosBits(), SeekPos + WriteBitCount);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanCreateSubstreamFromOverflowedStream)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.Seek(Buffer.GetBufferCapacity()*8 + 1);
UE_NET_ASSERT_TRUE(Writer.IsOverflown());
const uint32 PreviousBitPos = Writer.GetPosBits();
{
FNetBitStreamWriter Substream = Writer.CreateSubstream();
UE_NET_ASSERT_TRUE(Substream.IsOverflown());
Substream.Seek(0U);
UE_NET_ASSERT_EQ(Substream.GetBitsLeft(), 0U);
Substream.WriteBits(0, 1U);
UE_NET_ASSERT_TRUE(Substream.IsOverflown());
Writer.CommitSubstream(Substream);
}
const uint32 CurrentBitPos = Writer.GetPosBits();
UE_NET_ASSERT_EQ(CurrentBitPos, PreviousBitPos);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanCreateSubSubstream)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
constexpr uint32 SeekCount1 = 33;
constexpr uint32 WriteCount2 = 32;
{
FNetBitStreamWriter Substream1 = Writer.CreateSubstream();
Substream1.Seek(SeekCount1);
FNetBitStreamWriter Substream2 = Substream1.CreateSubstream();
Substream2.WriteBits(~0U, WriteCount2);
Substream1.CommitSubstream(Substream2);
Writer.CommitSubstream(Substream1);
}
const uint32 CurrentBitPos = Writer.GetPosBits();
UE_NET_ASSERT_EQ(CurrentBitPos, SeekCount1 + WriteCount2);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanWriteToSubSubstream)
{
uint32 Word = 0;
Writer.InitBytes(&Word, sizeof(Word));
Writer.WriteBits(0, 16U);
{
constexpr uint32 SubstreamBitCount = 15U;
FNetBitStreamWriter Substream1 = Writer.CreateSubstream(SubstreamBitCount);
FNetBitStreamWriter Substream2 = Substream1.CreateSubstream();
Substream2.WriteBits(~0U, SubstreamBitCount);
Substream1.CommitSubstream(Substream2);
Writer.CommitSubstream(Substream1);
Writer.WriteBits(1U, 1U);
}
Writer.CommitWrites();
UE_NET_ASSERT_EQ(Word, 0xFFFF0000U);
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanReadDataCommittedFromSubstream)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
constexpr uint32 SeekCount1 = 33;
constexpr uint32 WriteCount2 = 32;
constexpr uint32 WriteWord = 0x01020304U;
{
FNetBitStreamWriter Substream1 = Writer.CreateSubstream();
Substream1.Seek(SeekCount1);
FNetBitStreamWriter Substream2 = Substream1.CreateSubstream();
Substream2.WriteBits(WriteWord, WriteCount2);
Substream1.CommitSubstream(Substream2);
Writer.CommitSubstream(Substream1);
}
// Read and verify
{
Writer.CommitWrites();
FNetBitStreamReader Reader;
Reader.InitBits(Buffer.GetBuffer(), Writer.GetPosBits());
Reader.Seek(SeekCount1);
const uint32 ReadWord = Reader.ReadBits(WriteCount2);
const uint32 WordMask = ~0U >> (32 - WriteCount2);
UE_NET_ASSERT_EQ((ReadWord & WordMask), (WriteWord & WordMask));
}
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanWriteToEndOfSubStreamTest1)
{
constexpr uint32 Sentinel = 0xBAAAAAADU;
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
{
FNetBitStreamWriter Substream = Writer.CreateSubstream(Writer.GetBitsLeft() - 1U);
Substream.Seek(Substream.GetBitsLeft() - 32U);
Substream.WriteBits(Sentinel, 32U);
Writer.CommitSubstream(Substream);
Writer.CommitWrites();
}
// Test normal reading
{
FNetBitStreamReader Reader;
Reader.InitBits(Buffer.GetBuffer(), Writer.GetPosBits());
Reader.Seek(Reader.GetBitsLeft() - 32U);
const uint32 ReadSentinel = Reader.ReadBits(32);
UE_NET_ASSERT_EQ(ReadSentinel, Sentinel);
}
// Test reading via substream
{
FNetBitStreamReader Reader;
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8U);
FNetBitStreamReader Substream = Reader.CreateSubstream(Writer.GetPosBits());
Substream.Seek(Substream.GetBitsLeft() - 32U);
const uint32 ReadSentinel = Substream.ReadBits(32U);
UE_NET_ASSERT_EQ(ReadSentinel, Sentinel);
Reader.CommitSubstream(Substream);
}
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, CanWriteToEndOfSubStreamTest2)
{
constexpr uint32 Sentinel1 = 0xBAAAAAADU;
constexpr uint32 Sentinel2 = 0xBAADF00DU;
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
{
Writer.Seek(Writer.GetBitsLeft() - 32U);
Writer.WriteBits(Sentinel2, 32U);
Writer.Seek(0);
FNetBitStreamWriter Substream = Writer.CreateSubstream(Writer.GetBitsLeft() - 32U);
Substream.Seek(Substream.GetBitsLeft() - 32U);
Substream.WriteBits(Sentinel1, 32U);
Writer.CommitSubstream(Substream);
Writer.Seek(Buffer.GetBufferCapacity()*8U);
Writer.CommitWrites();
}
// Test normal reading
{
FNetBitStreamReader Reader;
Reader.InitBits(Buffer.GetBuffer(), Writer.GetPosBits());
Reader.Seek(Reader.GetBitsLeft() - 64U);
const uint32 ReadSentinel1 = Reader.ReadBits(32U);
UE_NET_ASSERT_EQ(ReadSentinel1, Sentinel1);
const uint32 ReadSentinel2 = Reader.ReadBits(32U);
UE_NET_ASSERT_EQ(ReadSentinel2, Sentinel2);
}
// Test reading via substream
{
FNetBitStreamReader Reader;
Reader.InitBits(Buffer.GetBuffer(), Writer.GetPosBits());
Reader.Seek(Reader.GetBitsLeft() - 64U);
FNetBitStreamReader Substream1 = Reader.CreateSubstream(32U);
const uint32 ReadSentinel1 = Substream1.ReadBits(32U);
UE_NET_ASSERT_EQ(ReadSentinel1, Sentinel1);
Reader.CommitSubstream(Substream1);
FNetBitStreamReader Substream2 = Reader.CreateSubstream();
const uint32 ReadSentinel2 = Substream2.ReadBits(32U);
UE_NET_ASSERT_EQ(ReadSentinel2, Sentinel2);
Reader.CommitSubstream(Substream2);
}
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterSubstreamTest, SubStreamSeek)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.WriteBits(0x12345678,32);
FNetBitStreamWriter SubStream = Writer.CreateSubstream();
const uint32 SubStreamStartPos = SubStream.GetPosBits();
SubStream.Seek(0);
UE_NET_ASSERT_EQ(SubStreamStartPos, SubStream.GetPosBits());
Writer.CommitSubstream(SubStream);
}
// Reader substream tests
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanCreateSubstream)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
Reader.Seek(1U);
FNetBitStreamReader Substream = Reader.CreateSubstream(~0U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
UE_NET_ASSERT_EQ(Substream.GetPosBits(), 0U);
UE_NET_ASSERT_EQ(Substream.GetBitsLeft(), Reader.GetBitsLeft());
Reader.DiscardSubstream(Substream);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanCreateSmallSubstream)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
Reader.Seek(1U);
FNetBitStreamReader Substream = Reader.CreateSubstream(3U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
UE_NET_ASSERT_EQ(Substream.GetPosBits(), 0U);
UE_NET_ASSERT_EQ(Substream.GetBitsLeft(), 3U);
Reader.DiscardSubstream(Substream);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanCreateEmptySubstream)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
Reader.Seek(1U);
FNetBitStreamReader Substream = Reader.CreateSubstream(0U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
UE_NET_ASSERT_EQ(Substream.GetPosBits(), 0U);
UE_NET_ASSERT_EQ(Substream.GetBitsLeft(), 0U);
Reader.DiscardSubstream(Substream);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanDiscardSubstream)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
Reader.Seek(1U);
const uint32 PreviousBitPos = Reader.GetPosBits();
{
FNetBitStreamReader Substream = Reader.CreateSubstream();
Substream.ReadBits(32U);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
Reader.DiscardSubstream(Substream);
}
// A discarded substream should not affect its parent's position.
UE_NET_ASSERT_EQ(Reader.GetPosBits(), PreviousBitPos);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanCommitSubstream)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
constexpr uint32 SeekPos = 33;
constexpr uint32 ReadBitCount = 32;
Reader.Seek(SeekPos);
{
FNetBitStreamReader Substream = Reader.CreateSubstream();
Substream.ReadBits(ReadBitCount);
UE_NET_ASSERT_FALSE(Substream.IsOverflown());
Reader.CommitSubstream(Substream);
}
UE_NET_ASSERT_EQ(Reader.GetPosBits(), SeekPos + ReadBitCount);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanCreateSubstreamFromOverflowedStream)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
Reader.Seek(Reader.GetBitsLeft() + 1);
UE_NET_ASSERT_TRUE(Reader.IsOverflown());
const uint32 PreviousBitPos = Reader.GetPosBits();
{
FNetBitStreamReader Substream = Reader.CreateSubstream();
UE_NET_ASSERT_TRUE(Substream.IsOverflown());
Substream.Seek(0U);
UE_NET_ASSERT_EQ(Substream.GetBitsLeft(), 0U);
Substream.ReadBits(1U);
UE_NET_ASSERT_TRUE(Substream.IsOverflown());
// Commit overflown substream. Because of the overflow this should not affects its parent's position.
Reader.CommitSubstream(Substream);
}
const uint32 CurrentBitPos = Reader.GetPosBits();
UE_NET_ASSERT_EQ(CurrentBitPos, PreviousBitPos);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanCreateSubSubstream)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8);
constexpr uint32 SeekPosSubstream1 = 33;
constexpr uint32 ReadCountSubstream2 = 32;
{
FNetBitStreamReader Substream1 = Reader.CreateSubstream();
Substream1.Seek(SeekPosSubstream1);
FNetBitStreamReader Substream2 = Substream1.CreateSubstream();
Substream2.ReadBits(ReadCountSubstream2);
Substream1.CommitSubstream(Substream2);
Reader.CommitSubstream(Substream1);
}
const uint32 CurrentBitPos = Reader.GetPosBits();
UE_NET_ASSERT_EQ(CurrentBitPos, SeekPosSubstream1 + ReadCountSubstream2);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanReadFromSubstream)
{
const uint32 Word = 0xFFFF0000U;
Reader.InitBits(&Word, sizeof(Word)*8);
Reader.Seek(16);
constexpr uint32 SubstreamBitCount = 15U;
FNetBitStreamReader Substream = Reader.CreateSubstream(SubstreamBitCount);
const uint32 SubStreamReadValue = Substream.ReadBits(SubstreamBitCount);
UE_NET_ASSERT_EQ(SubStreamReadValue, 0b111111111111111U);
Reader.CommitSubstream(Substream);
const uint32 StreamReadValue = Reader.ReadBits(1U);
UE_NET_ASSERT_EQ(StreamReadValue, 0b1U);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, CanReadFromSubSubstream)
{
const uint32 Word = 0xFFFF0000U;
Reader.InitBits(&Word, sizeof(Word)*8);
const uint32 FirstStreamReadValue = Reader.ReadBits(16U);
UE_NET_ASSERT_EQ(FirstStreamReadValue, 0U);
{
constexpr uint32 SubstreamBitCount = 15U;
FNetBitStreamReader Substream1 = Reader.CreateSubstream(SubstreamBitCount);
FNetBitStreamReader Substream2 = Substream1.CreateSubstream();
const uint32 SubSubStreamReadValue = Substream2.ReadBits(SubstreamBitCount);
UE_NET_ASSERT_EQ(SubSubStreamReadValue, 0b111111111111111U);
Substream1.CommitSubstream(Substream2);
Reader.CommitSubstream(Substream1);
}
const uint32 SecondStreamReadValue = Reader.ReadBits(1U);
UE_NET_ASSERT_EQ(SecondStreamReadValue, 0b1U);
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderSubstreamTest, SubStreamSeek)
{
Reader.InitBits(Buffer.GetBuffer(), Buffer.GetBufferCapacity()*8U);
Reader.ReadBits(32);
FNetBitStreamReader SubStream = Reader.CreateSubstream();
const uint32 SubStreamStartPos = SubStream.GetPosBits();
SubStream.Seek(0);
UE_NET_ASSERT_EQ(SubStreamStartPos, SubStream.GetPosBits());
Reader.CommitSubstream(SubStream);
}
// Misc tests
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, RollbackToValidPositionAfterOverflow)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
{
FNetBitStreamRollbackScope Rollback(Writer);
Writer.Seek(Buffer.GetBufferCapacity()*8U);
Writer.WriteBits(0U, 1U);
UE_NET_EXPECT_TRUE(Writer.IsOverflown());
}
UE_NET_ASSERT_EQ(0U, Writer.GetPosBits());
UE_NET_ASSERT_FALSE(Writer.IsOverflown());
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, NoRollbackIfNoOverflow)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
{
FNetBitStreamRollbackScope Rollback(Writer);
Writer.Seek(Buffer.GetBufferCapacity()*8U);
UE_NET_EXPECT_FALSE(Writer.IsOverflown());
}
UE_NET_ASSERT_EQ(Buffer.GetBufferCapacity()*8U, Writer.GetPosBits());
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, WriteScopeCanRewriteBits)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.WriteBits(0U, 32);
Writer.WriteBits(0U, 32);
Writer.WriteBits(0xFFFFFFFF, 32);
const uint32 ExpectedBitPos = Writer.GetPosBits();
{
// Seek back and rewrite first 32 bits
FNetBitStreamWriteScope WriteScope(Writer, 0U);
UE_NET_ASSERT_EQ(0U, Writer.GetPosBits());
Writer.WriteBits(0xDEADBEEF, 32);
}
UE_NET_ASSERT_EQ(ExpectedBitPos, Writer.GetPosBits());
// Read and verify
{
Writer.CommitWrites();
FNetBitStreamReader Reader;
Reader.InitBits(Buffer.GetBuffer(), Writer.GetPosBits());
UE_NET_ASSERT_EQ(0xDEADBEEF, Reader.ReadBits(32));
UE_NET_ASSERT_EQ(0U, Reader.ReadBits(32));
UE_NET_ASSERT_EQ(0xFFFFFFFF, Reader.ReadBits(32));
}
}
UE_NET_TEST_FIXTURE(FNetBitStreamWriterTest, WriteScopeOverflow)
{
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
Writer.WriteBits(0U, 32);
Writer.WriteBits(0U, 32);
Writer.WriteBits(0xFFFFFFFF, 32);
const uint32 ExpectedBitPos = Writer.GetPosBits();
UE_NET_EXPECT_FALSE(Writer.IsOverflown());
{
// Bad write scope
FNetBitStreamWriteScope WriteScope(Writer, Buffer.GetBufferCapacity()*8U + 1);
UE_NET_EXPECT_TRUE(Writer.IsOverflown());
Writer.WriteBits(0xDEADBEEF, 32);
}
UE_NET_ASSERT_EQ(ExpectedBitPos, Writer.GetPosBits());
UE_NET_EXPECT_FALSE(Writer.IsOverflown());
}
UE_NET_TEST_FIXTURE(FNetBitStreamReaderWriterTest, WriteBool)
{
static_assert(sizeof(bool) == sizeof(uint8), "Unexpected size of bool. Need to rewrite tests");
// We can't trust the compiler not to fiddle with bools and force them to be 0 or 1.
const uint8 TestValues[] = {0, 1, 128, 255};
constexpr SIZE_T ValueCount = sizeof(TestValues)/sizeof(TestValues[0]);
bool Values[ValueCount];
FPlatformMemory::Memcpy(Values, TestValues, sizeof(TestValues));
// Validate test values
{
const bool* TestValue0 = &Values[0];
const uint8 ExpectedValue0 = 0;
UE_NET_ASSERT_EQ(FPlatformMemory::Memcmp(TestValue0, &ExpectedValue0, 1), 0);
const bool* TestValue1 = &Values[1];
const uint8 ExpectedValue1 = 1;
UE_NET_ASSERT_EQ(FPlatformMemory::Memcmp(TestValue1, &ExpectedValue1, 1), 0);
const bool* TestValue2 = &Values[2];
const uint8 ExpectedValue2 = 128;
UE_NET_ASSERT_EQ(FPlatformMemory::Memcmp(TestValue2, &ExpectedValue2, 1), 0);
const bool* TestValue3 = &Values[3];
const uint8 ExpectedValue3 = 255;
UE_NET_ASSERT_EQ(FPlatformMemory::Memcmp(TestValue3, &ExpectedValue3, 1), 0);
}
// We expect WriteBool to write a 1 if the bool isn't exactly false (zero) and write a 0 when it's false.
// We also expect WriteBool to return the value written.
bool ExpectedValues[sizeof(Values)/sizeof(Values[0])];
bool ReturnValues[sizeof(Values)/sizeof(Values[0])];
bool ReadValues[sizeof(Values)/sizeof(Values[0])];
for (SIZE_T ValueIt = 0; ValueIt < ValueCount; ++ValueIt)
{
uint8 Value;
FPlatformMemory::Memcpy(&Value, &Values[ValueIt], 1);
ExpectedValues[ValueIt] = Value != 0;
}
Writer.InitBytes(Buffer.GetBuffer(), Buffer.GetBufferCapacity());
for (const bool& Value : Values)
{
ReturnValues[&Value - Values] = Writer.WriteBool(Value);
}
Writer.CommitWrites();
// Validate return value is the written value
UE_NET_ASSERT_EQ(FPlatformMemory::Memcmp(ReturnValues, ExpectedValues, sizeof(ReturnValues)), 0);
Reader.InitBits(Buffer.GetBuffer(), Writer.GetPosBits());
for (SIZE_T ValueIt = 0; ValueIt < ValueCount; ++ValueIt)
{
ReadValues[ValueIt] = Reader.ReadBool();
}
// Validate read value is the written value
UE_NET_ASSERT_EQ(FPlatformMemory::Memcmp(ReadValues, ExpectedValues, sizeof(ReturnValues)), 0);
}
}