فهرست منبع

Initial reimplementation of hash table store:

* Using uthash library.
* No test; compiles and imports Python graphs.
Stefano Cossu 3 سال پیش
والد
کامیت
aed81212b4
12فایلهای تغییر یافته به همراه655 افزوده شده و 558 حذف شده
  1. 3 0
      .gitmodules
  2. 4 10
      cpython/py_graph.h
  3. 1 0
      ext/uthash
  4. 31 29
      include/graph.h
  5. 47 18
      include/store_htable.h
  6. 5 2
      setup.py
  7. 1 1
      src/buffer.c
  8. 72 121
      src/graph.c
  9. 488 0
      src/store_htable.c
  10. 0 373
      src/store_htable.c.disabled
  11. 1 2
      src/store_mdb.c
  12. 2 2
      src/triple.c

+ 3 - 0
.gitmodules

@@ -9,3 +9,6 @@
 	path = ext/openldap
 	url = https://git.openldap.org/openldap/openldap.git
 	branch = mdb.RE/0.9
+[submodule "ext/uthash"]
+	path = ext/uthash
+	url = https://github.com/troydhanson/uthash.git

+ 4 - 10
cpython/py_graph.h

@@ -91,17 +91,14 @@ Graph_copy (PyTypeObject *cls, PyObject *src)
 static PyObject *
 Graph_richcmp (PyObject *self, PyObject *other, int op)
 {
-    // Only equality and non-equality supported.
+    // Only equality and non-equality are supported.
     if (op != Py_EQ && op != Py_NE) Py_RETURN_NOTIMPLEMENTED;
 
-    Py_RETURN_TRUE; // TODO use graph xor == 0
-    /*
     LSUP_Graph *t1 = ((GraphObject *) self)->ob_struct;
     LSUP_Graph *t2 = ((GraphObject *) other)->ob_struct;
 
     if (LSUP_graph_equals (t1, t2) ^ (op == Py_NE)) Py_RETURN_TRUE;
     Py_RETURN_FALSE;
-    */
  }
 
 
@@ -109,8 +106,6 @@ static inline PyObject *
 Graph_bool_op (
         PyTypeObject *cls, LSUP_bool_op op, PyObject *gr1, PyObject *gr2)
 {
-    Py_RETURN_NONE; //TODO Implement mdbstore bool ops
-    /*
     if (! PyObject_TypeCheck (gr1, cls) || ! PyObject_TypeCheck (gr2, cls))
         return NULL;
 
@@ -123,7 +118,6 @@ Graph_bool_op (
 
     Py_INCREF(res);
     return (PyObject *) res;
-    */
 }
 
 
@@ -142,7 +136,7 @@ Graph_add (PyObject *self, PyObject *triples)
     int rc = 0;
     size_t i;
     LSUP_SerTriple *sspo = STRP_DUMMY;
-    LSUP_GraphIterator *it = LSUP_graph_add_stream_init (
+    LSUP_GraphIterator *it = LSUP_graph_add_init (
             ((GraphObject *)self)->ob_struct);
 
     for (i = 0; (trp_obj = PyIter_Next (iter)); i++) {
@@ -156,7 +150,7 @@ Graph_add (PyObject *self, PyObject *triples)
         TRACE ("Inserting triple #%lu\n", i);
 
         LSUP_triple_serialize (((TripleObject *)trp_obj)->ob_struct, sspo);
-        LSUP_rc db_rc = LSUP_graph_add_stream_iter (it, sspo);
+        LSUP_rc db_rc = LSUP_graph_add_iter (it, sspo);
 
         if (db_rc == LSUP_OK) rc = LSUP_OK;
         if (UNLIKELY (db_rc < 0)) {
@@ -166,7 +160,7 @@ Graph_add (PyObject *self, PyObject *triples)
     }
 
 finalize:
-    LSUP_graph_add_stream_done (it);
+    LSUP_graph_add_done (it);
     LSUP_striple_free (sspo);
 
     PyObject *ret = PyLong_FromSize_t (LSUP_graph_iter_cur (it));

+ 1 - 0
ext/uthash

@@ -0,0 +1 @@
+Subproject commit 66e2668795d0aaf4977523f828e548470a680c33

+ 31 - 29
include/graph.h

@@ -2,6 +2,7 @@
 #define _LSUP_GRAPH_H
 
 #include "store_mdb.h"
+#include "store_htable.h"
 
 /*
  * Define backend types and checks.
@@ -90,38 +91,14 @@ void
 LSUP_graph_free (LSUP_Graph *gr);
 
 
-/** @brief Number of triples that can be stored without resizing the graph.
- *
- * @return Dynamic capacity of an in-memory graph or maximum allowed memory for
- *  an MDB graph.
- */
-size_t
-LSUP_graph_capacity (const LSUP_Graph *gr);
-
-
 /** @brief Number of triples in a graph.
  */
 size_t
 LSUP_graph_size (const LSUP_Graph *gr);
 
 
-/** @brief Change the capacity of an in-memory graph.
- *
- * This is useful ahead of a bulk load to save multiple reallocs. Otherwise,
- * the graph expands automatically on new inserts when capacity is reached.
- *
- * @param gr[in] Graph to be resized.
- *
- * @param size[in] New size. This will never be smaller than the current
- *  occupied space. Therefore setting this value to 0 effectively compacts the
- *  graph storage.
- *
- * @return LSUP_OK if the operation was successful; LSUP_VALUE_ERR if the store
- *  type of the graph is not LSUP_STORE_MEM; <0 if an error occurs while
- *  resizing.
- */
-LSUP_rc
-LSUP_graph_resize (LSUP_Graph *gr, size_t size);
+bool
+LSUP_graph_equals (const LSUP_Graph *gr1, const LSUP_Graph *gr2);
 
 
 /** @brief Read-only graph URI.
@@ -145,17 +122,37 @@ bool
 LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo);
 
 
+/** @brief Initialize an iterator to add triples.
+ *
+ * @param[in] gr Graph to add to. It is added to the iterator state.
+ *
+ * @return Iterator handle. This should be passed to #LSUP_graph_add_iter() and
+ * must be freed with #LSUP_graph_add_done().
+ */
 LSUP_GraphIterator *
-LSUP_graph_add_stream_init (LSUP_Graph *gr);
+LSUP_graph_add_init (LSUP_Graph *gr);
 
 
+
+/** @brief Add a single triple to the store.
+ *
+ * @param[in] it Iterator obtained with #LSUP_graph_add_init().
+ *
+ * @param[in] sspo Serialized triple to add.
+ */
 LSUP_rc
-LSUP_graph_add_stream_iter (
+LSUP_graph_add_iter (
         LSUP_GraphIterator *it, const LSUP_SerTriple *sspo);
 
 
+/** @brief Finalize an add iteration loop and free the iterator.
+ *
+ * DO NOT USE with iterators obtained with other than #LSUP_graph_add_init().
+ *
+ * @param[in] it Iterator to finalize.
+ */
 void
-LSUP_graph_add_stream_done (LSUP_GraphIterator *it);
+LSUP_graph_add_done (LSUP_GraphIterator *it);
 
 
 /** @brief Add triples and/or serialized triples to a graph.
@@ -215,6 +212,11 @@ LSUP_graph_iter_next (LSUP_GraphIterator *it, LSUP_Triple *spo);
 
 
 /** @brief Free a graph iterator.
+ *
+ * DO NOT USE with iterators obtained with #LSUP_graph_add_init(). Use
+ * #LSUP_graph_add_done() with those.
+ *
+ * @param[in] it Iterator to finalize.
  */
 void
 LSUP_graph_iter_free (LSUP_GraphIterator *it);

+ 47 - 18
include/store_htable.h

@@ -21,14 +21,13 @@
 #define _LSUP_STORE_HTABLE_H
 
 #include "triple.h"
-#include "htable.h"
 
 
-typedef struct HTStore LSUP_HTStore;
-typedef struct HTIterator LSUP_HTIterator;
+typedef struct ht_store_t LSUP_HTStore;
+typedef struct ht_iterator_t LSUP_HTIterator;
 
 LSUP_HTStore *
-LSUP_htstore_new(size_t capacity);
+LSUP_htstore_new (void);
 
 
 /** @brief Boolean operation on hash table triples.
@@ -36,11 +35,11 @@ LSUP_htstore_new(size_t capacity);
  * The resulting store is compacted to the minimum size necessary to hold all
  * results.
  *
- * @param op[in] Operation type. See #LSUP_bool_op
+ * @param[in] op Operation type. See #LSUP_bool_op
  *
- * @param s1[in] First store.
+ * @param[in] s1 First store.
  *
- * @param s2[in] Second store.
+ * @param[in] s2 Second store.
  *
  * @return New store resulting from the operation. It must be freed with
  * #LSUP_htstore_free after use.
@@ -49,14 +48,44 @@ LSUP_HTStore *
 LSUP_htstore_bool_op(
         const LSUP_bool_op op, const LSUP_HTStore *s1, const LSUP_HTStore *s2);
 
+
+/** @brief Free a hash table store.
+ */
 void
-LSUP_htstore_free(LSUP_HTStore *ht);
+LSUP_htstore_free (LSUP_HTStore *ht);
+
 
+/** @brief Copy contents of a store to another store.
+ *
+ * The destination is not initialized, so copy is cumulative with the existing
+ * content.
+ *
+ * @param[in] Store to copy to. It must be already initialized via
+ *  #LSUP_htstore_new(), #LSUP_HTstore_copy(), etc.
+ *
+ * @param[in] src Store to copy from.
+ */
 LSUP_rc
-LSUP_htstore_resize(LSUP_HTStore *ht, htsize_t size);
+LSUP_htstore_copy_contents (LSUP_HTStore *dest, const LSUP_HTStore *src);
+
+
+LSUP_HTIterator *
+LSUP_htstore_add_init (LSUP_HTStore *store);
+
 
+/** @brief Add triples to the store.
+ *
+ * @param[in] store Store handle.
+ *
+ * @param[in] sspo Triples to add, serialized into buffer triples.
+ */
 LSUP_rc
-LSUP_htstore_add(LSUP_HTStore *store, const LSUP_SerTriple *sspo);
+LSUP_htstore_add_iter (LSUP_HTIterator *it, const LSUP_SerTriple *sspo);
+
+
+void
+LSUP_htstore_add_done (LSUP_HTIterator *it);
+
 
 LSUP_rc
 LSUP_htstore_remove(
@@ -66,16 +95,16 @@ LSUP_HTIterator *
 LSUP_htstore_lookup(
         LSUP_HTStore *store, const LSUP_SerTriple *sspo, size_t *ct);
 
-htsize_t
-LSUP_htstore_size(LSUP_HTStore *ht);
+size_t
+LSUP_htstore_size (LSUP_HTStore *ht);
 
-htsize_t
-LSUP_htstore_capacity(const LSUP_HTStore *ht);
+void
+LSUP_htiter_free (LSUP_HTIterator *it);
 
-LSUP_rc
-LSUP_htiter_next(LSUP_HTIterator *it, LSUP_SerTriple *sspo);
+size_t
+LSUP_htiter_cur (LSUP_HTIterator *it);
 
-void
-LSUP_htiter_free(LSUP_HTIterator *it);
+LSUP_rc
+LSUP_htiter_next (LSUP_HTIterator *it, LSUP_SerTriple *sspo);
 
 #endif  // _LSUP_STORE_HTABLE_H

+ 5 - 2
setup.py

@@ -13,9 +13,9 @@ sources = (
     glob(path.join(SRC_DIR, '*.c')) +
     glob(path.join(MOD_DIR, '*.c')) +
     [
-        path.join(EXT_DIR, 'xxHash', 'xxhash.c'),
         path.join(EXT_DIR, 'openldap', 'libraries', 'liblmdb', 'mdb.c'),
         path.join(EXT_DIR, 'openldap', 'libraries', 'liblmdb', 'midl.c'),
+        path.join(EXT_DIR, 'xxHash', 'xxhash.c'),
     ]
 )
 
@@ -43,7 +43,10 @@ setup(
         Extension(
             "_lsup_rdf",
             sources,
-            include_dirs=[INCL_DIR],
+            include_dirs=[
+                INCL_DIR,
+                path.join(EXT_DIR, 'uthash', 'src'),
+            ],
             libraries=['uuid'],
             extra_compile_args=compile_args,
         ),

+ 1 - 1
src/buffer.c

@@ -88,4 +88,4 @@ void LSUP_buffer_free (LSUP_Buffer *buf)
 
 /* Extern inline prototypes. */
 
-inline LSUP_Key LSUP_buffer_hash (const LSUP_Buffer *buf);
+LSUP_Key LSUP_buffer_hash (const LSUP_Buffer *buf);

+ 72 - 121
src/graph.c

@@ -33,7 +33,7 @@ typedef struct Graph {
     LSUP_store_type         store_type;     // In-memory or MDB-backed
     LSUP_Term               *uri;                 // Graph "name" (URI)
     union {
-        //LSUP_HTStore *      ht_store;
+        LSUP_HTStore *      ht_store;
         LSUP_MDBStore *     mdb_store;
     };
 } Graph;
@@ -42,7 +42,7 @@ typedef struct Graph {
 typedef struct GraphIterator {
     const Graph *           graph;      // Parent graph.
     union {                             // Internal store iterator.
-        //LSUP_HTIterator *   ht_iter;
+        LSUP_HTIterator *   ht_iter;
         LSUP_MDBIterator *  mdb_iter;
     };
     size_t                  ct;         // Total matches.
@@ -119,13 +119,14 @@ LSUP_graph_new (const LSUP_store_type store_type)
     if (UNLIKELY (mdbstore_init() != LSUP_OK)) return NULL;
 
     if (gr->store_type == LSUP_STORE_MEM) {
-        // TODO uncomment gr->ht_store = LSUP_htstore_new (0);
-        // if (!gr->ht_store) return NULL;
+        gr->ht_store = LSUP_htstore_new();
+        if (UNLIKELY (!gr->ht_store)) return NULL;
 
     } else if (gr->store_type == LSUP_STORE_MDB) {
         gr->mdb_store = default_store;
+        if (UNLIKELY (!gr->mdb_store)) return NULL;
 
-    } else {
+    } else { // LSUP_STORE_MDB_TMP
         gr->mdb_store = default_tmp_store;
     }
 
@@ -172,7 +173,7 @@ LSUP_graph_copy (const Graph *src)
 }
 
 
-/* TODO uncomment 
+// TODO support boolean ops between any types of graphs.
 Graph *
 LSUP_graph_bool_op(
         const LSUP_bool_op op, const Graph *gr1, const Graph *gr2)
@@ -199,7 +200,6 @@ LSUP_graph_bool_op(
 
     return res;
 }
-*/
 
 
 void
@@ -208,9 +208,11 @@ LSUP_graph_free (LSUP_Graph *gr)
     if (LIKELY (gr != NULL)) {
         LSUP_term_free (gr->uri);
 
-        /*
         if (gr->store_type == LSUP_STORE_MEM)
-            NULL;// TODO uncomment LSUP_htstore_free (gr->ht_store);
+            LSUP_htstore_free (gr->ht_store);
+        /*
+        // NOTE This is not required bacause MDB store is only one for now,
+        // cleared at exit.
         else
             LSUP_mdbstore_free (gr->mdb_store);
         */
@@ -229,65 +231,66 @@ LSUP_graph_set_uri (LSUP_Graph *gr, const char *uri)
 { return LSUP_uri_init (gr->uri, uri); }
 
 
-LSUP_rc
-LSUP_graph_resize (LSUP_Graph *gr, size_t size)
-{
-    return LSUP_OK; // TODO remove line.
-    if (gr->store_type == LSUP_STORE_MEM)
-        return 0;// TODO uncomment LSUP_htstore_resize (gr->ht_store, size);
-
-    return LSUP_VALUE_ERR;
-}
-
-
 size_t
-LSUP_graph_capacity (const Graph *gr)
+LSUP_graph_size (const Graph *gr)
 {
+    TRACE ("Store type: %d\n", gr->store_type);
     if (gr->store_type == LSUP_STORE_MEM)
-        return 0;// TODO uncomment LSUP_htstore_capacity (gr->ht_store);
+        LSUP_htstore_size (gr->ht_store);
 
-    return LSUP_NOT_IMPL_ERR;
+    return LSUP_mdbstore_size (gr->mdb_store);
 }
 
 
-size_t
-LSUP_graph_size (const Graph *gr)
+bool
+LSUP_graph_equals (const Graph *gr1, const Graph *gr2)
 {
-    TRACE ("Store type: %d\n", gr->store_type);
-    if (gr->store_type == LSUP_STORE_MEM)
-        return 0;// TODO uncomment LSUP_htstore_size (gr->ht_store);
+    LSUP_Graph *res = LSUP_graph_bool_op (LSUP_BOOL_XOR, gr1, gr2);
 
-    return LSUP_mdbstore_size (gr->mdb_store);
+    return (LSUP_graph_size (res) == 0);
 }
 
-// TODO Add add_stream_init, add_stream_iter and add_stream_done for streaming
-// functions.
 
 GraphIterator *
-LSUP_graph_add_stream_init (LSUP_Graph *gr)
+LSUP_graph_add_init (LSUP_Graph *gr)
 {
     GraphIterator *it = malloc (sizeof (*it));
     if (UNLIKELY (!it)) return NULL;
 
-    LSUP_Buffer *sc = LSUP_buffer_new_from_term (gr->uri);
-    it->mdb_iter = LSUP_mdbstore_add_init (gr->mdb_store, sc);
-    LSUP_buffer_free (sc);
+    if (gr->store_type == LSUP_STORE_MEM) {
+        it->ht_iter = LSUP_htstore_add_init (gr->ht_store);
+    } else {
+        LSUP_Buffer *sc = LSUP_buffer_new_from_term (gr->uri);
+        it->mdb_iter = LSUP_mdbstore_add_init (gr->mdb_store, sc);
+        LSUP_buffer_free (sc);
+    }
 
     it->graph = gr;
-    //it->i = 0;
 
     return it;
 }
 
 
 LSUP_rc
-LSUP_graph_add_stream_iter (LSUP_GraphIterator *it, const LSUP_SerTriple *sspo)
-{ return LSUP_mdbstore_add_iter (it->mdb_iter, sspo); }
+LSUP_graph_add_iter (LSUP_GraphIterator *it, const LSUP_SerTriple *sspo)
+{
+    if (it->graph->store_type == LSUP_STORE_MEM) {
+        return LSUP_htstore_add_iter (it->ht_iter, sspo);
+    } else {
+        return LSUP_mdbstore_add_iter (it->mdb_iter, sspo);
+    }
+}
 
 
 void
-LSUP_graph_add_stream_done (LSUP_GraphIterator *it)
-{ LSUP_mdbstore_add_done(it->mdb_iter); }
+LSUP_graph_add_done (LSUP_GraphIterator *it)
+{
+    if (it->graph->store_type == LSUP_STORE_MEM) {
+        LSUP_htstore_add_done (it->ht_iter);
+    } else {
+        LSUP_mdbstore_add_done (it->mdb_iter);
+    }
+}
 
 
 LSUP_rc
@@ -296,100 +299,48 @@ LSUP_graph_add(
         const LSUP_Triple trp[], size_t trp_ct,
         const LSUP_SerTriple strp[], size_t strp_ct, size_t *inserted)
 {
-    LSUP_rc rc;
-
-    if (inserted) *inserted = 0;
-
     /*
      * NOTE It is possible to pass both sets of RDF triples and buffer triples.
      */
 
-    if (gr->store_type == LSUP_STORE_MEM) {
-        return LSUP_NOT_IMPL_ERR;
-    /* TODO
-        // 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;
-        }
-
-        rc = LSUP_NOACTION;
-
-        // Serialize and insert RDF triples.
-        if (trp_ct > 0) {
-            for (size_t i = 0; i < trp_ct; i++) {
-                LSUP_SerTriple sspo;
-                LSUP_triple_serialize (trp + i, &sspo);
-
-                TRACE ("Inserting triple #%lu\n", i);
-                LSUP_rc db_rc = LSUP_htstore_add (gr->ht_store, &sspo);
-                if (LIKELY (db_rc == LSUP_OK)) {
-                    rc = LSUP_OK;
-                    if (inserted) (*inserted) ++;
-                }
-
-                LSUP_striple_done (&sspo);
-
-                if (UNLIKELY (db_rc < 0)) return db_rc;
-            }
-        }
-
-        // 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);
-
-            if (LIKELY (db_rc == LSUP_OK)) {
-                rc = LSUP_OK;
-                if (inserted) (*inserted) ++;
-            }
-            if (UNLIKELY (db_rc < 0)) return db_rc;
-        }
-
-        return rc;
-    */
-    } else {
-        rc = LSUP_NOACTION;
+    LSUP_rc rc = LSUP_NOACTION;
 
-        // Initialize iterator.
-        LSUP_GraphIterator *it = LSUP_graph_add_stream_init (gr);
+    // Initialize iterator.
+    LSUP_GraphIterator *it = LSUP_graph_add_init (gr);
 
-        // Serialize and insert RDF triples.
-        if (trp_ct > 0) {
-            LSUP_SerTriple *sspo = STRP_DUMMY;
+    // Serialize and insert RDF triples.
+    LSUP_SerTriple *sspo = STRP_DUMMY;
+    for (size_t i = 0; i < trp_ct; i++) {
+        TRACE ("Inserting triple #%lu\n", i);
 
-            for (size_t i = 0; i < trp_ct; i++) {
-                TRACE ("Inserting triple #%lu\n", i);
+        LSUP_triple_serialize (trp + i, sspo);
+        LSUP_rc db_rc = LSUP_graph_add_iter (it, sspo);
 
-                LSUP_triple_serialize (trp + i, sspo);
-                LSUP_rc db_rc = LSUP_graph_add_stream_iter (it, sspo);
+        if (db_rc == LSUP_OK) rc = LSUP_OK;
+        if (UNLIKELY (db_rc < 0)) return db_rc;
+    }
+    LSUP_striple_free (sspo);
 
-                if (db_rc == LSUP_OK) rc = LSUP_OK;
-                if (UNLIKELY (db_rc < 0)) return db_rc;
-            }
+    // Insert serialized triples.
+    for (size_t i = 0; i < strp_ct; i++) {
+        TRACE ("Inserting serialized triple #%lu\n", i);
+        LSUP_rc db_rc = LSUP_graph_add_iter (it, strp + i);
 
-            LSUP_striple_free (sspo);
-        }
+        if (db_rc == LSUP_OK) rc = LSUP_OK;
+        if (UNLIKELY (db_rc < 0)) return db_rc;
+    }
 
-        // Insert serialized triples.
-        for (size_t i = 0; i < strp_ct; i++) {
-            TRACE ("Inserting triple #%lu\n", i);
-            LSUP_rc db_rc = LSUP_graph_add_stream_iter (it, strp + i);
+    LSUP_graph_add_done (it);
 
-            if (db_rc == LSUP_OK) rc = LSUP_OK;
-            if (UNLIKELY (db_rc < 0)) return db_rc;
+    if (inserted) {
+        if (gr->store_type == LSUP_STORE_MEM) {
+            *inserted = LSUP_htiter_cur (it->ht_iter);
+        } else {
+            *inserted = LSUP_mdbiter_cur (it->mdb_iter);
         }
-
-        LSUP_graph_add_stream_done (it);
-
-        if (inserted) *inserted = it->ct;
-
-        return rc;
     }
 
-    return LSUP_VALUE_ERR;
+    return rc;
 }
 
 
@@ -529,7 +480,7 @@ mdbstore_init()
 
     // RAM disk store.
     if (UNLIKELY (!default_tmp_store)) {
-        printf("Initilalizing RAM disk back end.\n");
+        printf ("Initializing RAM disk back end.\n");
         path = MDB_RAMDISK_PATH;
         if (LSUP_mdbstore_setup (path, true) != LSUP_OK) return LSUP_DB_ERR;
         default_tmp_store = LSUP_mdbstore_new (path, default_ctx);
@@ -538,7 +489,7 @@ mdbstore_init()
 
     // Permanent store.
     if (UNLIKELY (!default_store)) {
-        printf("Initilalizing persistent back end.\n");
+        printf ("Initializing persistent back end.\n");
         // NOTE This method will only allow one persistent disk back end per
         // application. TODO maybe later allow multiple backends if useful.
         path = getenv ("LSUP_MDB_STORE_PATH");

+ 488 - 0
src/store_htable.c

@@ -0,0 +1,488 @@
+#include "uthash.h"
+
+#include "store_htable.h"
+
+
+/**
+ * Callback type for key comparison.
+ */
+typedef bool (*LSUP_key_eq_fn_t)(
+        const LSUP_Key spok[], const LSUP_Key luk[]);
+
+
+typedef struct triple_entry_t {
+    LSUP_TripleKey  key;
+    UT_hash_handle  hh;
+} TripleEntry;
+
+typedef struct idx_entry_t {
+    LSUP_Key        key;
+    LSUP_Buffer *   sterm;
+    UT_hash_handle  hh;
+} IndexEntry;
+
+typedef struct ht_store_t {
+    TripleEntry *       keys;       // Triple keys (set).
+    IndexEntry *        idx;        // Dictionary of keys to serialized terms.
+} HTStore;
+
+typedef struct ht_iterator_t {
+    HTStore *           store;      // Store being iterated.
+    size_t              i;          // Lookup cursor.
+    LSUP_Key            luk[3];     // 0÷3 lookup keys.
+    LSUP_key_eq_fn_t    eq_fn;      // Equality function to test triples.
+    int                 rc;         // Return code for *next* result.
+    TripleEntry *       spok;       // Retrieved SPO key.
+} HTIterator;
+
+
+/**
+ * Identity hashing function.
+ *
+ * Since the key is already a strong hash, reuse it for bucket allocation.
+ */
+#define HASH_FUNCTION (s,len,hashv) (hashv) = (unsigned)(s)
+
+/* * * CALLBACKS * * */
+
+/**
+ * Dummy callback for queries with all parameters unbound. Returns true.
+*/
+static bool lookup_none_eq_fn(
+        const LSUP_Key spok[], const LSUP_Key luk[])
+{ return true; }
+
+/**
+ * Keyset lookup for S key.
+ */
+static bool lookup_sk_eq_fn(
+        const LSUP_Key spok[], const LSUP_Key luk[])
+{ return spok[0] == luk[0]; }
+
+/**
+ * Keyset lookup for P key.
+ */
+static bool lookup_pk_eq_fn(
+        const LSUP_Key spok[], const LSUP_Key luk[])
+{ return spok[1] == luk[0]; }
+
+/**
+ * Keyset lookup for O key.
+ */
+static bool lookup_ok_eq_fn(
+        const LSUP_Key spok[], const LSUP_Key luk[])
+{ return spok[2] == luk[0]; }
+
+/**
+ * Keyset lookup for S and P keys.
+ */
+static bool lookup_spk_eq_fn(
+        const LSUP_Key spok[], const LSUP_Key luk[])
+{ return spok[0] == luk[0] && spok[1] == luk[1]; }
+
+/**
+ * Keyset lookup for S and O keys.
+ */
+static bool lookup_sok_eq_fn(
+        const LSUP_Key spok[], const LSUP_Key luk[])
+{ return spok[0] == luk[0] && spok[2] == luk[1]; }
+
+/**
+ * Keyset lookup for P and O keys.
+ */
+static bool lookup_pok_eq_fn(
+        const LSUP_Key spok[], const LSUP_Key luk[])
+{ return spok[1] == luk[0] && spok[2] == luk[1]; }
+
+
+/* * * Other prototypes. * * */
+
+static inline LSUP_rc htiter_next_key (HTIterator *it);
+static inline LSUP_rc tkey_to_strp (
+        const HTStore *store, const LSUP_Key spok[], LSUP_SerTriple *sspo);
+
+
+/* * * API * * */
+
+HTStore *
+LSUP_htstore_new (void)
+{
+    HTStore *ht = malloc (sizeof (*ht));
+    if (UNLIKELY (!ht)) return NULL;
+
+    ht->keys = NULL;
+    ht->idx = NULL;
+
+    return ht;
+}
+
+
+HTStore *
+LSUP_htstore_copy (const HTStore *src)
+{
+    HTStore *dest = LSUP_htstore_new();
+    if (UNLIKELY (!dest)) return NULL;
+
+    if (LSUP_htstore_copy_contents (dest, src) < 0) {
+        LSUP_htstore_free (dest);
+        return NULL;
+    }
+
+    return dest;
+}
+
+
+LSUP_rc
+LSUP_htstore_copy_contents (HTStore *dest, const HTStore *src)
+{
+    TripleEntry *dest_trp = malloc (
+            HASH_COUNT (src->keys) * sizeof (*dest_trp));
+    if (UNLIKELY (!dest_trp)) return LSUP_MEM_ERR;
+
+    IndexEntry *dest_idx = malloc (
+            HASH_COUNT (src->idx) * sizeof (*dest_idx));
+    if (UNLIKELY (!dest_idx)) {
+        free (dest_trp);
+        return LSUP_MEM_ERR;
+    }
+
+    TripleEntry *src_trp;
+    IndexEntry *src_idx;
+    unsigned i;
+
+    // Copy triple data.
+    for (
+        src_trp = src->keys, i = 0; src_trp != NULL;
+        src_trp = src->keys->hh.next
+    ) {
+        memcpy (dest_trp + i, src_trp, sizeof (*dest_trp));
+        HASH_ADD (hh, dest->keys, key, TRP_KLEN, dest_trp + i);
+        i++;
+    }
+
+    // Copy index data.
+    for (
+        src_idx = src->idx, i = 0; src_idx != NULL;
+        src_idx = src->idx->hh.next
+    ) {
+        memcpy (dest_idx + i, src_idx, sizeof (*dest_idx));
+        HASH_ADD (hh, dest->idx, key, KLEN, dest_idx + i);
+        i++;
+    }
+
+    return LSUP_OK;
+}
+
+
+HTStore *
+LSUP_htstore_bool_op(
+        const LSUP_bool_op op, const HTStore *s1, const HTStore *s2)
+{
+    HTStore *dest;
+    TripleEntry *trp_cur;
+
+    dest = LSUP_htstore_new();
+
+    if (UNLIKELY (
+            op != LSUP_BOOL_UNION
+            && op != LSUP_BOOL_SUBTRACTION
+            && op != LSUP_BOOL_INTERSECTION
+            && op != LSUP_BOOL_XOR)) {
+        fprintf (stderr, "Operation not supported.\n");
+        goto fail;
+    }
+
+    if (op == LSUP_BOOL_UNION) {
+        dest = LSUP_htstore_copy (s1);
+        if (UNLIKELY (!dest) || LSUP_htstore_copy_contents (dest, s2) < 0)
+            goto fail;
+
+        return dest;
+    }
+
+    LSUP_SerTriple *sspo = STRP_DUMMY;
+
+    LSUP_HTIterator *it = LSUP_htstore_add_init(dest);
+
+    if (op == LSUP_BOOL_XOR) {
+        // Add triples from s2 if not found in s1.
+        for (
+            trp_cur = s2->keys; trp_cur != NULL;
+            trp_cur = s2->keys->hh.next
+        ) {
+            TripleEntry *ins = NULL;
+            HASH_FIND (hh, s1->keys, trp_cur->key, TRP_KLEN, ins);
+
+            if (!ins) {
+                tkey_to_strp (s2, trp_cur->key, sspo);
+                LSUP_htstore_add_iter (it, sspo);
+            }
+        }
+    }
+
+    for (
+        trp_cur = s1->keys; trp_cur != NULL;
+        trp_cur = s1->keys->hh.next
+    ) {
+        TripleEntry *ins = NULL;
+        HASH_FIND (hh, s2->keys, trp_cur->key, TRP_KLEN, ins);
+
+        // For XOR and subtraction, add if not found.
+        // For intersection, add if found.
+        if ((op == LSUP_BOOL_INTERSECTION) ^ (ins == NULL)) {
+            tkey_to_strp (s1, trp_cur->key, sspo);
+            LSUP_htstore_add_iter (it, sspo);
+        }
+    }
+    LSUP_striple_free (sspo);
+    LSUP_htstore_add_done (it);
+
+    return dest;
+
+fail:
+    LSUP_htstore_free (dest);
+    return NULL;
+}
+
+
+void
+LSUP_htstore_free (HTStore *ht)
+{
+    IndexEntry *idx_entry, *idx_tmp;
+    HASH_ITER (hh, ht->idx, idx_entry, idx_tmp) {
+        HASH_DEL (ht->idx, idx_entry);
+        free (idx_entry);
+    }
+
+    TripleEntry *trp_entry, *trp_tmp;
+    HASH_ITER (hh, ht->keys, trp_entry, trp_tmp) {
+        HASH_DEL (ht->keys, trp_entry);
+        free (trp_entry);
+    }
+
+    free (ht);
+}
+
+
+size_t
+LSUP_htstore_size (LSUP_HTStore *ht)
+{ return HASH_COUNT (ht->keys); }
+
+
+LSUP_HTIterator *
+LSUP_htstore_add_init (HTStore *store)
+{
+    HTIterator *it = malloc (sizeof (HTIterator));
+    if (UNLIKELY (!it)) return NULL;
+
+    it->store = store;
+    it->i = 0;
+
+    return it;
+}
+
+
+LSUP_rc
+LSUP_htstore_add_iter (HTIterator *it, const LSUP_SerTriple *sspo)
+{
+    LSUP_rc rc = LSUP_NOACTION;
+    LSUP_TripleKey spok = NULL_TRP;
+
+    // Add terms to index.
+    for (int i = 0; i < 3; i++) {
+        spok[i] = LSUP_buffer_hash (LSUP_striple_pos (sspo, i));
+        TRACE ("Indexing term key %lu\n", spok[i]);
+
+        IndexEntry *ins = NULL;
+        HASH_FIND (hh, it->store->idx, spok + i, KLEN, ins);
+        if (ins == NULL) {
+            ins = malloc (sizeof (*ins));
+            if (UNLIKELY (!ins)) return LSUP_MEM_ERR;
+            ins->key = spok[i];
+            ins->sterm = LSUP_striple_pos (sspo, i);
+            HASH_ADD (hh, it->store->idx, key, KLEN, ins);
+        }
+    }
+
+    // Add triple.
+    TRACE ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
+
+    TripleEntry *k_ins = NULL;
+    HASH_FIND (hh, it->store->keys, spok, TRP_KLEN, k_ins);
+    if (k_ins == NULL) {
+        k_ins = malloc (sizeof (*k_ins));
+        if (UNLIKELY (!k_ins)) return LSUP_MEM_ERR;
+
+        memcpy (k_ins->key, spok, TRP_KLEN);
+        HASH_ADD (hh, it->store->keys, key, TRP_KLEN, k_ins);
+
+        it->i++;
+        rc = LSUP_OK;
+    }
+
+    return rc;
+}
+
+
+void
+LSUP_htstore_add_done (HTIterator *it)
+{ free (it); }
+
+
+LSUP_rc
+LSUP_htstore_remove(
+        LSUP_HTStore *store, const LSUP_SerTriple *sspo, size_t *ct)
+{
+    LSUP_HTIterator *it = LSUP_htstore_lookup (store, sspo, ct);
+    if (UNLIKELY (!it)) return LSUP_DB_ERR;
+
+    *ct = 0;
+    while (htiter_next_key (it)) {
+        HASH_DEL (store->keys, it->spok);
+        free (it->spok);
+        //if (UNLIKELY (rc < 0)) return rc;
+
+        (*ct) ++;
+    }
+    // TODO clean up orphan indices in separate (async, scheduled) function.
+
+    return LSUP_OK;
+}
+
+
+HTIterator *
+LSUP_htstore_lookup (HTStore *store, const LSUP_SerTriple *sspo, size_t *ct)
+{
+    HTIterator *it = malloc (sizeof (*it));
+    if (UNLIKELY (!it)) return NULL;
+
+    it->store = store;
+    //it->cur = 0;
+    it->rc = LSUP_END;
+
+    if (HASH_COUNT (store->keys) == 0) return it;
+
+    LSUP_TripleKey spok = {
+        LSUP_buffer_hash (sspo->s),
+        LSUP_buffer_hash (sspo->p),
+        LSUP_buffer_hash (sspo->o),
+    };
+
+    // s p o
+    if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
+        memcpy (it->luk, spok, sizeof (LSUP_TripleKey));
+        it->eq_fn = NULL;
+
+    } else if (spok[0] != NULL_KEY) {
+        it->luk[0] = spok[0];
+
+        // s p ?
+        if (spok[1] != NULL_KEY) {
+            it->luk[1] = spok[1];
+            it->eq_fn = lookup_spk_eq_fn;
+
+        // s ? o
+        } else if (spok[2] != NULL_KEY) {
+            it->luk[1] = spok[2];
+            it->eq_fn = lookup_sok_eq_fn;
+
+        // s ? ?
+        } else {
+            it->eq_fn = lookup_sk_eq_fn;
+        }
+    //it->cur = 0;
+
+    } else if (spok[1] != NULL_KEY) {
+        it->luk[0] = spok[1];
+
+        // ? p o
+        if (spok[2] != NULL_KEY) {
+            it->luk[1] = spok[2];
+            it->eq_fn = lookup_pok_eq_fn;
+
+        // ? p ?
+        } else it->eq_fn = lookup_pk_eq_fn;
+
+    // ? ? o
+    } else if (spok[2] != NULL_KEY) {
+        it->luk[0] = spok[2];
+        it->eq_fn = lookup_ok_eq_fn;
+
+    // ? ? ?
+    } else it->eq_fn = lookup_none_eq_fn;
+
+    it->spok = it->store->keys->hh.next;
+    it->rc = it->spok == NULL ? LSUP_END : LSUP_OK;
+
+    return it;
+}
+
+
+void
+LSUP_htiter_free (LSUP_HTIterator *it)
+{ free (it); }
+
+
+size_t
+LSUP_htiter_cur (LSUP_HTIterator *it)
+{ return it->i; }
+
+
+LSUP_rc
+LSUP_htiter_next (HTIterator *it, LSUP_SerTriple *sspo)
+{
+    LSUP_rc rc = htiter_next_key (it);
+    if (UNLIKELY (rc != LSUP_OK)) return rc;
+
+    IndexEntry *cur;
+
+    HASH_FIND (hh, it->store->idx, it->spok + 0, KLEN, cur);
+    sspo->s = cur->sterm;
+    HASH_FIND (hh, it->store->idx, it->spok + 1, KLEN, cur);
+    sspo->p = cur->sterm;
+    HASH_FIND (hh, it->store->idx, it->spok + 2, KLEN, cur);
+    sspo->o = cur->sterm;
+
+    if (!sspo->s || !sspo->p || !sspo->o) {
+        sspo = NULL;
+        return LSUP_DB_ERR;
+    }
+
+    return LSUP_OK;
+}
+
+
+/* * * Statics * * */
+
+static inline LSUP_rc
+htiter_next_key (HTIterator *it)
+{
+    // Loop as long as there are entries, and until a match is found.
+    for (;;) {
+        if (it->rc != LSUP_OK) return it->rc;
+        if (it->eq_fn (it->spok->key, it->luk)) return LSUP_OK;
+
+        TripleEntry *cur = NULL, *tmp = NULL;
+        HASH_ITER (hh, it->store->keys, cur, tmp);
+        if (cur != NULL) {
+            it->spok = cur;
+            it->rc = LSUP_OK;
+        } else it->rc = LSUP_NORESULT;
+    }
+}
+
+
+inline static LSUP_rc
+tkey_to_strp (
+        const HTStore *store, const LSUP_Key spok[], LSUP_SerTriple *sspo)
+{
+    for (unsigned i = 0; i < 3; i++) {
+        IndexEntry *cur = NULL;
+        HASH_FIND (hh, store->idx, spok + i, KLEN, cur);
+        if (!UNLIKELY (cur)) return LSUP_ERROR;
+
+        memcpy (LSUP_striple_pos (sspo, i), cur->sterm, sizeof (*cur->sterm));
+    };
+
+    return LSUP_OK;
+}

+ 0 - 373
src/store_htable.c.disabled

@@ -1,373 +0,0 @@
-#include "store_htable.h"
-
-// 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.
-#define IDX_SIZE_RATIO 1.7
-
-
-/**
- * Callback type for key comparison.
- */
-typedef bool (*LSUP_key_eq_fn_t)(
-        const LSUP_Key spok[], const LSUP_Key luk[]);
-
-
-typedef struct HTStore {
-    LSUP_HTable *keys;
-    LSUP_HTable *idx;            // Dictionary of keys to serialized terms
-} HTStore;
-
-typedef struct HTIterator {
-    HTStore *           store;      // Store being iterated.
-    LSUP_HTable *       ht;         // Hash table to look up.
-    htsize_t            cur;        // Lookup cursor.
-    LSUP_Key            luk[3];     // 0÷3 lookup keys.
-    LSUP_key_eq_fn_t    eq_fn;      // Equality function to test triples.
-    int                 rc;         // Return code for *next* result.
-    LSUP_Key *          spok;       // Retrieved SPO key.
-} HTIterator;
-
-
-/**
- * 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; }
-
-
-/* * * CALLBACKS * * */
-
-/**
- * Dummy callback for queries with all parameters unbound. Returns true.
-*/
-static bool lookup_none_eq_fn(
-        const LSUP_Key spok[], const LSUP_Key luk[])
-{ return true; }
-
-/**
- * Keyset lookup for S key.
- */
-static bool lookup_sk_eq_fn(
-        const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[0] == luk[0]; }
-
-/**
- * Keyset lookup for P key.
- */
-static bool lookup_pk_eq_fn(
-        const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[1] == luk[0]; }
-
-/**
- * Keyset lookup for O key.
- */
-static bool lookup_ok_eq_fn(
-        const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[2] == luk[0]; }
-
-/**
- * Keyset lookup for S and P keys.
- */
-static bool lookup_spk_eq_fn(
-        const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[0] == luk[0] && spok[1] == luk[1]; }
-
-/**
- * Keyset lookup for S and O keys.
- */
-static bool lookup_sok_eq_fn(
-        const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[0] == luk[0] && spok[2] == luk[1]; }
-
-/**
- * Keyset lookup for P and O keys.
- */
-static bool lookup_pok_eq_fn(
-        const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[1] == luk[0] && spok[2] == luk[1]; }
-
-
-/* * * Other prototypes. * * */
-
-static inline LSUP_rc htiter_next_key(HTIterator *it);
-
-
-/* * * API * * */
-
-HTStore *
-LSUP_htstore_new(size_t capacity)
-{
-    HTStore *ht;
-    CRITICAL(ht = malloc (sizeof (*ht)));
-
-    ht->keys = LSUP_htable_new(
-            capacity, TRP_KLEN, 0, xx64_hash_fn, buffer_eq_fn);
-
-    ht->idx = LSUP_htable_new(
-        capacity * IDX_SIZE_RATIO, sizeof(uint64_t), sizeof(uintptr_t),
-        xx64_hash_fn, buffer_eq_fn);
-
-    return ht;
-}
-
-void
-LSUP_htstore_free(HTStore *ht)
-{
-    if (!ht) return;
-
-    LSUP_htable_free(ht->keys);
-
-    // Free up index entries and index.
-    htsize_t cur = 0;
-    LSUP_TripleKey spok;
-    LSUP_Buffer *sterm;
-    while(LSUP_htable_iter(
-                ht->idx, &cur, (void**)&spok, (void**)&sterm) == LSUP_OK) {
-        TRACE("Freeing indexed term buffer #%d at %p", cur, sterm);
-        LSUP_buffer_done(sterm);
-    }
-
-    LSUP_htable_free(ht->idx);
-    free(ht);
-}
-
-
-htsize_t
-LSUP_htstore_size(LSUP_HTStore *ht)
-{ return LSUP_htable_size(ht->keys); }
-
-
-htsize_t
-LSUP_htstore_capacity(const LSUP_HTStore *ht)
-{ return LSUP_htable_capacity(ht->keys); }
-
-
-LSUP_rc
-LSUP_htstore_resize(HTStore *ht, htsize_t size)
-{
-    LSUP_rc rc = LSUP_htable_resize(ht->keys, size);
-    if (rc != LSUP_OK) return rc;
-
-    return LSUP_htable_resize(ht->idx, size * IDX_SIZE_RATIO);
-}
-
-
-LSUP_rc
-LSUP_htstore_add(HTStore *store, const LSUP_SerTriple *sspo)
-{
-    LSUP_TripleKey spok = NULL_TRP;
-
-    // Add term to index.
-    for (int i = 0; i < 3; i++) {
-        spok[i] = LSUP_sterm_to_key(LSUP_striple_pos(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(store->idx, spok + i, NULL) == LSUP_OK)
-            LSUP_htable_put(store->idx, spok + i, LSUP_striple_pos(sspo, i));
-    }
-
-    // Add triple.
-    TRACE("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
-
-    return LSUP_htable_put(store->keys, spok, NULL);
-}
-
-
-LSUP_rc
-LSUP_htstore_remove(
-        LSUP_HTStore *store, const LSUP_SerTriple *sspo, size_t *ct)
-{
-    LSUP_HTIterator *it = LSUP_htstore_lookup(store, sspo, ct);
-    if (UNLIKELY (!it)) return LSUP_DB_ERR;
-
-    *ct = 0;
-    while (htiter_next_key (it)) {
-        LSUP_rc rc = LSUP_htable_remove(store->keys, it->spok);
-        if (UNLIKELY (rc < 0)) return rc;
-
-        (*ct) ++;
-    }
-    // TODO clean up orphan indices in separate function.
-
-    return LSUP_OK;
-}
-
-
-HTIterator *
-LSUP_htstore_lookup(HTStore *store, const LSUP_SerTriple *sspo, size_t *ct)
-{
-    HTIterator *it;
-    CRITICAL(it = malloc (sizeof (*it)));
-    it->store = store;
-    it->cur = 0;
-    it->rc = LSUP_END;
-
-    if (LSUP_htable_size(store->keys) == 0) return it;
-
-    LSUP_TripleKey spok = {
-        LSUP_sterm_to_key(sspo->s),
-        LSUP_sterm_to_key(sspo->p),
-        LSUP_sterm_to_key(sspo->o),
-    };
-
-    // s p o
-    if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
-        memcpy(it->luk, spok, sizeof(LSUP_TripleKey));
-        it->eq_fn = NULL;
-
-    } else if (spok[0] != NULL_KEY) {
-        it->luk[0] = spok[0];
-
-        // s p ?
-        if (spok[1] != NULL_KEY) {
-            it->luk[1] = spok[1];
-            it->eq_fn = lookup_spk_eq_fn;
-
-        // s ? o
-        } else if (spok[2] != NULL_KEY) {
-            it->luk[1] = spok[2];
-            it->eq_fn = lookup_sok_eq_fn;
-
-        // s ? ?
-        } else {
-            it->eq_fn = lookup_sk_eq_fn;
-        }
-
-    } else if (spok[1] != NULL_KEY) {
-        it->luk[0] = spok[1];
-
-        // ? p o
-        if (spok[2] != NULL_KEY) {
-            it->luk[1] = spok[2];
-            it->eq_fn = lookup_pok_eq_fn;
-
-        // ? p ?
-        } else it->eq_fn = lookup_pk_eq_fn;
-
-    // ? ? o
-    } else if (spok[2] != NULL_KEY) {
-        it->luk[0] = spok[2];
-        it->eq_fn = lookup_ok_eq_fn;
-
-    // ? ? ?
-    } else it->eq_fn = lookup_none_eq_fn;
-
-    it->rc = LSUP_htable_iter(
-            it->store->keys, &it->cur, (void**)&it->spok, NULL);
-
-    return it->rc >= 0 ? it : NULL;
-}
-
-
-static inline LSUP_rc
-htiter_next_key(HTIterator *it)
-{
-    for (;;) {
-        if (it->rc != LSUP_OK) return it->rc;
-        if (it->eq_fn(it->spok, it->luk)) return LSUP_OK;
-
-        it->rc = LSUP_htable_iter(
-                it->store->keys, &it->cur, (void**)&it->spok, NULL);
-    }
-}
-
-
-LSUP_rc
-LSUP_htiter_next(HTIterator *it, LSUP_SerTriple *sspo)
-{
-    LSUP_rc rc = htiter_next_key(it);
-    if (UNLIKELY (rc != LSUP_OK)) return rc;
-
-    for (int i = 0; i < 3; i++)
-        LSUP_htable_get(
-                it->store->idx, (void*)it->spok[i],
-                (void**)(LSUP_striple_pos(sspo, i)));
-
-    return rc;
-}
-
-
-void
-LSUP_htiter_free(LSUP_HTIterator *it)
-{ free(it); }
-
-
-HTStore *
-LSUP_htstore_bool_op(
-        const LSUP_bool_op op, const HTStore *s1, const HTStore *s2)
-{
-    HTStore *dest;
-    htsize_t cur;
-    void *key, *val;
-
-    dest = LSUP_htstore_new(0);
-
-    if (UNLIKELY (
-            op != LSUP_BOOL_UNION
-            && op != LSUP_BOOL_SUBTRACTION
-            && op != LSUP_BOOL_INTERSECTION
-            && op != LSUP_BOOL_XOR)) {
-        fprintf(stderr, "Operation not supported.\n");
-        goto fail;
-    }
-
-    if (op == LSUP_BOOL_UNION) {
-        dest->keys = LSUP_htable_copy(s1->keys);
-        while (LSUP_htable_iter(s2->keys, &cur, &key, NULL) != LSUP_END)
-            LSUP_htable_put(dest->keys, key, NULL);
-
-        dest->idx = LSUP_htable_copy(s1->idx);
-        while (LSUP_htable_iter(s2->idx, &cur, &key, &val) != LSUP_END)
-            LSUP_htable_put(dest->idx, key, val);
-
-    } else {
-        if (op == LSUP_BOOL_XOR) {
-            while (LSUP_htable_iter(s2->keys, &cur, &key, NULL) != LSUP_END) {
-                LSUP_rc get_rc = LSUP_htable_get(s1->keys, key, NULL);
-                if (get_rc == LSUP_NORESULT) {
-                    LSUP_htable_put(dest->keys, key, NULL);
-
-                    if (LSUP_htable_get(s2->idx, key, &val) == LSUP_OK)
-                        LSUP_htable_put(dest->idx, key, val);
-
-                } else if (UNLIKELY(get_rc < 0)) goto fail;
-            }
-        }
-
-        while (LSUP_htable_iter(s1->keys, &cur, &key, NULL) != LSUP_END) {
-            LSUP_rc get_rc = LSUP_htable_get(s2->keys, key, NULL);
-            if (
-                (op == LSUP_BOOL_INTERSECTION && get_rc == LSUP_OK)
-                || get_rc == LSUP_NORESULT
-            ) {
-                LSUP_htable_put(dest->keys, key, NULL);
-
-                if (LSUP_htable_get(s1->idx, key, &val) == LSUP_OK)
-                    LSUP_htable_put(dest->idx, key, val);
-
-            } else if (UNLIKELY(get_rc < 0)) goto fail;
-        }
-    }
-
-    return dest;
-
-fail:
-    LSUP_htstore_free(dest);
-    return NULL;
-}

+ 1 - 2
src/store_mdb.c

@@ -380,7 +380,6 @@ LSUP_rc
 LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_SerTriple *sspo)
 {
     int db_rc;
-    LSUP_rc rc;
     LSUP_TripleKey spok = NULL_TRP;
 
     // Add triple.
@@ -434,7 +433,7 @@ LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_SerTriple *sspo)
     }
 
     // Index.
-    rc = index_triple (it->store, OP_ADD, spok, it->ck);
+    LSUP_rc rc = index_triple (it->store, OP_ADD, spok, it->ck);
     if (rc == LSUP_OK) it->i++;
 
     return rc;

+ 2 - 2
src/triple.c

@@ -172,5 +172,5 @@ LSUP_striple_free (LSUP_SerTriple *sspo)
 
 /* Inline extern prototypes. */
 
-inline LSUP_Key LSUP_striple_hash (const LSUP_SerTriple *strp);
-inline LSUP_Key LSUP_triple_hash (const LSUP_Triple *trp);
+LSUP_Key LSUP_striple_hash (const LSUP_SerTriple *strp);
+LSUP_Key LSUP_triple_hash (const LSUP_Triple *trp);