Browse Source

Logging functions; environment module.

Stefano Cossu 4 years ago
parent
commit
ff83a788bb
25 changed files with 502 additions and 321 deletions
  1. 3 0
      .gitmodules
  2. 10 10
      Makefile
  3. 32 0
      README.md
  4. 1 1
      cpython/py_namespace.h
  5. 1 0
      ext/log
  6. 5 1
      include/buffer.h
  7. 46 55
      include/core.h
  8. 66 0
      include/environment.h
  9. 11 4
      include/graph.h
  10. 1 1
      include/store_mdb.h
  11. 48 16
      profile.c
  12. 1 1
      src/codec/nt_grammar.y
  13. 18 20
      src/codec/nt_lexer.re
  14. 41 1
      src/core.c
  15. 103 0
      src/environment.c
  16. 17 100
      src/graph.c
  17. 5 5
      src/store_htable.c
  18. 66 82
      src/store_mdb.c
  19. 3 3
      src/term.c
  20. 1 1
      test.c
  21. 2 2
      test/test_codec_nt.c
  22. 7 4
      test/test_graph.c
  23. 1 1
      test/test_store_ht.c
  24. 2 2
      test/test_store_mdb.c
  25. 11 11
      test/test_term.c

+ 3 - 0
.gitmodules

@@ -12,3 +12,6 @@
 [submodule "ext/uthash"]
 	path = ext/uthash
 	url = https://github.com/troydhanson/uthash.git
+[submodule "ext/log"]
+	path = ext/log
+	url = https://github.com/rxi/log.c.git

+ 10 - 10
Makefile

@@ -1,12 +1,12 @@
-CODEC_DIR=src/codec
-CC=gcc
-CFLAGS+= -Wall
-INCLUDE=-I. -Iinclude -Iext/xxHash -Iext/openldap/libraries/liblmdb \
-	-Iext/uthash/src
-LIB=-luuid -lpthread
-SRC=ext/xxHash/xxhash.c ext/openldap/libraries/liblmdb/mdb.c \
-	ext/openldap/libraries/liblmdb/midl.c src/*.c src/codec/*_grammar.c \
-	src/codec/*_parser.c
+CODEC_DIR = src/codec
+CC = gcc
+CFLAGS += -Wall -DLOG_USE_COLOR
+INCLUDE = -I. -Iinclude -Iext/xxHash -Iext/openldap/libraries/liblmdb \
+	-Iext/uthash/src -Iext/log/src
+LIB = -luuid -lpthread
+SRC = ext/xxHash/xxhash.c ext/openldap/libraries/liblmdb/mdb.c \
+	ext/openldap/libraries/liblmdb/midl.c ext/log/src/log.c \
+	src/*.c src/codec/*_grammar.c src/codec/*_parser.c
 
 .PHONY: build_parsers lint profile
 
@@ -61,7 +61,7 @@ memcheck: test valgrind
 
 profile: build_parsers
 	$(CC) \
-		$(CFLAGS) \
+		$(CFLAGS) -DDEBUG -g \
 		$(INCLUDE) \
 		$(LIB) \
 		$(SRC) profile.c \

+ 32 - 0
README.md

@@ -71,3 +71,35 @@ of features as a standalone library:
 - C++ bindings
 - JSON-LD de/serialization
 - SPARQL queries (We'll see... Will definitely need help)
+
+## Usage
+
+### Compile-Time Constants
+
+`DEBUG`: Set debug mode: memory map is at reduced size, logging is forced to
+TRACE level, etc.
+
+### Environment Variables
+
+`LSUP_MDB_STORE_PATH`: The file path for the persistent store back end. For
+production use it is strongly recommended to set this to a permanent location
+on the fastest storage volume available. If unset, the current directory will
+be used. The directory must exist.
+
+`LSUP_LOGLEVEL`: A number between 0 and 5, corresponding to:
+
+- 0: `TRACE`
+- 1: `DEBUG`
+- 2: `INFO`
+- 3: `WARN`
+- 4: `ERROR`
+- 5: `FATAL`
+
+If unspecified, it is set to 3.
+
+`LSUP_MDB_MAPSIZE` Virtual memory map size. It is recommended to leave this
+unset and let the software adjust it to the hardware architecture. By default,
+it is set to 1Tb for 64-bit systems and 4Gb for 32-bit systems. The map size by
+itself does not use up any extra resources.
+
+

+ 1 - 1
cpython/py_namespace.h

@@ -94,7 +94,7 @@ NSMap_normalize_uri (PyObject *self, PyObject *fq_uri_obj)
     if (rc < 0)  {
         PyErr_SetString (PyExc_ValueError, "Error normalizing URI.");
         return NULL;
-    }
+    } else if (rc == LSUP_NORESULT) Py_RETURN_NONE;
 
     PyObject *uri_obj = PyUnicode_FromString (pfx_uri);
 

+ 1 - 0
ext/log

@@ -0,0 +1 @@
+Subproject commit f9ea34994bd58ed342d2245cd4110bb5c6790153

+ 5 - 1
include/buffer.h

@@ -137,7 +137,11 @@ LSUP_buffer_as_str (const LSUP_Buffer *buf);
  * The return value is the same as memcmp.
  */
 inline int LSUP_buffer_cmp (const LSUP_Buffer *buf1, const LSUP_Buffer *buf2)
-{ return memcmp (buf1->addr, buf2->addr, max (buf1->size, buf2->size)); }
+{
+    return memcmp (
+            buf1->addr, buf2->addr,
+            (buf1->size > buf2->size ? buf1->size : buf2->size));
+}
 
 
 /** @brief Return whether two buffers are equal.

+ 46 - 55
include/core.h

@@ -14,27 +14,22 @@
 #include <sys/stat.h>
 #include <uuid/uuid.h>
 
+#include "log.h"
+
+
+// Logging and debugging.
 
 #ifdef DEBUG
-#define DEBUG_TEST 1
 // GDB breakpoints.
 #include <signal.h>
-#define BREAKPOINT if (DEBUG_TEST) raise (SIGINT)
-
-#define TRACE(fmt, ...) \
-    do {\
-        fprintf(stderr, "%s:%d:%s(): " fmt "\n", \
-                __FILE__,  __LINE__, __func__, __VA_ARGS__); \
-    } while (0)
+#define BREAKPOINT raise (SIGINT)
 
-#else  /* DEBUG */
-#define DEBUG_TEST 0
+#else
 #define BREAKPOINT
-#define TRACE(fmt, ...)
 
-#endif  /* DEBUG */
+#endif
+
 
-#define STR "%s"
 #define LIKELY(x)       __builtin_expect(!!(x), true)
 #define UNLIKELY(x)     __builtin_expect(!!(x), false)
 
@@ -48,36 +43,42 @@
 
 # define UUIDSTR_SIZE 37
 
-// Handy flags operations.
-#define SET_FLAG(n, f) ((n) |= (f))
-#define CLR_FLAG(n, f) ((n) &= ~(f))
-#define TGL_FLAG(n, f) ((n) ^= (f))
-#define CHK_FLAG(n, f) ((n) & (f))
-
-
 
 /* * * RETURN CODES * * */
 
 /**
  * 0 is success, positive integers (>88800) are warnings, and negative integers
- * (<-88800) are errors.
+ * (>-88900) are errors.
  */
-typedef enum {
-    LSUP_OK             = 0,
-
-    LSUP_NOACTION       = 88801,
-    LSUP_NORESULT       = 88802,
-    LSUP_END            = 88803,
-
-    LSUP_ERROR          = -88801,
-    LSUP_PARSE_ERR      = -88802,
-    LSUP_VALUE_ERR      = -88803,
-    LSUP_TXN_ERR        = -88804,
-    LSUP_DB_ERR         = -88805,
-    LSUP_NOT_IMPL_ERR   = -88806,
-    LSUP_IO_ERR         = -88807,
-    LSUP_MEM_ERR        = -88808,
-} LSUP_rc;
+typedef int LSUP_rc;
+
+#define LSUP_OK             0
+
+#define LSUP_NOACTION       88801
+#define LSUP_NORESULT       88802
+#define LSUP_END            88803
+// NOTE When adding new warning codes, use a value larger than the last one
+// in the list. Also change LSUP_MAX_WARN.
+
+#define LSUP_MIN_WARNING    LSUP_NOACTION
+#define LSUP_MAX_WARNING    LSUP_END
+
+#define LSUP_ERROR          -88899
+#define LSUP_PARSE_ERR      -88898
+#define LSUP_VALUE_ERR      -88897
+#define LSUP_TXN_ERR        -88896
+#define LSUP_DB_ERR         -88895
+#define LSUP_NOT_IMPL_ERR   -88894
+#define LSUP_IO_ERR         -88893
+#define LSUP_MEM_ERR        -88892
+// NOTE When adding new error codes, use a value larger than the last one
+// in the list. Also change LSUP_MAX_ERR.
+
+#define LSUP_MIN_ERROR      LSUP_ERROR
+#define LSUP_MAX_ERROR      LSUP_MEM_ERR
+
+extern char *warning_msg[], *error_msg[];
+
 
 typedef enum {
     LSUP_BOOL_UNION,
@@ -95,9 +96,11 @@ typedef LSUP_Key LSUP_QuadKey[4];
 typedef char uuid_str_t[UUIDSTR_SIZE];
 
 
+/*
 // Yes, a textbook mistake; but writing min and max for all int types is crazy.
 #define min(x, y) (x) < (y) ? (x) : (y)
 #define max(x, y) (x) > (y) ? (x) : (y)
+*/
 
 /** @brief Make recursive directories.
  *
@@ -112,23 +115,11 @@ LSUP_rc mkdir_p(const char *path, mode_t mode);
 LSUP_rc
 rm_r (const char *path);
 
-/** @brief Convert a Unicode (UTF-8) code point to 4-byte sequence.
+
+/** @brief Return an error message for a return code.
  */
-/*
-inline uint16_t utf8_to_bytes (uint16_t c) {
-    unsigned char b[4] = {0};
-
-    if (c<0x80) *b++=c;
-    else if (c<0x800) *b++=192+c/64, *b++=128+c%64;
-    else if (c-0xd800u<0x800) goto error;
-    else if (c<0x10000) *b++=224+c/4096, *b++=128+c/64%64, *b++=128+c%64;
-    else if (c<0x110000)
-        *b++=240+c/262144, *b++=128+c/4096%64, *b++=128+c/64%64, *b++=128+c%64;
-    else return NULL;
-
-    return (uint16_t)b;
-}
-*/
+const char *
+LSUP_strerror (LSUP_rc rc);
 
 
 /**
@@ -193,10 +184,10 @@ inline int utf8_encode(const uint32_t utf, unsigned char *out)
 #define PCHECK(exp, rc, marker) (rc) = (exp); if ((rc) < LSUP_OK) goto marker
 
 // Return rc if it is of LSUP_rc type and is negative (=error)
-#define RCCK(exp) LSUP_rc _rc = (exp); if (_rc < 0) return _rc
+#define RCCK(exp) LSUP_rc _rc = (exp); if (UNLIKELY (_rc < 0)) return _rc
 
 // Return NULL if it is of LSUP_rc type and is negative (=error)
-#define RCNL(exp) if((exp) < 0) return NULL
+#define RCNL(exp) if (UNLIKELY ((exp) < 0)) return NULL
 
 #define MALLOC_GUARD(var, rc) do {                                  \
     (var) = malloc (sizeof *(var));                                 \

+ 66 - 0
include/environment.h

@@ -0,0 +1,66 @@
+/** @file environment.h
+ *
+ * @brief Handle LSUP environment initialization and teardown.
+ */
+
+#ifndef _LSUP_ENVIRONMENT_H
+#define _LSUP_ENVIRONMENT_H
+
+#include "term.h"
+#include "store_mdb.h"
+#include "namespace.h"
+
+typedef struct env_t {
+    LSUP_Buffer *       default_ctx;            // Default context URI.
+    LSUP_MDBStore *     mdbstore;               // MDB permanent store handle.
+    LSUP_MDBStore *     mdbstore_ramdisk;       // MDB RAM disk store handle.
+    LSUP_NSMap *        nsm;                    // Namespace prefix map.
+} LSUP_Env;
+
+
+/** @brief Environment variable that gets passed around.
+ */
+extern LSUP_Env *LSUP_default_env;
+
+
+/** @brief Initialize default context and MDB environments.
+ *
+ * The ramdisk data will persist after the application is shut down, but they
+ * will be wiped clean the next time this function is called.
+ */
+LSUP_Env *
+LSUP_env_new (
+        const char *default_ctx, const char *mdb_path,
+        const char *mdb_ramdisk_path, const LSUP_NSMap *nsmap);
+
+
+/** @brief Initialize the default environment.
+ *
+ * This must be called before using the library.
+ *
+ * The default environment is cleaned up automatically on exit.
+ *
+ * This environment should suit most cases, unless an application needs to use
+ * multiple environments and call #LSUP_env_init with specific handles. Such
+ * other environment(s) must be freed up manually with #LSUP_env_done().
+ */
+LSUP_rc
+LSUP_init (void);
+
+
+/** @brief Close an environment.
+ *
+ * This only needs to be done for non-default environments. The environment
+ * handle is not freed.
+ */
+void
+LSUP_env_done (LSUP_Env *env);
+
+/** @brief Close the defailt environment.
+ *
+ * This is called by atexit(). If called before then, subsequent calls have
+ * no effect.
+ */
+void LSUP_done (void);
+
+#endif /* _LSUP_ENVIRONMENT_H */

+ 11 - 4
include/graph.h

@@ -3,6 +3,7 @@
 
 #include "triple.h"
 #include "namespace.h"
+#include "environment.h"
 
 /*
  * Define backend types and checks.
@@ -52,7 +53,14 @@ typedef struct GraphIterator LSUP_GraphIterator;
  * @return LSUP_OK if the graph was created, or < 0 if an error occurred.
  */
 LSUP_Graph *
-LSUP_graph_new (const LSUP_store_type store_type);
+LSUP_graph_new_env (const LSUP_Env *env, const LSUP_store_type store_type);
+
+
+/** @brief Create an empty graph with the default environment.
+ *
+ * This is likely to be used more often than #LSUP_graph_new_env().
+ */
+#define LSUP_graph_new(type) LSUP_graph_new_env (LSUP_default_env, type)
 
 
 /** @brief copy a graph into a new one.
@@ -204,7 +212,7 @@ LSUP_graph_add_done (LSUP_GraphIterator *it);
  *  inserted.
  */
 LSUP_rc
-LSUP_graph_add(
+LSUP_graph_add (
         LSUP_Graph *gr, const LSUP_Triple trp[],
         const LSUP_SerTriple strp[], size_t *inserted);
 
@@ -214,8 +222,7 @@ LSUP_graph_add(
  * This is a convenience method for external callers which most likely have
  * non-serialized triples at hand.
  */
-#define LSUP_graph_add_trp(gr, trp, ins) \
-    LSUP_graph_add (gr, trp, NULL, ins)
+#define LSUP_graph_add_trp(gr, trp, ins) LSUP_graph_add (gr, trp, NULL, ins)
 
 
 /** @brief Delete triples by a matching pattern.

+ 1 - 1
include/store_mdb.h

@@ -53,7 +53,7 @@ typedef LSUP_rc (*store_match_fn_t)(const LSUP_TripleKey spok, void *data);
  *  in which case it will be set either to the environment variable
  *  LSUP_STORE_PATH, or if that is not set, a default local path.
  */
-LSUP_rc LSUP_mdbstore_setup (char *path, bool clear);
+LSUP_rc LSUP_mdbstore_setup (const char *path, bool clear);
 
 
 /** @brief Open an MDB store.

+ 48 - 16
profile.c

@@ -2,41 +2,39 @@
 #include "graph.h"
 
 #ifndef NT
-#define NT 100000
+#define NT 1000
 #endif
 
 static LSUP_Triple *
 generate_triples()
 {
     LSUP_Triple *trp;
-    trp = malloc(NT * sizeof(LSUP_Triple));
+    trp = malloc((NT  + 1) * sizeof(LSUP_Triple));
     if (!trp) exit (-1);
 
-    char sstr[32], pstr[32], ostr[32];
-
     for (size_t i = 0; i < NT; i++) {
-        sprintf(sstr, "urn:s:%lu", i);
-        sprintf(pstr, "urn:p:%lu", i);
+        char sstr[32], pstr[32], ostr[32];
+
+        sprintf(sstr, "urn:s:%lu", i % (NT / 100));
+        sprintf(pstr, "urn:p:%lu", i % (NT / 1000));
         sprintf(ostr, "urn:o:%lu", i);
         LSUP_triple_init(
                 trp + i, LSUP_uri_new (sstr),
                 LSUP_uri_new (pstr), LSUP_uri_new (ostr));
     }
+    LSUP_triple_init (trp + NT, NULL, NULL, NULL);
     TRACE(STR, "Triples generated.");
 
     return trp;
 }
 
 static LSUP_rc
-insert_triples (LSUP_Triple *trp)
+insert_triples (LSUP_Graph *gr, LSUP_Triple *trp)
 {
-    LSUP_Graph *gr = LSUP_graph_new(LSUP_STORE_MDB_TMP);
-
-    LSUP_rc rc = LSUP_graph_add_trp(gr, trp, NT, NULL);
+    size_t ct;
+    LSUP_rc rc = LSUP_graph_add_trp(gr, trp, &ct);
     if (rc != LSUP_OK) printf ("Graph loading interrupted: %d.\n", rc);
-    else printf ("Graph populated.\n");
-
-    LSUP_graph_free(gr);
+    else printf ("Graph populated with %lu triples.\n", ct);
 
     return rc;
 }
@@ -50,7 +48,7 @@ int main()
     rm_r (getenv ("LSUP_MDB_STORE_PATH"));
 
     int rc;
-    clock_t start, tc1, tc2;
+    clock_t start, tc1, tc2, end;
     double wallclock, rate;
 
     printf ("Generating triples.\n");
@@ -61,16 +59,50 @@ int main()
     printf("Time elapsed: %lf s\n", wallclock);
 
     printf("Inserting triples.\n");
-    rc = insert_triples (trp);
+    LSUP_Graph *gr = LSUP_graph_new (LSUP_STORE_MDB);
+    if (!gr) {
+        fprintf (stderr, "Error creating graph!\n");
+        return -1;
+    }
+    rc = insert_triples (gr, trp);
+
+    for (size_t i = 0; i < NT; i++) {
+        LSUP_term_free (trp[i].s);
+        LSUP_term_free (trp[i].p);
+        LSUP_term_free (trp[i].o);
+    }
+    free (trp);
+
     tc2 = clock();
     wallclock = (tc2 - tc1) / CLOCKS_PER_SEC;
     printf("Time elapsed: %lf s\n", wallclock);
+    printf ("Graph size: %lu\n", LSUP_graph_size (gr));
+
+    printf("Lookup...\n");
+    size_t ct = 0;
+    LSUP_Triple *spo = TRP_DUMMY;
+    LSUP_Term *s = LSUP_uri_new ("urn:s:0");
+    LSUP_Term *p = LSUP_uri_new ("urn:p:3200");
+    LSUP_Term *o = LSUP_uri_new ("urn:o:3200");
+    LSUP_GraphIterator *it = LSUP_graph_lookup(gr, NULL, NULL, NULL, NULL);
+    while (LSUP_graph_iter_next (it, spo) != LSUP_END)
+        ct ++;
+    printf("Found triples per subject: %lu\n", ct);
+    LSUP_graph_iter_free (it);
+    end = clock();
+    wallclock = (end - tc2) / CLOCKS_PER_SEC;
+    printf("Time elapsed: %lf s\n", wallclock);
 
-    wallclock = (tc2 - start) / CLOCKS_PER_SEC;
+    wallclock = (end - start) / CLOCKS_PER_SEC;
     rate = NT / wallclock;
     printf(
             "%d triples created and inserted in %lf s (%lf triples/s)\n",
             NT, wallclock, rate);
 
+    LSUP_term_free (s);
+    LSUP_term_free (p);
+    LSUP_term_free (o);
+    LSUP_graph_free(gr);
+
     return rc;
 }

+ 1 - 1
src/codec/nt_grammar.y

@@ -30,7 +30,7 @@
 
 // Rules.
 
-ntriplesDoc ::= triples EOF. { TRACE (STR, "Parsed N-Triples document.\n"); }
+ntriplesDoc ::= triples EOF. { log_debug ("Parsed N-Triples document."); }
 
 triples     ::= eol.
 triples     ::= triple eol.

+ 18 - 20
src/codec/nt_lexer.re

@@ -64,7 +64,7 @@ static int fill(ParseIterator *it)
     if (shift < 1) {
         return 2;
     }
-    TRACE ("Shifting bytes: %lu\n", shift);
+    log_debug ("Shifting bytes: %lu", shift);
     memmove(it->buf, it->tok, it->lim - it->tok);
     it->lim -= shift;
     it->cur -= shift;
@@ -132,14 +132,14 @@ static YYCTYPE *unescape_unicode (const YYCTYPE *esc_str, size_t size)
                 tmp_chr[4] = '\0';
 
                 uint32_t tmp_val = strtol ((char*)tmp_chr, NULL, 16);
-                TRACE ("tmp_val: %d\n", tmp_val);
+                log_debug ("tmp_val: %d", tmp_val);
 
                 // Reuse tmp_chr to hold the byte values for the code point.
                 int nbytes = utf8_encode (tmp_val, tmp_chr);
 
                 // Copy bytes into destination.
                 memcpy (uc_str + j, tmp_chr, nbytes);
-                TRACE ("UC byte value: %x %x\n", uc_str[j], uc_str[j + 1]);
+                log_debug ("UC byte value: %x %x", uc_str[j], uc_str[j + 1]);
 
                 j += nbytes;
                 i += 4;
@@ -147,9 +147,7 @@ static YYCTYPE *unescape_unicode (const YYCTYPE *esc_str, size_t size)
             // 8-hex sequence.
             } else if (esc_str[i] == 'U') {
                 i ++; // Skip over 'U'
-                fprintf (
-                        stderr,
-                        "UTF-16 sequence unescaping not yet implemented.\n");
+                log_error ("UTF-16 sequence unescaping not yet implemented.");
                 return NULL; // TODO encode UTF-16
 
             // Unescape other escaped characters.
@@ -222,19 +220,19 @@ loop:
     EOL {
         it->line ++;
         it->bol = YYCURSOR;
-        TRACE("New line: #%u.\n", it->line);
+        log_debug ("New line: #%u.", it->line);
         return T_EOL;
     }
 
     $ {
-        TRACE(STR, "End of buffer.\n");
+        log_debug ("End of buffer.");
         return T_EOF;
     }
 
     IRIREF {
         YYCTYPE *data = unescape_unicode (it->tok + 1, YYCURSOR - it->tok - 2);
 
-        TRACE ("URI data: %s\n", data);
+        log_debug ("URI data: %s", data);
 
         *term = LSUP_uri_new ((char*)data);
         free (data);
@@ -246,7 +244,7 @@ loop:
         // Only unescape Unicode from data.
         size_t size = lit_data_e - it->tok - 2;
         YYCTYPE *data = unescape_unicode (it->tok + 1, size);
-        TRACE ("Literal data: %s\n", data);
+        log_debug ("Literal data: %s", data);
 
         YYCTYPE *datatype = NULL, *lang = NULL;
 
@@ -255,7 +253,7 @@ loop:
             datatype = malloc (size);
             memcpy (datatype, dtype_s + 1, size);
             datatype [size - 1] = '\0';
-            TRACE ("datatype: %s\n", datatype);
+            log_debug ("datatype: %s", datatype);
         }
 
         if (lang_s) {
@@ -263,7 +261,7 @@ loop:
             lang = malloc (size);
             memcpy (lang, lang_s + 1, size);
             lang [size - 1] = '\0';
-            TRACE ("lang: %s\n", lang);
+            log_debug ("lang: %s", lang);
         }
 
         *term = LSUP_term_new (LSUP_TERM_LITERAL, (char*)data, (char*)datatype, (char*)lang);
@@ -278,7 +276,7 @@ loop:
     BNODE {
         YYCTYPE *data = unescape_unicode (it->tok + 2, YYCURSOR - it->tok - 2);
 
-        TRACE ("BNode data: %s\n", data);
+        log_debug ("BNode data: %s", data);
 
         *term = LSUP_term_new (LSUP_TERM_BNODE, (char*)data, NULL, NULL);
         free (data);
@@ -287,14 +285,14 @@ loop:
     }
 
     DOT {
-        TRACE (STR, "End of triple.\n");
+        log_debug ("End of triple.");
         it->ct ++;
 
         return T_DOT;
     }
 
     WS {
-        TRACE (STR, "Separator.\n");
+        log_debug ("Separator.");
 
         return T_WS;
     }
@@ -304,15 +302,15 @@ loop:
         YYCTYPE *data = malloc (size);
         memcpy (data, it->tok, size);
         data [size - 1] = '\0';
-        TRACE ("Comment: `%s`\n", data);
+        log_debug ("Comment: `%s`", data);
         free (data);
 
         goto loop;
     }
 
     * {
-        TRACE (
-            "Invalid token @ %lu: %s (\\x%x)\n",
+        log_debug (
+            "Invalid token @ %lu: %s (\\x%x)",
             YYCURSOR - it->buf - 1, it->tok, *it->tok);
 
         return -1;
@@ -400,8 +398,8 @@ LSUP_nt_parse_doc (FILE *stream, LSUP_Graph **gr_p, size_t *ct, char **err_p)
 
     if (ct) *ct = parse_it.ct;
 
-    TRACE ("Parsed %u triples.\n", parse_it.ct);
-    TRACE ("Graph size: %lu\n", LSUP_graph_size (gr));
+    log_info ("Parsed %u triples.", parse_it.ct);
+    log_debug ("Graph size: %lu", LSUP_graph_size (gr));
 
     rc = parse_it.ct > 0 ? LSUP_OK : LSUP_NORESULT;
     *gr_p = gr;

+ 41 - 1
src/core.c

@@ -1,6 +1,33 @@
 #define _XOPEN_SOURCE 500
 #include <ftw.h>
 #include "core.h"
+#include "lmdb.h"
+
+
+/*
+ * The message corresponding to the rc is found by
+ * warning_msg[rc - LSUP_MIN_WARNING].
+ */
+char *warning_msg[] = {
+    "No action or change of state occurred.",
+    "No result.",
+    "End of the loop reached.",
+};
+
+/*
+ * The message corresponding to the rc is found by
+ * err_msg[rc - LSUP_MIN_ERROR].
+ */
+char *err_msg[] = {
+    "LSUP runtime error.",
+    "Error parsing input.",
+    "Invalid input.",
+    "MDB transaction error.",
+    "Database error.",
+    "Not implemented.",
+    "Input/Output error.",
+    "Memory error.",
+};
 
 
 int mkdir_p(const char *path, mode_t mode)
@@ -48,7 +75,7 @@ unlink_cb(
         const char *fpath, const struct stat *sb, int typeflag,
         struct FTW *ftwbuf)
 {
-    TRACE ("Removing %s\n", fpath);
+    log_debug ("Removing %s", fpath);
     int rv = remove(fpath);
 
     if (rv)
@@ -61,6 +88,19 @@ int rm_r(const char *path)
 { return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS); }
 
 
+const char *
+LSUP_strerror (LSUP_rc rc)
+{
+    if (rc >= LSUP_MIN_ERROR && rc <= LSUP_MAX_ERROR)
+        return err_msg[rc - LSUP_MIN_ERROR];
+
+    if (rc >= LSUP_MIN_WARNING && rc <= LSUP_MAX_WARNING)
+        return warning_msg[rc - LSUP_MIN_WARNING];
+
+    return mdb_strerror (rc);
+}
+
+
 /* Inline extern functions. */
 
 int utf8_encode(const uint32_t utf, unsigned char *out);

+ 103 - 0
src/environment.c

@@ -0,0 +1,103 @@
+#include "environment.h"
+
+
+// RAMdisk path for MDB volatile store.
+#define MDB_RAMDISK_PATH TMPDIR "/lsup_mem_graph"
+
+
+/**
+ * Static handles.
+ */
+#define DEFAULT_CTX_LABEL "urn:lsup:default"
+
+
+/** @brief Environment "singleton".
+ */
+LSUP_Env *LSUP_default_env = NULL;
+
+
+LSUP_Env *
+LSUP_env_new (
+        const char *default_ctx, const char *mdb_path,
+        const char *mdb_ramdisk_path, const LSUP_NSMap *nsmap)
+{
+    LSUP_Env *env;
+    MALLOC_GUARD (env, NULL);
+
+    // Default store context.
+    LSUP_Term *default_ctx_uri = LSUP_uri_new (default_ctx);
+    env->default_ctx = LSUP_buffer_new_from_term (default_ctx_uri);
+    LSUP_term_free (default_ctx_uri);
+    log_info ("Setting up default context.");
+
+    // Permanent store.
+    if (LSUP_mdbstore_setup (mdb_path, false) != LSUP_OK) return NULL;
+    env->mdbstore = LSUP_mdbstore_new (mdb_path, env->default_ctx);
+    if (UNLIKELY (!env->mdbstore)) return NULL;
+    log_info ("Initialized persistent back end at %s.", mdb_path);
+
+    // RAM disk store.
+    if (LSUP_mdbstore_setup (mdb_ramdisk_path, true) != LSUP_OK)
+        return NULL;
+    env->mdbstore_ramdisk = LSUP_mdbstore_new (
+            mdb_ramdisk_path, env->default_ctx);
+    if (UNLIKELY (!env->mdbstore_ramdisk)) return NULL;
+    log_info ("Initialized RAM disk back end at %s.", mdb_ramdisk_path);
+
+    return env;
+}
+
+
+LSUP_rc
+LSUP_init (void)
+{
+    LSUP_rc rc = LSUP_NOACTION;
+
+    if (LSUP_default_env == NULL) {
+#ifdef DEBUG
+        // In debug mode, always use max logging.
+        int loglevel = LOG_TRACE;
+#else
+        char *_loglevel = getenv ("LSUP_LOGLEVEL");
+        int loglevel = (_loglevel == NULL) ? LOG_WARN : atoi (_loglevel);
+#endif
+        log_set_level (loglevel);
+
+        // Default permanent store path.
+        char *mdb_path = getenv ("LSUP_MDB_STORE_PATH");
+        if (!mdb_path) {
+            mdb_path = DEFAULT_ENV_PATH;
+            log_warn (
+                "`LSUP_MDB_STORE_PATH' environment variable is not "
+                "set. The default location %s will be used as the graph "
+                "store.", mdb_path
+            );
+        }
+        LSUP_default_env = LSUP_env_new (
+                DEFAULT_CTX_LABEL, mdb_path, MDB_RAMDISK_PATH, NULL);
+
+        if (!LSUP_default_env) rc = LSUP_DB_ERR;
+
+        atexit (LSUP_done);
+
+        rc = LSUP_OK;
+    }
+
+    return rc;
+}
+
+
+void
+LSUP_env_free (LSUP_Env *env)
+{
+    LSUP_mdbstore_free (env->mdbstore);
+    LSUP_mdbstore_free (env->mdbstore_ramdisk);
+    LSUP_buffer_free (env->default_ctx);
+    LSUP_nsmap_free (env->nsm);
+    free (env);
+}
+
+
+void
+LSUP_done (void)
+{ LSUP_env_free (LSUP_default_env); }

+ 17 - 100
src/graph.c

@@ -13,9 +13,6 @@
 // factor low.
 #define IDX_SIZE_RATIO 1.7
 
-// RAMdisk path for MDB volatile store.
-#define MDB_RAMDISK_PATH TMPDIR "/lsup_mem_graph"
-
 typedef enum KSetFlag {
     LSUP_KS_NONE        = 0,
     LSUP_KS_CHECK_CAP   = 1 << 0,
@@ -23,16 +20,8 @@ typedef enum KSetFlag {
 } KSetFlag;
 
 
-/**
- * Static handles.
- */
-static const char *default_ctx_label = "urn:lsup:default";
-static LSUP_Buffer *default_ctx = NULL;
-static LSUP_MDBStore *default_store = NULL, *default_tmp_store = NULL;
-static LSUP_NSMap *default_nsm = NULL;
-
-
 typedef struct Graph {
+    const LSUP_Env *        env;            // LSUP environment.
     LSUP_store_type         store_type;     // Back end type: in-memory or MDB.
     LSUP_Term               *uri;           // Graph "name" (URI)
     union {                                 // Back end, defined by store_type.
@@ -59,11 +48,6 @@ typedef struct GraphIterator {
 size_t LSUP_graph_size (const LSUP_Graph *gr);
 
 
-/* * * Static prototypes. * * */
-
-static inline LSUP_rc mdbstore_init();
-
-
 /* * * Post-lookup callback prototypes * * */
 
 int match_add_fn(
@@ -79,20 +63,6 @@ static LSUP_rc
 graph_iter_next_buffer (GraphIterator *it, LSUP_SerTriple *sspo);
 
 
-/* Atexit functions. */
-void ctx_cleanup()
-{
-/*@ @brief Close default LMDB environments, context, and namespace map.
- *
- * Run at exit.
- */
-    LSUP_mdbstore_free (default_store);
-    LSUP_mdbstore_free (default_tmp_store);
-    LSUP_buffer_free (default_ctx);
-    LSUP_nsmap_free (default_nsm);
-}
-
-
 static inline bool is_null_trp (const LSUP_TripleKey *trp)
 {
     return (
@@ -111,35 +81,31 @@ check_backend (LSUP_store_type be)
 /* * * GRAPH * * */
 
 Graph *
-LSUP_graph_new (const LSUP_store_type store_type)
+LSUP_graph_new_env (const LSUP_Env *env, const LSUP_store_type store_type)
 {
-    if (!check_backend (store_type)) return NULL;
+    if (UNLIKELY (!env)) return NULL;
+    if (UNLIKELY (!check_backend (store_type))) return NULL;
 
     LSUP_Graph *gr;
     MALLOC_GUARD (gr, NULL);
 
     gr->uri = LSUP_uri_new (NULL);
     gr->store_type = store_type;
-
-    if (UNLIKELY (mdbstore_init() != LSUP_OK)) return NULL;
+    gr->env = env;
+    gr->nsm = env->nsm;
 
     if (gr->store_type == LSUP_STORE_MEM) {
         gr->ht_store = LSUP_htstore_new();
         if (UNLIKELY (!gr->ht_store)) return NULL;
 
-        if (!default_nsm) default_nsm = LSUP_nsmap_new();
-        gr->nsm = default_nsm;
-
     } else if (gr->store_type == LSUP_STORE_MDB) {
-        gr->mdb_store = default_store;
-        if (UNLIKELY (!gr->mdb_store)) return NULL;
-
-        gr->nsm = NULL;
+        gr->mdb_store = env->mdbstore;
 
     } else { // LSUP_STORE_MDB_TMP
-        gr->mdb_store = default_tmp_store;
+        gr->mdb_store = env->mdbstore_ramdisk;
     }
 
+    log_debug ("Graph created.");
     return gr;
 }
 
@@ -160,7 +126,7 @@ graph_copy_contents (const LSUP_Graph *src, LSUP_Graph *dest)
 
     LSUP_GraphIterator *add_it = LSUP_graph_add_init (dest);
     while (graph_iter_next_buffer (it, &sspo) != LSUP_END) {
-        TRACE ("Inserting triple #%lu\n", LSUP_graph_iter_cur (it));
+        log_debug ("Inserting triple #%lu", LSUP_graph_iter_cur (it));
         LSUP_rc add_rc = LSUP_graph_add_iter (add_it, &sspo);
         if (LIKELY (add_rc == LSUP_OK)) rc = LSUP_OK;
         else if (add_rc < 0) return add_rc;
@@ -174,7 +140,7 @@ graph_copy_contents (const LSUP_Graph *src, LSUP_Graph *dest)
 LSUP_Graph *
 LSUP_graph_copy (const Graph *src)
 {
-    LSUP_Graph *dest = LSUP_graph_new (src->store_type);
+    LSUP_Graph *dest = LSUP_graph_new_env (src->env, src->store_type);
     if (UNLIKELY (!dest)) return NULL;
 
     LSUP_rc rc = graph_copy_contents (src, dest);
@@ -318,6 +284,7 @@ LSUP_graph_add_done (LSUP_GraphIterator *it)
     }
 
     free (it);
+    log_trace ("Done adding.");
 }
 
 
@@ -341,7 +308,7 @@ LSUP_graph_add (
 
     if (trp) {
         for (size_t i = 0; trp[i].s != NULL; i++) {
-            TRACE ("Inserting triple #%lu\n", i);
+            log_trace ("Inserting triple #%lu", i);
 
             LSUP_triple_serialize (trp + i, sspo);
             LSUP_rc db_rc = LSUP_graph_add_iter (it, sspo);
@@ -355,7 +322,7 @@ LSUP_graph_add (
     // Insert serialized triples.
     if (strp) {
         for (size_t i = 0; strp[i].s != NULL; i++) {
-            TRACE ("Inserting serialized triple #%lu\n", i);
+            log_trace ("Inserting serialized triple #%lu", i);
             LSUP_rc db_rc = LSUP_graph_add_iter (it, strp + i);
 
             if (db_rc == LSUP_OK) rc = LSUP_OK;
@@ -477,9 +444,9 @@ LSUP_graph_iter_next (GraphIterator *it, LSUP_Triple *spo)
     LSUP_rc rc = graph_iter_next_buffer (it, sspo);
 
     if (rc == LSUP_OK) {
-        LSUP_term_deserialize (sspo->s, spo->s);
-        LSUP_term_deserialize (sspo->p, spo->p);
-        LSUP_term_deserialize (sspo->o, spo->o);
+        spo->s = LSUP_term_new_from_buffer (sspo->s);
+        spo->p = LSUP_term_new_from_buffer (sspo->p);
+        spo->o = LSUP_term_new_from_buffer (sspo->o);
 
     }
 
@@ -524,53 +491,3 @@ LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo)
 
 /* * * Static functions * * */
 
-/** @brief Initialize default context and MDB environments.
- *
- * This is done only once per process.
- *
- * The ramdisk store persists after the application is closed, but will be
- * wiped clean the next time this function is called.
- */
-static inline LSUP_rc
-mdbstore_init()
-{
-    char *path;
-
-    if (UNLIKELY (!default_ctx)) {
-        LSUP_Term *default_ctx_uri = LSUP_uri_new (default_ctx_label);
-        default_ctx = LSUP_buffer_new_from_term (default_ctx_uri);
-        LSUP_term_free (default_ctx_uri);
-        atexit (ctx_cleanup);
-    }
-
-    // RAM disk store.
-    if (UNLIKELY (!default_tmp_store)) {
-        printf ("Initializing RAM disk back end.\n");
-        path = MDB_RAMDISK_PATH;
-        if (LSUP_mdbstore_setup (path, true) != LSUP_OK) return LSUP_DB_ERR;
-        default_tmp_store = LSUP_mdbstore_new (path, default_ctx);
-        if (UNLIKELY (!default_tmp_store)) return LSUP_DB_ERR;
-    }
-
-    // Permanent store.
-    if (UNLIKELY (!default_store)) {
-        printf ("Initializing persistent back end.\n");
-        // NOTE This method will only allow one persistent disk back end per
-        // application. TODO maybe later allow multiple backends if useful.
-        path = getenv ("LSUP_MDB_STORE_PATH");
-        if (!path) {
-            path = DEFAULT_ENV_PATH;
-            fprintf(
-                stderr,
-                "WARNING: `LSUP_MDB_STORE_PATH' environment variable is not "
-                "set. The default location %s will be used as the graph "
-                "store.\n", path
-            );
-        }
-        if (LSUP_mdbstore_setup (path, false) != LSUP_OK) return LSUP_DB_ERR;
-        default_store = LSUP_mdbstore_new (path, default_ctx);
-        if (UNLIKELY (!default_store)) return LSUP_DB_ERR;
-    }
-
-    return LSUP_OK;
-}

+ 5 - 5
src/store_htable.c

@@ -304,12 +304,12 @@ LSUP_htstore_add_iter (HTIterator *it, const LSUP_SerTriple *sspo)
     };
 
     // Add triple.
-    TRACE ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
+    log_debug ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
 
     TripleEntry *k_ins = NULL;
     HASH_FIND (hh, it->store->keys, spok, TRP_KLEN, k_ins);
     if (k_ins == NULL) {
-        TRACE (STR, "Triple not found, inserting.");
+        log_debug ("Triple not found, inserting.");
         MALLOC_GUARD (k_ins, LSUP_MEM_ERR);
 
         memcpy (k_ins->key, spok, TRP_KLEN);
@@ -317,14 +317,14 @@ LSUP_htstore_add_iter (HTIterator *it, const LSUP_SerTriple *sspo)
 
         it->i++;
     } else {
-        TRACE (STR, "Triple found. Skipping.");
+        log_debug ("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]);
+        //log_debug ("Indexing term key %lx\n", spok[i]);
 
         IndexEntry *ins = NULL;
         HASH_FIND (hh, it->store->idx, spok + i, KLEN, ins);
@@ -473,7 +473,7 @@ LSUP_htiter_next (HTIterator *it, LSUP_SerTriple *sspo)
                 tkey_to_strp (it->store, it->entry->key, sspo);
                 if (!sspo->s || !sspo->p || !sspo->o) return LSUP_DB_ERR;
 
-                TRACE (
+                log_debug (
                     "Found spok: {%lx, %lx, %lx}",
                     it->entry->key[0], it->entry->key[1], it->entry->key[2]
                 );

+ 66 - 82
src/store_mdb.c

@@ -10,7 +10,7 @@
 /**
  * Memory map size.
  */
-#if (defined DEBUG)
+#if (defined DEBUG || defined TESTING)
     #define DEFAULT_MAPSIZE 1<<24 // 16Mb (limit for Valgrind)
 #elif !(defined __LP64__ || defined __LLP64__) || \
         defined _WIN32 && !defined _WIN64
@@ -200,7 +200,7 @@ inline static LSUP_rc lookup_3bound(MDBIterator *it, size_t *ct);
  */
 
 LSUP_rc
-LSUP_mdbstore_setup (char *path, bool clear)
+LSUP_mdbstore_setup (const char *path, bool clear)
 {
     int rc;
 
@@ -218,12 +218,13 @@ LSUP_mdbstore_setup (char *path, bool clear)
     mdb_env_create (&env);
 
     mdb_env_set_maxdbs (env, N_DB);
-    mdb_env_open (env, path, 0, ENV_FILE_MODE);
+    RCCK (mdb_env_open (env, path, 0, ENV_FILE_MODE));
+    log_debug ("Environment opened.");
 
     MDB_txn *txn;
     mdb_txn_begin (env, NULL, 0, &txn);
     for (int i = 0; i < N_DB; i++) {
-        TRACE ("Creating DB %s", db_labels[i]);
+        log_trace ("Creating DB %s", db_labels[i]);
         MDB_dbi dbi;
         rc = mdb_dbi_open (txn, db_labels[i], db_flags[i] | MDB_CREATE, &dbi);
         if (rc != MDB_SUCCESS) return rc;
@@ -244,7 +245,7 @@ LSUP_mdbstore_new (const char *path, const LSUP_Buffer *default_ctx)
     MALLOC_GUARD (store, NULL);
 
     db_rc = mdb_env_create (&store->env);
-    TRACE ("create rc: %d", db_rc);
+    log_trace ("create rc: %d", db_rc);
 
     store->default_ctx = (
             default_ctx ?
@@ -255,8 +256,8 @@ LSUP_mdbstore_new (const char *path, const LSUP_Buffer *default_ctx)
     char *env_mapsize = getenv ("LSUP_MDB_MAPSIZE");
     if (env_mapsize == NULL) mapsize = DEFAULT_MAPSIZE;
     else sscanf (env_mapsize, "%lu", &mapsize);
-    TRACE(
-            "Setting environment map size at %s to %lu bytes.\n",
+    log_info (
+            "Setting environment map size at %s to %lu bytes.",
             path, mapsize);
     db_rc = mdb_env_set_mapsize (store->env, mapsize);
 
@@ -292,7 +293,7 @@ LSUP_mdbstore_free (LSUP_MDBStore *store)
     if (store->state & LSSTORE_OPEN) {
         const char *path;
         mdb_env_get_path (store->env, &path);
-        TRACE ("Closing MDB env at %s.\n", path);
+        log_info ("Closing MDB env at %s.", path);
         mdb_env_close (store->env);
     }
 
@@ -358,7 +359,7 @@ LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc)
         it->ck = LSUP_buffer_hash (sc);
 
         // Insert t:st for context.
-        //TRACE ("Adding context: %s", sc);
+        //log_debug ("Adding context: %s", sc);
         it->key.mv_data = &it->ck;
         it->key.mv_size = KLEN;
         it->data.mv_data = sc->addr;
@@ -401,14 +402,14 @@ LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_SerTriple *sspo)
                 it->store->txn, it->store->dbi[IDX_T_ST],
                 &it->key, &it->data, MDB_NOOVERWRITE);
         if (db_rc != MDB_SUCCESS && db_rc != MDB_KEYEXIST) {
-            fprintf (
-                    stderr, "MDB error while inserting term: %s\n",
-                    mdb_strerror(db_rc));
+            log_error (
+                    "MDB error while inserting term: %s", LSUP_strerror(db_rc));
             return LSUP_DB_ERR;
         }
     }
 
-    TRACE ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
+    log_debug ("Inserting spok: {%lx, %lx, %lx}", spok[0], spok[1], spok[2]);
+    log_debug ("Into context: %lx", it->ck);
 
     // Insert spo:c.
     it->key.mv_data = spok;
@@ -424,9 +425,8 @@ LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_SerTriple *sspo)
 
     if (db_rc == MDB_KEYEXIST) return LSUP_NOACTION;
     if (db_rc != MDB_SUCCESS) {
-        fprintf (
-                stderr, "MDB error while inserting triple: %s\n",
-                mdb_strerror(db_rc));
+        log_error (
+                "MDB error while inserting triple: %s", LSUP_strerror(db_rc));
         return LSUP_DB_ERR;
     }
 
@@ -550,6 +550,7 @@ LSUP_mdbstore_lookup(
 
     it->store = store;
     it->ck = store->default_ctx ? LSUP_buffer_hash (sc) : NULL_KEY;
+    log_debug ("Lookup context: %lx", it->ck);
 
     if (ct) *ct = 0;
 
@@ -616,9 +617,7 @@ mdbiter_next_key (LSUP_MDBIterator *it)
     if (it->rc == MDB_NOTFOUND) return LSUP_END;
 
     if (UNLIKELY (it->rc != MDB_SUCCESS)) {
-        fprintf (
-                stderr, "%s:%d [%s]: Database error: %s\n",
-                __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+        log_error ("Database error: %s", LSUP_strerror (it->rc));
         return LSUP_DB_ERR;
     }
 
@@ -628,7 +627,7 @@ mdbiter_next_key (LSUP_MDBIterator *it)
      * it->rc is set to the result of the next iteration.
      */
     it->iter_op_fn (it);
-    TRACE (
+    log_debug (
             "Found  spok: {%lx, %lx, %lx}",
             it->spok[0], it->spok[1], it->spok[2]);
 
@@ -641,9 +640,7 @@ mdbiter_next_key (LSUP_MDBIterator *it)
         int db_rc;
         db_rc = mdb_cursor_open (it->txn, it->store->dbi[IDX_SPO_C], &cur);
         if (UNLIKELY (db_rc != MDB_SUCCESS)) {
-            fprintf (
-                    stderr, "%s:%d [%s]: Database error: %s\n",
-                    __FILE__, __LINE__, __func__, mdb_strerror (db_rc));
+            log_error ("Database error: %s", LSUP_strerror (db_rc));
             return LSUP_DB_ERR;
         }
 
@@ -652,7 +649,7 @@ mdbiter_next_key (LSUP_MDBIterator *it)
         data.mv_size = KLEN;
 
         while (rc == LSUP_NORESULT) {
-            //TRACE (STR, "begin ctx loop.");
+            //log_debug ("begin ctx loop.");
             // If ctx is specified, look if the matching triple is associated
             // with it. If not, move on to the next triple.
             // The loop normally exits when a triple with matching ctx is found
@@ -664,16 +661,16 @@ mdbiter_next_key (LSUP_MDBIterator *it)
 
             if (db_rc == MDB_SUCCESS) {
                 rc = LSUP_OK;
-                //TRACE (STR, "Triple found for context.");
+                //log_debug ("Triple found for context.");
             }
 
             else if (db_rc == MDB_NOTFOUND) {
-                //TRACE (STR, "No triples found for context.");
+                //log_debug ("No triples found for context.");
                 if (it->rc == MDB_NOTFOUND) rc = LSUP_END;
                 else it->iter_op_fn (it);
 
             } else {
-                fprintf (stderr, mdb_strerror (it->rc));
+                log_error ("Database error: %s", LSUP_strerror (db_rc));
                 rc = LSUP_DB_ERR;
             }
 
@@ -751,7 +748,7 @@ LSUP_mdbstore_remove(
 
     LSUP_MDBIterator *it = LSUP_mdbstore_lookup (store, ss, sp, so, sc, ct);
     if (UNLIKELY (!it)) return LSUP_DB_ERR;
-    if (ct) TRACE ("Found %lu triples to remove.", *ct);
+    if (ct) log_debug ("Found %lu triples to remove.", *ct);
 
     while (mdbiter_next_key (it) == LSUP_OK) {
         spok_v.mv_data = it->spok;
@@ -760,7 +757,7 @@ LSUP_mdbstore_remove(
         if (rc == MDB_NOTFOUND) continue;
         if (UNLIKELY (rc != MDB_SUCCESS)) goto _remove_abort;
 
-        TRACE (
+        log_trace (
                 "Removing {%lx, %lx, %lx}",
                 it->spok[0], it->spok[1], it->spok[2]);
 
@@ -803,9 +800,7 @@ LSUP_mdbstore_remove(
 
 _remove_abort:
     mdb_txn_abort (txn);
-    fprintf (
-            stderr, "%s:%d [%s]: Database error: %s\n",
-            __FILE__, __LINE__, __func__, mdb_strerror (rc));
+    log_error ("Database error: %s", LSUP_strerror (rc));
 
     return rc;
 }
@@ -829,11 +824,11 @@ index_triple(
     LSUP_rc rc = LSUP_NOACTION;
     MDB_val v1, v2;
 
-    TRACE ("Indexing triple: %lx %lx %lx\n", spok[0], spok[1], spok[2]);
+    log_trace ("Indexing triple: %lx %lx %lx", spok[0], spok[1], spok[2]);
 
     // Index c:spo.
     if (op == OP_REMOVE) {
-        TRACE (STR, "Indexing op: REMOVE");
+        log_trace ("Indexing op: REMOVE");
         if (ck != NULL_KEY) {
             MDB_cursor *cur;
 
@@ -854,7 +849,7 @@ index_triple(
         }
 
     } else if (op == OP_ADD) {
-        TRACE (STR, "Indexing op: ADD");
+        log_trace ("Indexing op: ADD");
         if (ck != NULL_KEY) {
             v1.mv_data = &ck;
             v1.mv_size = KLEN;
@@ -913,12 +908,10 @@ 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),
+            log_trace ("Indexing in %s: ", db_labels[lookup_indices[i]]);
+            log_trace (
+                    "%lx: %lx %lx", *(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);
 
@@ -926,12 +919,10 @@ 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),
+            log_trace ("Indexing in %s: ", db_labels[lookup_indices[i + 3]]);
+            log_trace (
+                    "%lx %lx: %lx", *(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);
 
@@ -974,21 +965,21 @@ it_next_1bound (MDBIterator *it)
     it->spok[it->term_order[1]] = lu_dset[it->i][0];
     it->spok[it->term_order[2]] = lu_dset[it->i][1];
 
-    TRACE(
+    log_trace (
             "Composed triple: {%lx %lx %lx}",
             it->spok[0], it->spok[1], it->spok[2]);
 
     // Ensure next block within the same page is not beyond the last.
     if (it->i < it->data.mv_size / DBL_KLEN - 1) {
         it->i ++;
-        //TRACE ("Increasing page cursor to %lu.", it->i);
-        //TRACE ("it->rc: %d", it->rc);
+        //log_debug ("Increasing page cursor to %lu.", it->i);
+        //log_debug ("it->rc: %d", it->rc);
 
     } else {
         // If the last block in the page is being yielded,
         // move cursor to beginning of next page.
         it->i = 0;
-        //TRACE ("Reset page cursor to %lu.", it->i);
+        //log_debug ("Reset page cursor to %lu.", it->i);
         it->rc = mdb_cursor_get (
                 it->cur, &it->key, &it->data, MDB_NEXT_MULTIPLE);
     }
@@ -1040,13 +1031,14 @@ it_next_3bound (MDBIterator *it)
 inline static LSUP_rc
 lookup_0bound (MDBIterator *it, size_t *ct)
 {
+    log_debug ("Looking up 0 bound terms.");
+    log_debug ("Lookup context: %lx", it->ck);
+
     if (it->store->txn) it->txn = it->store->txn;
     else {
         it->rc = mdb_txn_begin (it->store->env, NULL, MDB_RDONLY, &it->txn);
         if (it->rc != MDB_SUCCESS) {
-            fprintf (
-                    stderr, "%s:%d [%s]: Database error: %s\n",
-                    __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+            log_error ("Database error: %s", LSUP_strerror (it->rc));
             return LSUP_DB_ERR;
         }
     }
@@ -1073,21 +1065,25 @@ lookup_0bound (MDBIterator *it, size_t *ct)
 
             *ct = stat.ms_entries;
         }
-        TRACE ("Found %lu keys.", *ct);
+        log_debug ("Found %lu keys.", *ct);
     }
 
-    mdb_cursor_open (it->txn, it->store->dbi[IDX_SPO_C], &it->cur);
+    it->rc = mdb_cursor_open (it->txn, it->store->dbi[IDX_SPO_C], &it->cur);
+    if (it->rc != MDB_SUCCESS) {
+        log_error ("Database error: %s", LSUP_strerror (it->rc));
+        return LSUP_DB_ERR;
+    }
 
     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) {
-        fprintf (
-                stderr, "%s:%d [%s]: Database error: %s\n",
-                __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+        log_error ("Database error: %s", LSUP_strerror (it->rc));
         return LSUP_DB_ERR;
     }
 
@@ -1100,7 +1096,7 @@ lookup_1bound (uint8_t idx0, MDBIterator *it, size_t *ct)
 {
     it->term_order = (const uint8_t*)lookup_ordering_1bound[idx0];
 
-    TRACE ("Looking up 1 bound term: %lx\n", it->luk[0]);
+    log_debug ("Looking up 1 bound term: %lx", it->luk[0]);
 
     if (!it->txn) {
         if (it->store->txn) it->txn = it->store->txn;
@@ -1108,9 +1104,7 @@ lookup_1bound (uint8_t idx0, MDBIterator *it, size_t *ct)
             it->rc = mdb_txn_begin (
                     it->store->env, NULL, MDB_RDONLY, &it->txn);
             if (it->rc != MDB_SUCCESS) {
-                fprintf (
-                        stderr, "%s:%d [%s]: Database error: %s",
-                        __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+                log_error ("Database error: %s", LSUP_strerror (it->rc));
                 return LSUP_DB_ERR;
             }
         }
@@ -1125,7 +1119,7 @@ lookup_1bound (uint8_t idx0, MDBIterator *it, size_t *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) {
-            TRACE ("Counting in context: %lx\n", it->ck);
+            log_debug ("Counting in context: %lx", it->ck);
             MDBIterator *ct_it;
             MALLOC_GUARD (ct_it, LSUP_MEM_ERR);
 
@@ -1146,7 +1140,7 @@ lookup_1bound (uint8_t idx0, MDBIterator *it, size_t *ct)
 
             while (LSUP_mdbiter_next (ct_it, NULL) != LSUP_END) {
                 (*ct)++;
-                TRACE ("Counter increased to %lu.", *ct);
+                log_debug ("Counter increased to %lu.", *ct);
             }
 
             // Free the counter iterator without freeing the shared txn.
@@ -1167,9 +1161,7 @@ lookup_1bound (uint8_t idx0, MDBIterator *it, size_t *ct)
         it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
 
     if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
-        fprintf (
-                stderr, "%s:%d [%s]: Database error: %s",
-                __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+        log_error ("Database error: %s", LSUP_strerror (it->rc));
         return LSUP_DB_ERR;
     }
 
@@ -1203,8 +1195,8 @@ lookup_2bound(uint8_t idx0, uint8_t idx1, MDBIterator *it, size_t *ct)
                 luk2_offset = 0;
             }
             dbi = it->store->dbi[lookup_indices[i + 3]];
-            TRACE(
-                    "Looking up 2 bound in %s\n",
+            log_debug(
+                    "Looking up 2 bound in %s",
                     db_labels[lookup_indices[i + 3]]);
 
             break;
@@ -1212,7 +1204,7 @@ lookup_2bound(uint8_t idx0, uint8_t idx1, MDBIterator *it, size_t *ct)
     }
 
     if (dbi == 0) {
-        TRACE(
+        log_debug(
                 "Values %d and %d not found in lookup keys.",
                 idx0, idx1);
         return LSUP_VALUE_ERR;
@@ -1229,9 +1221,7 @@ lookup_2bound(uint8_t idx0, uint8_t idx1, MDBIterator *it, size_t *ct)
             it->rc = mdb_txn_begin (
                     it->store->env, NULL, MDB_RDONLY, &it->txn);
             if (it->rc != MDB_SUCCESS) {
-                fprintf (
-                        stderr, "%s:%d [%s]: Database error: %s",
-                        __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+                log_error ("Database error: %s", LSUP_strerror (it->rc));
                 return LSUP_DB_ERR;
             }
         }
@@ -1283,9 +1273,7 @@ lookup_2bound(uint8_t idx0, uint8_t idx1, MDBIterator *it, size_t *ct)
         it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_MULTIPLE);
 
     if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
-        fprintf (
-                stderr, "%s:%d [%s]: Database error: %s\n",
-                __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+        log_error ("Database error: %s", LSUP_strerror (it->rc));
         return LSUP_DB_ERR;
     }
 
@@ -1296,7 +1284,7 @@ lookup_2bound(uint8_t idx0, uint8_t idx1, MDBIterator *it, size_t *ct)
 inline static LSUP_rc
 lookup_3bound (MDBIterator *it, size_t *ct)
 {
-    TRACE(
+    log_debug (
             "Looking up 3 bound: {%lx, %lx, %lx}",
             it->luk[0], it->luk[1], it->luk[2]);
 
@@ -1304,9 +1292,7 @@ lookup_3bound (MDBIterator *it, size_t *ct)
     else {
         it->rc = mdb_txn_begin (it->store->env, NULL, MDB_RDONLY, &it->txn);
         if (it->rc != MDB_SUCCESS) {
-            fprintf (
-                    stderr, "%s:%d [%s]: Database error: %s\n",
-                    __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+            log_error ("Database error: %s", LSUP_strerror (it->rc));
             return LSUP_DB_ERR;
         }
     }
@@ -1332,9 +1318,7 @@ lookup_3bound (MDBIterator *it, size_t *ct)
     it->rc = mdb_cursor_get (it->cur, &it->key, &it->data, MDB_GET_BOTH);
 
     if (it->rc != MDB_SUCCESS && it->rc != MDB_NOTFOUND) {
-        fprintf (
-                stderr, "%s:%d [%s]: Database error: %s\n",
-                __FILE__, __LINE__, __func__, mdb_strerror (it->rc));
+        log_error ("Database error: %s", LSUP_strerror (it->rc));
         return LSUP_DB_ERR;
     }
 

+ 3 - 3
src/term.c

@@ -47,7 +47,7 @@ LSUP_Term *
 LSUP_term_new_from_buffer (const LSUP_Buffer *sterm)
 {
     LSUP_Term *term;
-    MALLOC_GUARD (term, NULL);
+    CALLOC_GUARD (term, NULL);
 
     if (UNLIKELY (LSUP_term_deserialize (sterm, term) != LSUP_OK)) {
         free (term);
@@ -62,7 +62,7 @@ LSUP_Buffer *
 LSUP_buffer_new_from_term (const LSUP_Term *term)
 {
     LSUP_Buffer *sterm;
-    MALLOC_GUARD (sterm, NULL);
+    CALLOC_GUARD (sterm, NULL);
     sterm->addr = NULL;
 
     if (LSUP_term_serialize (term, sterm) != LSUP_OK) {
@@ -199,7 +199,7 @@ LSUP_term_serialize (const LSUP_Term *term, LSUP_Buffer *sterm)
         }
     }
 
-    //TRACE ("Serialized term size: %lu", size);
+    //log_debug ("Serialized term size: %lu", size);
     LSUP_buffer_init (sterm, size, NULL);
 
     // Copy type.

+ 1 - 1
test.c

@@ -10,7 +10,7 @@ int main(int argc, char **argv) {
     // Set env variable to test path.
     putenv ("LSUP_MDB_STORE_PATH=" TMPDIR "/lsup_test_mdb");
     // Clear out database from previous test.
-    rm_r (getenv ("LSUP_MDB_STORE_PATH"));
+    LSUP_init();
 
     int rc;
 

+ 2 - 2
test/test_codec_nt.c

@@ -222,7 +222,7 @@ test_decode_nt_graph()
     EXPECT_INT_EQ (LSUP_graph_size (gr), 7);
 
     for (int i = 0; i < 7; i++) {
-        printf("Checking triple #%d... ", i);
+        log_info ("Checking triple #%d... ", i);
         EXPECT_INT_EQ (LSUP_graph_contains (gr, trp + i), 1);
         printf("OK.\n");
     }
@@ -243,7 +243,7 @@ test_decode_nt_bad_graph()
     char *err;
     EXPECT_INT_EQ (nt_codec.decode_graph (input, &gr, &ct, &err), LSUP_VALUE_ERR);
 
-    TRACE ("Error: %s", err);
+    log_info ("Error: %s", err);
     ASSERT (strstr (err, "`dc:title") != NULL, "Wrong error string report!");
     ASSERT (strstr (err, "line 3") != NULL, "Wrong error line report!");
     ASSERT (strstr (err, "character 16") != NULL, "Wrong error char report!");

+ 7 - 4
test/test_graph.c

@@ -2,7 +2,7 @@
 #include "graph.h"
 #include "assets/triples.h"
 
-#define N_LUT 12
+#define N_LUT 13
 
 static int
 _graph_new (LSUP_store_type type)
@@ -40,7 +40,7 @@ _graph_add (LSUP_store_type type)
     EXPECT_INT_EQ (LSUP_graph_size (gr), 8);
 
     for (int i = 0; i < sizeof (trp); i++) {
-        printf ("checking triple #%d... ", i);
+        log_info ("checking triple #%d... ", i);
         ASSERT (LSUP_graph_contains (gr, trp + i), "Triple not in graph!");
         printf ("OK.\n");
     }
@@ -64,6 +64,7 @@ _graph_lookup (LSUP_store_type type)
 
     // Lookup triples.
     LSUP_Term *lu_trp[N_LUT][3] = {
+        {NULL, NULL, NULL},                 // 8 matches
         {trp[0].s, NULL, NULL},             // 5 matches
         {NULL, trp[2].p, NULL},             // 3 matches
         {NULL, NULL, trp[5].o},             // 2 matches
@@ -79,11 +80,12 @@ _graph_lookup (LSUP_store_type type)
     };
 
     // Lookup result counts.
-    size_t lu_ct[N_LUT] = { 5, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0 };
+    size_t lu_ct[N_LUT] = { 8, 5, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0 };
 
     /* TODO
     // Index of lookup matches from trp.
     size_t lu_match[N_LUT][5] = {
+        {0, 1, 2, 3, 4, 5, 6, 7},
         {0, 3, 4, 5, 7},
         {2, 4, 7},
         {5, 7},
@@ -93,6 +95,7 @@ _graph_lookup (LSUP_store_type type)
 
     // Index of lookup non-matches from trp.
     size_t lu_no_match[N_LUT][8] = {
+        {},
         {1, 2, 6},
         {0, 1, 3, 5, 6},
         {0, 1, 2, 3, 4, 6},
@@ -112,7 +115,7 @@ _graph_lookup (LSUP_store_type type)
     EXPECT_INT_EQ (LSUP_graph_size (gr), 8);
 
     for (int i = 0; i < N_LUT; i++) {
-        printf ("Checking tiple #%d on %d... ", i, type);
+        log_info ("Checking triple #%d on %d... ", i, type);
         LSUP_GraphIterator *it = LSUP_graph_lookup (
                 gr, lu_trp[i][0], lu_trp[i][1], lu_trp[i][2], &ct);
         if (type != LSUP_STORE_MEM) // TODO not implemented in htable.

+ 1 - 1
test/test_store_ht.c

@@ -58,7 +58,7 @@ static int test_htstore()
     LSUP_SerTriple *sspo = STRP_DUMMY;
     for (int i = 0; i < NUM_TRP; i++) {
         size_t ct = 0;
-        TRACE ("Testing triple lookup #%d.", i);
+        log_info ("Testing triple lookup #%d.", i);
 
         LSUP_HTIterator *it = LSUP_htstore_lookup(
                 store, lut[i][0], lut[i][1], lut[i][2]);

+ 2 - 2
test/test_store_mdb.c

@@ -75,7 +75,7 @@ static int test_triple_store()
 
     for (int i = 0; i < 12; i++) {
         size_t ct;
-        TRACE ("Testing triple lookup #%d.\n", i);
+        log_info ("Testing triple lookup #%d.", i);
 
         LSUP_MDBIterator *it = LSUP_mdbstore_lookup(
                 store, lut[i][0], lut[i][1], lut[i][2], luc[i], &ct);
@@ -279,7 +279,7 @@ static int test_quad_store()
         printf ("}\n");
         */
 
-        printf ("Checking triple #%d...", i);
+        log_info ("Checking triple #%d...", i);
         LSUP_MDBIterator *it = LSUP_mdbstore_lookup(
                 store, lut[i][0], lut[i][1], lut[i][2], luc[i], &ct);
         ASSERT (it != NULL, "Lookup error!");

+ 11 - 11
test/test_term.c

@@ -7,15 +7,15 @@ static int test_term_new()
     char *datatype = "xsd:string";
     char *lang = "en-US";
 
-    TRACE (STR, "Test term, heap-allocated.");
+    log_info ("Test term, heap-allocated.");
     LSUP_Term *term = LSUP_term_new (LSUP_TERM_LITERAL, data, datatype, lang);
 
-    TRACE ("Term data: %s", term->data);
+    log_info ("Term data: %s", term->data);
     EXPECT_STR_EQ (term->data, data);
     EXPECT_STR_EQ (term->datatype, datatype);
     EXPECT_STR_EQ (term->lang, lang);
 
-    TRACE (STR, "Reset term.\n");
+    log_info ("Reset term.\n");
 
     char *uri_data = "urn:id:2144564356";
     LSUP_term_init (term, LSUP_TERM_URI, uri_data, NULL, NULL);
@@ -37,33 +37,33 @@ static int test_term_serialize_deserialize()
     LSUP_Term *dsterm = TERM_DUMMY;
 
     LSUP_term_serialize (uri, sterm);
-    TRACE ("%s", "Serialized URI: ");
+    log_info ("%s", "Serialized URI: ");
     LSUP_buffer_print (sterm);
-    TRACE ("%s", "\n");
+    log_info ("%s", "\n");
     LSUP_term_deserialize (sterm, dsterm);
     ASSERT (LSUP_term_equals (dsterm, uri), "URI serialization error!");
     LSUP_term_free (uri);
 
     LSUP_term_serialize (lit, sterm);
-    TRACE ("%s", "Serialized literal: ");
+    log_info ("%s", "Serialized literal: ");
     LSUP_buffer_print (sterm);
-    TRACE ("%s", "\n");
+    log_info ("%s", "\n");
     LSUP_term_deserialize (sterm, dsterm);
     ASSERT (LSUP_term_equals (dsterm, lit), "lit serialization error!");
     LSUP_term_free (lit);
 
     LSUP_term_serialize (tlit, sterm);
-    TRACE ("%s", "Serialized typed literal: ");
+    log_info ("%s", "Serialized typed literal: ");
     LSUP_buffer_print (sterm);
-    TRACE ("%s", "\n");
+    log_info ("%s", "\n");
     LSUP_term_deserialize (sterm, dsterm);
     ASSERT (LSUP_term_equals (dsterm, tlit), "tlit serialization error!");
     LSUP_term_free (tlit);
 
     LSUP_term_serialize (tllit, sterm);
-    TRACE ("%s", "Serialized typed and language-tagged URI: ");
+    log_info ("%s", "Serialized typed and language-tagged URI: ");
     LSUP_buffer_print (sterm);
-    TRACE ("%s", "\n");
+    log_info ("%s", "\n");
     LSUP_term_deserialize (sterm, dsterm);
     ASSERT (LSUP_term_equals (dsterm, tllit), "URI serialization error!");
     LSUP_term_free (tllit);