Path: blob/main/contrib/llvm-project/clang/lib/Lex/HeaderMap.cpp
35232 views
//===--- HeaderMap.cpp - A file that acts like dir of symlinks ------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the HeaderMap interface.9//10//===----------------------------------------------------------------------===//1112#include "clang/Lex/HeaderMap.h"13#include "clang/Basic/CharInfo.h"14#include "clang/Basic/FileManager.h"15#include "clang/Lex/HeaderMapTypes.h"16#include "llvm/ADT/SmallString.h"17#include "llvm/Support/Compiler.h"18#include "llvm/Support/DataTypes.h"19#include "llvm/Support/Debug.h"20#include "llvm/Support/MathExtras.h"21#include "llvm/Support/MemoryBuffer.h"22#include "llvm/Support/SwapByteOrder.h"23#include "llvm/Support/SystemZ/zOSSupport.h"24#include <cstring>25#include <memory>26#include <optional>27using namespace clang;2829/// HashHMapKey - This is the 'well known' hash function required by the file30/// format, used to look up keys in the hash table. The hash table uses simple31/// linear probing based on this function.32static inline unsigned HashHMapKey(StringRef Str) {33unsigned Result = 0;34const char *S = Str.begin(), *End = Str.end();3536for (; S != End; S++)37Result += toLowercase(*S) * 13;38return Result;39}40414243//===----------------------------------------------------------------------===//44// Verification and Construction45//===----------------------------------------------------------------------===//4647/// HeaderMap::Create - This attempts to load the specified file as a header48/// map. If it doesn't look like a HeaderMap, it gives up and returns null.49/// If it looks like a HeaderMap but is obviously corrupted, it puts a reason50/// into the string error argument and returns null.51std::unique_ptr<HeaderMap> HeaderMap::Create(FileEntryRef FE, FileManager &FM) {52// If the file is too small to be a header map, ignore it.53unsigned FileSize = FE.getSize();54if (FileSize <= sizeof(HMapHeader)) return nullptr;5556auto FileBuffer = FM.getBufferForFile(FE);57if (!FileBuffer || !*FileBuffer)58return nullptr;59bool NeedsByteSwap;60if (!checkHeader(**FileBuffer, NeedsByteSwap))61return nullptr;62return std::unique_ptr<HeaderMap>(new HeaderMap(std::move(*FileBuffer), NeedsByteSwap));63}6465bool HeaderMapImpl::checkHeader(const llvm::MemoryBuffer &File,66bool &NeedsByteSwap) {67if (File.getBufferSize() <= sizeof(HMapHeader))68return false;69const char *FileStart = File.getBufferStart();7071// We know the file is at least as big as the header, check it now.72const HMapHeader *Header = reinterpret_cast<const HMapHeader*>(FileStart);7374// Sniff it to see if it's a headermap by checking the magic number and75// version.76if (Header->Magic == HMAP_HeaderMagicNumber &&77Header->Version == HMAP_HeaderVersion)78NeedsByteSwap = false;79else if (Header->Magic == llvm::byteswap<uint32_t>(HMAP_HeaderMagicNumber) &&80Header->Version == llvm::byteswap<uint16_t>(HMAP_HeaderVersion))81NeedsByteSwap = true; // Mixed endianness headermap.82else83return false; // Not a header map.8485if (Header->Reserved != 0)86return false;8788// Check the number of buckets. It should be a power of two, and there89// should be enough space in the file for all of them.90uint32_t NumBuckets =91NeedsByteSwap ? llvm::byteswap(Header->NumBuckets) : Header->NumBuckets;92if (!llvm::isPowerOf2_32(NumBuckets))93return false;94if (File.getBufferSize() <95sizeof(HMapHeader) + sizeof(HMapBucket) * NumBuckets)96return false;9798// Okay, everything looks good.99return true;100}101102//===----------------------------------------------------------------------===//103// Utility Methods104//===----------------------------------------------------------------------===//105106107/// getFileName - Return the filename of the headermap.108StringRef HeaderMapImpl::getFileName() const {109return FileBuffer->getBufferIdentifier();110}111112unsigned HeaderMapImpl::getEndianAdjustedWord(unsigned X) const {113if (!NeedsBSwap) return X;114return llvm::byteswap<uint32_t>(X);115}116117/// getHeader - Return a reference to the file header, in unbyte-swapped form.118/// This method cannot fail.119const HMapHeader &HeaderMapImpl::getHeader() const {120// We know the file is at least as big as the header. Return it.121return *reinterpret_cast<const HMapHeader*>(FileBuffer->getBufferStart());122}123124/// getBucket - Return the specified hash table bucket from the header map,125/// bswap'ing its fields as appropriate. If the bucket number is not valid,126/// this return a bucket with an empty key (0).127HMapBucket HeaderMapImpl::getBucket(unsigned BucketNo) const {128assert(FileBuffer->getBufferSize() >=129sizeof(HMapHeader) + sizeof(HMapBucket) * BucketNo &&130"Expected bucket to be in range");131132HMapBucket Result;133Result.Key = HMAP_EmptyBucketKey;134135const HMapBucket *BucketArray =136reinterpret_cast<const HMapBucket*>(FileBuffer->getBufferStart() +137sizeof(HMapHeader));138const HMapBucket *BucketPtr = BucketArray+BucketNo;139140// Load the values, bswapping as needed.141Result.Key = getEndianAdjustedWord(BucketPtr->Key);142Result.Prefix = getEndianAdjustedWord(BucketPtr->Prefix);143Result.Suffix = getEndianAdjustedWord(BucketPtr->Suffix);144return Result;145}146147std::optional<StringRef> HeaderMapImpl::getString(unsigned StrTabIdx) const {148// Add the start of the string table to the idx.149StrTabIdx += getEndianAdjustedWord(getHeader().StringsOffset);150151// Check for invalid index.152if (StrTabIdx >= FileBuffer->getBufferSize())153return std::nullopt;154155const char *Data = FileBuffer->getBufferStart() + StrTabIdx;156unsigned MaxLen = FileBuffer->getBufferSize() - StrTabIdx;157unsigned Len = strnlen(Data, MaxLen);158159// Check whether the buffer is null-terminated.160if (Len == MaxLen && Data[Len - 1])161return std::nullopt;162163return StringRef(Data, Len);164}165166//===----------------------------------------------------------------------===//167// The Main Drivers168//===----------------------------------------------------------------------===//169170/// dump - Print the contents of this headermap to stderr.171LLVM_DUMP_METHOD void HeaderMapImpl::dump() const {172const HMapHeader &Hdr = getHeader();173unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets);174175llvm::dbgs() << "Header Map " << getFileName() << ":\n " << NumBuckets176<< ", " << getEndianAdjustedWord(Hdr.NumEntries) << "\n";177178auto getStringOrInvalid = [this](unsigned Id) -> StringRef {179if (std::optional<StringRef> S = getString(Id))180return *S;181return "<invalid>";182};183184for (unsigned i = 0; i != NumBuckets; ++i) {185HMapBucket B = getBucket(i);186if (B.Key == HMAP_EmptyBucketKey) continue;187188StringRef Key = getStringOrInvalid(B.Key);189StringRef Prefix = getStringOrInvalid(B.Prefix);190StringRef Suffix = getStringOrInvalid(B.Suffix);191llvm::dbgs() << " " << i << ". " << Key << " -> '" << Prefix << "' '"192<< Suffix << "'\n";193}194}195196StringRef HeaderMapImpl::lookupFilename(StringRef Filename,197SmallVectorImpl<char> &DestPath) const {198const HMapHeader &Hdr = getHeader();199unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets);200201// Don't probe infinitely. This should be checked before constructing.202assert(llvm::isPowerOf2_32(NumBuckets) && "Expected power of 2");203204// Linearly probe the hash table.205for (unsigned Bucket = HashHMapKey(Filename);; ++Bucket) {206HMapBucket B = getBucket(Bucket & (NumBuckets-1));207if (B.Key == HMAP_EmptyBucketKey) return StringRef(); // Hash miss.208209// See if the key matches. If not, probe on.210std::optional<StringRef> Key = getString(B.Key);211if (LLVM_UNLIKELY(!Key))212continue;213if (!Filename.equals_insensitive(*Key))214continue;215216// If so, we have a match in the hash table. Construct the destination217// path.218std::optional<StringRef> Prefix = getString(B.Prefix);219std::optional<StringRef> Suffix = getString(B.Suffix);220221DestPath.clear();222if (LLVM_LIKELY(Prefix && Suffix)) {223DestPath.append(Prefix->begin(), Prefix->end());224DestPath.append(Suffix->begin(), Suffix->end());225}226return StringRef(DestPath.begin(), DestPath.size());227}228}229230StringRef HeaderMapImpl::reverseLookupFilename(StringRef DestPath) const {231if (!ReverseMap.empty())232return ReverseMap.lookup(DestPath);233234const HMapHeader &Hdr = getHeader();235unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets);236StringRef RetKey;237for (unsigned i = 0; i != NumBuckets; ++i) {238HMapBucket B = getBucket(i);239if (B.Key == HMAP_EmptyBucketKey)240continue;241242std::optional<StringRef> Key = getString(B.Key);243std::optional<StringRef> Prefix = getString(B.Prefix);244std::optional<StringRef> Suffix = getString(B.Suffix);245if (LLVM_LIKELY(Key && Prefix && Suffix)) {246SmallVector<char, 1024> Buf;247Buf.append(Prefix->begin(), Prefix->end());248Buf.append(Suffix->begin(), Suffix->end());249StringRef Value(Buf.begin(), Buf.size());250ReverseMap[Value] = *Key;251252if (DestPath == Value)253RetKey = *Key;254}255}256return RetKey;257}258259260