Program Listing for File Variable.cpp¶
↰ Return to documentation for file (src/rdf4cpp/query/Variable.cpp)
#include "Variable.hpp"
#include <rdf4cpp/InvalidNode.hpp>
#include <rdf4cpp/util/CharMatcher.hpp>
#include <rdf4cpp/writer/TryWrite.hpp>
#include <uni_algo/all.h>
#include <cstring>
namespace rdf4cpp::query {
namespace detail_variable_inlining {
inline constexpr size_t max_inlined_name_len = (storage::identifier::NodeID::width / (sizeof(char) * 8)) - 1; // -1 for anonymous tagging boolean
struct InlinedRepr {
char name[max_inlined_name_len]{};
bool is_anonymous;
[[nodiscard]] storage::view::VariableBackendView view() const {
return storage::view::VariableBackendView{.name = std::string_view{name, strnlen(name, max_inlined_name_len)},
.is_anonymous = is_anonymous};
}
std::strong_ordering operator<=>(InlinedRepr const &other) const noexcept {
return view() <=> other.view();
}
};
[[nodiscard]] inline storage::identifier::NodeBackendID try_into_inlined(std::string_view const name, bool const is_anonymous) noexcept {
using namespace storage::identifier;
if (name.size() > max_inlined_name_len) {
return NodeBackendID{};
}
InlinedRepr inlined_data{};
inlined_data.is_anonymous = is_anonymous;
memcpy(&inlined_data.name, name.data(), name.size());
// since inlined_data is initialized, there are implicitly nulls after the string if size is less than max
auto const node_id = std::bit_cast<NodeID>(inlined_data);
return NodeBackendID{node_id, RDFNodeType::Variable, true};
}
[[nodiscard]] constexpr InlinedRepr from_inlined(storage::identifier::NodeBackendID id) noexcept {
RDF4CPP_ASSERT(id.is_inlined() && id.is_variable());
return std::bit_cast<InlinedRepr>(id.node_id());
}
} // namespace detail_variable_inlining
Variable::Variable() noexcept : Node{storage::identifier::NodeBackendHandle{}} {
}
Variable::Variable(std::string_view name, bool anonymous, storage::DynNodeStoragePtr node_storage)
: Variable{make_unchecked((validate(name, anonymous), name), anonymous, node_storage)} {}
Variable::Variable(storage::identifier::NodeBackendHandle handle) noexcept : Node{handle} {}
Variable Variable::make_named(std::string_view name, storage::DynNodeStoragePtr node_storage) {
return Variable{name, false, node_storage};
}
Variable Variable::make_anonymous(std::string_view name, storage::DynNodeStoragePtr node_storage) {
return Variable{name, true, node_storage};
}
Variable Variable::make_unchecked(std::string_view name, bool anonymous, storage::DynNodeStoragePtr node_storage) {
auto const node_backend_id = [&]() {
if (auto const inlined_id = detail_variable_inlining::try_into_inlined(name, anonymous); !inlined_id.null()) {
return inlined_id;
}
return node_storage.find_or_make_id(storage::view::VariableBackendView{.name = name, .is_anonymous = anonymous});
}();
return Variable{storage::identifier::NodeBackendHandle{node_backend_id, node_storage}};
}
Variable Variable::to_node_storage(storage::DynNodeStoragePtr node_storage) const {
if (handle_.storage() == node_storage || null()) {
return *this;
}
if (handle_.is_inlined()) {
return Variable{storage::identifier::NodeBackendHandle{handle_.id(), node_storage}};
}
auto const node_id = node_storage.find_or_make_id(handle_.variable_backend());
return Variable{storage::identifier::NodeBackendHandle{node_id, node_storage}};
}
Variable Variable::try_get_in_node_storage(storage::DynNodeStoragePtr node_storage) const noexcept {
if (handle_.storage() == node_storage || null()) {
return *this;
}
if (handle_.is_inlined()) {
return Variable{storage::identifier::NodeBackendHandle{handle_.id(), node_storage}};
}
auto const node_id = node_storage.find_id(handle_.variable_backend());
if (node_id.null()) {
return Variable{};
}
return Variable{storage::identifier::NodeBackendHandle{node_id, node_storage}};
}
Variable Variable::find(std::string_view name, bool anonymous, storage::DynNodeStoragePtr node_storage) noexcept {
auto const nid = [&]() {
if (auto const inlined_id = detail_variable_inlining::try_into_inlined(name, anonymous); !inlined_id.null()) {
return inlined_id;
}
return node_storage.find_id(storage::view::VariableBackendView{.name = name, .is_anonymous = anonymous});
}();
if (nid.null()) {
return Variable{};
}
return Variable{storage::identifier::NodeBackendHandle{nid, node_storage}};
}
Variable Variable::find_named(std::string_view name, storage::DynNodeStoragePtr node_storage) noexcept {
return find(name, false, node_storage);
}
Variable Variable::find_anonymous(std::string_view name, storage::DynNodeStoragePtr node_storage) noexcept {
return find(name, true, node_storage);
}
bool Variable::is_anonymous() const {
if (handle_.is_inlined()) {
return detail_variable_inlining::from_inlined(handle_.id()).is_anonymous;
}
// TODO: encode is_anonymous into variable ID
return handle_.variable_backend().is_anonymous;
}
CowString Variable::name() const {
if (handle_.is_inlined()) {
auto const inlined_repr = detail_variable_inlining::from_inlined(handle_.id());
auto const name = inlined_repr.view().name;
return CowString{CowString::owned, std::string{name}};
}
return CowString{CowString::borrowed, handle_.variable_backend().name};
}
FetchOrSerializeResult Variable::fetch_or_serialize_name(std::string_view &out, writer::BufWriterParts writer) const noexcept {
if (handle_.is_inlined()) {
auto const inlined_repr = detail_variable_inlining::from_inlined(handle_.id());
if (!rdf4cpp::writer::write_str(inlined_repr.view().name, writer)) {
return FetchOrSerializeResult::SerializationFailed;
}
return FetchOrSerializeResult::Serialized;
}
out = handle_.variable_backend().name;
return FetchOrSerializeResult::Fetched;
}
bool Variable::serialize(writer::BufWriterParts const writer) const noexcept {
if (null()) {
return rdf4cpp::writer::write_str("null", writer);
}
auto const run_ser = [&writer](std::string_view name, bool is_anon) {
if (is_anon) {
RDF4CPP_DETAIL_TRY_WRITE_STR("_:");
} else {
RDF4CPP_DETAIL_TRY_WRITE_STR("?");
}
RDF4CPP_DETAIL_TRY_WRITE_STR(name);
return true;
};
if (handle_.is_inlined()) {
auto backend = detail_variable_inlining::from_inlined(handle_.id());
auto const [name, is_anon] = std::move(backend).view();
return run_ser(name, is_anon);
} else {
auto const backend = handle_.variable_backend();
return run_ser(backend.name, backend.is_anonymous);
}
}
Variable::operator std::string() const {
return writer::StringWriter::oneshot([this](auto &w) noexcept {
return this->serialize(w);
});
}
std::ostream &operator<<(std::ostream &os, Variable const &variable) {
writer::BufOStreamWriter w{os};
variable.serialize(w);
w.finalize();
return os;
}
void Variable::validate(std::string_view n, bool anonymous) {
if (anonymous) {
return BlankNode::validate(n);
}
using namespace util::char_matcher_detail;
static constexpr auto first_matcher = ASCIINumMatcher{} | PNCharsUMatcher;
auto r = n | una::views::utf8;
auto it = r.begin();
if (it == r.end()) {
throw InvalidNode("invalid blank node label (empty string)");
}
if (!first_matcher.match(*it)) {
throw InvalidNode(std::format("invalid blank node label {}", n));
}
++it;
static constexpr auto matcher = ASCIINumMatcher{} | PNCharsUMatcher | PNChars_UnicodePartMatcher{};
while (it != r.end()) {
if (!matcher.match(*it)) {
throw InvalidNode(std::format("invalid blank node label {}", n));
}
++it;
}
}
std::strong_ordering Variable::order(Variable const &other) const noexcept {
if (is_inlined()) {
auto const this_deinlined = detail_variable_inlining::from_inlined(handle_.id());
if (other.is_inlined()) {
auto const other_deinlined = detail_variable_inlining::from_inlined(other.handle_.id());
return this_deinlined <=> other_deinlined;
}
return this_deinlined.view() <=> other.handle_.variable_backend();
} else {
if (other.is_inlined()) {
auto const other_deinlined = detail_variable_inlining::from_inlined(other.handle_.id());
return handle_.variable_backend() <=> other_deinlined.view();
}
return handle_.variable_backend() <=> other.handle_.variable_backend();
}
}
bool Variable::order_eq(Variable const &other) const noexcept {
return order(other) == std::strong_ordering::equal;
}
bool Variable::order_ne(Variable const &other) const noexcept {
return !order_eq(other);
}
bool Variable::eq(Variable const &other) const noexcept {
// for variables order_eq and eq are the same
return order_eq(other);
}
bool Variable::ne(Variable const &other) const noexcept {
return !eq(other);
}
} // namespace rdf4cpp::query
auto std::formatter<rdf4cpp::query::Variable>::format(rdf4cpp::query::Variable n, format_context &ctx) const -> decltype(ctx.out()) {
rdf4cpp::writer::BufOutputIteratorWriter w{ctx.out()};
n.serialize(w);
w.finalize();
return w.buffer().iter;
}