Kaynağa Gözat

WIP hashmap overhaul.

Stefano Cossu 2 yıl önce
ebeveyn
işleme
254c13c4e1

+ 14 - 10
cpython/py_graph.h

@@ -335,6 +335,7 @@ inline static int build_trp_pattern (PyObject *args, LSUP_Term *spo[])
 }
 
 
+/*
 static PyObject *
 Graph_new_set_from_store_lookup (PyTypeObject *cls, PyObject *args)
 {
@@ -365,6 +366,7 @@ Graph_new_set_from_store_lookup (PyTypeObject *cls, PyObject *args)
     Py_INCREF (ret);
     return ret;
 }
+*/
 
 
 static PyObject *
@@ -413,11 +415,11 @@ Graph_add (PyObject *self, PyObject *triples)
 
     PyObject *trp_obj;
     int rc = 0;
-    size_t i;
+    size_t ct = 0;
     LSUP_GraphIterator *it = LSUP_graph_add_init (
             ((GraphObject *)self)->ob_struct);
 
-    for (i = 0; (trp_obj = PyIter_Next (iter)); i++) {
+    while ((trp_obj = PyIter_Next (iter))) {
         if (!PyObject_TypeCheck (trp_obj, &TripleType)) {
             PyErr_SetString (
                     PyExc_ValueError, "Object is not a triple.");
@@ -425,26 +427,26 @@ Graph_add (PyObject *self, PyObject *triples)
             goto finally;
         }
 
-        log_trace ("Inserting triple #%lu", i);
+        log_trace ("Inserting triple #%lu", ct);
 
         LSUP_rc db_rc = LSUP_graph_add_iter (
                 it, ((TripleObject *) trp_obj)->ob_struct);
 
-        if (db_rc == LSUP_OK) rc = LSUP_OK;
-        if (UNLIKELY (db_rc < 0)) {
-            PyErr_SetString (
-                    PyExc_ValueError, "Unknown error while adding triples.");
+        if (db_rc == LSUP_OK) {
+            rc = LSUP_OK;
+            ct++;
+        } else if (UNLIKELY (db_rc < 0)) {
+            PyErr_SetString (PyExc_ValueError, "Error while adding triples.");
             rc = -1;
             goto finally;
         }
+        // If db_rc > 0, it's a no-op and the counter is not increased.
     }
 
 finally:
     LSUP_graph_add_done (it);
 
-    if (rc == LSUP_OK)
-        return PyLong_FromSize_t (LSUP_graph_iter_cur (it));
-
+    if (rc == LSUP_OK) return PyLong_FromSize_t (ct);
     return NULL;
 }
 
@@ -551,11 +553,13 @@ static PyMethodDef Graph_methods[] = {
         METH_CLASS | METH_VARARGS,
         "Create a graph from a RDF file."
     },
+    /*
     {
         "from_lookup", (PyCFunction) Graph_new_set_from_store_lookup,
         METH_CLASS | METH_VARARGS,
         "Create a set of graphs from a store SPO lookup."
     },
+    */
     {
         "store", (PyCFunction) Graph_store, METH_NOARGS,
         "Store a graph into the permanent back end."

+ 32 - 31
docs/dev/deps.dot

@@ -5,45 +5,46 @@ digraph "source tree" {
     fontsize="16";
     fontname="Helvetica";
 	clusterrank="local";
-	"py_triple" -> "py_term"
-	"store_mdb" -> "namespace"
-	"store_htable" -> "uthash"
-	"graph" -> "store_htable"
-	"store_htable" -> "buffer"
-	"nt_parser" -> "nt_grammar"
-	"py_graph" -> "codec_nt"
-	"codec_nt" -> "nt_parser"
 	"codec_nt" -> "codec_base"
-	"codec_base" -> "graph"
-	"store_mdb" -> "lmdb"
+	"namespace" -> "core"
+	"buffer" -> "core"
+	"py_graph" -> "codec_nt"
+	"store_htable" -> "hashmap"
 	"core" -> "lmdb"
-	"graph" -> "store_mdb"
-	"store_mdb" -> "buffer"
 	"graph" -> "environment"
-	"buffer" -> "core"
+	"core" -> "xxhash"
+	"environment" -> "hashmap"
+	"graph" -> "store_htable"
 	"core" -> "log"
-	"nt_grammar" -> "graph"
-	"py_term" -> "term"
-	"py_graph" -> "graph"
+	"codec_nt" -> "nt_parser"
+	"py_lsup_rdf" -> "py_namespace"
 	"graph" -> "term"
-	"term" -> "buffer"
-	"environment" -> "term"
-	"profile" -> "lsup_rdf"
+	"store_mdb" -> "buffer"
 	"py_term" -> "py_namespace"
-	"buffer" -> "xxhash"
-	"py_lsup_rdf" -> "py_graph"
-	"namespace" -> "core"
-	"store_mdb" -> "store"
 	"environment" -> "store_mdb"
-	"namespace" -> "uthash"
-	"store_mdb" -> "bootstrap"
-	"store_mdb" -> "uthash"
-	"lsup_rdf" -> "codec_nt"
+	"store_mdb" -> "store"
+	"environment" -> "term"
+	"term" -> "hashmap"
+	"store_mdb" -> "namespace"
+	"graph" -> "store_mdb"
+	"term" -> "buffer"
+	"py_triple" -> "py_term"
+	"nt_parser" -> "nt_grammar"
+	"store_htable" -> "buffer"
 	"term" -> "namespace"
-	"py_namespace" -> "namespace"
-	"py_graph" -> "py_triple"
+	"lsup_rdf" -> "codec_nt"
+	"profile" -> "lsup_rdf"
+	"store_mdb" -> "lmdb"
 	"term" -> "tpl"
+	"py_graph" -> "py_triple"
+	"store_htable" -> "store"
+	"py_namespace" -> "namespace"
+	"namespace" -> "hashmap"
+	"py_term" -> "term"
+	"store_mdb" -> "bootstrap"
+	"py_graph" -> "graph"
 	"nt_parser" -> "graph"
-	"py_lsup_rdf" -> "py_namespace"
-	"term" -> "uthash"
+	"py_lsup_rdf" -> "py_graph"
+	"codec_base" -> "graph"
+	"nt_grammar" -> "graph"
 }

BIN
docs/dev/deps.pdf


+ 1 - 1
ext/hashmap

@@ -1 +1 @@
-Subproject commit 86ac5d4b2d510ebf7ca2d74e59a94a839e686fb0
+Subproject commit 774694ec6dd36f1bb8e5fd3a55a2c47e48295e21

+ 1 - 1
ext/log

@@ -1 +1 @@
-Subproject commit f9ea34994bd58ed342d2245cd4110bb5c6790153
+Subproject commit 9b7a338c3cee9a68f118c22c9e322b809fda198d

+ 5 - 8
include/graph.h

@@ -62,7 +62,7 @@ LSUP_graph_new_env (
     LSUP_graph_new_env (LSUP_default_env, uri, type)
 
 
-/** @brief Create an array of graph from triples matching a pattern in a store.
+/** @brief Create an array of graphs from pattern matching on an MDB store.
  *
  * The s, p, o lookup terms behave as in #LSUP_store_lookup().
  *
@@ -75,12 +75,15 @@ LSUP_graph_new_env (
  * @param[in] env Environment to look into. If NULL, it is set to the deafult
  *  environment.
  *
- * @return A NULL-terminated array of graphs.
+ * @return A NULL-terminated array of graph handles.
  */
+/* TODO Only used in CPython from_lookup. Review the utility of the function.
+ * Remove if not useful.
 LSUP_Graph **
 LSUP_graph_new_lookup_env (
         const LSUP_Env *env, const LSUP_Term *s,
         const LSUP_Term *p, const LSUP_Term *o);
+*/
 
 /** @brief Shortcut for #LSUP_graph_new_lookup_env() on default MDB store.
  */
@@ -346,10 +349,4 @@ LSUP_graph_iter_next (LSUP_GraphIterator *it, LSUP_Triple *spo);
 void
 LSUP_graph_iter_free (LSUP_GraphIterator *it);
 
-
-/** @brief Get the iterator counter.
- */
-size_t
-LSUP_graph_iter_cur (LSUP_GraphIterator *it);
-
 #endif

+ 13 - 9
include/namespace.h

@@ -44,8 +44,7 @@ LSUP_nsmap_free (LSUP_NSMap *map);
 
 /** @brief Add a prefix -> namespace pair to the map or update it.
  *
- * If the prefix already exists, it is updated with the new value, if
- * different.
+ * If the prefix already exists, it is quietly updated with the new value.
  *
  * @param[in] map The map to add to.
  *
@@ -53,12 +52,11 @@ LSUP_nsmap_free (LSUP_NSMap *map);
  *
  * @param[in] nsstr Fully qualified namespace.
  *
- * @return LSUP_OK if the record was added or replaced; LSUP_NOACTION if the
- *  record already existed with the same value; LSUP_MEM_ERR if an allocation
- *  error occurred.
+ * @return LSUP_OK if the record was added or replaced; LSUP_MEM_ERR if an
+ * allocation error occurred.
  */
 LSUP_rc
-LSUP_nsmap_add (LSUP_NSMap *map, const ns_pfx pfx, const char *nsstr);
+LSUP_nsmap_add (LSUP_NSMap *map, const char *pfx, const char *nsstr);
 
 
 /** @brief Remove a prefix -> namespace pair from a map.
@@ -70,7 +68,7 @@ LSUP_nsmap_add (LSUP_NSMap *map, const ns_pfx pfx, const char *nsstr);
  * @return LSUP_OK on successful delete; LSUP_NOACTION if no record was found.
  */
 LSUP_rc
-LSUP_nsmap_remove (LSUP_NSMap *map, const ns_pfx pfx);
+LSUP_nsmap_remove (LSUP_NSMap *map, const char *pfx);
 
 
 /** @brief Get the namespace for a prefix.
@@ -83,7 +81,7 @@ LSUP_nsmap_remove (LSUP_NSMap *map, const ns_pfx pfx);
  *  should not be modified directly.
  */
 const char *
-LSUP_nsmap_get_ns (LSUP_NSMap *map, const ns_pfx pfx);
+LSUP_nsmap_get_ns (LSUP_NSMap *map, const char *pfx);
 
 
 /** @brief Get the prefix for a namespace.
@@ -117,6 +115,12 @@ LSUP_nsmap_normalize_uri (
 
 
 /** @brief Convert a FQ URI string to a prefixed string if the prefix is found.
+ *
+ * TODO Note that this function does not attempt to find the longest or
+ * shortest namespace prefix in case of conflicts (e.g. when both
+ * `http://example.edu/` and `http://example.edu/data/` are mapped and
+ * `http://example.edu/data/51937642` is being denormalized). In such
+ * case, the first prefix that is found is assigned.
  *
  * @param[in] map Namespace map to look up.
  *
@@ -131,7 +135,7 @@ LSUP_nsmap_normalize_uri (
  */
 LSUP_rc
 LSUP_nsmap_denormalize_uri (
-        const LSUP_NSMap *map, const char *fq_uri, char **pfx_uri);
+        LSUP_NSMap *map, const char *fq_uri, char **pfx_uri);
 
 
 /** @brief Dump all entries of a namespace map.

+ 14 - 16
include/store.h

@@ -1,6 +1,15 @@
+/** @file store.h
+ *
+ * @brief Common back end store definitions.
+ *
+ * New store implementations should include this header.
+ */
+
+
 #ifndef _LSUP_STORE_H
 #define _LSUP_STORE_H
 
+
 /*
  * Store feature flags.
  *
@@ -11,21 +20,10 @@
  * the medium (i.e. a "permanent" store may have been created ad hoc on a
  * tempfs).
  */
-#define LSUP_STORE_PERM     1<<0    // Store is on a permanent location.
-#define LSUP_STORE_CTX      1<<1    // Store supports contexts (quads).
-#define LSUP_STORE_IDX      1<<2    // Store is fully SPO(C)-indexed.
-#define LSUP_STORE_TXN      1<<3    // Supports manual transaction handling.
-#define LSUP_STORE_NET      1<<4    // Store is over a network protocol.
-
-
-/* * * CALLBACKS * * */
-
-uint64_t key_hash_fn (
-        const void *item, uint64_t seed0, uint64_t seed1)
-{ return (uint64_t) item; }
-
-
-int key_cmp_fn (const void *a, const void *b, void *udata)
-{ return (LSUP_Key) a - (LSUP_Key) b; }
+#define LSUP_STORE_PERM     1<<0    /// Store is on a permanent location.
+#define LSUP_STORE_CTX      1<<1    /// Store supports contexts (quads).
+#define LSUP_STORE_IDX      1<<2    /// Store is fully SPO(C)-indexed.
+#define LSUP_STORE_TXN      1<<3    /// Supports manual transaction handling.
+#define LSUP_STORE_NET      1<<4    /// Store is over a network protocol.
 
 #endif  /* _LSUP_STORE_H */

+ 16 - 5
include/store_htable.h

@@ -144,12 +144,16 @@ LSUP_htstore_add_done (LSUP_HTIterator *it);
  *
  * @param[in] so Serialized object term. If NULL, the term is unbound.
  *
+ * @param[out] ct If not NULL, it is populated with the number of results. Use
+ *  only if necessary: since the hash table is not indexed, retrieving the
+ *  count requires looping over the results, thus slowing down the operation.
+ *
  * @return Iterator for lookup results.
  */
 LSUP_HTIterator *
 LSUP_htstore_lookup (
         LSUP_HTStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
-        const LSUP_Buffer *so);
+        const LSUP_Buffer *so, size_t *ct);
 
 
 /** @brief Find the next triple in a lookup and return the result.
@@ -170,11 +174,21 @@ LSUP_htiter_next (LSUP_HTIterator *it, LSUP_BufferTriple *sspo);
  *
  * @param[in] it Iterator handle.
  *
- * @return Number of results yielded, or triples added. at a certain point of
+ * @return Number of results yielded, or triples added, at a certain point of
  *  an iterator.
  */
+/*
 size_t
 LSUP_htiter_count (const LSUP_HTIterator *it);
+*/
+
+
+/** @brief Free an iterator.
+ *
+ * @param[in] it Iterator handle obtained from #LSUP_htstore_lookup().
+ */
+void
+LSUP_htiter_free (LSUP_HTIterator *it);
 
 
 /** @brief Remove triples by pattern matching.
@@ -201,7 +215,4 @@ LSUP_htstore_remove (
         LSUP_HTStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
         const LSUP_Buffer *so, size_t *ct);
 
-void
-LSUP_htiter_free (LSUP_HTIterator *it);
-
 #endif  // _LSUP_STORE_HTABLE_H

+ 22 - 7
include/store_mdb.h

@@ -280,11 +280,22 @@ LSUP_mdbstore_lookup(
  * owned by the database. It must not be written to or freed. To modify
  * the data or use them beyond the caller's scope, this memory must be copied.
  *
- * @param it[in] Opaque iterator handle obtained with #LSUP_mdbstore_lookup.
+ * @param[in] it Opaque iterator handle obtained with #LSUP_mdbstore_lookup.
  *
- * @param sspo[out] #LSUP_BufferTriple to be populated with three serialized terms
- * if found, NULL if not found. Internal callers (e.g. counters) may pass NULL
- * if they don't need the serialized triples.
+ * @param[out] sspo #LSUP_BufferTriple to be populated with three serialized
+ * terms if found, NULL if not found. Internal callers (e.g. counters) may pass
+ * NULL if they don't need the serialized triples.
+ *
+ * @param[out] ctx If not NULL, it is populated with a NULL-terminated array of
+ *  LSUP_Buffer structs, one for each context associated with the matching
+ *  triple. These contexts are the same regardless of the context filter used
+ *  in the lookup. The array is freed with a simple #free().
+ *
+ *  To iterate over the context array, use this loop:
+ *
+ *      size_t i = 0;
+ *      while (ctx[i].addr)
+ *          do_something(ctx + i++); // Buffer data are memory-mapped and RO.
  *
  * @return LSUP_OK if results were found; LSUP_END if no (more) results were
  * found; LSUP_DB_ERR if a MDB_* error occurred.
@@ -303,7 +314,7 @@ LSUP_rc LSUP_mdbiter_next (
  *  of succcessfully inserted records.
  */
 size_t
-LSUP_mdbiter_cur (LSUP_MDBIterator *it);
+LSUP_mdbiter_count (LSUP_MDBIterator *it);
 
 
 /** @brief Free an iterator allocated by a lookup.
@@ -329,6 +340,8 @@ void LSUP_mdbiter_free (LSUP_MDBIterator *it);
  *
  * ss, sp, so arguments are used as in #LSUP_mdbstore_lookup().
  *
+ * TODO Remove deprecated function and use the standard lookup method.
+ *
  * @param store[in] The store to be queried.
  *
  * @param ss[in] Serialized subject. It may be NULL.
@@ -340,10 +353,12 @@ void LSUP_mdbiter_free (LSUP_MDBIterator *it);
  * @return Array of context handles. Memory is allocated by this function and
  * must be freed by the caller.
  */
+/*
 LSUP_Buffer **
 LSUP_mdbstore_lookup_contexts (
         LSUP_MDBStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
         const LSUP_Buffer *so);
+*/
 
 
 /** @brief Get all namespace prefixes in the store.
@@ -399,10 +414,10 @@ LSUP_mdbstore_add_term (LSUP_MDBStore *store, const LSUP_Buffer *sterm);
  *
  * @param[in] key Key to look up.
  *
- * @raturn 1 if the term exists, 0 if it does not exist; <0 on error.
+ * @return 1 if the term exists, 0 if it does not exist; <0 on error.
  */
 int
 LSUP_mdbstore_tkey_exists (LSUP_MDBStore *store, LSUP_Key tkey);
 
 
-#endif
+#endif  /* _LSUP_STORE_MDB_H */

+ 6 - 2
src/environment.c

@@ -23,11 +23,15 @@ LSUP_Env *LSUP_default_env = NULL;
 
 static uint64_t term_cache_hash_fn (
         const void *item, uint64_t seed0, uint64_t seed1)
-{ return LSUP_term_hash ((LSUP_Term *) item); }
+{ return LSUP_term_hash (((LSUP_KeyedTerm *) item)->term); }
 
 
 static int term_cache_cmp_fn (const void *a, const void *b, void *udata)
-{ return LSUP_term_equals ((const LSUP_Term *) a, (const LSUP_Term *) b); }
+{
+    return LSUP_term_equals (
+            ((const LSUP_KeyedTerm *) a)->term,
+            ((const LSUP_KeyedTerm *) b)->term);
+}
 
 
 static void term_cache_free_fn (void *item)

+ 17 - 25
src/graph.c

@@ -73,7 +73,7 @@ LSUP_graph_new_env (
     gr->nsm = env->nsm;
 
     if (gr->store_type == LSUP_STORE_MEM) {
-        gr->ht_store = LSUP_htstore_new();
+        gr->ht_store = LSUP_htstore_new(0);
         if (UNLIKELY (!gr->ht_store)) return NULL;
 
     } else if (gr->store_type == LSUP_STORE_MDB) {
@@ -88,6 +88,7 @@ LSUP_graph_new_env (
 }
 
 
+/*
 LSUP_Graph **
 LSUP_graph_new_lookup_env (
         const LSUP_Env *env, const LSUP_Term *s,
@@ -97,18 +98,19 @@ LSUP_graph_new_lookup_env (
         log_error ("No valid environment passed. Did you call LSUP_init()?");
         return NULL;
     }
-    LSUP_Buffer *ss = LSUP_term_serialize (s);
-    LSUP_Buffer *sp = LSUP_term_serialize (p);
-    LSUP_Buffer *so = LSUP_term_serialize (o);
+    LSUP_Buffer
+        *ss = LSUP_term_serialize (s),
+        *sp = LSUP_term_serialize (p),
+        *so = LSUP_term_serialize (o);
 
-    LSUP_Buffer **ctx_a = LSUP_mdbstore_lookup_contexts (
-            env->mdb_store, ss, sp, so);
-    if (UNLIKELY (!ctx_a)) return NULL;
+    LSUP_MDBIterator *it;
+    it = LSUP_mdbstore_lookup (env->mdb_store, ss, sp, so, NULL, NULL);
 
     LSUP_buffer_free (ss);
     LSUP_buffer_free (sp);
     LSUP_buffer_free (so);
 
+    LSUP_Buffer *ctx;
     // Count for allocation.
     size_t i;
     for (i = 0; ctx_a[i] != NULL; i++) {}
@@ -126,6 +128,7 @@ LSUP_graph_new_lookup_env (
 
     return gr_a;
 }
+*/
 
 
 LSUP_Graph *
@@ -351,13 +354,14 @@ LSUP_graph_add_done (LSUP_GraphIterator *it)
 
 
 LSUP_rc
-LSUP_graph_add (Graph *gr, const LSUP_Triple trp[], size_t *inserted)
+LSUP_graph_add (Graph *gr, const LSUP_Triple trp[], size_t *ct)
 {
     LSUP_rc rc = LSUP_NOACTION;
 
     // Initialize iterator.
     LSUP_GraphIterator *it = LSUP_graph_add_init (gr);
 
+    if (ct) *ct = 0;
     // Serialize and insert RDF triples.
     for (size_t i = 0; trp[i].s != NULL; i++) {
         log_trace ("Inserting triple #%lu", i);
@@ -366,14 +370,7 @@ LSUP_graph_add (Graph *gr, const LSUP_Triple trp[], size_t *inserted)
 
         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);
-        }
+        if (ct && db_rc == LSUP_OK) (*ct)++;
     }
 
     LSUP_graph_add_done (it);
@@ -434,11 +431,11 @@ LSUP_graph_lookup (const Graph *gr, const LSUP_Term *s, const LSUP_Term *p,
     LSUP_Buffer *so = LSUP_term_serialize (o);
     LSUP_Buffer *sc = LSUP_term_serialize (gr->uri);
 
-    if (it->graph->store_type == LSUP_STORE_MEM) {
-        it->ht_iter = LSUP_htstore_lookup (it->graph->ht_store, ss, sp, so);
-        if (ct) *ct = it->ct;
+    if (it->graph->store_type == LSUP_STORE_MEM)
+        it->ht_iter = LSUP_htstore_lookup (
+                it->graph->ht_store, ss, sp, so, ct);
 
-    } else it->mdb_iter = LSUP_mdbstore_lookup (
+    else it->mdb_iter = LSUP_mdbstore_lookup (
             it->graph->mdb_store, ss, sp, so, sc, ct);
 
     LSUP_buffer_free (ss);
@@ -503,11 +500,6 @@ LSUP_graph_iter_free (GraphIterator *it)
 }
 
 
-size_t
-LSUP_graph_iter_cur (GraphIterator *it)
-{ return LSUP_mdbiter_cur (it->mdb_iter); }
-
-
 bool
 LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo)
 {

+ 51 - 59
src/namespace.c

@@ -13,11 +13,6 @@ typedef struct ns_entry_t {
 
 typedef struct hashmap NSMap;
 
-struct cmp_iter_t {
-    const char *        iri;        // IRI to compare.
-    NSEntry             entry;      // Pointer to found entry or NULL.
-};
-
 /** @brief Iterator for dumping NS map.
  */
 struct dump_iter_t {
@@ -37,7 +32,8 @@ static int nsmap_comp_fn (const void *a, const void *b, void *udata)
     return strncmp (nsa->pfx, nsb->pfx, PFX_LEN);
 }
 
-static uint64_t nsmap_hash_fn (const void *item, uint64_t seed0, uint64_t seed1)
+static uint64_t nsmap_hash_fn (
+        const void *item, uint64_t seed0, uint64_t seed1)
 {
     const NSEntry *nse = item;
     return (uint64_t) LSUP_HASH64 (nse->pfx, PFX_LEN, seed0);
@@ -46,25 +42,6 @@ static uint64_t nsmap_hash_fn (const void *item, uint64_t seed0, uint64_t seed1)
 static void nsmap_free_fn (void *item)
 { free (((NSEntry *) item)->ns); }
 
-/** @brief Find if a given IRI has a mapped namespace prefix.
- *
- * If found, the prefix is returned in the passed udata structure.
- */
-static bool nsmap_ns_cmp_fn (const void *item, void *udata)
-{
-    const NSEntry *entry = item;
-    struct cmp_iter_t *cmp = udata;
-
-    if (strncmp (entry->ns, cmp->iri, strlen (entry->ns)) == 0) {
-        memcpy (&(cmp->entry), entry, sizeof (cmp->entry));
-
-        return false;
-    }
-
-    cmp->entry.ns = NULL;
-    return true;
-}
-
 
 static bool nsmap_dump_ns_iter_fn (const void *item, void *udata)
 {
@@ -96,47 +73,51 @@ LSUP_nsmap_free (NSMap *map)
 
 
 LSUP_rc
-LSUP_nsmap_add (NSMap *map, const ns_pfx pfx, const char *nsstr)
+LSUP_nsmap_add (NSMap *map, const char *pfx, const char *nsstr)
 {
-    NSEntry *entry;
-    MALLOC_GUARD (entry, LSUP_MEM_ERR);
+    NSEntry entry_s;
 
-    entry->ns = strdup (nsstr);
-    size_t pfx_len = strlen(pfx);
-    if (pfx_len >= PFX_LEN)
+    if (strlen(pfx) >= PFX_LEN)
         log_warn(
                 "Prefix `%s` is longer than the maximum allowed size "
                 "(%d characters). Truncating.", pfx, PFX_LEN - 1);
-    strncpy (entry->pfx, pfx, PFX_LEN - 1);
+    strncpy (entry_s.pfx, pfx, sizeof (entry_s.pfx));
+    char *ns = strdup (nsstr);
+    entry_s.ns = ns;
 
-    hashmap_set (map, entry);
+    NSEntry *ret = hashmap_set (map, &entry_s);
     if (hashmap_oom (map)) return LSUP_MEM_ERR;
+    // Free replaced NS string.
+    if (ret) {
+        free (ret->ns);
+        free (ret);
+    }
 
     return LSUP_OK;
 }
 
 
 LSUP_rc
-LSUP_nsmap_remove (NSMap *map, const ns_pfx pfx)
+LSUP_nsmap_remove (NSMap *map, const char *pfx)
 {
-    NSEntry key;
-    strncpy (key.pfx, pfx, PFX_LEN);
-    NSEntry *entry = hashmap_delete (map, &key);
+    NSEntry entry_s;
+    strncpy (entry_s.pfx, pfx, PFX_LEN);
+    NSEntry *entry = hashmap_delete (map, &entry_s);
 
     if (!entry) return LSUP_NOACTION;
 
-    free (entry);
+    free (entry->ns);
 
     return LSUP_OK;
 }
 
 
 const char *
-LSUP_nsmap_get_ns (NSMap *map, const ns_pfx pfx)
+LSUP_nsmap_get_ns (NSMap *map, const char *pfx)
 {
-    NSEntry key;
-    strncpy (key.pfx, pfx, PFX_LEN);
-    NSEntry *entry = hashmap_get (map, &key);
+    NSEntry entry_s;
+    strncpy (entry_s.pfx, pfx, PFX_LEN);
+    NSEntry *entry = hashmap_get (map, &entry_s);
 
     return (entry) ? entry->ns : NULL;
 }
@@ -145,11 +126,14 @@ LSUP_nsmap_get_ns (NSMap *map, const ns_pfx pfx)
 const char *
 LSUP_nsmap_get_pfx (NSMap *map, const char *ns)
 {
-    struct cmp_iter_t cmp = {.iri = ns};
-
-    hashmap_scan (map, nsmap_ns_cmp_fn, &cmp);
+    const NSEntry *entry;
+    size_t i = 0;
+    while (hashmap_iter (map, &i, (void **) &entry)) {
+        if (strncmp (entry->ns, ns, strlen (ns)) == 0)
+            return entry->pfx;
+    }
 
-    return cmp.entry.ns;
+    return NULL;
 }
 
 
@@ -200,24 +184,32 @@ LSUP_nsmap_normalize_uri (
 
 LSUP_rc
 LSUP_nsmap_denormalize_uri (
-        const NSMap *map, const char *fq_uri, char **pfx_uri_p)
+        NSMap *map, const char *fq_uri, char **pfx_uri_p)
 {
+    /*
+     * This is different from LSUP_nsmap_get_ns, in that the URI being looked
+     * at will unlikely match exactly the full namespace stored in the map.
+     * This function has to count the characters left over from the match in
+     * order to add the URI suffix.
+     */
+    const NSEntry *entry;
+    const char *pfx = NULL;
+    size_t i = 0, offset;
+    while (hashmap_iter (map, &i, (void **) &entry)) {
+        offset = strlen(entry->ns);
+        if (strncmp (entry->ns, fq_uri, offset) == 0) {
+            pfx = entry->pfx;
+            break;
+        }
+    }
     char *pfx_uri = NULL;
 
-    struct cmp_iter_t cmp = {.iri=fq_uri};
-
-    hashmap_scan ((NSMap *) map, nsmap_ns_cmp_fn, &cmp);
-
-    if (cmp.entry.ns) {
-        pfx_uri = malloc (
-                strlen (cmp.entry.pfx)
-                + strlen (fq_uri) - strlen (cmp.entry.ns)
-                + 2); // one for terminating \x00, one for the colon.
+    if (pfx) {
+        // +2: one for terminating \x00, one for the colon.
+        pfx_uri = malloc (strlen (pfx) + strlen (fq_uri) - offset + 2);
         if (UNLIKELY (! (pfx_uri))) return LSUP_MEM_ERR;
 
-        sprintf (
-                pfx_uri, "%s:%s",
-                cmp.entry.pfx, fq_uri + strlen (cmp.entry.ns));
+        sprintf (pfx_uri, "%s:%s", pfx, fq_uri + offset);
 
     }
 

+ 101 - 156
src/store_htable.c

@@ -11,62 +11,27 @@ typedef bool (*LSUP_key_eq_fn_t)(
 
 
 typedef struct idx_entry_t {
-    LSUP_Key            key;        // Serialized term key.
-    LSUP_Buffer *       sterm;      // Serialized term.
+    LSUP_Key            key;        ///> Serialized term key.
+    LSUP_Buffer *       sterm;      ///> Serialized term.
 } IndexEntry;
 
 typedef struct ht_store_t {
-    struct hashmap *    keys;       // Triple keys (set).
-    struct hashmap *    idx;        // Dictionary of keys to serialized terms.
+    struct hashmap *    keys;       ///> Triple keys (set).
+    struct hashmap *    idx;        ///> Map of keys to serialized terms.
 } HTStore;
 
 typedef struct ht_iterator_t {
-    HTStore *           store;      // Store being iterated.
-    size_t              cur;        // Internal has table cursor.
-    size_t              ct;         // Number of records found at any point of
-                                    // a lookup iteration, or number of records
-                                    // added at any point of an add loop.
-    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.
-                                    // When the end of results is reached, this
-                                    // is set to LSUP_END.
-    LSUP_TripleKey *    entry;      // Retrieved SPO key.
+    HTStore *           store;      ///> Store being iterated.
+    size_t              cur;        ///> Internal hash table 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.
+                                    ///> When the end of results is reached,
+                                    ///> this is set to LSUP_END.
+    LSUP_TripleKey *    entry;      ///> Retrieved SPO key.
 } HTIterator;
 
 
-/** @brief Copy triple data.
- */
-static bool htstore_copy_key_iter_fn (const void *item, void *udata)
-{
-    LSUP_TripleKey *spok = (LSUP_TripleKey *)item;
-    HTStore *dest = udata;
-
-    hashmap_set (dest->keys, spok);
-    if (UNLIKELY (hashmap_oom (dest->keys))) return false;
-
-    return true;
-}
-
-
-/** @brief Copy index data.
- */
-static bool htstore_copy_idx_iter_fn (const void *item, void *udata)
-{
-    IndexEntry *entry = (IndexEntry *)item;
-    HTStore *dest = udata;
-
-    hashmap_set (dest->idx, entry);
-    if (UNLIKELY (hashmap_oom (dest->idx))) return false;
-
-    return true;
-}
-
-
-/** @brief Copy triples only if not 
- * */
-
-
 /**
  * Dummy callback for queries with all parameters unbound. Returns true.
 */
@@ -86,14 +51,14 @@ static bool lookup_sk_eq_fn (
  */
 static bool lookup_pk_eq_fn (
         const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[1] == luk[0]; }
+{ return spok[1] == luk[1]; }
 
 /**
  * Keyset lookup for O key.
  */
 static bool lookup_ok_eq_fn (
         const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[2] == luk[0]; }
+{ return spok[2] == luk[2]; }
 
 /**
  * Keyset lookup for S and P keys.
@@ -107,14 +72,14 @@ static bool lookup_spk_eq_fn (
  */
 static bool lookup_sok_eq_fn (
         const LSUP_Key spok[], const LSUP_Key luk[])
-{ return spok[0] == luk[0] && spok[2] == luk[1]; }
+{ return spok[0] == luk[0] && spok[2] == luk[2]; }
 
 /**
  * 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]; }
+{ return spok[1] == luk[1] && spok[2] == luk[2]; }
 
 /**
  * Keyset lookup for S, P and O keys.
@@ -124,6 +89,17 @@ static bool lookup_spok_eq_fn (
 { return spok[0] == luk[0] && spok[1] == luk[1] && spok[2] == luk[2]; }
 
 
+/* * * CALLBACKS * * */
+
+uint64_t trp_key_hash_fn (
+        const void *item, uint64_t seed0, uint64_t seed1)
+{ return (uint64_t) (LSUP_HASH (item, TRP_KLEN, seed0)); }
+
+
+int trp_key_cmp_fn (const void *a, const void *b, void *udata)
+{ return memcmp (a, b, TRP_KLEN); }
+
+
 /**
  * Hash callback function for index hashmap.
  */
@@ -136,11 +112,7 @@ static uint64_t htstore_idx_hash_fn (
  * Compare callback function for index hashmap.
  */
 static int htstore_idx_cmp_fn (const void *a, const void *b, void *udata)
-{
-    return LSUP_buffer_cmp (
-            ((const IndexEntry *) a)->sterm,
-            ((const IndexEntry *) b)->sterm);
-}
+{ return ((const IndexEntry *) a)->key - ((const IndexEntry *) b)->key; }
 
 
 /**
@@ -156,11 +128,17 @@ inline static LSUP_rc
 tkey_to_strp (
         const HTStore *store, const LSUP_Key *spok, LSUP_BufferTriple *sspo);
 
+
 static LSUP_rc
-htstore_add_key_iter (HTIterator *it, const LSUP_TripleKey spok);
+htstore_add_key_iter (HTIterator *it, const LSUP_Key *spok);
 
+
+/** @brief Advance iterator by next key.
+ *
+ * Result is stored in it->entry.
+ */
 static LSUP_rc
-htiter_next_key (HTIterator *it, LSUP_TripleKey *spok);
+htiter_next_key (HTIterator *it);
 
 
 /* * * API * * */
@@ -178,7 +156,7 @@ LSUP_htstore_new (const size_t size)
 
     ht->keys = hashmap_new (
             sizeof (LSUP_TripleKey), size, LSUP_HASH_SEED, 0,
-            key_hash_fn, key_cmp_fn, NULL,
+            trp_key_hash_fn, trp_key_cmp_fn, NULL,
             NULL);
 
     return ht;
@@ -203,10 +181,18 @@ LSUP_htstore_copy (const HTStore *src)
 LSUP_rc
 LSUP_htstore_copy_contents (HTStore *dest, const HTStore *src)
 {
-    if (hashmap_scan (src->keys, htstore_copy_key_iter_fn, dest) == false)
-        return LSUP_MEM_ERR;
-    if (hashmap_scan (src->idx, htstore_copy_idx_iter_fn, dest) == false)
-        return LSUP_MEM_ERR;
+    size_t i = 0;
+    LSUP_TripleKey *spok;
+    IndexEntry *entry;
+    while (hashmap_iter (src->keys, &i, (void **) &spok)) {
+        hashmap_set (dest->keys, spok);
+        if (UNLIKELY (hashmap_oom (dest->keys))) return LSUP_MEM_ERR;
+    }
+    i = 0;
+    while (hashmap_iter (src->idx, &i, (void **) &entry)) {
+        hashmap_set (dest->idx, entry);
+        if (UNLIKELY (hashmap_oom (dest->idx))) return LSUP_MEM_ERR;
+    }
 
     return LSUP_OK;
 }
@@ -236,7 +222,7 @@ LSUP_htstore_bool_op(
         return dest;
     }
 
-    LSUP_TripleKey src_tkey, dest_tkey;
+    LSUP_TripleKey *src_tkey;
 
     LSUP_HTIterator *it = LSUP_htstore_add_init(dest);
 
@@ -245,7 +231,7 @@ LSUP_htstore_bool_op(
         // Add triples from s2 if not found in s1.
         while (hashmap_iter (s2->keys, &i, (void **) &src_tkey)) {
             if (!hashmap_get (s1->keys, src_tkey))
-                htstore_add_key_iter (it, src_tkey);
+                htstore_add_key_iter (it, *src_tkey);
         }
     }
 
@@ -257,7 +243,7 @@ LSUP_htstore_bool_op(
             (op == LSUP_BOOL_INTERSECTION)
             ^ (hashmap_get (s2->keys, src_tkey) == NULL)
         )
-            htstore_add_key_iter (it, src_tkey);
+            htstore_add_key_iter (it, *src_tkey);
     }
     LSUP_htstore_add_done (it);
 
@@ -309,7 +295,6 @@ LSUP_htstore_add_init (HTStore *store)
     MALLOC_GUARD (it, NULL);
 
     it->store = store;
-    it->ct = 0;
 
     return it;
 }
@@ -349,60 +334,52 @@ LSUP_htstore_remove(
 {
     LSUP_rc rc = LSUP_NOACTION;
 
-    LSUP_HTIterator *it = LSUP_htstore_lookup (store, ss, sp, so);
+    LSUP_HTIterator *it = LSUP_htstore_lookup (store, ss, sp, so, ct);
     if (UNLIKELY (!it)) return LSUP_DB_ERR;
 
-    LSUP_TripleKey *tmp;
-    while (htiter_next_key (it, tmp)) {
+    while (htiter_next_key (it)) {
         if (it->rc == LSUP_OK) {
-            tmp = hashmap_delete (store->keys, tmp);
-            free (tmp);
+            LSUP_TripleKey *del = hashmap_delete (store->keys, it->entry);
+            free (del);
+            rc = LSUP_OK;
         }
     }
-    if (ct) *ct = it->ct;
 
     LSUP_htiter_free (it);
 
-    return LSUP_OK;
+    return rc;
 }
 
 
 HTIterator *
 LSUP_htstore_lookup (HTStore *store, const LSUP_Buffer *ss,
-        const LSUP_Buffer *sp, const LSUP_Buffer *so)
+        const LSUP_Buffer *sp, const LSUP_Buffer *so, size_t *ct)
 {
     HTIterator *it;
-    MALLOC_GUARD (it, NULL);
+    CALLOC_GUARD (it, NULL);
 
     it->store = store;
-    it->cur = 0;
-    it->ct = 0;
     it->rc = LSUP_END;
 
     if (hashmap_count (store->keys) == 0) return it;
 
-    LSUP_TripleKey spok = {
-        LSUP_buffer_hash (ss),
-        LSUP_buffer_hash (sp),
-        LSUP_buffer_hash (so),
-    };
+    if (ct) *ct = 0;
+
+    it->luk[0] = LSUP_buffer_hash (ss);
+    it->luk[1] = LSUP_buffer_hash (sp);
+    it->luk[2] = LSUP_buffer_hash (so);
 
     // s p o
-    if (spok[0] != NULL_KEY && spok[1] != NULL_KEY && spok[2] != NULL_KEY) {
-        memcpy (it->luk, spok, sizeof (LSUP_TripleKey));
+    if (ss && sp && so) {
         it->eq_fn = lookup_spok_eq_fn;
 
-    } else if (spok[0] != NULL_KEY) {
-        it->luk[0] = spok[0];
-
+    } else if (ss) {
         // s p ?
-        if (spok[1] != NULL_KEY) {
-            it->luk[1] = spok[1];
+        if (sp) {
             it->eq_fn = lookup_spk_eq_fn;
 
         // s ? o
-        } else if (spok[2] != NULL_KEY) {
-            it->luk[1] = spok[2];
+        } else if (so) {
             it->eq_fn = lookup_sok_eq_fn;
 
         // s ? ?
@@ -410,28 +387,33 @@ LSUP_htstore_lookup (HTStore *store, const LSUP_Buffer *ss,
             it->eq_fn = lookup_sk_eq_fn;
         }
 
-    } else if (spok[1] != NULL_KEY) {
-        it->luk[0] = spok[1];
-
+    } else if (sp) {
         // ? p o
-        if (spok[2] != NULL_KEY) {
-            it->luk[1] = spok[2];
+        if (so) {
             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];
+    } else if (so) {
         it->eq_fn = lookup_ok_eq_fn;
 
     // ? ? ?
     } else it->eq_fn = lookup_none_eq_fn;
 
-    // Position cursor at first record in hash table.
-    if (hashmap_iter (it->store->keys, &it->cur, (void **) it->entry))
-        it->rc = LSUP_OK;  // Else it's LSUP_END already.
+    log_trace ("LUK: {%lx, %lx, %lx}", it->luk[0], it->luk[1], it->luk[2]);
+
+    if (ct) {
+        // Loop over results to determine count.
+        while (htiter_next_key (it) == LSUP_OK) {
+            (*ct)++;
+            log_trace ("ct: %lu", *ct);
+        }
+        // Reposition cursor to the hashtable beginning.
+        it->cur = 0;
+        it->rc = LSUP_OK;
+    }
 
     return it;
 }
@@ -441,62 +423,28 @@ void
 LSUP_htiter_free (LSUP_HTIterator *it)
 { free (it); }
 
-
-size_t
-LSUP_htiter_count (const LSUP_HTIterator *it)
-{ return it->cur; }
-
-
 LSUP_rc
-htiter_next_key (HTIterator *it, LSUP_TripleKey *spok)
+htiter_next_key (HTIterator *it)
 {
     if (UNLIKELY (!it)) return LSUP_VALUE_ERR;
 
-    // If the previous iteration hit the end, return.
-    if (it->rc != LSUP_OK) return it->rc;
-
     // This value is for internal looping only. It shall never be returned.
     it->rc = LSUP_NORESULT;
-    /*
-#ifdef DEBUG
-    log_trace ("Lookup key: ");
-    IndexEntry *tmp = NULL;
-    HASH_FIND (hh, it->store->idx, it->luk + 0, KLEN, tmp);
-    if (tmp) LSUP_buffer_print(tmp->sterm); else printf("NULL\n");
-    HASH_FIND (hh, it->store->idx, it->luk + 1, KLEN, tmp);
-    if (tmp) LSUP_buffer_print(tmp->sterm); else printf("NULL\n");
-    HASH_FIND (hh, it->store->idx, it->luk + 2, KLEN, tmp);
-    if (tmp) LSUP_buffer_print(tmp->sterm); else printf("NULL\n");
-#endif
-    */
-
-    // Iteration resumes from lookup where first hashmap_iter was called.
-    do {
-        if (!it->entry) return LSUP_END;
 
+    do {
         // Loop through all triples until a match is found, or end is reached.
-        if (it->eq_fn (*it->entry, it->luk)) {
-            log_trace (
-                "Found spok: {%lx, %lx, %lx}",
-                it->entry[0], it->entry[1], it->entry[2]
-            );
-            /*
-#ifdef DEBUG
-            IndexEntry *tmp = NULL;
-            HASH_FIND (hh, it->store->idx, it->entry->key + 0, KLEN, tmp);
-            LSUP_buffer_print(tmp->sterm);
-            HASH_FIND (hh, it->store->idx, it->entry->key + 1, KLEN, tmp);
-            LSUP_buffer_print(tmp->sterm);
-            HASH_FIND (hh, it->store->idx, it->entry->key + 2, KLEN, tmp);
-            LSUP_buffer_print(tmp->sterm);
-#endif
-            */
-
-            it->rc = LSUP_OK;
-            it->ct++;
+        if (hashmap_iter (it->store->keys, &it->cur, (void **) &it->entry)) {
+            //log_trace("it->cur: %lu", it->cur);
+            if (it->eq_fn (*it->entry, it->luk)) {
+                log_trace (
+                    "Found spok: {%lx, %lx, %lx}",
+                    it->entry[0][0], it->entry[0][1], it->entry[0][2]
+                );
+
+                it->rc = LSUP_OK;
+            }
         }
-        if (hashmap_iter (it->store->keys, &it->cur, (void **) it->entry))
-            it->rc = LSUP_OK;
+        else it->rc = LSUP_END;
 
     } while (it->rc == LSUP_NORESULT);
 
@@ -507,8 +455,7 @@ htiter_next_key (HTIterator *it, LSUP_TripleKey *spok)
 LSUP_rc
 LSUP_htiter_next (HTIterator *it, LSUP_BufferTriple *sspo)
 {
-    LSUP_TripleKey *cur;
-    LSUP_rc rc = htiter_next_key (it, cur);
+    LSUP_rc rc = htiter_next_key (it);
     if (rc != LSUP_OK) return rc;
 
     return tkey_to_strp (it->store, *it->entry, sspo);
@@ -541,22 +488,20 @@ tkey_to_strp (
 
 
 static LSUP_rc
-htstore_add_key_iter (HTIterator *it, const LSUP_TripleKey spok)
+htstore_add_key_iter (HTIterator *it, const LSUP_Key *spok)
 {
     // Add triple.
     log_trace ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
 
-    if (hashmap_get (it->store->keys, &spok)) {
+    if (hashmap_get (it->store->keys, spok)) {
         log_trace ("Triple found. Not adding.");
         return LSUP_NOACTION;
     }
 
     log_trace ("Triple not found, inserting.");
 
-    hashmap_set (it->store->keys, &spok);
+    hashmap_set (it->store->keys, (void *)spok);
     if (hashmap_oom(it->store->keys)) return LSUP_MEM_ERR;
 
-    it->ct++;
-
     return LSUP_OK;
 }

+ 87 - 107
src/store_mdb.c

@@ -1,7 +1,5 @@
 #include <ftw.h>
 
-#include "hashmap.h"
-
 #include "store_mdb.h"
 #include "data/bootstrap.h"
 
@@ -65,45 +63,27 @@ typedef struct mdbstore_t {
 typedef void (*iter_op_fn_t)(LSUP_MDBIterator *it);
 
 
-/** @brief Triple iterator.
- */
+/// Triple iterator.
 typedef struct mdbstore_iter_t {
-    MDBStore *          store;      // MDB store pointer.
-    MDB_txn *           txn;        // MDB transaction.
-    MDB_cursor *        cur;        // MDB cursor.
-    MDB_cursor *        ctx_cur;    // MDB c:spo index cursor.
-    MDB_val             key, data;  // Internal data handlers.
-    LSUP_TripleKey      spok;       // Triple to be populated with match.
-    LSUP_Key            ck;         // Ctx key to filter by. May be NULL_TRP.
-    iter_op_fn_t        iter_op_fn; // Function used to look up next match.
-    const uint8_t *     term_order; // Term order used in 1-2bound look-ups.
-    LSUP_Key            luk[3];     // 0÷3 lookup keys.
-    size_t              i;          // Internal counter for paged lookups.
-    int                 rc;         // MDB_* return code for the next result.
+    MDBStore *          store;      ///< MDB store handle.
+    MDB_txn *           txn;        ///< MDB transaction.
+    MDB_cursor *        cur;        ///< MDB cursor.
+    MDB_cursor *        ctx_cur;    ///< MDB c:spo index cursor.
+    MDB_val             key, data;  ///< Internal data handlers.
+    LSUP_TripleKey      spok;       ///< Triple to be populated with match.
+    LSUP_Key *          ck;         ///< Context array to be populated for each
+                                    ///< matching triple if requested.
+    iter_op_fn_t        iter_op_fn; ///< Function used to look up next match.
+    const uint8_t *     term_order; ///< Term order used in 1-2bound look-ups.
+    LSUP_Key            luk[3];     ///< 0÷3 lookup keys.
+    LSUP_Key            luc;        ///< Ctx key to filter by. May be NULL_KEY.
+    size_t              i;          ///< Internal counter for paged lookups.
+    size_t              ct;         ///< Current count of records inserted or
+                                    ///< results found.
+    int                 rc;         ///< MDB_* return code for the next result.
 } MDBIterator;
 
 
-/*
-// Set of single keys.
-typedef struct key_set_t {
-    LSUP_Key            key;
-    UT_hash_handle      hh;
-} KeySet;
-
-// Set of triple keys.
-typedef struct triple_set_t {
-    LSUP_TripleKey      spok;
-    UT_hash_handle      hh;
-} TripleSet;
-
-typedef struct ctx_triple_map_t {
-    LSUP_Key            ck;
-    struct hashmap *    spok;
-} CtxTripleMap;
-*/
-// Set of single keys.
-typedef struct hashmap KeySet;
-
 /*
  * Static variables.
  */
@@ -409,16 +389,16 @@ LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc)
 
     // Take care of context first.
     // Serialize and hash.
-    it->ck = NULL_KEY;
+    it->luc = NULL_KEY;
 
     if (store->default_ctx != NULL) {
         if (sc == NULL) sc = store->default_ctx;
 
-        it->ck = LSUP_buffer_hash (sc);
+        it->luc = LSUP_buffer_hash (sc);
 
         // Insert t:st for context.
         //log_debug ("Adding context: %s", sc);
-        it->key.mv_data = &it->ck;
+        it->key.mv_data = &it->luc;
         it->key.mv_size = KLEN;
         it->data.mv_data = sc->addr;
         it->data.mv_size = sc->size;
@@ -462,15 +442,15 @@ LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_BufferTriple *sspo)
     }
 
     log_trace ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
-    log_trace ("Into context: %lx", it->ck);
+    log_trace ("Into context: %lx", it->luc);
 
     // Insert spo:c.
     it->key.mv_data = spok;
     it->key.mv_size = TRP_KLEN;
 
     // In triple mode, data is empty (= NULL_KEY).
-    it->data.mv_data = &it->ck;
-    it->data.mv_size = it->ck == NULL_KEY ? 0 : KLEN;
+    it->data.mv_data = &it->luc;
+    it->data.mv_size = it->luc == NULL_KEY ? 0 : KLEN;
 
     db_rc = mdb_put(
             it->store->txn, it->store->dbi[IDX_SPO_C],
@@ -484,7 +464,7 @@ LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_BufferTriple *sspo)
     }
 
     // Index.
-    LSUP_rc rc = index_triple (it->store, OP_ADD, spok, it->ck);
+    LSUP_rc rc = index_triple (it->store, OP_ADD, spok, it->luc);
     if (rc == LSUP_OK) it->i++;
 
     return rc;
@@ -565,15 +545,13 @@ key_to_sterm (LSUP_MDBIterator *it, const LSUP_Key key, LSUP_Buffer *sterm)
     db_rc = mdb_get (it->txn, it->store->dbi[IDX_T_ST], &key_v, &data_v);
 
     if (db_rc == MDB_SUCCESS) {
-        free (sterm->addr);
         sterm->addr = data_v.mv_data;
         sterm->size = data_v.mv_size;
         rc = LSUP_OK;
     } else if (db_rc == MDB_NOTFOUND) {
-        free (sterm->addr);
         sterm->addr = NULL;
         sterm->size = 0;
-    } else rc = LSUP_ERROR;
+    } else rc = LSUP_DB_ERR;
 
     return rc;
 }
@@ -594,8 +572,8 @@ LSUP_mdbstore_lookup(
     CALLOC_GUARD (it, NULL);
 
     it->store = store;
-    it->ck = store->default_ctx ? LSUP_buffer_hash (sc) : NULL_KEY;
-    log_debug ("Lookup context: %lx", it->ck);
+    it->luc = store->default_ctx ? LSUP_buffer_hash (sc) : NULL_KEY;
+    log_debug ("Lookup context: %lx", it->luc);
 
     if (ct) *ct = 0;
 
@@ -677,7 +655,7 @@ LSUP_mdbstore_lookup(
  * in, if not NULL.
  */
 inline static LSUP_rc
-mdbiter_next_key (LSUP_MDBIterator *it, KeySet *ckset)
+mdbiter_next_key (LSUP_MDBIterator *it)
 {
     if (UNLIKELY (!it)) return LSUP_VALUE_ERR;
 
@@ -703,10 +681,10 @@ mdbiter_next_key (LSUP_MDBIterator *it, KeySet *ckset)
     int db_rc;
 
     key.mv_size = TRP_KLEN;
-    data.mv_data = &it->ck;
+    data.mv_data = &it->luc;
     data.mv_size = KLEN;
 
-    if (it->ck) {
+    if (it->luc) {
         rc = LSUP_NORESULT;  // Intermediary value, will never be returned.
 
         while (rc == LSUP_NORESULT) {
@@ -738,24 +716,30 @@ mdbiter_next_key (LSUP_MDBIterator *it, KeySet *ckset)
 
     } else rc = LSUP_OK;
 
-    // Get all contexts for a triple if requested. Add up to previous
-    // iterations if the same pointer is passed.
-    if (ckset) {
-        key.mv_data = it->spok;
-        db_rc = mdb_cursor_get (it->ctx_cur, &key, &data, MDB_SET_KEY);
-        if (db_rc == MDB_SUCCESS) {
-            do {
-                LSUP_Key entry;
-                if (!hashmap_get (ckset, data.mv_data)) {
-                    entry = *(LSUP_Key *) data.mv_data;
-                    hashmap_set (ckset, &entry);
-                }
-            } while (
-                    mdb_cursor_get (it->ctx_cur, &key, &data, MDB_NEXT_DUP)
-                    == MDB_SUCCESS);
-        }
+    // Get all contexts for a triple.
+    key.mv_data = it->spok;
+    db_rc = mdb_cursor_get (it->ctx_cur, &key, &data, MDB_SET_KEY);
+    if (db_rc != MDB_SUCCESS) {
+        log_error ("No context found for triple!");
+        return LSUP_DB_ERR;
     }
 
+    size_t ct;
+    db_rc = mdb_cursor_count (it->ctx_cur, &ct);
+    if (db_rc != MDB_SUCCESS) return LSUP_DB_ERR;
+    // 1 spare for sentinel. Always allocated even on zero matches.
+    it->ck = malloc (sizeof (*it->ck) * (ct + 1));
+    //log_trace("Slots allocated @%p: %lu of size %lu", it->ck, ct + 1, sizeof (*it->ck) * (ct + 1));
+    size_t i = 0;
+    do {
+        //log_trace("Copying to slot #%lu @%p", i, it->ck + i);
+        memcpy (it->ck + i++, data.mv_data, sizeof (*it->ck));
+    } while (
+            mdb_cursor_get (it->ctx_cur, &key, &data, MDB_NEXT_DUP)
+            == MDB_SUCCESS);
+    //log_trace ("setting sentinel @%p", it->ck + i);
+    it->ck[i] = NULL_KEY;
+
     return rc;
 }
 
@@ -764,16 +748,7 @@ LSUP_rc
 LSUP_mdbiter_next (
         LSUP_MDBIterator *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx_p)
 {
-    LSUP_rc rc;
-
-    KeySet *ckset = NULL;
-    if (ctx_p) {
-        KeySet *ckset = hashmap_new (
-                sizeof (LSUP_Key), 0, LSUP_HASH_SEED, 0,
-                key_hash_fn, key_cmp_fn, NULL, NULL);
-        if (UNLIKELY (hashmap_oom (ckset))) return LSUP_MEM_ERR;
-    }
-    rc = mdbiter_next_key (it, ckset);
+    LSUP_rc rc = mdbiter_next_key (it);
 
     if (rc == LSUP_OK) {
         if (sspo) {
@@ -784,29 +759,31 @@ LSUP_mdbiter_next (
             // TODO error handling.
         }
 
-        // One-shot contexts for current triple.
+        // Contexts for current triple.
         if (ctx_p) {
-            LSUP_Key *ck;
-            // 1 extra slot for sentinel.
-            LSUP_Buffer *ctx = calloc (
-                    (hashmap_count (ckset) + 1), sizeof (*ctx));
+            // Preallocate.
+            size_t i = 0;
+            while (it->ck[i++]); // Include sentinel in count.
+            LSUP_Buffer *ctx;
+            log_trace("Allocating %lu context buffers.", i);
+            ctx = malloc(i * sizeof (*ctx));
             if (!ctx) return LSUP_MEM_ERR;
 
-            size_t i = 0;
-            while (hashmap_iter (ckset, &i, (void **) &ck))
-                key_to_sterm (it, *ck, ctx + i);
+            for (i = 0; it->ck[i]; i++)
+                key_to_sterm (it, it->ck[i], ctx + i);
+            memset (ctx + i, 0, sizeof (*ctx)); // Sentinel
 
             // TODO error handling.
+            *ctx_p = ctx;
         }
     }
-    if (ckset) hashmap_free (ckset);
 
     return rc;
 }
 
 
 size_t
-LSUP_mdbiter_cur (LSUP_MDBIterator *it)
+LSUP_mdbiter_count (LSUP_MDBIterator *it)
 { return it->i; }
 
 
@@ -855,7 +832,7 @@ LSUP_mdbstore_remove(
     if (UNLIKELY (!it)) return LSUP_DB_ERR;
     if (ct) log_debug ("Found %lu triples to remove.", *ct);
 
-    while (mdbiter_next_key (it, NULL) == LSUP_OK) {
+    while (mdbiter_next_key (it) == LSUP_OK) {
         spok_v.mv_data = it->spok;
 
         db_rc = mdb_cursor_get (dcur, &spok_v, &ck_v, MDB_GET_BOTH);
@@ -914,6 +891,7 @@ fail:
 }
 
 
+/* TODO remove deprecated function.
 LSUP_Buffer **
 LSUP_mdbstore_lookup_contexts (
         LSUP_MDBStore *store, const LSUP_Buffer *ss, const LSUP_Buffer *sp,
@@ -923,9 +901,9 @@ LSUP_mdbstore_lookup_contexts (
             store, ss, sp, so, NULL, NULL);
 
     LSUP_rc rc = LSUP_NORESULT;
-    KeySet *ckey, *ckeys = NULL, *tmp;
+    KeySet *ckey, *tmp;
     while (rc != LSUP_END)
-        rc = mdbiter_next_key (it, &ckeys);
+        rc = mdbiter_next_key (it, ckeys);
 
     size_t i = 0;
     LSUP_Buffer **ctx_a = calloc (HASH_COUNT (ckeys) + 1, sizeof (*ctx_a));
@@ -941,6 +919,7 @@ LSUP_mdbstore_lookup_contexts (
 
     return ctx_a;
 }
+*/
 
 
 LSUP_rc
@@ -1389,12 +1368,12 @@ lookup_0bound (MDBIterator *it, size_t *ct)
     log_debug ("Looking up 0 bound terms.");
 
     if (ct) {
-        if (it->ck != NULL_KEY) {
+        if (it->luc != NULL_KEY) {
             // Look up by given context.
             it->rc = mdb_cursor_open (
                     it->txn, it->store->dbi[IDX_C_SPO], &it->cur);
 
-            it->key.mv_data = &it->ck;
+            it->key.mv_data = &it->luc;
             it->key.mv_size = KLEN;
 
             it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_SET);
@@ -1451,24 +1430,27 @@ lookup_1bound (uint8_t idx0, MDBIterator *it, size_t *ct)
     if (ct) {
         // If a context is specified, the only way to count triples matching
         // the context is to loop over them.
-        if (it->ck != NULL_KEY) {
-            log_debug ("Counting in context: %lx", it->ck);
-            MDBIterator *ct_it = NULL;
+        if (it->luc != NULL_KEY) {
+            log_debug ("Counting in context: %lx", it->luc);
+            MDBIterator *ct_it;
             MALLOC_GUARD (ct_it, LSUP_MEM_ERR);
+            /*
+            memcpy (ct_it, it, sizeof (*ct_it));
+            */
 
-            ct_it->luk[0] = it->luk[0];
-            ct_it->ck = it->ck;
             ct_it->store = it->store;
             ct_it->txn = it->txn;
+            ct_it->ctx_cur = it->ctx_cur;
             ct_it->key = it->key;
             ct_it->data = it->data;
+            ct_it->luk[0] = it->luk[0];
+            ct_it->luc = it->luc;
             ct_it->i = 0;
-            ct_it->ctx_cur = it->ctx_cur;
 
             LSUP_rc rc = lookup_1bound (idx0, ct_it, NULL);
             if (rc < 0) return rc;
 
-            while (LSUP_mdbiter_next (ct_it, NULL, NULL) != LSUP_END) {
+            while (mdbiter_next_key (ct_it) != LSUP_END) {
                 (*ct)++;
                 log_trace ("Counter increased to %lu.", *ct);
             }
@@ -1554,21 +1536,19 @@ lookup_2bound(uint8_t idx0, uint8_t idx1, MDBIterator *it, size_t *ct)
     if (ct) {
         // If a context is specified, the only way to count triples matching
         // the context is to loop over them.
-        if (it->ck != NULL_KEY) {
+        if (it->luc != NULL_KEY) {
             MDBIterator *ct_it;
             MALLOC_GUARD (ct_it, LSUP_MEM_ERR);
 
             ct_it->luk[0] = it->luk[0];
             ct_it->luk[1] = it->luk[1];
-            ct_it->ck = it->ck;
+            ct_it->luc = it->luc;
             ct_it->store = it->store;
             ct_it->txn = it->txn;
             ct_it->ctx_cur = it->ctx_cur;
             lookup_2bound (idx0, idx1, ct_it, NULL);
 
-            while (LSUP_mdbiter_next (ct_it, NULL, NULL) != LSUP_END) {
-                ct[0] ++;
-            }
+            while (mdbiter_next_key (ct_it) != LSUP_END) (*ct) ++;
 
             // Free the counter iterator without freeing the shared txn.
             if (ct_it->cur) mdb_cursor_close (ct_it->cur);
@@ -1605,12 +1585,12 @@ lookup_3bound (MDBIterator *it, size_t *ct)
 
     it->key.mv_data = it->luk;
 
-    if (it->ck != NULL_KEY) {
+    if (it->luc != NULL_KEY) {
         it->rc = mdb_cursor_open (
                 it->txn, it->store->dbi[IDX_SPO_C], &it->cur);
 
         it->key.mv_size = TRP_KLEN;
-        it->data.mv_data = &it->ck;
+        it->data.mv_data = &it->luc;
         it->data.mv_size = KLEN;
 
     } else {

+ 13 - 5
test/test_namespace.c

@@ -11,16 +11,24 @@ test_namespace()
             LSUP_nsmap_add (nsm, "dc", "http://purl.org/dc/elements/1.1/"));
     EXPECT_PASS (LSUP_nsmap_add (nsm, "dcterms", "http://purl.org/dc/terms/"));
 
+    // Look up prefix.
     EXPECT_STR_EQ (
-            LSUP_nsmap_get (nsm, "dc"), "http://purl.org/dc/elements/1.1/");
+            LSUP_nsmap_get_ns (nsm, "dc"), "http://purl.org/dc/elements/1.1/");
     EXPECT_STR_EQ (
-            LSUP_nsmap_get (nsm, "dcterms"), "http://purl.org/dc/terms/");
+            LSUP_nsmap_get_ns (nsm, "dcterms"), "http://purl.org/dc/terms/");
     // Prefixes longer than 7 chars are truncated.
     ASSERT (
-            LSUP_nsmap_get (nsm, "dctermsxxx") == NULL,
+            LSUP_nsmap_get_ns (nsm, "dctermsxxx") == NULL,
             "Non-existent NS found!");
-    ASSERT (LSUP_nsmap_get (nsm, "none") == NULL, "Non-existent NS found!");
+    ASSERT (LSUP_nsmap_get_ns (nsm, "none") == NULL, "Non-existent NS found!");
 
+    // Lookup NS.
+    EXPECT_STR_EQ (
+            LSUP_nsmap_get_pfx (nsm, "http://purl.org/dc/elements/1.1/"), "dc");
+    EXPECT_STR_EQ (
+            LSUP_nsmap_get_pfx (nsm, "http://purl.org/dc/terms/"), "dcterms");
+
+    // Normallize and denormalize URIs.
     char *fq_uri, *pfx_uri;
     fq_uri = "http://purl.org/dc/elements/1.1/title";
 
@@ -35,7 +43,7 @@ test_namespace()
     ASSERT (
             LSUP_nsmap_remove (nsm, "none") == LSUP_NOACTION,
             "Wrong result for removal of non-existent prefix!");
-    ASSERT (LSUP_nsmap_get (nsm, "dc") == NULL, "Deleted NS found!");
+    ASSERT (LSUP_nsmap_get_ns (nsm, "dc") == NULL, "Deleted NS found!");
 
     LSUP_nsmap_free (nsm);
     free (fq_uri);

+ 7 - 9
test/test_store_ht.c

@@ -6,7 +6,7 @@
  */
 static int test_htstore()
 {
-    LSUP_HTStore *store = LSUP_htstore_new();
+    LSUP_HTStore *store = LSUP_htstore_new(0);
     ASSERT (store != NULL, "Error initializing store!");
 
     LSUP_Triple *trp = create_triples();
@@ -18,13 +18,13 @@ static int test_htstore()
     // Test adding.
     LSUP_HTIterator *it = LSUP_htstore_add_init (store);
     for (size_t i = 0; i < NUM_TRP; i++) {
+        //printf("Insert #%lu\n", i);
         LSUP_rc rc = LSUP_htstore_add_iter (it, ser_trp[i]);
         ASSERT (
                 (i < 8 && rc == LSUP_OK) || rc == LSUP_NOACTION,
                 "Wrong return code on insert!");
     }
 
-    EXPECT_INT_EQ (LSUP_htiter_cur (it), 8);
     EXPECT_INT_EQ (LSUP_htstore_size (store), 8);
 
     LSUP_htstore_add_done (it);
@@ -57,17 +57,15 @@ static int test_htstore()
 
     LSUP_BufferTriple *sspo = STRP_DUMMY;
     for (int i = 0; i < NUM_TRP; i++) {
-        size_t ct = 0;
+        size_t ct, ct2 = 0;
         log_info ("Testing triple lookup #%d.", i);
 
         LSUP_HTIterator *it = LSUP_htstore_lookup(
-                store, lut[i][0], lut[i][1], lut[i][2]);
-        while (LSUP_htiter_next (it, sspo) != LSUP_END) {
-            // Verify that the internal counter aligns with an external one.
-            ct ++;
-            EXPECT_INT_EQ (ct, LSUP_htiter_cur(it));
-        }
+                store, lut[i][0], lut[i][1], lut[i][2], &ct);
         EXPECT_INT_EQ (ct, results[i]);
+        // Verify that the internal counter aligns with an external one.
+        while (LSUP_htiter_next (it, sspo) != LSUP_END) ct2++;
+        EXPECT_INT_EQ (ct, ct2);
 
         LSUP_htiter_free (it);
     }

+ 16 - 10
test/test_store_mdb.c

@@ -260,10 +260,13 @@ static int test_quad_store()
     };
 
     int ctx_ct[10] = {
-        2,
-        2, 1, 0, 1,
-        2, 1, 2,
-        2, 0,
+        // BEGIN ctx1 (triples 0÷5)
+        1, 1, 1, 1,
+        // BEGIN ctx2 (triples 4÷9)
+        2, 2,
+        // END ctx 1
+        1, 1, 1, 1,
+        // END ctx 2
     };
 
     for (int i = 0; i < 41; i++) {
@@ -280,14 +283,17 @@ static int test_quad_store()
 
     // Check triple contexts.
     for (int i = 0; i < 10; i++) {
+        LSUP_MDBIterator *it = LSUP_mdbstore_lookup (
+                store, ser_trp[i].s, ser_trp[i].p, ser_trp[i].o, NULL, NULL);
         log_info ("Checking contexts for triple %d.", i);
-        LSUP_Buffer **ctx_a = LSUP_mdbstore_lookup_contexts (
-                store, lut[i][0], lut[i][1], lut[i][2]);
-        ASSERT (ctx_a != NULL, "Value is NULL!");
+        LSUP_Buffer *ctx_a;
+        EXPECT_PASS (LSUP_mdbiter_next (it, NULL, &ctx_a));
+        LSUP_mdbiter_free (it);
+
+        ASSERT (ctx_a != NULL, "No contexts found!");
 
-        int j = 0;
-        while (ctx_a[j] != NULL)
-            free (ctx_a[j++]); // Buffer data are memory-mapped. Not freeing.
+        size_t j = 0;
+        while (ctx_a[j].addr) j++;
         free (ctx_a);
 
         EXPECT_INT_EQ (j, ctx_ct[i]);