//===- PDBStringTableBuilder.cpp - PDB String Table -------------*- 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/Native/PDBStringTableBuilder.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/Hash.h" #include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" #include "llvm/DebugInfo/PDB/Native/RawTypes.h" #include "llvm/Support/BinaryStreamWriter.h" #include "llvm/Support/Endian.h" using namespace llvm; using namespace llvm::msf; using namespace llvm::support; using namespace llvm::support::endian; using namespace llvm::pdb; uint32_t PDBStringTableBuilder::insert(StringRef S) { return Strings.insert(S); } static uint32_t computeBucketCount(uint32_t NumStrings) { // The /names stream is basically an on-disk open-addressing hash table. // Hash collisions are resolved by linear probing. We cannot make // utilization 100% because it will make the linear probing extremely // slow. But lower utilization wastes disk space. As a reasonable // load factor, we choose 80%. We need +1 because slot 0 is reserved. return (NumStrings + 1) * 1.25; } uint32_t PDBStringTableBuilder::calculateHashTableSize() const { uint32_t Size = sizeof(uint32_t); // Hash table begins with 4-byte size field. Size += sizeof(uint32_t) * computeBucketCount(Strings.size()); return Size; } uint32_t PDBStringTableBuilder::calculateSerializedSize() const { uint32_t Size = 0; Size += sizeof(PDBStringTableHeader); Size += Strings.calculateSerializedSize(); Size += calculateHashTableSize(); Size += sizeof(uint32_t); // The /names stream ends with the string count. return Size; } void PDBStringTableBuilder::setStrings( const codeview::DebugStringTableSubsection &Strings) { this->Strings = Strings; } Error PDBStringTableBuilder::writeHeader(BinaryStreamWriter &Writer) const { // Write a header PDBStringTableHeader H; H.Signature = PDBStringTableSignature; H.HashVersion = 1; H.ByteSize = Strings.calculateSerializedSize(); if (auto EC = Writer.writeObject(H)) return EC; assert(Writer.bytesRemaining() == 0); return Error::success(); } Error PDBStringTableBuilder::writeStrings(BinaryStreamWriter &Writer) const { if (auto EC = Strings.commit(Writer)) return EC; assert(Writer.bytesRemaining() == 0); return Error::success(); } Error PDBStringTableBuilder::writeHashTable(BinaryStreamWriter &Writer) const { // Write a hash table. uint32_t BucketCount = computeBucketCount(Strings.size()); if (auto EC = Writer.writeInteger(BucketCount)) return EC; std::vector Buckets(BucketCount); for (auto &Pair : Strings) { StringRef S = Pair.getKey(); uint32_t Offset = Pair.getValue(); uint32_t Hash = hashStringV1(S); for (uint32_t I = 0; I != BucketCount; ++I) { uint32_t Slot = (Hash + I) % BucketCount; if (Slot == 0) continue; // Skip reserved slot if (Buckets[Slot] != 0) continue; Buckets[Slot] = Offset; break; } } if (auto EC = Writer.writeArray(ArrayRef(Buckets))) return EC; assert(Writer.bytesRemaining() == 0); return Error::success(); } Error PDBStringTableBuilder::writeEpilogue(BinaryStreamWriter &Writer) const { if (auto EC = Writer.writeInteger(Strings.size())) return EC; assert(Writer.bytesRemaining() == 0); return Error::success(); } Error PDBStringTableBuilder::commit(BinaryStreamWriter &Writer) const { BinaryStreamWriter SectionWriter; std::tie(SectionWriter, Writer) = Writer.split(sizeof(PDBStringTableHeader)); if (auto EC = writeHeader(SectionWriter)) return EC; std::tie(SectionWriter, Writer) = Writer.split(Strings.calculateSerializedSize()); if (auto EC = writeStrings(SectionWriter)) return EC; std::tie(SectionWriter, Writer) = Writer.split(calculateHashTableSize()); if (auto EC = writeHashTable(SectionWriter)) return EC; std::tie(SectionWriter, Writer) = Writer.split(sizeof(uint32_t)); if (auto EC = writeEpilogue(SectionWriter)) return EC; return Error::success(); }