#include "clang/Lex/HeaderMap.h"
#include "clang/Lex/HeaderMapTypes.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/FileManager.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SwapByteOrder.h"
#include "llvm/Support/Debug.h"
#include <cstring>
#include <memory>
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;
}
const HeaderMap *HeaderMap::Create(const FileEntry *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 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_32(HMAP_HeaderMagicNumber) &&
Header->Version == llvm::ByteSwap_16(HMAP_HeaderVersion))
NeedsByteSwap = true; else
return false;
if (Header->Reserved != 0)
return false;
uint32_t NumBuckets = NeedsByteSwap
? llvm::sys::getSwappedBytes(Header->NumBuckets)
: Header->NumBuckets;
if (!llvm::isPowerOf2_32(NumBuckets))
return false;
if (File.getBufferSize() <
sizeof(HMapHeader) + sizeof(HMapBucket) * NumBuckets)
return false;
return true;
}
const char *HeaderMapImpl::getFileName() const {
return FileBuffer->getBufferIdentifier();
}
unsigned HeaderMapImpl::getEndianAdjustedWord(unsigned X) const {
if (!NeedsBSwap) return X;
return llvm::ByteSwap_32(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;
}
Optional<StringRef> HeaderMapImpl::getString(unsigned StrTabIdx) const {
StrTabIdx += getEndianAdjustedWord(getHeader().StringsOffset);
if (StrTabIdx >= FileBuffer->getBufferSize())
return None;
const char *Data = FileBuffer->getBufferStart() + StrTabIdx;
unsigned MaxLen = FileBuffer->getBufferSize() - StrTabIdx;
unsigned Len = strnlen(Data, MaxLen);
if (Len == MaxLen && Data[Len - 1])
return None;
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 (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";
}
}
const FileEntry *HeaderMap::LookupFile(
StringRef Filename, FileManager &FM) const {
SmallString<1024> Path;
StringRef Dest = HeaderMapImpl::lookupFilename(Filename, Path);
if (Dest.empty())
return nullptr;
return FM.getFile(Dest);
}
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();
Optional<StringRef> Key = getString(B.Key);
if (LLVM_UNLIKELY(!Key))
continue;
if (!Filename.equals_lower(*Key))
continue;
Optional<StringRef> Prefix = getString(B.Prefix);
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());
}
}