10b57cec5SDimitry Andric //===- NamedStreamMap.cpp - PDB Named Stream Map --------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/NamedStreamMap.h" 100b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h" 110b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 120b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/Hash.h" 130b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/HashTable.h" 140b57cec5SDimitry Andric #include "llvm/DebugInfo/PDB/Native/RawError.h" 150b57cec5SDimitry Andric #include "llvm/Support/BinaryStreamReader.h" 160b57cec5SDimitry Andric #include "llvm/Support/BinaryStreamWriter.h" 170b57cec5SDimitry Andric #include "llvm/Support/Endian.h" 180b57cec5SDimitry Andric #include "llvm/Support/Error.h" 190b57cec5SDimitry Andric #include <algorithm> 200b57cec5SDimitry Andric #include <cassert> 210b57cec5SDimitry Andric #include <cstdint> 220b57cec5SDimitry Andric 230b57cec5SDimitry Andric using namespace llvm; 240b57cec5SDimitry Andric using namespace llvm::pdb; 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric NamedStreamMapTraits::NamedStreamMapTraits(NamedStreamMap &NS) : NS(&NS) {} 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric uint16_t NamedStreamMapTraits::hashLookupKey(StringRef S) const { 290b57cec5SDimitry Andric // In the reference implementation, this uses 300b57cec5SDimitry Andric // HASH Hasher<ULONG*, USHORT*>::hashPbCb(PB pb, size_t cb, ULONG ulMod). 310b57cec5SDimitry Andric // Here, the type HASH is a typedef of unsigned short. 320b57cec5SDimitry Andric // ** It is not a bug that we truncate the result of hashStringV1, in fact 330b57cec5SDimitry Andric // it is a bug if we do not! ** 340b57cec5SDimitry Andric // See NMTNI::hash() in the reference implementation. 350b57cec5SDimitry Andric return static_cast<uint16_t>(hashStringV1(S)); 360b57cec5SDimitry Andric } 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric StringRef NamedStreamMapTraits::storageKeyToLookupKey(uint32_t Offset) const { 390b57cec5SDimitry Andric return NS->getString(Offset); 400b57cec5SDimitry Andric } 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric uint32_t NamedStreamMapTraits::lookupKeyToStorageKey(StringRef S) { 430b57cec5SDimitry Andric return NS->appendStringData(S); 440b57cec5SDimitry Andric } 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric NamedStreamMap::NamedStreamMap() : HashTraits(*this), OffsetIndexMap(1) {} 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric Error NamedStreamMap::load(BinaryStreamReader &Stream) { 490b57cec5SDimitry Andric uint32_t StringBufferSize; 500b57cec5SDimitry Andric if (auto EC = Stream.readInteger(StringBufferSize)) 510b57cec5SDimitry Andric return joinErrors(std::move(EC), 520b57cec5SDimitry Andric make_error<RawError>(raw_error_code::corrupt_file, 530b57cec5SDimitry Andric "Expected string buffer size")); 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric StringRef Buffer; 560b57cec5SDimitry Andric if (auto EC = Stream.readFixedString(Buffer, StringBufferSize)) 570b57cec5SDimitry Andric return EC; 580b57cec5SDimitry Andric NamesBuffer.assign(Buffer.begin(), Buffer.end()); 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric return OffsetIndexMap.load(Stream); 610b57cec5SDimitry Andric } 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric Error NamedStreamMap::commit(BinaryStreamWriter &Writer) const { 640b57cec5SDimitry Andric // The first field is the number of bytes of string data. 650b57cec5SDimitry Andric if (auto EC = Writer.writeInteger<uint32_t>(NamesBuffer.size())) 660b57cec5SDimitry Andric return EC; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric // Then the actual string data. 690b57cec5SDimitry Andric StringRef Data(NamesBuffer.data(), NamesBuffer.size()); 700b57cec5SDimitry Andric if (auto EC = Writer.writeFixedString(Data)) 710b57cec5SDimitry Andric return EC; 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric // And finally the Offset Index map. 740b57cec5SDimitry Andric if (auto EC = OffsetIndexMap.commit(Writer)) 750b57cec5SDimitry Andric return EC; 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric return Error::success(); 780b57cec5SDimitry Andric } 790b57cec5SDimitry Andric 800b57cec5SDimitry Andric uint32_t NamedStreamMap::calculateSerializedLength() const { 810b57cec5SDimitry Andric return sizeof(uint32_t) // String data size 820b57cec5SDimitry Andric + NamesBuffer.size() // String data 830b57cec5SDimitry Andric + OffsetIndexMap.calculateSerializedLength(); // Offset Index Map 840b57cec5SDimitry Andric } 850b57cec5SDimitry Andric 860b57cec5SDimitry Andric uint32_t NamedStreamMap::size() const { return OffsetIndexMap.size(); } 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric StringRef NamedStreamMap::getString(uint32_t Offset) const { 890b57cec5SDimitry Andric assert(NamesBuffer.size() > Offset); 900b57cec5SDimitry Andric return StringRef(NamesBuffer.data() + Offset); 910b57cec5SDimitry Andric } 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric uint32_t NamedStreamMap::hashString(uint32_t Offset) const { 940b57cec5SDimitry Andric return hashStringV1(getString(Offset)); 950b57cec5SDimitry Andric } 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric bool NamedStreamMap::get(StringRef Stream, uint32_t &StreamNo) const { 980b57cec5SDimitry Andric auto Iter = OffsetIndexMap.find_as(Stream, HashTraits); 990b57cec5SDimitry Andric if (Iter == OffsetIndexMap.end()) 1000b57cec5SDimitry Andric return false; 1010b57cec5SDimitry Andric StreamNo = (*Iter).second; 1020b57cec5SDimitry Andric return true; 1030b57cec5SDimitry Andric } 1040b57cec5SDimitry Andric 1050b57cec5SDimitry Andric StringMap<uint32_t> NamedStreamMap::entries() const { 1060b57cec5SDimitry Andric StringMap<uint32_t> Result; 1070b57cec5SDimitry Andric for (const auto &Entry : OffsetIndexMap) { 1080b57cec5SDimitry Andric StringRef Stream(NamesBuffer.data() + Entry.first); 1090b57cec5SDimitry Andric Result.try_emplace(Stream, Entry.second); 1100b57cec5SDimitry Andric } 1110b57cec5SDimitry Andric return Result; 1120b57cec5SDimitry Andric } 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric uint32_t NamedStreamMap::appendStringData(StringRef S) { 1150b57cec5SDimitry Andric uint32_t Offset = NamesBuffer.size(); 116*e8d8bef9SDimitry Andric llvm::append_range(NamesBuffer, S); 1170b57cec5SDimitry Andric NamesBuffer.push_back('\0'); 1180b57cec5SDimitry Andric return Offset; 1190b57cec5SDimitry Andric } 1200b57cec5SDimitry Andric 1210b57cec5SDimitry Andric void NamedStreamMap::set(StringRef Stream, uint32_t StreamNo) { 1220b57cec5SDimitry Andric OffsetIndexMap.set_as(Stream, support::ulittle32_t(StreamNo), HashTraits); 1230b57cec5SDimitry Andric } 124