clang-tools 24.0.0git
Serialization.cpp
Go to the documentation of this file.
1//===-- Serialization.cpp - Binary serialization of index data ------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Serialization.h"
10#include "Headers.h"
11#include "RIFF.h"
12#include "index/MemIndex.h"
14#include "index/SymbolOrigin.h"
15#include "index/dex/Dex.h"
16#include "support/Logger.h"
17#include "support/Trace.h"
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"
25#include <cstdint>
26#include <vector>
27
28namespace clang {
29namespace clangd {
30namespace {
31
32// IO PRIMITIVES
33// We use little-endian 32 bit ints, sometimes with variable-length encoding.
34//
35// Variable-length int encoding (varint) uses the bottom 7 bits of each byte
36// to encode the number, and the top bit to indicate whether more bytes follow.
37// e.g. 9a 2f means [0x1a and keep reading, 0x2f and stop].
38// This represents 0x1a | 0x2f<<7 = 6042.
39// A 32-bit integer takes 1-5 bytes to encode; small numbers are more compact.
40
41// Reads binary data from a StringRef, and keeps track of position.
42class Reader {
43 const char *Begin, *End;
44 bool Err = false;
45
46public:
47 Reader(llvm::StringRef Data) : Begin(Data.begin()), End(Data.end()) {}
48 // The "error" bit is set by reading past EOF or reading invalid data.
49 // When in an error state, reads may return zero values: callers should check.
50 bool err() const { return Err; }
51 // Did we read all the data, or encounter an error?
52 bool eof() const { return Begin == End || Err; }
53 // All the data we didn't read yet.
54 llvm::StringRef rest() const { return llvm::StringRef(Begin, End - Begin); }
55
56 uint8_t consume8() {
57 if (LLVM_UNLIKELY(Begin == End)) {
58 Err = true;
59 return 0;
60 }
61 return *Begin++;
62 }
63
64 uint32_t consume32() {
65 if (LLVM_UNLIKELY(Begin + 4 > End)) {
66 Err = true;
67 return 0;
68 }
69 auto Ret = llvm::support::endian::read32le(Begin);
70 Begin += 4;
71 return Ret;
72 }
73
74 llvm::StringRef consume(int N) {
75 if (LLVM_UNLIKELY(Begin + N > End)) {
76 Err = true;
77 return llvm::StringRef();
78 }
79 llvm::StringRef Ret(Begin, N);
80 Begin += N;
81 return Ret;
82 }
83
84 uint32_t consumeVar() {
85 constexpr static uint8_t More = 1 << 7;
86
87 // Use a 32 bit unsigned here to prevent promotion to signed int (unless int
88 // is wider than 32 bits).
89 uint32_t B = consume8();
90 if (LLVM_LIKELY(!(B & More)))
91 return B;
92 uint32_t Val = B & ~More;
93 for (int Shift = 7; B & More && Shift < 32; Shift += 7) {
94 B = consume8();
95 // 5th byte of a varint can only have lowest 4 bits set.
96 assert((Shift != 28 || B == (B & 0x0f)) && "Invalid varint encoding");
97 Val |= (B & ~More) << Shift;
98 }
99 return Val;
100 }
101
102 llvm::StringRef consumeString(llvm::ArrayRef<llvm::StringRef> Strings) {
103 auto StringIndex = consumeVar();
104 if (LLVM_UNLIKELY(StringIndex >= Strings.size())) {
105 Err = true;
106 return llvm::StringRef();
107 }
108 return Strings[StringIndex];
109 }
110
111 SymbolID consumeID() {
112 llvm::StringRef Raw = consume(SymbolID::RawSize); // short if truncated.
113 return LLVM_UNLIKELY(err()) ? SymbolID() : SymbolID::fromRaw(Raw);
114 }
115
116 // Read a varint (as consumeVar) and resize the container accordingly.
117 // If the size is invalid, return false and mark an error.
118 // (The caller should abort in this case).
119 template <typename T> [[nodiscard]] bool consumeSize(T &Container) {
120 auto Size = consumeVar();
121 // Conservatively assume each element is at least one byte.
122 if (Size > (size_t)(End - Begin)) {
123 Err = true;
124 return false;
125 }
126 Container.resize(Size);
127 return true;
128 }
129};
130
131void write32(uint32_t I, llvm::raw_ostream &OS) {
132 char Buf[4];
133 llvm::support::endian::write32le(Buf, I);
134 OS.write(Buf, sizeof(Buf));
135}
136
137void writeVar(uint32_t I, llvm::raw_ostream &OS) {
138 constexpr static uint8_t More = 1 << 7;
139 if (LLVM_LIKELY(I < 1 << 7)) {
140 OS.write(I);
141 return;
142 }
143 for (;;) {
144 OS.write(I | More);
145 I >>= 7;
146 if (I < 1 << 7) {
147 OS.write(I);
148 return;
149 }
150 }
151}
152
153// STRING TABLE ENCODING
154// Index data has many string fields, and many strings are identical.
155// We store each string once, and refer to them by index.
156//
157// The string table's format is:
158// - UncompressedSize : uint32 (or 0 for no compression)
159// - CompressedData : byte[CompressedSize]
160//
161// CompressedData is a zlib-compressed byte[UncompressedSize].
162// It contains a sequence of null-terminated strings, e.g. "foo\0bar\0".
163// These are sorted to improve compression.
164
165// Maps each string to a canonical representation.
166// Strings remain owned externally (e.g. by SymbolSlab).
167class StringTableOut {
168 llvm::DenseSet<llvm::StringRef> Unique;
169 std::vector<llvm::StringRef> Sorted;
170 // Since strings are interned, look up can be by pointer.
171 llvm::DenseMap<std::pair<const char *, size_t>, unsigned> Index;
172
173public:
174 StringTableOut() {
175 // Ensure there's at least one string in the table.
176 // Table size zero is reserved to indicate no compression.
177 Unique.insert("");
178 }
179 // Add a string to the table. Overwrites S if an identical string exists.
180 void intern(llvm::StringRef &S) { S = *Unique.insert(S).first; };
181 // Finalize the table and write it to OS. No more strings may be added.
182 void finalize(llvm::raw_ostream &OS) {
183 Sorted = {Unique.begin(), Unique.end()};
184 llvm::sort(Sorted);
185 for (unsigned I = 0; I < Sorted.size(); ++I)
186 Index.try_emplace({Sorted[I].data(), Sorted[I].size()}, I);
187
188 std::string RawTable;
189 for (llvm::StringRef S : Sorted) {
190 RawTable.append(std::string(S));
191 RawTable.push_back(0);
192 }
193 if (llvm::compression::zlib::isAvailable()) {
194 llvm::SmallVector<uint8_t, 0> Compressed;
195 llvm::compression::zlib::compress(llvm::arrayRefFromStringRef(RawTable),
196 Compressed);
197 write32(RawTable.size(), OS);
198 OS << llvm::toStringRef(Compressed);
199 } else {
200 write32(0, OS); // No compression.
201 OS << RawTable;
202 }
203 }
204 // Get the ID of an string, which must be interned. Table must be finalized.
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;
209 }
210};
211
212struct StringTableIn {
213 llvm::BumpPtrAllocator Arena;
214 std::vector<llvm::StringRef> Strings;
215};
216
217llvm::Expected<StringTableIn> readStringTable(llvm::StringRef Data) {
218 Reader R(Data);
219 size_t UncompressedSize = R.consume32();
220 if (R.err())
221 return error("Truncated string table");
222
223 llvm::StringRef Uncompressed;
224 llvm::SmallVector<uint8_t, 0> UncompressedStorage;
225 if (UncompressedSize == 0) // No compression
226 Uncompressed = R.rest();
227 else if (llvm::compression::zlib::isAvailable()) {
228 // Don't allocate a massive buffer if UncompressedSize was corrupted
229 // This is effective for sharded index, but not big monolithic ones, as
230 // once compressed size reaches 4MB nothing can be ruled out.
231 // Theoretical max ratio from https://zlib.net/zlib_tech.html
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);
236
237 if (llvm::Error E = llvm::compression::zlib::decompress(
238 llvm::arrayRefFromStringRef(R.rest()), UncompressedStorage,
239 UncompressedSize))
240 return std::move(E);
241 Uncompressed = toStringRef(UncompressedStorage);
242 } else
243 return error("Compressed string table, but zlib is unavailable");
244
245 StringTableIn Table;
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)));
253 R.consume8();
254 }
255 if (R.err())
256 return error("Truncated string table");
257 return std::move(Table);
258}
259
260// SYMBOL ENCODING
261// Each field of clangd::Symbol is encoded in turn (see implementation).
262// - StringRef fields encode as varint (index into the string table)
263// - enums encode as the underlying type
264// - most numbers encode as varint
265
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);
272 }
273}
274
275SymbolLocation readLocation(Reader &Data,
276 llvm::ArrayRef<llvm::StringRef> Strings) {
277 SymbolLocation Loc;
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());
282 }
283 return Loc;
284}
285
286IncludeGraphNode readIncludeGraphNode(Reader &Data,
287 llvm::ArrayRef<llvm::StringRef> Strings) {
289 IGN.Flags = static_cast<IncludeGraphNode::SourceFlag>(Data.consume8());
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))
294 return IGN;
295 for (llvm::StringRef &Include : IGN.DirectIncludes)
296 Include = Data.consumeString(Strings);
297 return IGN;
298}
299
300void writeIncludeGraphNode(const IncludeGraphNode &IGN,
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()),
306 IGN.Digest.size());
307 OS << Hash;
308 writeVar(IGN.DirectIncludes.size(), OS);
309 for (llvm::StringRef Include : IGN.DirectIncludes)
310 writeVar(Strings.index(Include), OS);
311}
312
313void writeSymbol(const Symbol &Sym, const StringTableOut &Strings,
314 llvm::raw_ostream &OS) {
315 OS << Sym.ID.raw(); // TODO: once we start writing xrefs and posting lists,
316 // symbol IDs should probably be in a string table.
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);
332
333 auto WriteInclude = [&](const Symbol::IncludeHeaderWithReferences &Include) {
334 writeVar(Strings.index(Include.IncludeHeader), OS);
335 writeVar((Include.References << 2) | Include.SupportedDirectives, OS);
336 };
337 writeVar(Sym.IncludeHeaders.size(), OS);
338 for (const auto &Include : Sym.IncludeHeaders)
339 WriteInclude(Include);
340}
341
342Symbol readSymbol(Reader &Data, llvm::ArrayRef<llvm::StringRef> Strings,
343 SymbolOrigin Origin) {
344 Symbol Sym;
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();
354 Sym.Flags = static_cast<Symbol::SymbolFlag>(Data.consume8());
355 Sym.Origin = Origin;
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))
363 return Sym;
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;
369 }
370 return Sym;
371}
372
373// REFS ENCODING
374// A refs section has data grouped by Symbol. Each symbol has:
375// - SymbolID: 8 bytes
376// - NumRefs: varint
377// - Ref[NumRefs]
378// Fields of Ref are encoded in turn, see implementation.
379
380void writeRefs(const SymbolID &ID, llvm::ArrayRef<Ref> Refs,
381 const StringTableOut &Strings, llvm::raw_ostream &OS) {
382 OS << ID.raw();
383 writeVar(Refs.size(), OS);
384 for (const auto &Ref : Refs) {
385 OS.write(static_cast<unsigned char>(Ref.Kind));
386 writeLocation(Ref.Location, Strings, OS);
387 OS << Ref.Container.raw();
388 }
389}
390
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))
396 return Result;
397 for (auto &Ref : Result.second) {
398 Ref.Kind = static_cast<RefKind>(Data.consume8());
399 Ref.Location = readLocation(Data, Strings);
400 Ref.Container = Data.consumeID();
401 }
402 return Result;
403}
404
405// RELATIONS ENCODING
406// A relations section is a flat list of relations. Each relation has:
407// - SymbolID (subject): 8 bytes
408// - relation kind (predicate): 1 byte
409// - SymbolID (object): 8 bytes
410// In the future, we might prefer a packed representation if the need arises.
411
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();
416}
417
418Relation readRelation(Reader &Data) {
419 SymbolID Subject = Data.consumeID();
420 RelationKind Predicate = static_cast<RelationKind>(Data.consume8());
421 SymbolID Object = Data.consumeID();
422 return {Subject, Predicate, Object};
423}
424
425struct InternedCompileCommand {
426 llvm::StringRef Directory;
427 std::vector<llvm::StringRef> CommandLine;
428};
429
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);
437}
438
439InternedCompileCommand
440readCompileCommand(Reader CmdReader, llvm::ArrayRef<llvm::StringRef> Strings) {
441 InternedCompileCommand Cmd;
442 Cmd.Directory = CmdReader.consumeString(Strings);
443 if (!CmdReader.consumeSize(Cmd.CommandLine))
444 return Cmd;
445 for (llvm::StringRef &C : Cmd.CommandLine)
446 C = CmdReader.consumeString(Strings);
447 return Cmd;
448}
449
450// FILE ENCODING
451// A file is a RIFF chunk with type 'CdIx'.
452// It contains the sections:
453// - meta: version number
454// - srcs: information related to include graph
455// - stri: string table
456// - symb: symbols
457// - refs: references to symbols
458
459// The current versioning scheme is simple - non-current versions are rejected.
460// If you make a breaking change, bump this version number to invalidate stored
461// data. Later we may want to support some backward compatibility.
462constexpr static uint32_t Version = 21;
463
464llvm::Expected<IndexFileIn> readRIFF(llvm::StringRef Data,
465 SymbolOrigin Origin) {
466 auto RIFF = riff::readFile(Data);
467 if (!RIFF)
468 return RIFF.takeError();
469 if (RIFF->Type != riff::fourCC("CdIx"))
470 return error("wrong RIFF filetype: {0}", riff::fourCCStr(RIFF->Type));
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()),
474 Chunk.Data);
475
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);
482
483 // meta chunk is checked above, as we prefer the "version mismatch" error.
484 for (llvm::StringRef RequiredChunk : {"stri"})
485 if (!Chunks.count(RequiredChunk))
486 return error("missing required chunk {0}", RequiredChunk);
487
488 auto Strings = readStringTable(Chunks.lookup("stri"));
489 if (!Strings)
490 return Strings.takeError();
491
492 IndexFileIn Result;
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);
500 // We change all the strings inside the structure to point at the keys in
501 // the map, since it is the only copy of the string that's going to live.
502 Entry->getValue().URI = Entry->getKey();
503 for (auto &Include : Entry->getValue().DirectIncludes)
504 Include = Result.Sources->try_emplace(Include).first->getKey();
505 }
506 if (SrcsReader.err())
507 return error("malformed or truncated include uri");
508 }
509
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();
518 }
519 if (Chunks.count("refs")) {
520 Reader RefsReader(Chunks.lookup("refs"));
521 RefSlab::Builder Refs;
522 while (!RefsReader.eof()) {
523 auto RefsBundle = readRefs(RefsReader, Strings->Strings);
524 for (const auto &Ref : RefsBundle.second) // FIXME: bulk insert?
525 Refs.insert(RefsBundle.first, Ref);
526 }
527 if (RefsReader.err())
528 return error("malformed or truncated refs");
529 Result.Refs = std::move(Refs).build();
530 }
531 if (Chunks.count("rela")) {
532 Reader RelationsReader(Chunks.lookup("rela"));
533 RelationSlab::Builder Relations;
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();
539 }
540 if (Chunks.count("cmdl")) {
541 Reader CmdReader(Chunks.lookup("cmdl"));
542 InternedCompileCommand Cmd =
543 readCompileCommand(CmdReader, Strings->Strings);
544 if (CmdReader.err())
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);
551 }
552 return std::move(Result);
553}
554
555template <class Callback>
556void visitStrings(IncludeGraphNode &IGN, const Callback &CB) {
557 CB(IGN.URI);
558 for (llvm::StringRef &Include : IGN.DirectIncludes)
559 CB(Include);
560}
561
562void writeRIFF(const IndexFileOut &Data, llvm::raw_ostream &OS) {
563 assert(Data.Symbols && "An index file without symbols makes no sense!");
565 RIFF.Type = riff::fourCC("CdIx");
566
567 llvm::SmallString<4> Meta;
568 {
569 llvm::raw_svector_ostream MetaOS(Meta);
570 write32(Version, MetaOS);
571 }
572 RIFF.Chunks.push_back({riff::fourCC("meta"), Meta});
573
574 StringTableOut Strings;
575 std::vector<Symbol> Symbols;
576 for (const auto &Sym : *Data.Symbols) {
577 Symbols.emplace_back(Sym);
578 visitStrings(Symbols.back(),
579 [&](llvm::StringRef &S) { Strings.intern(S); });
580 }
581 std::vector<IncludeGraphNode> Sources;
582 if (Data.Sources)
583 for (const auto &Source : *Data.Sources) {
584 Sources.push_back(Source.getValue());
585 visitStrings(Sources.back(),
586 [&](llvm::StringRef &S) { Strings.intern(S); });
587 }
588
589 std::vector<std::pair<SymbolID, std::vector<Ref>>> Refs;
590 if (Data.Refs) {
591 for (const auto &Sym : *Data.Refs) {
592 Refs.emplace_back(Sym);
593 for (auto &Ref : Refs.back().second) {
594 llvm::StringRef File = Ref.Location.FileURI;
595 Strings.intern(File);
596 Ref.Location.FileURI = File.data();
597 }
598 }
599 }
600
601 std::vector<Relation> Relations;
602 if (Data.Relations) {
603 for (const auto &Relation : *Data.Relations) {
604 Relations.emplace_back(Relation);
605 // No strings to be interned in relations.
606 }
607 }
608
609 InternedCompileCommand InternedCmd;
610 if (Data.Cmd) {
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());
617 }
618 }
619
620 std::string StringSection;
621 {
622 llvm::raw_string_ostream StringOS(StringSection);
623 Strings.finalize(StringOS);
624 }
625 RIFF.Chunks.push_back({riff::fourCC("stri"), StringSection});
626
627 std::string SymbolSection;
628 {
629 llvm::raw_string_ostream SymbolOS(SymbolSection);
630 for (const auto &Sym : Symbols)
631 writeSymbol(Sym, Strings, SymbolOS);
632 }
633 RIFF.Chunks.push_back({riff::fourCC("symb"), SymbolSection});
634
635 std::string RefsSection;
636 if (Data.Refs) {
637 {
638 llvm::raw_string_ostream RefsOS(RefsSection);
639 for (const auto &Sym : Refs)
640 writeRefs(Sym.first, Sym.second, Strings, RefsOS);
641 }
642 RIFF.Chunks.push_back({riff::fourCC("refs"), RefsSection});
643 }
644
645 std::string RelationSection;
646 if (Data.Relations) {
647 {
648 llvm::raw_string_ostream RelationOS{RelationSection};
649 for (const auto &Relation : Relations)
650 writeRelation(Relation, RelationOS);
651 }
652 RIFF.Chunks.push_back({riff::fourCC("rela"), RelationSection});
653 }
654
655 std::string SrcsSection;
656 {
657 {
658 llvm::raw_string_ostream SrcsOS(SrcsSection);
659 for (const auto &SF : Sources)
660 writeIncludeGraphNode(SF, Strings, SrcsOS);
661 }
662 RIFF.Chunks.push_back({riff::fourCC("srcs"), SrcsSection});
663 }
664
665 std::string CmdlSection;
666 if (Data.Cmd) {
667 {
668 llvm::raw_string_ostream CmdOS(CmdlSection);
669 writeCompileCommand(InternedCmd, Strings, CmdOS);
670 }
671 RIFF.Chunks.push_back({riff::fourCC("cmdl"), CmdlSection});
672 }
673
674 OS << RIFF;
675}
676
677} // namespace
678
679// Defined in YAMLSerialization.cpp.
680void writeYAML(const IndexFileOut &, llvm::raw_ostream &);
681llvm::Expected<IndexFileIn> readYAML(llvm::StringRef, SymbolOrigin Origin);
682
683llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O) {
684 switch (O.Format) {
686 writeRIFF(O, OS);
687 break;
689 writeYAML(O, OS);
690 break;
691 }
692 return OS;
693}
694
695llvm::Expected<IndexFileIn> readIndexFile(llvm::StringRef Data,
696 SymbolOrigin Origin) {
697 if (Data.starts_with("RIFF")) {
698 return readRIFF(Data, Origin);
699 }
700 if (auto YAMLContents = readYAML(Data, Origin)) {
701 return std::move(*YAMLContents);
702 } else {
703 return error("Not a RIFF file and failed to parse as YAML: {0}",
704 YAMLContents.takeError());
705 }
706}
707
708std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename,
709 SymbolOrigin Origin, bool UseDex,
710 bool SupportContainedRefs) {
711 trace::Span OverallTracer("LoadIndex");
712 auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename);
713 if (!Buffer) {
714 elog("Can't open {0}: {1}", SymbolFilename, Buffer.getError().message());
715 return nullptr;
716 }
717
719 RefSlab Refs;
720 RelationSlab Relations;
721 {
722 trace::Span Tracer("ParseIndex");
723 if (auto I = readIndexFile(Buffer->get()->getBuffer(), Origin)) {
724 if (I->Symbols)
725 Symbols = std::move(*I->Symbols);
726 if (I->Refs)
727 Refs = std::move(*I->Refs);
728 if (I->Relations)
729 Relations = std::move(*I->Relations);
730 } else {
731 elog("Bad index file: {0}", I.takeError());
732 return nullptr;
733 }
734 }
735
736 size_t NumSym = Symbols.size();
737 size_t NumRefs = Refs.numRefs();
738 size_t NumRelations = Relations.size();
739
740 trace::Span Tracer("BuildIndex");
741 auto Index = UseDex
742 ? dex::Dex::build(std::move(Symbols), std::move(Refs),
743 std::move(Relations), SupportContainedRefs)
744 : MemIndex::build(std::move(Symbols), std::move(Refs),
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);
752 return Index;
753}
754
755} // namespace clangd
756} // namespace clang
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.
Definition MemIndex.cpp:18
RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
Definition Ref.h:135
An efficient structure of storing large set of symbol references in memory.
Definition Ref.h:111
RelationSlab::Builder is a mutable container that can 'freeze' to RelationSlab.
Definition Relation.h:75
void insert(const Relation &R)
Adds a relation to the slab.
Definition Relation.h:78
static constexpr size_t RawSize
Definition SymbolID.h:50
llvm::StringRef raw() const
Definition SymbolID.cpp:23
SymbolSlab::Builder is a mutable container that can 'freeze' to SymbolSlab.
Definition Symbol.h:238
An immutable symbol container that stores a set of symbols.
Definition Symbol.h:215
static std::unique_ptr< SymbolIndex > build(SymbolSlab, RefSlab, RelationSlab, bool SupportContainedRefs)
Builds an index from slabs. The index takes ownership of the slab.
Definition Dex.cpp:35
Records an event whose duration is the lifetime of the Span object.
Definition Trace.h:143
std::vector< std::pair< DocID, float > > consume(Iterator &It)
Advances the iterator until it is exhausted.
Definition Iterator.cpp:357
llvm::Expected< File > readFile(llvm::StringRef Stream)
Definition RIFF.cpp:48
constexpr FourCC fourCC(const char(&Literal)[5])
Definition RIFF.h:43
constexpr llvm::StringRef fourCCStr(const FourCC &Data)
Definition RIFF.h:46
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
void visitStrings(Symbol &S, const Callback &CB)
Invokes Callback with each StringRef& contained in the Symbol.
Definition Symbol.h:185
llvm::Expected< IndexFileIn > readIndexFile(llvm::StringRef Data, SymbolOrigin Origin)
llvm::Expected< IndexFileIn > readYAML(llvm::StringRef, SymbolOrigin Origin)
void vlog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:72
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition Function.h:28
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition Logger.h:79
llvm::raw_ostream & operator<<(llvm::raw_ostream &OS, const CodeCompletion &C)
RefKind
Describes the kind of a cross-reference.
Definition Ref.h:28
void writeYAML(const IndexFileOut &, llvm::raw_ostream &)
void elog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:61
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.
Definition Ref.h:88
RefKind Kind
Definition Ref.h:91
SymbolID Container
The ID of the symbol whose definition contains this reference.
Definition Ref.h:95
SymbolLocation Location
The source location where the symbol is named.
Definition Ref.h:90
Represents a relation between two symbols.
Definition Relation.h:32
Ensure we have enough bits to represent all SymbolTag values.
Definition Symbol.h:49