From 677d85c7f3df3563e990f41964f6ae7d560567a0 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 12 May 2018 13:28:47 +0200 Subject: Add model --- include/serd/serd.h | 420 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 419 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/serd/serd.h b/include/serd/serd.h index 04d8708b..14e321b5 100644 --- a/include/serd/serd.h +++ b/include/serd/serd.h @@ -102,6 +102,15 @@ typedef struct SerdCursorImpl SerdCursor; /// Lexical environment for relative URIs or CURIEs (base URI and namespaces) typedef struct SerdEnvImpl SerdEnv; +/// An indexed set of statements +typedef struct SerdModelImpl SerdModel; + +/// An iterator that points to a statement in a model +typedef struct SerdIterImpl SerdIter; + +/// A range of statements in a model +typedef struct SerdRangeImpl SerdRange; + /// Streaming parser that reads a text stream and writes to a sink typedef struct SerdReaderImpl SerdReader; @@ -118,6 +127,7 @@ typedef enum { SERD_ERR_UNKNOWN, ///< Unknown error SERD_ERR_BAD_SYNTAX, ///< Invalid syntax SERD_ERR_BAD_ARG, ///< Invalid argument + SERD_ERR_BAD_ITER, ///< Use of invalidated iterator SERD_ERR_NOT_FOUND, ///< Not found SERD_ERR_ID_CLASH, ///< Encountered clashing blank node IDs SERD_ERR_BAD_CURIE, ///< Invalid CURIE (e.g. prefix does not exist) @@ -153,6 +163,14 @@ typedef enum { /// Bitwise OR of SerdStatementFlag values typedef uint32_t SerdStatementFlags; +/// Flags that control the style of a model serialisation +typedef enum { + SERD_NO_INLINE_OBJECTS = 1u << 0u ///< Disable object inlining +} SerdSerialisationFlag; + +/// Bitwise OR of SerdStatementFlag values +typedef uint32_t SerdSerialisationFlags; + /** Type of a node. @@ -229,7 +247,22 @@ typedef enum { SERD_GRAPH = 3 ///< Graph ("context") } SerdField; -/// A syntactic RDF node +/// Flags that control model storage and indexing +typedef enum { + SERD_INDEX_SPO = 1u << 0u, ///< Subject, Predicate, Object + SERD_INDEX_SOP = 1u << 1u, ///< Subject, Object, Predicate + SERD_INDEX_OPS = 1u << 2u, ///< Object, Predicate, Subject + SERD_INDEX_OSP = 1u << 3u, ///< Object, Subject, Predicate + SERD_INDEX_PSO = 1u << 4u, ///< Predicate, Subject, Object + SERD_INDEX_POS = 1u << 5u, ///< Predicate, Object, Subject + SERD_INDEX_GRAPHS = 1u << 6u, ///< Support multiple graphs in model + SERD_STORE_CURSORS = 1u << 7u ///< Store original cursor of statements +} SerdModelFlag; + +/// Bitwise OR of SerdModelFlag values +typedef uint32_t SerdModelFlags; + +/// An RDF node typedef struct SerdNodeImpl SerdNode; /// An unterminated immutable slice of a string @@ -1661,12 +1694,263 @@ void serd_nodes_deref(SerdNodes* SERD_NONNULL nodes, const SerdNode* SERD_NONNULL node); +/** + @} + @defgroup serd_model Model + @{ +*/ + +/** + Create a new model + + @param world The world in which to make this model. + + @param flags Model options, including enabled indices, for example `SERD_SPO + | SERD_OPS`. Be sure to enable an index where the most significant node(s) + are not variables in your queries. For example, to make (? P O) queries, + enable either SERD_OPS or SERD_POS. +*/ +SERD_API +SerdModel* SERD_ALLOCATED +serd_model_new(SerdWorld* SERD_NONNULL world, SerdModelFlags flags); + +/// Return a deep copy of `model` +SERD_API +SerdModel* SERD_ALLOCATED +serd_model_copy(const SerdModel* SERD_NONNULL model); + +/// Return true iff `a` is equal to `b`, ignoring statement cursor metadata +SERD_API +bool +serd_model_equals(const SerdModel* SERD_NULLABLE a, + const SerdModel* SERD_NULLABLE b); + +/// Close and free `model` +SERD_API +void +serd_model_free(SerdModel* SERD_NULLABLE model); + +/// Get the world associated with `model` +SERD_PURE_API +SerdWorld* SERD_NONNULL +serd_model_world(SerdModel* SERD_NONNULL model); + +/// Get the flags enabled on `model` +SERD_PURE_API +SerdModelFlags +serd_model_flags(const SerdModel* SERD_NONNULL model); + +/// Return the number of statements stored in `model` +SERD_PURE_API +size_t +serd_model_size(const SerdModel* SERD_NONNULL model); + +/// Return true iff there are no statements stored in `model` +SERD_PURE_API +bool +serd_model_empty(const SerdModel* SERD_NONNULL model); + +/// Return an iterator to the start of `model` +SERD_API +SerdIter* SERD_ALLOCATED +serd_model_begin(const SerdModel* SERD_NONNULL model); + +/// Return an iterator to the end of `model` +SERD_PURE_API +const SerdIter* SERD_NONNULL +serd_model_end(const SerdModel* SERD_NONNULL model); + +/// Return a range of all statements in `model` +SERD_API +SerdRange* SERD_ALLOCATED +serd_model_all(const SerdModel* SERD_NONNULL model); + +/** + Search for statements by a quad pattern + + @return An iterator to the first match, or NULL if no matches found. +*/ +SERD_API +SerdIter* SERD_ALLOCATED +serd_model_find(const SerdModel* SERD_NONNULL model, + const SerdNode* SERD_NULLABLE s, + const SerdNode* SERD_NULLABLE p, + const SerdNode* SERD_NULLABLE o, + const SerdNode* SERD_NULLABLE g); + +/** + Search for statements by a quad pattern + + @return A range containing all matching statements. +*/ +SERD_API +SerdRange* SERD_ALLOCATED +serd_model_range(const SerdModel* SERD_NONNULL model, + const SerdNode* SERD_NULLABLE s, + const SerdNode* SERD_NULLABLE p, + const SerdNode* SERD_NULLABLE o, + const SerdNode* SERD_NULLABLE 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. + @return The first matching node, or NULL if no matches are found. +*/ +SERD_API +const SerdNode* SERD_NULLABLE +serd_model_get(const SerdModel* SERD_NONNULL model, + const SerdNode* SERD_NULLABLE s, + const SerdNode* SERD_NULLABLE p, + const SerdNode* SERD_NULLABLE o, + const SerdNode* SERD_NULLABLE g); + +/** + Search for a single statement that matches a pattern + + This function is mainly useful for predicates that only have one value. + + @return The first matching statement, or NULL if none are found. +*/ +SERD_API +const SerdStatement* SERD_NULLABLE +serd_model_get_statement(const SerdModel* SERD_NONNULL model, + const SerdNode* SERD_NULLABLE s, + const SerdNode* SERD_NULLABLE p, + const SerdNode* SERD_NULLABLE o, + const SerdNode* SERD_NULLABLE g); + +/// Return true iff a statement exists +SERD_API +bool +serd_model_ask(const SerdModel* SERD_NONNULL model, + const SerdNode* SERD_NULLABLE s, + const SerdNode* SERD_NULLABLE p, + const SerdNode* SERD_NULLABLE o, + const SerdNode* SERD_NULLABLE g); + +/// Return the number of matching statements +SERD_API +size_t +serd_model_count(const SerdModel* SERD_NONNULL model, + const SerdNode* SERD_NULLABLE s, + const SerdNode* SERD_NULLABLE p, + const SerdNode* SERD_NULLABLE o, + const SerdNode* SERD_NULLABLE g); + +/** + Add a statement to a model from nodes + + This function fails if there are any active iterators on `model`. +*/ +SERD_API +SerdStatus +serd_model_add(SerdModel* SERD_NONNULL model, + const SerdNode* SERD_NULLABLE s, + const SerdNode* SERD_NULLABLE p, + const SerdNode* SERD_NULLABLE o, + const SerdNode* SERD_NULLABLE g); + +/** + Add a statement to a model + + This function fails if there are any active iterators on `model`. + If statement is null, then SERD_FAILURE is returned. +*/ +SERD_API +SerdStatus +serd_model_insert(SerdModel* SERD_NONNULL model, + const SerdStatement* SERD_NULLABLE statement); + +/** + Add a range of statements to a model + + This function fails if there are any active iterators on `model`. +*/ +SERD_API +SerdStatus +serd_model_add_range(SerdModel* SERD_NONNULL model, + SerdRange* SERD_NONNULL range); + +/** + 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. +*/ +SERD_API +SerdStatus +serd_model_erase(SerdModel* SERD_NONNULL model, SerdIter* SERD_NONNULL iter); + +/** + Remove a range from a model + + Calling this function invalidates all iterators on `model` except `iter`. + + @param model The model which `range` points to. + @param range Range to erase, which will be empty on return; +*/ +SERD_API +SerdStatus +serd_model_erase_range(SerdModel* SERD_NONNULL model, + SerdRange* SERD_NONNULL range); + +/** + @} + @defgroup serd_inserter Inserter + @{ +*/ + +/// Create an inserter for writing statements to a model +SERD_API +SerdSink* SERD_ALLOCATED +serd_inserter_new(SerdModel* SERD_NONNULL model, + SerdEnv* SERD_NONNULL env, + const SerdNode* SERD_NULLABLE default_graph); + /** @} @defgroup serd_statement Statement @{ */ +/** + Create a new statement + + Note that, to minimise model overhead, statements do not own their nodes, so + they must have a longer lifetime than the statement for it to be valid. For + statements in models, this is the lifetime of the model. For user-created + statements, the simplest way to handle this is to use `SerdNodes`. + + @param s The subject + @param p The predicate ("key") + @param o The object ("value") + @param g The graph ("context") + @param cursor Optional cursor at the origin of this statement + @return A new statement that must be freed with serd_statement_free() +*/ +SERD_API +SerdStatement* SERD_ALLOCATED +serd_statement_new(const SerdNode* SERD_NONNULL s, + const SerdNode* SERD_NONNULL p, + const SerdNode* SERD_NONNULL o, + const SerdNode* SERD_NULLABLE g, + const SerdCursor* SERD_NULLABLE cursor); + +/// Return a copy of `statement` +SERD_API +SerdStatement* SERD_ALLOCATED +serd_statement_copy(const SerdStatement* SERD_NULLABLE statement); + +/// Free `statement` +SERD_API +void +serd_statement_free(SerdStatement* SERD_NULLABLE statement); + /// Return the given node in `statement` SERD_PURE_API const SerdNode* SERD_NULLABLE @@ -1698,6 +1982,140 @@ SERD_PURE_API const SerdCursor* SERD_NULLABLE serd_statement_cursor(const SerdStatement* SERD_NONNULL statement); +/** + Return true iff `a` is equal to `b`, ignoring statement cursor metadata + + Only returns true if nodes are equivalent, does not perform wildcard + matching. +*/ +SERD_PURE_API +bool +serd_statement_equals(const SerdStatement* SERD_NULLABLE a, + const SerdStatement* SERD_NULLABLE b); + +/** + Return true iff `statement` matches the given pattern + + Nodes match if they are equivalent, or if one of them is NULL. The + statement matches if every node matches. +*/ +SERD_PURE_API +bool +serd_statement_matches(const SerdStatement* SERD_NONNULL statement, + const SerdNode* SERD_NULLABLE subject, + const SerdNode* SERD_NULLABLE predicate, + const SerdNode* SERD_NULLABLE object, + const SerdNode* SERD_NULLABLE graph); + +/** + @} + @defgroup serd_iterator Iterator + @{ +*/ + +/// Return a new copy of `iter` +SERD_API +SerdIter* SERD_ALLOCATED +serd_iter_copy(const SerdIter* SERD_NULLABLE iter); + +/// Return the statement pointed to by `iter` +SERD_API +const SerdStatement* SERD_NULLABLE +serd_iter_get(const SerdIter* SERD_NONNULL iter); + +/** + Increment `iter` to point to the next statement + + @return True iff `iter` has reached the end. +*/ +SERD_API +bool +serd_iter_next(SerdIter* SERD_NONNULL iter); + +/// Return true iff `lhs` is equal to `rhs` +SERD_PURE_API +bool +serd_iter_equals(const SerdIter* SERD_NULLABLE lhs, + const SerdIter* SERD_NULLABLE rhs); + +/// Free `iter` +SERD_API +void +serd_iter_free(SerdIter* SERD_NULLABLE iter); + +/** + @} + @defgroup serd_range Range + @{ +*/ + +/// Return a new copy of `range` +SERD_API +SerdRange* SERD_ALLOCATED +serd_range_copy(const SerdRange* SERD_NULLABLE range); + +/// Free `range` +SERD_API +void +serd_range_free(SerdRange* SERD_NULLABLE range); + +/// Return the first statement in `range`, or NULL if `range` is empty +SERD_API +const SerdStatement* SERD_NULLABLE +serd_range_front(const SerdRange* SERD_NONNULL range); + +/// Return true iff `lhs` is equal to `rhs` +SERD_PURE_API +bool +serd_range_equals(const SerdRange* SERD_NULLABLE lhs, + const SerdRange* SERD_NULLABLE rhs); + +/// Increment the start of `range` to point to the next statement +SERD_API +bool +serd_range_next(SerdRange* SERD_NONNULL range); + +/// Return true iff there are no statements in `range` +SERD_PURE_API +bool +serd_range_empty(const SerdRange* SERD_NULLABLE range); + +/// Return an iterator to the start of `range` +SERD_PURE_API +const SerdIter* SERD_NULLABLE +serd_range_cbegin(const SerdRange* SERD_NONNULL range); + +/// Return an iterator to the end of `range` +SERD_PURE_API +const SerdIter* SERD_NULLABLE +serd_range_cend(const SerdRange* SERD_NONNULL range); + +/// Return an iterator to the start of `range` +SERD_PURE_API +SerdIter* SERD_NULLABLE +serd_range_begin(SerdRange* SERD_NONNULL range); + +/// Return an iterator to the end of `range` +SERD_PURE_API +SerdIter* SERD_NULLABLE +serd_range_end(SerdRange* SERD_NONNULL range); + +/** + Write `range` to `sink` + + The serialisation style can be controlled with `flags`. The default is to + write statements in an order suited for pretty-printing with Turtle or TriG + with as many objects written inline as possible. If + `SERD_NO_INLINE_OBJECTS` is given, a simple sorted stream is written + instead, which is significantly faster since no searching is required, but + can result in ugly output for Turtle or Trig. +*/ +SERD_API +SerdStatus +serd_range_serialise(const SerdRange* SERD_NONNULL range, + const SerdSink* SERD_NONNULL sink, + SerdSerialisationFlags flags); + /** @} @defgroup serd_cursor Cursor -- cgit v1.2.1