|
@@ -0,0 +1,364 @@
|
|
|
|
+#include <math.h>
|
|
|
|
+
|
|
|
|
+#include "structures/keyset.h"
|
|
|
|
+
|
|
|
|
+#define KLEN sizeof(LSUP_Key)
|
|
|
|
+#define DBL_KLEN sizeof(LSUP_DoubleKey)
|
|
|
|
+#define TRP_KLEN sizeof(LSUP_TripleKey)
|
|
|
|
+#define QUAD_KLEN sizeof(LSUP_QuadKey)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+LSUP_TripleKey NULL_TRP = {NULL_KEY, NULL_KEY, NULL_KEY};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * CALLBACKS
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+typedef bool (*LSUP_key_cmp_fn_t)(
|
|
|
|
+ const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2);
|
|
|
|
+
|
|
|
|
+// Keyset lookup for S LSUP_Key.
|
|
|
|
+static inline 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 LSUP_Key.
|
|
|
|
+static inline 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 LSUP_Key.
|
|
|
|
+static inline 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 inline 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 inline 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 inline 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 inline bool lookup_none_cmp_fn(
|
|
|
|
+ const LSUP_TripleKey* spok, const LSUP_Key k1, const LSUP_Key k2)
|
|
|
|
+{ return true; }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// Inline extern prototypes.
|
|
|
|
+
|
|
|
|
+bool LSUP_keyset_seek(LSUP_Keyset* ks, size_t idx);
|
|
|
|
+size_t LSUP_keyset_size(LSUP_Keyset* ks);
|
|
|
|
+size_t LSUP_keyset_tell(LSUP_Keyset* ks);
|
|
|
|
+const LSUP_TripleKey *LSUP_keyset_peek(LSUP_Keyset *ks);
|
|
|
|
+bool LSUP_keyset_contains(
|
|
|
|
+ const LSUP_Keyset *ks, const LSUP_TripleKey *val);
|
|
|
|
+bool LSUP_keyset_next(LSUP_Keyset *ks);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// Inline utils.
|
|
|
|
+
|
|
|
|
+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]);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_init(LSUP_Keyset *ks, size_t capacity, float expand_ratio)
|
|
|
|
+{
|
|
|
|
+ CRITICAL (ks->data = malloc(capacity * TRP_KLEN));
|
|
|
|
+ ks->capacity = capacity;
|
|
|
|
+ ks->cur = 0;
|
|
|
|
+ ks->free_i = 0;
|
|
|
|
+ ks->expand_ratio = expand_ratio;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+LSUP_Keyset *LSUP_keyset_new(size_t capacity, float expand_ratio) {
|
|
|
|
+ LSUP_Keyset *ks = malloc(sizeof(LSUP_Keyset));
|
|
|
|
+
|
|
|
|
+ LSUP_keyset_init(ks, capacity, expand_ratio);
|
|
|
|
+
|
|
|
|
+ return(ks);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Populate the provided `val` variable with the next available record.
|
|
|
|
+ *
|
|
|
|
+ * If the cursor is already at the last record, `val` is set to `NULL`.
|
|
|
|
+ *
|
|
|
|
+ * NOTE: This function copies data. For no-copy peek, use `LSUP_keyset_peek()`.
|
|
|
|
+ */
|
|
|
|
+bool LSUP_keyset_get_next(LSUP_Keyset *ks, LSUP_TripleKey *val) {
|
|
|
|
+
|
|
|
|
+ if (LSUP_keyset_next(ks)) {
|
|
|
|
+ memcpy(val, ks->data + ks->cur, TRP_KLEN);
|
|
|
|
+
|
|
|
|
+ return(1);
|
|
|
|
+ } else {
|
|
|
|
+ val = NULL;
|
|
|
|
+ return(0);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_resize(LSUP_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(0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_add(
|
|
|
|
+ LSUP_Keyset *ks, const LSUP_TripleKey *val, LSUP_KSFlag flags)
|
|
|
|
+{
|
|
|
|
+ if((flags & LSUP_KS_CHECK_DUP) && LSUP_keyset_contains(ks, val))
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ if((flags & LSUP_KS_CHECK_CAP) && ks->free_i >= ks->capacity) {
|
|
|
|
+ if(ks->expand_ratio > 0) {
|
|
|
|
+ LSUP_keyset_resize(
|
|
|
|
+ ks, 1 + (size_t)(ks->capacity * (1 + ks->expand_ratio)));
|
|
|
|
+ } else {
|
|
|
|
+ return -2; // TODO: ENOMEM
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memcpy(ks->data + ks->free_i, val, TRP_KLEN);
|
|
|
|
+
|
|
|
|
+ ks->cur = ks->free_i;
|
|
|
|
+ ks->free_i ++;
|
|
|
|
+
|
|
|
|
+ return(0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_remove(LSUP_Keyset *ks, const LSUP_TripleKey *val) {
|
|
|
|
+
|
|
|
|
+ LSUP_keyset_seek(ks, 0);
|
|
|
|
+
|
|
|
|
+ while (LSUP_keyset_next(ks)) {
|
|
|
|
+ if (memcmp(val, ks->data + ks->cur, TRP_KLEN) == 0) {
|
|
|
|
+ memcpy(ks->data + ks->cur, &NULL_TRP, TRP_KLEN);
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return(0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_copy(const LSUP_Keyset *src, LSUP_Keyset *dest) {
|
|
|
|
+
|
|
|
|
+ LSUP_keyset_init(dest, src->capacity, src->expand_ratio);
|
|
|
|
+
|
|
|
|
+ memcpy(dest->data, src->data, src->capacity * TRP_KLEN);
|
|
|
|
+
|
|
|
|
+ LSUP_keyset_seek(dest, 0);
|
|
|
|
+ dest->free_i = src->free_i;
|
|
|
|
+
|
|
|
|
+ return(0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_sparse_copy(LSUP_Keyset *src, LSUP_Keyset *dest) {
|
|
|
|
+
|
|
|
|
+ LSUP_keyset_init(dest, src->capacity, src->expand_ratio);
|
|
|
|
+
|
|
|
|
+ if (LSUP_keyset_seek(src, 0)) {
|
|
|
|
+ do {
|
|
|
|
+ if (LIKELY(memcmp(
|
|
|
|
+ LSUP_keyset_peek(src),
|
|
|
|
+ &NULL_TRP, TRP_KLEN) != 0)) {
|
|
|
|
+ LSUP_keyset_add(dest, LSUP_keyset_peek(src), 0);
|
|
|
|
+ }
|
|
|
|
+ } while (LSUP_keyset_next(src));
|
|
|
|
+
|
|
|
|
+ LSUP_keyset_seek(dest, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return(0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_lookup(
|
|
|
|
+ LSUP_Keyset *ks, LSUP_Keyset *res,
|
|
|
|
+ const LSUP_Key sk, const LSUP_Key pk, const LSUP_Key ok) {
|
|
|
|
+
|
|
|
|
+ LSUP_Key k1, k2;
|
|
|
|
+ LSUP_key_cmp_fn_t cmp_fn;
|
|
|
|
+
|
|
|
|
+ if (sk && pk && ok) {
|
|
|
|
+ LSUP_keyset_init(res, 1, 1);
|
|
|
|
+ LSUP_TripleKey spok = {sk, pk, ok};
|
|
|
|
+ if(LSUP_keyset_contains(ks, &spok)) {
|
|
|
|
+ LSUP_keyset_add(res, &spok, 0);
|
|
|
|
+ return 0;
|
|
|
|
+ } else {
|
|
|
|
+ return 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else if (sk) {
|
|
|
|
+ k1 = sk;
|
|
|
|
+
|
|
|
|
+ if (pk) { // s p ?
|
|
|
|
+ k2 = pk;
|
|
|
|
+ cmp_fn = lookup_skpk_cmp_fn;
|
|
|
|
+
|
|
|
|
+ } else if (ok) { // s ? o
|
|
|
|
+ k2 = ok;
|
|
|
|
+ cmp_fn = lookup_skok_cmp_fn;
|
|
|
|
+
|
|
|
|
+ } else { // s ? ?
|
|
|
|
+ cmp_fn = lookup_sk_cmp_fn;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else if (pk) {
|
|
|
|
+ k1 = pk;
|
|
|
|
+
|
|
|
|
+ if (ok) { // ? p o
|
|
|
|
+ k2 = ok;
|
|
|
|
+ cmp_fn = lookup_pkok_cmp_fn;
|
|
|
|
+
|
|
|
|
+ } else { // ? p ?
|
|
|
|
+ cmp_fn = lookup_pk_cmp_fn;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ } else if (ok) { // ? ? o
|
|
|
|
+ k1 = ok;
|
|
|
|
+ cmp_fn = lookup_ok_cmp_fn;
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ printf("WARNING: no bound terms, making a compact copy.\n");
|
|
|
|
+ return LSUP_keyset_sparse_copy(ks, res);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LSUP_keyset_init(res, ks->capacity, ks->expand_ratio);
|
|
|
|
+
|
|
|
|
+ LSUP_keyset_seek(ks, 0);
|
|
|
|
+ do {
|
|
|
|
+ if (cmp_fn(LSUP_keyset_peek(ks), k1, k2)) {
|
|
|
|
+ LSUP_keyset_add(res, LSUP_keyset_peek(ks), 0);
|
|
|
|
+ }
|
|
|
|
+ } while (LSUP_keyset_next(ks));
|
|
|
|
+
|
|
|
|
+ // Compact result keyset.
|
|
|
|
+ LSUP_keyset_resize(res, 0);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_join(LSUP_Keyset *ks1, LSUP_Keyset *ks2, LSUP_Keyset *res)
|
|
|
|
+{
|
|
|
|
+ LSUP_keyset_sparse_copy(ks1, res);
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+ } while (LSUP_keyset_next(ks2));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_subtract(LSUP_Keyset *ks1, LSUP_Keyset *ks2, LSUP_Keyset *res)
|
|
|
|
+{
|
|
|
|
+ LSUP_keyset_init(res, ks1->capacity, ks1->expand_ratio);
|
|
|
|
+
|
|
|
|
+ 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));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_intersect(LSUP_Keyset *ks1, LSUP_Keyset *ks2, LSUP_Keyset *res)
|
|
|
|
+{
|
|
|
|
+ LSUP_keyset_init(res, ks1->capacity, ks1->expand_ratio);
|
|
|
|
+
|
|
|
|
+ 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));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int LSUP_keyset_xor(LSUP_Keyset *ks1, LSUP_Keyset *ks2, LSUP_Keyset *res)
|
|
|
|
+{
|
|
|
|
+ LSUP_keyset_init(res, ks1->capacity + ks2->capacity, ks1->expand_ratio);
|
|
|
|
+
|
|
|
|
+ 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 (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));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+void LSUP_keyset_done(LSUP_Keyset *ks)
|
|
|
|
+{
|
|
|
|
+ if(LIKELY(ks->data != NULL))
|
|
|
|
+ free(ks->data);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+void LSUP_keyset_free(LSUP_Keyset *ks)
|
|
|
|
+{
|
|
|
|
+ if(LIKELY(ks != NULL)) {
|
|
|
|
+ LSUP_keyset_done(ks);
|
|
|
|
+ free(ks);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|