|
@@ -4,6 +4,9 @@
|
|
|
// Initial size of lookup graph. It will double each time capacity is reached.
|
|
|
#define LOOKUP_GR_INIT_SIZE 64
|
|
|
|
|
|
+// Expand hash table memory by this factor to keep a good load factor.
|
|
|
+#define PREALLOC_FACTOR 1.4
|
|
|
+
|
|
|
// Assume VERY coarsly that the number of unique terms will be in general
|
|
|
// 1.7 times the number of triples. This is conservative to maintain load
|
|
|
// factor low.
|
|
@@ -21,99 +24,34 @@ typedef enum KSetFlag {
|
|
|
* Static handles.
|
|
|
*/
|
|
|
static const char *default_ctx_label = "urn:lsup:default";
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * Identity hashing function.
|
|
|
- *
|
|
|
- * Since the key is already a strong hash, reuse it for bucket allocation.
|
|
|
- */
|
|
|
-static inline uint64_t id_hash_fn(const void *key, ksize_t size, uint64_t seed)
|
|
|
-{ return *(uint64_t*)key; }
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * General XX64 hash. Strong (non-crypto) and extremely fast.
|
|
|
- */
|
|
|
-static inline uint64_t xx64_hash_fn(
|
|
|
- const void *key, ksize_t size, uint64_t seed)
|
|
|
-{ return XXH64(key, size, seed); }
|
|
|
-
|
|
|
-
|
|
|
-static inline bool buffer_eq_fn(const void *a, const void *b, ksize_t size)
|
|
|
-{ return memcmp(a, b, size) == 0; }
|
|
|
+static LSUP_Buffer *default_ctx = NULL;
|
|
|
|
|
|
|
|
|
typedef struct Graph {
|
|
|
- LSUP_store_type store_type; // In-memory or MDB-backed
|
|
|
- LSUP_Term *uri; // Graph "name" (URI)
|
|
|
- LSUP_HTable *keys;
|
|
|
- LSUP_HTable *idx; // Dictionary of keys to serialized terms
|
|
|
+ LSUP_store_type store_type; // In-memory or MDB-backed
|
|
|
+ LSUP_Term *uri; // Graph "name" (URI)
|
|
|
+ union {
|
|
|
+ LSUP_HTStore * ht_store;
|
|
|
+ LSUP_MDBStore * mdb_store;
|
|
|
+ };
|
|
|
} Graph;
|
|
|
|
|
|
-/**
|
|
|
- * Extern inline functions.
|
|
|
- */
|
|
|
-size_t LSUP_graph_size(const LSUP_Graph *gr);
|
|
|
-
|
|
|
-size_t LSUP_graph_capacity(const LSUP_Graph *gr);
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * Callback type for key comparison.
|
|
|
- */
|
|
|
-typedef bool (*LSUP_key_cmp_fn_t)(
|
|
|
- const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2);
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * Dummy callback for queries with all parameters unbound. Returns true.
|
|
|
-static bool lookup_none_cmp_fn(
|
|
|
- const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2)
|
|
|
-{ return true; }
|
|
|
-*/
|
|
|
|
|
|
-/**
|
|
|
- * Keyset lookup for S key.
|
|
|
- */
|
|
|
-static bool lookup_sk_cmp_fn(
|
|
|
- const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2)
|
|
|
-{ return spok[0][0] == k1; }
|
|
|
-
|
|
|
-/**
|
|
|
- * Keyset lookup for P key.
|
|
|
- */
|
|
|
-static bool lookup_pk_cmp_fn(
|
|
|
- const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2)
|
|
|
-{ return spok[0][1] == k1; }
|
|
|
-
|
|
|
-/**
|
|
|
- * Keyset lookup for O key.
|
|
|
- */
|
|
|
-static bool lookup_ok_cmp_fn(
|
|
|
- const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2)
|
|
|
-{ return spok[0][2] == k1; }
|
|
|
-
|
|
|
-/**
|
|
|
- * Keyset lookup for S and P keys.
|
|
|
- */
|
|
|
-static bool lookup_skpk_cmp_fn(
|
|
|
- const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2)
|
|
|
-{ return spok[0][0] == k1 && spok[0][1] == k2; }
|
|
|
+typedef struct GraphIterator {
|
|
|
+ const Graph * graph; // Parent graph.
|
|
|
+ union { // Internal store iterator.
|
|
|
+ LSUP_HTIterator * ht_iter;
|
|
|
+ LSUP_MDBIterator * mdb_iter;
|
|
|
+ };
|
|
|
+ size_t ct; // Total matches.
|
|
|
+ size_t i; // Cursor.
|
|
|
+} GraphIterator;
|
|
|
|
|
|
-/**
|
|
|
- * Keyset lookup for S and O keys.
|
|
|
- */
|
|
|
-static bool lookup_skok_cmp_fn(
|
|
|
- const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2)
|
|
|
-{ return spok[0][0] == k1 && spok[0][2] == k2; }
|
|
|
|
|
|
/**
|
|
|
- * Keyset lookup for P and O keys.
|
|
|
+ * Extern inline functions.
|
|
|
*/
|
|
|
-static bool lookup_pkok_cmp_fn(
|
|
|
- const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2)
|
|
|
-{ return spok[0][1] == k1 && spok[0][2] == k2; }
|
|
|
+size_t LSUP_graph_size(const LSUP_Graph *gr);
|
|
|
|
|
|
|
|
|
/* * * Post-lookup callback prototypes * * */
|
|
@@ -127,9 +65,13 @@ int match_rm_fn(
|
|
|
LSUP_Graph *src, LSUP_Graph *dest, const LSUP_TripleKey *spok,
|
|
|
void *ctx);
|
|
|
|
|
|
+static LSUP_rc
|
|
|
+graph_iter_next_buffer(GraphIterator *it, LSUP_SerTriple *sspo);
|
|
|
+
|
|
|
|
|
|
+/* Atexit functions. */
|
|
|
+void ctx_cleanup() { free(default_ctx); }
|
|
|
|
|
|
-/* * * KEYSETS * * */
|
|
|
|
|
|
static inline bool is_null_trp(const LSUP_TripleKey *trp)
|
|
|
{
|
|
@@ -142,403 +84,386 @@ static inline bool is_null_trp(const LSUP_TripleKey *trp)
|
|
|
|
|
|
/* * * GRAPH * * */
|
|
|
|
|
|
-int
|
|
|
-LSUP_graph_init(
|
|
|
- LSUP_Graph *gr, size_t capacity, char *uri_str,
|
|
|
- LSUP_store_type store_type)
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_new(const LSUP_store_type store_type, Graph **gr_p)
|
|
|
{
|
|
|
- if (uri_str == NULL) {
|
|
|
- gr->uri = LSUP_term_new(
|
|
|
- LSUP_TERM_URI, LSUP_term_gen_random_str(), NULL, NULL);
|
|
|
- } else {
|
|
|
- gr->uri = LSUP_term_new(LSUP_TERM_URI, uri_str, NULL, NULL);
|
|
|
- }
|
|
|
-
|
|
|
- gr->keys = LSUP_htable_new(
|
|
|
- capacity, TRP_KLEN, 0, xx64_hash_fn, buffer_eq_fn, 0);
|
|
|
-
|
|
|
- switch (store_type ) {
|
|
|
- case LSUP_STORE_MEM:
|
|
|
- gr->idx = LSUP_htable_new(
|
|
|
- capacity * IDX_SIZE_RATIO, sizeof(uint64_t), sizeof(uintptr_t),
|
|
|
- xx64_hash_fn, buffer_eq_fn, 0);
|
|
|
- break;
|
|
|
+ LSUP_Graph *gr;
|
|
|
+ CRITICAL(gr = malloc(sizeof(LSUP_Graph)));
|
|
|
|
|
|
- case LSUP_STORE_MDB:
|
|
|
- // TODO
|
|
|
+ *gr_p = gr;
|
|
|
|
|
|
- default:
|
|
|
- return -1;
|
|
|
+ // Initialize default context only once per process.
|
|
|
+ if(UNLIKELY(!default_ctx)) {
|
|
|
+ LSUP_Term *default_ctx_uri = LSUP_uri_new(default_ctx_label);
|
|
|
+ LSUP_term_serialize(default_ctx_uri, default_ctx);
|
|
|
+ LSUP_term_free(default_ctx_uri);
|
|
|
+ atexit(ctx_cleanup);
|
|
|
}
|
|
|
|
|
|
- return LSUP_OK;
|
|
|
-}
|
|
|
+ if (store_type == LSUP_STORE_MEM) {
|
|
|
+ LSUP_htstore_new(0, &gr->ht_store);
|
|
|
|
|
|
+ } else if (store_type == LSUP_STORE_MDB) {
|
|
|
+ LSUP_mdbstore_new(
|
|
|
+ getenv("LSUP_MDB_STORE_PATH"), default_ctx, &gr->mdb_store);
|
|
|
|
|
|
-LSUP_Graph *
|
|
|
-LSUP_graph_new(size_t capacity, char *uri_str, LSUP_store_type store_type)
|
|
|
-{
|
|
|
- LSUP_Graph *gr;
|
|
|
- CRITICAL(gr = malloc(sizeof(LSUP_Graph)));
|
|
|
+ } else return LSUP_VALUE_ERR;
|
|
|
|
|
|
- LSUP_graph_init(gr, capacity, uri_str, store_type);
|
|
|
-
|
|
|
- return gr;
|
|
|
+ return LSUP_OK;
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* Copy triples from a source graph into a destination one.
|
|
|
*
|
|
|
- * The destination graph is not initialized, so the copy is cumulative.
|
|
|
+ * The destination graph is not initialized here, so the copy is cumulative.
|
|
|
*/
|
|
|
-static int graph_copy_contents(LSUP_Graph *src, LSUP_Graph *dest)
|
|
|
+static LSUP_rc
|
|
|
+graph_copy_contents(const LSUP_Graph *src, LSUP_Graph *dest)
|
|
|
{
|
|
|
- LSUP_Triple trp;
|
|
|
- trp.s = NULL;
|
|
|
- trp.p = NULL;
|
|
|
- trp.o = NULL;
|
|
|
+ LSUP_rc rc = LSUP_NOACTION;
|
|
|
+ const LSUP_Triple trp = {NULL, NULL, NULL};
|
|
|
+ GraphIterator *it;
|
|
|
|
|
|
- return LSUP_graph_match_callback(
|
|
|
- src, dest, &trp, &match_add_fn, true, NULL);
|
|
|
-}
|
|
|
+ LSUP_graph_lookup(src, &trp, &it);
|
|
|
|
|
|
+ LSUP_SerTriple sspo;
|
|
|
|
|
|
-int
|
|
|
-LSUP_graph_copy(LSUP_Graph *dest, LSUP_Graph *src)
|
|
|
-{
|
|
|
- LSUP_graph_init(dest, LSUP_graph_size(src), NULL, src->store_type);
|
|
|
+ while (graph_iter_next_buffer(it, &sspo) != LSUP_END) {
|
|
|
+ TRACE("Inserting triple #%lu\n", it->i);
|
|
|
+ LSUP_rc add_rc = LSUP_graph_add(dest, NULL, 0, &sspo, 1);
|
|
|
+ if (LIKELY (add_rc == LSUP_OK)) rc = LSUP_OK;
|
|
|
+ else if (add_rc < 0) return add_rc;
|
|
|
+ }
|
|
|
|
|
|
- return graph_copy_contents(src, dest);
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
|
|
|
-int
|
|
|
-LSUP_graph_resize(LSUP_Graph *gr, size_t size)
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_copy(const Graph *src, Graph **dest_p)
|
|
|
{
|
|
|
- LSUP_htable_resize(gr->keys, size);
|
|
|
- LSUP_htable_resize(gr->idx, size * IDX_SIZE_RATIO);
|
|
|
-
|
|
|
- return LSUP_OK;
|
|
|
-}
|
|
|
+ LSUP_rc rc;
|
|
|
+ LSUP_Graph *dest;
|
|
|
|
|
|
+ rc = LSUP_graph_new(src->store_type, &dest);
|
|
|
+ if (UNLIKELY (rc != LSUP_OK)) return rc;
|
|
|
|
|
|
-size_t
|
|
|
-LSUP_graph_capacity(const LSUP_Graph *gr)
|
|
|
-{ return LSUP_htable_capacity(gr->keys); }
|
|
|
-
|
|
|
-
|
|
|
-LSUP_Term *
|
|
|
-LSUP_graph_uri(const LSUP_Graph *gr) { return gr->uri; }
|
|
|
+ rc = graph_copy_contents(src, dest);
|
|
|
|
|
|
+ if (LIKELY (rc == LSUP_OK)) *dest_p = dest;
|
|
|
|
|
|
-size_t
|
|
|
-LSUP_graph_size(const LSUP_Graph *gr) { return LSUP_htable_size(gr->keys); }
|
|
|
+ return rc;
|
|
|
+}
|
|
|
|
|
|
|
|
|
-int
|
|
|
-LSUP_graph_add_triple(LSUP_Graph *gr, const LSUP_Triple *spo)
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_bool_op(
|
|
|
+ const LSUP_bool_op op, const Graph *gr1, const Graph *gr2,
|
|
|
+ Graph **res_p)
|
|
|
{
|
|
|
- LSUP_SerTerm sspo[3];
|
|
|
-
|
|
|
- LSUP_term_serialize(spo->s, sspo);
|
|
|
- LSUP_term_serialize(spo->p, sspo + 1);
|
|
|
- LSUP_term_serialize(spo->o, sspo + 2);
|
|
|
-
|
|
|
- LSUP_TripleKey spok = NULL_TRP;
|
|
|
-
|
|
|
- // Add term to index.
|
|
|
- for (int i = 0; i < 3; i++) {
|
|
|
- spok[i] = LSUP_sterm_to_key(sspo + i);
|
|
|
- TRACE("Indexing term key %lu\n", spok[i]);
|
|
|
-
|
|
|
- // If term is already in the index, discard and free it.
|
|
|
- if (LSUP_htable_get(gr->idx, spok + i, NULL) == LSUP_OK) {
|
|
|
- //LSUP_SerTerm *sterm = sspo + i;
|
|
|
- //CRITICAL(sterm = malloc(sizeof(LSUP_Buffer)));
|
|
|
- LSUP_htable_put(gr->idx, spok + i, sspo + i);
|
|
|
- } else {
|
|
|
- TRACE("%s", "Term is already indexed.");
|
|
|
- LSUP_buffer_done(sspo + i);
|
|
|
- }
|
|
|
+ if (UNLIKELY (gr1->store_type != LSUP_STORE_MEM)) {
|
|
|
+ fprintf(
|
|
|
+ stderr,
|
|
|
+ "First operand %s is not an in-memory graph. "
|
|
|
+ "Cannot perform boolean operation.",
|
|
|
+ gr1->uri->data);
|
|
|
+ return LSUP_VALUE_ERR;
|
|
|
+ }
|
|
|
+ if (UNLIKELY (gr2->store_type != LSUP_STORE_MEM)) {
|
|
|
+ fprintf(
|
|
|
+ stderr,
|
|
|
+ "Second operand %s is not an in-memory graph. "
|
|
|
+ "Cannot perform boolean operation.",
|
|
|
+ gr2->uri->data);
|
|
|
+ return LSUP_VALUE_ERR;
|
|
|
}
|
|
|
|
|
|
- // Add triple.
|
|
|
- TRACE("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
|
|
|
+ LSUP_Graph *res;
|
|
|
+ LSUP_graph_new(LSUP_STORE_MEM, &res);
|
|
|
|
|
|
- return LSUP_htable_put(gr->keys, spok, NULL);
|
|
|
+ return LSUP_htstore_bool_op(
|
|
|
+ op, gr1->ht_store, gr2->ht_store, &res->ht_store);
|
|
|
}
|
|
|
|
|
|
|
|
|
-int
|
|
|
-LSUP_graph_add(LSUP_Graph *gr, const LSUP_Triple data[], size_t data_size)
|
|
|
+void
|
|
|
+LSUP_graph_free(LSUP_Graph *gr)
|
|
|
{
|
|
|
- // TODO Decouple this and build interface for memory and MDB integration.
|
|
|
+ if (LIKELY(gr != NULL)) {
|
|
|
+ LSUP_term_free(gr->uri);
|
|
|
|
|
|
- // Resize all at once if needed.
|
|
|
- if (LSUP_graph_capacity(gr) < LSUP_graph_size(gr) + data_size)
|
|
|
- LSUP_graph_resize(gr, LSUP_graph_size(gr) + data_size);
|
|
|
+ if (gr->store_type == LSUP_STORE_MEM)
|
|
|
+ LSUP_htstore_free(gr->ht_store);
|
|
|
+ else
|
|
|
+ LSUP_mdbstore_free(gr->mdb_store);
|
|
|
|
|
|
- int rc = LSUP_NOACTION;
|
|
|
- for (size_t i = 0; i < data_size; i++) {
|
|
|
- TRACE("Inserting triple #%lu\n", i);
|
|
|
- if (LIKELY(LSUP_graph_add_triple(gr, data + i) == LSUP_OK))
|
|
|
- rc = LSUP_OK;
|
|
|
+ free(gr);
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- return rc;
|
|
|
+
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_resize(LSUP_Graph *gr, size_t size)
|
|
|
+{
|
|
|
+ if (gr->store_type == LSUP_STORE_MEM)
|
|
|
+ return LSUP_htstore_resize(gr->ht_store, size);
|
|
|
+
|
|
|
+ return LSUP_VALUE_ERR;
|
|
|
}
|
|
|
|
|
|
|
|
|
-bool
|
|
|
-LSUP_graph_contains(const LSUP_Graph *gr, const LSUP_Triple *spo)
|
|
|
+LSUP_Term *
|
|
|
+LSUP_graph_uri(const LSUP_Graph *gr) { return gr->uri; }
|
|
|
+
|
|
|
+
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_set_uri(LSUP_Graph *gr, const char *uri)
|
|
|
{
|
|
|
- LSUP_TripleKey spok = {
|
|
|
- LSUP_term_to_key(spo->s),
|
|
|
- LSUP_term_to_key(spo->p),
|
|
|
- LSUP_term_to_key(spo->o),
|
|
|
- };
|
|
|
+ gr->uri = uri ? LSUP_uri_new(uri) : LSUP_uri_random();
|
|
|
|
|
|
- return LSUP_htable_get(gr->keys, spok, NULL) == LSUP_OK;
|
|
|
+ return LSUP_OK;
|
|
|
}
|
|
|
|
|
|
|
|
|
-int
|
|
|
-LSUP_graph_match_callback(
|
|
|
- LSUP_Graph *gr, LSUP_Graph *res, const LSUP_Triple *spo,
|
|
|
- keyset_match_fn_t callback_fn, bool match_cond, void *ctx)
|
|
|
+size_t
|
|
|
+LSUP_graph_capacity(const Graph *gr)
|
|
|
{
|
|
|
- if (LSUP_htable_size(gr->keys) == 0)
|
|
|
- return LSUP_NOACTION;
|
|
|
-
|
|
|
- htsize_t cur = 0;
|
|
|
- LSUP_Key k1, k2;
|
|
|
- LSUP_key_cmp_fn_t cmp_fn;
|
|
|
- LSUP_TripleKey i_spok;
|
|
|
-
|
|
|
- LSUP_TripleKey spok = {
|
|
|
- LSUP_term_to_key(spo->s),
|
|
|
- LSUP_term_to_key(spo->p),
|
|
|
- LSUP_term_to_key(spo->o),
|
|
|
- };
|
|
|
+ if(gr->store_type == LSUP_STORE_MEM)
|
|
|
+ return LSUP_htstore_capacity(gr->ht_store);
|
|
|
|
|
|
- if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
|
|
|
- if (match_cond == true) {
|
|
|
- // Shortcut for 3-term match—only if match_cond is true.
|
|
|
- LSUP_graph_init(res, 1, NULL, LSUP_STORE_MEM);
|
|
|
- int rc = LSUP_htable_get(gr->keys, spok, NULL);
|
|
|
- if(rc == LSUP_OK) {
|
|
|
- callback_fn(gr, res, &spok, ctx);
|
|
|
- return LSUP_OK;
|
|
|
- } else {
|
|
|
- return LSUP_NOACTION;
|
|
|
- }
|
|
|
- } else {
|
|
|
- // For negative condition (i.e. "apply this function to all triples
|
|
|
- // except the matching one")
|
|
|
- int rc = LSUP_NOACTION;
|
|
|
- while (LSUP_htable_iter(
|
|
|
- gr->keys, &cur, (void**)&i_spok, NULL) == LSUP_OK) {
|
|
|
- if (LIKELY(
|
|
|
- i_spok[2] != spok[2] ||
|
|
|
- i_spok[0] != spok[0] ||
|
|
|
- i_spok[1] != spok[1]
|
|
|
- )) {
|
|
|
- rc = callback_fn(gr, res, &i_spok, ctx);
|
|
|
- }
|
|
|
- }
|
|
|
+ return LSUP_NOT_IMPL_ERR;
|
|
|
+}
|
|
|
|
|
|
- return rc;
|
|
|
- }
|
|
|
|
|
|
- } else if (spok[0] != NULL_KEY) {
|
|
|
- k1 = spok[0];
|
|
|
+size_t
|
|
|
+LSUP_graph_size(const Graph *gr)
|
|
|
+{
|
|
|
+ if(gr->store_type == LSUP_STORE_MEM)
|
|
|
+ return LSUP_htstore_size(gr->ht_store);
|
|
|
+
|
|
|
+ return LSUP_mdbstore_size(gr->mdb_store);
|
|
|
+}
|
|
|
|
|
|
- if (spok[1] != NULL_KEY) { // s p ?
|
|
|
- k2 = spok[1];
|
|
|
- cmp_fn = lookup_skpk_cmp_fn;
|
|
|
|
|
|
- } else if (spok[2] != NULL_KEY) { // s ? o
|
|
|
- k2 = spok[2];
|
|
|
- cmp_fn = lookup_skok_cmp_fn;
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_add(
|
|
|
+ LSUP_Graph *gr,
|
|
|
+ const LSUP_Triple trp[], size_t trp_ct,
|
|
|
+ const LSUP_SerTriple strp[], size_t strp_ct)
|
|
|
+{
|
|
|
+ LSUP_rc rc;
|
|
|
|
|
|
- } else { // s ? ?
|
|
|
- cmp_fn = lookup_sk_cmp_fn;
|
|
|
+ /*
|
|
|
+ * NOTE It is possible to pass both sets of RDF triples and buffer triples.
|
|
|
+ */
|
|
|
|
|
|
+ if (gr->store_type == LSUP_STORE_MEM) {
|
|
|
+ // Resize all at once if needed.
|
|
|
+ htsize_t prealloc = LSUP_htstore_size(gr->ht_store) + trp_ct + strp_ct;
|
|
|
+ if (LSUP_htstore_capacity(gr->ht_store) < prealloc) {
|
|
|
+ rc = LSUP_htstore_resize(gr->ht_store, prealloc * PREALLOC_FACTOR);
|
|
|
+
|
|
|
+ if (UNLIKELY(rc != LSUP_OK)) return rc;
|
|
|
}
|
|
|
|
|
|
- } else if (spok[1] != NULL_KEY) {
|
|
|
- k1 = spok[1];
|
|
|
+ rc = LSUP_NOACTION;
|
|
|
+
|
|
|
+ // Serialize and insert RDF triples.
|
|
|
+ if (trp_ct > 0) {
|
|
|
+ LSUP_SerTriple sspo;
|
|
|
|
|
|
- if (spok[2] != NULL_KEY) { // ? p o
|
|
|
- k2 = spok[2];
|
|
|
- cmp_fn = lookup_pkok_cmp_fn;
|
|
|
+ for (size_t i = 0; i < trp_ct; i++) {
|
|
|
|
|
|
- } else { // ? p ?
|
|
|
- cmp_fn = lookup_pk_cmp_fn;
|
|
|
+ LSUP_term_serialize(trp[i].s, sspo.s);
|
|
|
+ LSUP_term_serialize(trp[i].p, sspo.p);
|
|
|
+ LSUP_term_serialize(trp[i].o, sspo.o);
|
|
|
+
|
|
|
+ TRACE("Inserting triple #%lu\n", i);
|
|
|
+ if (LIKELY(LSUP_htstore_add(gr->ht_store, &sspo) == LSUP_OK))
|
|
|
+ rc = LSUP_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ LSUP_buffer_done(sspo.s);
|
|
|
+ LSUP_buffer_done(sspo.p);
|
|
|
+ LSUP_buffer_done(sspo.o);
|
|
|
}
|
|
|
|
|
|
- } else if (spok[2] != NULL_KEY) { // ? ? o
|
|
|
- k1 = spok[2];
|
|
|
- cmp_fn = lookup_ok_cmp_fn;
|
|
|
+ // Insert serialized triples.
|
|
|
+ for (size_t i = 0; i < strp_ct; i++) {
|
|
|
+ TRACE("Inserting triple #%lu\n", i);
|
|
|
+ LSUP_rc db_rc = LSUP_htstore_add(gr->ht_store, strp + i);
|
|
|
|
|
|
- } else {
|
|
|
- printf("WARNING: no bound terms, making a compact copy.\n");
|
|
|
- return LSUP_graph_copy(res, gr);
|
|
|
- }
|
|
|
+ if (UNLIKELY (db_rc < 0)) return db_rc;
|
|
|
+ if (LIKELY (db_rc == LSUP_OK)) rc = LSUP_OK;
|
|
|
+ }
|
|
|
|
|
|
- while (LSUP_htable_iter(gr->keys, &cur, (void**)&i_spok, NULL) == LSUP_OK) {
|
|
|
- if (cmp_fn(&i_spok, k1, k2) == match_cond)
|
|
|
- callback_fn(gr, res, &i_spok, ctx);
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
- return LSUP_OK;
|
|
|
-}
|
|
|
+ if (gr->store_type == LSUP_STORE_MDB) {
|
|
|
+ rc = LSUP_NOACTION;
|
|
|
|
|
|
+ LSUP_Buffer sc;
|
|
|
+ LSUP_term_serialize(gr->uri, &sc);
|
|
|
|
|
|
-int LSUP_graph_lookup(LSUP_Graph *gr, LSUP_Graph *res, const LSUP_Triple *spo)
|
|
|
-{
|
|
|
- LSUP_graph_init(res, LOOKUP_GR_INIT_SIZE, NULL, LSUP_STORE_MEM);
|
|
|
+ LSUP_MDBIterator *it;
|
|
|
+ LSUP_mdbstore_add_init(gr->mdb_store, &sc, &it);
|
|
|
|
|
|
- return LSUP_graph_match_callback(gr, res, spo, &match_add_fn, true, NULL);
|
|
|
-}
|
|
|
+ // Serialize and insert RDF triples.
|
|
|
+ if (trp_ct > 0) {
|
|
|
+ LSUP_SerTriple sspo;
|
|
|
|
|
|
+ for (size_t i = 0; i < trp_ct; i++) {
|
|
|
|
|
|
-int LSUP_graph_join(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
|
-{
|
|
|
- LSUP_graph_copy(res, gr1);
|
|
|
-
|
|
|
- return graph_copy_contents(gr2, res);
|
|
|
-}
|
|
|
+ LSUP_term_serialize(trp[i].s, sspo.s);
|
|
|
+ LSUP_term_serialize(trp[i].p, sspo.p);
|
|
|
+ LSUP_term_serialize(trp[i].o, sspo.o);
|
|
|
|
|
|
+ TRACE("Inserting triple #%lu\n", i);
|
|
|
+ LSUP_rc db_rc = LSUP_mdbstore_add_iter(it, &sspo);
|
|
|
|
|
|
-int LSUP_graph_subtract(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
|
-{
|
|
|
- if (LSUP_htable_size(gr2->keys) == 0) return LSUP_graph_copy(gr1, res);
|
|
|
+ if (UNLIKELY(db_rc < 0)) return db_rc;
|
|
|
+ if (LIKELY(db_rc == LSUP_OK)) rc = LSUP_OK;
|
|
|
+ }
|
|
|
|
|
|
- LSUP_graph_init(res, LSUP_graph_capacity(gr1), NULL, LSUP_STORE_MEM);
|
|
|
+ LSUP_buffer_done(sspo.s);
|
|
|
+ LSUP_buffer_done(sspo.p);
|
|
|
+ LSUP_buffer_done(sspo.o);
|
|
|
+ }
|
|
|
|
|
|
- if (LSUP_htable_size(gr1->keys) == 0) return LSUP_OK;
|
|
|
+ // Insert serialized triples.
|
|
|
+ for (size_t i = 0; i < strp_ct; i++) {
|
|
|
+ TRACE("Inserting triple #%lu\n", i);
|
|
|
+ LSUP_rc db_rc = LSUP_mdbstore_add_iter(it, strp + i);
|
|
|
|
|
|
- htsize_t cur = 0;
|
|
|
- LSUP_TripleKey spok;
|
|
|
+ if (UNLIKELY (db_rc < 0)) return db_rc;
|
|
|
+ if (LIKELY (db_rc == LSUP_OK)) rc = LSUP_OK;
|
|
|
+ }
|
|
|
|
|
|
- while(LSUP_htable_iter(gr1->keys, &cur, (void**)&spok, NULL) == LSUP_OK) {
|
|
|
- if (LSUP_htable_get(gr2->keys, (void**)&spok, NULL) == LSUP_NORESULT)
|
|
|
- match_add_fn(res, gr1, &spok, NULL);
|
|
|
+ LSUP_mdbstore_add_done(it, NULL);
|
|
|
}
|
|
|
|
|
|
- return LSUP_OK;
|
|
|
+ return LSUP_VALUE_ERR;
|
|
|
}
|
|
|
|
|
|
|
|
|
-int LSUP_graph_intersect(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_remove(Graph *gr, const LSUP_Triple *spo, size_t *ct)
|
|
|
{
|
|
|
- LSUP_graph_init(res, LSUP_graph_capacity(gr1), NULL, LSUP_STORE_MEM);
|
|
|
+ LSUP_rc rc;
|
|
|
+ LSUP_SerTriple sspo_s;
|
|
|
+ LSUP_SerTriple *sspo = &sspo_s;
|
|
|
+ LSUP_Buffer *sc;
|
|
|
|
|
|
- if (LSUP_htable_size(gr1->keys) == 0 || LSUP_htable_size(gr2->keys) == 0)
|
|
|
- return LSUP_OK;
|
|
|
+ LSUP_term_serialize(spo->s, sspo->s);
|
|
|
+ LSUP_term_serialize(spo->p, sspo->s);
|
|
|
+ LSUP_term_serialize(spo->o, sspo->s);
|
|
|
+ LSUP_term_serialize(gr->uri, sc);
|
|
|
|
|
|
- htsize_t cur = 0;
|
|
|
- LSUP_TripleKey spok;
|
|
|
+ if (gr->store_type == LSUP_STORE_MEM)
|
|
|
+ rc = LSUP_htstore_remove(gr->ht_store, sspo, ct);
|
|
|
+ else
|
|
|
+ rc = LSUP_mdbstore_remove(gr->mdb_store, sspo, sc, ct);
|
|
|
|
|
|
- while(LSUP_htable_iter(gr1->keys, &cur, (void**)&spok, NULL) == LSUP_OK) {
|
|
|
- if (LSUP_htable_get(gr2->keys, (void**)&spok, NULL) == LSUP_OK)
|
|
|
- match_add_fn(res, gr1, &spok, NULL);
|
|
|
- }
|
|
|
+ LSUP_striple_done(sspo);
|
|
|
+ LSUP_buffer_done(sc);
|
|
|
|
|
|
- return LSUP_OK;
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
|
|
|
-int LSUP_graph_xor(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_lookup(
|
|
|
+ const Graph *gr, const LSUP_Triple *spo, GraphIterator **it_p)
|
|
|
{
|
|
|
- if (LSUP_htable_size(gr1->keys) == 0) return LSUP_graph_copy(gr2, res);
|
|
|
- if (LSUP_htable_size(gr2->keys) == 0) return LSUP_graph_copy(gr1, res);
|
|
|
+ LSUP_rc rc;
|
|
|
+ GraphIterator *it;
|
|
|
+ CRITICAL(it = malloc(sizeof(GraphIterator)));
|
|
|
+ *it_p = it;
|
|
|
|
|
|
- LSUP_graph_init(
|
|
|
- res, min(LSUP_graph_capacity(gr1), LSUP_graph_capacity(gr2)),
|
|
|
- NULL, LSUP_STORE_MEM);
|
|
|
+ it->graph = gr;
|
|
|
|
|
|
- htsize_t cur = 0;
|
|
|
- LSUP_TripleKey spok;
|
|
|
+ LSUP_SerTriple sspo_s;
|
|
|
+ LSUP_SerTriple *sspo = &sspo_s;
|
|
|
+ LSUP_Buffer *sc;
|
|
|
|
|
|
- while(LSUP_htable_iter(gr1->keys, &cur, (void**)&spok, NULL) == LSUP_OK) {
|
|
|
- if (LSUP_htable_get(gr2->keys, (void**)&spok, NULL) == LSUP_NORESULT)
|
|
|
- match_add_fn(res, gr1, &spok, NULL);
|
|
|
- }
|
|
|
+ LSUP_term_serialize(spo->s, sspo->s);
|
|
|
+ LSUP_term_serialize(spo->p, sspo->s);
|
|
|
+ LSUP_term_serialize(spo->o, sspo->s);
|
|
|
+ LSUP_term_serialize(gr->uri, sc);
|
|
|
|
|
|
- cur = 0;
|
|
|
+ if (gr->store_type == LSUP_STORE_MEM) {
|
|
|
+ rc = LSUP_htstore_lookup(gr->ht_store, sspo, &it->ht_iter, &it->ct);
|
|
|
|
|
|
- while(LSUP_htable_iter(gr2->keys, &cur, (void**)&spok, NULL) == LSUP_OK) {
|
|
|
- if (LSUP_htable_get(gr1->keys, (void**)&spok, NULL) == LSUP_NORESULT)
|
|
|
- match_add_fn(res, gr2, &spok, NULL);
|
|
|
+ } else {
|
|
|
+ rc = LSUP_mdbstore_lookup(
|
|
|
+ gr->mdb_store, sspo, sc, &it->mdb_iter, &it->ct);
|
|
|
}
|
|
|
|
|
|
- return LSUP_OK;
|
|
|
+ LSUP_striple_done(sspo);
|
|
|
+ LSUP_buffer_done(sc);
|
|
|
+
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
|
|
|
-void
|
|
|
-LSUP_graph_free(LSUP_Graph *gr)
|
|
|
+/** @brief Advance iterator and return serialized triple.
|
|
|
+ *
|
|
|
+ * This is an internal function to pass raw buffers between higher-level
|
|
|
+ * functions without serializing and deserializing triples.
|
|
|
+ */
|
|
|
+static LSUP_rc
|
|
|
+graph_iter_next_buffer(GraphIterator *it, LSUP_SerTriple *sspo)
|
|
|
{
|
|
|
- if (LIKELY(gr != NULL)) {
|
|
|
- LSUP_term_free(gr->uri);
|
|
|
+ LSUP_rc rc;
|
|
|
|
|
|
- // Free up triples.
|
|
|
- LSUP_htable_free(gr->keys);
|
|
|
-
|
|
|
- // Free up index entries and index.
|
|
|
- htsize_t cur = 0;
|
|
|
- LSUP_TripleKey spok;
|
|
|
- LSUP_Buffer *sterm;
|
|
|
- while(LSUP_htable_iter(
|
|
|
- gr->idx, &cur, (void**)&spok, (void**)&sterm) == LSUP_OK) {
|
|
|
- TRACE("Freeing indexed term buffer #%d at %p", cur, sterm);
|
|
|
- LSUP_buffer_done(sterm);
|
|
|
- }
|
|
|
+ if (it->graph->store_type == LSUP_STORE_MEM)
|
|
|
+ rc = LSUP_htiter_next(it->ht_iter, sspo);
|
|
|
+ else rc = LSUP_mdbiter_next(it->mdb_iter, sspo);
|
|
|
|
|
|
- LSUP_htable_free(gr->idx);
|
|
|
+}
|
|
|
|
|
|
- free(gr);
|
|
|
+
|
|
|
+LSUP_rc
|
|
|
+LSUP_graph_iter_next(GraphIterator *it, LSUP_Triple *spo)
|
|
|
+{
|
|
|
+ LSUP_SerTriple sspo;
|
|
|
+ LSUP_rc rc = graph_iter_next_buffer(it, &sspo);
|
|
|
+
|
|
|
+ if (rc == LSUP_OK) {
|
|
|
+ LSUP_term_deserialize(sspo.s, spo->s);
|
|
|
+ LSUP_term_deserialize(sspo.p, spo->p);
|
|
|
+ LSUP_term_deserialize(sspo.o, spo->o);
|
|
|
+
|
|
|
+ it->i++;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
+ return rc;
|
|
|
+}
|
|
|
|
|
|
-/* * CALLBACKS * */
|
|
|
|
|
|
-/**
|
|
|
- * Callback for adding a matched triple.
|
|
|
- *
|
|
|
- * Adds the current triple in src to dest. No duplicate check.
|
|
|
- *
|
|
|
- * The source graph cursor must be set to the triple to be copied.
|
|
|
- */
|
|
|
-int match_add_fn(
|
|
|
- LSUP_Graph *src, LSUP_Graph *dest, const LSUP_TripleKey *spok,
|
|
|
- void *ctx)
|
|
|
+void
|
|
|
+LSUP_graph_iter_free(GraphIterator *it)
|
|
|
{
|
|
|
- // Add term to index.
|
|
|
- for (int i = 0; i < 3; i++) {
|
|
|
- // Index terms if not yet presents in destination.
|
|
|
- void *src_val, *dest_val;
|
|
|
-
|
|
|
- if(LSUP_htable_get(src->idx, *spok + i, &src_val) == LSUP_OK) {
|
|
|
- CRITICAL(dest_val = malloc(sizeof(LSUP_Buffer)));
|
|
|
- LSUP_buffer_copy(dest_val, src_val);
|
|
|
- LSUP_htable_put(dest->idx, *spok + i, dest_val);
|
|
|
- }
|
|
|
- }
|
|
|
+ if (it->graph->store_type == LSUP_STORE_MEM)
|
|
|
+ LSUP_htiter_free(it->ht_iter);
|
|
|
+ else
|
|
|
+ LSUP_mdbiter_free(it->mdb_iter);
|
|
|
|
|
|
- // Add triple.
|
|
|
- return LSUP_htable_put(dest->keys, spok, NULL);
|
|
|
+ free(it);
|
|
|
}
|
|
|
|
|
|
|
|
|
-/**
|
|
|
- * Callback for removing a matched triple.
|
|
|
- */
|
|
|
-int match_rm_fn(
|
|
|
- LSUP_Graph *src, LSUP_Graph *dest, const LSUP_TripleKey *spok,
|
|
|
- void *ctx)
|
|
|
-{ return LSUP_htable_del(dest->keys, spok); }
|
|
|
+bool
|
|
|
+LSUP_graph_contains(const LSUP_Graph *gr, const LSUP_Triple *spo)
|
|
|
+{
|
|
|
+ GraphIterator *it;
|
|
|
+
|
|
|
+ LSUP_graph_lookup(gr, spo, &it);
|
|
|
+ bool rc = LSUP_graph_iter_next(it, NULL) != LSUP_NORESULT;
|
|
|
+
|
|
|
+ LSUP_graph_iter_free(it);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|