Files
clice/include/Index/Loader.h
2024-11-07 16:13:10 +08:00

188 lines
5.7 KiB
C++

#pragma once
#include "Serialize.h"
namespace clice::index {
struct FullLocation {
Position begin;
Position end;
FileRef file;
LocationRef include;
llvm::StringRef filepath;
};
/// A helper class to load the index from a binary file and provide some utility functions.
class Loader {
public:
Loader(std::vector<char>&& binary) :
binary(std::move(binary)),
index(*reinterpret_cast<const binary::Index*>(this->binary.data())) {}
template <typename T>
auto make_range(binary::array<T> array) const {
auto begin = reinterpret_cast<const T*>(binary.data() + array.offset);
return llvm::ArrayRef(begin, begin + array.size);
}
llvm::ArrayRef<binary::File> files() const {
return make_range(index.files);
}
llvm::ArrayRef<binary::Symbol> symbols() const {
return make_range(index.symbols);
}
llvm::ArrayRef<Relation> relations(const binary::Symbol& symbol) const {
return make_range(symbol.relations);
}
llvm::ArrayRef<Occurrence> occurrences() const {
return make_range(index.occurrences);
}
llvm::ArrayRef<Location> locations() const {
return make_range(index.locations);
}
const binary::File& file(FileRef file) const {
return files()[file.offset];
}
const binary::Symbol& symbol(SymbolRef sym) const {
return symbols()[sym.offset];
}
const Location& location(LocationRef loc) const {
return locations()[loc.offset];
}
llvm::StringRef string(binary::string str) const {
return llvm::StringRef(binary.data() + str.offset, str.size);
}
Position begin(LocationRef loc) const {
return location(loc).begin;
}
Position end(LocationRef loc) const {
return location(loc).end;
}
llvm::StringRef filepath(LocationRef loc) const {
return string(file(location(loc).file).path);
}
LocationRef includeLoc(FileRef file) const {
return this->file(file).include;
}
LocationRef includeLoc(LocationRef loc) const {
return includeLoc(location(loc).file);
}
/// Locate the symbol at the given position.
const binary::Symbol* locateSymbol(FileRef file, Position pos) const {
/// We use the default `<=>` operator when sorting the occurrences.
/// And file is the first key, so locations in the same file are contiguous.
/// Then we can use binary search to locate the file first, then search the occurrences in
/// the file.
auto fileStart = std::lower_bound(occurrences().begin(),
occurrences().end(),
file,
[&](Occurrence occurrence, FileRef file) {
return location(occurrence.location).file < file;
});
if(fileStart == occurrences().end() || location(fileStart->location).file != file) {
return nullptr;
}
auto occurrence = std::lower_bound(fileStart,
occurrences().end(),
pos,
[&](const Occurrence& occurrence, const Position& pos) {
return end(occurrence.location) < pos;
});
if(occurrence == occurrences().end() || begin(occurrence->location) > pos) {
return nullptr;
}
return &symbol(occurrence->symbol);
}
FullLocation decompose(LocationRef loc) const {
auto location = this->location(loc);
auto file = this->file(location.file);
return {
location.begin,
location.end,
location.file,
file.include,
string(file.path),
};
}
template <typename Callback>
bool lookupRelation(const binary::Symbol* symbol,
RelationKinds kind,
const Callback& callback) {
if(symbol) {
for(auto& relation: relations(*symbol)) {
if(relation.kind.is(kind)) {
FullLocation location = decompose(relation.location);
callback(location);
}
}
return true;
}
return false;
}
/// Locate the symbol with the given id.
llvm::ArrayRef<binary::Symbol> locateSymbol(uint64_t id) {
struct Compare {
bool operator() (const binary::Symbol& symbol, uint64_t id) const {
return symbol.id < id;
}
bool operator() (uint64_t id, const binary::Symbol& symbol) const {
return id < symbol.id;
}
};
auto range = std::equal_range(symbols().begin(), symbols().end(), id, Compare{});
return {range.first, range.second};
}
/// Locate the file with the given path.
llvm::ArrayRef<binary::File> locateFile(llvm::StringRef path) {
struct Compare {
Loader& loader;
bool operator() (const binary::File& file, llvm::StringRef path) const {
return loader.string(file.path) < path;
}
bool operator() (llvm::StringRef path, const binary::File& file) const {
return path < loader.string(file.path);
}
};
auto range = std::equal_range(files().begin(), files().end(), path, Compare{*this});
return {range.first, range.second};
}
const binary::Index& getIndex() const {
return index;
}
private:
std::vector<char> binary;
const binary::Index& index;
};
} // namespace clice::index