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 "SymbolLocation.h" 13 #include "SymbolOrigin.h" 14 #include "dex/Dex.h" 15 #include "support/Logger.h" 16 #include "support/Trace.h" 17 #include "clang/Tooling/CompilationDatabase.h" 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/Support/Compression.h" 20 #include "llvm/Support/Endian.h" 21 #include "llvm/Support/Error.h" 22 #include "llvm/Support/raw_ostream.h" 23 #include <vector> 24 25 namespace clang { 26 namespace clangd { 27 namespace { 28 llvm::Error makeError(const llvm::Twine &Msg) { 29 return llvm::make_error<llvm::StringError>(Msg, 30 llvm::inconvertibleErrorCode()); 31 } 32 33 // IO PRIMITIVES 34 // We use little-endian 32 bit ints, sometimes with variable-length encoding. 35 // 36 // Variable-length int encoding (varint) uses the bottom 7 bits of each byte 37 // to encode the number, and the top bit to indicate whether more bytes follow. 38 // e.g. 9a 2f means [0x1a and keep reading, 0x2f and stop]. 39 // This represents 0x1a | 0x2f<<7 = 6042. 40 // A 32-bit integer takes 1-5 bytes to encode; small numbers are more compact. 41 42 // Reads binary data from a StringRef, and keeps track of position. 43 class Reader { 44 const char *Begin, *End; 45 bool Err = false; 46 47 public: 48 Reader(llvm::StringRef Data) : Begin(Data.begin()), End(Data.end()) {} 49 // The "error" bit is set by reading past EOF or reading invalid data. 50 // When in an error state, reads may return zero values: callers should check. 51 bool err() const { return Err; } 52 // Did we read all the data, or encounter an error? 53 bool eof() const { return Begin == End || Err; } 54 // All the data we didn't read yet. 55 llvm::StringRef rest() const { return llvm::StringRef(Begin, End - Begin); } 56 57 uint8_t consume8() { 58 if (LLVM_UNLIKELY(Begin == End)) { 59 Err = true; 60 return 0; 61 } 62 return *Begin++; 63 } 64 65 uint32_t consume32() { 66 if (LLVM_UNLIKELY(Begin + 4 > End)) { 67 Err = true; 68 return 0; 69 } 70 auto Ret = llvm::support::endian::read32le(Begin); 71 Begin += 4; 72 return Ret; 73 } 74 75 llvm::StringRef consume(int N) { 76 if (LLVM_UNLIKELY(Begin + N > End)) { 77 Err = true; 78 return llvm::StringRef(); 79 } 80 llvm::StringRef Ret(Begin, N); 81 Begin += N; 82 return Ret; 83 } 84 85 uint32_t consumeVar() { 86 constexpr static uint8_t More = 1 << 7; 87 uint8_t B = consume8(); 88 if (LLVM_LIKELY(!(B & More))) 89 return B; 90 uint32_t Val = B & ~More; 91 for (int Shift = 7; B & More && Shift < 32; Shift += 7) { 92 B = consume8(); 93 Val |= (B & ~More) << Shift; 94 } 95 return Val; 96 } 97 98 llvm::StringRef consumeString(llvm::ArrayRef<llvm::StringRef> Strings) { 99 auto StringIndex = consumeVar(); 100 if (LLVM_UNLIKELY(StringIndex >= Strings.size())) { 101 Err = true; 102 return llvm::StringRef(); 103 } 104 return Strings[StringIndex]; 105 } 106 107 SymbolID consumeID() { 108 llvm::StringRef Raw = consume(SymbolID::RawSize); // short if truncated. 109 return LLVM_UNLIKELY(err()) ? SymbolID() : SymbolID::fromRaw(Raw); 110 } 111 }; 112 113 void write32(uint32_t I, llvm::raw_ostream &OS) { 114 char Buf[4]; 115 llvm::support::endian::write32le(Buf, I); 116 OS.write(Buf, sizeof(Buf)); 117 } 118 119 void writeVar(uint32_t I, llvm::raw_ostream &OS) { 120 constexpr static uint8_t More = 1 << 7; 121 if (LLVM_LIKELY(I < 1 << 7)) { 122 OS.write(I); 123 return; 124 } 125 for (;;) { 126 OS.write(I | More); 127 I >>= 7; 128 if (I < 1 << 7) { 129 OS.write(I); 130 return; 131 } 132 } 133 } 134 135 // STRING TABLE ENCODING 136 // Index data has many string fields, and many strings are identical. 137 // We store each string once, and refer to them by index. 138 // 139 // The string table's format is: 140 // - UncompressedSize : uint32 (or 0 for no compression) 141 // - CompressedData : byte[CompressedSize] 142 // 143 // CompressedData is a zlib-compressed byte[UncompressedSize]. 144 // It contains a sequence of null-terminated strings, e.g. "foo\0bar\0". 145 // These are sorted to improve compression. 146 147 // Maps each string to a canonical representation. 148 // Strings remain owned externally (e.g. by SymbolSlab). 149 class StringTableOut { 150 llvm::DenseSet<llvm::StringRef> Unique; 151 std::vector<llvm::StringRef> Sorted; 152 // Since strings are interned, look up can be by pointer. 153 llvm::DenseMap<std::pair<const char *, size_t>, unsigned> Index; 154 155 public: 156 StringTableOut() { 157 // Ensure there's at least one string in the table. 158 // Table size zero is reserved to indicate no compression. 159 Unique.insert(""); 160 } 161 // Add a string to the table. Overwrites S if an identical string exists. 162 void intern(llvm::StringRef &S) { S = *Unique.insert(S).first; }; 163 // Finalize the table and write it to OS. No more strings may be added. 164 void finalize(llvm::raw_ostream &OS) { 165 Sorted = {Unique.begin(), Unique.end()}; 166 llvm::sort(Sorted); 167 for (unsigned I = 0; I < Sorted.size(); ++I) 168 Index.try_emplace({Sorted[I].data(), Sorted[I].size()}, I); 169 170 std::string RawTable; 171 for (llvm::StringRef S : Sorted) { 172 RawTable.append(std::string(S)); 173 RawTable.push_back(0); 174 } 175 if (llvm::zlib::isAvailable()) { 176 llvm::SmallString<1> Compressed; 177 llvm::cantFail(llvm::zlib::compress(RawTable, Compressed)); 178 write32(RawTable.size(), OS); 179 OS << Compressed; 180 } else { 181 write32(0, OS); // No compression. 182 OS << RawTable; 183 } 184 } 185 // Get the ID of an string, which must be interned. Table must be finalized. 186 unsigned index(llvm::StringRef S) const { 187 assert(!Sorted.empty() && "table not finalized"); 188 assert(Index.count({S.data(), S.size()}) && "string not interned"); 189 return Index.find({S.data(), S.size()})->second; 190 } 191 }; 192 193 struct StringTableIn { 194 llvm::BumpPtrAllocator Arena; 195 std::vector<llvm::StringRef> Strings; 196 }; 197 198 llvm::Expected<StringTableIn> readStringTable(llvm::StringRef Data) { 199 Reader R(Data); 200 size_t UncompressedSize = R.consume32(); 201 if (R.err()) 202 return makeError("Truncated string table"); 203 204 llvm::StringRef Uncompressed; 205 llvm::SmallString<1> UncompressedStorage; 206 if (UncompressedSize == 0) // No compression 207 Uncompressed = R.rest(); 208 else { 209 if (llvm::Error E = llvm::zlib::uncompress(R.rest(), UncompressedStorage, 210 UncompressedSize)) 211 return std::move(E); 212 Uncompressed = UncompressedStorage; 213 } 214 215 StringTableIn Table; 216 llvm::StringSaver Saver(Table.Arena); 217 R = Reader(Uncompressed); 218 for (Reader R(Uncompressed); !R.eof();) { 219 auto Len = R.rest().find(0); 220 if (Len == llvm::StringRef::npos) 221 return makeError("Bad string table: not null terminated"); 222 Table.Strings.push_back(Saver.save(R.consume(Len))); 223 R.consume8(); 224 } 225 if (R.err()) 226 return makeError("Truncated string table"); 227 return std::move(Table); 228 } 229 230 // SYMBOL ENCODING 231 // Each field of clangd::Symbol is encoded in turn (see implementation). 232 // - StringRef fields encode as varint (index into the string table) 233 // - enums encode as the underlying type 234 // - most numbers encode as varint 235 236 void writeLocation(const SymbolLocation &Loc, const StringTableOut &Strings, 237 llvm::raw_ostream &OS) { 238 writeVar(Strings.index(Loc.FileURI), OS); 239 for (const auto &Endpoint : {Loc.Start, Loc.End}) { 240 writeVar(Endpoint.line(), OS); 241 writeVar(Endpoint.column(), OS); 242 } 243 } 244 245 SymbolLocation readLocation(Reader &Data, 246 llvm::ArrayRef<llvm::StringRef> Strings) { 247 SymbolLocation Loc; 248 Loc.FileURI = Data.consumeString(Strings).data(); 249 for (auto *Endpoint : {&Loc.Start, &Loc.End}) { 250 Endpoint->setLine(Data.consumeVar()); 251 Endpoint->setColumn(Data.consumeVar()); 252 } 253 return Loc; 254 } 255 256 IncludeGraphNode readIncludeGraphNode(Reader &Data, 257 llvm::ArrayRef<llvm::StringRef> Strings) { 258 IncludeGraphNode IGN; 259 IGN.Flags = static_cast<IncludeGraphNode::SourceFlag>(Data.consume8()); 260 IGN.URI = Data.consumeString(Strings); 261 llvm::StringRef Digest = Data.consume(IGN.Digest.size()); 262 std::copy(Digest.bytes_begin(), Digest.bytes_end(), IGN.Digest.begin()); 263 IGN.DirectIncludes.resize(Data.consumeVar()); 264 for (llvm::StringRef &Include : IGN.DirectIncludes) 265 Include = Data.consumeString(Strings); 266 return IGN; 267 } 268 269 void writeIncludeGraphNode(const IncludeGraphNode &IGN, 270 const StringTableOut &Strings, 271 llvm::raw_ostream &OS) { 272 OS.write(static_cast<uint8_t>(IGN.Flags)); 273 writeVar(Strings.index(IGN.URI), OS); 274 llvm::StringRef Hash(reinterpret_cast<const char *>(IGN.Digest.data()), 275 IGN.Digest.size()); 276 OS << Hash; 277 writeVar(IGN.DirectIncludes.size(), OS); 278 for (llvm::StringRef Include : IGN.DirectIncludes) 279 writeVar(Strings.index(Include), OS); 280 } 281 282 void writeSymbol(const Symbol &Sym, const StringTableOut &Strings, 283 llvm::raw_ostream &OS) { 284 OS << Sym.ID.raw(); // TODO: once we start writing xrefs and posting lists, 285 // symbol IDs should probably be in a string table. 286 OS.write(static_cast<uint8_t>(Sym.SymInfo.Kind)); 287 OS.write(static_cast<uint8_t>(Sym.SymInfo.Lang)); 288 writeVar(Strings.index(Sym.Name), OS); 289 writeVar(Strings.index(Sym.Scope), OS); 290 writeVar(Strings.index(Sym.TemplateSpecializationArgs), OS); 291 writeLocation(Sym.Definition, Strings, OS); 292 writeLocation(Sym.CanonicalDeclaration, Strings, OS); 293 writeVar(Sym.References, OS); 294 OS.write(static_cast<uint8_t>(Sym.Flags)); 295 OS.write(static_cast<uint8_t>(Sym.Origin)); 296 writeVar(Strings.index(Sym.Signature), OS); 297 writeVar(Strings.index(Sym.CompletionSnippetSuffix), OS); 298 writeVar(Strings.index(Sym.Documentation), OS); 299 writeVar(Strings.index(Sym.ReturnType), OS); 300 writeVar(Strings.index(Sym.Type), OS); 301 302 auto WriteInclude = [&](const Symbol::IncludeHeaderWithReferences &Include) { 303 writeVar(Strings.index(Include.IncludeHeader), OS); 304 writeVar(Include.References, OS); 305 }; 306 writeVar(Sym.IncludeHeaders.size(), OS); 307 for (const auto &Include : Sym.IncludeHeaders) 308 WriteInclude(Include); 309 } 310 311 Symbol readSymbol(Reader &Data, llvm::ArrayRef<llvm::StringRef> Strings) { 312 Symbol Sym; 313 Sym.ID = Data.consumeID(); 314 Sym.SymInfo.Kind = static_cast<index::SymbolKind>(Data.consume8()); 315 Sym.SymInfo.Lang = static_cast<index::SymbolLanguage>(Data.consume8()); 316 Sym.Name = Data.consumeString(Strings); 317 Sym.Scope = Data.consumeString(Strings); 318 Sym.TemplateSpecializationArgs = Data.consumeString(Strings); 319 Sym.Definition = readLocation(Data, Strings); 320 Sym.CanonicalDeclaration = readLocation(Data, Strings); 321 Sym.References = Data.consumeVar(); 322 Sym.Flags = static_cast<Symbol::SymbolFlag>(Data.consume8()); 323 Sym.Origin = static_cast<SymbolOrigin>(Data.consume8()); 324 Sym.Signature = Data.consumeString(Strings); 325 Sym.CompletionSnippetSuffix = Data.consumeString(Strings); 326 Sym.Documentation = Data.consumeString(Strings); 327 Sym.ReturnType = Data.consumeString(Strings); 328 Sym.Type = Data.consumeString(Strings); 329 Sym.IncludeHeaders.resize(Data.consumeVar()); 330 for (auto &I : Sym.IncludeHeaders) { 331 I.IncludeHeader = Data.consumeString(Strings); 332 I.References = Data.consumeVar(); 333 } 334 return Sym; 335 } 336 337 // REFS ENCODING 338 // A refs section has data grouped by Symbol. Each symbol has: 339 // - SymbolID: 8 bytes 340 // - NumRefs: varint 341 // - Ref[NumRefs] 342 // Fields of Ref are encoded in turn, see implementation. 343 344 void writeRefs(const SymbolID &ID, llvm::ArrayRef<Ref> Refs, 345 const StringTableOut &Strings, llvm::raw_ostream &OS) { 346 OS << ID.raw(); 347 writeVar(Refs.size(), OS); 348 for (const auto &Ref : Refs) { 349 OS.write(static_cast<unsigned char>(Ref.Kind)); 350 writeLocation(Ref.Location, Strings, OS); 351 } 352 } 353 354 std::pair<SymbolID, std::vector<Ref>> 355 readRefs(Reader &Data, llvm::ArrayRef<llvm::StringRef> Strings) { 356 std::pair<SymbolID, std::vector<Ref>> Result; 357 Result.first = Data.consumeID(); 358 Result.second.resize(Data.consumeVar()); 359 for (auto &Ref : Result.second) { 360 Ref.Kind = static_cast<RefKind>(Data.consume8()); 361 Ref.Location = readLocation(Data, Strings); 362 } 363 return Result; 364 } 365 366 // RELATIONS ENCODING 367 // A relations section is a flat list of relations. Each relation has: 368 // - SymbolID (subject): 8 bytes 369 // - relation kind (predicate): 1 byte 370 // - SymbolID (object): 8 bytes 371 // In the future, we might prefer a packed representation if the need arises. 372 373 void writeRelation(const Relation &R, llvm::raw_ostream &OS) { 374 OS << R.Subject.raw(); 375 OS.write(static_cast<uint8_t>(R.Predicate)); 376 OS << R.Object.raw(); 377 } 378 379 Relation readRelation(Reader &Data) { 380 SymbolID Subject = Data.consumeID(); 381 RelationKind Predicate = static_cast<RelationKind>(Data.consume8()); 382 SymbolID Object = Data.consumeID(); 383 return {Subject, Predicate, Object}; 384 } 385 386 struct InternedCompileCommand { 387 llvm::StringRef Directory; 388 std::vector<llvm::StringRef> CommandLine; 389 }; 390 391 void writeCompileCommand(const InternedCompileCommand &Cmd, 392 const StringTableOut &Strings, 393 llvm::raw_ostream &CmdOS) { 394 writeVar(Strings.index(Cmd.Directory), CmdOS); 395 writeVar(Cmd.CommandLine.size(), CmdOS); 396 for (llvm::StringRef C : Cmd.CommandLine) 397 writeVar(Strings.index(C), CmdOS); 398 } 399 400 InternedCompileCommand 401 readCompileCommand(Reader CmdReader, llvm::ArrayRef<llvm::StringRef> Strings) { 402 InternedCompileCommand Cmd; 403 Cmd.Directory = CmdReader.consumeString(Strings); 404 Cmd.CommandLine.resize(CmdReader.consumeVar()); 405 for (llvm::StringRef &C : Cmd.CommandLine) 406 C = CmdReader.consumeString(Strings); 407 return Cmd; 408 } 409 410 // FILE ENCODING 411 // A file is a RIFF chunk with type 'CdIx'. 412 // It contains the sections: 413 // - meta: version number 414 // - srcs: information related to include graph 415 // - stri: string table 416 // - symb: symbols 417 // - refs: references to symbols 418 419 // The current versioning scheme is simple - non-current versions are rejected. 420 // If you make a breaking change, bump this version number to invalidate stored 421 // data. Later we may want to support some backward compatibility. 422 constexpr static uint32_t Version = 13; 423 424 llvm::Expected<IndexFileIn> readRIFF(llvm::StringRef Data) { 425 auto RIFF = riff::readFile(Data); 426 if (!RIFF) 427 return RIFF.takeError(); 428 if (RIFF->Type != riff::fourCC("CdIx")) 429 return makeError("wrong RIFF type"); 430 llvm::StringMap<llvm::StringRef> Chunks; 431 for (const auto &Chunk : RIFF->Chunks) 432 Chunks.try_emplace(llvm::StringRef(Chunk.ID.data(), Chunk.ID.size()), 433 Chunk.Data); 434 435 for (llvm::StringRef RequiredChunk : {"meta", "stri"}) 436 if (!Chunks.count(RequiredChunk)) 437 return makeError("missing required chunk " + RequiredChunk); 438 439 Reader Meta(Chunks.lookup("meta")); 440 if (Meta.consume32() != Version) 441 return makeError("wrong version"); 442 443 auto Strings = readStringTable(Chunks.lookup("stri")); 444 if (!Strings) 445 return Strings.takeError(); 446 447 IndexFileIn Result; 448 if (Chunks.count("srcs")) { 449 Reader SrcsReader(Chunks.lookup("srcs")); 450 Result.Sources.emplace(); 451 while (!SrcsReader.eof()) { 452 auto IGN = readIncludeGraphNode(SrcsReader, Strings->Strings); 453 auto Entry = Result.Sources->try_emplace(IGN.URI).first; 454 Entry->getValue() = std::move(IGN); 455 // We change all the strings inside the structure to point at the keys in 456 // the map, since it is the only copy of the string that's going to live. 457 Entry->getValue().URI = Entry->getKey(); 458 for (auto &Include : Entry->getValue().DirectIncludes) 459 Include = Result.Sources->try_emplace(Include).first->getKey(); 460 } 461 if (SrcsReader.err()) 462 return makeError("malformed or truncated include uri"); 463 } 464 465 if (Chunks.count("symb")) { 466 Reader SymbolReader(Chunks.lookup("symb")); 467 SymbolSlab::Builder Symbols; 468 while (!SymbolReader.eof()) 469 Symbols.insert(readSymbol(SymbolReader, Strings->Strings)); 470 if (SymbolReader.err()) 471 return makeError("malformed or truncated symbol"); 472 Result.Symbols = std::move(Symbols).build(); 473 } 474 if (Chunks.count("refs")) { 475 Reader RefsReader(Chunks.lookup("refs")); 476 RefSlab::Builder Refs; 477 while (!RefsReader.eof()) { 478 auto RefsBundle = readRefs(RefsReader, Strings->Strings); 479 for (const auto &Ref : RefsBundle.second) // FIXME: bulk insert? 480 Refs.insert(RefsBundle.first, Ref); 481 } 482 if (RefsReader.err()) 483 return makeError("malformed or truncated refs"); 484 Result.Refs = std::move(Refs).build(); 485 } 486 if (Chunks.count("rela")) { 487 Reader RelationsReader(Chunks.lookup("rela")); 488 RelationSlab::Builder Relations; 489 while (!RelationsReader.eof()) { 490 auto Relation = readRelation(RelationsReader); 491 Relations.insert(Relation); 492 } 493 if (RelationsReader.err()) 494 return makeError("malformed or truncated relations"); 495 Result.Relations = std::move(Relations).build(); 496 } 497 if (Chunks.count("cmdl")) { 498 Reader CmdReader(Chunks.lookup("cmdl")); 499 if (CmdReader.err()) 500 return makeError("malformed or truncated commandline section"); 501 InternedCompileCommand Cmd = 502 readCompileCommand(CmdReader, Strings->Strings); 503 Result.Cmd.emplace(); 504 Result.Cmd->Directory = std::string(Cmd.Directory); 505 Result.Cmd->CommandLine.reserve(Cmd.CommandLine.size()); 506 for (llvm::StringRef C : Cmd.CommandLine) 507 Result.Cmd->CommandLine.emplace_back(C); 508 } 509 return std::move(Result); 510 } 511 512 template <class Callback> 513 void visitStrings(IncludeGraphNode &IGN, const Callback &CB) { 514 CB(IGN.URI); 515 for (llvm::StringRef &Include : IGN.DirectIncludes) 516 CB(Include); 517 } 518 519 void writeRIFF(const IndexFileOut &Data, llvm::raw_ostream &OS) { 520 assert(Data.Symbols && "An index file without symbols makes no sense!"); 521 riff::File RIFF; 522 RIFF.Type = riff::fourCC("CdIx"); 523 524 llvm::SmallString<4> Meta; 525 { 526 llvm::raw_svector_ostream MetaOS(Meta); 527 write32(Version, MetaOS); 528 } 529 RIFF.Chunks.push_back({riff::fourCC("meta"), Meta}); 530 531 StringTableOut Strings; 532 std::vector<Symbol> Symbols; 533 for (const auto &Sym : *Data.Symbols) { 534 Symbols.emplace_back(Sym); 535 visitStrings(Symbols.back(), 536 [&](llvm::StringRef &S) { Strings.intern(S); }); 537 } 538 std::vector<IncludeGraphNode> Sources; 539 if (Data.Sources) 540 for (const auto &Source : *Data.Sources) { 541 Sources.push_back(Source.getValue()); 542 visitStrings(Sources.back(), 543 [&](llvm::StringRef &S) { Strings.intern(S); }); 544 } 545 546 std::vector<std::pair<SymbolID, std::vector<Ref>>> Refs; 547 if (Data.Refs) { 548 for (const auto &Sym : *Data.Refs) { 549 Refs.emplace_back(Sym); 550 for (auto &Ref : Refs.back().second) { 551 llvm::StringRef File = Ref.Location.FileURI; 552 Strings.intern(File); 553 Ref.Location.FileURI = File.data(); 554 } 555 } 556 } 557 558 std::vector<Relation> Relations; 559 if (Data.Relations) { 560 for (const auto &Relation : *Data.Relations) { 561 Relations.emplace_back(Relation); 562 // No strings to be interned in relations. 563 } 564 } 565 566 InternedCompileCommand InternedCmd; 567 if (Data.Cmd) { 568 InternedCmd.CommandLine.reserve(Data.Cmd->CommandLine.size()); 569 InternedCmd.Directory = Data.Cmd->Directory; 570 Strings.intern(InternedCmd.Directory); 571 for (llvm::StringRef C : Data.Cmd->CommandLine) { 572 InternedCmd.CommandLine.emplace_back(C); 573 Strings.intern(InternedCmd.CommandLine.back()); 574 } 575 } 576 577 std::string StringSection; 578 { 579 llvm::raw_string_ostream StringOS(StringSection); 580 Strings.finalize(StringOS); 581 } 582 RIFF.Chunks.push_back({riff::fourCC("stri"), StringSection}); 583 584 std::string SymbolSection; 585 { 586 llvm::raw_string_ostream SymbolOS(SymbolSection); 587 for (const auto &Sym : Symbols) 588 writeSymbol(Sym, Strings, SymbolOS); 589 } 590 RIFF.Chunks.push_back({riff::fourCC("symb"), SymbolSection}); 591 592 std::string RefsSection; 593 if (Data.Refs) { 594 { 595 llvm::raw_string_ostream RefsOS(RefsSection); 596 for (const auto &Sym : Refs) 597 writeRefs(Sym.first, Sym.second, Strings, RefsOS); 598 } 599 RIFF.Chunks.push_back({riff::fourCC("refs"), RefsSection}); 600 } 601 602 std::string RelationSection; 603 if (Data.Relations) { 604 { 605 llvm::raw_string_ostream RelationOS{RelationSection}; 606 for (const auto &Relation : Relations) 607 writeRelation(Relation, RelationOS); 608 } 609 RIFF.Chunks.push_back({riff::fourCC("rela"), RelationSection}); 610 } 611 612 std::string SrcsSection; 613 { 614 { 615 llvm::raw_string_ostream SrcsOS(SrcsSection); 616 for (const auto &SF : Sources) 617 writeIncludeGraphNode(SF, Strings, SrcsOS); 618 } 619 RIFF.Chunks.push_back({riff::fourCC("srcs"), SrcsSection}); 620 } 621 622 std::string CmdlSection; 623 if (Data.Cmd) { 624 { 625 llvm::raw_string_ostream CmdOS(CmdlSection); 626 writeCompileCommand(InternedCmd, Strings, CmdOS); 627 } 628 RIFF.Chunks.push_back({riff::fourCC("cmdl"), CmdlSection}); 629 } 630 631 OS << RIFF; 632 } 633 634 } // namespace 635 636 // Defined in YAMLSerialization.cpp. 637 void writeYAML(const IndexFileOut &, llvm::raw_ostream &); 638 llvm::Expected<IndexFileIn> readYAML(llvm::StringRef); 639 640 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IndexFileOut &O) { 641 switch (O.Format) { 642 case IndexFileFormat::RIFF: 643 writeRIFF(O, OS); 644 break; 645 case IndexFileFormat::YAML: 646 writeYAML(O, OS); 647 break; 648 } 649 return OS; 650 } 651 652 llvm::Expected<IndexFileIn> readIndexFile(llvm::StringRef Data) { 653 if (Data.startswith("RIFF")) { 654 return readRIFF(Data); 655 } else if (auto YAMLContents = readYAML(Data)) { 656 return std::move(*YAMLContents); 657 } else { 658 return makeError("Not a RIFF file and failed to parse as YAML: " + 659 llvm::toString(YAMLContents.takeError())); 660 } 661 } 662 663 std::unique_ptr<SymbolIndex> loadIndex(llvm::StringRef SymbolFilename, 664 bool UseDex) { 665 trace::Span OverallTracer("LoadIndex"); 666 auto Buffer = llvm::MemoryBuffer::getFile(SymbolFilename); 667 if (!Buffer) { 668 elog("Can't open {0}", SymbolFilename); 669 return nullptr; 670 } 671 672 SymbolSlab Symbols; 673 RefSlab Refs; 674 RelationSlab Relations; 675 { 676 trace::Span Tracer("ParseIndex"); 677 if (auto I = readIndexFile(Buffer->get()->getBuffer())) { 678 if (I->Symbols) 679 Symbols = std::move(*I->Symbols); 680 if (I->Refs) 681 Refs = std::move(*I->Refs); 682 if (I->Relations) 683 Relations = std::move(*I->Relations); 684 } else { 685 elog("Bad Index: {0}", I.takeError()); 686 return nullptr; 687 } 688 } 689 690 size_t NumSym = Symbols.size(); 691 size_t NumRefs = Refs.numRefs(); 692 size_t NumRelations = Relations.size(); 693 694 trace::Span Tracer("BuildIndex"); 695 auto Index = UseDex ? dex::Dex::build(std::move(Symbols), std::move(Refs), 696 std::move(Relations)) 697 : MemIndex::build(std::move(Symbols), std::move(Refs), 698 std::move(Relations)); 699 vlog("Loaded {0} from {1} with estimated memory usage {2} bytes\n" 700 " - number of symbols: {3}\n" 701 " - number of refs: {4}\n" 702 " - number of relations: {5}", 703 UseDex ? "Dex" : "MemIndex", SymbolFilename, 704 Index->estimateMemoryUsage(), NumSym, NumRefs, NumRelations); 705 return Index; 706 } 707 708 } // namespace clangd 709 } // namespace clang 710