summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/sord/sord.h657
-rw-r--r--include/sord/sordmm.hpp714
2 files changed, 1371 insertions, 0 deletions
diff --git a/include/sord/sord.h b/include/sord/sord.h
new file mode 100644
index 0000000..4665b50
--- /dev/null
+++ b/include/sord/sord.h
@@ -0,0 +1,657 @@
+/*
+ Copyright 2011-2016 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file sord.h API for Sord, a lightweight RDF model library.
+*/
+
+#ifndef SORD_SORD_H
+#define SORD_SORD_H
+
+#include "serd/serd.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef SORD_SHARED
+# ifdef _WIN32
+# define SORD_LIB_IMPORT __declspec(dllimport)
+# define SORD_LIB_EXPORT __declspec(dllexport)
+# else
+# define SORD_LIB_IMPORT __attribute__((visibility("default")))
+# define SORD_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef SORD_INTERNAL
+# define SORD_API SORD_LIB_EXPORT
+# else
+# define SORD_API SORD_LIB_IMPORT
+# endif
+#else
+# define SORD_API
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ @defgroup sord Sord
+ A lightweight RDF model library.
+
+ Sord stores RDF (subject object predicate context) quads, where the context
+ may be omitted (to represent triples in the default graph).
+ @{
+*/
+
+/**
+ Sord World.
+ The World represents all library state, including interned strings.
+*/
+typedef struct SordWorldImpl SordWorld;
+
+/**
+ Sord Model.
+
+ A model is an indexed set of Quads (i.e. it can contain several RDF
+ graphs). It may be searched using various patterns depending on which
+ indices are enabled.
+*/
+typedef struct SordModelImpl SordModel;
+
+/**
+ Model Inserter.
+
+ An inserter is used for writing statements to a model using the Serd sink
+ interface. This makes it simple to write to a model directly using a
+ SerdReader, or any other code that writes statements to a SerdStatementSink.
+*/
+typedef struct SordInserterImpl SordInserter;
+
+/**
+ Model Iterator.
+*/
+typedef struct SordIterImpl SordIter;
+
+/**
+ RDF Node.
+ A Node is a component of a Quad. Nodes may be URIs, blank nodes, or
+ (in the case of quad objects only) string literals. Literal nodes may
+ have an associate language or datatype (but not both).
+*/
+typedef struct SordNodeImpl SordNode;
+
+/**
+ Quad of nodes (a statement), or a quad pattern.
+
+ Nodes are ordered (S P O G). The ID of the default graph is 0.
+*/
+typedef const SordNode* SordQuad[4];
+
+/**
+ Index into a SordQuad.
+*/
+typedef enum {
+ SORD_SUBJECT = 0, /**< Subject */
+ SORD_PREDICATE = 1, /**< Predicate ("key") */
+ SORD_OBJECT = 2, /**< Object ("value") */
+ SORD_GRAPH = 3 /**< Graph ("context") */
+} SordQuadIndex;
+
+/**
+ Type of a node.
+*/
+typedef enum {
+ SORD_URI = 1, /**< URI */
+ SORD_BLANK = 2, /**< Blank node identifier */
+ SORD_LITERAL = 3 /**< Literal (string with optional lang or datatype) */
+} SordNodeType;
+
+/**
+ Indexing option.
+*/
+typedef enum {
+ SORD_SPO = 1, /**< Subject, Predicate, Object */
+ SORD_SOP = 1 << 1, /**< Subject, Object, Predicate */
+ SORD_OPS = 1 << 2, /**< Object, Predicate, Subject */
+ SORD_OSP = 1 << 3, /**< Object, Subject, Predicate */
+ SORD_PSO = 1 << 4, /**< Predicate, Subject, Object */
+ SORD_POS = 1 << 5 /**< Predicate, Object, Subject */
+} SordIndexOption;
+
+/**
+ @name World
+ @{
+*/
+
+/**
+ Create a new Sord World.
+ It is safe to use multiple worlds in one process, though no data
+ (e.g. nodes) can be shared between worlds, and this should be avoided if
+ possible for performance reasons.
+*/
+SORD_API
+SordWorld*
+sord_world_new(void);
+
+/**
+ Free `world`.
+*/
+SORD_API
+void
+sord_world_free(SordWorld* world);
+
+/**
+ Set a function to be called when errors occur.
+
+ The `error_sink` will be called with `handle` as its first argument. If
+ no error function is set, errors are printed to stderr.
+*/
+SORD_API
+void
+sord_world_set_error_sink(SordWorld* world,
+ SerdErrorSink error_sink,
+ void* handle);
+
+/**
+ @}
+ @name Node
+ @{
+*/
+
+/**
+ Get a URI node from a string.
+
+ Note this function measures `str`, which is a common bottleneck.
+ Use sord_node_from_serd_node() instead if `str` is already measured.
+*/
+SORD_API
+SordNode*
+sord_new_uri(SordWorld* world, const uint8_t* uri);
+
+/**
+ Get a URI node from a relative URI string.
+*/
+SORD_API
+SordNode*
+sord_new_relative_uri(SordWorld* world,
+ const uint8_t* uri,
+ const uint8_t* base_uri);
+
+/**
+ Get a blank node from a string.
+
+ Note this function measures `str`, which is a common bottleneck.
+ Use sord_node_from_serd_node() instead if `str` is already measured.
+*/
+SORD_API
+SordNode*
+sord_new_blank(SordWorld* world, const uint8_t* str);
+
+/**
+ Get a literal node from a string.
+
+ Note this function measures `str`, which is a common bottleneck.
+ Use sord_node_from_serd_node() instead if `str` is already measured.
+*/
+SORD_API
+SordNode*
+sord_new_literal(SordWorld* world,
+ SordNode* datatype,
+ const uint8_t* str,
+ const char* lang);
+
+/**
+ Copy a node (obtain a reference).
+
+ Node that since nodes are interned and reference counted, this does not
+ actually create a deep copy of `node`.
+*/
+SORD_API
+SordNode*
+sord_node_copy(const SordNode* node);
+
+/**
+ Free a node (drop a reference).
+*/
+SORD_API
+void
+sord_node_free(SordWorld* world, SordNode* node);
+
+/**
+ Return the type of a node (SORD_URI, SORD_BLANK, or SORD_LITERAL).
+*/
+SORD_API
+SordNodeType
+sord_node_get_type(const SordNode* node);
+
+/**
+ Return the string value of a node.
+*/
+SORD_API
+const uint8_t*
+sord_node_get_string(const SordNode* node);
+
+/**
+ Return the string value of a node, and set `bytes` to its length in bytes.
+*/
+SORD_API
+const uint8_t*
+sord_node_get_string_counted(const SordNode* node, size_t* bytes);
+
+/**
+ Return the string value of a node, and set `bytes` to its length in bytes,
+ and `count` to its length in characters.
+*/
+SORD_API
+const uint8_t*
+sord_node_get_string_measured(const SordNode* node,
+ size_t* bytes,
+ size_t* chars);
+
+/**
+ Return the language of a literal node (or NULL).
+*/
+SORD_API
+const char*
+sord_node_get_language(const SordNode* node);
+
+/**
+ Return the datatype URI of a literal node (or NULL).
+*/
+SORD_API
+SordNode*
+sord_node_get_datatype(const SordNode* node);
+
+/**
+ Return the flags (string attributes) of a node.
+*/
+SORD_API
+SerdNodeFlags
+sord_node_get_flags(const SordNode* node);
+
+/**
+ Return true iff node can be serialised as an inline object.
+
+ More specifically, this returns true iff the node is the object field
+ of exactly one statement, and therefore can be inlined since it needn't
+ be referred to by name.
+*/
+SORD_API
+bool
+sord_node_is_inline_object(const SordNode* node);
+
+/**
+ Return true iff `a` is equal to `b`.
+
+ Note this is much faster than comparing the node's strings.
+*/
+SORD_API
+bool
+sord_node_equals(const SordNode* a,
+ const SordNode* b);
+
+/**
+ Return a SordNode as a SerdNode.
+
+ The returned node is shared and must not be freed or modified.
+*/
+SORD_API
+const SerdNode*
+sord_node_to_serd_node(const SordNode* node);
+
+/**
+ Create a new SordNode from a SerdNode.
+
+ The returned node must be freed using sord_node_free().
+*/
+SORD_API
+SordNode*
+sord_node_from_serd_node(SordWorld* world,
+ SerdEnv* env,
+ const SerdNode* node,
+ const SerdNode* datatype,
+ const SerdNode* lang);
+
+/**
+ @}
+ @name Model
+ @{
+*/
+
+/**
+ Create a new model.
+
+ @param world The world in which to make this model.
+
+ @param indices SordIndexOption flags (e.g. SORD_SPO|SORD_OPS). Be sure to
+ enable an index where the most significant node(s) are not variables in your
+ queries (e.g. to make (? P O) queries, enable either SORD_OPS or SORD_POS).
+
+ @param graphs If true, store (and index) graph contexts.
+*/
+SORD_API
+SordModel*
+sord_new(SordWorld* world,
+ unsigned indices,
+ bool graphs);
+
+/**
+ Close and free `model`.
+*/
+SORD_API
+void
+sord_free(SordModel* model);
+
+/**
+ Get the world associated with `model`.
+*/
+SORD_API
+SordWorld*
+sord_get_world(SordModel* model);
+
+/**
+ Return the number of nodes stored in `world`.
+
+ Nodes are included in this count iff they are a part of a quad in `world`.
+*/
+SORD_API
+size_t
+sord_num_nodes(const SordWorld* world);
+
+/**
+ Return the number of quads stored in `model`.
+*/
+SORD_API
+size_t
+sord_num_quads(const SordModel* model);
+
+/**
+ Return an iterator to the start of `model`.
+*/
+SORD_API
+SordIter*
+sord_begin(const SordModel* model);
+
+/**
+ Search for statements by a quad pattern.
+ @return an iterator to the first match, or NULL if no matches found.
+*/
+SORD_API
+SordIter*
+sord_find(SordModel* model, const SordQuad pat);
+
+/**
+ Search for statements by nodes.
+ @return an iterator to the first match, or NULL if no matches found.
+*/
+SORD_API
+SordIter*
+sord_search(SordModel* model,
+ const SordNode* s,
+ const SordNode* p,
+ const SordNode* o,
+ const SordNode* g);
+/**
+ Search for a single node that matches a pattern.
+ Exactly one of `s`, `p`, `o` must be NULL.
+ This function is mainly useful for predicates that only have one value.
+ The returned node must be freed using sord_node_free().
+ @return the first matching node, or NULL if no matches are found.
+*/
+SORD_API
+SordNode*
+sord_get(SordModel* model,
+ const SordNode* s,
+ const SordNode* p,
+ const SordNode* o,
+ const SordNode* g);
+
+/**
+ Return true iff a statement exists.
+*/
+SORD_API
+bool
+sord_ask(SordModel* model,
+ const SordNode* s,
+ const SordNode* p,
+ const SordNode* o,
+ const SordNode* g);
+
+/**
+ Return the number of matching statements.
+*/
+SORD_API
+uint64_t
+sord_count(SordModel* model,
+ const SordNode* s,
+ const SordNode* p,
+ const SordNode* o,
+ const SordNode* g);
+
+/**
+ Check if `model` contains a triple pattern.
+
+ @return true if `model` contains a match for `pat`, otherwise false.
+*/
+SORD_API
+bool
+sord_contains(SordModel* model, const SordQuad pat);
+
+/**
+ Add a quad to a model.
+
+ Calling this function invalidates all iterators on `model`.
+
+ @return true on success, false, on error.
+*/
+SORD_API
+bool
+sord_add(SordModel* model, const SordQuad tup);
+
+/**
+ Remove a quad from a model.
+
+ Calling this function invalidates all iterators on `model`. To remove quads
+ while iterating, use sord_erase() instead.
+*/
+SORD_API
+void
+sord_remove(SordModel* model, const SordQuad tup);
+
+/**
+ Remove a quad from a model via an iterator.
+
+ Calling this function invalidates all iterators on `model` except `iter`.
+
+ @param model The model which `iter` points to.
+ @param iter Iterator to the element to erase, which is incremented to the
+ next value on return.
+*/
+SORD_API
+SerdStatus
+sord_erase(SordModel* model, SordIter* iter);
+
+/**
+ @}
+ @name Inserter
+ @{
+*/
+
+/**
+ Create an inserter for writing statements to a model.
+*/
+SORD_API
+SordInserter*
+sord_inserter_new(SordModel* model,
+ SerdEnv* env);
+
+/**
+ Free an inserter.
+*/
+SORD_API
+void
+sord_inserter_free(SordInserter* inserter);
+
+/**
+ Set the current base URI for writing to the model.
+
+ Note this function can be safely casted to SerdBaseSink.
+*/
+SORD_API
+SerdStatus
+sord_inserter_set_base_uri(SordInserter* inserter,
+ const SerdNode* uri);
+
+/**
+ Set a namespace prefix for writing to the model.
+
+ Note this function can be safely casted to SerdPrefixSink.
+*/
+SORD_API
+SerdStatus
+sord_inserter_set_prefix(SordInserter* inserter,
+ const SerdNode* name,
+ const SerdNode* uri);
+
+/**
+ Write a statement to the model.
+
+ Note this function can be safely casted to SerdStatementSink.
+*/
+SORD_API
+SerdStatus
+sord_inserter_write_statement(SordInserter* inserter,
+ SerdStatementFlags flags,
+ const SerdNode* graph,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const SerdNode* object,
+ const SerdNode* object_datatype,
+ const SerdNode* object_lang);
+
+/**
+ @}
+ @name Iteration
+ @{
+*/
+
+/**
+ Set `quad` to the quad pointed to by `iter`.
+*/
+SORD_API
+void
+sord_iter_get(const SordIter* iter, SordQuad tup);
+
+/**
+ Return a field of the quad pointed to by `iter`.
+
+ Returns NULL if `iter` is NULL or is at the end.
+*/
+SORD_API
+const SordNode*
+sord_iter_get_node(const SordIter* iter, SordQuadIndex index);
+
+/**
+ Return the store pointed to by `iter`.
+*/
+SORD_API
+const SordModel*
+sord_iter_get_model(SordIter* iter);
+
+/**
+ Increment `iter` to point to the next statement.
+*/
+SORD_API
+bool
+sord_iter_next(SordIter* iter);
+
+/**
+ Return true iff `iter` is at the end of its range.
+*/
+SORD_API
+bool
+sord_iter_end(const SordIter* iter);
+
+/**
+ Free `iter`.
+*/
+SORD_API
+void
+sord_iter_free(SordIter* iter);
+
+/**
+ @}
+ @name Utilities
+ @{
+*/
+
+/**
+ Match two quads (using ID comparison only).
+
+ This function is a straightforward and fast equivalence match with wildcard
+ support (ID 0 is a wildcard). It does not actually read node data.
+ @return true iff `x` and `y` match.
+*/
+SORD_API
+bool
+sord_quad_match(const SordQuad x, const SordQuad y);
+
+/**
+ @}
+ @name Serialisation
+ @{
+*/
+
+/**
+ Return a reader that will read into `model`.
+*/
+SORD_API
+SerdReader*
+sord_new_reader(SordModel* model,
+ SerdEnv* env,
+ SerdSyntax syntax,
+ SordNode* graph);
+
+/**
+ Write a model to a writer.
+*/
+SORD_API
+bool
+sord_write(SordModel* model,
+ SerdWriter* writer,
+ SordNode* graph);
+
+/**
+ Write a range to a writer.
+
+ This increments `iter` to its end, then frees it.
+*/
+SORD_API
+bool
+sord_write_iter(SordIter* iter,
+ SerdWriter* writer);
+
+/**
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* SORD_SORD_H */
diff --git a/include/sord/sordmm.hpp b/include/sord/sordmm.hpp
new file mode 100644
index 0000000..1cb34e7
--- /dev/null
+++ b/include/sord/sordmm.hpp
@@ -0,0 +1,714 @@
+/*
+ Copyright 2011-2013 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file sordmm.hpp
+ Public Sord C++ API.
+*/
+
+#ifndef SORD_SORDMM_HPP
+#define SORD_SORDMM_HPP
+
+#if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif
+
+#include "serd/serd.h"
+#include "sord/sord.h"
+
+#if defined(__clang__)
+# pragma clang diagnostic pop
+#endif
+
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <set>
+#include <sstream>
+#include <string>
+
+#define SORD_NS_XSD "http://www.w3.org/2001/XMLSchema#"
+
+namespace Sord {
+
+/** Utility base class to prevent copying or moving. */
+class Noncopyable {
+public:
+ Noncopyable() = default;
+ ~Noncopyable() = default;
+
+ Noncopyable(const Noncopyable&) = delete;
+ const Noncopyable& operator=(const Noncopyable&) = delete;
+
+ Noncopyable(Noncopyable&&) = delete;
+ Noncopyable& operator=(Noncopyable&&) = delete;
+};
+
+/** C++ wrapper for a Sord object. */
+template <typename T>
+class Wrapper {
+public:
+ inline Wrapper(T* c_obj = nullptr) : _c_obj(c_obj) {}
+
+ inline T* c_obj() { return _c_obj; }
+ inline const T* c_obj() const { return _c_obj; }
+
+protected:
+ T* _c_obj;
+};
+
+/** Collection of RDF namespaces with prefixes. */
+class Namespaces : public Wrapper<SerdEnv> {
+public:
+ Namespaces() : Wrapper<SerdEnv>(serd_env_new(nullptr)) {}
+ ~Namespaces() { serd_env_free(_c_obj); }
+
+ static inline SerdNode string_to_node(SerdType type, const std::string& s) {
+ SerdNode ret = {reinterpret_cast<const uint8_t*>(s.c_str()),
+ s.length(),
+ s.length(),
+ 0,
+ type};
+ return ret;
+ }
+
+ inline void add(const std::string& name,
+ const std::string& uri) {
+ const SerdNode name_node = string_to_node(SERD_LITERAL, name);
+ const SerdNode uri_node = string_to_node(SERD_URI, uri);
+ serd_env_set_prefix(_c_obj, &name_node, &uri_node);
+ }
+
+ inline std::string qualify(std::string uri) const {
+ const SerdNode uri_node = string_to_node(SERD_URI, uri);
+ SerdNode prefix;
+ SerdChunk suffix;
+ if (serd_env_qualify(_c_obj, &uri_node, &prefix, &suffix)) {
+ std::string ret(reinterpret_cast<const char*>(prefix.buf),
+ prefix.n_bytes);
+ ret.append(":").append(reinterpret_cast<const char*>(suffix.buf),
+ suffix.len);
+ return ret;
+ }
+ return uri;
+ }
+
+ inline std::string expand(const std::string& curie) const {
+ assert(curie.find(':') != std::string::npos);
+ SerdNode curie_node = string_to_node(SERD_CURIE, curie);
+ SerdChunk uri_prefix;
+ SerdChunk uri_suffix;
+ if (!serd_env_expand(_c_obj, &curie_node, &uri_prefix, &uri_suffix)) {
+ std::string ret(reinterpret_cast<const char*>(uri_prefix.buf),
+ uri_prefix.len);
+ ret.append(reinterpret_cast<const char*>(uri_suffix.buf),
+ uri_suffix.len);
+ return ret;
+ }
+ std::cerr << "CURIE `" << curie << "' has unknown prefix." << std::endl;
+ return curie;
+ }
+};
+
+/** Sord library state. */
+class World : public Noncopyable, public Wrapper<SordWorld> {
+public:
+ inline World()
+ : _next_blank_id(0)
+ {
+ _c_obj = sord_world_new();
+ }
+
+ inline ~World() {
+ sord_world_free(_c_obj);
+ }
+
+ inline uint64_t blank_id() { return _next_blank_id++; }
+
+ inline void add_prefix(const std::string& prefix, const std::string& uri) {
+ _prefixes.add(prefix, uri);
+ }
+
+ inline const Namespaces& prefixes() const { return _prefixes; }
+ inline Namespaces& prefixes() { return _prefixes; }
+ inline SordWorld* world() { return _c_obj; }
+
+private:
+ Namespaces _prefixes;
+ std::set<std::string> _blank_ids;
+ uint64_t _next_blank_id;
+};
+
+/** An RDF Node (resource, literal, etc)
+ */
+class Node : public Wrapper<SordNode> {
+public:
+ enum Type {
+ UNKNOWN = 0,
+ URI = SORD_URI,
+ BLANK = SORD_BLANK,
+ LITERAL = SORD_LITERAL
+ };
+
+ inline Node() : Wrapper<SordNode>(nullptr), _world(nullptr) {}
+
+ inline Node(World& world, Type t, const std::string& s);
+ inline Node(World& world);
+ inline Node(World& world, const SordNode* node);
+ inline Node(World& world, SordNode* node, bool copy=false);
+ inline Node(const Node& other);
+ inline ~Node();
+
+ inline Type type() const {
+ return _c_obj ? static_cast<Type>(sord_node_get_type(_c_obj)) : UNKNOWN;
+ }
+
+ inline const SordNode* get_node() const { return _c_obj; }
+ inline SordNode* get_node() { return _c_obj; }
+
+ const SerdNode* to_serd_node() const {
+ return sord_node_to_serd_node(_c_obj);
+ }
+
+ inline bool is_valid() const { return type() != UNKNOWN; }
+
+ inline bool operator<(const Node& other) const {
+ if (type() != other.type()) {
+ return type() < other.type();
+ } else {
+ return to_string() < other.to_string();
+ }
+ }
+
+ Node& operator=(const Node& other) {
+ if (&other != this) {
+ if (_c_obj) {
+ sord_node_free(_world->c_obj(), _c_obj);
+ }
+ _world = other._world;
+ _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : nullptr;
+ }
+ return *this;
+ }
+
+ inline bool operator==(const Node& other) const {
+ return sord_node_equals(_c_obj, other._c_obj);
+ }
+
+ inline const uint8_t* to_u_string() const;
+ inline const char* to_c_string() const;
+ inline std::string to_string() const;
+
+ inline bool is_literal_type(const char* type_uri) const;
+
+ inline bool is_uri() const { return _c_obj && type() == URI; }
+ inline bool is_blank() const { return _c_obj && type() == BLANK; }
+ inline bool is_int() const { return is_literal_type(SORD_NS_XSD "integer"); }
+ inline bool is_float() const { return is_literal_type(SORD_NS_XSD "decimal"); }
+ inline bool is_bool() const { return is_literal_type(SORD_NS_XSD "boolean"); }
+
+ inline int to_int() const;
+ inline float to_float() const;
+ inline bool to_bool() const;
+
+ inline static Node blank_id(World& world, const std::string& base="b") {
+ const uint64_t num = world.blank_id();
+ std::ostringstream ss;
+ ss << base << num;
+ return Node(world, Node::BLANK, ss.str());
+ }
+
+private:
+ World* _world;
+};
+
+inline std::ostream&
+operator<<(std::ostream& os, const Node& node)
+{
+ return os << node.to_string();
+}
+
+class URI : public Node {
+public:
+ inline URI(World& world, const std::string& s)
+ : Node(world, Node::URI, s) {}
+ inline URI(World& world, const std::string& s, const std::string& base)
+ : Node(world,
+ sord_new_relative_uri(
+ world.world(),
+ reinterpret_cast<const uint8_t*>(s.c_str()),
+ reinterpret_cast<const uint8_t*>(base.c_str())))
+ {
+ }
+};
+
+class Curie : public Node {
+public:
+ inline Curie(World& world, const std::string& s)
+ : Node(world, Node::URI, world.prefixes().expand(s)) {}
+};
+
+class Literal : public Node {
+public:
+ inline Literal(World& world, const std::string& s)
+ : Node(world, Node::LITERAL, s) {}
+
+ static inline Node decimal(World& world, double d, unsigned frac_digits) {
+ const SerdNode val = serd_node_new_decimal(d, frac_digits);
+ const SerdNode type = serd_node_from_string(
+ SERD_URI, reinterpret_cast<const uint8_t*>(SORD_NS_XSD "decimal"));
+
+ return Node(
+ world,
+ sord_node_from_serd_node(
+ world.c_obj(), world.prefixes().c_obj(), &val, &type, nullptr),
+ false);
+ }
+
+ static inline Node integer(World& world, int64_t i) {
+ const SerdNode val = serd_node_new_integer(i);
+ const SerdNode type = serd_node_from_string(
+ SERD_URI, reinterpret_cast<const uint8_t*>(SORD_NS_XSD "integer"));
+
+ return Node(
+ world,
+ sord_node_from_serd_node(
+ world.c_obj(), world.prefixes().c_obj(), &val, &type, nullptr),
+ false);
+ }
+};
+
+inline
+Node::Node(World& world, Type type, const std::string& s)
+ : _world(&world)
+{
+ switch (type) {
+ case URI:
+ _c_obj =
+ sord_new_uri(world.world(),
+ reinterpret_cast<const unsigned char*>(s.c_str()));
+ break;
+ case LITERAL:
+ _c_obj =
+ sord_new_literal(world.world(),
+ nullptr,
+ reinterpret_cast<const unsigned char*>(s.c_str()),
+ nullptr);
+ break;
+ case BLANK:
+ _c_obj =
+ sord_new_blank(world.world(),
+ reinterpret_cast<const unsigned char*>(s.c_str()));
+ break;
+ default:
+ _c_obj = nullptr;
+ }
+
+ assert(this->type() == type);
+}
+
+inline
+Node::Node(World& world)
+ : _world(&world)
+{
+ Node me = blank_id(world);
+ *this = me;
+}
+
+inline
+Node::Node(World& world, const SordNode* node)
+ : _world(&world)
+{
+ _c_obj = sord_node_copy(node);
+}
+
+inline
+Node::Node(World& world, SordNode* node, bool copy)
+ : _world(&world)
+{
+ _c_obj = copy ? sord_node_copy(node) : node;
+}
+
+inline
+Node::Node(const Node& other) // NOLINT(bugprone-copy-constructor-init)
+ : Wrapper<SordNode>()
+ , _world(other._world)
+{
+ if (_world) {
+ _c_obj = other._c_obj ? sord_node_copy(other._c_obj) : nullptr;
+ }
+
+ assert((!_c_obj && !other._c_obj) || to_string() == other.to_string());
+}
+
+inline
+Node::~Node()
+{
+ if (_world) {
+ sord_node_free(_world->c_obj(), _c_obj);
+ }
+}
+
+inline std::string
+Node::to_string() const
+{
+ return _c_obj ? reinterpret_cast<const char*>(sord_node_get_string(_c_obj))
+ : "";
+}
+
+inline const char*
+Node::to_c_string() const
+{
+ return reinterpret_cast<const char*>(sord_node_get_string(_c_obj));
+}
+
+inline const uint8_t*
+Node::to_u_string() const
+{
+ return sord_node_get_string(_c_obj);
+}
+
+inline bool
+Node::is_literal_type(const char* type_uri) const
+{
+ if (_c_obj && sord_node_get_type(_c_obj) == SORD_LITERAL) {
+ const SordNode* datatype = sord_node_get_datatype(_c_obj);
+ if (datatype && !strcmp(reinterpret_cast<const char*>(
+ sord_node_get_string(datatype)),
+ type_uri)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+inline int
+Node::to_int() const
+{
+ assert(is_int());
+ char* endptr = nullptr;
+ return strtol(reinterpret_cast<const char*>(sord_node_get_string(_c_obj)),
+ &endptr,
+ 10);
+}
+
+inline float
+Node::to_float() const
+{
+ assert(is_float());
+ return serd_strtod(reinterpret_cast<const char*>(
+ sord_node_get_string(_c_obj)),
+ nullptr);
+}
+
+inline bool
+Node::to_bool() const
+{
+ assert(is_bool());
+ return !strcmp(reinterpret_cast<const char*>(sord_node_get_string(_c_obj)),
+ "true");
+}
+
+struct Iter : public Wrapper<SordIter> {
+ inline Iter(World& world, SordIter* c_obj)
+ : Wrapper<SordIter>(c_obj), _world(world) {}
+
+ Iter(const Iter&) = delete;
+ Iter& operator=(const Iter&) = delete;
+
+ inline Iter(Iter&& iter) noexcept
+ : Wrapper<SordIter>(iter)
+ , _world(iter._world)
+ {}
+
+ inline ~Iter() { sord_iter_free(_c_obj); }
+
+ inline bool end() const { return sord_iter_end(_c_obj); }
+ inline bool next() const { return sord_iter_next(_c_obj); }
+ inline Iter& operator++() {
+ assert(!end());
+ next();
+ return *this;
+ }
+ inline Node get_subject() const {
+ SordQuad quad;
+ sord_iter_get(_c_obj, quad);
+ return Node(_world, quad[SORD_SUBJECT]);
+ }
+ inline Node get_predicate() const {
+ SordQuad quad;
+ sord_iter_get(_c_obj, quad);
+ return Node(_world, quad[SORD_PREDICATE]);
+ }
+ inline Node get_object() const {
+ SordQuad quad;
+ sord_iter_get(_c_obj, quad);
+ return Node(_world, quad[SORD_OBJECT]);
+ }
+ World& _world;
+};
+
+/** An RDF Model (collection of triples).
+ */
+class Model : public Noncopyable, public Wrapper<SordModel> {
+public:
+ inline Model(World& world,
+ const std::string& base_uri,
+ unsigned indices = (SORD_SPO | SORD_OPS),
+ bool graphs = true);
+
+ inline ~Model();
+
+ inline const Node& base_uri() const { return _base; }
+
+ size_t num_quads() const { return sord_num_quads(_c_obj); }
+
+ inline void load_file(SerdEnv* env,
+ SerdSyntax syntax,
+ const std::string& uri,
+ const std::string& base_uri="");
+
+ inline void load_string(SerdEnv* env,
+ SerdSyntax syntax,
+ const char* str,
+ size_t len,
+ const std::string& base_uri);
+
+ inline SerdStatus write_to_file(
+ const std::string& uri,
+ SerdSyntax syntax = SERD_TURTLE,
+ SerdStyle style = static_cast<SerdStyle>(
+ SERD_STYLE_ABBREVIATED
+ |SERD_STYLE_CURIED
+ |SERD_STYLE_RESOLVED));
+
+ inline std::string write_to_string(
+ const std::string& base_uri,
+ SerdSyntax syntax = SERD_TURTLE,
+ SerdStyle style = static_cast<SerdStyle>(
+ SERD_STYLE_ABBREVIATED
+ |SERD_STYLE_CURIED
+ |SERD_STYLE_RESOLVED));
+
+ inline void add_statement(const Node& subject,
+ const Node& predicate,
+ const Node& object);
+
+ inline Iter find(const Node& subject,
+ const Node& predicate,
+ const Node& object);
+
+ inline Node get(const Node& subject,
+ const Node& predicate,
+ const Node& object);
+
+ inline World& world() const { return _world; }
+
+private:
+ World& _world;
+ Node _base;
+};
+
+/** Create an empty in-memory RDF model.
+ */
+inline
+Model::Model(World& world,
+ const std::string& base_uri,
+ unsigned indices,
+ bool graphs)
+ : _world(world)
+ , _base(world, Node::URI, base_uri)
+{
+ _c_obj = sord_new(_world.world(), indices, graphs);
+}
+
+inline void
+Model::load_string(SerdEnv* env,
+ SerdSyntax syntax,
+ const char* str,
+ size_t /*len*/,
+ const std::string& /*base_uri*/)
+{
+ SerdReader* reader = sord_new_reader(_c_obj, env, syntax, nullptr);
+ serd_reader_read_string(reader, reinterpret_cast<const uint8_t*>(str));
+ serd_reader_free(reader);
+}
+
+inline Model::~Model()
+{
+ sord_free(_c_obj);
+}
+
+inline void
+Model::load_file(SerdEnv* env,
+ SerdSyntax syntax,
+ const std::string& data_uri,
+ const std::string& /*base_uri*/)
+{
+ uint8_t* path =
+ serd_file_uri_parse(reinterpret_cast<const uint8_t*>(data_uri.c_str()),
+ nullptr);
+ if (!path) {
+ fprintf(stderr, "Failed to parse file URI <%s>\n", data_uri.c_str());
+ return;
+ }
+
+ // FIXME: blank prefix parameter?
+ SerdReader* reader = sord_new_reader(_c_obj, env, syntax, nullptr);
+ serd_reader_read_file(reader, path);
+ serd_reader_free(reader);
+ serd_free(path);
+}
+
+inline SerdStatus
+Model::write_to_file(const std::string& uri, SerdSyntax syntax, SerdStyle style)
+{
+ uint8_t* path =
+ serd_file_uri_parse(reinterpret_cast<const uint8_t*>(uri.c_str()),
+ nullptr);
+ if (!path) {
+ fprintf(stderr, "Failed to parse file URI <%s>\n", uri.c_str());
+ return SERD_ERR_BAD_ARG;
+ }
+
+ FILE* const fd = fopen(reinterpret_cast<const char*>(path), "w");
+ if (!fd) {
+ fprintf(stderr, "Failed to open file %s\n", path);
+ serd_free(path);
+ return SERD_ERR_UNKNOWN;
+ }
+ serd_free(path);
+
+ SerdURI base_uri = SERD_URI_NULL;
+ if (serd_uri_parse(reinterpret_cast<const uint8_t*>(uri.c_str()),
+ &base_uri)) {
+ fprintf(stderr, "Invalid base URI <%s>\n", uri.c_str());
+ fclose(fd);
+ return SERD_ERR_BAD_ARG;
+ }
+
+ SerdWriter* writer = serd_writer_new(syntax,
+ style,
+ _world.prefixes().c_obj(),
+ &base_uri,
+ serd_file_sink,
+ fd);
+
+ serd_env_foreach(_world.prefixes().c_obj(),
+ reinterpret_cast<SerdPrefixSink>(serd_writer_set_prefix),
+ writer);
+
+ sord_write(_c_obj, writer, nullptr);
+ serd_writer_free(writer);
+ fclose(fd);
+
+ return SERD_SUCCESS;
+}
+
+extern "C" {
+
+static size_t
+string_sink(const void* buf, size_t len, void* stream)
+{
+ try {
+ auto* str = static_cast<std::string*>(stream);
+ str->append(static_cast<const char*>(buf), len);
+ return len;
+ } catch (...) {
+ return 0;
+ }
+}
+
+}
+
+inline std::string
+Model::write_to_string(const std::string& base_uri_str,
+ SerdSyntax syntax,
+ SerdStyle style)
+{
+ SerdURI base_uri = SERD_URI_NULL;
+ if (serd_uri_parse(reinterpret_cast<const uint8_t*>(base_uri_str.c_str()),
+ &base_uri)) {
+ fprintf(stderr, "Invalid base URI <%s>\n", base_uri_str.c_str());
+ return "";
+ }
+
+ std::string ret;
+
+ SerdWriter* writer = serd_writer_new(syntax,
+ style,
+ _world.prefixes().c_obj(),
+ &base_uri,
+ string_sink,
+ &ret);
+
+ const SerdNode base_uri_node = serd_node_from_string(
+ SERD_URI, reinterpret_cast<const uint8_t*>(base_uri_str.c_str()));
+ serd_writer_set_base_uri(writer, &base_uri_node);
+
+ serd_env_foreach(_world.prefixes().c_obj(),
+ reinterpret_cast<SerdPrefixSink>(serd_writer_set_prefix),
+ writer);
+
+ sord_write(_c_obj, writer, nullptr);
+
+ serd_writer_free(writer);
+ return ret;
+}
+
+inline void
+Model::add_statement(const Node& subject,
+ const Node& predicate,
+ const Node& object)
+{
+ SordQuad quad = { subject.c_obj(),
+ predicate.c_obj(),
+ object.c_obj(),
+ nullptr };
+
+ sord_add(_c_obj, quad);
+}
+
+inline Iter
+Model::find(const Node& subject,
+ const Node& predicate,
+ const Node& object)
+{
+ SordQuad quad = { subject.c_obj(),
+ predicate.c_obj(),
+ object.c_obj(),
+ nullptr };
+
+ return Iter(_world, sord_find(_c_obj, quad));
+}
+
+inline Node
+Model::get(const Node& subject,
+ const Node& predicate,
+ const Node& object)
+{
+ SordNode* c_node = sord_get(
+ _c_obj, subject.c_obj(), predicate.c_obj(), object.c_obj(), nullptr);
+ return Node(_world, c_node, false);
+}
+
+} // namespace Sord
+
+#endif // SORD_SORDMM_HPP