#include "clang/Lex/HeaderMap.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/FileManager.h"
#include "clang/Lex/HeaderMapTypes.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SwapByteOrder.h"
#include "llvm/Support/SystemZ/zOSSupport.h"
#include <cstring>
#include <memory>
#include <optional>
using namespace clang;
static inline unsigned HashHMapKey(StringRef Str) {
unsigned Result = 0;
const char *S = Str.begin(), *End = Str.end();
for (; S != End; S++)
Result += toLowercase(*S) * 13;
return Result;
}
std::unique_ptr<HeaderMap> HeaderMap::Create(FileEntryRef FE, FileManager &FM) {
unsigned FileSize = FE.getSize();
if (FileSize <= sizeof(HMapHeader)) return nullptr;
auto FileBuffer = FM.getBufferForFile(FE);
if (!FileBuffer || !*FileBuffer)
return nullptr;
bool NeedsByteSwap;
if (!checkHeader(**FileBuffer, NeedsByteSwap))
return nullptr;
return std::unique_ptr<HeaderMap>(new HeaderMap(std::move(*FileBuffer), NeedsByteSwap));
}
bool HeaderMapImpl::checkHeader(const llvm::MemoryBuffer &File,
bool &NeedsByteSwap) {
if (File.getBufferSize() <= sizeof(HMapHeader))
return false;
const char *FileStart = File.getBufferStart();
const HMapHeader *Header = reinterpret_cast<const HMapHeader*>(FileStart);
if (Header->Magic == HMAP_HeaderMagicNumber &&
Header->Version == HMAP_HeaderVersion)
NeedsByteSwap = false;
else if (Header->Magic == llvm::byteswap<uint32_t>(HMAP_HeaderMagicNumber) &&
Header->Version == llvm::byteswap<uint16_t>(HMAP_HeaderVersion))
NeedsByteSwap = true;
else
return false;
if (Header->Reserved != 0)
return false;
uint32_t NumBuckets =
NeedsByteSwap ? llvm::byteswap(Header->NumBuckets) : Header->NumBuckets;
if (!llvm::isPowerOf2_32(NumBuckets))
return false;
if (File.getBufferSize() <
sizeof(HMapHeader) + sizeof(HMapBucket) * NumBuckets)
return false;
return true;
}
StringRef HeaderMapImpl::getFileName() const {
return FileBuffer->getBufferIdentifier();
}
unsigned HeaderMapImpl::getEndianAdjustedWord(unsigned X) const {
if (!NeedsBSwap) return X;
return llvm::byteswap<uint32_t>(X);
}
const HMapHeader &HeaderMapImpl::getHeader() const {
return *reinterpret_cast<const HMapHeader*>(FileBuffer->getBufferStart());
}
HMapBucket HeaderMapImpl::getBucket(unsigned BucketNo) const {
assert(FileBuffer->getBufferSize() >=
sizeof(HMapHeader) + sizeof(HMapBucket) * BucketNo &&
"Expected bucket to be in range");
HMapBucket Result;
Result.Key = HMAP_EmptyBucketKey;
const HMapBucket *BucketArray =
reinterpret_cast<const HMapBucket*>(FileBuffer->getBufferStart() +
sizeof(HMapHeader));
const HMapBucket *BucketPtr = BucketArray+BucketNo;
Result.Key = getEndianAdjustedWord(BucketPtr->Key);
Result.Prefix = getEndianAdjustedWord(BucketPtr->Prefix);
Result.Suffix = getEndianAdjustedWord(BucketPtr->Suffix);
return Result;
}
std::optional<StringRef> HeaderMapImpl::getString(unsigned StrTabIdx) const {
StrTabIdx += getEndianAdjustedWord(getHeader().StringsOffset);
if (StrTabIdx >= FileBuffer->getBufferSize())
return std::nullopt;
const char *Data = FileBuffer->getBufferStart() + StrTabIdx;
unsigned MaxLen = FileBuffer->getBufferSize() - StrTabIdx;
unsigned Len = strnlen(Data, MaxLen);
if (Len == MaxLen && Data[Len - 1])
return std::nullopt;
return StringRef(Data, Len);
}
LLVM_DUMP_METHOD void HeaderMapImpl::dump() const {
const HMapHeader &Hdr = getHeader();
unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets);
llvm::dbgs() << "Header Map " << getFileName() << ":\n " << NumBuckets
<< ", " << getEndianAdjustedWord(Hdr.NumEntries) << "\n";
auto getStringOrInvalid = [this](unsigned Id) -> StringRef {
if (std::optional<StringRef> S = getString(Id))
return *S;
return "<invalid>";
};
for (unsigned i = 0; i != NumBuckets; ++i) {
HMapBucket B = getBucket(i);
if (B.Key == HMAP_EmptyBucketKey) continue;
StringRef Key = getStringOrInvalid(B.Key);
StringRef Prefix = getStringOrInvalid(B.Prefix);
StringRef Suffix = getStringOrInvalid(B.Suffix);
llvm::dbgs() << " " << i << ". " << Key << " -> '" << Prefix << "' '"
<< Suffix << "'\n";
}
}
StringRef HeaderMapImpl::lookupFilename(StringRef Filename,
SmallVectorImpl<char> &DestPath) const {
const HMapHeader &Hdr = getHeader();
unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets);
assert(llvm::isPowerOf2_32(NumBuckets) && "Expected power of 2");
for (unsigned Bucket = HashHMapKey(Filename);; ++Bucket) {
HMapBucket B = getBucket(Bucket & (NumBuckets-1));
if (B.Key == HMAP_EmptyBucketKey) return StringRef();
std::optional<StringRef> Key = getString(B.Key);
if (LLVM_UNLIKELY(!Key))
continue;
if (!Filename.equals_insensitive(*Key))
continue;
std::optional<StringRef> Prefix = getString(B.Prefix);
std::optional<StringRef> Suffix = getString(B.Suffix);
DestPath.clear();
if (LLVM_LIKELY(Prefix && Suffix)) {
DestPath.append(Prefix->begin(), Prefix->end());
DestPath.append(Suffix->begin(), Suffix->end());
}
return StringRef(DestPath.begin(), DestPath.size());
}
}
StringRef HeaderMapImpl::reverseLookupFilename(StringRef DestPath) const {
if (!ReverseMap.empty())
return ReverseMap.lookup(DestPath);
const HMapHeader &Hdr = getHeader();
unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets);
StringRef RetKey;
for (unsigned i = 0; i != NumBuckets; ++i) {
HMapBucket B = getBucket(i);
if (B.Key == HMAP_EmptyBucketKey)
continue;
std::optional<StringRef> Key = getString(B.Key);
std::optional<StringRef> Prefix = getString(B.Prefix);
std::optional<StringRef> Suffix = getString(B.Suffix);
if (LLVM_LIKELY(Key && Prefix && Suffix)) {
SmallVector<char, 1024> Buf;
Buf.append(Prefix->begin(), Prefix->end());
Buf.append(Suffix->begin(), Suffix->end());
StringRef Value(Buf.begin(), Buf.size());
ReverseMap[Value] = *Key;
if (DestPath == Value)
RetKey = *Key;
}
}
return RetKey;
}