diff options
author | David Robillard <d@drobilla.net> | 2020-12-24 18:58:56 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2020-12-24 18:58:56 +0100 |
commit | 11f9eb373f9d29101fbc5e6012c86de462b7b267 (patch) | |
tree | e38bf101bb2ec3e3cf0b95859b8b455ecf7aeba2 /include | |
parent | cc33f8221b4e04d3e86b7ff072f996386a93d4be (diff) | |
download | sord-11f9eb373f9d29101fbc5e6012c86de462b7b267.tar.gz sord-11f9eb373f9d29101fbc5e6012c86de462b7b267.tar.bz2 sord-11f9eb373f9d29101fbc5e6012c86de462b7b267.zip |
Move headers to a conventional "include" directory
Diffstat (limited to 'include')
-rw-r--r-- | include/sord/sord.h | 657 | ||||
-rw-r--r-- | include/sord/sordmm.hpp | 714 |
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 |