| //===- 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<uint8_t> Data, endianness Endian, uint32_t Align) |
| : Data(Data), PartitionIndex(alignDown(Data.size() / 2, Align)), |
| Endian(Endian) {} |
| |
| endianness getEndian() const override { return Endian; } |
| |
| Error readBytes(uint64_t Offset, uint64_t Size, |
| ArrayRef<uint8_t> &Buffer) override { |
| if (auto EC = checkOffsetForRead(Offset, Size)) |
| return EC; |
| uint64_t S = startIndex(Offset); |
| auto Ref = Data.drop_front(S); |
| if (Ref.size() >= Size) { |
| Buffer = Ref.take_front(Size); |
| return Error::success(); |
| } |
| |
| uint64_t BytesLeft = Size - Ref.size(); |
| uint8_t *Ptr = Allocator.Allocate<uint8_t>(Size); |
| ::memcpy(Ptr, Ref.data(), Ref.size()); |
| ::memcpy(Ptr + Ref.size(), Data.data(), BytesLeft); |
| Buffer = ArrayRef<uint8_t>(Ptr, Size); |
| return Error::success(); |
| } |
| |
| Error readLongestContiguousChunk(uint64_t Offset, |
| ArrayRef<uint8_t> &Buffer) override { |
| if (auto EC = checkOffsetForRead(Offset, 1)) |
| return EC; |
| uint64_t S = startIndex(Offset); |
| Buffer = Data.drop_front(S); |
| return Error::success(); |
| } |
| |
| uint64_t getLength() override { return Data.size(); } |
| |
| Error writeBytes(uint64_t Offset, ArrayRef<uint8_t> SrcData) override { |
| if (auto EC = checkOffsetForWrite(Offset, SrcData.size())) |
| return EC; |
| if (SrcData.empty()) |
| return Error::success(); |
| |
| uint64_t S = startIndex(Offset); |
| MutableArrayRef<uint8_t> Ref(Data); |
| Ref = Ref.drop_front(S); |
| if (Ref.size() >= SrcData.size()) { |
| ::memcpy(Ref.data(), SrcData.data(), SrcData.size()); |
| return Error::success(); |
| } |
| |
| uint64_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: |
| uint64_t startIndex(uint64_t Offset) const { |
| return (Offset + PartitionIndex) % Data.size(); |
| } |
| |
| uint64_t endIndex(uint64_t Offset, uint64_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<uint8_t> Data; |
| uint32_t PartitionIndex = 0; |
| endianness Endian; |
| BumpPtrAllocator Allocator; |
| }; |
| |
| constexpr endianness Endians[] = {big, little, native}; |
| constexpr uint32_t NumEndians = std::size(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<BinaryStream> Input; |
| std::unique_ptr<WritableBinaryStream> Output; |
| }; |
| |
| void initializeInput(ArrayRef<uint8_t> Input, uint32_t Align) { |
| InputData = Input; |
| |
| BrokenInputData.resize(InputData.size()); |
| if (!Input.empty()) { |
| uint64_t PartitionIndex = alignDown(InputData.size() / 2, Align); |
| uint64_t RightBytes = InputData.size() - PartitionIndex; |
| uint64_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<BinaryByteStream>(InputData, Endians[I]); |
| auto InBrokenStream = std::make_unique<BrokenStream>( |
| BrokenInputData, Endians[I], Align); |
| |
| Streams[I * 2].Input = std::move(InByteStream); |
| Streams[I * 2 + 1].Input = std::move(InBrokenStream); |
| } |
| } |
| |
| void initializeOutput(uint64_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<MutableBinaryByteStream>(OutputData, Endians[I]); |
| Streams[I * 2 + 1].Output = std::make_unique<BrokenStream>( |
| BrokenOutputData, Endians[I], Align); |
| } |
| } |
| |
| void initializeOutputFromInput(uint32_t Align) { |
| for (uint32_t I = 0; I < NumEndians; ++I) { |
| Streams[I * 2].Output = |
| std::make_unique<MutableBinaryByteStream>(InputData, Endians[I]); |
| Streams[I * 2 + 1].Output = std::make_unique<BrokenStream>( |
| BrokenInputData, Endians[I], Align); |
| } |
| } |
| |
| void initializeInputFromOutput(uint32_t Align) { |
| for (uint32_t I = 0; I < NumEndians; ++I) { |
| Streams[I * 2].Input = |
| std::make_unique<BinaryByteStream>(OutputData, Endians[I]); |
| Streams[I * 2 + 1].Input = std::make_unique<BrokenStream>( |
| BrokenOutputData, Endians[I], Align); |
| } |
| } |
| |
| std::vector<uint8_t> InputData; |
| std::vector<uint8_t> BrokenInputData; |
| |
| std::vector<uint8_t> OutputData; |
| std::vector<uint8_t> BrokenOutputData; |
| |
| std::vector<StreamPair> Streams; |
| }; |
| |
| // Tests that a we can read from a BinaryByteStream without a StreamReader. |
| TEST_F(BinaryStreamTest, BinaryByteStreamBounds) { |
| std::vector<uint8_t> InputData = {1, 2, 3, 4, 5}; |
| initializeInput(InputData, 1); |
| |
| for (auto &Stream : Streams) { |
| ArrayRef<uint8_t> 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(ArrayRef(InputData).slice(2, 1), Buffer); |
| ASSERT_THAT_ERROR(Stream.Input->readBytes(0, 4, Buffer), Succeeded()); |
| EXPECT_EQ(ArrayRef(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<uint8_t> InputData = {1, 2, 3, 4, 5}; |
| initializeInput(InputData, 1); |
| |
| for (const auto &Stream : Streams) { |
| ArrayRef<uint8_t> 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(ArrayRef(InputData).slice(2, 1), Buffer); |
| |
| // Reading everything from offset 2 on. |
| ASSERT_THAT_ERROR(Ref.readLongestContiguousChunk(2, Buffer), Succeeded()); |
| if (Stream.IsContiguous) |
| EXPECT_EQ(ArrayRef(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(ArrayRef(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(ArrayRef(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(ArrayRef(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<uint8_t> InputData = {1, 2, 3, 4, 5, 4, 3, 2, 1}; |
| auto RefData = ArrayRef(InputData); |
| initializeInput(InputData, 1); |
| |
| ArrayRef<uint8_t> 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<uint8_t> 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) { |
| uint64_t ExpectedSize = Stream.Input->getLength() - Offset; |
| |
| // Read everything from Offset until the end of the input data. |
| ArrayRef<uint8_t> 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<uint8_t> Data2; |
| ASSERT_THAT_ERROR(Stream.Output->readBytes(Offset, ExpectedSize, Data2), |
| Succeeded()); |
| EXPECT_EQ(ArrayRef(InputData).drop_front(Offset), Data2); |
| } |
| |
| std::vector<uint8_t> 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<uint8_t> InputData = {'T', 'e', 's', 't', 'T', 'e', 's', 't'}; |
| auto Test = ArrayRef(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<uint8_t>(InputData), Stream.data()); |
| } |
| |
| // Test that FixedStreamArray works correctly. |
| TEST_F(BinaryStreamTest, FixedStreamArray) { |
| std::vector<uint32_t> Ints = {90823, 12908, 109823, 209823}; |
| ArrayRef<uint8_t> IntBytes(reinterpret_cast<uint8_t *>(Ints.data()), |
| Ints.size() * sizeof(uint32_t)); |
| |
| initializeInput(IntBytes, alignof(uint32_t)); |
| |
| for (auto &Stream : Streams) { |
| ASSERT_EQ(InputData.size(), Stream.Input->getLength()); |
| |
| FixedStreamArray<uint32_t> 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<std::pair<uint32_t, uint32_t>> Pairs = {{867, 5309}, {555, 1212}}; |
| ArrayRef<uint8_t> PairBytes(reinterpret_cast<uint8_t *>(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<std::pair<uint32_t, uint32_t>> 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<uint8_t> StringBytes( |
| reinterpret_cast<const uint8_t *>(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<uint8_t> Bytes; |
| if (auto EC = Stream.readBytes(0, Len, Bytes)) |
| return EC; |
| Item = |
| StringRef(reinterpret_cast<const char *>(Bytes.data()), Bytes.size()); |
| ++Index; |
| return Error::success(); |
| } |
| |
| uint32_t Index = 0; |
| }; |
| |
| for (auto &Stream : Streams) { |
| VarStreamArray<StringRef, StringExtractor> 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<uint8_t> 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<int> Ints = {1, 2, 3, 4, 5}; |
| ArrayRef<uint8_t> IntBytes(reinterpret_cast<uint8_t *>(&Ints[0]), |
| Ints.size() * sizeof(int)); |
| |
| initializeInput(IntBytes, alignof(int)); |
| for (auto &Stream : Streams) { |
| BinaryStreamReader Reader(*Stream.Input); |
| ArrayRef<int> IntsRef; |
| ASSERT_THAT_ERROR(Reader.readArray(IntsRef, Ints.size()), Succeeded()); |
| ASSERT_EQ(0U, Reader.bytesRemaining()); |
| EXPECT_EQ(ArrayRef(Ints), IntsRef); |
| |
| Reader.setOffset(0); |
| FixedStreamArray<int> FixedIntsRef; |
| ASSERT_THAT_ERROR(Reader.readArray(FixedIntsRef, Ints.size()), Succeeded()); |
| ASSERT_EQ(0U, Reader.bytesRemaining()); |
| ASSERT_EQ(Ints, std::vector<int>(FixedIntsRef.begin(), FixedIntsRef.end())); |
| } |
| } |
| |
| TEST_F(BinaryStreamTest, StreamReaderEnum) { |
| enum class MyEnum : int64_t { Foo = -10, Bar = 0, Baz = 10 }; |
| |
| std::vector<MyEnum> 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<MyEnum> 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<uint64_t> 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<uint64_t> 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<int64_t> 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<int64_t> 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<Foo> Foos; |
| Foos.push_back({-42, 42.42, 42}); |
| Foos.push_back({100, 3.1415, static_cast<char>(-89)}); |
| Foos.push_back({200, 2.718, static_cast<char>(-12) }); |
| |
| const uint8_t *Bytes = reinterpret_cast<const uint8_t *>(&Foos[0]); |
| |
| initializeInput(ArrayRef(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<uint8_t> 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<int> SourceInts = {1, 2, 3, 4, 5}; |
| ArrayRef<uint8_t> SourceBytes(reinterpret_cast<uint8_t *>(&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<int> Ints; |
| ArrayRef<int> 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(ArrayRef(SourceInts), Ints2); |
| } |
| } |
| |
| TEST_F(BinaryStreamTest, StreamWriterStrings) { |
| 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<StringRef> InStrings; |
| BinaryStreamReader Reader(*Stream.Input); |
| while (!Reader.empty()) { |
| StringRef S; |
| ASSERT_THAT_ERROR(Reader.readCString(S), Succeeded()); |
| InStrings.push_back(S); |
| } |
| EXPECT_EQ(ArrayRef(Strings), ArrayRef(InStrings)); |
| } |
| } |
| |
| TEST_F(BinaryStreamTest, StreamWriterPadToAlignment) { |
| // This test may seem excessive but it is checking for past bugs and corner |
| // cases by making sure that the stream is allowed to grow and that |
| // both multiple pad chunks and single chunk extensions work. |
| AppendingBinaryByteStream Stream(support::little); |
| BinaryStreamWriter Writer(Stream); |
| |
| // Offset 0: '0' |
| EXPECT_THAT_ERROR(Writer.writeInteger('0'), Succeeded()); |
| // Offset 1..110: 0 |
| EXPECT_THAT_ERROR(Writer.padToAlignment(111), Succeeded()); |
| // Offset 111: '*' |
| EXPECT_THAT_ERROR(Writer.writeInteger('*'), Succeeded()); |
| // Offset 112..120: 0 |
| EXPECT_THAT_ERROR(Writer.padToAlignment(11), Succeeded()); |
| |
| BinaryStreamReader Reader(Stream); |
| char c; |
| // Offset 0 |
| EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Succeeded()); |
| EXPECT_EQ('0', c); |
| // Offset 1..110 |
| for (int i = 0; i < 110; ++i) { |
| char c; |
| EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Succeeded()); |
| EXPECT_EQ('\0', c); |
| } |
| // Offset 111 |
| EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Succeeded()); |
| EXPECT_EQ('*', c); |
| // Offset 112..120 |
| for (int i = 0; i < 9; ++i) { |
| char c; |
| EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Succeeded()); |
| EXPECT_EQ('\0', c); |
| } |
| |
| // EOF. |
| EXPECT_THAT_ERROR(Reader.readInteger<char>(c), Failed()); |
| } |
| |
| 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<uint8_t> Bytes) : Bytes(Bytes) {} |
| |
| ArrayRef<uint8_t> Bytes; |
| }; |
| } |
| |
| namespace llvm { |
| template <> struct BinaryItemTraits<BinaryItemStreamObject> { |
| static size_t length(const BinaryItemStreamObject &Item) { |
| return Item.Bytes.size(); |
| } |
| |
| static ArrayRef<uint8_t> bytes(const BinaryItemStreamObject &Item) { |
| return Item.Bytes; |
| } |
| }; |
| } |
| |
| namespace { |
| |
| TEST_F(BinaryStreamTest, BinaryItemStream) { |
| std::vector<BinaryItemStreamObject> Objects; |
| |
| struct Foo { |
| int X; |
| double Y; |
| }; |
| std::vector<Foo> Foos = {{1, 1.0}, {2, 2.0}, {3, 3.0}}; |
| BumpPtrAllocator Allocator; |
| for (const auto &F : Foos) { |
| uint8_t *Ptr = static_cast<uint8_t *>(Allocator.Allocate(sizeof(Foo), |
| alignof(Foo))); |
| MutableArrayRef<uint8_t> 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<BinaryItemStreamObject> 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 |