//===- llvm/unittest/Support/BinaryStreamTest.cpp -------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Support/Allocator.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/BinaryItemStream.h" #include "llvm/Support/BinaryStreamArray.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/BinaryStreamRef.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::support; namespace { class BrokenStream : public WritableBinaryStream { public: BrokenStream(MutableArrayRef Data, endianness Endian, uint32_t Align) : Data(Data), PartitionIndex(alignDown(Data.size() / 2, Align)), Endian(Endian) {} endianness getEndian() const override { return Endian; } Error readBytes(uint32_t Offset, uint32_t Size, ArrayRef &Buffer) override { if (auto EC = checkOffsetForRead(Offset, Size)) return EC; uint32_t S = startIndex(Offset); auto Ref = Data.drop_front(S); if (Ref.size() >= Size) { Buffer = Ref.take_front(Size); return Error::success(); } uint32_t BytesLeft = Size - Ref.size(); uint8_t *Ptr = Allocator.Allocate(Size); ::memcpy(Ptr, Ref.data(), Ref.size()); ::memcpy(Ptr + Ref.size(), Data.data(), BytesLeft); Buffer = makeArrayRef(Ptr, Size); return Error::success(); } Error readLongestContiguousChunk(uint32_t Offset, ArrayRef &Buffer) override { if (auto EC = checkOffsetForRead(Offset, 1)) return EC; uint32_t S = startIndex(Offset); Buffer = Data.drop_front(S); return Error::success(); } uint32_t getLength() override { return Data.size(); } Error writeBytes(uint32_t Offset, ArrayRef SrcData) override { if (auto EC = checkOffsetForWrite(Offset, SrcData.size())) return EC; if (SrcData.empty()) return Error::success(); uint32_t S = startIndex(Offset); MutableArrayRef Ref(Data); Ref = Ref.drop_front(S); if (Ref.size() >= SrcData.size()) { ::memcpy(Ref.data(), SrcData.data(), SrcData.size()); return Error::success(); } uint32_t BytesLeft = SrcData.size() - Ref.size(); ::memcpy(Ref.data(), SrcData.data(), Ref.size()); ::memcpy(&Data[0], SrcData.data() + Ref.size(), BytesLeft); return Error::success(); } Error commit() override { return Error::success(); } private: uint32_t startIndex(uint32_t Offset) const { return (Offset + PartitionIndex) % Data.size(); } uint32_t endIndex(uint32_t Offset, uint32_t Size) const { return (startIndex(Offset) + Size - 1) % Data.size(); } // Buffer is organized like this: // ------------------------------------------------- // | N/2 | N/2+1 | ... | N-1 | 0 | 1 | ... | N/2-1 | // ------------------------------------------------- // So reads from the beginning actually come from the middle. MutableArrayRef Data; uint32_t PartitionIndex = 0; endianness Endian; BumpPtrAllocator Allocator; }; constexpr endianness Endians[] = {big, little, native}; constexpr uint32_t NumEndians = llvm::array_lengthof(Endians); constexpr uint32_t NumStreams = 2 * NumEndians; class BinaryStreamTest : public testing::Test { public: BinaryStreamTest() {} void SetUp() override { Streams.clear(); Streams.resize(NumStreams); for (uint32_t I = 0; I < NumStreams; ++I) Streams[I].IsContiguous = (I % 2 == 0); InputData.clear(); OutputData.clear(); } protected: struct StreamPair { bool IsContiguous; std::unique_ptr Input; std::unique_ptr Output; }; void initializeInput(ArrayRef Input, uint32_t Align) { InputData = Input; BrokenInputData.resize(InputData.size()); if (!Input.empty()) { uint32_t PartitionIndex = alignDown(InputData.size() / 2, Align); uint32_t RightBytes = InputData.size() - PartitionIndex; uint32_t LeftBytes = PartitionIndex; if (RightBytes > 0) ::memcpy(&BrokenInputData[PartitionIndex], Input.data(), RightBytes); if (LeftBytes > 0) ::memcpy(&BrokenInputData[0], Input.data() + RightBytes, LeftBytes); } for (uint32_t I = 0; I < NumEndians; ++I) { auto InByteStream = std::make_unique(InputData, Endians[I]); auto InBrokenStream = std::make_unique( BrokenInputData, Endians[I], Align); Streams[I * 2].Input = std::move(InByteStream); Streams[I * 2 + 1].Input = std::move(InBrokenStream); } } void initializeOutput(uint32_t Size, uint32_t Align) { OutputData.resize(Size); BrokenOutputData.resize(Size); for (uint32_t I = 0; I < NumEndians; ++I) { Streams[I * 2].Output = std::make_unique(OutputData, Endians[I]); Streams[I * 2 + 1].Output = std::make_unique( BrokenOutputData, Endians[I], Align); } } void initializeOutputFromInput(uint32_t Align) { for (uint32_t I = 0; I < NumEndians; ++I) { Streams[I * 2].Output = std::make_unique(InputData, Endians[I]); Streams[I * 2 + 1].Output = std::make_unique( BrokenInputData, Endians[I], Align); } } void initializeInputFromOutput(uint32_t Align) { for (uint32_t I = 0; I < NumEndians; ++I) { Streams[I * 2].Input = std::make_unique(OutputData, Endians[I]); Streams[I * 2 + 1].Input = std::make_unique( BrokenOutputData, Endians[I], Align); } } std::vector InputData; std::vector BrokenInputData; std::vector OutputData; std::vector BrokenOutputData; std::vector Streams; }; // Tests that a we can read from a BinaryByteStream without a StreamReader. TEST_F(BinaryStreamTest, BinaryByteStreamBounds) { std::vector InputData = {1, 2, 3, 4, 5}; initializeInput(InputData, 1); for (auto &Stream : Streams) { ArrayRef Buffer; // 1. If the read fits it should work. ASSERT_EQ(InputData.size(), Stream.Input->getLength()); ASSERT_THAT_ERROR(Stream.Input->readBytes(2, 1, Buffer), Succeeded()); EXPECT_EQ(makeArrayRef(InputData).slice(2, 1), Buffer); ASSERT_THAT_ERROR(Stream.Input->readBytes(0, 4, Buffer), Succeeded()); EXPECT_EQ(makeArrayRef(InputData).slice(0, 4), Buffer); // 2. Reading past the bounds of the input should fail. EXPECT_THAT_ERROR(Stream.Input->readBytes(4, 2, Buffer), Failed()); } } TEST_F(BinaryStreamTest, StreamRefBounds) { std::vector InputData = {1, 2, 3, 4, 5}; initializeInput(InputData, 1); for (const auto &Stream : Streams) { ArrayRef Buffer; BinaryStreamRef Ref(*Stream.Input); // Read 1 byte from offset 2 should work ASSERT_EQ(InputData.size(), Ref.getLength()); ASSERT_THAT_ERROR(Ref.readBytes(2, 1, Buffer), Succeeded()); EXPECT_EQ(makeArrayRef(InputData).slice(2, 1), Buffer); // Reading everything from offset 2 on. ASSERT_THAT_ERROR(Ref.readLongestContiguousChunk(2, Buffer), Succeeded()); if (Stream.IsContiguous) EXPECT_EQ(makeArrayRef(InputData).slice(2), Buffer); else EXPECT_FALSE(Buffer.empty()); // Reading 6 bytes from offset 0 is too big. EXPECT_THAT_ERROR(Ref.readBytes(0, 6, Buffer), Failed()); EXPECT_THAT_ERROR(Ref.readLongestContiguousChunk(6, Buffer), Failed()); // Reading 1 byte from offset 2 after dropping 1 byte is the same as reading // 1 byte from offset 3. Ref = Ref.drop_front(1); ASSERT_THAT_ERROR(Ref.readBytes(2, 1, Buffer), Succeeded()); if (Stream.IsContiguous) EXPECT_EQ(makeArrayRef(InputData).slice(3, 1), Buffer); else EXPECT_FALSE(Buffer.empty()); // Reading everything from offset 2 on after dropping 1 byte. ASSERT_THAT_ERROR(Ref.readLongestContiguousChunk(2, Buffer), Succeeded()); if (Stream.IsContiguous) EXPECT_EQ(makeArrayRef(InputData).slice(3), Buffer); else EXPECT_FALSE(Buffer.empty()); // Reading 2 bytes from offset 2 after dropping 2 bytes is the same as // reading 2 bytes from offset 4, and should fail. Ref = Ref.drop_front(1); EXPECT_THAT_ERROR(Ref.readBytes(2, 2, Buffer), Failed()); // But if we read the longest contiguous chunk instead, we should still // get the 1 byte at the end. ASSERT_THAT_ERROR(Ref.readLongestContiguousChunk(2, Buffer), Succeeded()); EXPECT_EQ(makeArrayRef(InputData).take_back(), Buffer); } } TEST_F(BinaryStreamTest, StreamRefDynamicSize) { StringRef Strings[] = {"1", "2", "3", "4"}; AppendingBinaryByteStream Stream(support::little); BinaryStreamWriter Writer(Stream); BinaryStreamReader Reader(Stream); const uint8_t *Byte; StringRef Str; // When the stream is empty, it should report a 0 length and we should get an // error trying to read even 1 byte from it. BinaryStreamRef ConstRef(Stream); EXPECT_EQ(0U, ConstRef.getLength()); EXPECT_THAT_ERROR(Reader.readObject(Byte), Failed()); // But if we write to it, its size should increase and we should be able to // read not just a byte, but the string that was written. EXPECT_THAT_ERROR(Writer.writeCString(Strings[0]), Succeeded()); EXPECT_EQ(2U, ConstRef.getLength()); EXPECT_THAT_ERROR(Reader.readObject(Byte), Succeeded()); Reader.setOffset(0); EXPECT_THAT_ERROR(Reader.readCString(Str), Succeeded()); EXPECT_EQ(Str, Strings[0]); // If we drop some bytes from the front, we should still track the length as // the // underlying stream grows. BinaryStreamRef Dropped = ConstRef.drop_front(1); EXPECT_EQ(1U, Dropped.getLength()); EXPECT_THAT_ERROR(Writer.writeCString(Strings[1]), Succeeded()); EXPECT_EQ(4U, ConstRef.getLength()); EXPECT_EQ(3U, Dropped.getLength()); // If we drop zero bytes from the back, we should continue tracking the // length. Dropped = Dropped.drop_back(0); EXPECT_THAT_ERROR(Writer.writeCString(Strings[2]), Succeeded()); EXPECT_EQ(6U, ConstRef.getLength()); EXPECT_EQ(5U, Dropped.getLength()); // If we drop non-zero bytes from the back, we should stop tracking the // length. Dropped = Dropped.drop_back(1); EXPECT_THAT_ERROR(Writer.writeCString(Strings[3]), Succeeded()); EXPECT_EQ(8U, ConstRef.getLength()); EXPECT_EQ(4U, Dropped.getLength()); } TEST_F(BinaryStreamTest, DropOperations) { std::vector InputData = {1, 2, 3, 4, 5, 4, 3, 2, 1}; auto RefData = makeArrayRef(InputData); initializeInput(InputData, 1); ArrayRef Result; BinaryStreamRef Original(InputData, support::little); ASSERT_EQ(InputData.size(), Original.getLength()); EXPECT_THAT_ERROR(Original.readBytes(0, InputData.size(), Result), Succeeded()); EXPECT_EQ(RefData, Result); auto Dropped = Original.drop_front(2); EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result), Succeeded()); EXPECT_EQ(RefData.drop_front(2), Result); Dropped = Original.drop_back(2); EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result), Succeeded()); EXPECT_EQ(RefData.drop_back(2), Result); Dropped = Original.keep_front(2); EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result), Succeeded()); EXPECT_EQ(RefData.take_front(2), Result); Dropped = Original.keep_back(2); EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result), Succeeded()); EXPECT_EQ(RefData.take_back(2), Result); Dropped = Original.drop_symmetric(2); EXPECT_THAT_ERROR(Dropped.readBytes(0, Dropped.getLength(), Result), Succeeded()); EXPECT_EQ(RefData.drop_front(2).drop_back(2), Result); } // Test that we can write to a BinaryStream without a StreamWriter. TEST_F(BinaryStreamTest, MutableBinaryByteStreamBounds) { std::vector InputData = {'T', 'e', 's', 't', '\0'}; initializeInput(InputData, 1); initializeOutput(InputData.size(), 1); // For every combination of input stream and output stream. for (auto &Stream : Streams) { ASSERT_EQ(InputData.size(), Stream.Input->getLength()); // 1. Try two reads that are supposed to work. One from offset 0, and one // from the middle. uint32_t Offsets[] = {0, 3}; for (auto Offset : Offsets) { uint32_t ExpectedSize = Stream.Input->getLength() - Offset; // Read everything from Offset until the end of the input data. ArrayRef Data; ASSERT_THAT_ERROR(Stream.Input->readBytes(Offset, ExpectedSize, Data), Succeeded()); ASSERT_EQ(ExpectedSize, Data.size()); // Then write it to the destination. ASSERT_THAT_ERROR(Stream.Output->writeBytes(0, Data), Succeeded()); // Then we read back what we wrote, it should match the corresponding // slice of the original input data. ArrayRef Data2; ASSERT_THAT_ERROR(Stream.Output->readBytes(Offset, ExpectedSize, Data2), Succeeded()); EXPECT_EQ(makeArrayRef(InputData).drop_front(Offset), Data2); } std::vector BigData = {0, 1, 2, 3, 4}; // 2. If the write is too big, it should fail. EXPECT_THAT_ERROR(Stream.Output->writeBytes(3, BigData), Failed()); } } TEST_F(BinaryStreamTest, AppendingStream) { AppendingBinaryByteStream Stream(llvm::support::little); EXPECT_EQ(0U, Stream.getLength()); std::vector InputData = {'T', 'e', 's', 't', 'T', 'e', 's', 't'}; auto Test = makeArrayRef(InputData).take_front(4); // Writing past the end of the stream is an error. EXPECT_THAT_ERROR(Stream.writeBytes(4, Test), Failed()); // Writing exactly at the end of the stream is ok. EXPECT_THAT_ERROR(Stream.writeBytes(0, Test), Succeeded()); EXPECT_EQ(Test, Stream.data()); // And now that the end of the stream is where we couldn't write before, now // we can write. EXPECT_THAT_ERROR(Stream.writeBytes(4, Test), Succeeded()); EXPECT_EQ(MutableArrayRef(InputData), Stream.data()); } // Test that FixedStreamArray works correctly. TEST_F(BinaryStreamTest, FixedStreamArray) { std::vector Ints = {90823, 12908, 109823, 209823}; ArrayRef IntBytes(reinterpret_cast(Ints.data()), Ints.size() * sizeof(uint32_t)); initializeInput(IntBytes, alignof(uint32_t)); for (auto &Stream : Streams) { ASSERT_EQ(InputData.size(), Stream.Input->getLength()); FixedStreamArray Array(*Stream.Input); auto Iter = Array.begin(); ASSERT_EQ(Ints[0], *Iter++); ASSERT_EQ(Ints[1], *Iter++); ASSERT_EQ(Ints[2], *Iter++); ASSERT_EQ(Ints[3], *Iter++); ASSERT_EQ(Array.end(), Iter); } } // Ensure FixedStreamArrayIterator::operator-> works. // Added for coverage of r302257. TEST_F(BinaryStreamTest, FixedStreamArrayIteratorArrow) { std::vector> Pairs = {{867, 5309}, {555, 1212}}; ArrayRef PairBytes(reinterpret_cast(Pairs.data()), Pairs.size() * sizeof(Pairs[0])); initializeInput(PairBytes, alignof(uint32_t)); for (auto &Stream : Streams) { ASSERT_EQ(InputData.size(), Stream.Input->getLength()); const FixedStreamArray> Array(*Stream.Input); auto Iter = Array.begin(); ASSERT_EQ(Pairs[0].first, Iter->first); ASSERT_EQ(Pairs[0].second, Iter->second); ++Iter; ASSERT_EQ(Pairs[1].first, Iter->first); ASSERT_EQ(Pairs[1].second, Iter->second); ++Iter; ASSERT_EQ(Array.end(), Iter); } } // Test that VarStreamArray works correctly. TEST_F(BinaryStreamTest, VarStreamArray) { StringLiteral Strings("1. Test2. Longer Test3. Really Long Test4. Super " "Extra Longest Test Of All"); ArrayRef StringBytes( reinterpret_cast(Strings.data()), Strings.size()); initializeInput(StringBytes, 1); struct StringExtractor { public: Error operator()(BinaryStreamRef Stream, uint32_t &Len, StringRef &Item) { if (Index == 0) Len = strlen("1. Test"); else if (Index == 1) Len = strlen("2. Longer Test"); else if (Index == 2) Len = strlen("3. Really Long Test"); else Len = strlen("4. Super Extra Longest Test Of All"); ArrayRef Bytes; if (auto EC = Stream.readBytes(0, Len, Bytes)) return EC; Item = StringRef(reinterpret_cast(Bytes.data()), Bytes.size()); ++Index; return Error::success(); } uint32_t Index = 0; }; for (auto &Stream : Streams) { VarStreamArray Array(*Stream.Input); auto Iter = Array.begin(); ASSERT_EQ("1. Test", *Iter++); ASSERT_EQ("2. Longer Test", *Iter++); ASSERT_EQ("3. Really Long Test", *Iter++); ASSERT_EQ("4. Super Extra Longest Test Of All", *Iter++); ASSERT_EQ(Array.end(), Iter); } } TEST_F(BinaryStreamTest, StreamReaderBounds) { std::vector Bytes; initializeInput(Bytes, 1); for (auto &Stream : Streams) { StringRef S; BinaryStreamReader Reader(*Stream.Input); EXPECT_EQ(0U, Reader.bytesRemaining()); EXPECT_THAT_ERROR(Reader.readFixedString(S, 1), Failed()); } Bytes.resize(5); initializeInput(Bytes, 1); for (auto &Stream : Streams) { StringRef S; BinaryStreamReader Reader(*Stream.Input); EXPECT_EQ(Bytes.size(), Reader.bytesRemaining()); EXPECT_THAT_ERROR(Reader.readFixedString(S, 5), Succeeded()); EXPECT_THAT_ERROR(Reader.readFixedString(S, 6), Failed()); } } TEST_F(BinaryStreamTest, StreamReaderIntegers) { support::ulittle64_t Little{908234}; support::ubig32_t Big{28907823}; short NS = 2897; int NI = -89723; unsigned long NUL = 902309023UL; constexpr uint32_t Size = sizeof(Little) + sizeof(Big) + sizeof(NS) + sizeof(NI) + sizeof(NUL); initializeOutput(Size, alignof(support::ulittle64_t)); initializeInputFromOutput(alignof(support::ulittle64_t)); for (auto &Stream : Streams) { BinaryStreamWriter Writer(*Stream.Output); ASSERT_THAT_ERROR(Writer.writeObject(Little), Succeeded()); ASSERT_THAT_ERROR(Writer.writeObject(Big), Succeeded()); ASSERT_THAT_ERROR(Writer.writeInteger(NS), Succeeded()); ASSERT_THAT_ERROR(Writer.writeInteger(NI), Succeeded()); ASSERT_THAT_ERROR(Writer.writeInteger(NUL), Succeeded()); const support::ulittle64_t *Little2; const support::ubig32_t *Big2; short NS2; int NI2; unsigned long NUL2; // 1. Reading fields individually. BinaryStreamReader Reader(*Stream.Input); ASSERT_THAT_ERROR(Reader.readObject(Little2), Succeeded()); ASSERT_THAT_ERROR(Reader.readObject(Big2), Succeeded()); ASSERT_THAT_ERROR(Reader.readInteger(NS2), Succeeded()); ASSERT_THAT_ERROR(Reader.readInteger(NI2), Succeeded()); ASSERT_THAT_ERROR(Reader.readInteger(NUL2), Succeeded()); ASSERT_EQ(0U, Reader.bytesRemaining()); EXPECT_EQ(Little, *Little2); EXPECT_EQ(Big, *Big2); EXPECT_EQ(NS, NS2); EXPECT_EQ(NI, NI2); EXPECT_EQ(NUL, NUL2); } } TEST_F(BinaryStreamTest, StreamReaderIntegerArray) { // 1. Arrays of integers std::vector Ints = {1, 2, 3, 4, 5}; ArrayRef IntBytes(reinterpret_cast(&Ints[0]), Ints.size() * sizeof(int)); initializeInput(IntBytes, alignof(int)); for (auto &Stream : Streams) { BinaryStreamReader Reader(*Stream.Input); ArrayRef IntsRef; ASSERT_THAT_ERROR(Reader.readArray(IntsRef, Ints.size()), Succeeded()); ASSERT_EQ(0U, Reader.bytesRemaining()); EXPECT_EQ(makeArrayRef(Ints), IntsRef); Reader.setOffset(0); FixedStreamArray FixedIntsRef; ASSERT_THAT_ERROR(Reader.readArray(FixedIntsRef, Ints.size()), Succeeded()); ASSERT_EQ(0U, Reader.bytesRemaining()); ASSERT_EQ(Ints, std::vector(FixedIntsRef.begin(), FixedIntsRef.end())); } } TEST_F(BinaryStreamTest, StreamReaderEnum) { enum class MyEnum : int64_t { Foo = -10, Bar = 0, Baz = 10 }; std::vector Enums = {MyEnum::Bar, MyEnum::Baz, MyEnum::Foo}; initializeOutput(Enums.size() * sizeof(MyEnum), alignof(MyEnum)); initializeInputFromOutput(alignof(MyEnum)); for (auto &Stream : Streams) { BinaryStreamWriter Writer(*Stream.Output); for (auto Value : Enums) ASSERT_THAT_ERROR(Writer.writeEnum(Value), Succeeded()); BinaryStreamReader Reader(*Stream.Input); FixedStreamArray FSA; for (size_t I = 0; I < Enums.size(); ++I) { MyEnum Value; ASSERT_THAT_ERROR(Reader.readEnum(Value), Succeeded()); EXPECT_EQ(Enums[I], Value); } ASSERT_EQ(0U, Reader.bytesRemaining()); } } TEST_F(BinaryStreamTest, StreamReaderULEB128) { std::vector TestValues = { 0, // Zero 0x7F, // One byte 0xFF, // One byte, all-ones 0xAAAA, // Two bytes 0xAAAAAAAA, // Four bytes 0xAAAAAAAAAAAAAAAA, // Eight bytes 0xffffffffffffffff // Eight bytess, all-ones }; // Conservatively assume a 10-byte encoding for each of our LEB128s, with no // alignment requirement. initializeOutput(10 * TestValues.size(), 1); initializeInputFromOutput(1); for (auto &Stream : Streams) { // Write fields. BinaryStreamWriter Writer(*Stream.Output); for (const auto &Value : TestValues) ASSERT_THAT_ERROR(Writer.writeULEB128(Value), Succeeded()); // Read fields. BinaryStreamReader Reader(*Stream.Input); std::vector Results; Results.resize(TestValues.size()); for (unsigned I = 0; I != TestValues.size(); ++I) ASSERT_THAT_ERROR(Reader.readULEB128(Results[I]), Succeeded()); for (unsigned I = 0; I != TestValues.size(); ++I) EXPECT_EQ(TestValues[I], Results[I]); } } TEST_F(BinaryStreamTest, StreamReaderSLEB128) { std::vector TestValues = { 0, // Zero 0x7F, // One byte -0x7F, // One byte, negative 0xFF, // One byte, all-ones 0xAAAA, // Two bytes -0xAAAA, // Two bytes, negative 0xAAAAAAAA, // Four bytes -0xAAAAAAAA, // Four bytes, negative 0x2AAAAAAAAAAAAAAA, // Eight bytes -0x7ffffffffffffff // Eight bytess, negative }; // Conservatively assume a 10-byte encoding for each of our LEB128s, with no // alignment requirement. initializeOutput(10 * TestValues.size(), 1); initializeInputFromOutput(1); for (auto &Stream : Streams) { // Write fields. BinaryStreamWriter Writer(*Stream.Output); for (const auto &Value : TestValues) ASSERT_THAT_ERROR(Writer.writeSLEB128(Value), Succeeded()); // Read fields. BinaryStreamReader Reader(*Stream.Input); std::vector Results; Results.resize(TestValues.size()); for (unsigned I = 0; I != TestValues.size(); ++I) ASSERT_THAT_ERROR(Reader.readSLEB128(Results[I]), Succeeded()); for (unsigned I = 0; I != TestValues.size(); ++I) EXPECT_EQ(TestValues[I], Results[I]); } } TEST_F(BinaryStreamTest, StreamReaderObject) { struct Foo { int X; double Y; char Z; bool operator==(const Foo &Other) const { return X == Other.X && Y == Other.Y && Z == Other.Z; } }; std::vector Foos; Foos.push_back({-42, 42.42, 42}); Foos.push_back({100, 3.1415, static_cast(-89)}); Foos.push_back({200, 2.718, static_cast(-12) }); const uint8_t *Bytes = reinterpret_cast(&Foos[0]); initializeInput(makeArrayRef(Bytes, 3 * sizeof(Foo)), alignof(Foo)); for (auto &Stream : Streams) { // 1. Reading object pointers. BinaryStreamReader Reader(*Stream.Input); const Foo *FPtrOut = nullptr; const Foo *GPtrOut = nullptr; const Foo *HPtrOut = nullptr; ASSERT_THAT_ERROR(Reader.readObject(FPtrOut), Succeeded()); ASSERT_THAT_ERROR(Reader.readObject(GPtrOut), Succeeded()); ASSERT_THAT_ERROR(Reader.readObject(HPtrOut), Succeeded()); EXPECT_EQ(0U, Reader.bytesRemaining()); EXPECT_EQ(Foos[0], *FPtrOut); EXPECT_EQ(Foos[1], *GPtrOut); EXPECT_EQ(Foos[2], *HPtrOut); } } TEST_F(BinaryStreamTest, StreamReaderStrings) { std::vector Bytes = {'O', 'n', 'e', '\0', 'T', 'w', 'o', '\0', 'T', 'h', 'r', 'e', 'e', '\0', 'F', 'o', 'u', 'r', '\0'}; initializeInput(Bytes, 1); for (auto &Stream : Streams) { BinaryStreamReader Reader(*Stream.Input); StringRef S1; StringRef S2; StringRef S3; StringRef S4; ASSERT_THAT_ERROR(Reader.readCString(S1), Succeeded()); ASSERT_THAT_ERROR(Reader.readCString(S2), Succeeded()); ASSERT_THAT_ERROR(Reader.readCString(S3), Succeeded()); ASSERT_THAT_ERROR(Reader.readCString(S4), Succeeded()); ASSERT_EQ(0U, Reader.bytesRemaining()); EXPECT_EQ("One", S1); EXPECT_EQ("Two", S2); EXPECT_EQ("Three", S3); EXPECT_EQ("Four", S4); S1 = S2 = S3 = S4 = ""; Reader.setOffset(0); ASSERT_THAT_ERROR(Reader.readFixedString(S1, 3), Succeeded()); ASSERT_THAT_ERROR(Reader.skip(1), Succeeded()); ASSERT_THAT_ERROR(Reader.readFixedString(S2, 3), Succeeded()); ASSERT_THAT_ERROR(Reader.skip(1), Succeeded()); ASSERT_THAT_ERROR(Reader.readFixedString(S3, 5), Succeeded()); ASSERT_THAT_ERROR(Reader.skip(1), Succeeded()); ASSERT_THAT_ERROR(Reader.readFixedString(S4, 4), Succeeded()); ASSERT_THAT_ERROR(Reader.skip(1), Succeeded()); ASSERT_EQ(0U, Reader.bytesRemaining()); EXPECT_EQ("One", S1); EXPECT_EQ("Two", S2); EXPECT_EQ("Three", S3); EXPECT_EQ("Four", S4); } } TEST_F(BinaryStreamTest, StreamWriterBounds) { initializeOutput(5, 1); for (auto &Stream : Streams) { BinaryStreamWriter Writer(*Stream.Output); // 1. Can write a string that exactly fills the buffer. EXPECT_EQ(5U, Writer.bytesRemaining()); EXPECT_THAT_ERROR(Writer.writeFixedString("abcde"), Succeeded()); EXPECT_EQ(0U, Writer.bytesRemaining()); // 2. Can write an empty string even when you're full EXPECT_THAT_ERROR(Writer.writeFixedString(""), Succeeded()); EXPECT_THAT_ERROR(Writer.writeFixedString("a"), Failed()); // 3. Can't write a string that is one character too long. Writer.setOffset(0); EXPECT_THAT_ERROR(Writer.writeFixedString("abcdef"), Failed()); } } TEST_F(BinaryStreamTest, StreamWriterIntegerArrays) { // 3. Arrays of integers std::vector SourceInts = {1, 2, 3, 4, 5}; ArrayRef SourceBytes(reinterpret_cast(&SourceInts[0]), SourceInts.size() * sizeof(int)); initializeInput(SourceBytes, alignof(int)); initializeOutputFromInput(alignof(int)); for (auto &Stream : Streams) { BinaryStreamReader Reader(*Stream.Input); BinaryStreamWriter Writer(*Stream.Output); ArrayRef Ints; ArrayRef Ints2; // First read them, then write them, then read them back. ASSERT_THAT_ERROR(Reader.readArray(Ints, SourceInts.size()), Succeeded()); ASSERT_THAT_ERROR(Writer.writeArray(Ints), Succeeded()); BinaryStreamReader ReaderBacker(*Stream.Output); ASSERT_THAT_ERROR(ReaderBacker.readArray(Ints2, SourceInts.size()), Succeeded()); EXPECT_EQ(makeArrayRef(SourceInts), Ints2); } } TEST_F(BinaryStreamTest, StringWriterStrings) { StringRef Strings[] = {"First", "Second", "Third", "Fourth"}; size_t Length = 0; for (auto S : Strings) Length += S.size() + 1; initializeOutput(Length, 1); initializeInputFromOutput(1); for (auto &Stream : Streams) { BinaryStreamWriter Writer(*Stream.Output); for (auto S : Strings) ASSERT_THAT_ERROR(Writer.writeCString(S), Succeeded()); std::vector InStrings; BinaryStreamReader Reader(*Stream.Input); while (!Reader.empty()) { StringRef S; ASSERT_THAT_ERROR(Reader.readCString(S), Succeeded()); InStrings.push_back(S); } EXPECT_EQ(makeArrayRef(Strings), makeArrayRef(InStrings)); } } TEST_F(BinaryStreamTest, StreamWriterAppend) { StringRef Strings[] = {"First", "Second", "Third", "Fourth"}; AppendingBinaryByteStream Stream(support::little); BinaryStreamWriter Writer(Stream); for (auto &Str : Strings) { EXPECT_THAT_ERROR(Writer.writeCString(Str), Succeeded()); } BinaryStreamReader Reader(Stream); for (auto &Str : Strings) { StringRef S; EXPECT_THAT_ERROR(Reader.readCString(S), Succeeded()); EXPECT_EQ(Str, S); } } } namespace { struct BinaryItemStreamObject { explicit BinaryItemStreamObject(ArrayRef Bytes) : Bytes(Bytes) {} ArrayRef Bytes; }; } namespace llvm { template <> struct BinaryItemTraits { static size_t length(const BinaryItemStreamObject &Item) { return Item.Bytes.size(); } static ArrayRef bytes(const BinaryItemStreamObject &Item) { return Item.Bytes; } }; } namespace { TEST_F(BinaryStreamTest, BinaryItemStream) { std::vector Objects; struct Foo { int X; double Y; }; std::vector Foos = {{1, 1.0}, {2, 2.0}, {3, 3.0}}; BumpPtrAllocator Allocator; for (const auto &F : Foos) { uint8_t *Ptr = static_cast(Allocator.Allocate(sizeof(Foo), alignof(Foo))); MutableArrayRef Buffer(Ptr, sizeof(Foo)); MutableBinaryByteStream Stream(Buffer, llvm::support::big); BinaryStreamWriter Writer(Stream); ASSERT_THAT_ERROR(Writer.writeObject(F), Succeeded()); Objects.push_back(BinaryItemStreamObject(Buffer)); } BinaryItemStream ItemStream(big); ItemStream.setItems(Objects); BinaryStreamReader Reader(ItemStream); for (const auto &F : Foos) { const Foo *F2; ASSERT_THAT_ERROR(Reader.readObject(F2), Succeeded()); EXPECT_EQ(F.X, F2->X); EXPECT_DOUBLE_EQ(F.Y, F2->Y); } } } // end anonymous namespace