glean/rts/inventory.h (124 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include "glean/rts/fact.h"
#include "glean/rts/id.h"
#include "glean/rts/lookup.h"
#include "glean/rts/substitution.h"
#include "glean/rts/bytecode/subroutine.h"
#include <vector>
namespace facebook {
namespace glean {
namespace rts {
struct Renamer {
template<typename F>
explicit Renamer(F f)
: rename([f = std::move(f)](uint64_t id, uint64_t pid) {
return f(Id::fromWord(id), Pid::fromWord(pid)).toWord();
})
{
#if __cplusplus >= 201703L
static_assert(
std::is_same_v<std::invoke_result_t<F, Id, Pid>, Id>);
#endif
}
Renamer(const Renamer&) = delete;
const std::function<uint64_t(uint64_t, uint64_t)> rename;
};
struct Substituter {
explicit Substituter(const Substitution* subst)
: renamer([subst](Id id, Pid) { return subst->subst(id); })
{}
explicit Substituter(const Substitution *subst, size_t offset)
: renamer(
[subst,offset](Id id, Pid)
{ return id < subst->finish() ? subst->subst(id) : id + offset; })
{}
Renamer renamer;
};
struct Traverser {
template<typename F>
explicit Traverser(F f)
: traverse([f = std::move(f)](uint64_t id, uint64_t pid) {
f(Id::fromWord(id), Pid::fromWord(pid));
})
{}
Traverser(const Traverser&) = delete;
const std::function<void(uint64_t, uint64_t)> traverse;
};
// NOTE: Any changes here should also be propagated to the internal.thrift
// types and serialize and deserialize should be updated accordingly.
//
// We aren't using the Thrift types here because it isn't clear if those will
// be needed in the long term and it's nice to be able to have methods.
/// Information about a predicate in an open DB.
struct Predicate {
Pid id;
std::string name;
int32_t version;
/// Typechecker for clauses. It should take the following arguments:
///
/// std::function<Id(Id id, Id type)> - fact substitution
/// const void * - begin of clause/key
/// const void * - end of key/begin of value
/// const void * - end of clause/value
/// binary::Output * - substituted clause
/// uint64_t * - size of substituted key
std::shared_ptr<Subroutine> typechecker;
/// Generic fact traversal. Takes these arguments:
///
/// std::function<void(Id id, Pid type)> - called for each fact ID
/// const void * - begin of clause/key
/// const void * - end of key/begin of value
/// const void * - end of clause/value
std::shared_ptr<Subroutine> traverser;
bool operator==(const Predicate& other) const;
bool operator!=(const Predicate& other) const {
return !(*this == other);
}
void typecheck(
const Renamer& renamer,
Fact::Clause clause,
binary::Output& output,
uint64_t& key_size) const {
const uint64_t args[] = {
reinterpret_cast<uint64_t>(&renamer.rename),
reinterpret_cast<uint64_t>(clause.data),
reinterpret_cast<uint64_t>(clause.data + clause.key_size),
reinterpret_cast<uint64_t>(clause.data + clause.size()),
reinterpret_cast<uint64_t>(&output),
reinterpret_cast<uint64_t>(&key_size)
};
typechecker->execute(args);
}
void substitute(
const Substituter& substituter,
Fact::Clause clause,
binary::Output& output,
uint64_t& key_size) const {
// TODO: We implement substitution via the typechecker for now but it we
// might want to generate a more efficient subroutine just for substitution.
typecheck(substituter.renamer, clause, output, key_size);
}
void traverse(
const Traverser& handler,
Fact::Clause clause) const {
runTraverse(*traverser, handler, clause);
}
static void runTraverse(
Subroutine& sub,
const Traverser& handler,
Fact::Clause clause) {
const uint64_t args[] = {
reinterpret_cast<uint64_t>(&handler.traverse),
reinterpret_cast<uint64_t>(clause.data),
reinterpret_cast<uint64_t>(clause.data + clause.key_size),
reinterpret_cast<uint64_t>(clause.data + clause.size()),
};
sub.execute(args);
}
};
/// Information about predicates in an open DB.
class Inventory {
public:
Inventory();
// The ids in 'predicates' are expected to be mostly dense (gaps are ok for
// now but the Inventory will use O(max_id - min_id) space.
explicit Inventory(std::vector<Predicate> predicates);
const Predicate * FOLLY_NULLABLE lookupPredicate(Pid id) const &;
Pid firstId() const {
return first_id;
}
Pid firstFreeId() const {
return firstId() + preds.size();
}
// TEMPORARY
std::vector<const Predicate *> predicates() const;
std::string serialize() const;
static Inventory deserialize(folly::ByteRange);
bool operator==(const Inventory& other) const {
return first_id == other.first_id
&& preds == other.preds;
}
bool operator!=(const Inventory& other) const {
return !(*this == other);
}
private:
Pid first_id;
std::vector<Predicate> preds;
// an INVALID Predicate::id means there is no predicate with that id
};
}
}
}