Kaynağa Gözat

Major memory error cleanup.

Stefano Cossu 3 yıl önce
ebeveyn
işleme
7652c9d3ad
18 değiştirilmiş dosya ile 439 ekleme ve 212 silme
  1. 13 1
      include/codec_base.h
  2. 18 1
      include/core.h
  3. 1 1
      include/graph.h
  4. 18 2
      include/triple.h
  5. 4 1
      profile.c
  6. 66 31
      src/codec_nt.c
  7. 47 29
      src/graph.c
  8. 8 10
      src/namespace.c
  9. 52 35
      src/store_htable.c
  10. 24 13
      src/store_mdb.c
  11. 7 8
      src/term.c
  12. 10 0
      src/triple.c
  13. 6 3
      test.c
  14. 105 50
      test/test_codec_nt.c
  15. 36 3
      test/test_graph.c
  16. 2 0
      test/test_namespace.c
  17. 20 24
      test/test_store_ht.c
  18. 2 0
      test/test_store_mdb.c

+ 13 - 1
include/codec_base.h

@@ -4,7 +4,17 @@
 #include "graph.h"
 
 
-typedef struct codec_iter_t LSUP_CodecIterator;
+typedef struct codec_iter_t {
+    char *              rep;        // String representation of a RDF fragment.
+    LSUP_Triple *       trp;        // RDF fragment being encoded or decoded.
+    LSUP_GraphIterator *gr_it;      // Graph iterator.
+    LSUP_NSMap *        nsm;        // Namespace map.
+    size_t              cur;        // Internal cursor.
+    LSUP_rc             rc;         // Internal return code.
+    char *              str_s;      // Temporary string.
+    char *              str_p;      // Temporary string.
+    char *              str_o;      // Temporary string.
+} LSUP_CodecIterator;
 
 
 /** @brief Term encoder callback type.
@@ -45,6 +55,8 @@ typedef void (*gr_codec_done_fn_t)(LSUP_CodecIterator *it);
 typedef LSUP_rc (*gr_dec_fn_t)(const char *rep, LSUP_Graph **gr);
 
 
+
+
 /** @brief Codec structure.
  *
  * An instance of this structure is usually defined at compile time (see

+ 18 - 1
include/core.h

@@ -19,7 +19,7 @@
 #else
 #define DEBUG_TEST 0
 #endif
-#define STR "%s\n"
+#define STR "%s"
 #define TRACE(fmt, ...) \
         do {\
             if (DEBUG_TEST) \
@@ -117,4 +117,21 @@ rm_r (const char *path);
 // Return NULL if it is of LSUP_rc type and is negative (=error)
 #define RCNL(exp) if((exp) < 0) return NULL
 
+#define MALLOC_GUARD(var, rc) do {                                  \
+    (var) = malloc (sizeof *(var));                                 \
+    if (UNLIKELY (var == NULL)) return (rc);                        \
+} while (0);
+
+#define CALLOC_GUARD(var, rc) do {                                  \
+    (var) = calloc (1, sizeof *(var));                              \
+    if (UNLIKELY (var == NULL)) return (rc);                        \
+} while (0);
+
+/*
+#define MALLOC_GUARD_ME(var) MALLOC_GUARD((var), LSUP_MEM_ERR)      \
+#define CALLOC_GUARD_ME(var) CALLOC_GUARD((var), LSUP_MEM_ERR)      \
+#define MALLOC_GUARD_NL(var) MALLOC_GUARD((var), NULL)              \
+#define CALLOC_GUARD_NL(var) CALLOC_GUARD((var), NULL)              \
+*/
+
 #endif

+ 1 - 1
include/graph.h

@@ -131,7 +131,7 @@ LSUP_graph_set_uri (LSUP_Graph *gr, const char *uri);
  * @return Namespace handler for in-memory graphs, NULL for MDB graphs.
  */
 LSUP_NSMap *
-LSUP_graph_namespace (LSUP_Graph *gr);
+LSUP_graph_namespace (const LSUP_Graph *gr);
 
 
 /** @brief Set the namespace map for an in-memory graph.

+ 18 - 2
include/triple.h

@@ -25,13 +25,13 @@ typedef enum {
 LSUP_Triple *
 LSUP_triple_new(LSUP_Term *s, LSUP_Term *p, LSUP_Term *o);
 
-#define TRP_DUMMY LSUP_triple_new (TERM_DUMMY, TERM_DUMMY, TERM_DUMMY)
+#define TRP_DUMMY LSUP_triple_new (NULL, NULL, NULL)
 
 
 LSUP_SerTriple *
 LSUP_striple_new(LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o);
 
-#define STRP_DUMMY LSUP_striple_new (BUF_DUMMY, BUF_DUMMY, BUF_DUMMY)
+#define STRP_DUMMY LSUP_striple_new (NULL, NULL, NULL)
 
 
 LSUP_Triple *
@@ -109,6 +109,9 @@ LSUP_striple_done (LSUP_SerTriple *sspo);
 
 
 /** @brief Free a triple and all its internal pointers.
+ *
+ * NOTE: If the term pointers are not to be freed (e.g. they are owned by a
+ * back end), use a simple free(spo) instead of this.
  *
  * @param spo[in] Triple to be freed.
  */
@@ -117,6 +120,9 @@ LSUP_triple_free (LSUP_Triple *spo);
 
 
 /** @brief Free a buffer triple and all its internal pointers.
+ *
+ * NOTE: If the buffer pointers are not to be freed (e.g. they are owned by a
+ * back end), use a simple free(sspo) instead of this.
  *
  * @param sspo[in] Buffer triple to be freed.
  */
@@ -124,6 +130,16 @@ void
 LSUP_striple_free (LSUP_SerTriple *sspo);
 
 
+/** @brief Free a buffer triple and its buffer handles but not the buffer data.
+ *
+ * This is useful when freeing a "dummy" triple whose buffers are owned by the
+ * caller but the data the terms point to are owned by the store.
+ *
+ */
+void
+LSUP_striple_free_shallow (LSUP_SerTriple *sspo);
+
+
 #define _FN_BODY \
     if (n == TRP_POS_S) return trp->s; \
     if (n == TRP_POS_P) return trp->p; \

+ 4 - 1
profile.c

@@ -9,7 +9,9 @@ static LSUP_Triple *
 generate_triples()
 {
     LSUP_Triple *trp;
-    CRITICAL(trp = malloc(NT * sizeof(LSUP_Triple)));
+    trp = malloc(NT * sizeof(LSUP_Triple));
+    if (!trp) exit (-1);
+
     char sstr[32], pstr[32], ostr[32];
 
     for (size_t i = 0; i < NT; i++) {
@@ -51,6 +53,7 @@ int main()
     clock_t start, tc1, tc2;
     double wallclock, rate;
 
+    printf ("Generating triples.\n");
     start = clock();
     LSUP_Triple *trp = generate_triples();
     tc1 = clock();

+ 66 - 31
src/codec_nt.c

@@ -17,15 +17,6 @@
 #define XSD_STRING "http://www.w3.org/2001/XMLSchema#string"
 
 
-typedef struct codec_iter_t {
-    char *              rep;        // String representation of a RDF fragment.
-    LSUP_Triple *       trp;        // RDF fragment being encoded or decoded.
-    LSUP_NSMap *        nsm;        // Namespace map.
-    size_t              cur;        // Internal cursor.
-    LSUP_rc             rc;         // Internal return code.
-} CodecIterator;
-
-
 /* * * Static prototypes. * * */
 
 static LSUP_rc escape_lit (const char *in, char **out_p);
@@ -37,32 +28,40 @@ static LSUP_rc
 term_to_nt (const LSUP_Term *term, const LSUP_NSMap *nsm, char **out_p)
 {
     LSUP_rc rc;
-    char *out = NULL;
+    char *out = NULL, *tmp, *escaped;
     size_t buf_len;
 
+    // Free previous content if not NULL.
+    if (*out_p != NULL) out = realloc (*out_p, 0);
+
     switch (term->type) {
         case LSUP_TERM_URI:
-            out = malloc (strlen (term->data) + 3);
-            if (UNLIKELY (!out)) return LSUP_MEM_ERR;
+            tmp = realloc (out, strlen (term->data) + 3);
+            if (UNLIKELY (!tmp)) return LSUP_MEM_ERR;
+            out = tmp;
 
             sprintf (out, "<%s>", term->data);
             rc = LSUP_OK;
             break;
 
         case LSUP_TERM_LITERAL:
-            buf_len = strlen (term->data) + 3; // Room for ""
+            // Calculate string length.
+            if (escape_lit (term->data, &escaped) != LSUP_OK)
+                return LSUP_ERROR;
+            buf_len = strlen (escaped) + 3; // Room for "" and terminator
 
             if (term->datatype && strcmp (term->datatype, XSD_STRING) != 0)
                 buf_len += strlen (term->datatype) + 2; // Room for ^^
 
-            if (strlen (term->lang) > 0) buf_len += strlen(term->lang) + 1; //@
+            if (strlen (term->lang) > 0)
+                buf_len += strlen(term->lang) + 1; // Room for @
 
-            out = malloc (buf_len);
-            if (UNLIKELY (!out)) return LSUP_MEM_ERR;
+            TRACE ("nt rep length: %lu\n", buf_len);
+
+            tmp = realloc (out, buf_len);
+            if (UNLIKELY (!tmp)) return LSUP_MEM_ERR;
+            out = tmp;
 
-            char *escaped;
-            if (escape_lit (term->data, &escaped) != LSUP_OK)
-                return LSUP_ERROR;
             sprintf (out, "\"%s\"", escaped);
             free (escaped);
 
@@ -78,8 +77,9 @@ term_to_nt (const LSUP_Term *term, const LSUP_NSMap *nsm, char **out_p)
             break;
 
         case LSUP_TERM_BNODE:
-            out = malloc (strlen (term->data) + 2);
-            if (UNLIKELY (!out)) return LSUP_MEM_ERR;
+            tmp = realloc (out, strlen (term->data) + 3);
+            if (UNLIKELY (!tmp)) return LSUP_MEM_ERR;
+            out = tmp;
 
             sprintf (out, "_:%s", term->data);
             rc = LSUP_OK;
@@ -104,28 +104,63 @@ nt_to_term (const char *rep, const LSUP_NSMap *nsm, LSUP_Term **term)
 }
 
 
-static CodecIterator *
+static LSUP_CodecIterator *
 gr_to_nt_init (const LSUP_Graph *gr)
 {
-    // TODO
-    return NULL;
+    LSUP_CodecIterator *it;
+    MALLOC_GUARD (it, NULL);
+    LSUP_Triple lut = {NULL, NULL, NULL};
+
+    it->gr_it = LSUP_graph_lookup(gr, &lut, &it->cur);
+    it->nsm = LSUP_graph_namespace (gr);
+    it->cur = 0;
+    it->trp = LSUP_triple_new (TERM_DUMMY, TERM_DUMMY, TERM_DUMMY);
+    it->rep = NULL;
+    it->str_s = NULL;
+    it->str_p = NULL;
+    it->str_o = NULL;
+
+    return it;
 }
 
 
 static LSUP_rc
-gr_to_nt_iter (CodecIterator *it) {
-    // TODO
-    return LSUP_NOT_IMPL_ERR;
+gr_to_nt_iter (LSUP_CodecIterator *it) {
+    LSUP_rc rc = LSUP_graph_iter_next (it->gr_it, it->trp);
+    if (rc != LSUP_OK) return rc;
+
+    term_to_nt (it->trp->s, it->nsm, &it->str_s);
+    term_to_nt (it->trp->p, it->nsm, &it->str_p);
+    term_to_nt (it->trp->o, it->nsm, &it->str_o);
+
+    char *tmp = realloc (
+            it->rep, strlen (it->str_s) + strlen (it->str_p)
+            + strlen (it->str_o) + 6);
+    if (UNLIKELY (!tmp)) return LSUP_MEM_ERR;
+
+    it->rep = tmp;
+    sprintf (it->rep, "%s %s %s .\n", it->str_s, it->str_p, it->str_o);
+
+    it->cur++;
+
+    return LSUP_OK;
 }
 
 
 static void
-gr_to_nt_done (CodecIterator *it) {
+gr_to_nt_done (LSUP_CodecIterator *it)
+{
+    LSUP_graph_iter_free (it->gr_it);
+    LSUP_triple_free (it->trp);
+    free (it->rep);
+    free (it->str_s);
+    free (it->str_p);
+    free (it->str_o);
     free (it);
 }
 
 
-static CodecIterator *
+static LSUP_CodecIterator *
 nt_to_gr_init (const LSUP_Graph *gr)
 {
     // TODO
@@ -134,14 +169,14 @@ nt_to_gr_init (const LSUP_Graph *gr)
 
 
 static LSUP_rc
-nt_to_gr_iter (CodecIterator *it) {
+nt_to_gr_iter (LSUP_CodecIterator *it) {
     // TODO
     return LSUP_NOT_IMPL_ERR;
 }
 
 
 static void
-nt_to_gr_done (CodecIterator *it) {
+nt_to_gr_done (LSUP_CodecIterator *it) {
     free (it);
 }
 

+ 47 - 29
src/graph.c

@@ -115,8 +115,8 @@ LSUP_graph_new (const LSUP_store_type store_type)
 {
     if (!check_backend (store_type)) return NULL;
 
-    LSUP_Graph *gr = malloc (sizeof (LSUP_Graph));
-    if (UNLIKELY (!gr)) return NULL;
+    LSUP_Graph *gr;
+    MALLOC_GUARD (gr, NULL);
 
     gr->uri = LSUP_uri_new (NULL);
     gr->store_type = store_type;
@@ -242,7 +242,7 @@ LSUP_graph_set_uri (LSUP_Graph *gr, const char *uri)
 
 
 LSUP_NSMap *
-LSUP_graph_namespace (Graph *gr)
+LSUP_graph_namespace (const Graph *gr)
 { return gr->nsm; }
 
 
@@ -274,8 +274,8 @@ LSUP_graph_equals (const Graph *gr1, const Graph *gr2)
 GraphIterator *
 LSUP_graph_add_init (LSUP_Graph *gr)
 {
-    GraphIterator *it = malloc (sizeof (*it));
-    if (UNLIKELY (!it)) return NULL;
+    GraphIterator *it;
+    CALLOC_GUARD (it, NULL);
 
     if (gr->store_type == LSUP_STORE_MEM) {
         it->ht_iter = LSUP_htstore_add_init (gr->ht_store);
@@ -310,6 +310,8 @@ LSUP_graph_add_done (LSUP_GraphIterator *it)
     } else {
         LSUP_mdbstore_add_done (it->mdb_iter);
     }
+
+    free (it);
 }
 
 
@@ -329,7 +331,9 @@ LSUP_graph_add(
     LSUP_GraphIterator *it = LSUP_graph_add_init (gr);
 
     // Serialize and insert RDF triples.
-    LSUP_SerTriple *sspo = STRP_DUMMY;
+    LSUP_SerTriple *sspo = LSUP_striple_new (BUF_DUMMY, BUF_DUMMY, BUF_DUMMY);
+    if (UNLIKELY (!sspo)) return LSUP_MEM_ERR;
+
     for (size_t i = 0; i < trp_ct; i++) {
         TRACE ("Inserting triple #%lu\n", i);
 
@@ -350,8 +354,6 @@ LSUP_graph_add(
         if (UNLIKELY (db_rc < 0)) return db_rc;
     }
 
-    LSUP_graph_add_done (it);
-
     if (inserted) {
         if (gr->store_type == LSUP_STORE_MEM) {
             *inserted = LSUP_htiter_cur (it->ht_iter);
@@ -360,6 +362,8 @@ LSUP_graph_add(
         }
     }
 
+    LSUP_graph_add_done (it);
+
     return rc;
 }
 
@@ -373,7 +377,7 @@ LSUP_graph_remove (Graph *gr, const LSUP_Triple *spo, size_t *ct)
     LSUP_Buffer *sc = LSUP_buffer_new_from_term (gr->uri);
 
     if (gr->store_type == LSUP_STORE_MEM)
-        rc = 0;// TODO uncomment LSUP_htstore_remove (gr->ht_store, &sspo, ct);
+        rc = LSUP_htstore_remove (gr->ht_store, sspo, ct);
     else
         rc = LSUP_mdbstore_remove (gr->mdb_store, sspo, sc, ct);
 
@@ -387,8 +391,8 @@ LSUP_graph_remove (Graph *gr, const LSUP_Triple *spo, size_t *ct)
 GraphIterator *
 LSUP_graph_lookup (const Graph *gr, const LSUP_Triple *spo, size_t *ct)
 {
-    GraphIterator *it = malloc (sizeof (GraphIterator));
-    if (UNLIKELY (!it)) return NULL;
+    GraphIterator *it;
+    MALLOC_GUARD (it, NULL);
 
     it->graph = gr;
 
@@ -396,7 +400,8 @@ LSUP_graph_lookup (const Graph *gr, const LSUP_Triple *spo, size_t *ct)
     LSUP_Buffer *sc = LSUP_buffer_new_from_term (gr->uri);
 
     if (gr->store_type == LSUP_STORE_MEM) {
-        // TODO uncomment it->ht_iter = LSUP_htstore_lookup (gr->ht_store, sspo, &it->ct);
+        it->ht_iter = LSUP_htstore_lookup (gr->ht_store, sspo);
+        if (ct) *ct = it->ct;
 
     } else {
         it->mdb_iter = LSUP_mdbstore_lookup (gr->mdb_store, sspo, sc, ct);
@@ -420,7 +425,7 @@ graph_iter_next_buffer (GraphIterator *it, LSUP_SerTriple *sspo)
     LSUP_rc rc;
 
     if (it->graph->store_type == LSUP_STORE_MEM)
-        rc = 0;// TODO uncomment LSUP_htiter_next (it->ht_iter, sspo);
+        rc = LSUP_htiter_next (it->ht_iter, sspo);
     else rc = LSUP_mdbiter_next (it->mdb_iter, sspo);
 
     return rc;
@@ -430,27 +435,38 @@ graph_iter_next_buffer (GraphIterator *it, LSUP_SerTriple *sspo)
 LSUP_rc
 LSUP_graph_iter_next (GraphIterator *it, LSUP_Triple *spo)
 {
-    LSUP_SerTriple *sspo = NULL;
-    if (spo) sspo = STRP_DUMMY;
+    /*
+     * NOTE: Memory and MDB back ends treat sspo differently, whereas the
+     * memory one owns the whole buffer structure, while the MDB one owns only
+     * the data. Therefore they must be initialized and freed differently.
+     */
+
+    LSUP_SerTriple *sspo;
+    LSUP_Buffer *ss, *sp, *so;
+
+    if (it->graph->store_type == LSUP_STORE_MEM) {
+        ss = sp = so = NULL;
+    } else {
+        // Craft buffers manually so that their addresses are NULL and need not
+        // be freed.
+        CALLOC_GUARD (ss, LSUP_MEM_ERR);
+        CALLOC_GUARD (sp, LSUP_MEM_ERR);
+        CALLOC_GUARD (so, LSUP_MEM_ERR);
+    }
+    sspo = LSUP_striple_new (ss, sp, so);
 
     LSUP_rc rc = graph_iter_next_buffer (it, sspo);
 
     if (rc == LSUP_OK) {
-        if (spo) {
-            LSUP_term_deserialize (sspo->s, spo->s);
-            LSUP_term_deserialize (sspo->p, spo->p);
-            LSUP_term_deserialize (sspo->o, spo->o);
-        }
-    }
+        LSUP_term_deserialize (sspo->s, spo->s);
+        LSUP_term_deserialize (sspo->p, spo->p);
+        LSUP_term_deserialize (sspo->o, spo->o);
 
-    // Addresses of triple buffers are owned by the DB. Do not free.
-    if (sspo) {
-        free (sspo->s);
-        free (sspo->p);
-        free (sspo->o);
-        free (sspo);
     }
 
+    if (it->graph->store_type == LSUP_STORE_MEM) free (sspo);
+    else LSUP_striple_free_shallow (sspo);
+
     return rc;
 }
 
@@ -459,7 +475,7 @@ void
 LSUP_graph_iter_free (GraphIterator *it)
 {
     if (it->graph->store_type == LSUP_STORE_MEM)
-        NULL;// TODO uncomment LSUP_htiter_free (it->ht_iter);
+        LSUP_htiter_free (it->ht_iter);
     else
         LSUP_mdbiter_free (it->mdb_iter);
 
@@ -476,8 +492,10 @@ bool
 LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo)
 {
     GraphIterator *it = LSUP_graph_lookup (gr, spo, NULL);
-    bool rc = LSUP_graph_iter_next (it, NULL) != LSUP_NORESULT;
+    LSUP_Triple *tmp_spo = LSUP_triple_new (TERM_DUMMY, TERM_DUMMY, TERM_DUMMY);
+    bool rc = LSUP_graph_iter_next (it, tmp_spo) != LSUP_NORESULT;
 
+    LSUP_triple_free (tmp_spo);
     LSUP_graph_iter_free (it);
 
     return rc;

+ 8 - 10
src/namespace.c

@@ -22,11 +22,8 @@ typedef struct ns_map_t {
 NSMap *
 LSUP_nsmap_new (void)
 {
-    NSMap *map = malloc (sizeof (*map));
-    if (UNLIKELY (!map)) return NULL;
-
-    map->pn = NULL;
-    map->np = NULL;
+    NSMap *map;
+    CALLOC_GUARD (map, NULL);
 
     return map;
 }
@@ -49,7 +46,6 @@ LSUP_nsmap_free (NSMap *map)
 
     HASH_ITER (hh, map->np, idx_entry, idx_tmp) {
         HASH_DEL (map->np, idx_entry);
-        free (idx_entry->ns);
         free (idx_entry);
     }
 
@@ -74,8 +70,9 @@ LSUP_nsmap_add (NSMap *map, const ns_pfx pfx, const char *nsstr)
     }
 
     // Add.
-    entry = malloc (sizeof (*entry));
-    if (UNLIKELY (!entry)) return LSUP_MEM_ERR;
+    MALLOC_GUARD (entry, LSUP_MEM_ERR);
+    //entry = malloc (sizeof (*entry));
+    //if (UNLIKELY (!entry)) return LSUP_MEM_ERR;
 
     entry->ns = strdup (nsstr);
     strcpy (entry->pfx, pfx);
@@ -93,8 +90,9 @@ LSUP_nsmap_add (NSMap *map, const ns_pfx pfx, const char *nsstr)
     }
 
     // Add.
-    idx_entry = malloc (sizeof (*idx_entry));
-    if (UNLIKELY (!entry)) return LSUP_MEM_ERR;
+    MALLOC_GUARD (idx_entry, LSUP_MEM_ERR);
+    //idx_entry = malloc (sizeof (*idx_entry));
+    //if (UNLIKELY (!entry)) return LSUP_MEM_ERR;
 
     idx_entry->ns = entry;
 

+ 52 - 35
src/store_htable.c

@@ -35,14 +35,14 @@ typedef struct ht_iterator_t {
     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 *       bucket;     // Retrieved SPO key bucket.
+    TripleEntry *       entry;     // Retrieved SPO key.
 } HTIterator;
 
 
 /**
  * Identity hashing function.
  *
- * Since the key is already a strong hash, reuse it for bucket allocation.
+ * Since the key is already a strong hash, reuse it for entry allocation.
  */
 //#define HASH_FUNCTION (s,len,hashv) (hashv) = (unsigned)(s)
 
@@ -97,6 +97,13 @@ static bool lookup_pok_eq_fn(
         const LSUP_Key spok[], const LSUP_Key luk[])
 { return spok[1] == luk[0] && spok[2] == luk[1]; }
 
+/**
+ * Keyset lookup for S, P and O keys.
+ */
+static bool lookup_spok_eq_fn(
+        const LSUP_Key spok[], const LSUP_Key luk[])
+{ return spok[0] == luk[0] && spok[1] == luk[1] && spok[2] == luk[2]; }
+
 
 /* * * Other prototypes. * * */
 
@@ -109,8 +116,8 @@ static inline LSUP_rc tkey_to_strp (
 HTStore *
 LSUP_htstore_new (void)
 {
-    HTStore *ht = malloc (sizeof (*ht));
-    if (UNLIKELY (!ht)) return NULL;
+    HTStore *ht;
+    MALLOC_GUARD (ht, NULL);
 
     ht->keys = NULL;
     ht->idx = NULL;
@@ -251,9 +258,12 @@ void
 LSUP_htstore_free (HTStore *ht)
 {
     IndexEntry *idx_entry, *idx_tmp;
+    size_t ct = 0;
     HASH_ITER (hh, ht->idx, idx_entry, idx_tmp) {
         HASH_DEL (ht->idx, idx_entry);
+        LSUP_buffer_free (idx_entry->sterm);
         free (idx_entry);
+        ct++;
     }
 
     TripleEntry *trp_entry, *trp_tmp;
@@ -274,8 +284,8 @@ LSUP_htstore_size (LSUP_HTStore *ht)
 LSUP_HTIterator *
 LSUP_htstore_add_init (HTStore *store)
 {
-    HTIterator *it = malloc (sizeof (HTIterator));
-    if (UNLIKELY (!it)) return NULL;
+    HTIterator *it;
+    MALLOC_GUARD (it, NULL);
 
     it->store = store;
     it->i = 0;
@@ -299,31 +309,31 @@ LSUP_htstore_add_iter (HTIterator *it, const LSUP_SerTriple *sspo)
     TripleEntry *k_ins = NULL;
     HASH_FIND (hh, it->store->keys, spok, TRP_KLEN, k_ins);
     if (k_ins == NULL) {
-        TRACE (STR, "Triple not found, inserting.\n");
-        k_ins = malloc (sizeof (*k_ins));
-        if (UNLIKELY (!k_ins)) return LSUP_MEM_ERR;
+        TRACE (STR, "Triple not found, inserting.");
+        MALLOC_GUARD (k_ins, LSUP_MEM_ERR);
 
         memcpy (k_ins->key, spok, TRP_KLEN);
         HASH_ADD (hh, it->store->keys, key, TRP_KLEN, k_ins);
 
         it->i++;
     } else {
-        TRACE (STR, "Triple found. Skipping.\n");
+        TRACE (STR, "Triple found. Skipping.");
         return LSUP_NOACTION;
     }
 
     // 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 %lx\n", spok[i]);
+        //TRACE ("Indexing term key %lx\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;
+            MALLOC_GUARD (ins, LSUP_MEM_ERR);
             ins->key = spok[i];
-            ins->sterm = LSUP_striple_pos (sspo, i);
+            ins->sterm = LSUP_buffer_new (
+                    (LSUP_striple_pos (sspo, i))->size,
+                    (LSUP_striple_pos (sspo, i))->addr);
             HASH_ADD (hh, it->store->idx, key, KLEN, ins);
         }
     }
@@ -334,7 +344,7 @@ LSUP_htstore_add_iter (HTIterator *it, const LSUP_SerTriple *sspo)
 
 void
 LSUP_htstore_add_done (HTIterator *it)
-{ free (it); }
+{ LSUP_htiter_free (it); }
 
 
 LSUP_rc
@@ -347,10 +357,10 @@ LSUP_htstore_remove(
     *ct = 0;
 
     TripleEntry *tmp = malloc (sizeof (*tmp));
-    HASH_ITER (hh, store->keys, it->bucket, tmp) {
-        if (it->eq_fn (it->bucket->key, it->luk)) {
-            HASH_DEL (store->keys, it->bucket);
-            free (it->bucket);
+    HASH_ITER (hh, store->keys, it->entry, tmp) {
+        if (it->eq_fn (it->entry->key, it->luk)) {
+            HASH_DEL (store->keys, it->entry);
+            free (it->entry);
 
             (*ct)++;
         }
@@ -365,8 +375,8 @@ LSUP_htstore_remove(
 HTIterator *
 LSUP_htstore_lookup (HTStore *store, const LSUP_SerTriple *sspo)
 {
-    HTIterator *it = malloc (sizeof (*it));
-    if (UNLIKELY (!it)) return NULL;
+    HTIterator *it;
+    MALLOC_GUARD (it, NULL);
 
     it->store = store;
     //it->cur = 0;
@@ -383,7 +393,7 @@ LSUP_htstore_lookup (HTStore *store, const LSUP_SerTriple *sspo)
     // 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;
+        it->eq_fn = lookup_spok_eq_fn;
 
     } else if (spok[0] != NULL_KEY) {
         it->luk[0] = spok[0];
@@ -423,8 +433,8 @@ LSUP_htstore_lookup (HTStore *store, const LSUP_SerTriple *sspo)
     // ? ? ?
     } else it->eq_fn = lookup_none_eq_fn;
 
-    it->bucket = it->store->keys; // First record in hash table.
-    it->rc = it->bucket == NULL ? LSUP_END : LSUP_OK;
+    it->entry = it->store->keys; // First record in hash table.
+    it->rc = it->entry == NULL ? LSUP_END : LSUP_OK;
     it->i = 0;
 
     return it;
@@ -450,23 +460,23 @@ LSUP_htiter_next (HTIterator *it, LSUP_SerTriple *sspo)
     it->rc = LSUP_NORESULT;
 
     while (it->rc == LSUP_NORESULT) {
-        if (!it->bucket) it->rc = LSUP_END;
+        if (!it->entry) it->rc = LSUP_END;
 
         else {
-            if (it->eq_fn (it->bucket->key, it->luk)) {
-                tkey_to_strp (it->store, it->bucket->key, sspo);
+            if (it->eq_fn (it->entry->key, it->luk)) {
+                tkey_to_strp (it->store, it->entry->key, sspo);
                 if (!sspo->s || !sspo->p || !sspo->o) return LSUP_DB_ERR;
 
                 TRACE (
                     "Found spok: {%lx, %lx, %lx}",
-                    it->bucket->key[0], it->bucket->key[1], it->bucket->key[2]
+                    it->entry->key[0], it->entry->key[1], it->entry->key[2]
                 );
 
                 it->rc = LSUP_OK;
                 it->i++;
             }
 
-            it->bucket = it->bucket->hh.next;
+            it->entry = it->entry->hh.next;
         }
     }
     return it->rc;
@@ -479,13 +489,20 @@ 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;
+    // owned by the store.
+    IndexEntry *tmp = NULL;
 
-        memcpy (LSUP_striple_pos (sspo, i), cur->sterm, sizeof (*cur->sterm));
-    };
+    HASH_FIND (hh, store->idx, spok + 0, KLEN, tmp);
+    sspo->s = tmp->sterm;
+    sspo->s->size = tmp->sterm->size;
+
+    HASH_FIND (hh, store->idx, spok + 1, KLEN, tmp);
+    sspo->p = tmp->sterm;
+    sspo->s->size = tmp->sterm->size;
+
+    HASH_FIND (hh, store->idx, spok + 2, KLEN, tmp);
+    if (UNLIKELY (!tmp)) return LSUP_ERROR;
+    sspo->o = tmp->sterm;
 
     return LSUP_OK;
 }

+ 24 - 13
src/store_mdb.c

@@ -244,8 +244,8 @@ MDBStore *
 LSUP_mdbstore_new (const char *path, const LSUP_Buffer *default_ctx)
 {
     int db_rc;
-    LSUP_MDBStore *store = malloc (sizeof (LSUP_MDBStore));
-    if (UNLIKELY (!store)) return NULL;
+    LSUP_MDBStore *store;
+    MALLOC_GUARD (store, NULL);
 
     db_rc = mdb_env_create (&store->env);
     TRACE ("create rc: %d", db_rc);
@@ -343,8 +343,8 @@ LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc)
     /* An iterator is used here. Some members are a bit misused but it does
      * its job without having to define a very similar struct.
      */
-    MDBIterator *it = malloc (sizeof (*it));
-    if (UNLIKELY (!it)) return NULL;
+    MDBIterator *it;
+    MALLOC_GUARD (it, NULL);
 
     it->store = store;
     it->i = 0;
@@ -549,8 +549,8 @@ LSUP_mdbstore_lookup(
         LSUP_buffer_hash (sspo->o),
     };
 
-    LSUP_MDBIterator *it = malloc (sizeof (*it));
-    if (UNLIKELY (!it)) return NULL;
+    LSUP_MDBIterator *it;
+    CALLOC_GUARD (it, NULL);
 
     it->store = store;
     it->ck = store->default_ctx ? LSUP_buffer_hash (sc) : NULL_KEY;
@@ -703,11 +703,10 @@ void
 LSUP_mdbiter_free (MDBIterator *it)
 {
     if (it) {
-        mdb_cursor_close (it->cur);
+        if (it->cur) mdb_cursor_close (it->cur);
         if (it->store->txn != it->txn) mdb_txn_abort (it->txn);
 
         free (it);
-        it = NULL;
     }
 }
 
@@ -886,10 +885,12 @@ index_triple(
 
         } else { // OP_ADD is guaranteed.
             // 1-bound index.
+            /*
             TRACE ("Indexing in %s: ", db_labels[lookup_indices[i]]);
             TRACE(
                     "%lx: %lx %lx\n", *(size_t*)(v1.mv_data),
                     *(size_t*)(v2.mv_data), *(size_t*)(v2.mv_data) + 1);
+            */
 
             db_rc = mdb_put (store->txn, db1, &v1, &v2, MDB_NODUPDATA);
 
@@ -897,10 +898,12 @@ index_triple(
             else if (db_rc != MDB_KEYEXIST) return LSUP_DB_ERR;
 
             // 2-bound index.
+            /*
             TRACE ("Indexing in %s: ", db_labels[lookup_indices[i + 3]]);
             TRACE(
                     "%lx %lx: %lx\n", *(size_t*)(v2.mv_data),
                     *(size_t*)(v2.mv_data) + 1, *(size_t*)(v1.mv_data));
+            */
 
             db_rc = mdb_put (store->txn, db2, &v2, &v1, MDB_NODUPDATA);
 
@@ -1028,6 +1031,7 @@ lookup_0bound (MDBStore *store, MDBIterator *it, size_t *ct)
             if (it->rc == MDB_SUCCESS) mdb_cursor_count (it->cur, ct);
 
             mdb_cursor_close (it->cur);
+            it->cur = NULL;
 
         } else {
             // Look up all contexts.
@@ -1041,6 +1045,9 @@ lookup_0bound (MDBStore *store, MDBIterator *it, size_t *ct)
     mdb_cursor_open (it->txn, store->dbi[IDX_SPO_C], &it->cur);
 
     it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_FIRST);
+    mdb_cursor_close (it->cur);
+    it->cur = NULL;
+
     it->iter_op_fn = it_next_0bound;
 
     if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
@@ -1077,8 +1084,8 @@ lookup_1bound (MDBStore *store, uint8_t idx0, MDBIterator *it, size_t *ct)
         // the context is to loop over them.
         if (it->ck != NULL_KEY) {
             TRACE ("Counting in context: %lx\n", it->ck);
-            MDBIterator *ct_it = malloc (sizeof (MDBIterator));
-            if (UNLIKELY (!ct_it)) return LSUP_MEM_ERR;
+            MDBIterator *ct_it;
+            MALLOC_GUARD (ct_it, LSUP_MEM_ERR);
 
             ct_it->luk[0] = it->luk[0];
             /*
@@ -1100,7 +1107,8 @@ lookup_1bound (MDBStore *store, uint8_t idx0, MDBIterator *it, size_t *ct)
                 TRACE ("Counter increased to %lu.", *ct);
             }
 
-            mdb_cursor_close (ct_it->cur);
+            // Free the counter iterator without freeing the shared txn.
+            if (ct_it->cur) mdb_cursor_close (ct_it->cur);
             free (ct_it);
 
         } else {
@@ -1191,8 +1199,8 @@ lookup_2bound(
         // If a context is specified, the only way to count triples matching
         // the context is to loop over them.
         if (it->ck != NULL_KEY) {
-            MDBIterator *ct_it = malloc (sizeof (MDBIterator));
-            if (UNLIKELY (!ct_it)) return LSUP_MEM_ERR;
+            MDBIterator *ct_it;
+            MALLOC_GUARD (ct_it, LSUP_MEM_ERR);
 
             ct_it->luk[0] = it->luk[0];
             ct_it->luk[1] = it->luk[1];
@@ -1208,6 +1216,8 @@ lookup_2bound(
             while (LSUP_mdbiter_next (ct_it, NULL) != LSUP_END) {
                 ct[0] ++;
             }
+
+            // Free the counter iterator without freeing the shared txn.
             if (ct_it->cur) mdb_cursor_close (ct_it->cur);
             free (ct_it);
 
@@ -1263,6 +1273,7 @@ lookup_3bound (MDBStore *store, MDBIterator *it, size_t *ct)
     it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_BOTH);
 
     mdb_cursor_close (it->cur);
+    it->cur = NULL;
 
     if (ct && it->rc == MDB_SUCCESS) *ct = 1;
 

+ 7 - 8
src/term.c

@@ -28,8 +28,7 @@ LSUP_term_new (
         LSUP_term_type type, const char *data, char *datatype, char *lang)
 {
     LSUP_Term *term;
-    term = calloc (1, sizeof (*term));
-    if (UNLIKELY (!term)) return NULL;
+    CALLOC_GUARD (term, NULL);
 
     // If undefined, just set the type.
     if (type == LSUP_TERM_UNDEFINED) term->type = type;
@@ -47,8 +46,8 @@ LSUP_term_new (
 LSUP_Term *
 LSUP_term_new_from_buffer (const LSUP_Buffer *sterm)
 {
-    LSUP_Term *term = malloc (sizeof (*term));
-    if (UNLIKELY (!term)) return NULL;
+    LSUP_Term *term;
+    MALLOC_GUARD (term, NULL);
 
     if (UNLIKELY (LSUP_term_deserialize (sterm, term) != LSUP_OK)) {
         free (term);
@@ -62,8 +61,8 @@ LSUP_term_new_from_buffer (const LSUP_Buffer *sterm)
 LSUP_Buffer *
 LSUP_buffer_new_from_term (const LSUP_Term *term)
 {
-    LSUP_Buffer *sterm = malloc (sizeof (*sterm));
-    if (UNLIKELY (!sterm)) return NULL;
+    LSUP_Buffer *sterm;
+    MALLOC_GUARD (sterm, NULL);
     sterm->addr = NULL;
 
     if (LSUP_term_serialize (term, sterm) != LSUP_OK) {
@@ -170,8 +169,8 @@ LSUP_term_init(
  *
  * "hello"@en-US
  *
- * 0      1           7               18             size=26
- * | \x03 | hello\x00 | xsd:string\x00 | en-US\x00\x00\x00 |
+ * 0      1           7               18     size=24
+ * | \x03 | hello\x00 | xsd:string\x00 | en-US\x00 |
  * type   data        datatype         lang
  */
 LSUP_rc

+ 10 - 0
src/triple.c

@@ -170,6 +170,16 @@ LSUP_striple_free (LSUP_SerTriple *sspo)
 }
 
 
+void
+LSUP_striple_free_shallow (LSUP_SerTriple *sspo)
+{
+    if (UNLIKELY (!sspo)) return;
+
+    sspo->s->addr = sspo->p->addr = sspo->o->addr = NULL;
+    LSUP_striple_free (sspo);
+}
+
+
 /* Inline extern prototypes. */
 
 LSUP_Key LSUP_striple_hash (const LSUP_SerTriple *strp);

+ 6 - 3
test.c

@@ -17,20 +17,23 @@ int main(int argc, char **argv) {
     if (
         term_tests() ||
         namespace_tests() ||
-        codec_nt_tests() ||
         store_ht_tests() ||
         store_mdb_tests() ||
         graph_tests() ||
+        codec_nt_tests() ||
         0
     ) {
         printf("Test failed.");
         rc = -1;
     } else {
-        printf("ALL TESTS PASSED\n");
+        printf(
+              "\n****************\n"
+                "ALL TESTS PASSED\n"
+                "****************\n");
         rc = 0;
     }
 
-    printf("Tests run: %d\n", tests_run);
+    printf("\nTests run: %d\n", tests_run);
 
     return rc;
 }

+ 105 - 50
test/test_codec_nt.c

@@ -1,91 +1,145 @@
 #include "test.h"
 #include "codec_nt.h"
 
-static int
-test_encode_nt_term()
+#define TERM_CT 10
+
+static LSUP_Term **
+init_terms (void)
 {
-    LSUP_Term *uri1 = LSUP_uri_new ("urn:local:s1");
-    LSUP_Term *uri2 = LSUP_uri_new ("http://example.org/p1");
-    LSUP_Term *lit1 = LSUP_term_new (LSUP_TERM_LITERAL, "hello", NULL, NULL);
-    LSUP_Term *lit2 = LSUP_term_new (
-            LSUP_TERM_LITERAL, "hello", NULL, "en-US");
-    LSUP_Term *lit3 = LSUP_term_new (
+    LSUP_Term **terms = malloc (TERM_CT * sizeof (*terms));
+    terms[0] = LSUP_uri_new ("urn:local:s1");
+    terms[1] = LSUP_uri_new ("http://example.org/p1");
+    terms[2] = LSUP_term_new (LSUP_TERM_LITERAL, "hello", NULL, NULL);
+    terms[3] = LSUP_term_new (LSUP_TERM_LITERAL, "hello", NULL, "en-US");
+    terms[4] = LSUP_term_new (
             LSUP_TERM_LITERAL, "hello",
             "http://www.w3.org/2001/XMLSchema#string", "es-ES");
-    LSUP_Term *lit4 = LSUP_term_new (
+    terms[5] = LSUP_term_new (
             LSUP_TERM_LITERAL, "25",
             "http://www.w3.org/2001/XMLSchema#integer", NULL);
-    LSUP_Term *lit5 = LSUP_term_new (
+    terms[6] = LSUP_term_new (
             LSUP_TERM_LITERAL, "This \\is\\ a \"multi-line\"\n'literal'\t.",
             NULL, NULL);
-    LSUP_Term *bnode1 = LSUP_term_new (LSUP_TERM_BNODE, "bn1", NULL, NULL);
-    LSUP_Term *undef1 = LSUP_term_new (
-            LSUP_TERM_UNDEFINED, "bogus", NULL, NULL);
-    LSUP_Term *undef2 = TERM_DUMMY;
+    terms[7] = LSUP_term_new (LSUP_TERM_BNODE, "bn1", NULL, NULL);
+    terms[8] = LSUP_term_new (LSUP_TERM_UNDEFINED, "bogus", NULL, NULL);
+    terms[9] = TERM_DUMMY;
+
+    return terms;
+}
+
+
+static void
+free_terms (LSUP_Term **terms)
+{
+    for (int i = 0; i < TERM_CT; i++)
+        LSUP_term_free (terms[i]);
+
+    free (terms);
+}
+
+static int
+test_encode_nt_term()
+{
+    LSUP_Term **terms = init_terms();
 
     LSUP_NSMap *nsm = LSUP_nsmap_new();
     LSUP_nsmap_add (nsm, "local", "urn:local:");
     LSUP_nsmap_add (nsm, "ext", "http://example.org");
 
-    char *out;
-    EXPECT_PASS (nt_codec.term_encoder (uri1, NULL, &out));
+    char *out = NULL;
+    EXPECT_PASS (nt_codec.term_encoder (terms[0], NULL, &out));
     EXPECT_STR_EQ (out, "<urn:local:s1>");
-    free (out);
 
-    EXPECT_PASS (nt_codec.term_encoder (uri1, nsm, &out));
+    EXPECT_PASS (nt_codec.term_encoder (terms[0], nsm, &out));
     EXPECT_STR_EQ (out, "<urn:local:s1>");
-    free (out);
 
-    EXPECT_PASS (nt_codec.term_encoder (uri2, NULL, &out));
+    EXPECT_PASS (nt_codec.term_encoder (terms[1], NULL, &out));
     EXPECT_STR_EQ (out, "<http://example.org/p1>");
-    free (out);
 
-    EXPECT_PASS (nt_codec.term_encoder (lit1, NULL, &out));
+    EXPECT_PASS (nt_codec.term_encoder (terms[2], NULL, &out));
     EXPECT_STR_EQ (out, "\"hello\"");
-    free (out);
 
-    EXPECT_PASS (nt_codec.term_encoder (lit2, NULL, &out));
+    EXPECT_PASS (nt_codec.term_encoder (terms[3], NULL, &out));
     EXPECT_STR_EQ (out, "\"hello\"@en-US");
-    free (out);
 
-    EXPECT_PASS (nt_codec.term_encoder (lit3, NULL, &out));
-    EXPECT_STR_EQ (
-            out, "\"hello\"@es-ES");
-    free (out);
+    EXPECT_PASS (nt_codec.term_encoder (terms[4], NULL, &out));
+    EXPECT_STR_EQ (out, "\"hello\"@es-ES");
 
-    EXPECT_PASS (nt_codec.term_encoder (lit4, NULL, &out));
-    EXPECT_STR_EQ (
-            out, "\"25\"^^http://www.w3.org/2001/XMLSchema#integer");
-    free (out);
+    EXPECT_PASS (nt_codec.term_encoder (terms[5], NULL, &out));
+    EXPECT_STR_EQ (out, "\"25\"^^http://www.w3.org/2001/XMLSchema#integer");
 
-    EXPECT_PASS (nt_codec.term_encoder (lit5, NULL, &out));
+    EXPECT_PASS (nt_codec.term_encoder (terms[6], NULL, &out));
     EXPECT_STR_EQ (
             out,
             "\"This \\\\is\\\\ a \\\"multi-line\\\"\\n\\'literal\\'\\t.\"");
-    free (out);
 
-    EXPECT_PASS (nt_codec.term_encoder (bnode1, NULL, &out));
+    EXPECT_PASS (nt_codec.term_encoder (terms[7], NULL, &out));
     EXPECT_STR_EQ (out, "_:bn1");
-    free (out);
 
-    EXPECT_INT_EQ (nt_codec.term_encoder (undef1, NULL, &out), LSUP_VALUE_ERR);
+    EXPECT_INT_EQ (nt_codec.term_encoder (terms[8], NULL, &out), LSUP_VALUE_ERR);
     ASSERT (out == NULL, "Encoding of undefined term should be NULL!");
-    free (out);
 
-    EXPECT_INT_EQ (nt_codec.term_encoder (undef2, NULL, &out), LSUP_VALUE_ERR);
+    EXPECT_INT_EQ (nt_codec.term_encoder (terms[9], NULL, &out), LSUP_VALUE_ERR);
     ASSERT (out == NULL, "Encoding of undefined term should be NULL!");
+
+    free (out);
+    LSUP_nsmap_free (nsm);
+    free_terms(terms);
+
+    return 0;
+}
+
+
+static int test_encode_nt_graph()
+{
+    LSUP_Term **terms = init_terms();
+
+    LSUP_Graph *gr = LSUP_graph_new (LSUP_STORE_MEM);
+    if (!gr) return LSUP_MEM_ERR;
+
+    LSUP_Triple trp[7];
+
+    LSUP_triple_init (trp + 0, terms[0], terms[1], terms[2]);
+    LSUP_triple_init (trp + 1, terms[0], terms[1], terms[3]);
+    LSUP_triple_init (trp + 2, terms[0], terms[1], terms[4]);
+    LSUP_triple_init (trp + 3, terms[0], terms[1], terms[7]);
+    LSUP_triple_init (trp + 4, terms[7], terms[1], terms[4]);
+    LSUP_triple_init (trp + 5, terms[7], terms[1], terms[5]);
+    LSUP_triple_init (trp + 6, terms[7], terms[1], terms[6]);
+
+    size_t ins;
+    LSUP_graph_add_trp (gr, trp, 7, &ins);
+
+    char *out = calloc (1, 1);
+    LSUP_CodecIterator *it = nt_codec.gr_encode_init (gr);
+    ASSERT (it != NULL, "Error creating codec iterator!");
+
+    while (nt_codec.gr_encode_iter (it) != LSUP_END) {
+        out = realloc (out, strlen(out) + strlen (it->rep) + 1);
+        out = strcat (out, it->rep);
+    }
+    nt_codec.gr_encode_done (it);
+    LSUP_graph_free (gr);
+    //printf("Serialized graph: %s\n", out);
+
+    // Triples must not be checked in strict order.
+    char *test_out[6] = {
+        "<urn:local:s1> <http://example.org/p1> \"hello\" .\n",
+        "<urn:local:s1> <http://example.org/p1> \"hello\"@es-ES .\n",
+        "<urn:local:s1> <http://example.org/p1> _:bn1 .\n",
+        "_:bn1 <http://example.org/p1> \"hello\"@es-ES .\n",
+        "_:bn1 <http://example.org/p1> "
+            "\"25\"^^http://www.w3.org/2001/XMLSchema#integer .\n",
+        "_:bn1 <http://example.org/p1> "
+            "\"This \\\\is\\\\ a \\\"multi-line\\\"\\n\\\'literal\\\'\\t.\""
+            " .\n",
+    };
+    for (int i = 0; i < 6; i++)
+        ASSERT (strstr (out, test_out[i]) != NULL, "String not found!");
+
     free (out);
 
-    LSUP_term_free (uri1);
-    LSUP_term_free (uri2);
-    LSUP_term_free (lit1);
-    LSUP_term_free (lit2);
-    LSUP_term_free (lit3);
-    LSUP_term_free (lit4);
-    LSUP_term_free (lit5);
-    LSUP_term_free (bnode1);
-    LSUP_term_free (undef1);
-    LSUP_term_free (undef2);
+    free_terms(terms);
 
     return 0;
 }
@@ -94,5 +148,6 @@ test_encode_nt_term()
 int codec_nt_tests()
 {
     RUN (test_encode_nt_term);
+    RUN (test_encode_nt_graph);
     return 0;
 }

+ 36 - 3
test/test_graph.c

@@ -20,6 +20,34 @@ test_graph_mem_new ()
 }
 
 
+static int
+test_graph_mem_add()
+{
+    LSUP_Triple *trp = create_triples();
+
+    LSUP_Graph *gr = LSUP_graph_new (LSUP_STORE_MEM);
+    ASSERT (gr != NULL, "Error creating graph!");
+
+    size_t ct;
+    LSUP_graph_add_trp (gr, trp, NUM_TRP, &ct);
+
+    EXPECT_INT_EQ (ct, 8);
+    EXPECT_INT_EQ (LSUP_graph_size (gr), 8);
+
+    for (int i = 0; i < sizeof (trp); i++) {
+        printf ("checking triple #%d... ", i);
+        ASSERT (LSUP_graph_contains (gr, trp + i), "Triple not in graph!");
+        printf ("OK.\n");
+    }
+
+    free_triples (trp); // gr copied data.
+
+    LSUP_graph_free (gr);
+
+    return 0;
+}
+
+
 static int
 test_graph_mdb_new ()
 {
@@ -30,7 +58,9 @@ test_graph_mdb_new ()
     EXPECT_PASS (LSUP_graph_set_uri (gr, "urn:gr:1"));
     EXPECT_STR_EQ (LSUP_graph_uri (gr)->data, "urn:gr:1");
 
-    ASSERT (strcmp (LSUP_graph_uri (gr)->data, "urn:gr:1") == 0, "Graph URI mismatch!");
+    ASSERT (
+            strcmp (LSUP_graph_uri (gr)->data, "urn:gr:1") == 0,
+            "Graph URI mismatch!");
     EXPECT_INT_EQ (LSUP_graph_size (gr), 0);
 
     LSUP_graph_free (gr);
@@ -45,10 +75,14 @@ test_graph_mdb_add()
     LSUP_Triple *trp = create_triples();
 
     LSUP_Graph *gr = LSUP_graph_new (LSUP_STORE_MDB);
+    ASSERT (gr != NULL, "Error creating graph!");
 
     size_t ct;
     LSUP_graph_add_trp (gr, trp, NUM_TRP, &ct);
 
+    EXPECT_INT_EQ (ct, 8);
+    EXPECT_INT_EQ (LSUP_graph_size (gr), 8);
+
     for (int i = 0; i < sizeof (trp); i++) {
         printf ("checking triple #%d... ", i);
         ASSERT (LSUP_graph_contains (gr, trp + i), "Triple not in graph!");
@@ -57,8 +91,6 @@ test_graph_mdb_add()
 
     free_triples (trp); // gr copied data.
 
-    EXPECT_INT_EQ (LSUP_graph_size (gr), 8);
-
     LSUP_graph_free (gr);
 
     return 0;
@@ -68,6 +100,7 @@ test_graph_mdb_add()
 int graph_tests()
 {
     RUN (test_graph_mem_new);
+    RUN (test_graph_mem_add);
     RUN (test_graph_mdb_new);
     RUN (test_graph_mdb_add);
     return 0;

+ 2 - 0
test/test_namespace.c

@@ -27,6 +27,8 @@ test_namespace()
             "Wrong result for removal of non-existent prefix!");
     ASSERT (LSUP_nsmap_get (nsm, "dc") == NULL, "Deleted NS found!");
 
+    LSUP_nsmap_free (nsm);
+
     return 0;
 }
 

+ 20 - 24
test/test_store_ht.c

@@ -10,17 +10,15 @@ static int test_htstore()
     ASSERT (store != NULL, "Error initializing store!");
 
     LSUP_Triple *trp = create_triples();
-    LSUP_SerTriple ser_trp[NUM_TRP];
+    LSUP_SerTriple *ser_trp[NUM_TRP];
 
-    for (int i = 0; i < NUM_TRP; i++) {
-        LSUP_striple_init (ser_trp + i, BUF_DUMMY, BUF_DUMMY, BUF_DUMMY);
-        LSUP_triple_serialize (trp + i, ser_trp + i);
-    }
+    for (int i = 0; i < NUM_TRP; i++)
+        ser_trp[i] = LSUP_striple_new_from_triple (trp + i);
 
     // Test adding.
     LSUP_HTIterator *it = LSUP_htstore_add_init (store);
     for (size_t i = 0; i < NUM_TRP; i++) {
-        LSUP_rc rc = LSUP_htstore_add_iter (it, ser_trp + 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!");
@@ -35,19 +33,19 @@ static int test_htstore()
     LSUP_SerTriple lut[12] = {
         {NULL, NULL, NULL},
 
-        {ser_trp[0].s, NULL, NULL},
-        {ser_trp[2].s, NULL, NULL},
-        {NULL, ser_trp[0].p, NULL},
-        {NULL, ser_trp[0].s, NULL},
-        {NULL, NULL, ser_trp[6].o},
+        {ser_trp[0]->s, NULL, NULL},
+        {ser_trp[2]->s, NULL, NULL},
+        {NULL, ser_trp[0]->p, NULL},
+        {NULL, ser_trp[0]->s, NULL},
+        {NULL, NULL, ser_trp[6]->o},
 
-        {ser_trp[4].s, ser_trp[4].p, NULL},
-        {NULL, ser_trp[7].p, ser_trp[7].o},
-        {ser_trp[5].s, NULL, ser_trp[5].o},
-        {ser_trp[5].s, NULL, ser_trp[5].o},
+        {ser_trp[4]->s, ser_trp[4]->p, NULL},
+        {NULL, ser_trp[7]->p, ser_trp[7]->o},
+        {ser_trp[5]->s, NULL, ser_trp[5]->o},
+        {ser_trp[5]->s, NULL, ser_trp[5]->o},
 
-        {ser_trp[4].s, ser_trp[4].p, ser_trp[4].o},
-        {ser_trp[4].s, ser_trp[4].p, ser_trp[6].o},
+        {ser_trp[4]->s, ser_trp[4]->p, ser_trp[4]->o},
+        {ser_trp[4]->s, ser_trp[4]->p, ser_trp[6]->o},
     };
 
     size_t results[12] = {
@@ -57,12 +55,12 @@ static int test_htstore()
         1, 0,
     };
 
+    LSUP_SerTriple *sspo = STRP_DUMMY;
     for (int i = 0; i < NUM_TRP; i++) {
         size_t ct = 0;
-        TRACE ("Testing triple lookup #%d.\n", i);
+        TRACE ("Testing triple lookup #%d.", i);
 
         LSUP_HTIterator *it = LSUP_htstore_lookup(store, lut + i);
-        LSUP_SerTriple *sspo = STRP_DUMMY;
         while (LSUP_htiter_next (it, sspo) != LSUP_END) {
             // Verify that the internal counter aligns with an external one.
             ct ++;
@@ -72,12 +70,10 @@ static int test_htstore()
 
         LSUP_htiter_free (it);
     }
+    free (sspo);
 
-    for (int i = 0; i < NUM_TRP; i++) {
-        LSUP_buffer_free (ser_trp[i].s);
-        LSUP_buffer_free (ser_trp[i].p);
-        LSUP_buffer_free (ser_trp[i].o);
-    }
+    for (int i = 0; i < NUM_TRP; i++)
+        LSUP_striple_free (ser_trp[i]);
 
     LSUP_htstore_free (store);
     free_triples (trp);

+ 2 - 0
test/test_store_mdb.c

@@ -259,6 +259,7 @@ static int test_quad_store()
     for (int i = 0; i < 41; i++) {
         size_t ct;
 
+        /*
         printf ("Testing triple lookup #%d: {", i);
 
         if (lut[i].s) LSUP_buffer_print (lut[i].s);
@@ -276,6 +277,7 @@ static int test_quad_store()
         if (luc[i]) LSUP_buffer_print (luc[i]);
         else printf ("NULL");
         printf ("}\n");
+        */
 
         LSUP_MDBIterator *it = LSUP_mdbstore_lookup(
                 store, lut + i, luc[i], &ct);