Program Listing for File BlankNode.cpp¶
↰ Return to documentation for file (src/rdf4cpp/BlankNode.cpp)
#include "BlankNode.hpp"
#include <rdf4cpp/InvalidNode.hpp>
#include <rdf4cpp/util/CharMatcher.hpp>
#include <rdf4cpp/writer/TryWrite.hpp>
#include <rdf4cpp/Assert.hpp>
#include <uni_algo/all.h>
#include <cstring>
namespace rdf4cpp {
namespace detail_bnode_inlining {
inline constexpr size_t max_inlined_name_len = storage::identifier::NodeID::width / (sizeof(char) * 8);
struct InlinedRepr {
char identifier[max_inlined_name_len]{};
[[nodiscard]] storage::view::BNodeBackendView view() const {
return storage::view::BNodeBackendView{.identifier = std::string_view{identifier, strnlen(identifier, max_inlined_name_len)}};
}
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 identifier) noexcept {
using namespace storage::identifier;
if (identifier.size() > max_inlined_name_len) {
return NodeBackendID{};
}
InlinedRepr inlined_data{};
memcpy(&inlined_data.identifier, identifier.data(), identifier.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::BNode, true};
}
[[nodiscard]] constexpr InlinedRepr from_inlined(storage::identifier::NodeBackendID id) noexcept {
RDF4CPP_ASSERT(id.is_inlined() && id.is_blank_node());
return std::bit_cast<InlinedRepr>(id.node_id());
}
} // namespace detail_bnode_inlining
BlankNode::BlankNode() noexcept : Node{storage::identifier::NodeBackendHandle{}} {
}
BlankNode::BlankNode(std::string_view identifier, storage::DynNodeStoragePtr node_storage)
: BlankNode{make_unchecked((validate(identifier), identifier), node_storage)} {
}
BlankNode::BlankNode(storage::identifier::NodeBackendHandle handle) noexcept : Node{handle} {
}
BlankNode BlankNode::make_null() noexcept {
return BlankNode{};
}
BlankNode BlankNode::make(std::string_view identifier, storage::DynNodeStoragePtr node_storage) {
return BlankNode{identifier, node_storage};
}
BlankNode BlankNode::make_unchecked(std::string_view identifier, storage::DynNodeStoragePtr node_storage) {
auto const id = [&]() {
if (auto const inlined = detail_bnode_inlining::try_into_inlined(identifier); !inlined.null()) {
return inlined;
}
return node_storage.find_or_make_id(storage::view::BNodeBackendView{.identifier = identifier});
}();
return BlankNode{storage::identifier::NodeBackendHandle{id, node_storage}};
}
BlankNode BlankNode::to_node_storage(storage::DynNodeStoragePtr node_storage) const {
if (handle_.storage() == node_storage || null()) {
return *this;
}
if (handle_.is_inlined()) {
return BlankNode{storage::identifier::NodeBackendHandle{handle_.id(), node_storage}};
}
auto const node_id = node_storage.find_or_make_id(handle_.bnode_backend());
return BlankNode{storage::identifier::NodeBackendHandle{node_id, node_storage}};
}
BlankNode BlankNode::try_get_in_node_storage(storage::DynNodeStoragePtr node_storage) const noexcept {
if (handle_.storage() == node_storage || null()) {
return *this;
}
if (handle_.is_inlined()) {
return BlankNode{storage::identifier::NodeBackendHandle{handle_.id(), node_storage}};
}
auto const node_id = node_storage.find_id(handle_.bnode_backend());
if (node_id.null()) {
return BlankNode{};
}
return BlankNode{storage::identifier::NodeBackendHandle{node_id, node_storage}};
}
BlankNode BlankNode::find(std::string_view identifier, storage::DynNodeStoragePtr node_storage) noexcept {
auto const nid = [&]() {
if (auto const inlined = detail_bnode_inlining::try_into_inlined(identifier); !inlined.null()) {
return inlined;
}
return node_storage.find_id(storage::view::BNodeBackendView{.identifier = identifier});
}();
if (nid.null()) {
return BlankNode{};
}
return BlankNode{storage::identifier::NodeBackendHandle{nid, node_storage}};
}
CowString BlankNode::identifier() const noexcept {
if (handle_.is_inlined()) {
auto const inlined = detail_bnode_inlining::from_inlined(handle_.id());
auto const identifier = inlined.view().identifier;
return CowString{CowString::owned, std::string{identifier}};
}
return CowString{CowString::borrowed, handle_.bnode_backend().identifier};
}
FetchOrSerializeResult BlankNode::fetch_or_serialize_identifier(std::string_view &out, writer::BufWriterParts writer) const noexcept {
if (handle_.is_inlined()) {
auto const inlined = detail_bnode_inlining::from_inlined(handle_.id());
if (!rdf4cpp::writer::write_str(inlined.view().identifier, writer)) [[unlikely]] {
return FetchOrSerializeResult::SerializationFailed;
}
return FetchOrSerializeResult::Serialized;
}
out = handle_.bnode_backend().identifier;
return FetchOrSerializeResult::Fetched;
}
bool BlankNode::serialize(writer::BufWriterParts const writer) const noexcept {
if (null()) {
return rdf4cpp::writer::write_str("null", writer);
}
auto const ident = identifier();
RDF4CPP_DETAIL_TRY_WRITE_STR("_:");
RDF4CPP_DETAIL_TRY_WRITE_STR(ident.view());
return true;
}
BlankNode::operator std::string() const noexcept {
return writer::StringWriter::oneshot([this](auto &w) noexcept {
return this->serialize(w);
});
}
std::ostream &operator<<(std::ostream &os, BlankNode const &bnode) {
writer::BufOStreamWriter w{os};
bnode.serialize(w);
w.finalize();
return os;
}
void BlankNode::validate(std::string_view v) {
using namespace util::char_matcher_detail;
static constexpr auto first_matcher = ASCIINumMatcher{} | PNCharsUMatcher;
auto r = v | 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 {}", v));
}
auto lastchar = *it;
++it;
static constexpr auto pn_matcher = PNCharsMatcher | ASCIIPatternMatcher{"."};
while (it != r.end()) {
if (!pn_matcher.match(*it))
{
throw InvalidNode(std::format("invalid blank node label {}", v));
}
lastchar = *it;
++it;
}
if (lastchar == '.') {
throw InvalidNode(std::format("invalid blank node label {}", v));
}
}
std::strong_ordering BlankNode::order(BlankNode const &other) const noexcept {
if (is_inlined()) {
auto const this_delined = detail_bnode_inlining::from_inlined(handle_.id());
if (other.is_inlined()) {
auto const other_deinlined = detail_bnode_inlining::from_inlined(other.handle_.id());
return this_delined <=> other_deinlined;
}
return this_delined.view() <=> other.handle_.bnode_backend();
} else {
if (other.is_inlined()) {
auto const other_deinlined = detail_bnode_inlining::from_inlined(other.handle_.id());
return handle_.bnode_backend() <=> other_deinlined.view();
}
return handle_.bnode_backend() <=> other.handle_.bnode_backend();
}
}
bool BlankNode::order_eq(BlankNode const &other) const noexcept {
return order(other) == std::strong_ordering::equivalent;
}
bool BlankNode::order_ne(BlankNode const &other) const noexcept {
return !order_eq(other);
}
bool BlankNode::eq(BlankNode const &other) const noexcept {
// there is no difference between order_eq and eq for blank nodes
return order_eq(other);
}
bool BlankNode::ne(BlankNode const &other) const noexcept {
return !eq(other);
}
inline namespace shorthands {
BlankNode operator""_bnode(char const *str, size_t len) {
return BlankNode{std::string_view{str, len}};
}
} // namespace literals
} // namespace rdf4cpp
auto std::formatter<rdf4cpp::BlankNode>::format(rdf4cpp::BlankNode n, format_context &ctx) const -> decltype(ctx.out()) {
rdf4cpp::writer::BufOutputIteratorWriter w{ctx.out()};
n.serialize(w);
w.finalize();
return w.buffer().iter;
}