|
@@ -1,10 +1,44 @@
|
|
#include "graph.h"
|
|
#include "graph.h"
|
|
|
|
|
|
-// Max size of random graph name.
|
|
|
|
-#define RANDOM_NAME_SIZE 64
|
|
|
|
// Initial size of lookup graph. It will double each time capacity is reached.
|
|
// Initial size of lookup graph. It will double each time capacity is reached.
|
|
#define LOOKUP_GR_INIT_SIZE 64
|
|
#define LOOKUP_GR_INIT_SIZE 64
|
|
|
|
|
|
|
|
+typedef enum KSetFlag {
|
|
|
|
+ LSUP_KS_NONE = 0,
|
|
|
|
+ LSUP_KS_CHECK_CAP = 1 << 0,
|
|
|
|
+ LSUP_KS_CHECK_DUP = 1 << 1
|
|
|
|
+} KSetFlag;
|
|
|
|
+
|
|
|
|
+typedef struct Keyset {
|
|
|
|
+ LSUP_TripleKey *data;
|
|
|
|
+ size_t capacity;
|
|
|
|
+ size_t cur;
|
|
|
|
+ size_t free_i;
|
|
|
|
+} Keyset;
|
|
|
|
+
|
|
|
|
+struct IndexEntry {
|
|
|
|
+ LSUP_Key key;
|
|
|
|
+ LSUP_SerTerm *val;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+typedef struct Index {
|
|
|
|
+ size_t free_i;
|
|
|
|
+ size_t capacity;
|
|
|
|
+ struct IndexEntry *entries;
|
|
|
|
+} Index;
|
|
|
|
+
|
|
|
|
+typedef struct Graph {
|
|
|
|
+ LSUP_store_type store_type;
|
|
|
|
+ Keyset *keys;
|
|
|
|
+ LSUP_Term *uri;
|
|
|
|
+ Index *idx;
|
|
|
|
+} Graph;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// "NULL" triple, a value that is never user-provided. Used to fill deleted
|
|
|
|
+// triples in a keyset.
|
|
|
|
+LSUP_TripleKey NULL_TRP = {NULL_KEY, NULL_KEY, NULL_KEY};
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Extern inline functions.
|
|
* Extern inline functions.
|
|
*/
|
|
*/
|
|
@@ -13,24 +47,324 @@ size_t LSUP_graph_size(LSUP_Graph *gr);
|
|
size_t LSUP_graph_capacity(LSUP_Graph *gr);
|
|
size_t LSUP_graph_capacity(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);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// 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; }
|
|
|
|
+
|
|
|
|
+// 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.
|
|
|
|
+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; }
|
|
|
|
+
|
|
|
|
+// 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; }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* * * Post-lookup callbacks * * */
|
|
|
|
+
|
|
|
|
+int match_add_fn(LSUP_Graph *src, LSUP_Graph *dest, void *ctx);
|
|
|
|
+
|
|
|
|
+int match_rm_fn(LSUP_Graph *src, LSUP_Graph *dest, void *ctx);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* * * KEYSETS * * */
|
|
|
|
+
|
|
|
|
+static inline bool is_null_trp(const LSUP_TripleKey *trp)
|
|
|
|
+{
|
|
|
|
+ return (
|
|
|
|
+ (*trp)[0] == NULL_TRP[0]
|
|
|
|
+ && (*trp)[1] == NULL_TRP[1]
|
|
|
|
+ && (*trp)[2] == NULL_TRP[2]);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int keyset_init(Keyset *ks, size_t capacity)
|
|
|
|
+{
|
|
|
|
+ CRITICAL (ks->data = malloc(capacity * TRP_KLEN));
|
|
|
|
+ ks->capacity = capacity;
|
|
|
|
+ ks->cur = 0;
|
|
|
|
+ ks->free_i = 0;
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Move cursor to a non-empty position.
|
|
|
|
+ */
|
|
|
|
+static inline bool keyset_seek(Keyset* ks, size_t idx)
|
|
|
|
+{
|
|
|
|
+ if (idx >= ks->free_i) return false;
|
|
|
|
+
|
|
|
|
+ ks->cur = idx;
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static inline LSUP_TripleKey *keyset_peek(Keyset *ks) {
|
|
|
|
+ return ks->data + ks->cur;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static inline bool keyset_contains(
|
|
|
|
+ const Keyset *ks, const LSUP_TripleKey *val) {
|
|
|
|
+
|
|
|
|
+ for (size_t i = 0; i < ks->free_i; i++) {
|
|
|
|
+ // scan from the least to the most probable to match.
|
|
|
|
+ if (
|
|
|
|
+ (*val)[2] == ks->data[i][2] &&
|
|
|
|
+ (*val)[0] == ks->data[i][0] &&
|
|
|
|
+ (*val)[1] == ks->data[i][1]) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static inline bool keyset_next(Keyset *ks)
|
|
|
|
+{
|
|
|
|
+ if (ks->free_i > 0 && ks->cur < ks->free_i - 1) {
|
|
|
|
+ ks->cur ++;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int keyset_resize(Keyset *ks, size_t new_size) {
|
|
|
|
+ new_size = max(new_size, ks->free_i);
|
|
|
|
+
|
|
|
|
+ CRITICAL (ks->data = realloc(
|
|
|
|
+ ks->data, max(new_size, ks->free_i) * TRP_KLEN))
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int keyset_add(
|
|
|
|
+ Keyset *ks, const LSUP_TripleKey *val, const KSetFlag flags)
|
|
|
|
+{
|
|
|
|
+ if((flags & LSUP_KS_CHECK_DUP) && keyset_contains(ks, val))
|
|
|
|
+ return LSUP_NOACTION;
|
|
|
|
+
|
|
|
|
+ if((flags & LSUP_KS_CHECK_CAP) && ks->free_i >= ks->capacity)
|
|
|
|
+ keyset_resize(ks, ks->capacity * 2);
|
|
|
|
+
|
|
|
|
+ memcpy(ks->data + ks->free_i, val, TRP_KLEN);
|
|
|
|
+
|
|
|
|
+ ks->cur = ks->free_i;
|
|
|
|
+ ks->free_i ++;
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int keyset_remove(Keyset *ks, const LSUP_TripleKey *val) {
|
|
|
|
+
|
|
|
|
+ keyset_seek(ks, 0);
|
|
|
|
+
|
|
|
|
+ while (keyset_next(ks)) {
|
|
|
|
+ if (memcmp(val, ks->data + ks->cur, TRP_KLEN) == 0) {
|
|
|
|
+ memcpy(ks->data + ks->cur, &NULL_TRP, TRP_KLEN);
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int keyset_copy(const Keyset *src, Keyset *dest) {
|
|
|
|
+
|
|
|
|
+ keyset_init(dest, src->capacity);
|
|
|
|
+
|
|
|
|
+ memcpy(dest->data, src->data, src->capacity * TRP_KLEN);
|
|
|
|
+
|
|
|
|
+ keyset_seek(dest, 0);
|
|
|
|
+ dest->free_i = src->free_i;
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int keyset_sparse_copy(Keyset *src, Keyset *dest) {
|
|
|
|
+
|
|
|
|
+ keyset_init(dest, src->capacity);
|
|
|
|
+
|
|
|
|
+ if (keyset_seek(src, 0)) {
|
|
|
|
+ do {
|
|
|
|
+ if (LIKELY(memcmp(
|
|
|
|
+ keyset_peek(src),
|
|
|
|
+ &NULL_TRP, TRP_KLEN) != 0)) {
|
|
|
|
+ keyset_add(dest, keyset_peek(src), 0);
|
|
|
|
+ }
|
|
|
|
+ } while (keyset_next(src));
|
|
|
|
+
|
|
|
|
+ keyset_seek(dest, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static void keyset_free(Keyset *ks)
|
|
|
|
+{
|
|
|
|
+ if(LIKELY(ks != NULL)) {
|
|
|
|
+ if(LIKELY(ks->data != NULL))
|
|
|
|
+ free(ks->data);
|
|
|
|
+
|
|
|
|
+ free(ks);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* * * INDEX * * */
|
|
|
|
+
|
|
|
|
+static Index *index_new(size_t capacity)
|
|
|
|
+{
|
|
|
|
+ Index *idx = malloc(sizeof(struct Index));
|
|
|
|
+
|
|
|
|
+ if (capacity == 0) return NULL;
|
|
|
|
+
|
|
|
|
+ CRITICAL (idx->entries = malloc(sizeof(struct IndexEntry) * capacity));
|
|
|
|
+
|
|
|
|
+ idx->free_i = 0;
|
|
|
|
+ idx->capacity = capacity;
|
|
|
|
+
|
|
|
|
+ return idx;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static void index_resize(Index *idx, size_t capacity)
|
|
|
|
+{
|
|
|
|
+ CRITICAL (idx->entries = (struct IndexEntry*)realloc(
|
|
|
|
+ idx->entries,
|
|
|
|
+ sizeof(struct IndexEntry) * capacity));
|
|
|
|
+
|
|
|
|
+ idx->capacity = capacity;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static LSUP_SerTerm *index_lookup(Index *idx, LSUP_Key key)
|
|
|
|
+{
|
|
|
|
+ LSUP_SerTerm *match = NULL;
|
|
|
|
+
|
|
|
|
+ for (size_t i = 0; i < idx->free_i; i++) {
|
|
|
|
+ if (idx->entries[i].key == key) {
|
|
|
|
+ match = idx->entries[i].val;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return match;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int index_add_pair(Index *idx, LSUP_Key key, LSUP_SerTerm *sterm)
|
|
|
|
+{
|
|
|
|
+ // Fail quietly if key exists already.
|
|
|
|
+ if (index_lookup(idx, key) == NULL) {
|
|
|
|
+ if (idx->free_i >= idx->capacity) {
|
|
|
|
+ index_resize(idx, idx->capacity * 1.5);
|
|
|
|
+ TRACE("Capacity now at %lu\n", idx->capacity);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ struct IndexEntry *entry = idx->entries + idx->free_i;
|
|
|
|
+
|
|
|
|
+ entry->key = key;
|
|
|
|
+ entry->val = LSUP_buffer_new(sterm->size);
|
|
|
|
+ memcpy(entry->val->addr, sterm->addr, sterm->size);
|
|
|
|
+
|
|
|
|
+ idx->free_i ++;
|
|
|
|
+ TRACE("Size now at %lu\n", idx->free_i);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int index_add(Index *idx, LSUP_SerTerm *sterm)
|
|
|
|
+{
|
|
|
|
+ LSUP_Key key = LSUP_sterm_to_key(sterm);
|
|
|
|
+
|
|
|
|
+ return index_add_pair(idx, key, sterm);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static void index_free(Index *idx)
|
|
|
|
+{
|
|
|
|
+ for (size_t i = 0; i < idx->free_i; i++) {
|
|
|
|
+ LSUP_buffer_done(idx->entries[i].val);
|
|
|
|
+ free(idx->entries[i].val);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ free(idx->entries);
|
|
|
|
+ free(idx);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/* * * GRAPH * * */
|
|
|
|
+
|
|
int
|
|
int
|
|
LSUP_graph_init(
|
|
LSUP_graph_init(
|
|
LSUP_Graph *gr, size_t capacity, char *uri_str,
|
|
LSUP_Graph *gr, size_t capacity, char *uri_str,
|
|
LSUP_store_type store_type)
|
|
LSUP_store_type store_type)
|
|
{
|
|
{
|
|
if (uri_str == NULL) {
|
|
if (uri_str == NULL) {
|
|
- char gr_name[RANDOM_NAME_SIZE];
|
|
|
|
- sprintf(gr_name, "urn:tmp:%d", rand());
|
|
|
|
|
|
+ char gr_name[UUIDSTR_SIZE + 9];
|
|
|
|
+
|
|
|
|
+ uuid_t uuid;
|
|
|
|
+ uuid_generate_random(uuid);
|
|
|
|
+
|
|
|
|
+ uuid_str_t uuid_str;
|
|
|
|
+ uuid_unparse_lower(uuid, uuid_str);
|
|
|
|
+ sprintf(gr_name, "urn:lsup:%s", uuid_str);
|
|
gr->uri = LSUP_term_new(LSUP_TERM_URI, gr_name, NULL, NULL);
|
|
gr->uri = LSUP_term_new(LSUP_TERM_URI, gr_name, NULL, NULL);
|
|
} else {
|
|
} else {
|
|
gr->uri = LSUP_term_new(LSUP_TERM_URI, uri_str, NULL, NULL);
|
|
gr->uri = LSUP_term_new(LSUP_TERM_URI, uri_str, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
- gr->keys = LSUP_keyset_new(capacity);
|
|
|
|
|
|
+ gr->keys = malloc(sizeof(Keyset));
|
|
|
|
+ keyset_init(gr->keys, capacity);
|
|
|
|
|
|
switch (store_type ) {
|
|
switch (store_type ) {
|
|
case LSUP_STORE_MEM:
|
|
case LSUP_STORE_MEM:
|
|
- gr->idx = LSUP_index_new(gr->keys->capacity);
|
|
|
|
|
|
+ gr->idx = index_new(gr->keys->capacity);
|
|
break;
|
|
break;
|
|
|
|
|
|
case LSUP_STORE_MDB:
|
|
case LSUP_STORE_MDB:
|
|
@@ -40,7 +374,7 @@ LSUP_graph_init(
|
|
return -1;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return LSUP_OK;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -56,14 +390,48 @@ LSUP_graph_new(size_t capacity, char *uri_str, LSUP_store_type store_type)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * Copy triples from a source graph into a destination one.
|
|
|
|
+ *
|
|
|
|
+ * The destination graph is not initialized, so the copy is cumulative.
|
|
|
|
+ */
|
|
|
|
+static int graph_copy_contents(LSUP_Graph *src, LSUP_Graph *dest)
|
|
|
|
+{
|
|
|
|
+ LSUP_Triple trp;
|
|
|
|
+ trp.s = NULL;
|
|
|
|
+ trp.p = NULL;
|
|
|
|
+ trp.o = NULL;
|
|
|
|
+
|
|
|
|
+ return LSUP_graph_match_callback(
|
|
|
|
+ src, dest, &trp, match_add_fn, true, NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
int
|
|
int
|
|
-LSUP_graph_add(LSUP_Graph *gr, LSUP_Triple data[], size_t data_size)
|
|
|
|
|
|
+LSUP_graph_copy(LSUP_Graph *src, LSUP_Graph *dest)
|
|
|
|
+{
|
|
|
|
+ LSUP_graph_init(dest, src->keys->capacity, NULL, src->store_type);
|
|
|
|
+
|
|
|
|
+ return graph_copy_contents(src, dest);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+size_t
|
|
|
|
+LSUP_graph_capacity(LSUP_Graph *gr) { return gr->keys->capacity; }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+size_t
|
|
|
|
+LSUP_graph_size(LSUP_Graph *gr) { return gr->keys->free_i; }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int
|
|
|
|
+LSUP_graph_add(LSUP_Graph *gr, const LSUP_Triple data[], size_t data_size)
|
|
{
|
|
{
|
|
// TODO Decouple this and build interface for memory and MDB integration.
|
|
// TODO Decouple this and build interface for memory and MDB integration.
|
|
|
|
|
|
// Resize all at once if needed.
|
|
// Resize all at once if needed.
|
|
if (gr->keys->capacity < gr->keys->free_i + data_size)
|
|
if (gr->keys->capacity < gr->keys->free_i + data_size)
|
|
- LSUP_keyset_resize(gr->keys, gr->keys->free_i + data_size);
|
|
|
|
|
|
+ keyset_resize(gr->keys, gr->keys->free_i + data_size);
|
|
|
|
|
|
for (size_t i = 0; i < data_size; i++) {
|
|
for (size_t i = 0; i < data_size; i++) {
|
|
LSUP_SerTerm sspo[3];
|
|
LSUP_SerTerm sspo[3];
|
|
@@ -79,9 +447,9 @@ LSUP_graph_add(LSUP_Graph *gr, LSUP_Triple data[], size_t data_size)
|
|
// Add terms to index.
|
|
// Add terms to index.
|
|
// TODO Optimize. This copies sterm data twice: once to serialize,
|
|
// TODO Optimize. This copies sterm data twice: once to serialize,
|
|
// once to add to index.
|
|
// once to add to index.
|
|
- LSUP_index_add_pair(gr->idx, sk, sspo);
|
|
|
|
- LSUP_index_add_pair(gr->idx, pk, sspo + 1);
|
|
|
|
- LSUP_index_add_pair(gr->idx, ok, sspo + 2);
|
|
|
|
|
|
+ index_add_pair(gr->idx, sk, sspo);
|
|
|
|
+ index_add_pair(gr->idx, pk, sspo + 1);
|
|
|
|
+ index_add_pair(gr->idx, ok, sspo + 2);
|
|
|
|
|
|
LSUP_buffer_done(sspo);
|
|
LSUP_buffer_done(sspo);
|
|
LSUP_buffer_done(sspo + 1);
|
|
LSUP_buffer_done(sspo + 1);
|
|
@@ -89,10 +457,10 @@ LSUP_graph_add(LSUP_Graph *gr, LSUP_Triple data[], size_t data_size)
|
|
|
|
|
|
// Add triple.
|
|
// Add triple.
|
|
LSUP_TripleKey trp = {sk, pk, ok};
|
|
LSUP_TripleKey trp = {sk, pk, ok};
|
|
- LSUP_keyset_add(gr->keys, &trp, LSUP_KS_CHECK_DUP);
|
|
|
|
|
|
+ keyset_add(gr->keys, &trp, LSUP_KS_CHECK_DUP);
|
|
}
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return LSUP_OK;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -105,25 +473,116 @@ LSUP_graph_contains(const LSUP_Graph *gr, const LSUP_Triple *spo)
|
|
|
|
|
|
LSUP_TripleKey spok = {sk, pk, ok};
|
|
LSUP_TripleKey spok = {sk, pk, ok};
|
|
|
|
|
|
- return LSUP_keyset_contains(gr->keys, &spok);
|
|
|
|
|
|
+ return keyset_contains(gr->keys, &spok);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
-int LSUP_graph_join(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
|
|
|
|
+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)
|
|
{
|
|
{
|
|
- LSUP_keyset_sparse_copy(ks1, res);
|
|
|
|
|
|
+ if (!keyset_seek(gr->keys, 0))
|
|
|
|
+ return LSUP_NOACTION;
|
|
|
|
|
|
- if (LSUP_keyset_seek(ks2, 0)) {
|
|
|
|
- do {
|
|
|
|
- const LSUP_TripleKey *spok = LSUP_keyset_peek(ks2);
|
|
|
|
- if (!is_null_trp(spok)) {
|
|
|
|
- LSUP_keyset_add(
|
|
|
|
- res, spok, LSUP_KS_CHECK_DUP | LSUP_KS_CHECK_CAP);
|
|
|
|
|
|
+ LSUP_Key k1, k2;
|
|
|
|
+ LSUP_key_cmp_fn_t cmp_fn;
|
|
|
|
+
|
|
|
|
+ LSUP_Key sk = LSUP_term_to_key(spo->s);
|
|
|
|
+ LSUP_Key pk = LSUP_term_to_key(spo->p);
|
|
|
|
+ LSUP_Key ok = LSUP_term_to_key(spo->o);
|
|
|
|
+
|
|
|
|
+ if (sk != NULL_KEY && pk != NULL_KEY && ok != NULL_KEY) {
|
|
|
|
+ LSUP_TripleKey spok = {sk, pk, ok};
|
|
|
|
+
|
|
|
|
+ if (match_cond == true) {
|
|
|
|
+ // Shortcut for 3-term match—only if match_cond is true.
|
|
|
|
+ keyset_init(res->keys, 1);
|
|
|
|
+ if(keyset_contains(gr->keys, &spok)) {
|
|
|
|
+ callback_fn(gr, res, ctx);
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+ } else {
|
|
|
|
+ return LSUP_NOACTION;
|
|
}
|
|
}
|
|
- } while (LSUP_keyset_next(ks2));
|
|
|
|
|
|
+ } else {
|
|
|
|
+ // For negative condition (i.e. "apply this function to all triples
|
|
|
|
+ // except the matching one"), the whole set is scanned.
|
|
|
|
+ const LSUP_TripleKey *cur = keyset_peek(gr->keys);
|
|
|
|
+ int rc = LSUP_NOACTION;
|
|
|
|
+ do {
|
|
|
|
+ if (
|
|
|
|
+ *cur[0] != spok[0] ||
|
|
|
|
+ *cur[1] != spok[1] ||
|
|
|
|
+ *cur[2] != spok[2]
|
|
|
|
+ ) {
|
|
|
|
+ callback_fn(gr, res, ctx);
|
|
|
|
+ rc = LSUP_OK;
|
|
|
|
+ }
|
|
|
|
+ } while (keyset_next(gr->keys));
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else if (sk != NULL_KEY) {
|
|
|
|
+ k1 = sk;
|
|
|
|
+
|
|
|
|
+ if (pk != NULL_KEY) { // s p ?
|
|
|
|
+ k2 = pk;
|
|
|
|
+ cmp_fn = lookup_skpk_cmp_fn;
|
|
|
|
+
|
|
|
|
+ } else if (ok != NULL_KEY) { // s ? o
|
|
|
|
+ k2 = ok;
|
|
|
|
+ cmp_fn = lookup_skok_cmp_fn;
|
|
|
|
+
|
|
|
|
+ } else { // s ? ?
|
|
|
|
+ cmp_fn = lookup_sk_cmp_fn;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else if (pk != NULL_KEY) {
|
|
|
|
+ k1 = pk;
|
|
|
|
+
|
|
|
|
+ if (ok != NULL_KEY) { // ? p o
|
|
|
|
+ k2 = ok;
|
|
|
|
+ cmp_fn = lookup_pkok_cmp_fn;
|
|
|
|
+
|
|
|
|
+ } else { // ? p ?
|
|
|
|
+ cmp_fn = lookup_pk_cmp_fn;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else if (ok != NULL_KEY) { // ? ? o
|
|
|
|
+ k1 = ok;
|
|
|
|
+ cmp_fn = lookup_ok_cmp_fn;
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ printf("WARNING: no bound terms, making a compact copy.\n");
|
|
|
|
+ return LSUP_graph_copy(gr, res);
|
|
}
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ do {
|
|
|
|
+ if (cmp_fn(keyset_peek(gr->keys), k1, k2) == match_cond) {
|
|
|
|
+ callback_fn(gr, res, ctx);
|
|
|
|
+ }
|
|
|
|
+ } while (keyset_next(gr->keys));
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+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_graph_match_callback(gr, res, spo, &match_add_fn, true, NULL);
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_graph_join(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
|
|
+{
|
|
|
|
+ LSUP_graph_copy(gr1, res);
|
|
|
|
+
|
|
|
|
+ return graph_copy_contents(gr2, res);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -131,16 +590,17 @@ int LSUP_graph_subtract(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
{
|
|
{
|
|
LSUP_graph_init(res, gr1->keys->capacity, NULL, LSUP_STORE_MEM);
|
|
LSUP_graph_init(res, gr1->keys->capacity, NULL, LSUP_STORE_MEM);
|
|
|
|
|
|
- if (LSUP_keyset_seek(ks1, 0)) {
|
|
|
|
- do {
|
|
|
|
- const LSUP_TripleKey *spok = LSUP_keyset_peek(ks1);
|
|
|
|
- if (!is_null_trp(spok) && !LSUP_keyset_contains(ks2, spok)) {
|
|
|
|
- LSUP_keyset_add(res, spok, 0);
|
|
|
|
- }
|
|
|
|
- } while (LSUP_keyset_next(ks1));
|
|
|
|
- }
|
|
|
|
|
|
+ if (!keyset_seek(gr1->keys, 0))
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ const LSUP_TripleKey *spok = keyset_peek(gr1->keys);
|
|
|
|
+ if (!is_null_trp(spok) && !keyset_contains(gr2->keys, spok)) {
|
|
|
|
+ match_add_fn(res, gr1, NULL);
|
|
|
|
+ }
|
|
|
|
+ } while (keyset_next(gr1->keys));
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ return LSUP_OK;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -148,53 +608,96 @@ int LSUP_graph_intersect(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
{
|
|
{
|
|
LSUP_graph_init(res, gr1->keys->capacity, NULL, LSUP_STORE_MEM);
|
|
LSUP_graph_init(res, gr1->keys->capacity, NULL, LSUP_STORE_MEM);
|
|
|
|
|
|
- if (LSUP_keyset_seek(ks1, 0)) {
|
|
|
|
- do {
|
|
|
|
- const LSUP_TripleKey *spok = LSUP_keyset_peek(ks1);
|
|
|
|
- if (!is_null_trp(spok) && LSUP_keyset_contains(ks2, spok)) {
|
|
|
|
- LSUP_keyset_add(res, spok, 0);
|
|
|
|
- }
|
|
|
|
- } while (LSUP_keyset_next(ks1));
|
|
|
|
- }
|
|
|
|
|
|
+ if (!keyset_seek(gr1->keys, 0) || gr2->keys->free_i == 0)
|
|
|
|
+ return LSUP_OK;
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ do {
|
|
|
|
+ const LSUP_TripleKey *spok = keyset_peek(gr1->keys);
|
|
|
|
+ if (!is_null_trp(spok) && keyset_contains(gr2->keys, spok)) {
|
|
|
|
+ match_add_fn(res, gr1, NULL);
|
|
|
|
+ }
|
|
|
|
+ } while (keyset_next(gr1->keys));
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
int LSUP_graph_xor(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
int LSUP_graph_xor(LSUP_Graph *gr1, LSUP_Graph *gr2, LSUP_Graph *res)
|
|
{
|
|
{
|
|
- LSUP_graph_init(res, gr1->keys->capacity, NULL, LSUP_STORE_MEM);
|
|
|
|
|
|
+ if (!keyset_seek(gr1->keys, 0))
|
|
|
|
+ return LSUP_graph_copy(gr2, res);
|
|
|
|
|
|
- if (LSUP_keyset_seek(ks1, 0)) {
|
|
|
|
- do {
|
|
|
|
- const LSUP_TripleKey *spok = LSUP_keyset_peek(ks1);
|
|
|
|
- if (!is_null_trp(spok) && !LSUP_keyset_contains(ks2, spok)) {
|
|
|
|
- LSUP_keyset_add(res, spok, 0);
|
|
|
|
- }
|
|
|
|
- } while (LSUP_keyset_next(ks1));
|
|
|
|
- }
|
|
|
|
|
|
+ if (!keyset_seek(gr2->keys, 0))
|
|
|
|
+ return LSUP_graph_copy(gr1, res);
|
|
|
|
|
|
- if (LSUP_keyset_seek(ks2, 0)) {
|
|
|
|
- do {
|
|
|
|
- const LSUP_TripleKey *spok = LSUP_keyset_peek(ks2);
|
|
|
|
- if (!is_null_trp(spok) && !LSUP_keyset_contains(ks1, spok)) {
|
|
|
|
- LSUP_keyset_add(res, spok, 0);
|
|
|
|
- }
|
|
|
|
- } while (LSUP_keyset_next(ks2));
|
|
|
|
- }
|
|
|
|
|
|
+ LSUP_graph_init(res, gr1->keys->capacity, NULL, LSUP_STORE_MEM);
|
|
|
|
|
|
- return 0;
|
|
|
|
|
|
+ do {
|
|
|
|
+ const LSUP_TripleKey *spok = keyset_peek(gr1->keys);
|
|
|
|
+ if (!is_null_trp(spok) && !keyset_contains(gr2->keys, spok)) {
|
|
|
|
+ match_add_fn(gr1, res, NULL);
|
|
|
|
+ }
|
|
|
|
+ } while (keyset_next(gr1->keys));
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ const LSUP_TripleKey *spok = keyset_peek(gr2->keys);
|
|
|
|
+ if (!is_null_trp(spok) && !keyset_contains(gr1->keys, spok)) {
|
|
|
|
+ match_add_fn(gr2, res, NULL);
|
|
|
|
+ }
|
|
|
|
+ } while (keyset_next(gr2->keys));
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
void
|
|
LSUP_graph_free(LSUP_Graph *gr)
|
|
LSUP_graph_free(LSUP_Graph *gr)
|
|
{
|
|
{
|
|
- if(LIKELY(gr != NULL)) {
|
|
|
|
|
|
+ if (LIKELY(gr != NULL)) {
|
|
LSUP_term_free(gr->uri);
|
|
LSUP_term_free(gr->uri);
|
|
- LSUP_keyset_free(gr->keys);
|
|
|
|
- LSUP_index_free(gr->idx);
|
|
|
|
|
|
+ keyset_free(gr->keys);
|
|
|
|
+ index_free(gr->idx);
|
|
free(gr);
|
|
free(gr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+/* * 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, void *ctx)
|
|
|
|
+{
|
|
|
|
+ if(LSUP_graph_size(src) >= LSUP_graph_capacity(src))
|
|
|
|
+ keyset_resize(dest->keys, dest->keys->capacity * 2);
|
|
|
|
+
|
|
|
|
+ LSUP_TripleKey *spok = keyset_peek(src->keys);
|
|
|
|
+
|
|
|
|
+ memcpy(dest->keys->data + dest->keys->free_i, spok, TRP_KLEN);
|
|
|
|
+
|
|
|
|
+ index_add_pair(
|
|
|
|
+ src->idx, *spok[0], index_lookup(src->idx, *spok[0]));
|
|
|
|
+ index_add_pair(
|
|
|
|
+ src->idx, *spok[1], index_lookup(src->idx, *spok[1]));
|
|
|
|
+ index_add_pair(
|
|
|
|
+ src->idx, *spok[2], index_lookup(src->idx, *spok[2]));
|
|
|
|
+
|
|
|
|
+ dest->keys->cur = dest->keys->free_i;
|
|
|
|
+ dest->keys->free_i ++;
|
|
|
|
+
|
|
|
|
+ return LSUP_OK;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Callback for removing a matched triple.
|
|
|
|
+ */
|
|
|
|
+int match_rm_fn(LSUP_Graph *src, LSUP_Graph *dest, void *ctx)
|
|
|
|
+{
|
|
|
|
+ memcpy(keyset_peek(src->keys), &NULL_TRP, TRP_KLEN);
|
|
|
|
+}
|