diff --git a/include/llvm/DebugInfo/CodeView/StreamArray.h b/include/llvm/DebugInfo/CodeView/StreamArray.h index b6ecc75e439..0b9349aac75 100644 --- a/include/llvm/DebugInfo/CodeView/StreamArray.h +++ b/include/llvm/DebugInfo/CodeView/StreamArray.h @@ -112,10 +112,14 @@ public: VarStreamArrayIterator(const ArrayType &Array, const Extractor &E, bool *HadError = nullptr) : IterRef(Array.Stream), Array(&Array), HadError(HadError), Extract(E) { - auto EC = Extract(IterRef, ThisLen, ThisValue); - if (EC) { - consumeError(std::move(EC)); - markError(); + if (IterRef.getLength() == 0) + moveToEnd(); + else { + auto EC = Extract(IterRef, ThisLen, ThisValue); + if (EC) { + consumeError(std::move(EC)); + markError(); + } } } VarStreamArrayIterator() {} diff --git a/include/llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h b/include/llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h index ab4300858ce..2c7350f3c3e 100644 --- a/include/llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h +++ b/include/llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h @@ -24,7 +24,7 @@ class PDBFile; class DbiStreamBuilder { public: - DbiStreamBuilder(PDBFile &File); + DbiStreamBuilder(); DbiStreamBuilder(const DbiStreamBuilder &) = delete; DbiStreamBuilder &operator=(const DbiStreamBuilder &) = delete; @@ -37,10 +37,11 @@ public: void setFlags(uint16_t F); void setMachineType(PDB_Machine M); - Expected> build(); + uint32_t calculateSerializedLength() const; + + Expected> build(PDBFile &File); private: - PDBFile &File; Optional VerHeader; uint32_t Age; uint16_t BuildNumber; diff --git a/include/llvm/DebugInfo/PDB/Raw/InfoStream.h b/include/llvm/DebugInfo/PDB/Raw/InfoStream.h index 9cca4877709..1980bec7153 100644 --- a/include/llvm/DebugInfo/PDB/Raw/InfoStream.h +++ b/include/llvm/DebugInfo/PDB/Raw/InfoStream.h @@ -27,7 +27,7 @@ class PDBFile; class InfoStream { friend class InfoStreamBuilder; - struct Header { + struct HeaderInfo { support::ulittle32_t Version; support::ulittle32_t Signature; support::ulittle32_t Age; diff --git a/include/llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h b/include/llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h index e8ee572c1a0..872f300e7e4 100644 --- a/include/llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h +++ b/include/llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h @@ -14,18 +14,17 @@ #include "llvm/Support/Error.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" -#include "llvm/DebugInfo/PDB/Raw/NameMap.h" +#include "llvm/DebugInfo/PDB/Raw/NameMapBuilder.h" #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/RawConstants.h" namespace llvm { namespace pdb { -class NameMap; class PDBFile; class InfoStreamBuilder { public: - InfoStreamBuilder(IPDBFile &File); + InfoStreamBuilder(); InfoStreamBuilder(const InfoStreamBuilder &) = delete; InfoStreamBuilder &operator=(const InfoStreamBuilder &) = delete; @@ -34,15 +33,17 @@ public: void setAge(uint32_t A); void setGuid(PDB_UniqueId G); - Expected> build(); + uint32_t calculateSerializedLength() const; + + Expected> build(PDBFile &File); private: - IPDBFile &File; Optional Ver; Optional Sig; Optional Age; Optional Guid; - Optional NamedStreams; + + NameMapBuilder NamedStreams; }; } } diff --git a/include/llvm/DebugInfo/PDB/Raw/MsfBuilder.h b/include/llvm/DebugInfo/PDB/Raw/MsfBuilder.h index b2b4c7ab496..707d3d84072 100644 --- a/include/llvm/DebugInfo/PDB/Raw/MsfBuilder.h +++ b/include/llvm/DebugInfo/PDB/Raw/MsfBuilder.h @@ -63,6 +63,9 @@ public: /// Request the block map to be at a specific block address. This is useful /// when editing a PDB and you want the layout to be as stable as possible. Error setBlockMapAddr(uint32_t Addr); + Error setDirectoryBlocksHint(ArrayRef DirBlocks); + void setUnknown0(uint32_t Unk0); + void setUnknown1(uint32_t Unk1); /// Add a stream to the MSF file with the given size, occupying the given /// list of blocks. This is useful when reading a PDB file and you want a @@ -123,6 +126,8 @@ private: BumpPtrAllocator &Allocator; bool IsGrowable; + uint32_t Unknown0; + uint32_t Unknown1; uint32_t BlockSize; uint32_t MininumBlocks; uint32_t BlockMapAddr; diff --git a/include/llvm/DebugInfo/PDB/Raw/NameMapBuilder.h b/include/llvm/DebugInfo/PDB/Raw/NameMapBuilder.h new file mode 100644 index 00000000000..698e767d9e6 --- /dev/null +++ b/include/llvm/DebugInfo/PDB/Raw/NameMapBuilder.h @@ -0,0 +1,34 @@ +//===- NameMapBuilder.h - PDB Name Map Builder ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DEBUGINFO_PDB_RAW_PDBNAMEMAPBUILDER_H +#define LLVM_DEBUGINFO_PDB_RAW_PDBNAMEMAPBUILDER_H + +#include "llvm/Support/Error.h" + +#include +#include + +namespace llvm { +namespace pdb { +class NameMap; + +class NameMapBuilder { +public: + NameMapBuilder(); + + Expected> build(); + + uint32_t calculateSerializedLength() const; +}; + +} // end namespace pdb +} // end namespace llvm + +#endif // LLVM_DEBUGINFO_PDB_RAW_PDBNAMEMAPBUILDER_H diff --git a/include/llvm/DebugInfo/PDB/Raw/PDBFile.h b/include/llvm/DebugInfo/PDB/Raw/PDBFile.h index 71d7c6144c3..82551971dda 100644 --- a/include/llvm/DebugInfo/PDB/Raw/PDBFile.h +++ b/include/llvm/DebugInfo/PDB/Raw/PDBFile.h @@ -14,6 +14,7 @@ #include "llvm/DebugInfo/CodeView/StreamArray.h" #include "llvm/DebugInfo/CodeView/StreamInterface.h" #include "llvm/DebugInfo/PDB/Raw/IPDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/MsfCommon.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" @@ -37,38 +38,10 @@ class PublicsStream; class SymbolStream; class TpiStream; -static const char MsfMagic[] = {'M', 'i', 'c', 'r', 'o', 's', 'o', 'f', - 't', ' ', 'C', '/', 'C', '+', '+', ' ', - 'M', 'S', 'F', ' ', '7', '.', '0', '0', - '\r', '\n', '\x1a', 'D', 'S', '\0', '\0', '\0'}; - class PDBFile : public IPDBFile { friend PDBFileBuilder; public: - // The superblock is overlaid at the beginning of the file (offset 0). - // It starts with a magic header and is followed by information which - // describes the layout of the file system. - struct SuperBlock { - char MagicBytes[sizeof(MsfMagic)]; - // The file system is split into a variable number of fixed size elements. - // These elements are referred to as blocks. The size of a block may vary - // from system to system. - support::ulittle32_t BlockSize; - // This field's purpose is not yet known. - support::ulittle32_t Unknown0; - // This contains the number of blocks resident in the file system. In - // practice, NumBlocks * BlockSize is equivalent to the size of the PDB - // file. - support::ulittle32_t NumBlocks; - // This contains the number of bytes which make up the directory. - support::ulittle32_t NumDirectoryBytes; - // This field's purpose is not yet known. - support::ulittle32_t Unknown1; - // This contains the block # of the block map. - support::ulittle32_t BlockMapAddr; - }; - explicit PDBFile(std::unique_ptr PdbFileBuffer); ~PDBFile() override; @@ -103,14 +76,6 @@ public: Error parseFileHeaders(); Error parseStreamData(); - static uint64_t bytesToBlocks(uint64_t NumBytes, uint64_t BlockSize) { - return alignTo(NumBytes, BlockSize) / BlockSize; - } - - static uint64_t blockToOffset(uint64_t BlockNumber, uint64_t BlockSize) { - return BlockNumber * BlockSize; - } - Expected getPDBInfoStream(); Expected getPDBDbiStream(); Expected getPDBTpiStream(); @@ -122,12 +87,12 @@ public: Error commit(); private: - Error setSuperBlock(const SuperBlock *Block); + Error setSuperBlock(const msf::SuperBlock *Block); BumpPtrAllocator Allocator; std::unique_ptr Buffer; - const PDBFile::SuperBlock *SB; + const msf::SuperBlock *SB; ArrayRef StreamSizes; ArrayRef DirectoryBlocks; std::vector> StreamMap; diff --git a/include/llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h b/include/llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h index ba7ca935e5f..47c755b4326 100644 --- a/include/llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h +++ b/include/llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h @@ -11,10 +11,12 @@ #define LLVM_DEBUGINFO_PDB_RAW_PDBFILEBUILDER_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" +#include "llvm/DebugInfo/PDB/Raw/MsfBuilder.h" #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include @@ -32,27 +34,24 @@ class PDBFile; class PDBFileBuilder { public: explicit PDBFileBuilder( - std::unique_ptr PdbFileBuffer); + std::unique_ptr FileBuffer); PDBFileBuilder(const PDBFileBuilder &) = delete; PDBFileBuilder &operator=(const PDBFileBuilder &) = delete; - Error setSuperBlock(const PDBFile::SuperBlock &B); - void setStreamSizes(ArrayRef S); - void setDirectoryBlocks(ArrayRef D); - void setStreamMap(const std::vector> &S); - Error generateSimpleStreamMap(); + Error initialize(const msf::SuperBlock &Super); + MsfBuilder &getMsfBuilder(); InfoStreamBuilder &getInfoBuilder(); DbiStreamBuilder &getDbiBuilder(); Expected> build(); private: - std::unique_ptr PdbFileBuffer; std::unique_ptr Info; std::unique_ptr Dbi; std::unique_ptr File; + std::unique_ptr Msf; }; } } diff --git a/lib/DebugInfo/PDB/CMakeLists.txt b/lib/DebugInfo/PDB/CMakeLists.txt index d4d6fa80bc6..b5a2bc1600f 100644 --- a/lib/DebugInfo/PDB/CMakeLists.txt +++ b/lib/DebugInfo/PDB/CMakeLists.txt @@ -42,6 +42,7 @@ add_pdb_impl_folder(Raw Raw/MsfCommon.cpp Raw/NameHashTable.cpp Raw/NameMap.cpp + Raw/NameMapBuilder.cpp Raw/PDBFile.cpp Raw/PDBFileBuilder.cpp Raw/PublicsStream.cpp diff --git a/lib/DebugInfo/PDB/Raw/DbiStream.cpp b/lib/DebugInfo/PDB/Raw/DbiStream.cpp index bfe0251f4ce..3c0586c728f 100644 --- a/lib/DebugInfo/PDB/Raw/DbiStream.cpp +++ b/lib/DebugInfo/PDB/Raw/DbiStream.cpp @@ -182,9 +182,11 @@ Error DbiStream::reload() { return make_error(raw_error_code::corrupt_file, "Found unexpected bytes in DBI Stream."); - StreamReader ECReader(ECSubstream); - if (auto EC = ECNames.load(ECReader)) - return EC; + if (ECSubstream.getLength() > 0) { + StreamReader ECReader(ECSubstream); + if (auto EC = ECNames.load(ECReader)) + return EC; + } return Error::success(); } @@ -267,6 +269,9 @@ void llvm::pdb::DbiStream::visitSectionContributions( } Error DbiStream::initializeSectionContributionData() { + if (SecContrSubstream.getLength() == 0) + return Error::success(); + StreamReader SCReader(SecContrSubstream); if (auto EC = SCReader.readEnum(SectionContribVersion)) return EC; @@ -282,6 +287,9 @@ Error DbiStream::initializeSectionContributionData() { // Initializes this->SectionHeaders. Error DbiStream::initializeSectionHeadersData() { + if (DbgStreams.size() == 0) + return Error::success(); + uint32_t StreamNum = getDebugStreamIndex(DbgHeaderType::SectionHdr); if (StreamNum >= Pdb.getNumStreams()) return make_error(raw_error_code::no_stream); @@ -307,6 +315,9 @@ Error DbiStream::initializeSectionHeadersData() { // Initializes this->Fpos. Error DbiStream::initializeFpoRecords() { + if (DbgStreams.size() == 0) + return Error::success(); + uint32_t StreamNum = getDebugStreamIndex(DbgHeaderType::NewFPO); // This means there is no FPO data. @@ -335,6 +346,9 @@ Error DbiStream::initializeFpoRecords() { } Error DbiStream::initializeSectionMapData() { + if (SecMapSubstream.getLength() == 0) + return Error::success(); + StreamReader SMReader(SecMapSubstream); const SecMapHeader *Header; if (auto EC = SMReader.readObject(Header)) @@ -357,6 +371,9 @@ Error DbiStream::initializeFileInfo() { // with the caveat that `NumSourceFiles` cannot be trusted, so // it is computed by summing `ModFileCounts`. // + if (FileInfoSubstream.getLength() == 0) + return Error::success(); + const FileInfoSubstreamHeader *FH; StreamReader FISR(FileInfoSubstream); if (auto EC = FISR.readObject(FH)) @@ -436,4 +453,10 @@ Expected DbiStream::getFileNameForIndex(uint32_t Index) const { return Name; } -Error DbiStream::commit() { return Error::success(); } +Error DbiStream::commit() { + StreamWriter Writer(*Stream); + if (auto EC = Writer.writeObject(*Header)) + return EC; + + return Error::success(); +} diff --git a/lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp b/lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp index ff5ce61c212..34ff8ae3a90 100644 --- a/lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp +++ b/lib/DebugInfo/PDB/Raw/DbiStreamBuilder.cpp @@ -18,9 +18,9 @@ using namespace llvm; using namespace llvm::codeview; using namespace llvm::pdb; -DbiStreamBuilder::DbiStreamBuilder(PDBFile &File) - : File(File), Age(1), BuildNumber(0), PdbDllVersion(0), PdbDllRbld(0), - Flags(0), MachineType(PDB_Machine::x86) {} +DbiStreamBuilder::DbiStreamBuilder() + : Age(1), BuildNumber(0), PdbDllVersion(0), PdbDllRbld(0), Flags(0), + MachineType(PDB_Machine::x86) {} void DbiStreamBuilder::setVersionHeader(PdbRaw_DbiVer V) { VerHeader = V; } @@ -36,7 +36,12 @@ void DbiStreamBuilder::setFlags(uint16_t F) { Flags = F; } void DbiStreamBuilder::setMachineType(PDB_Machine M) { MachineType = M; } -Expected> DbiStreamBuilder::build() { +uint32_t DbiStreamBuilder::calculateSerializedLength() const { + // For now we only support serializing the header. + return sizeof(DbiStream::HeaderInfo); +} + +Expected> DbiStreamBuilder::build(PDBFile &File) { if (!VerHeader.hasValue()) return make_error(raw_error_code::unspecified, "Missing DBI Stream Version"); diff --git a/lib/DebugInfo/PDB/Raw/InfoStream.cpp b/lib/DebugInfo/PDB/Raw/InfoStream.cpp index e2f85ab880f..c33a764587c 100644 --- a/lib/DebugInfo/PDB/Raw/InfoStream.cpp +++ b/lib/DebugInfo/PDB/Raw/InfoStream.cpp @@ -27,7 +27,7 @@ InfoStream::InfoStream(std::unique_ptr Stream) Error InfoStream::reload() { codeview::StreamReader Reader(*Stream); - const Header *H; + const HeaderInfo *H; if (auto EC = Reader.readObject(H)) return joinErrors( std::move(EC), @@ -78,7 +78,7 @@ PDB_UniqueId InfoStream::getGuid() const { return Guid; } Error InfoStream::commit() { StreamWriter Writer(*Stream); - Header H; + HeaderInfo H; H.Age = Age; H.Signature = Signature; H.Version = Version; @@ -87,4 +87,4 @@ Error InfoStream::commit() { return EC; return NamedStreams.commit(Writer); -} \ No newline at end of file +} diff --git a/lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp b/lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp index 21042e9a534..5a0e835dd56 100644 --- a/lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp +++ b/lib/DebugInfo/PDB/Raw/InfoStreamBuilder.cpp @@ -18,7 +18,7 @@ using namespace llvm; using namespace llvm::codeview; using namespace llvm::pdb; -InfoStreamBuilder::InfoStreamBuilder(IPDBFile &File) : File(File) {} +InfoStreamBuilder::InfoStreamBuilder() {} void InfoStreamBuilder::setVersion(PdbRaw_ImplVer V) { Ver = V; } @@ -28,7 +28,12 @@ void InfoStreamBuilder::setAge(uint32_t A) { Age = A; } void InfoStreamBuilder::setGuid(PDB_UniqueId G) { Guid = G; } -Expected> InfoStreamBuilder::build() { +uint32_t InfoStreamBuilder::calculateSerializedLength() const { + return sizeof(InfoStream::HeaderInfo) + + NamedStreams.calculateSerializedLength(); +} + +Expected> InfoStreamBuilder::build(PDBFile &File) { if (!Ver.hasValue()) return make_error(raw_error_code::unspecified, "Missing PDB Stream Version"); diff --git a/lib/DebugInfo/PDB/Raw/MsfBuilder.cpp b/lib/DebugInfo/PDB/Raw/MsfBuilder.cpp index ab739d339a9..fecefbfeb8d 100644 --- a/lib/DebugInfo/PDB/Raw/MsfBuilder.cpp +++ b/lib/DebugInfo/PDB/Raw/MsfBuilder.cpp @@ -24,7 +24,7 @@ MsfBuilder::MsfBuilder(uint32_t BlockSize, uint32_t MinBlockCount, bool CanGrow, BumpPtrAllocator &Allocator) : Allocator(Allocator), IsGrowable(CanGrow), BlockSize(BlockSize), MininumBlocks(MinBlockCount), BlockMapAddr(kDefaultBlockMapAddr), - FreeBlocks(MinBlockCount + 2U, true) { + FreeBlocks(std::max(MinBlockCount, 2U), true) { FreeBlocks[kSuperBlockBlock] = false; FreeBlocks[BlockMapAddr] = false; } @@ -59,6 +59,25 @@ Error MsfBuilder::setBlockMapAddr(uint32_t Addr) { return Error::success(); } +void MsfBuilder::setUnknown0(uint32_t Unk0) { Unknown0 = Unk0; } + +void MsfBuilder::setUnknown1(uint32_t Unk1) { Unknown1 = Unk1; } + +Error MsfBuilder::setDirectoryBlocksHint(ArrayRef DirBlocks) { + for (auto B : DirectoryBlocks) + FreeBlocks[B] = true; + for (auto B : DirBlocks) { + if (!isBlockFree(B)) { + return make_error(raw_error_code::unspecified, + "Attempt to reuse an allocated block"); + } + FreeBlocks[B] = false; + } + + DirectoryBlocks = DirBlocks; + return Error::success(); +} + Error MsfBuilder::allocateBlocks(uint32_t NumBlocks, MutableArrayRef Blocks) { if (NumBlocks == 0) @@ -198,16 +217,28 @@ Expected MsfBuilder::build() { L.SB->BlockMapAddr = BlockMapAddr; L.SB->BlockSize = BlockSize; L.SB->NumDirectoryBytes = computeDirectoryByteSize(); - L.SB->Unknown0 = 0; - L.SB->Unknown1 = 0; + L.SB->Unknown0 = Unknown0; + L.SB->Unknown1 = Unknown1; uint32_t NumDirectoryBlocks = bytesToBlocks(L.SB->NumDirectoryBytes, BlockSize); - // The directory blocks should be re-allocated as a stable pointer. - std::vector DirectoryBlocks; - DirectoryBlocks.resize(NumDirectoryBlocks); - if (auto EC = allocateBlocks(NumDirectoryBlocks, DirectoryBlocks)) - return std::move(EC); + if (NumDirectoryBlocks > DirectoryBlocks.size()) { + // Our hint wasn't enough to satisfy the entire directory. Allocate + // remaining pages. + std::vector ExtraBlocks; + uint32_t NumExtraBlocks = NumDirectoryBlocks - DirectoryBlocks.size(); + ExtraBlocks.resize(NumExtraBlocks); + if (auto EC = allocateBlocks(NumExtraBlocks, ExtraBlocks)) + return std::move(EC); + DirectoryBlocks.insert(DirectoryBlocks.end(), ExtraBlocks.begin(), + ExtraBlocks.end()); + } else if (NumDirectoryBlocks < DirectoryBlocks.size()) { + uint32_t NumUnnecessaryBlocks = DirectoryBlocks.size() - NumDirectoryBlocks; + for (auto B : + ArrayRef(DirectoryBlocks).drop_back(NumUnnecessaryBlocks)) + FreeBlocks[B] = true; + DirectoryBlocks.resize(NumDirectoryBlocks); + } // Don't set the number of blocks in the file until after allocating Blocks // for diff --git a/lib/DebugInfo/PDB/Raw/NameMapBuilder.cpp b/lib/DebugInfo/PDB/Raw/NameMapBuilder.cpp new file mode 100644 index 00000000000..fe033c368e7 --- /dev/null +++ b/lib/DebugInfo/PDB/Raw/NameMapBuilder.cpp @@ -0,0 +1,26 @@ +//===- NameMapBuilder.cpp - PDB Name Map Builder ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Raw/NameMapBuilder.h" + +#include "llvm/DebugInfo/PDB/Raw/NameMap.h" + +using namespace llvm; +using namespace llvm::pdb; + +NameMapBuilder::NameMapBuilder() {} + +Expected> NameMapBuilder::build() { + return llvm::make_unique(); +} + +uint32_t NameMapBuilder::calculateSerializedLength() const { + // For now we write an empty name map, nothing else. + return 5 * sizeof(uint32_t); +} diff --git a/lib/DebugInfo/PDB/Raw/PDBFile.cpp b/lib/DebugInfo/PDB/Raw/PDBFile.cpp index 2aa4d4c426f..8b09dc57cde 100644 --- a/lib/DebugInfo/PDB/Raw/PDBFile.cpp +++ b/lib/DebugInfo/PDB/Raw/PDBFile.cpp @@ -53,7 +53,7 @@ uint32_t PDBFile::getBlockMapIndex() const { return SB->BlockMapAddr; } uint32_t PDBFile::getUnknown1() const { return SB->Unknown1; } uint32_t PDBFile::getNumDirectoryBlocks() const { - return bytesToBlocks(SB->NumDirectoryBytes, SB->BlockSize); + return msf::bytesToBlocks(SB->NumDirectoryBytes, SB->BlockSize); } uint64_t PDBFile::getBlockMapOffset() const { @@ -75,7 +75,7 @@ uint32_t PDBFile::getFileSize() const { return Buffer->getLength(); } Expected> PDBFile::getBlockData(uint32_t BlockIndex, uint32_t NumBytes) const { - uint64_t StreamBlockOffset = blockToOffset(BlockIndex, getBlockSize()); + uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize()); ArrayRef Result; if (auto EC = Buffer->readBytes(StreamBlockOffset, NumBytes, Result)) @@ -94,7 +94,7 @@ Error PDBFile::setBlockData(uint32_t BlockIndex, uint32_t Offset, raw_error_code::invalid_block_address, "setBlockData attempted to write out of block bounds."); - uint64_t StreamBlockOffset = blockToOffset(BlockIndex, getBlockSize()); + uint64_t StreamBlockOffset = msf::blockToOffset(BlockIndex, getBlockSize()); StreamBlockOffset += Offset; return Buffer->writeBytes(StreamBlockOffset, Data); } @@ -143,7 +143,8 @@ Error PDBFile::parseStreamData() { uint32_t StreamSize = getStreamByteSize(I); // FIXME: What does StreamSize ~0U mean? uint64_t NumExpectedStreamBlocks = - StreamSize == UINT32_MAX ? 0 : bytesToBlocks(StreamSize, SB->BlockSize); + StreamSize == UINT32_MAX ? 0 : msf::bytesToBlocks(StreamSize, + SB->BlockSize); // For convenience, we store the block array contiguously. This is because // if someone calls setStreamMap(), it is more convenient to be able to call @@ -293,51 +294,15 @@ Expected PDBFile::getStringTable() { return *StringTable; } -Error PDBFile::setSuperBlock(const SuperBlock *Block) { - SB = Block; - - // Check the magic bytes. - if (memcmp(SB->MagicBytes, MsfMagic, sizeof(MsfMagic)) != 0) - return make_error(raw_error_code::corrupt_file, - "MSF magic header doesn't match"); - - // We don't support blocksizes which aren't a multiple of four bytes. - if (SB->BlockSize % sizeof(support::ulittle32_t) != 0) - return make_error(raw_error_code::corrupt_file, - "Block size is not multiple of 4."); - - switch (SB->BlockSize) { - case 512: - case 1024: - case 2048: - case 4096: - break; - default: - // An invalid block size suggests a corrupt PDB file. - return make_error(raw_error_code::corrupt_file, - "Unsupported block size."); - } +Error PDBFile::setSuperBlock(const msf::SuperBlock *Block) { + if (auto EC = msf::validateSuperBlock(*Block)) + return EC; if (Buffer->getLength() % SB->BlockSize != 0) return make_error(raw_error_code::corrupt_file, "File size is not a multiple of block size"); - // We don't support directories whose sizes aren't a multiple of four bytes. - if (SB->NumDirectoryBytes % sizeof(support::ulittle32_t) != 0) - return make_error(raw_error_code::corrupt_file, - "Directory size is not multiple of 4."); - - // The number of blocks which comprise the directory is a simple function of - // the number of bytes it contains. - uint64_t NumDirectoryBlocks = getNumDirectoryBlocks(); - - // The directory, as we understand it, is a block which consists of a list of - // block numbers. It is unclear what would happen if the number of blocks - // couldn't fit on a single block. - if (NumDirectoryBlocks > SB->BlockSize / sizeof(support::ulittle32_t)) - return make_error(raw_error_code::corrupt_file, - "Too many directory blocks."); - + SB = Block; return Error::success(); } diff --git a/lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp b/lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp index f859c1fcc2e..b1ae760c6fb 100644 --- a/lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp +++ b/lib/DebugInfo/PDB/Raw/PDBFileBuilder.cpp @@ -9,6 +9,8 @@ #include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h" +#include "llvm/ADT/BitVector.h" + #include "llvm/DebugInfo/CodeView/StreamInterface.h" #include "llvm/DebugInfo/CodeView/StreamWriter.h" #include "llvm/DebugInfo/PDB/Raw/DbiStream.h" @@ -20,99 +22,72 @@ using namespace llvm; using namespace llvm::codeview; using namespace llvm::pdb; +using namespace llvm::support; PDBFileBuilder::PDBFileBuilder( - std::unique_ptr PdbFileBuffer) - : File(llvm::make_unique(std::move(PdbFileBuffer))) {} + std::unique_ptr FileBuffer) + : File(llvm::make_unique(std::move(FileBuffer))) {} -Error PDBFileBuilder::setSuperBlock(const PDBFile::SuperBlock &B) { - auto SB = static_cast( - File->Allocator.Allocate(sizeof(PDBFile::SuperBlock), - llvm::AlignOf::Alignment)); - ::memcpy(SB, &B, sizeof(PDBFile::SuperBlock)); - return File->setSuperBlock(SB); -} +Error PDBFileBuilder::initialize(const msf::SuperBlock &Super) { + auto ExpectedMsf = + MsfBuilder::create(File->Allocator, Super.BlockSize, Super.NumBlocks); + if (!ExpectedMsf) + return ExpectedMsf.takeError(); -void PDBFileBuilder::setStreamSizes(ArrayRef S) { - File->StreamSizes = S; -} - -void PDBFileBuilder::setDirectoryBlocks(ArrayRef D) { - File->DirectoryBlocks = D; -} - -void PDBFileBuilder::setStreamMap( - const std::vector> &S) { - File->StreamMap = S; -} - -Error PDBFileBuilder::generateSimpleStreamMap() { - if (File->StreamSizes.empty()) - return Error::success(); - - static std::vector> StaticMap; - File->StreamMap.clear(); - StaticMap.clear(); - - // Figure out how many blocks are needed for all streams, and set the first - // used block to the highest block so that we can write the rest of the - // blocks contiguously. - uint32_t TotalFileBlocks = File->getBlockCount(); - std::vector ReservedBlocks; - ReservedBlocks.push_back(support::ulittle32_t(0)); - ReservedBlocks.push_back(File->SB->BlockMapAddr); - ReservedBlocks.insert(ReservedBlocks.end(), File->DirectoryBlocks.begin(), - File->DirectoryBlocks.end()); - - uint32_t BlocksNeeded = 0; - for (auto Size : File->StreamSizes) - BlocksNeeded += File->bytesToBlocks(Size, File->getBlockSize()); - - support::ulittle32_t NextBlock(TotalFileBlocks - BlocksNeeded - - ReservedBlocks.size()); - - StaticMap.resize(File->StreamSizes.size()); - for (uint32_t S = 0; S < File->StreamSizes.size(); ++S) { - uint32_t Size = File->StreamSizes[S]; - uint32_t NumBlocks = File->bytesToBlocks(Size, File->getBlockSize()); - auto &ThisStream = StaticMap[S]; - for (uint32_t I = 0; I < NumBlocks;) { - NextBlock += 1; - if (std::find(ReservedBlocks.begin(), ReservedBlocks.end(), NextBlock) != - ReservedBlocks.end()) - continue; - - ++I; - assert(NextBlock < File->getBlockCount()); - ThisStream.push_back(NextBlock); - } - File->StreamMap.push_back(ThisStream); - } + auto &MsfResult = *ExpectedMsf; + if (auto EC = MsfResult.setBlockMapAddr(Super.BlockMapAddr)) + return EC; + MsfResult.setUnknown0(Super.Unknown0); + MsfResult.setUnknown1(Super.Unknown1); + Msf = llvm::make_unique(std::move(MsfResult)); return Error::success(); } +MsfBuilder &PDBFileBuilder::getMsfBuilder() { return *Msf; } + InfoStreamBuilder &PDBFileBuilder::getInfoBuilder() { if (!Info) - Info = llvm::make_unique(*File); + Info = llvm::make_unique(); return *Info; } DbiStreamBuilder &PDBFileBuilder::getDbiBuilder() { if (!Dbi) - Dbi = llvm::make_unique(*File); + Dbi = llvm::make_unique(); return *Dbi; } Expected> PDBFileBuilder::build() { if (Info) { - auto ExpectedInfo = Info->build(); + uint32_t Length = Info->calculateSerializedLength(); + if (auto EC = Msf->setStreamSize(StreamPDB, Length)) + return std::move(EC); + } + if (Dbi) { + uint32_t Length = Dbi->calculateSerializedLength(); + if (auto EC = Msf->setStreamSize(StreamDBI, Length)) + return std::move(EC); + } + + auto ExpectedLayout = Msf->build(); + if (!ExpectedLayout) + return ExpectedLayout.takeError(); + + const msf::Layout &L = *ExpectedLayout; + File->StreamMap = L.StreamMap; + File->StreamSizes = L.StreamSizes; + File->DirectoryBlocks = L.DirectoryBlocks; + File->SB = L.SB; + + if (Info) { + auto ExpectedInfo = Info->build(*File); if (!ExpectedInfo) return ExpectedInfo.takeError(); File->Info = std::move(*ExpectedInfo); } if (Dbi) { - auto ExpectedDbi = Dbi->build(); + auto ExpectedDbi = Dbi->build(*File); if (!ExpectedDbi) return ExpectedDbi.takeError(); File->Dbi = std::move(*ExpectedDbi); diff --git a/test/DebugInfo/PDB/pdbdump-headers.test b/test/DebugInfo/PDB/pdbdump-headers.test index 80914cbe61a..dabce470852 100644 --- a/test/DebugInfo/PDB/pdbdump-headers.test +++ b/test/DebugInfo/PDB/pdbdump-headers.test @@ -16,7 +16,6 @@ ; EMPTY-NEXT: Unknown1: 0 ; EMPTY-NEXT: BlockMapAddr: 24 ; EMPTY-NEXT: NumDirectoryBlocks: 1 -; EMPTY-NEXT: BlockMapOffset: 98304 ; EMPTY-NEXT: DirectoryBlocks: [23] ; EMPTY-NEXT: NumStreams: 17 ; EMPTY-NEXT: } @@ -952,7 +951,6 @@ ; ALL: Unknown1: 0 ; ALL: BlockMapAddr: 24 ; ALL: NumDirectoryBlocks: 1 -; ALL: BlockMapOffset: 98304 ; ALL: DirectoryBlocks: [23] ; ALL: NumStreams: 17 ; ALL: } @@ -1668,7 +1666,6 @@ ; BIG-NEXT: Unknown1: 0 ; BIG-NEXT: BlockMapAddr: 97 ; BIG-NEXT: NumDirectoryBlocks: 1 -; BIG-NEXT: BlockMapOffset: 397312 ; BIG-NEXT: DirectoryBlocks: [96] ; BIG-NEXT: NumStreams: 64 ; BIG-NEXT: } diff --git a/test/DebugInfo/PDB/pdbdump-write.test b/test/DebugInfo/PDB/pdbdump-write.test index 0421ef1e3e6..09358932941 100644 --- a/test/DebugInfo/PDB/pdbdump-write.test +++ b/test/DebugInfo/PDB/pdbdump-write.test @@ -5,9 +5,13 @@ ; the YAML, the PDB might be missing data required for any standard tool ; to recognize it. Finally, it dumps the same set of fields from the newly ; constructed PDB to YAML, and verifies that the YAML is the same as the -; original YAML generated from the good PDB. +; original YAML generated from the good PDB. Note that when doing the +; final comparison it must dump the original and the new pdb without any +; stream metadata, since the layout of the MSF file might be different +; (for example if we don't write the entire stream) ; ; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream %p/Inputs/empty.pdb > %t.1 ; RUN: llvm-pdbdump yaml2pdb -pdb=%t.2 %t.1 -; RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream %t.2 > %t.3 -; RUN: diff %t.1 %t.3 +; RUN: llvm-pdbdump pdb2yaml -pdb-stream %p/Inputs/empty.pdb > %t.3 +; RUN: llvm-pdbdump pdb2yaml -pdb-stream %t.2 > %t.4 +; RUN: diff %t.3 %t.4 diff --git a/test/DebugInfo/PDB/pdbdump-yaml.test b/test/DebugInfo/PDB/pdbdump-yaml.test index a7394beb8cc..e0029d83517 100644 --- a/test/DebugInfo/PDB/pdbdump-yaml.test +++ b/test/DebugInfo/PDB/pdbdump-yaml.test @@ -13,7 +13,6 @@ ; YAML-NEXT: Unknown1: 0 ; YAML-NEXT: BlockMapAddr: 24 ; YAML-NEXT: NumDirectoryBlocks: 1 -; YAML-NEXT: BlockMapOffset: 98304 ; YAML-NEXT: DirectoryBlocks: ; YAML-NEXT: - 23 ; YAML-NEXT: NumStreams: 17 diff --git a/tools/llvm-pdbdump/LLVMOutputStyle.cpp b/tools/llvm-pdbdump/LLVMOutputStyle.cpp index d8eefa08377..6fee3a2b026 100644 --- a/tools/llvm-pdbdump/LLVMOutputStyle.cpp +++ b/tools/llvm-pdbdump/LLVMOutputStyle.cpp @@ -101,7 +101,6 @@ Error LLVMOutputStyle::dumpFileHeaders() { P.printNumber("Unknown1", File.getUnknown1()); P.printNumber("BlockMapAddr", File.getBlockMapIndex()); P.printNumber("NumDirectoryBlocks", File.getNumDirectoryBlocks()); - P.printNumber("BlockMapOffset", File.getBlockMapOffset()); // The directory is not contiguous. Instead, the block map contains a // contiguous list of block numbers whose contents, when concatenated in diff --git a/tools/llvm-pdbdump/PdbYaml.cpp b/tools/llvm-pdbdump/PdbYaml.cpp index a341ab07308..6525e3c165b 100644 --- a/tools/llvm-pdbdump/PdbYaml.cpp +++ b/tools/llvm-pdbdump/PdbYaml.cpp @@ -115,16 +115,14 @@ void MappingTraits::mapping(IO &IO, PdbObject &Obj) { void MappingTraits::mapping(IO &IO, MsfHeaders &Obj) { IO.mapRequired("SuperBlock", Obj.SuperBlock); IO.mapRequired("NumDirectoryBlocks", Obj.NumDirectoryBlocks); - IO.mapRequired("BlockMapOffset", Obj.BlockMapOffset); IO.mapRequired("DirectoryBlocks", Obj.DirectoryBlocks); IO.mapRequired("NumStreams", Obj.NumStreams); IO.mapRequired("FileSize", Obj.FileSize); } -void MappingTraits::mapping(IO &IO, - PDBFile::SuperBlock &SB) { +void MappingTraits::mapping(IO &IO, msf::SuperBlock &SB) { if (!IO.outputting()) { - ::memcpy(SB.MagicBytes, MsfMagic, sizeof(MsfMagic)); + ::memcpy(SB.MagicBytes, msf::Magic, sizeof(msf::Magic)); } IO.mapRequired("BlockSize", SB.BlockSize); diff --git a/tools/llvm-pdbdump/PdbYaml.h b/tools/llvm-pdbdump/PdbYaml.h index 91c0a585926..8a964a354c0 100644 --- a/tools/llvm-pdbdump/PdbYaml.h +++ b/tools/llvm-pdbdump/PdbYaml.h @@ -14,6 +14,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/DebugInfo/PDB/PDBTypes.h" +#include "llvm/DebugInfo/PDB/Raw/MsfCommon.h" #include "llvm/DebugInfo/PDB/Raw/PDBFile.h" #include "llvm/DebugInfo/PDB/Raw/RawConstants.h" #include "llvm/Support/Endian.h" @@ -26,16 +27,15 @@ namespace pdb { namespace yaml { struct MsfHeaders { - PDBFile::SuperBlock SuperBlock; + msf::SuperBlock SuperBlock; uint32_t NumDirectoryBlocks; - uint32_t BlockMapOffset; - std::vector DirectoryBlocks; + std::vector DirectoryBlocks; uint32_t NumStreams; uint32_t FileSize; }; struct StreamBlockList { - std::vector Blocks; + std::vector Blocks; }; struct PdbInfoStream { @@ -57,7 +57,7 @@ struct PdbDbiStream { struct PdbObject { Optional Headers; - Optional> StreamSizes; + Optional> StreamSizes; Optional> StreamMap; Optional PdbStream; Optional DbiStream; @@ -77,8 +77,8 @@ template <> struct MappingTraits { static void mapping(IO &IO, pdb::yaml::MsfHeaders &Obj); }; -template <> struct MappingTraits { - static void mapping(IO &IO, pdb::PDBFile::SuperBlock &SB); +template <> struct MappingTraits { + static void mapping(IO &IO, pdb::msf::SuperBlock &SB); }; template <> struct MappingTraits { @@ -95,7 +95,7 @@ template <> struct MappingTraits { } } -LLVM_YAML_IS_SEQUENCE_VECTOR(support::ulittle32_t) +LLVM_YAML_IS_SEQUENCE_VECTOR(uint32_t) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::pdb::yaml::StreamBlockList) #endif // LLVM_TOOLS_LLVMPDBDUMP_PDBYAML_H diff --git a/tools/llvm-pdbdump/YAMLOutputStyle.cpp b/tools/llvm-pdbdump/YAMLOutputStyle.cpp index aa68128cd80..e216ab98c13 100644 --- a/tools/llvm-pdbdump/YAMLOutputStyle.cpp +++ b/tools/llvm-pdbdump/YAMLOutputStyle.cpp @@ -23,8 +23,7 @@ using namespace llvm::pdb; YAMLOutputStyle::YAMLOutputStyle(PDBFile &File) : File(File), Out(outs()) {} Error YAMLOutputStyle::dump() { - if (opts::pdb2yaml::StreamDirectory || opts::pdb2yaml::PdbStream || - opts::pdb2yaml::DbiStream) + if (opts::pdb2yaml::StreamDirectory) opts::pdb2yaml::StreamMetadata = true; if (auto EC = dumpFileHeaders()) @@ -54,7 +53,6 @@ Error YAMLOutputStyle::dumpFileHeaders() { Obj.Headers.emplace(); Obj.Headers->SuperBlock.NumBlocks = File.getBlockCount(); Obj.Headers->SuperBlock.BlockMapAddr = File.getBlockMapIndex(); - Obj.Headers->BlockMapOffset = File.getBlockMapOffset(); Obj.Headers->SuperBlock.BlockSize = File.getBlockSize(); auto Blocks = File.getDirectoryBlockArray(); Obj.Headers->DirectoryBlocks.assign(Blocks.begin(), Blocks.end()); @@ -73,7 +71,9 @@ Error YAMLOutputStyle::dumpStreamMetadata() { if (!opts::pdb2yaml::StreamMetadata) return Error::success(); - Obj.StreamSizes = File.getStreamSizes(); + Obj.StreamSizes.emplace(); + Obj.StreamSizes->assign(File.getStreamSizes().begin(), + File.getStreamSizes().end()); return Error::success(); } @@ -85,7 +85,7 @@ Error YAMLOutputStyle::dumpStreamDirectory() { Obj.StreamMap.emplace(); for (auto &Stream : StreamMap) { pdb::yaml::StreamBlockList BlockList; - BlockList.Blocks = Stream; + BlockList.Blocks.assign(Stream.begin(), Stream.end()); Obj.StreamMap->push_back(BlockList); } diff --git a/tools/llvm-pdbdump/llvm-pdbdump.cpp b/tools/llvm-pdbdump/llvm-pdbdump.cpp index 5a3ced37a9d..467b5bfe1b6 100644 --- a/tools/llvm-pdbdump/llvm-pdbdump.cpp +++ b/tools/llvm-pdbdump/llvm-pdbdump.cpp @@ -279,14 +279,12 @@ cl::opt StreamDirectory( "stream-directory", cl::desc("Dump each stream's block map (implies -stream-metadata)"), cl::sub(PdbToYamlSubcommand), cl::init(false)); -cl::opt PdbStream( - "pdb-stream", - cl::desc("Dump the PDB Stream (Stream 1) (implies -stream-metadata)"), - cl::sub(PdbToYamlSubcommand), cl::init(false)); -cl::opt DbiStream( - "dbi-stream", - cl::desc("Dump the DBI Stream (Stream 2) (implies -stream-metadata)"), - cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt PdbStream("pdb-stream", + cl::desc("Dump the PDB Stream (Stream 1)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt DbiStream("dbi-stream", + cl::desc("Dump the DBI Stream (Stream 2)"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); cl::list InputFilename(cl::Positional, cl::desc(""), cl::Required, @@ -324,20 +322,37 @@ static void yamlToPdb(StringRef Path) { llvm::make_unique(std::move(*OutFileOrError)); PDBFileBuilder Builder(std::move(FileByteStream)); - ExitOnErr(Builder.setSuperBlock(YamlObj.Headers->SuperBlock)); - if (YamlObj.StreamSizes.hasValue()) { - Builder.setStreamSizes(YamlObj.StreamSizes.getValue()); + ExitOnErr(Builder.initialize(YamlObj.Headers->SuperBlock)); + ExitOnErr(Builder.getMsfBuilder().setDirectoryBlocksHint( + YamlObj.Headers->DirectoryBlocks)); + if (!YamlObj.StreamSizes.hasValue()) { + ExitOnErr(make_error( + generic_error_code::unspecified, + "Cannot generate a PDB when stream sizes are not known")); } - Builder.setDirectoryBlocks(YamlObj.Headers->DirectoryBlocks); if (YamlObj.StreamMap.hasValue()) { - std::vector> StreamMap; - for (auto &E : YamlObj.StreamMap.getValue()) { - StreamMap.push_back(E.Blocks); + if (YamlObj.StreamMap->size() != YamlObj.StreamSizes->size()) { + ExitOnErr(make_error(generic_error_code::unspecified, + "YAML specifies different number of " + "streams in stream sizes and stream " + "map")); + } + + auto &Sizes = *YamlObj.StreamSizes; + auto &Map = *YamlObj.StreamMap; + for (uint32_t I = 0; I < Sizes.size(); ++I) { + uint32_t Size = Sizes[I]; + std::vector Blocks; + for (auto E : Map[I].Blocks) + Blocks.push_back(E); + ExitOnErr(Builder.getMsfBuilder().addStream(Size, Blocks)); } - Builder.setStreamMap(StreamMap); } else { - ExitOnErr(Builder.generateSimpleStreamMap()); + auto &Sizes = *YamlObj.StreamSizes; + for (auto S : Sizes) { + ExitOnErr(Builder.getMsfBuilder().addStream(S)); + } } if (YamlObj.PdbStream.hasValue()) { diff --git a/unittests/DebugInfo/PDB/MsfBuilderTest.cpp b/unittests/DebugInfo/PDB/MsfBuilderTest.cpp index f0b48ec1577..d20f5e52d88 100644 --- a/unittests/DebugInfo/PDB/MsfBuilderTest.cpp +++ b/unittests/DebugInfo/PDB/MsfBuilderTest.cpp @@ -84,7 +84,8 @@ TEST_F(MsfBuilderTest, TestUsedBlocksMarkedAsUsed) { // Allocate some extra blocks at the end so we can verify that they're free // after the initialization. std::vector Blocks = {2, 3, 4, 5, 6, 7, 8, 9, 10}; - auto ExpectedMsf = MsfBuilder::create(Allocator, 4096, Blocks.size() + 10); + auto ExpectedMsf = + MsfBuilder::create(Allocator, 4096, 2 + Blocks.size() + 10); EXPECT_EXPECTED(ExpectedMsf); auto &Msf = *ExpectedMsf; @@ -267,7 +268,7 @@ TEST_F(MsfBuilderTest, TestBlockCountsWhenAddingStreams) { } } -TEST_F(MsfBuilderTest, TestBuildMsfLayout) { +TEST_F(MsfBuilderTest, BuildMsfLayout) { // Test that we can generate an Msf Layout structure from a valid layout // specification. auto ExpectedMsf = MsfBuilder::create(Allocator, 4096); @@ -298,3 +299,56 @@ TEST_F(MsfBuilderTest, TestBuildMsfLayout) { EXPECT_EQ(ExpectedNumBlocks, L.StreamMap[I].size()); } } + +TEST_F(MsfBuilderTest, UseDirectoryBlockHint) { + Expected ExpectedMsf = + MsfBuilder::create(Allocator, 4096, 4, false); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({2})); + EXPECT_NO_ERROR(Msf.addStream(2048, {3})); + + auto ExpectedLayout = Msf.build(); + EXPECT_EXPECTED(ExpectedLayout); + Layout &L = *ExpectedLayout; + EXPECT_EQ(4U, L.SB->NumBlocks); + EXPECT_EQ(1U, L.DirectoryBlocks.size()); + EXPECT_EQ(1U, L.StreamMap[0].size()); + + EXPECT_EQ(2U, L.DirectoryBlocks[0]); + EXPECT_EQ(3U, L.StreamMap[0].front()); +} + +TEST_F(MsfBuilderTest, DirectoryBlockHintInsufficient) { + Expected ExpectedMsf = MsfBuilder::create(Allocator, 4096, 4); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({2})); + + uint32_t Size = 4096 * 4096 / 4; + EXPECT_NO_ERROR(Msf.addStream(Size)); + + auto ExpectedLayout = Msf.build(); + EXPECT_EXPECTED(ExpectedLayout); + Layout &L = *ExpectedLayout; + EXPECT_EQ(2U, L.DirectoryBlocks.size()); + EXPECT_EQ(2U, L.DirectoryBlocks[0]); +} + +TEST_F(MsfBuilderTest, DirectoryBlockHintOverestimated) { + Expected ExpectedMsf = MsfBuilder::create(Allocator, 4096, 4); + EXPECT_EXPECTED(ExpectedMsf); + auto &Msf = *ExpectedMsf; + + EXPECT_NO_ERROR(Msf.setDirectoryBlocksHint({2, 3})); + + EXPECT_NO_ERROR(Msf.addStream(2048)); + + auto ExpectedLayout = Msf.build(); + EXPECT_EXPECTED(ExpectedLayout); + Layout &L = *ExpectedLayout; + EXPECT_EQ(1U, L.DirectoryBlocks.size()); + EXPECT_EQ(2U, L.DirectoryBlocks[0]); +}