//===----------------------------------------------------------------------===// // // 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::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 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 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 NamedValuesSchema::load(ObjectRef Object) const { auto Node = CAS.getProxy(Object); if (!Node) return Node.takeError(); return load(*Node); } Expected NamedValuesSchema::load(ObjectProxy Object) const { if (!isNode(Object)) return createStringError(inconvertibleErrorCode(), "object does not conform to NamedValuesSchema"); return NamedValuesProxy(*this, Object); } Expected NamedValuesSchema::construct(ArrayRef Entries) { // ScratchPad for output. SmallString<256> Data; SmallVector Refs; Refs.push_back(*NamedValuesKindRef); // Ensure a stable order for entries and ignore name collisions. SmallVector 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 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); }