[pdb] Refactor library to more clearly separate reading/writing

Reviewed By: amccarth, ruiu
Differential Revision: https://reviews.llvm.org/D22693

llvm-svn: 277019
diff --git a/llvm/lib/DebugInfo/Msf/MappedBlockStream.cpp b/llvm/lib/DebugInfo/Msf/MappedBlockStream.cpp
index 06ca694..7d5be88 100644
--- a/llvm/lib/DebugInfo/Msf/MappedBlockStream.cpp
+++ b/llvm/lib/DebugInfo/Msf/MappedBlockStream.cpp
@@ -10,6 +10,7 @@
 #include "llvm/DebugInfo/Msf/MappedBlockStream.h"
 
 #include "llvm/DebugInfo/Msf/IMsfFile.h"
+#include "llvm/DebugInfo/Msf/MsfCommon.h"
 #include "llvm/DebugInfo/Msf/MsfError.h"
 #include "llvm/DebugInfo/Msf/MsfStreamLayout.h"
 
@@ -17,13 +18,11 @@
 using namespace llvm::msf;
 
 namespace {
-// This exists so that we can use make_unique (which requires a public default
-// constructor, while still keeping the constructor of MappedBlockStream
-// protected, forcing users to go through the `create` interface.
-class MappedBlockStreamImpl : public MappedBlockStream {
+template <typename Base> class MappedBlockStreamImpl : public Base {
 public:
-  MappedBlockStreamImpl(const MsfStreamLayout &Layout, const IMsfFile &File)
-      : MappedBlockStream(Layout, File) {}
+  template <typename... Args>
+  MappedBlockStreamImpl(Args &&... Params)
+      : Base(std::forward<Args>(Params)...) {}
 };
 }
 
@@ -33,16 +32,46 @@
                         std::min(I1.second, I2.second));
 }
 
-MappedBlockStream::MappedBlockStream(const MsfStreamLayout &Layout,
-                                     const IMsfFile &File)
-    : Msf(File), Layout(Layout) {}
+MappedBlockStream::MappedBlockStream(uint32_t BlockSize, uint32_t NumBlocks,
+                                     const MsfStreamLayout &Layout,
+                                     const ReadableStream &MsfData)
+    : BlockSize(BlockSize), NumBlocks(NumBlocks), StreamLayout(Layout),
+      MsfData(MsfData) {}
+
+std::unique_ptr<MappedBlockStream>
+MappedBlockStream::createStream(uint32_t BlockSize, uint32_t NumBlocks,
+                                const MsfStreamLayout &Layout,
+                                const ReadableStream &MsfData) {
+  return llvm::make_unique<MappedBlockStreamImpl<MappedBlockStream>>(
+      BlockSize, NumBlocks, Layout, MsfData);
+}
+
+std::unique_ptr<MappedBlockStream>
+MappedBlockStream::createIndexedStream(const MsfLayout &Layout,
+                                       const ReadableStream &MsfData,
+                                       uint32_t StreamIndex) {
+  MsfStreamLayout SL;
+  SL.Blocks = Layout.StreamMap[StreamIndex];
+  SL.Length = Layout.StreamSizes[StreamIndex];
+  return llvm::make_unique<MappedBlockStreamImpl<MappedBlockStream>>(
+      Layout.SB->BlockSize, Layout.SB->NumBlocks, SL, MsfData);
+}
+
+std::unique_ptr<MappedBlockStream>
+MappedBlockStream::createDirectoryStream(const MsfLayout &Layout,
+                                         const ReadableStream &MsfData) {
+  MsfStreamLayout SL;
+  SL.Blocks = Layout.DirectoryBlocks;
+  SL.Length = Layout.SB->NumDirectoryBytes;
+  return createStream(Layout.SB->BlockSize, Layout.SB->NumBlocks, SL, MsfData);
+}
 
 Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size,
                                    ArrayRef<uint8_t> &Buffer) const {
   // Make sure we aren't trying to read beyond the end of the stream.
-  if (Size > Layout.Length)
+  if (Size > StreamLayout.Length)
     return make_error<MsfError>(msf_error_code::insufficient_buffer);
-  if (Offset > Layout.Length - Size)
+  if (Offset > StreamLayout.Length - Size)
     return make_error<MsfError>(msf_error_code::insufficient_buffer);
 
   if (tryReadContiguously(Offset, Size, Buffer))
@@ -120,33 +149,33 @@
 Error MappedBlockStream::readLongestContiguousChunk(
     uint32_t Offset, ArrayRef<uint8_t> &Buffer) const {
   // Make sure we aren't trying to read beyond the end of the stream.
-  if (Offset >= Layout.Length)
+  if (Offset >= StreamLayout.Length)
     return make_error<MsfError>(msf_error_code::insufficient_buffer);
-  uint32_t First = Offset / Msf.getBlockSize();
+  uint32_t First = Offset / BlockSize;
   uint32_t Last = First;
 
-  while (Last < Msf.getBlockCount() - 1) {
-    if (Layout.Blocks[Last] != Layout.Blocks[Last + 1] - 1)
+  while (Last < NumBlocks - 1) {
+    if (StreamLayout.Blocks[Last] != StreamLayout.Blocks[Last + 1] - 1)
       break;
     ++Last;
   }
 
-  uint32_t OffsetInFirstBlock = Offset % Msf.getBlockSize();
-  uint32_t BytesFromFirstBlock = Msf.getBlockSize() - OffsetInFirstBlock;
+  uint32_t OffsetInFirstBlock = Offset % BlockSize;
+  uint32_t BytesFromFirstBlock = BlockSize - OffsetInFirstBlock;
   uint32_t BlockSpan = Last - First + 1;
-  uint32_t ByteSpan =
-      BytesFromFirstBlock + (BlockSpan - 1) * Msf.getBlockSize();
-  auto Result = Msf.getBlockData(Layout.Blocks[First], Msf.getBlockSize());
-  if (!Result)
-    return Result.takeError();
-  Buffer = Result->drop_front(OffsetInFirstBlock);
-  Buffer = ArrayRef<uint8_t>(Buffer.data(), ByteSpan);
+  uint32_t ByteSpan = BytesFromFirstBlock + (BlockSpan - 1) * BlockSize;
+
+  ArrayRef<uint8_t> BlockData;
+  uint32_t MsfOffset = blockToOffset(StreamLayout.Blocks[First], BlockSize);
+  if (auto EC = MsfData.readBytes(MsfOffset, BlockSize, BlockData))
+    return EC;
+
+  BlockData = BlockData.drop_front(OffsetInFirstBlock);
+  Buffer = ArrayRef<uint8_t>(BlockData.data(), ByteSpan);
   return Error::success();
 }
 
-uint32_t MappedBlockStream::getLength() const { return Layout.Length; }
-
-Error MappedBlockStream::commit() const { return Error::success(); }
+uint32_t MappedBlockStream::getLength() const { return StreamLayout.Length; }
 
 bool MappedBlockStream::tryReadContiguously(uint32_t Offset, uint32_t Size,
                                             ArrayRef<uint8_t> &Buffer) const {
@@ -155,57 +184,60 @@
   // all subsequent blocks are contiguous.  For example, a 10k read with a 4k
   // block size can be filled with a reference if, from the starting offset,
   // 3 blocks in a row are contiguous.
-  uint32_t BlockNum = Offset / Msf.getBlockSize();
-  uint32_t OffsetInBlock = Offset % Msf.getBlockSize();
-  uint32_t BytesFromFirstBlock =
-      std::min(Size, Msf.getBlockSize() - OffsetInBlock);
+  uint32_t BlockNum = Offset / BlockSize;
+  uint32_t OffsetInBlock = Offset % BlockSize;
+  uint32_t BytesFromFirstBlock = std::min(Size, BlockSize - OffsetInBlock);
   uint32_t NumAdditionalBlocks =
-      llvm::alignTo(Size - BytesFromFirstBlock, Msf.getBlockSize()) /
-      Msf.getBlockSize();
+      llvm::alignTo(Size - BytesFromFirstBlock, BlockSize) / BlockSize;
 
   uint32_t RequiredContiguousBlocks = NumAdditionalBlocks + 1;
-  uint32_t E = Layout.Blocks[BlockNum];
+  uint32_t E = StreamLayout.Blocks[BlockNum];
   for (uint32_t I = 0; I < RequiredContiguousBlocks; ++I, ++E) {
-    if (Layout.Blocks[I + BlockNum] != E)
+    if (StreamLayout.Blocks[I + BlockNum] != E)
       return false;
   }
 
-  uint32_t FirstBlockAddr = Layout.Blocks[BlockNum];
-  auto Result = Msf.getBlockData(FirstBlockAddr, Msf.getBlockSize());
-  if (!Result) {
-    consumeError(Result.takeError());
+  // Read out the entire block where the requested offset starts.  Then drop
+  // bytes from the beginning so that the actual starting byte lines up with
+  // the requested starting byte.  Then, since we know this is a contiguous
+  // cross-block span, explicitly resize the ArrayRef to cover the entire
+  // request length.
+  ArrayRef<uint8_t> BlockData;
+  uint32_t FirstBlockAddr = StreamLayout.Blocks[BlockNum];
+  uint32_t MsfOffset = blockToOffset(FirstBlockAddr, BlockSize);
+  if (auto EC = MsfData.readBytes(MsfOffset, BlockSize, BlockData)) {
+    consumeError(std::move(EC));
     return false;
   }
-  auto Data = Result->drop_front(OffsetInBlock);
-  Buffer = ArrayRef<uint8_t>(Data.data(), Size);
+  BlockData = BlockData.drop_front(OffsetInBlock);
+  Buffer = ArrayRef<uint8_t>(BlockData.data(), Size);
   return true;
 }
 
 Error MappedBlockStream::readBytes(uint32_t Offset,
                                    MutableArrayRef<uint8_t> Buffer) const {
-  uint32_t BlockNum = Offset / Msf.getBlockSize();
-  uint32_t OffsetInBlock = Offset % Msf.getBlockSize();
+  uint32_t BlockNum = Offset / BlockSize;
+  uint32_t OffsetInBlock = Offset % BlockSize;
 
   // Make sure we aren't trying to read beyond the end of the stream.
-  if (Buffer.size() > Layout.Length)
+  if (Buffer.size() > StreamLayout.Length)
     return make_error<MsfError>(msf_error_code::insufficient_buffer);
-  if (Offset > Layout.Length - Buffer.size())
+  if (Offset > StreamLayout.Length - Buffer.size())
     return make_error<MsfError>(msf_error_code::insufficient_buffer);
 
   uint32_t BytesLeft = Buffer.size();
   uint32_t BytesWritten = 0;
   uint8_t *WriteBuffer = Buffer.data();
   while (BytesLeft > 0) {
-    uint32_t StreamBlockAddr = Layout.Blocks[BlockNum];
+    uint32_t StreamBlockAddr = StreamLayout.Blocks[BlockNum];
 
-    auto Result = Msf.getBlockData(StreamBlockAddr, Msf.getBlockSize());
-    if (!Result)
-      return Result.takeError();
+    ArrayRef<uint8_t> BlockData;
+    uint32_t Offset = blockToOffset(StreamBlockAddr, BlockSize);
+    if (auto EC = MsfData.readBytes(Offset, BlockSize, BlockData))
+      return EC;
 
-    auto Data = *Result;
-    const uint8_t *ChunkStart = Data.data() + OffsetInBlock;
-    uint32_t BytesInChunk =
-        std::min(BytesLeft, Msf.getBlockSize() - OffsetInBlock);
+    const uint8_t *ChunkStart = BlockData.data() + OffsetInBlock;
+    uint32_t BytesInChunk = std::min(BytesLeft, BlockSize - OffsetInBlock);
     ::memcpy(WriteBuffer + BytesWritten, ChunkStart, BytesInChunk);
 
     BytesWritten += BytesInChunk;
@@ -217,36 +249,14 @@
   return Error::success();
 }
 
-Error MappedBlockStream::writeBytes(uint32_t Offset,
-                                    ArrayRef<uint8_t> Buffer) const {
-  // Make sure we aren't trying to write beyond the end of the stream.
-  if (Buffer.size() > Layout.Length)
-    return make_error<MsfError>(msf_error_code::insufficient_buffer);
+uint32_t MappedBlockStream::getNumBytesCopied() const {
+  return static_cast<uint32_t>(Pool.getBytesAllocated());
+}
 
-  if (Offset > Layout.Length - Buffer.size())
-    return make_error<MsfError>(msf_error_code::insufficient_buffer);
+void MappedBlockStream::invalidateCache() { CacheMap.shrink_and_clear(); }
 
-  uint32_t BlockNum = Offset / Msf.getBlockSize();
-  uint32_t OffsetInBlock = Offset % Msf.getBlockSize();
-
-  uint32_t BytesLeft = Buffer.size();
-  uint32_t BytesWritten = 0;
-  while (BytesLeft > 0) {
-    uint32_t StreamBlockAddr = Layout.Blocks[BlockNum];
-    uint32_t BytesToWriteInChunk =
-        std::min(BytesLeft, Msf.getBlockSize() - OffsetInBlock);
-
-    const uint8_t *Chunk = Buffer.data() + BytesWritten;
-    ArrayRef<uint8_t> ChunkData(Chunk, BytesToWriteInChunk);
-    if (auto EC = Msf.setBlockData(StreamBlockAddr, OffsetInBlock, ChunkData))
-      return EC;
-
-    BytesLeft -= BytesToWriteInChunk;
-    BytesWritten += BytesToWriteInChunk;
-    ++BlockNum;
-    OffsetInBlock = 0;
-  }
-
+void MappedBlockStream::fixCacheAfterWrite(uint32_t Offset,
+                                           ArrayRef<uint8_t> Data) const {
   // If this write overlapped a read which previously came from the pool,
   // someone may still be holding a pointer to that alloc which is now invalid.
   // Compute the overlapping range and update the cache entry, so any
@@ -254,7 +264,7 @@
   for (const auto &MapEntry : CacheMap) {
     // If the end of the written extent precedes the beginning of the cached
     // extent, ignore this map entry.
-    if (Offset + BytesWritten < MapEntry.first)
+    if (Offset + Data.size() < MapEntry.first)
       continue;
     for (const auto &Alloc : MapEntry.second) {
       // If the end of the cached extent precedes the beginning of the written
@@ -263,7 +273,7 @@
         continue;
 
       // If we get here, they are guaranteed to overlap.
-      Interval WriteInterval = std::make_pair(Offset, Offset + BytesWritten);
+      Interval WriteInterval = std::make_pair(Offset, Offset + Data.size());
       Interval CachedInterval =
           std::make_pair(MapEntry.first, MapEntry.first + Alloc.size());
       // If they overlap, we need to write the new data into the overlapping
@@ -276,35 +286,95 @@
           AbsoluteDifference(WriteInterval.first, Intersection.first);
       uint32_t DestOffset =
           AbsoluteDifference(CachedInterval.first, Intersection.first);
-      ::memcpy(Alloc.data() + DestOffset, Buffer.data() + SrcOffset, Length);
+      ::memcpy(Alloc.data() + DestOffset, Data.data() + SrcOffset, Length);
     }
   }
+}
+
+WritableMappedBlockStream::WritableMappedBlockStream(
+    uint32_t BlockSize, uint32_t NumBlocks, const MsfStreamLayout &Layout,
+    const WritableStream &MsfData)
+    : ReadInterface(BlockSize, NumBlocks, Layout, MsfData),
+      WriteInterface(MsfData) {}
+
+std::unique_ptr<WritableMappedBlockStream>
+WritableMappedBlockStream::createStream(uint32_t BlockSize, uint32_t NumBlocks,
+                                        const MsfStreamLayout &Layout,
+                                        const WritableStream &MsfData) {
+  return llvm::make_unique<MappedBlockStreamImpl<WritableMappedBlockStream>>(
+      BlockSize, NumBlocks, Layout, MsfData);
+}
+
+std::unique_ptr<WritableMappedBlockStream>
+WritableMappedBlockStream::createIndexedStream(const MsfLayout &Layout,
+                                               const WritableStream &MsfData,
+                                               uint32_t StreamIndex) {
+  MsfStreamLayout SL;
+  SL.Blocks = Layout.StreamMap[StreamIndex];
+  SL.Length = Layout.StreamSizes[StreamIndex];
+  return createStream(Layout.SB->BlockSize, Layout.SB->NumBlocks, SL, MsfData);
+}
+
+std::unique_ptr<WritableMappedBlockStream>
+WritableMappedBlockStream::createDirectoryStream(
+    const MsfLayout &Layout, const WritableStream &MsfData) {
+  MsfStreamLayout SL;
+  SL.Blocks = Layout.DirectoryBlocks;
+  SL.Length = Layout.SB->NumDirectoryBytes;
+  return createStream(Layout.SB->BlockSize, Layout.SB->NumBlocks, SL, MsfData);
+}
+
+Error WritableMappedBlockStream::readBytes(uint32_t Offset, uint32_t Size,
+                                           ArrayRef<uint8_t> &Buffer) const {
+  return ReadInterface.readBytes(Offset, Size, Buffer);
+}
+
+Error WritableMappedBlockStream::readLongestContiguousChunk(
+    uint32_t Offset, ArrayRef<uint8_t> &Buffer) const {
+  return ReadInterface.readLongestContiguousChunk(Offset, Buffer);
+}
+
+uint32_t WritableMappedBlockStream::getLength() const {
+  return ReadInterface.getLength();
+}
+
+Error WritableMappedBlockStream::writeBytes(uint32_t Offset,
+                                            ArrayRef<uint8_t> Buffer) const {
+  // Make sure we aren't trying to write beyond the end of the stream.
+  if (Buffer.size() > getStreamLength())
+    return make_error<MsfError>(msf_error_code::insufficient_buffer);
+
+  if (Offset > getStreamLayout().Length - Buffer.size())
+    return make_error<MsfError>(msf_error_code::insufficient_buffer);
+
+  uint32_t BlockNum = Offset / getBlockSize();
+  uint32_t OffsetInBlock = Offset % getBlockSize();
+
+  uint32_t BytesLeft = Buffer.size();
+  uint32_t BytesWritten = 0;
+  while (BytesLeft > 0) {
+    uint32_t StreamBlockAddr = getStreamLayout().Blocks[BlockNum];
+    uint32_t BytesToWriteInChunk =
+        std::min(BytesLeft, getBlockSize() - OffsetInBlock);
+
+    const uint8_t *Chunk = Buffer.data() + BytesWritten;
+    ArrayRef<uint8_t> ChunkData(Chunk, BytesToWriteInChunk);
+    uint32_t MsfOffset = blockToOffset(StreamBlockAddr, getBlockSize());
+    MsfOffset += OffsetInBlock;
+    if (auto EC = WriteInterface.writeBytes(MsfOffset, ChunkData))
+      return EC;
+
+    BytesLeft -= BytesToWriteInChunk;
+    BytesWritten += BytesToWriteInChunk;
+    ++BlockNum;
+    OffsetInBlock = 0;
+  }
+
+  ReadInterface.fixCacheAfterWrite(Offset, Buffer);
 
   return Error::success();
 }
 
-uint32_t MappedBlockStream::getNumBytesCopied() const {
-  return static_cast<uint32_t>(Pool.getBytesAllocated());
-}
-
-Expected<std::unique_ptr<MappedBlockStream>>
-MappedBlockStream::createIndexedStream(uint32_t StreamIdx,
-                                       const IMsfFile &File) {
-  if (StreamIdx >= File.getNumStreams())
-    return make_error<MsfError>(msf_error_code::no_stream);
-  MsfStreamLayout L;
-  L.Blocks = File.getStreamBlockList(StreamIdx);
-  L.Length = File.getStreamByteSize(StreamIdx);
-  return llvm::make_unique<MappedBlockStreamImpl>(L, File);
-}
-
-Expected<std::unique_ptr<MappedBlockStream>>
-MappedBlockStream::createDirectoryStream(uint32_t Length,
-                                         ArrayRef<support::ulittle32_t> Blocks,
-                                         const IMsfFile &File) {
-  MsfStreamLayout L;
-  L.Blocks = Blocks;
-  L.Length = Length;
-
-  return llvm::make_unique<MappedBlockStreamImpl>(L, File);
+Error WritableMappedBlockStream::commit() const {
+  return WriteInterface.commit();
 }