From c9fe9fdb61e15b64d03a8da062648ecd3f86d700 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 12 May 2018 13:28:47 +0200 Subject: WIP: Add model --- serd/serd.h | 484 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 483 insertions(+), 1 deletion(-) (limited to 'serd') diff --git a/serd/serd.h b/serd/serd.h index 160db907..29bff936 100644 --- a/serd/serd.h +++ b/serd/serd.h @@ -82,6 +82,33 @@ typedef struct SerdStatementImpl SerdStatement; */ typedef struct SerdCursorImpl SerdCursor; +/** + Model. + + A model is an indexed set of statements. It may be searched using various + patterns depending on which indices are enabled. +*/ +typedef struct SerdModelImpl SerdModel; + +/** + 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 SerdInserterImpl SerdInserter; + +/** + Model Iterator. +*/ +typedef struct SerdIterImpl SerdIter; + +/** + Model Range. +*/ +typedef struct SerdRangeImpl SerdRange; + /** Environment. @@ -127,6 +154,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) */ @@ -179,6 +207,18 @@ typedef enum { */ typedef uint32_t SerdStatementFlags; +/** + Flags that control style for a model serialisation. +*/ +typedef enum { + SERD_NO_INLINE_OBJECTS = 1 << 0, /**< Do not inline objects where possible */ +} SerdSerialisationFlag; + +/** + Bitwise OR of SerdStatementFlag values. +*/ +typedef uint32_t SerdSerialisationFlags; + /** Type of a syntactic RDF node. @@ -253,6 +293,35 @@ typedef enum { SERD_GRAPH = 3 /**< Graph ("context") */ } SerdField; +/** + Flags for model features. +*/ +typedef enum { + SERD_MODEL_GRAPHS = 1 /**< Support multiple graphs in model. */ +} SerdModelFlag; + +/** + Bitwise OR of SerdModelFlag values. +*/ +typedef uint32_t SerdModelFlags; + +/** + Indexing option. +*/ +typedef enum { + SERD_SPO = 1, /**< Subject, Predicate, Object */ + SERD_SOP = 1 << 1, /**< Subject, Object, Predicate */ + SERD_OPS = 1 << 2, /**< Object, Predicate, Subject */ + SERD_OSP = 1 << 3, /**< Object, Subject, Predicate */ + SERD_PSO = 1 << 4, /**< Predicate, Subject, Object */ + SERD_POS = 1 << 5 /**< Predicate, Object, Subject */ +} SerdIndexOption; + +/** + Bitwise OR of SerdIndexOption values. +*/ +typedef uint32_t SerdIndexOptions; + /** Bitwise OR of SerdNodeFlag values. */ @@ -633,6 +702,16 @@ SERD_API bool serd_node_equals(const SerdNode* a, const SerdNode* b); +/** + Compare two nodes. + + Returns less than, equal to, or greater than zero if `a` is less than, equal + to, or greater than `b`, respectively. +*/ +SERD_API +int +serd_node_compare(const SerdNode* a, const SerdNode* b); + /** Create a new URI from a string. */ @@ -983,6 +1062,13 @@ serd_env_foreach(const SerdEnv* env, SerdPrefixSink func, void* handle); +/** + Send all prefixes in `env` to `sink`. +*/ +SERD_API +void +serd_env_send_prefixes(const SerdEnv* env, SerdSink* sink); + /** @} @name Sink @@ -1233,7 +1319,7 @@ serd_writer_free(SerdWriter* writer); Return a sink interface that emits statements via `writer`. */ SERD_API -const SerdSink* +SerdSink* serd_writer_get_sink(SerdWriter* writer); /** @@ -1362,6 +1448,251 @@ SERD_API void serd_nodes_deref(SerdNodes* nodes, const SerdNode* node); +/** + @} + @name Model + @{ +*/ + +/** + Create a new model. + + @param world The world in which to make this model. + + @param indices 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). + + @param flags Model options. +*/ +SERD_API +SerdModel* +serd_model_new(SerdWorld* world, SerdIndexOptions indices, SerdModelFlags flags); + +/** + Return a deep copy of `model`. +*/ +SERD_API +SerdModel* +serd_model_copy(const SerdModel* model); + +/** + Return true iff `a` is equal to `b`, ignoring statement cursor metadata. +*/ +SERD_API +bool +serd_model_equals(const SerdModel* a, const SerdModel* b); + +/** + Close and free `model`. +*/ +SERD_API +void +serd_model_free(SerdModel* model); + +/** + Get the world associated with `model`. +*/ +SERD_API +SerdWorld* +serd_model_get_world(SerdModel* model); + +/** + Return the number of statements stored in `model`. +*/ +SERD_API +size_t +serd_model_size(const SerdModel* model); + +/** + Return true iff there are no statements stored in `model`. +*/ +SERD_API +bool +serd_model_empty(const SerdModel* model); + +/** + Return an iterator to the start of `model`. +*/ +SERD_API +SerdIter* +serd_model_begin(const SerdModel* model); + +/** + Return an iterator to the end of `model`. +*/ +SERD_API +const SerdIter* +serd_model_end(const SerdModel* model); + +/** + Return a range of all statements in `model`. +*/ +SERD_API +SerdRange* +serd_model_all(const SerdModel* 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_model_find(const SerdModel* model, + const SerdNode* s, + const SerdNode* p, + const SerdNode* o, + const SerdNode* g); + +/** + Search for statements by a quad pattern. + @return A range containins all matching statements. +*/ +SERD_API +SerdRange* +serd_model_range(const SerdModel* model, + const SerdNode* s, + const SerdNode* p, + const SerdNode* o, + const SerdNode* 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_model_get(const SerdModel* model, + const SerdNode* s, + const SerdNode* p, + const SerdNode* o, + const SerdNode* 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_model_get_statement(const SerdModel* model, + const SerdNode* s, + const SerdNode* p, + const SerdNode* o, + const SerdNode* g); + +/** + Return true iff a statement exists. +*/ +SERD_API +bool +serd_model_ask(const SerdModel* model, + const SerdNode* s, + const SerdNode* p, + const SerdNode* o, + const SerdNode* g); + +/** + Return the number of matching statements. +*/ +SERD_API +size_t +serd_model_count(const SerdModel* model, + const SerdNode* s, + const SerdNode* p, + const SerdNode* o, + const SerdNode* 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* model, + const SerdNode* s, + const SerdNode* p, + const SerdNode* o, + const SerdNode* g); + +/** + Add a statement to a model. + + This function fails if there are any active iterators on `model`. +*/ +SERD_API +SerdStatus +serd_model_insert(SerdModel* model, const SerdStatement* 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* model, SerdRange* 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* model, SerdIter* 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* model, SerdRange* range); + +/** + @} + @name Inserter + @{ +*/ + +/** + Create an inserter for writing statements to a model. +*/ +SERD_API +SerdInserter* +serd_inserter_new(SerdModel* model, + SerdEnv* env, + const SerdNode* default_graph); + +/** + Free an inserter. +*/ +SERD_API +void +serd_inserter_free(SerdInserter* inserter); + +/** + Return a sink interface that adds statements via `inserter`. +*/ +SERD_API +const SerdSink* +serd_inserter_get_sink(SerdInserter* inserter); + /** @} @name Statement @@ -1410,6 +1741,157 @@ SERD_API const SerdCursor* serd_statement_get_cursor(const SerdStatement* 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_API +bool +serd_statement_equals(const SerdStatement* a, const SerdStatement* b); + +/** + Return true iff `statement` matches the given pattern. + + The matching rules are the same used for querying: nodes match if they are + equivalent, and NULL acts as a wildcard that matches any node. +*/ +SERD_API +bool +serd_statement_matches(const SerdStatement* statement, + const SerdNode* subject, + const SerdNode* predicate, + const SerdNode* object, + const SerdNode* graph); + +/** + @} + @name Iterator + @{ +*/ + +/** + Return a new copy of `iter`. +*/ +SERD_API +SerdIter* +serd_iter_copy(const SerdIter* iter); + +/** + Return the statement pointed to by `iter`. +*/ +SERD_API +const SerdStatement* +serd_iter_get(const SerdIter* iter); + +/** + Increment `iter` to point to the next statement. + + @return True iff `iter` has reached the end. +*/ +SERD_API +bool +serd_iter_next(SerdIter* iter); + +/** + Return true iff `lhs` is equal to `rhs`. +*/ +SERD_API +bool +serd_iter_equals(const SerdIter* lhs, const SerdIter* rhs); + +/** + Free `iter`. +*/ +SERD_API +void +serd_iter_free(SerdIter* iter); + +/** + @} + @name Range + @{ +*/ + +/** + Return a new copy of `range`. +*/ +SERD_API +SerdRange* +serd_range_copy(const SerdRange* range); + +/** + Free `range`. +*/ +SERD_API +void +serd_range_free(SerdRange* range); + +/** + Return the first statement in `range`, or NULL if `range` is empty. +*/ +SERD_API +const SerdStatement* +serd_range_front(const SerdRange* range); + +/** + Return true iff `lhs` is equal to `rhs`. +*/ +SERD_API +bool +serd_range_equals(const SerdRange* lhs, const SerdRange* rhs); + +/** + Increment the start of `range` to point to the next statement. +*/ +SERD_API +bool +serd_range_next(SerdRange* range); + +/** + Return the number of statements in `range`. +*/ +SERD_API +size_t +serd_range_size(const SerdRange* range); + +/** + Return true iff there are no statements in `range`. +*/ +SERD_API +bool +serd_range_empty(const SerdRange* range); + +/** + Return an iterator to the start of `range`. +*/ +SERD_API +const SerdIter* +serd_range_begin(const SerdRange* range); + +/** + Return an iterator to the end of `range`. +*/ +SERD_API +const SerdIter* +serd_range_end(const SerdRange* 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* range, + const SerdSink* sink, + SerdSerialisationFlags flags); + /** @} @name Cursor -- cgit v1.2.1