]> granicus.if.org Git - llvm/commitdiff
[BinaryStream] Support growable streams.
authorZachary Turner <zturner@google.com>
Mon, 27 Nov 2017 18:48:37 +0000 (18:48 +0000)
committerZachary Turner <zturner@google.com>
Mon, 27 Nov 2017 18:48:37 +0000 (18:48 +0000)
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
include/llvm/Support/BinaryByteStream.h
include/llvm/Support/BinaryItemStream.h
include/llvm/Support/BinaryStream.h
include/llvm/Support/BinaryStreamRef.h
lib/DebugInfo/MSF/MappedBlockStream.cpp
lib/Support/BinaryStreamRef.cpp
lib/Support/BinaryStreamWriter.cpp
unittests/DebugInfo/MSF/MappedBlockStreamTest.cpp
unittests/Support/BinaryStreamTest.cpp

index 37df1659b97fabed8cdaeffa2a2ea9d13a990d8a..a0e0d7d64f27436e082ffd5edbe01feb407fe21b 100644 (file)
@@ -47,6 +47,11 @@ inline StringRef toStringRef(ArrayRef<uint8_t> Input) {
   return StringRef(reinterpret_cast<const char *>(Input.begin()), Input.size());
 }
 
+/// Construct a string ref from an array ref of unsigned chars.
+inline ArrayRef<uint8_t> arrayRefFromStringRef(StringRef Input) {
+  return {Input.bytes_begin(), Input.bytes_end()};
+}
+
 /// Interpret the given character \p C as a hexadecimal digit and return its
 /// value.
 ///
index 694be28e07e16c33d021c4db16f411dbd5ff4706..a87a9bea0e3c6b1c82297d8db3cb2391c47e526f 100644 (file)
@@ -41,7 +41,7 @@ public:
 
   Error readBytes(uint32_t Offset, uint32_t Size,
                   ArrayRef<uint8_t> &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<uint8_t> &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<uint8_t *>(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<uint8_t> 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<uint8_t> &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<uint8_t> &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<uint8_t> 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<BinaryStreamError>(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<uint8_t> data() { return Data; }
+};
+
 /// \brief An implementation of WritableBinaryStream backed by an llvm
 /// FileOutputBuffer.
 class FileBufferByteStream : public WritableBinaryStream {
index fe7e6caeaafb765360d8b41ac083bf3db7767849..278723ddf8dafb0592ed5d696d7b33e4d9525747 100644 (file)
@@ -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<BinaryStreamError>(stream_error_code::stream_too_short);
index a227117e063e99157cf1f784b600b1baec329f56..d69a03eccfdbc434dbe296cea63c854317548724 100644 (file)
@@ -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"
 
 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<BinaryStreamError>(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<BinaryStreamError>(stream_error_code::invalid_offset);
+    return Error::success();
+  }
 };
 
 } // end namespace llvm
index 5679bd459abf292ba5da3fc22dbe344e85d3f46a..5cf355be6fe9caa5269fa11f2fd305d4bc3dff1d 100644 (file)
@@ -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 RefType, class StreamType> 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<StreamType> SharedImpl, uint32_t Offset,
-                      uint32_t Length)
+                      Optional<uint32_t> Length)
       : SharedImpl(SharedImpl), BorrowedImpl(SharedImpl.get()),
         ViewOffset(Offset), Length(Length) {}
   BinaryStreamRefBase(StreamType &BorrowedImpl, uint32_t Offset,
-                      uint32_t Length)
+                      Optional<uint32_t> 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<const RefType &>(*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<const RefType &>(*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<BinaryStreamError>(stream_error_code::invalid_offset);
     if (getLength() < DataSize + Offset)
@@ -115,7 +144,7 @@ protected:
   std::shared_ptr<StreamType> SharedImpl;
   StreamType *BorrowedImpl = nullptr;
   uint32_t ViewOffset = 0;
-  uint32_t Length = 0;
+  Optional<uint32_t> Length;
 };
 
 /// \brief BinaryStreamRef is to BinaryStream what ArrayRef is to an Array.  It
@@ -130,13 +159,14 @@ class BinaryStreamRef
   friend BinaryStreamRefBase<BinaryStreamRef, BinaryStream>;
   friend class WritableBinaryStreamRef;
   BinaryStreamRef(std::shared_ptr<BinaryStream> Impl, uint32_t ViewOffset,
-                  uint32_t Length)
+                  Optional<uint32_t> 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<uint32_t> Length);
   explicit BinaryStreamRef(ArrayRef<uint8_t> Data,
                            llvm::support::endianness Endian);
   explicit BinaryStreamRef(StringRef Data, llvm::support::endianness Endian);
@@ -195,14 +225,23 @@ class WritableBinaryStreamRef
                                  WritableBinaryStream> {
   friend BinaryStreamRefBase<WritableBinaryStreamRef, WritableBinaryStream>;
   WritableBinaryStreamRef(std::shared_ptr<WritableBinaryStream> Impl,
-                          uint32_t ViewOffset, uint32_t Length)
+                          uint32_t ViewOffset, Optional<uint32_t> 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<BinaryStreamError>(stream_error_code::invalid_offset);
+    return Error::success();
+  }
+
 public:
   WritableBinaryStreamRef() = default;
   WritableBinaryStreamRef(WritableBinaryStream &Stream);
   WritableBinaryStreamRef(WritableBinaryStream &Stream, uint32_t Offset,
-                          uint32_t Length);
+                          Optional<uint32_t> Length);
   explicit WritableBinaryStreamRef(MutableArrayRef<uint8_t> Data,
                                    llvm::support::endianness Endian);
   WritableBinaryStreamRef(const WritableBinaryStreamRef &Other) = default;
index f28f944131b8821b71bc3a8841e341af17a429d0..dec28eb3069729e8459eecad6214f00f17586db4 100644 (file)
@@ -89,7 +89,7 @@ MappedBlockStream::createFpmStream(const MSFLayout &Layout,
 Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size,
                                    ArrayRef<uint8_t> &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<uint8_t> &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<uint8_t> 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();
index 70ee156f19f8e18d2be11fa2284c45acc852aed8..60a03fe9930f253077c839a623cb70c238ad2023 100644 (file)
@@ -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<uint32_t> Length)
     : BinaryStreamRefBase(Stream, Offset, Length) {}
 BinaryStreamRef::BinaryStreamRef(ArrayRef<uint8_t> Data, endianness Endian)
     : BinaryStreamRefBase(std::make_shared<ArrayRefImpl>(Data, Endian), 0,
@@ -79,14 +79,14 @@ BinaryStreamRef::BinaryStreamRef(StringRef Data, endianness Endian)
 
 Error BinaryStreamRef::readBytes(uint32_t Offset, uint32_t Size,
                                  ArrayRef<uint8_t> &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<uint8_t> &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<uint32_t> Length)
     : BinaryStreamRefBase(Stream, Offset, Length) {}
 
 WritableBinaryStreamRef::WritableBinaryStreamRef(MutableArrayRef<uint8_t> Data,
@@ -117,7 +117,7 @@ WritableBinaryStreamRef::WritableBinaryStreamRef(MutableArrayRef<uint8_t> Data,
 
 Error WritableBinaryStreamRef::writeBytes(uint32_t Offset,
                                           ArrayRef<uint8_t> Data) const {
-  if (auto EC = checkOffset(Offset, Data.size()))
+  if (auto EC = checkOffsetForWrite(Offset, Data.size()))
     return EC;
 
   return BorrowedImpl->writeBytes(ViewOffset + Offset, Data);
index c4276518b1919dadfefada7fbe68e3747543aa3d..bfad1280b9297d229e3b23d1d4da4743a424ae0b 100644 (file)
@@ -42,7 +42,8 @@ Error BinaryStreamWriter::writeCString(StringRef Str) {
 }
 
 Error BinaryStreamWriter::writeFixedString(StringRef Str) {
-  return writeBytes(ArrayRef<uint8_t>(Str.bytes_begin(), Str.bytes_end()));
+
+  return writeBytes(arrayRefFromStringRef(Str));
 }
 
 Error BinaryStreamWriter::writeStreamRef(BinaryStreamRef Ref) {
index 3a3937e3405ce3907f9ec5fa5fd989cd453b4906..ee52a0915675d1d1f327975b795dae9031944deb 100644 (file)
@@ -42,7 +42,7 @@ public:
 
   Error readBytes(uint32_t Offset, uint32_t Size,
                   ArrayRef<uint8_t> &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<uint8_t> &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<uint8_t> 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();
index cbad0f390c848d8af5cf26fcaf3a99e621b2a044..7aa033c76e27de34ba393a38c229b395fdc5839c 100644 (file)
@@ -36,7 +36,7 @@ public:
 
   Error readBytes(uint32_t Offset, uint32_t Size,
                   ArrayRef<uint8_t> &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<uint8_t> &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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t>(InputData), Stream.data());
+}
+
 // Test that FixedStreamArray works correctly.
 TEST_F(BinaryStreamTest, FixedStreamArray) {
   std::vector<uint32_t> 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 {