| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/CAS/NamedValuesSchema.h" | |
| #include "llvm/Support/Endian.h" | |
| #include "llvm/Support/EndianStream.h" | |
| #include "llvm/Support/StringSaver.h" | |
| using namespace llvm; | |
| using namespace llvm::cas; | |
| char NamedValuesSchema::ID = 0; | |
| constexpr StringLiteral NamedValuesSchema::SchemaName; | |
| void NamedValuesSchema::anchor() {} | |
| bool NamedValuesSchema::isNode(const ObjectProxy &Node) const { | |
| // Load the first ref to check its content. | |
| if (Node.getNumReferences() < 1) | |
| return false; | |
| auto FirstRef = Node.getReference(0); | |
| return FirstRef == *NamedValuesKindRef; | |
| } | |
| NamedValuesSchema::NamedValuesSchema(cas::ObjectStore &CAS, Error &E) | |
| : NamedValuesSchema::RTTIExtends(CAS) { | |
| ErrorAsOutParameter EAOP(E); | |
| auto Kind = CAS.storeFromString({}, SchemaName); | |
| if (!Kind) { | |
| E = Kind.takeError(); | |
| return; | |
| } | |
| NamedValuesKindRef = *Kind; | |
| } | |
| Expected<NamedValuesSchema> NamedValuesSchema::create(ObjectStore &CAS) { | |
| Error E = Error::success(); | |
| NamedValuesSchema S(CAS, E); | |
| if (E) | |
| return std::move(E); | |
| return S; | |
| } | |
| size_t NamedValuesSchema::getNumEntries(NamedValuesProxy Values) const { | |
| return Values.getNumReferences() - 1; | |
| } | |
| Error NamedValuesSchema::forEachEntry( | |
| NamedValuesProxy Values, | |
| function_ref<Error(const NamedValuesEntry &)> Callback) const { | |
| for (size_t I = 0, IE = getNumEntries(Values); I != IE; ++I) | |
| if (Error E = Callback(loadEntry(Values, I))) | |
| return E; | |
| return Error::success(); | |
| } | |
| NamedValuesEntry NamedValuesSchema::loadEntry(NamedValuesProxy Values, | |
| size_t I) const { | |
| StringRef Name = Values.getName(I); | |
| auto ObjectRef = Values.getReference(I + 1); | |
| return {Name, ObjectRef}; | |
| } | |
| std::optional<size_t> NamedValuesSchema::lookupEntry(NamedValuesProxy Values, | |
| StringRef Name) const { | |
| size_t NumNames = getNumEntries(Values); | |
| if (!NumNames) | |
| return std::nullopt; | |
| // Start with a binary search, if there are enough entries. | |
| // FIXME: MaxLinearSearchSize is a heuristic and not optimized. | |
| const size_t MaxLinearSearchSize = 4; | |
| size_t Last = NumNames; | |
| size_t First = 0; | |
| while (Last - First > MaxLinearSearchSize) { | |
| auto I = First + (Last - First) / 2; | |
| StringRef NameI = Values.getName(I); | |
| switch (Name.compare(NameI)) { | |
| case 0: | |
| return I; | |
| case -1: | |
| Last = I; | |
| break; | |
| case 1: | |
| First = I + 1; | |
| break; | |
| } | |
| } | |
| // Use a linear search for small list. | |
| for (; First != Last; ++First) | |
| if (Name == Values.getName(First)) | |
| return First; | |
| return std::nullopt; | |
| } | |
| Expected<NamedValuesProxy> NamedValuesSchema::load(ObjectRef Object) const { | |
| auto Node = CAS.getProxy(Object); | |
| if (!Node) | |
| return Node.takeError(); | |
| return load(*Node); | |
| } | |
| Expected<NamedValuesProxy> NamedValuesSchema::load(ObjectProxy Object) const { | |
| if (!isNode(Object)) | |
| return createStringError(inconvertibleErrorCode(), | |
| "object does not conform to NamedValuesSchema"); | |
| return NamedValuesProxy(*this, Object); | |
| } | |
| Expected<NamedValuesProxy> | |
| NamedValuesSchema::construct(ArrayRef<NamedValuesEntry> Entries) { | |
| // ScratchPad for output. | |
| SmallString<256> Data; | |
| SmallVector<ObjectRef, 16> Refs; | |
| Refs.push_back(*NamedValuesKindRef); | |
| // Ensure a stable order for entries and ignore name collisions. | |
| SmallVector<NamedValuesEntry> Sorted(Entries); | |
| llvm::stable_sort(Sorted); | |
| if (llvm::unique(Sorted) != Sorted.end()) | |
| return createStringError("entry names are not unique"); | |
| raw_svector_ostream OS(Data); | |
| support::endian::Writer Writer(OS, endianness::little); | |
| // Encode the entries in the Data. The layout of the named values schema | |
| // object is: | |
| // * Name offset table: The offset of in the data blob for where to find the | |
| // string. It has N + 1 entries and you can find the name of n-th entry at | |
| // offset[n] -> offset[n+1]. Each offset is encoded as little-endian | |
| // uint32_t. | |
| // * Object: ObjectRef for each entry is at n + 1 refs for the object (with | |
| // the first one being the named value kind ID). | |
| // Write Name. | |
| // The start of the string table index. | |
| uint32_t StrIdx = sizeof(uint32_t) * (Sorted.size() + 1); | |
| for (auto &Entry : Sorted) { | |
| Writer.write(StrIdx); | |
| StrIdx += Entry.Name.size(); | |
| // Append refs. | |
| Refs.push_back(Entry.Ref); | |
| } | |
| // Write the end index for the last string. | |
| Writer.write(StrIdx); | |
| // Write names in the end of the block. | |
| for (auto &Entry : Sorted) | |
| OS << Entry.Name; | |
| auto Proxy = CAS.createProxy(Refs, Data); | |
| if (!Proxy) | |
| return Proxy.takeError(); | |
| return NamedValuesProxy(*this, *Proxy); | |
| } | |
| void NamedValuesSchema::Builder::add(StringRef Name, ObjectRef Ref) { | |
| StringSaver Saver(Alloc); | |
| Nodes.emplace_back(Saver.save(Name), Ref); | |
| } | |
| Expected<NamedValuesProxy> NamedValuesSchema::Builder::build() { | |
| auto Schema = NamedValuesSchema::create(CAS); | |
| if (!Schema) | |
| return Schema.takeError(); | |
| return Schema->construct(Nodes); | |
| } | |
| StringRef NamedValuesProxy::getName(size_t I) const { | |
| uint32_t StartIdx = | |
| support::endian::read32le(getData().data() + sizeof(uint32_t) * I); | |
| uint32_t EndIdx = | |
| support::endian::read32le(getData().data() + sizeof(uint32_t) * (I + 1)); | |
| return StringRef(getData().data() + StartIdx, EndIdx - StartIdx); | |
| } |