From 81a153c99a29006cf55b31f0ba898faf3ecd02b5 Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Mon, 27 Nov 2017 18:48:37 +0000 Subject: [PATCH] [BinaryStream] Support growable streams. The existing library assumed that a stream's length would never change. This makes some things simpler, but it's not flexible enough for what we need, especially for writable streams where what you really want is for each call to write to actually append. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@319070 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ADT/StringExtras.h | 5 + include/llvm/Support/BinaryByteStream.h | 72 ++++++++++++++- include/llvm/Support/BinaryItemStream.h | 2 +- include/llvm/Support/BinaryStream.h | 26 +++++- include/llvm/Support/BinaryStreamRef.h | 69 +++++++++++--- lib/DebugInfo/MSF/MappedBlockStream.cpp | 8 +- lib/Support/BinaryStreamRef.cpp | 16 ++-- lib/Support/BinaryStreamWriter.cpp | 3 +- .../DebugInfo/MSF/MappedBlockStreamTest.cpp | 6 +- unittests/Support/BinaryStreamTest.cpp | 92 ++++++++++++++++++- 10 files changed, 260 insertions(+), 39 deletions(-) diff --git a/include/llvm/ADT/StringExtras.h b/include/llvm/ADT/StringExtras.h index 37df1659b97..a0e0d7d64f2 100644 --- a/include/llvm/ADT/StringExtras.h +++ b/include/llvm/ADT/StringExtras.h @@ -47,6 +47,11 @@ inline StringRef toStringRef(ArrayRef Input) { return StringRef(reinterpret_cast(Input.begin()), Input.size()); } +/// Construct a string ref from an array ref of unsigned chars. +inline ArrayRef arrayRefFromStringRef(StringRef Input) { + return {Input.bytes_begin(), Input.bytes_end()}; +} + /// Interpret the given character \p C as a hexadecimal digit and return its /// value. /// diff --git a/include/llvm/Support/BinaryByteStream.h b/include/llvm/Support/BinaryByteStream.h index 694be28e07e..a87a9bea0e3 100644 --- a/include/llvm/Support/BinaryByteStream.h +++ b/include/llvm/Support/BinaryByteStream.h @@ -41,7 +41,7 @@ public: Error readBytes(uint32_t Offset, uint32_t Size, ArrayRef &Buffer) override { - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; Buffer = Data.slice(Offset, Size); return Error::success(); @@ -49,7 +49,7 @@ public: Error readLongestContiguousChunk(uint32_t Offset, ArrayRef &Buffer) override { - if (auto EC = checkOffset(Offset, 1)) + if (auto EC = checkOffsetForRead(Offset, 1)) return EC; Buffer = Data.slice(Offset); return Error::success(); @@ -114,7 +114,7 @@ public: if (Buffer.empty()) return Error::success(); - if (auto EC = checkOffset(Offset, Buffer.size())) + if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) return EC; uint8_t *DataPtr = const_cast(Data.data()); @@ -131,6 +131,72 @@ private: BinaryByteStream ImmutableStream; }; +/// \brief An implementation of WritableBinaryStream which can write at its end +/// causing the underlying data to grow. This class owns the underlying data. +class AppendingBinaryByteStream : public WritableBinaryStream { + std::vector Data; + llvm::support::endianness Endian; + +public: + AppendingBinaryByteStream() = default; + AppendingBinaryByteStream(llvm::support::endianness Endian) + : Endian(Endian) {} + + void clear() { Data.clear(); } + + llvm::support::endianness getEndian() const override { return Endian; } + + Error readBytes(uint32_t Offset, uint32_t Size, + ArrayRef &Buffer) override { + if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) + return EC; + + Buffer = makeArrayRef(Data).slice(Offset, Size); + return Error::success(); + } + + Error readLongestContiguousChunk(uint32_t Offset, + ArrayRef &Buffer) override { + if (auto EC = checkOffsetForWrite(Offset, 1)) + return EC; + + Buffer = makeArrayRef(Data).slice(Offset); + return Error::success(); + } + + uint32_t getLength() override { return Data.size(); } + + Error writeBytes(uint32_t Offset, ArrayRef Buffer) override { + if (Buffer.empty()) + return Error::success(); + + // This is well-defined for any case except where offset is strictly + // greater than the current length. If offset is equal to the current + // length, we can still grow. If offset is beyond the current length, we + // would have to decide how to deal with the intermediate uninitialized + // bytes. So we punt on that case for simplicity and just say it's an + // error. + if (Offset > getLength()) + return make_error(stream_error_code::invalid_offset); + + uint32_t RequiredSize = Offset + Buffer.size(); + if (RequiredSize > Data.size()) + Data.resize(RequiredSize); + + ::memcpy(Data.data() + Offset, Buffer.data(), Buffer.size()); + return Error::success(); + } + + Error commit() override { return Error::success(); } + + /// \brief Return the properties of this stream. + virtual BinaryStreamFlags getFlags() const override { + return BSF_Write | BSF_Append; + } + + MutableArrayRef data() { return Data; } +}; + /// \brief An implementation of WritableBinaryStream backed by an llvm /// FileOutputBuffer. class FileBufferByteStream : public WritableBinaryStream { diff --git a/include/llvm/Support/BinaryItemStream.h b/include/llvm/Support/BinaryItemStream.h index fe7e6caeaaf..278723ddf8d 100644 --- a/include/llvm/Support/BinaryItemStream.h +++ b/include/llvm/Support/BinaryItemStream.h @@ -45,7 +45,7 @@ public: if (!ExpectedIndex) return ExpectedIndex.takeError(); const auto &Item = Items[*ExpectedIndex]; - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; if (Size > Traits::length(Item)) return make_error(stream_error_code::stream_too_short); diff --git a/include/llvm/Support/BinaryStream.h b/include/llvm/Support/BinaryStream.h index a227117e063..d69a03eccfd 100644 --- a/include/llvm/Support/BinaryStream.h +++ b/include/llvm/Support/BinaryStream.h @@ -11,6 +11,7 @@ #define LLVM_SUPPORT_BINARYSTREAM_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitmaskEnum.h" #include "llvm/Support/BinaryStreamError.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -18,6 +19,13 @@ namespace llvm { +enum BinaryStreamFlags { + BSF_None = 0, + BSF_Write = 1, // Stream supports writing. + BSF_Append = 2, // Writing can occur at offset == length. + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ BSF_Append) +}; + /// \brief An interface for accessing data in a stream-like format, but which /// discourages copying. Instead of specifying a buffer in which to copy /// data on a read, the API returns an ArrayRef to data owned by the stream's @@ -45,8 +53,11 @@ public: /// \brief Return the number of bytes of data in this stream. virtual uint32_t getLength() = 0; + /// \brief Return the properties of this stream. + virtual BinaryStreamFlags getFlags() const { return BSF_None; } + protected: - Error checkOffset(uint32_t Offset, uint32_t DataSize) { + Error checkOffsetForRead(uint32_t Offset, uint32_t DataSize) { if (Offset > getLength()) return make_error(stream_error_code::invalid_offset); if (getLength() < DataSize + Offset) @@ -71,6 +82,19 @@ public: /// \brief For buffered streams, commits changes to the backing store. virtual Error commit() = 0; + + /// \brief Return the properties of this stream. + BinaryStreamFlags getFlags() const override { return BSF_Write; } + +protected: + Error checkOffsetForWrite(uint32_t Offset, uint32_t DataSize) { + if (!(getFlags() & BSF_Append)) + return checkOffsetForRead(Offset, DataSize); + + if (Offset > getLength()) + return make_error(stream_error_code::invalid_offset); + return Error::success(); + } }; } // end namespace llvm diff --git a/include/llvm/Support/BinaryStreamRef.h b/include/llvm/Support/BinaryStreamRef.h index 5679bd459ab..5cf355be6fe 100644 --- a/include/llvm/Support/BinaryStreamRef.h +++ b/include/llvm/Support/BinaryStreamRef.h @@ -11,6 +11,7 @@ #define LLVM_SUPPORT_BINARYSTREAMREF_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/BinaryStream.h" #include "llvm/Support/BinaryStreamError.h" #include "llvm/Support/Error.h" @@ -24,12 +25,18 @@ namespace llvm { template class BinaryStreamRefBase { protected: BinaryStreamRefBase() = default; + explicit BinaryStreamRefBase(StreamType &BorrowedImpl) + : BorrowedImpl(&BorrowedImpl), ViewOffset(0) { + if (!(BorrowedImpl.getFlags() & BSF_Append)) + Length = BorrowedImpl.getLength(); + } + BinaryStreamRefBase(std::shared_ptr SharedImpl, uint32_t Offset, - uint32_t Length) + Optional Length) : SharedImpl(SharedImpl), BorrowedImpl(SharedImpl.get()), ViewOffset(Offset), Length(Length) {} BinaryStreamRefBase(StreamType &BorrowedImpl, uint32_t Offset, - uint32_t Length) + Optional Length) : BorrowedImpl(&BorrowedImpl), ViewOffset(Offset), Length(Length) {} BinaryStreamRefBase(const BinaryStreamRefBase &Other) = default; BinaryStreamRefBase &operator=(const BinaryStreamRefBase &Other) = default; @@ -42,28 +49,50 @@ public: return BorrowedImpl->getEndian(); } - uint32_t getLength() const { return Length; } + uint32_t getLength() const { + if (Length.hasValue()) + return *Length; + + return BorrowedImpl ? (BorrowedImpl->getLength() - ViewOffset) : 0; + } - /// Return a new BinaryStreamRef with the first \p N elements removed. + /// Return a new BinaryStreamRef with the first \p N elements removed. If + /// this BinaryStreamRef is length-tracking, then the resulting one will be + /// too. RefType drop_front(uint32_t N) const { if (!BorrowedImpl) return RefType(); - N = std::min(N, Length); + N = std::min(N, getLength()); RefType Result(static_cast(*this)); + if (N == 0) + return Result; + Result.ViewOffset += N; - Result.Length -= N; + if (Result.Length.hasValue()) + *Result.Length -= N; return Result; } - /// Return a new BinaryStreamRef with the first \p N elements removed. + /// Return a new BinaryStreamRef with the last \p N elements removed. If + /// this BinaryStreamRef is length-tracking and \p N is greater than 0, then + /// this BinaryStreamRef will no longer length-track. RefType drop_back(uint32_t N) const { if (!BorrowedImpl) return RefType(); - N = std::min(N, Length); RefType Result(static_cast(*this)); - Result.Length -= N; + N = std::min(N, getLength()); + + if (N == 0) + return Result; + + // Since we're dropping non-zero bytes from the end, stop length-tracking + // by setting the length of the resulting StreamRef to an explicit value. + if (!Result.Length.hasValue()) + Result.Length = getLength(); + + *Result.Length -= N; return Result; } @@ -104,7 +133,7 @@ public: } protected: - Error checkOffset(uint32_t Offset, uint32_t DataSize) const { + Error checkOffsetForRead(uint32_t Offset, uint32_t DataSize) const { if (Offset > getLength()) return make_error(stream_error_code::invalid_offset); if (getLength() < DataSize + Offset) @@ -115,7 +144,7 @@ protected: std::shared_ptr SharedImpl; StreamType *BorrowedImpl = nullptr; uint32_t ViewOffset = 0; - uint32_t Length = 0; + Optional Length; }; /// \brief BinaryStreamRef is to BinaryStream what ArrayRef is to an Array. It @@ -130,13 +159,14 @@ class BinaryStreamRef friend BinaryStreamRefBase; friend class WritableBinaryStreamRef; BinaryStreamRef(std::shared_ptr Impl, uint32_t ViewOffset, - uint32_t Length) + Optional Length) : BinaryStreamRefBase(Impl, ViewOffset, Length) {} public: BinaryStreamRef() = default; BinaryStreamRef(BinaryStream &Stream); - BinaryStreamRef(BinaryStream &Stream, uint32_t Offset, uint32_t Length); + BinaryStreamRef(BinaryStream &Stream, uint32_t Offset, + Optional Length); explicit BinaryStreamRef(ArrayRef Data, llvm::support::endianness Endian); explicit BinaryStreamRef(StringRef Data, llvm::support::endianness Endian); @@ -195,14 +225,23 @@ class WritableBinaryStreamRef WritableBinaryStream> { friend BinaryStreamRefBase; WritableBinaryStreamRef(std::shared_ptr Impl, - uint32_t ViewOffset, uint32_t Length) + uint32_t ViewOffset, Optional Length) : BinaryStreamRefBase(Impl, ViewOffset, Length) {} + Error checkOffsetForWrite(uint32_t Offset, uint32_t DataSize) const { + if (!(BorrowedImpl->getFlags() & BSF_Append)) + return checkOffsetForRead(Offset, DataSize); + + if (Offset > getLength()) + return make_error(stream_error_code::invalid_offset); + return Error::success(); + } + public: WritableBinaryStreamRef() = default; WritableBinaryStreamRef(WritableBinaryStream &Stream); WritableBinaryStreamRef(WritableBinaryStream &Stream, uint32_t Offset, - uint32_t Length); + Optional Length); explicit WritableBinaryStreamRef(MutableArrayRef Data, llvm::support::endianness Endian); WritableBinaryStreamRef(const WritableBinaryStreamRef &Other) = default; diff --git a/lib/DebugInfo/MSF/MappedBlockStream.cpp b/lib/DebugInfo/MSF/MappedBlockStream.cpp index f28f944131b..dec28eb3069 100644 --- a/lib/DebugInfo/MSF/MappedBlockStream.cpp +++ b/lib/DebugInfo/MSF/MappedBlockStream.cpp @@ -89,7 +89,7 @@ MappedBlockStream::createFpmStream(const MSFLayout &Layout, Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, ArrayRef &Buffer) { // Make sure we aren't trying to read beyond the end of the stream. - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; if (tryReadContiguously(Offset, Size, Buffer)) @@ -167,7 +167,7 @@ Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size, Error MappedBlockStream::readLongestContiguousChunk(uint32_t Offset, ArrayRef &Buffer) { // Make sure we aren't trying to read beyond the end of the stream. - if (auto EC = checkOffset(Offset, 1)) + if (auto EC = checkOffsetForRead(Offset, 1)) return EC; uint32_t First = Offset / BlockSize; @@ -243,7 +243,7 @@ Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t OffsetInBlock = Offset % BlockSize; // Make sure we aren't trying to read beyond the end of the stream. - if (auto EC = checkOffset(Offset, Buffer.size())) + if (auto EC = checkOffsetForRead(Offset, Buffer.size())) return EC; uint32_t BytesLeft = Buffer.size(); @@ -388,7 +388,7 @@ uint32_t WritableMappedBlockStream::getLength() { Error WritableMappedBlockStream::writeBytes(uint32_t Offset, ArrayRef Buffer) { // Make sure we aren't trying to write beyond the end of the stream. - if (auto EC = checkOffset(Offset, Buffer.size())) + if (auto EC = checkOffsetForWrite(Offset, Buffer.size())) return EC; uint32_t BlockNum = Offset / getBlockSize(); diff --git a/lib/Support/BinaryStreamRef.cpp b/lib/Support/BinaryStreamRef.cpp index 70ee156f19f..60a03fe9930 100644 --- a/lib/Support/BinaryStreamRef.cpp +++ b/lib/Support/BinaryStreamRef.cpp @@ -66,9 +66,9 @@ private: } BinaryStreamRef::BinaryStreamRef(BinaryStream &Stream) - : BinaryStreamRef(Stream, 0, Stream.getLength()) {} + : BinaryStreamRefBase(Stream) {} BinaryStreamRef::BinaryStreamRef(BinaryStream &Stream, uint32_t Offset, - uint32_t Length) + Optional Length) : BinaryStreamRefBase(Stream, Offset, Length) {} BinaryStreamRef::BinaryStreamRef(ArrayRef Data, endianness Endian) : BinaryStreamRefBase(std::make_shared(Data, Endian), 0, @@ -79,14 +79,14 @@ BinaryStreamRef::BinaryStreamRef(StringRef Data, endianness Endian) Error BinaryStreamRef::readBytes(uint32_t Offset, uint32_t Size, ArrayRef &Buffer) const { - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; return BorrowedImpl->readBytes(ViewOffset + Offset, Size, Buffer); } Error BinaryStreamRef::readLongestContiguousChunk( uint32_t Offset, ArrayRef &Buffer) const { - if (auto EC = checkOffset(Offset, 1)) + if (auto EC = checkOffsetForRead(Offset, 1)) return EC; if (auto EC = @@ -95,18 +95,18 @@ Error BinaryStreamRef::readLongestContiguousChunk( // This StreamRef might refer to a smaller window over a larger stream. In // that case we will have read out more bytes than we should return, because // we should not read past the end of the current view. - uint32_t MaxLength = Length - Offset; + uint32_t MaxLength = getLength() - Offset; if (Buffer.size() > MaxLength) Buffer = Buffer.slice(0, MaxLength); return Error::success(); } WritableBinaryStreamRef::WritableBinaryStreamRef(WritableBinaryStream &Stream) - : WritableBinaryStreamRef(Stream, 0, Stream.getLength()) {} + : BinaryStreamRefBase(Stream) {} WritableBinaryStreamRef::WritableBinaryStreamRef(WritableBinaryStream &Stream, uint32_t Offset, - uint32_t Length) + Optional Length) : BinaryStreamRefBase(Stream, Offset, Length) {} WritableBinaryStreamRef::WritableBinaryStreamRef(MutableArrayRef Data, @@ -117,7 +117,7 @@ WritableBinaryStreamRef::WritableBinaryStreamRef(MutableArrayRef Data, Error WritableBinaryStreamRef::writeBytes(uint32_t Offset, ArrayRef Data) const { - if (auto EC = checkOffset(Offset, Data.size())) + if (auto EC = checkOffsetForWrite(Offset, Data.size())) return EC; return BorrowedImpl->writeBytes(ViewOffset + Offset, Data); diff --git a/lib/Support/BinaryStreamWriter.cpp b/lib/Support/BinaryStreamWriter.cpp index c4276518b19..bfad1280b92 100644 --- a/lib/Support/BinaryStreamWriter.cpp +++ b/lib/Support/BinaryStreamWriter.cpp @@ -42,7 +42,8 @@ Error BinaryStreamWriter::writeCString(StringRef Str) { } Error BinaryStreamWriter::writeFixedString(StringRef Str) { - return writeBytes(ArrayRef(Str.bytes_begin(), Str.bytes_end())); + + return writeBytes(arrayRefFromStringRef(Str)); } Error BinaryStreamWriter::writeStreamRef(BinaryStreamRef Ref) { diff --git a/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp b/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp index 3a3937e3405..ee52a091567 100644 --- a/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp +++ b/unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp @@ -42,7 +42,7 @@ public: Error readBytes(uint32_t Offset, uint32_t Size, ArrayRef &Buffer) override { - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; Buffer = Data.slice(Offset, Size); return Error::success(); @@ -50,7 +50,7 @@ public: Error readLongestContiguousChunk(uint32_t Offset, ArrayRef &Buffer) override { - if (auto EC = checkOffset(Offset, 1)) + if (auto EC = checkOffsetForRead(Offset, 1)) return EC; Buffer = Data.drop_front(Offset); return Error::success(); @@ -59,7 +59,7 @@ public: uint32_t getLength() override { return Data.size(); } Error writeBytes(uint32_t Offset, ArrayRef SrcData) override { - if (auto EC = checkOffset(Offset, SrcData.size())) + if (auto EC = checkOffsetForWrite(Offset, SrcData.size())) return EC; ::memcpy(&Data[Offset], SrcData.data(), SrcData.size()); return Error::success(); diff --git a/unittests/Support/BinaryStreamTest.cpp b/unittests/Support/BinaryStreamTest.cpp index cbad0f390c8..7aa033c76e2 100644 --- a/unittests/Support/BinaryStreamTest.cpp +++ b/unittests/Support/BinaryStreamTest.cpp @@ -36,7 +36,7 @@ public: Error readBytes(uint32_t Offset, uint32_t Size, ArrayRef &Buffer) override { - if (auto EC = checkOffset(Offset, Size)) + if (auto EC = checkOffsetForRead(Offset, Size)) return EC; uint32_t S = startIndex(Offset); auto Ref = Data.drop_front(S); @@ -55,7 +55,7 @@ public: Error readLongestContiguousChunk(uint32_t Offset, ArrayRef &Buffer) override { - if (auto EC = checkOffset(Offset, 1)) + if (auto EC = checkOffsetForRead(Offset, 1)) return EC; uint32_t S = startIndex(Offset); Buffer = Data.drop_front(S); @@ -65,7 +65,7 @@ public: uint32_t getLength() override { return Data.size(); } Error writeBytes(uint32_t Offset, ArrayRef SrcData) override { - if (auto EC = checkOffset(Offset, SrcData.size())) + if (auto EC = checkOffsetForWrite(Offset, SrcData.size())) return EC; if (SrcData.empty()) return Error::success(); @@ -267,6 +267,56 @@ TEST_F(BinaryStreamTest, StreamRefBounds) { } } +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(0, 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(2, 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(1, Dropped.getLength()); + + EXPECT_THAT_ERROR(Writer.writeCString(Strings[1]), Succeeded()); + EXPECT_EQ(4, ConstRef.getLength()); + EXPECT_EQ(3, 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(6, ConstRef.getLength()); + EXPECT_EQ(5, 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(8, ConstRef.getLength()); + EXPECT_EQ(4, Dropped.getLength()); +} + TEST_F(BinaryStreamTest, DropOperations) { std::vector InputData = {1, 2, 3, 4, 5, 4, 3, 2, 1}; auto RefData = makeArrayRef(InputData); @@ -345,6 +395,25 @@ TEST_F(BinaryStreamTest, MutableBinaryByteStreamBounds) { } } +TEST_F(BinaryStreamTest, AppendingStream) { + AppendingBinaryByteStream Stream(llvm::support::little); + EXPECT_EQ(0, 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}; @@ -693,6 +762,23 @@ TEST_F(BinaryStreamTest, StringWriterStrings) { 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 { -- 2.50.1