18#include "clang/Tooling/CompilationDatabase.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/Support/Compiler.h"
21#include "llvm/Support/Compression.h"
22#include "llvm/Support/Endian.h"
23#include "llvm/Support/Error.h"
24#include "llvm/Support/raw_ostream.h"
43 const char *Begin, *End;
47 Reader(llvm::StringRef Data) : Begin(Data.begin()), End(Data.end()) {}
50 bool err()
const {
return Err; }
52 bool eof()
const {
return Begin == End || Err; }
54 llvm::StringRef rest()
const {
return llvm::StringRef(Begin, End - Begin); }
57 if (LLVM_UNLIKELY(Begin == End)) {
64 uint32_t consume32() {
65 if (LLVM_UNLIKELY(Begin + 4 > End)) {
69 auto Ret = llvm::support::endian::read32le(Begin);
74 llvm::StringRef
consume(
int N) {
75 if (LLVM_UNLIKELY(Begin + N > End)) {
77 return llvm::StringRef();
79 llvm::StringRef Ret(Begin, N);
84 uint32_t consumeVar() {
85 constexpr static uint8_t More = 1 << 7;
89 uint32_t
B = consume8();
90 if (LLVM_LIKELY(!(B & More)))
92 uint32_t Val =
B & ~More;
93 for (
int Shift = 7;
B & More && Shift < 32; Shift += 7) {
96 assert((Shift != 28 || B == (B & 0x0f)) &&
"Invalid varint encoding");
97 Val |= (
B & ~More) << Shift;
102 llvm::StringRef consumeString(llvm::ArrayRef<llvm::StringRef> Strings) {
103 auto StringIndex = consumeVar();
104 if (LLVM_UNLIKELY(StringIndex >= Strings.size())) {
106 return llvm::StringRef();
108 return Strings[StringIndex];
119 template <
typename T> [[nodiscard]]
bool consumeSize(T &Container) {
120 auto Size = consumeVar();
122 if (Size > (
size_t)(End - Begin)) {
126 Container.resize(Size);
131void write32(uint32_t I, llvm::raw_ostream &OS) {
133 llvm::support::endian::write32le(Buf, I);
134 OS.write(Buf,
sizeof(Buf));
137void writeVar(uint32_t I, llvm::raw_ostream &OS) {
138 constexpr static uint8_t More = 1 << 7;
139 if (LLVM_LIKELY(I < 1 << 7)) {
167class StringTableOut {
168 llvm::DenseSet<llvm::StringRef> Unique;
169 std::vector<llvm::StringRef> Sorted;
171 llvm::DenseMap<std::pair<const char *, size_t>,
unsigned> Index;
180 void intern(llvm::StringRef &S) { S = *Unique.insert(S).first; };
182 void finalize(llvm::raw_ostream &OS) {
183 Sorted = {Unique.begin(), Unique.end()};
185 for (
unsigned I = 0; I < Sorted.size(); ++I)
186 Index.try_emplace({Sorted[I].data(), Sorted[I].size()}, I);
188 std::string RawTable;
189 for (llvm::StringRef S : Sorted) {
190 RawTable.append(std::string(S));
191 RawTable.push_back(0);
193 if (llvm::compression::zlib::isAvailable()) {
194 llvm::SmallVector<uint8_t, 0> Compressed;
195 llvm::compression::zlib::compress(llvm::arrayRefFromStringRef(RawTable),
197 write32(RawTable.size(), OS);
198 OS << llvm::toStringRef(Compressed);
205 unsigned index(llvm::StringRef S)
const {
206 assert(!Sorted.empty() &&
"table not finalized");
207 assert(Index.count({S.data(), S.size()}) &&
"string not interned");
208 return Index.find({S.data(), S.size()})->second;
212struct StringTableIn {
213 llvm::BumpPtrAllocator Arena;
214 std::vector<llvm::StringRef> Strings;
217llvm::Expected<StringTableIn> readStringTable(llvm::StringRef Data) {
219 size_t UncompressedSize = R.consume32();
221 return error(
"Truncated string table");
223 llvm::StringRef Uncompressed;
224 llvm::SmallVector<uint8_t, 0> UncompressedStorage;
225 if (UncompressedSize == 0)
226 Uncompressed = R.rest();
227 else if (llvm::compression::zlib::isAvailable()) {
232 constexpr int MaxCompressionRatio = 1032;
233 if (UncompressedSize / MaxCompressionRatio > R.rest().size())
234 return error(
"Bad stri table: uncompress {0} -> {1} bytes is implausible",
235 R.rest().size(), UncompressedSize);
237 if (llvm::Error E = llvm::compression::zlib::decompress(
238 llvm::arrayRefFromStringRef(R.rest()), UncompressedStorage,
241 Uncompressed = toStringRef(UncompressedStorage);
243 return error(
"Compressed string table, but zlib is unavailable");
246 llvm::StringSaver Saver(Table.Arena);
247 R = Reader(Uncompressed);
248 for (Reader R(Uncompressed); !R.eof();) {
249 auto Len = R.rest().find(0);
250 if (Len == llvm::StringRef::npos)
251 return error(
"Bad string table: not null terminated");
252 Table.Strings.push_back(Saver.save(R.consume(Len)));
256 return error(
"Truncated string table");
257 return std::move(Table);
266void writeLocation(
const SymbolLocation &Loc,
const StringTableOut &Strings,
267 llvm::raw_ostream &OS) {
268 writeVar(Strings.index(Loc.FileURI), OS);
269 for (
const auto &Endpoint : {Loc.Start, Loc.End}) {
270 writeVar(Endpoint.line(), OS);
271 writeVar(Endpoint.column(), OS);
276 llvm::ArrayRef<llvm::StringRef> Strings) {
278 Loc.
FileURI = Data.consumeString(Strings).data();
279 for (
auto *Endpoint : {&Loc.Start, &Loc.End}) {
280 Endpoint->setLine(Data.consumeVar());
281 Endpoint->setColumn(Data.consumeVar());
287 llvm::ArrayRef<llvm::StringRef> Strings) {
290 IGN.URI = Data.consumeString(Strings);
291 llvm::StringRef Digest = Data.consume(IGN.Digest.size());
292 std::copy(Digest.bytes_begin(), Digest.bytes_end(), IGN.Digest.begin());
293 if (!Data.consumeSize(IGN.DirectIncludes))
295 for (llvm::StringRef &Include : IGN.DirectIncludes)
296 Include = Data.consumeString(Strings);
301 const StringTableOut &Strings,
302 llvm::raw_ostream &OS) {
303 OS.write(
static_cast<uint8_t
>(IGN.Flags));
304 writeVar(Strings.index(IGN.URI), OS);
305 llvm::StringRef Hash(
reinterpret_cast<const char *
>(IGN.Digest.data()),
308 writeVar(IGN.DirectIncludes.size(), OS);
309 for (llvm::StringRef Include : IGN.DirectIncludes)
310 writeVar(Strings.index(Include), OS);
313void writeSymbol(
const Symbol &Sym,
const StringTableOut &Strings,
314 llvm::raw_ostream &OS) {
317 OS.write(
static_cast<uint8_t
>(Sym.SymInfo.Kind));
318 OS.write(
static_cast<uint8_t
>(Sym.SymInfo.Lang));
319 writeVar(Strings.index(Sym.Name), OS);
320 writeVar(Strings.index(Sym.Scope), OS);
321 writeVar(Strings.index(Sym.TemplateSpecializationArgs), OS);
322 writeLocation(Sym.Definition, Strings, OS);
323 writeLocation(Sym.CanonicalDeclaration, Strings, OS);
324 writeVar(Sym.References, OS);
325 OS.write(
static_cast<uint8_t
>(Sym.Flags));
326 writeVar(Strings.index(Sym.Signature), OS);
327 writeVar(Strings.index(Sym.CompletionSnippetSuffix), OS);
328 writeVar(Strings.index(Sym.Documentation), OS);
329 writeVar(Strings.index(Sym.ReturnType), OS);
330 writeVar(Strings.index(Sym.Type), OS);
331 writeVar(Sym.Tags, OS);
334 writeVar(Strings.index(Include.IncludeHeader), OS);
335 writeVar((Include.References << 2) | Include.SupportedDirectives, OS);
337 writeVar(Sym.IncludeHeaders.size(), OS);
338 for (
const auto &Include : Sym.IncludeHeaders)
339 WriteInclude(Include);
342Symbol readSymbol(Reader &Data, llvm::ArrayRef<llvm::StringRef> Strings,
345 Sym.ID = Data.consumeID();
346 Sym.SymInfo.Kind =
static_cast<index::SymbolKind
>(Data.consume8());
347 Sym.SymInfo.Lang =
static_cast<index::SymbolLanguage
>(Data.consume8());
348 Sym.Name = Data.consumeString(Strings);
349 Sym.Scope = Data.consumeString(Strings);
350 Sym.TemplateSpecializationArgs = Data.consumeString(Strings);
351 Sym.Definition = readLocation(Data, Strings);
352 Sym.CanonicalDeclaration = readLocation(Data, Strings);
353 Sym.References = Data.consumeVar();
356 Sym.Signature = Data.consumeString(Strings);
357 Sym.CompletionSnippetSuffix = Data.consumeString(Strings);
358 Sym.Documentation = Data.consumeString(Strings);
359 Sym.ReturnType = Data.consumeString(Strings);
360 Sym.Type = Data.consumeString(Strings);
361 Sym.Tags = Data.consumeVar();
362 if (!Data.consumeSize(Sym.IncludeHeaders))
364 for (
auto &I : Sym.IncludeHeaders) {
365 I.IncludeHeader = Data.consumeString(Strings);
366 uint32_t RefsWithDirectives = Data.consumeVar();
367 I.References = RefsWithDirectives >> 2;
368 I.SupportedDirectives = RefsWithDirectives & 0x3;
380void writeRefs(
const SymbolID &ID, llvm::ArrayRef<Ref> Refs,
381 const StringTableOut &Strings, llvm::raw_ostream &OS) {
383 writeVar(Refs.size(), OS);
384 for (
const auto &
Ref : Refs) {
385 OS.write(
static_cast<unsigned char>(
Ref.
Kind));
391std::pair<SymbolID, std::vector<Ref>>
392readRefs(Reader &Data, llvm::ArrayRef<llvm::StringRef> Strings) {
393 std::pair<SymbolID, std::vector<Ref>> Result;
394 Result.first = Data.consumeID();
395 if (!Data.consumeSize(Result.second))
397 for (
auto &
Ref : Result.second) {
412void writeRelation(
const Relation &R, llvm::raw_ostream &OS) {
413 OS << R.Subject.raw();
414 OS.write(
static_cast<uint8_t
>(R.Predicate));
415 OS << R.Object.raw();
418Relation readRelation(Reader &Data) {
419 SymbolID Subject = Data.consumeID();
422 return {Subject, Predicate,
Object};
425struct InternedCompileCommand {
426 llvm::StringRef Directory;
427 std::vector<llvm::StringRef> CommandLine;
430void writeCompileCommand(
const InternedCompileCommand &Cmd,
431 const StringTableOut &Strings,
432 llvm::raw_ostream &CmdOS) {
433 writeVar(Strings.index(Cmd.Directory), CmdOS);
434 writeVar(Cmd.CommandLine.size(), CmdOS);
435 for (llvm::StringRef C : Cmd.CommandLine)
436 writeVar(Strings.index(C), CmdOS);
439InternedCompileCommand
440readCompileCommand(Reader CmdReader, llvm::ArrayRef<llvm::StringRef> Strings) {
441 InternedCompileCommand Cmd;
442 Cmd.Directory = CmdReader.consumeString(Strings);
443 if (!CmdReader.consumeSize(Cmd.CommandLine))
445 for (llvm::StringRef &C : Cmd.CommandLine)
446 C = CmdReader.consumeString(Strings);
462constexpr static uint32_t Version = 21;
464llvm::Expected<IndexFileIn> readRIFF(llvm::StringRef Data,
468 return RIFF.takeError();
471 llvm::StringMap<llvm::StringRef> Chunks;
472 for (
const auto &Chunk :
RIFF->Chunks)
473 Chunks.try_emplace(llvm::StringRef(Chunk.ID.data(), Chunk.ID.size()),
476 if (!Chunks.count(
"meta"))
477 return error(
"missing meta chunk");
478 Reader Meta(Chunks.lookup(
"meta"));
479 auto SeenVersion = Meta.consume32();
480 if (SeenVersion != Version)
481 return error(
"wrong version: want {0}, got {1}", Version, SeenVersion);
484 for (llvm::StringRef RequiredChunk : {
"stri"})
485 if (!Chunks.count(RequiredChunk))
486 return error(
"missing required chunk {0}", RequiredChunk);
488 auto Strings = readStringTable(Chunks.lookup(
"stri"));
490 return Strings.takeError();
493 if (Chunks.count(
"srcs")) {
494 Reader SrcsReader(Chunks.lookup(
"srcs"));
495 Result.Sources.emplace();
496 while (!SrcsReader.eof()) {
497 auto IGN = readIncludeGraphNode(SrcsReader, Strings->Strings);
498 auto Entry = Result.Sources->try_emplace(IGN.URI).first;
499 Entry->getValue() = std::move(IGN);
502 Entry->getValue().URI = Entry->getKey();
503 for (
auto &Include : Entry->getValue().DirectIncludes)
504 Include = Result.Sources->try_emplace(Include).first->getKey();
506 if (SrcsReader.err())
507 return error(
"malformed or truncated include uri");
510 if (Chunks.count(
"symb")) {
511 Reader SymbolReader(Chunks.lookup(
"symb"));
513 while (!SymbolReader.eof())
514 Symbols.insert(readSymbol(SymbolReader, Strings->Strings, Origin));
515 if (SymbolReader.err())
516 return error(
"malformed or truncated symbol");
517 Result.Symbols = std::move(
Symbols).build();
519 if (Chunks.count(
"refs")) {
520 Reader RefsReader(Chunks.lookup(
"refs"));
522 while (!RefsReader.eof()) {
523 auto RefsBundle = readRefs(RefsReader, Strings->Strings);
524 for (
const auto &
Ref : RefsBundle.second)
525 Refs.insert(RefsBundle.first,
Ref);
527 if (RefsReader.err())
528 return error(
"malformed or truncated refs");
529 Result.Refs = std::move(Refs).build();
531 if (Chunks.count(
"rela")) {
532 Reader RelationsReader(Chunks.lookup(
"rela"));
534 while (!RelationsReader.eof())
535 Relations.
insert(readRelation(RelationsReader));
536 if (RelationsReader.err())
537 return error(
"malformed or truncated relations");
538 Result.Relations = std::move(Relations).build();
540 if (Chunks.count(
"cmdl")) {
541 Reader CmdReader(Chunks.lookup(
"cmdl"));
542 InternedCompileCommand Cmd =
543 readCompileCommand(CmdReader, Strings->Strings);
545 return error(
"malformed or truncated commandline section");
546 Result.Cmd.emplace();
547 Result.Cmd->Directory = std::string(Cmd.Directory);
548 Result.Cmd->CommandLine.reserve(Cmd.CommandLine.size());
549 for (llvm::StringRef C : Cmd.CommandLine)
550 Result.Cmd->CommandLine.emplace_back(C);
552 return std::move(Result);
555template <
class Callback>
558 for (llvm::StringRef &Include : IGN.DirectIncludes)
562void writeRIFF(
const IndexFileOut &Data, llvm::raw_ostream &OS) {
563 assert(Data.Symbols &&
"An index file without symbols makes no sense!");
567 llvm::SmallString<4> Meta;
569 llvm::raw_svector_ostream MetaOS(Meta);
570 write32(Version, MetaOS);
574 StringTableOut Strings;
576 for (
const auto &Sym : *Data.Symbols) {
579 [&](llvm::StringRef &S) { Strings.intern(S); });
581 std::vector<IncludeGraphNode> Sources;
583 for (
const auto &Source : *Data.Sources) {
584 Sources.push_back(Source.getValue());
586 [&](llvm::StringRef &S) { Strings.intern(S); });
589 std::vector<std::pair<SymbolID, std::vector<Ref>>> Refs;
591 for (
const auto &Sym : *Data.Refs) {
592 Refs.emplace_back(Sym);
593 for (
auto &
Ref : Refs.back().second) {
595 Strings.intern(
File);
601 std::vector<Relation> Relations;
602 if (Data.Relations) {
603 for (
const auto &
Relation : *Data.Relations) {
609 InternedCompileCommand InternedCmd;
611 InternedCmd.CommandLine.reserve(Data.Cmd->CommandLine.size());
612 InternedCmd.Directory = Data.Cmd->Directory;
613 Strings.intern(InternedCmd.Directory);
614 for (llvm::StringRef C : Data.Cmd->CommandLine) {
615 InternedCmd.CommandLine.emplace_back(C);
616 Strings.intern(InternedCmd.CommandLine.back());
620 std::string StringSection;
622 llvm::raw_string_ostream StringOS(StringSection);
623 Strings.finalize(StringOS);
627 std::string SymbolSection;
629 llvm::raw_string_ostream SymbolOS(SymbolSection);
630 for (
const auto &Sym :
Symbols)
631 writeSymbol(Sym, Strings, SymbolOS);
635 std::string RefsSection;
638 llvm::raw_string_ostream RefsOS(RefsSection);
639 for (
const auto &Sym : Refs)
640 writeRefs(Sym.first, Sym.second, Strings, RefsOS);
645 std::string RelationSection;
646 if (Data.Relations) {
648 llvm::raw_string_ostream RelationOS{RelationSection};
649 for (
const auto &
Relation : Relations)
650 writeRelation(
Relation, RelationOS);
655 std::string SrcsSection;
658 llvm::raw_string_ostream SrcsOS(SrcsSection);
659 for (
const auto &SF : Sources)
660 writeIncludeGraphNode(SF, Strings, SrcsOS);
665 std::string CmdlSection;
668 llvm::raw_string_ostream CmdOS(CmdlSection);
669 writeCompileCommand(InternedCmd, Strings, CmdOS);
697 if (Data.starts_with(
"RIFF")) {
698 return readRIFF(Data, Origin);
700 if (
auto YAMLContents =
readYAML(Data, Origin)) {
701 return std::move(*YAMLContents);
703 return error(
"Not a RIFF file and failed to parse as YAML: {0}",
704 YAMLContents.takeError());
708std::unique_ptr<SymbolIndex>
loadIndex(llvm::StringRef SymbolFilename,
710 bool SupportContainedRefs) {
712 auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename);
714 elog(
"Can't open {0}: {1}", SymbolFilename, Buffer.getError().message());
723 if (
auto I =
readIndexFile(Buffer->get()->getBuffer(), Origin)) {
725 Symbols = std::move(*I->Symbols);
727 Refs = std::move(*I->Refs);
729 Relations = std::move(*I->Relations);
731 elog(
"Bad index file: {0}", I.takeError());
736 size_t NumSym =
Symbols.size();
737 size_t NumRefs = Refs.numRefs();
738 size_t NumRelations = Relations.size();
743 std::move(Relations), SupportContainedRefs)
745 std::move(Relations));
746 vlog(
"Loaded {0} from {1} with estimated memory usage {2} bytes\n"
747 " - number of symbols: {3}\n"
748 " - number of refs: {4}\n"
749 " - number of relations: {5}",
750 UseDex ?
"Dex" :
"MemIndex", SymbolFilename,
751 Index->estimateMemoryUsage(), NumSym, NumRefs, NumRelations);
This defines Dex - a symbol index implementation based on query iterators over symbol tokens,...
static std::unique_ptr< SymbolIndex > build(SymbolSlab Symbols, RefSlab Refs, RelationSlab Relations)
Builds an index from slabs. The index takes ownership of the data.
RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
An efficient structure of storing large set of symbol references in memory.
RelationSlab::Builder is a mutable container that can 'freeze' to RelationSlab.
void insert(const Relation &R)
Adds a relation to the slab.
static constexpr size_t RawSize
llvm::StringRef raw() const
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
An immutable symbol container that stores a set of symbols.
static std::unique_ptr< SymbolIndex > build(SymbolSlab, RefSlab, RelationSlab, bool SupportContainedRefs)
Builds an index from slabs. The index takes ownership of the slab.
Records an event whose duration is the lifetime of the Span object.
std::vector< std::pair< DocID, float > > consume(Iterator &It)
Advances the iterator until it is exhausted.
llvm::Expected< File > readFile(llvm::StringRef Stream)
constexpr FourCC fourCC(const char(&Literal)[5])
constexpr llvm::StringRef fourCCStr(const FourCC &Data)
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
void visitStrings(Symbol &S, const Callback &CB)
Invokes Callback with each StringRef& contained in the Symbol.
llvm::Expected< IndexFileIn > readIndexFile(llvm::StringRef Data, SymbolOrigin Origin)
llvm::Expected< IndexFileIn > readYAML(llvm::StringRef, SymbolOrigin Origin)
void vlog(const char *Fmt, Ts &&... Vals)
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
RefKind
Describes the kind of a cross-reference.
void writeYAML(const IndexFileOut &, llvm::raw_ostream &)
void elog(const char *Fmt, Ts &&... Vals)
std::unique_ptr< SymbolIndex > loadIndex(llvm::StringRef SymbolFilename, SymbolOrigin Origin, bool UseDex, bool SupportContainedRefs)
std::array< uint8_t, 20 > SymbolID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Represents a symbol occurrence in the source file.
SymbolID Container
The ID of the symbol whose definition contains this reference.
SymbolLocation Location
The source location where the symbol is named.
Represents a relation between two symbols.
Ensure we have enough bits to represent all SymbolTag values.