blob: 5c6886162c043c3e062132922102cffa33d88b63 [file] [log] [blame] [edit]
//===----------------------------------------------------------------------===//
//
// 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);
}