Procházet zdrojové kódy

Refactor term API:

- Move buffer, term and triple members to avoid circular deps.
* Use tpl to serialize and deserialize.
* Dereference data type and lang in a global hash map.
* Remove serialize & deserialize methods for term and triple.
* Add store flags.
Stefano Cossu před 3 roky
rodič
revize
4551b041d5

+ 3 - 0
.gitmodules

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

+ 16 - 3
Makefile

@@ -1,13 +1,18 @@
 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
+INCLUDE_BASE = . -Iinclude -Iext/xxHash -Iext/openldap/libraries/liblmdb \
+	-Iext/tpl/src -Iext/uthash/src -Iext/log/src
+INCLUDE = -I$(INCLUDE_BASE)
 LIB = -luuid -lpthread
-SRC = ext/xxHash/xxhash.c ext/openldap/libraries/liblmdb/mdb.c \
+SRC = ext/xxHash/xxhash.c ext/tpl/src/tpl.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
 
+DEPS := $(shell echo "${INCLUDE_BASE}" | sed -e 's/ -I/,/g')
+DOCS = docs
+
 .PHONY: build_parsers lint profile
 
 default: test
@@ -68,4 +73,12 @@ profile: build_parsers
 		-o bin/profile
 
 
+# Build a visual dependency graph.
+# Requires cinclude2dot (https://www.flourish.org/cinclude2dot) and Graphviz.
+depgraph: src/* include/*
+	cinclude2dot --merge=module --include=$(DEPS) \
+		--exclude='test|ext' >| $(DOCS)/dev/deps.dot
+	dot $(DOCS)/dev/deps.dot -Tpdf >| $(DOCS)/dev/deps.pdf
+
+
 build_parsers:; $(MAKE) -C $(CODEC_DIR)

+ 9 - 8
cpython/py_graph.h

@@ -394,10 +394,9 @@ Graph_add (PyObject *self, PyObject *triples)
         return NULL;
     }
 
-    PyObject *trp_obj = NULL;
+    PyObject *trp_obj;
     int rc = 0;
     size_t i;
-    LSUP_SerTriple *sspo = LSUP_striple_new (BUF_DUMMY, BUF_DUMMY, BUF_DUMMY);
     LSUP_GraphIterator *it = LSUP_graph_add_init (
             ((GraphObject *)self)->ob_struct);
 
@@ -406,28 +405,30 @@ Graph_add (PyObject *self, PyObject *triples)
             PyErr_SetString (
                     PyExc_ValueError, "Object is not a triple.");
             rc = -1;
-            goto finalize;
+            goto finally;
         }
 
         log_trace ("Inserting triple #%lu", i);
 
-        LSUP_triple_serialize (((TripleObject *)trp_obj)->ob_struct, sspo);
+        LSUP_BufferTriple *sspo = LSUP_btriple_from_triple (
+                ((TripleObject *) trp_obj)->ob_struct);
         LSUP_rc db_rc = LSUP_graph_add_iter (it, sspo);
 
+        LSUP_btriple_free (sspo);
+
         if (db_rc == LSUP_OK) rc = LSUP_OK;
         if (UNLIKELY (db_rc < 0)) {
             rc = -1;
-            goto finalize;
+            goto finally;
         }
     }
 
-finalize:
+finally:
     LSUP_graph_add_done (it);
-    LSUP_striple_free (sspo);
 
     PyObject *ret = PyLong_FromSize_t (LSUP_graph_iter_cur (it));
 
-    if (rc == 0) {
+    if (rc == LSUP_OK) {
         Py_INCREF (ret);
         return ret;
     }

+ 1 - 1
cpython/py_lsup_rdf.c

@@ -66,7 +66,7 @@ PyInit_term()
 
     if (
         PyModule_AddIntConstant (m, "TERM_UNDEFINED", LSUP_TERM_UNDEFINED) < 0
-        || PyModule_AddIntConstant (m, "TERM_URI", LSUP_TERM_URI) < 0
+        || PyModule_AddIntConstant (m, "TERM_IRIREF", LSUP_TERM_IRIREF) < 0
         || PyModule_AddIntConstant (m, "TERM_BNODE", LSUP_TERM_BNODE) < 0
         || PyModule_AddIntConstant (m, "TERM_LITERAL", LSUP_TERM_LITERAL) < 0
     ) return NULL;

+ 1 - 1
cpython/py_term.h

@@ -28,7 +28,7 @@ Term_init (TermObject *self, PyObject *args, PyObject *kwargs)
             &term_type, &data, &datatype, &lang))
         return -1;
 
-    self->ob_struct = LSUP_term_new ((LSUP_term_type)term_type, data, datatype, lang);
+    self->ob_struct = LSUP_term_new ((LSUP_TermType) term_type, data, datatype, lang);
     if (!self->ob_struct) {
         PyErr_SetString (PyExc_ValueError, "Could not create term.");
         return -1;

+ 1 - 0
ext/tpl

@@ -0,0 +1 @@
+Subproject commit 6569fcc176d6ec0a91776d5d0ba712f753b84466

+ 82 - 10
include/buffer.h

@@ -31,6 +31,19 @@ typedef struct LSUP_Buffer {
 } LSUP_Buffer;
 
 
+/** @brief Triple of byte buffers.
+ *
+ * This is a generic data triple. Store implementations should handle this
+ * data type rather than RDF terms and triples. Conversion to/from RDF terms
+ * and triples is done in the term and triple modules.
+ */
+typedef struct buffer_triple_t {
+    LSUP_Buffer *s;
+    LSUP_Buffer *p;
+    LSUP_Buffer *o;
+} LSUP_BufferTriple;
+
+
 /** Initialize or reuse a buffer handle.
  *
  * The handle must have been created with #LSUP_buffer_new*().
@@ -100,16 +113,7 @@ void LSUP_buffer_free (LSUP_Buffer *buf);
  */
 inline LSUP_Key
 LSUP_buffer_hash (const LSUP_Buffer *buf)
-{ return (buf == NULL) ? NULL_KEY : XXH64(buf->addr, buf->size, HASH_SEED); }
-
-
-/** @brief Combine hash values.
- *
- * TODO Adapt to different sizes of LSUP_Key.
- */
-inline LSUP_Key LSUP_btriple_hash (
-        const LSUP_Buffer *b1, const LSUP_Buffer *b2)
-{ return XXH64 (b2->addr, b2->size, XXH64 (b1->addr, b1->size, HASH_SEED)); }
+{ return (buf == NULL) ? NULL_KEY : XXH64 (buf->addr, buf->size, HASH_SEED); }
 
 
 /** @brief Print a byte string of a given length in a human-readable format.
@@ -156,4 +160,72 @@ inline bool LSUP_buffer_eq (const LSUP_Buffer *buf1, const LSUP_Buffer *buf2)
     return (LSUP_buffer_cmp (buf1, buf2) == 0) ? true : false;
 }
 
+
+/*
+ * Buffer triples.
+ */
+
+LSUP_BufferTriple *
+LSUP_btriple_new(LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o);
+
+
+/** @brief Initialize internal term pointers in a heap-allocated buffer triple.
+ *
+ * The triple must be freed with #LSUP_btriple_free().
+ *
+ * @param sspo[in] Serialized triple pointer to initialize.
+ */
+LSUP_rc
+LSUP_btriple_init (
+        LSUP_BufferTriple *sspo, LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o);
+
+
+/** @brief Free the internal pointers of a buffer triple.
+ *
+ * @param sspo[in] Buffer triple to be freed.
+ */
+void
+LSUP_btriple_done (LSUP_BufferTriple *sspo);
+
+
+/** @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.
+ */
+void
+LSUP_btriple_free (LSUP_BufferTriple *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_btriple_free_shallow (LSUP_BufferTriple *sspo);
+
+
+/** @brief Hash a buffer triple.
+ *
+ * TODO This doesn't handle blank nodes correctly.
+ */
+inline LSUP_Key
+LSUP_btriple_hash (const LSUP_BufferTriple *strp)
+{
+    return XXH64 (
+        strp->s->addr, strp->s->size,
+        XXH64 (
+            strp->p->addr, strp->p->size,
+            XXH64 (strp->o->addr, strp->o->size, HASH_SEED)
+        )
+    );
+}
+
+
+#define STRP_DUMMY LSUP_btriple_new (NULL, NULL, NULL)
+
 #endif

+ 19 - 2
include/environment.h

@@ -6,10 +6,21 @@
 #ifndef _LSUP_ENVIRONMENT_H
 #define _LSUP_ENVIRONMENT_H
 
-#include "term.h"
+#include "uthash.h"
+
 #include "store_mdb.h"
-#include "namespace.h"
 
+
+/*
+ * Data types.
+ */
+
+/** @brief Environment structure containing "global" LSUP variables.
+ *
+ * Multiple environments may be opened within one program. One environment,
+ * the default one, must always be opened before performing any operation with
+ * the library.
+ */
 typedef struct env_t {
     LSUP_Buffer *       default_ctx;            // Default context URI.
     LSUP_MDBStore *     mdb_store;              // MDB permanent store handle.
@@ -63,4 +74,10 @@ LSUP_env_done (LSUP_Env *env);
  */
 void LSUP_done (void);
 
+LSUP_rc
+LSUP_env_push_id (LSUP_Env *env, const uint32_t key, const char *data);
+
+const char *
+LSUP_env_get_id (LSUP_Env *env, const uint32_t key);
+
 #endif /* _LSUP_ENVIRONMENT_H */

+ 2 - 4
include/graph.h

@@ -1,8 +1,6 @@
 #ifndef _LSUP_GRAPH_H
 #define _LSUP_GRAPH_H
 
-#include "triple.h"
-#include "namespace.h"
 #include "environment.h"
 
 /*
@@ -252,7 +250,7 @@ LSUP_graph_add_init (LSUP_Graph *gr);
  */
 LSUP_rc
 LSUP_graph_add_iter (
-        LSUP_GraphIterator *it, const LSUP_SerTriple *sspo);
+        LSUP_GraphIterator *it, const LSUP_BufferTriple *sspo);
 
 
 /** @brief Finalize an add iteration loop and free the iterator.
@@ -282,7 +280,7 @@ LSUP_graph_add_done (LSUP_GraphIterator *it);
 LSUP_rc
 LSUP_graph_add (
         LSUP_Graph *gr, const LSUP_Triple trp[],
-        const LSUP_SerTriple strp[], size_t *inserted);
+        const LSUP_BufferTriple strp[], size_t *inserted);
 
 
 /** @brief Insert RDF triples into a graph.

+ 17 - 0
include/store.h

@@ -0,0 +1,17 @@
+#ifndef _LSUP_STORE_H
+#define _LSUP_STORE_H
+
+/*
+ * Store feature flags.
+ *
+ * NOTE: LSUP_STORE_PERM need only be set by an implementation based on whether
+ * its path is on a default temporary dir (e.g. MDB_RAMDISK_PATH). If this flag
+ * is not set, it means the data will be cleared before the next execution of
+ * the program. However, its being set does not guarantee the persistence of
+ * the medium (i.e. a "permanent" store may have been created ad hoc on a
+ * tempfs).
+ */
+#define     LSUP_STORE_PERM     1<<0    // Store is on a permanent location.
+#define     LSUP_STORE_CTX      1<<1    // Store supports contexts (quads).
+
+#endif  /* _LSUP_STORE_H */

+ 2 - 2
include/store_htable.h

@@ -80,7 +80,7 @@ LSUP_htstore_add_init (LSUP_HTStore *store);
  * @param[in] sspo Triples to add, serialized into buffer triples.
  */
 LSUP_rc
-LSUP_htstore_add_iter (LSUP_HTIterator *it, const LSUP_SerTriple *sspo);
+LSUP_htstore_add_iter (LSUP_HTIterator *it, const LSUP_BufferTriple *sspo);
 
 
 void
@@ -107,6 +107,6 @@ size_t
 LSUP_htiter_cur (LSUP_HTIterator *it);
 
 LSUP_rc
-LSUP_htiter_next (LSUP_HTIterator *it, LSUP_SerTriple *sspo);
+LSUP_htiter_next (LSUP_HTIterator *it, LSUP_BufferTriple *sspo);
 
 #endif  // _LSUP_STORE_HTABLE_H

+ 13 - 5
include/store_mdb.h

@@ -2,7 +2,7 @@
  *
  * @brief LMDB graph store backend.
  *
- * This module stores triples in a LMDB embedded store, optionally organized
+ * This module stores triples in a LMDB embedded store, organized
  * into named graphs. The store is optimized and indexed for fast lookup of any
  * number of bound terms.
  *
@@ -27,6 +27,14 @@
 #include "namespace.h"
 
 
+// FIXME find a better cross-platform path.
+#define DEFAULT_ENV_PATH "./mdb_store"
+
+// RAMdisk path for MDB volatile store.
+#define MDB_RAMDISK_PATH TMPDIR "/lsup_mem_graph"
+#include "store.h"
+
+
 typedef char DbLabel[8];
 typedef struct mdbstore_t LSUP_MDBStore;
 typedef struct mdbstore_iter_t LSUP_MDBIterator;
@@ -144,7 +152,7 @@ LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc);
  *  already existed; LSUP_DB_ERR if an MDB error occurred.
  */
 LSUP_rc
-LSUP_mdbstore_add_iter (LSUP_MDBIterator *it, const LSUP_SerTriple *sspo);
+LSUP_mdbstore_add_iter (LSUP_MDBIterator *it, const LSUP_BufferTriple *sspo);
 
 
 /** @brief Finalize an add loop and free iterator.
@@ -194,7 +202,7 @@ LSUP_mdbstore_add_abort (LSUP_MDBIterator *it);
  */
 LSUP_rc LSUP_mdbstore_add(
         LSUP_MDBStore *store, const LSUP_Buffer *sc,
-        const LSUP_SerTriple strp[], const size_t ct, size_t *inserted);
+        const LSUP_BufferTriple strp[], const size_t ct, size_t *inserted);
 
 
 /** @brief Delete triples by pattern matching.
@@ -264,7 +272,7 @@ LSUP_mdbstore_lookup(
  *
  * @param it[in] Opaque iterator handle obtained with #LSUP_mdbstore_lookup.
  *
- * @param sspo[out] #LSUP_SerTriple to be populated with three serialized terms
+ * @param sspo[out] #LSUP_BufferTriple to be populated with three serialized terms
  * if found, NULL if not found. Internal callers (e.g. counters) may pass NULL
  * if they don't need the serialized triples.
  *
@@ -272,7 +280,7 @@ LSUP_mdbstore_lookup(
  * found; LSUP_DB_ERR if a MDB_* error occurred.
  */
 LSUP_rc LSUP_mdbiter_next (
-        LSUP_MDBIterator *it, LSUP_SerTriple *sspo, LSUP_Buffer **ctx);
+        LSUP_MDBIterator *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx);
 
 
 /** @brief Iterator's internal counter.

+ 105 - 69
include/term.h

@@ -4,58 +4,113 @@
 #include <assert.h>
 #include <regex.h>
 
+#include "uthash.h"
+
 #include "buffer.h"
 #include "namespace.h"
 
-#define LANG_SIZE 8 // Size in chars of lang tag
-
 // "NULL" triple, a value that is never user-provided. Used to fill deleted
 // triples in a keyset.
 #define NULL_TRP {NULL_KEY, NULL_KEY, NULL_KEY}
 
 #define UUID4_URN_SIZE UUIDSTR_SIZE + 10
 
-
-typedef XXH64_hash_t LSUP_TermHash64;
-typedef char langtag[LANG_SIZE];
-typedef char LSUP_term_type;
-
+/*
+ * Term types.
+ */
+/* Undefined placeholder or result of an error. Invalid for most operations. */
 #define LSUP_TERM_UNDEFINED      0
-#define LSUP_TERM_URI            1
+/* IRI reference. */
+#define LSUP_TERM_IRIREF         1
+/* Blank node. */
 #define LSUP_TERM_BNODE          2
+/* Literal without language tag. */
 #define LSUP_TERM_LITERAL        3
+/* Language-tagged string literal. */
+#define LSUP_TERM_LT_LITERAL     4
+
+/*
+ * In-term identifier types.
+ */
+/* Data type IRI. */
+#define LSUP_ID_DATATYPE        10
+/* Language tag string. */
+#define LSUP_ID_LANG            11
+/* Temporary blank node ID. TODO implement. */
+#define LSUP_ID_BNODE           12
 
 /** @brief Default data type for untyped literals.
  */
 #define DEFAULT_DTYPE           "http://www.w3.org/2001/XMLSchema#string"
 
+/** @brief URI parsing regular expression. Conforms to RFC3986.
+ */
+#define LSUP_URI_REGEX_STR \
+    "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"
+
+
+/*
+ * Data types.
+ */
+
+typedef XXH64_hash_t LSUP_Hash64;
+typedef char LSUP_TermType;
+
 
 typedef struct term_t {
-    char *              data;       // URI or literal value, or BNode label.
-    char *              datatype;   // Data type for literals.
-    // This language variable currently supports a 2-digit ISO 639 language
+    char *              data;       // URI, literal value, or BNode label.
     union {
-        langtag         lang;       // Language tag. This variable currently
-                                    // supports a 2-digit ISO 639 language
-                                    // code and a 2-character ISO 3166-1
-                                    // country code, separated by a hyphen. See
-                                    // https://tools.ietf.org/html/bcp47#section-2.1
-        uint64_t        bnode_id;   // Blank node ID. TODO implement.
+        uint32_t        datatype;   // Data type hash for LSUP_TERM_LITERAL.
+        uint32_t        lang;       // Lang tag hash for LSUP_TERM_LT_LITERAL.
+        uint32_t        bnode_id;   // Blank node ID. TODO implement.
     };
-    LSUP_term_type      type;       // Term type.
+    LSUP_TermType      type;       // Term type.
 } LSUP_Term;
 
 
+/** @brief Hash cache for lang tags and data types.
+ */
+typedef struct id_cache_t {
+    uint32_t            key;
+    char *              data;
+    UT_hash_handle      hh;
+} IDCache;
+
+
+/*
+ * Extern variables.
+ */
+
+/** @brief Global ID cache.
+ *
+ * Map of internal term identifiers, such as literal data types, language tags
+ * and BNode identifiers.
+ */
+extern IDCache *LSUP_id_cache;
+
+/** @brief Compiled hash of default literal data type.
+ */
+extern uint32_t LSUP_default_dtype_key;
+
+/** @brief URI validation pattern, compiled in #LSUP_init().
+ */
+extern regex_t *LSUP_uri_ptn;
+
+
+
+/*
+ * Function prototypes.
+ */
+
 /** @brief Create a new term.
  *
- * @param type[in] Term type. One of #LSUP_term_type.
+ * @param type[in] Term type. One of #LSUP_TermType.
  *
  * @param data[in] Term data: textual URI, literal value without data type
  *  or langtag, etc.
  *
- * @param datatype[in]: data type for literals.
- *
- * @param lang[in]: language tag for string literals.
+ * @param metadata[in]: language tag for language-tagged literals or data type
+ *  for other literals.
  *
  * @param term[out] Pointer to a new term, which must be freed with
  *  #LSUP_term_free after use.
@@ -63,13 +118,12 @@ typedef struct term_t {
  * @return LSUP_OK if successful, LSUP_VALUE_ERR if validation fails.
  */
 LSUP_Term *
-LSUP_term_new(
-        LSUP_term_type type, const char *data, char *datatype, char *lang);
+LSUP_term_new (LSUP_TermType type, const char *data, const char *metadata);
 
 
 /** @brief Placeholder term to use with LSUP_term_reset.
  */
-#define TERM_DUMMY LSUP_term_new (LSUP_TERM_UNDEFINED, NULL, NULL, NULL)
+#define TERM_DUMMY LSUP_term_new (LSUP_TERM_UNDEFINED, NULL, NULL)
 
 
 /** @brief Shortcut to create a URI.
@@ -98,11 +152,11 @@ LSUP_uri_new (const char *data)
         data = uri;
     }
 
-    return LSUP_term_new (LSUP_TERM_URI, data, NULL, NULL);
+    return LSUP_term_new (LSUP_TERM_IRIREF, data, NULL);
 }
 
 
-/* @brief Reuse a pre-allocated term structure.
+/* @brief Initialize or reuse a pre-allocated term structure.
  *
  * The structure must have been previously created with #LSUP_term_new. It can
  * be reinitialized multiple times without freeing it. It must be eventually
@@ -110,8 +164,8 @@ LSUP_uri_new (const char *data)
  */
 LSUP_rc
 LSUP_term_init(
-        LSUP_Term *term, LSUP_term_type type,
-        const char *data, char *datatype, char *lang);
+        LSUP_Term *term, LSUP_TermType type,
+        const char *data, const char *metadata);
 
 
 LSUP_Term *
@@ -125,41 +179,8 @@ LSUP_buffer_new_from_term (const LSUP_Term *term);
 /**
  * @brief Shortcut to initialize a URI.
  */
-inline LSUP_rc
-LSUP_uri_init (LSUP_Term *term, const char *data)
-{
-    if (!data) {
-        uuid_t uuid;
-        uuid_generate_random (uuid);
-
-        uuid_str_t uuid_str;
-        uuid_unparse_lower (uuid, uuid_str);
-
-        char uri[UUIDSTR_SIZE + 10];
-        sprintf (uri, "urn:uuid4:%s", uuid_str);
-
-        data = uri;
-    }
-
-    return LSUP_term_init (term, LSUP_TERM_URI, data, NULL, NULL);
-}
-
-
-/** @brief Simple ad-hoc serialization function.
- *
- * The resulting term must be freed with #LSUP_term_free after use.
- */
 LSUP_rc
-LSUP_term_serialize (const LSUP_Term *term, LSUP_Buffer *sterm);
-
-
-/** @brief Deserialize a buffer into a term.
- *
- * The buffer must be a well-formed serialization of a term, e.g. as obtained
- * by #LSUP_term_serialize.
- */
-LSUP_rc
-LSUP_term_deserialize (const LSUP_Buffer *sterm, LSUP_Term *term);
+LSUP_uri_init (LSUP_Term *term, const char *data);
 
 
 /** @brief Hash a buffer.
@@ -185,16 +206,31 @@ LSUP_term_hash (const LSUP_Term *term)
  */
 bool LSUP_term_equals (const LSUP_Term *term1, const LSUP_Term *term2);
 
-/*
-// TODO Implement when xxhash v0.8 is released with stable xxhash128 function.
-inline XXH128_hash_t
-LSUP_term_hash128(const LSUP_Term *term);
-*/
-
 void
 LSUP_term_done (LSUP_Term *term);
 
 void
 LSUP_term_free (LSUP_Term *term);
 
+
+/** @brief Add an identifier to the term cache.
+ *
+ * @param[in] key 32-bit hash of the inserted term.
+ *
+ * @param[in] data Term to insert.
+ */
+LSUP_rc
+LSUP_tcache_add_id (const uint32_t key, const char *data);
+
+
+/** @brief Get an identifier from the cache.
+ *
+ * @param[in] key Key for the queried term.
+ *
+ * @return The retieved term if found, or NULL. The string must not be modified
+ *  or freed.
+ */
+const char *
+LSUP_tcache_get_id (const uint32_t key);
+
 #endif

+ 7 - 104
include/triple.h

@@ -9,12 +9,6 @@ typedef struct LSUP_Triple {
     LSUP_Term *o;
 } LSUP_Triple;
 
-typedef struct LSUP_SerTriple {
-    LSUP_Buffer *s;
-    LSUP_Buffer *p;
-    LSUP_Buffer *o;
-} LSUP_SerTriple;
-
 typedef enum {
     TRP_POS_S = 0,
     TRP_POS_P = 1,
@@ -28,18 +22,12 @@ LSUP_triple_new(LSUP_Term *s, LSUP_Term *p, LSUP_Term *o);
 #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 (NULL, NULL, NULL)
-
-
 LSUP_Triple *
-LSUP_triple_new_from_striple (const LSUP_SerTriple *sspo);
+LSUP_triple_new_from_btriple (const LSUP_BufferTriple *sspo);
 
 
-LSUP_SerTriple *
-LSUP_striple_new_from_triple (const LSUP_Triple *spo);
+LSUP_BufferTriple *
+LSUP_btriple_new_from_triple (const LSUP_Triple *spo);
 
 
 /** @brief Initialize internal term pointers in a heap-allocated triple.
@@ -53,45 +41,6 @@ LSUP_rc
 LSUP_triple_init (LSUP_Triple *spo, LSUP_Term *s, LSUP_Term *p, LSUP_Term *o);
 
 
-/** @brief Initialize internal term pointers in a heap-allocated buffer triple.
- *
- * The triple must be freed with #LSUP_striple_free().
- *
- * @param sspo[in] Serialized triple pointer to initialize.
- */
-LSUP_rc
-LSUP_striple_init (
-        LSUP_SerTriple *sspo, LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o);
-
-
-/** @brief Serialize a RDF triple into a buffer triple.
- *
- * The internal structure must be freed with #LSUP_striple_done after use.
- *
- * @param spo[in] Triple to be serialized.
- * @param sspo[out] Buffer triple handle. It must point to an already allocated
- *  structure.
- *
- * @return LSUP_OK or an error code resulting from #LSUP_term_serialize.
- */
-LSUP_rc
-LSUP_triple_serialize (const LSUP_Triple *spo, LSUP_SerTriple *sspo);
-
-
-/** @brief Deserialize a buffer triple into a RDF triple.
- *
- * The internal structure must be freed with #LSUP_triple_done after use.
- *
- * @param sspo[in] Buffer triple to be serialized.
- * @param spo[out] RDF triple handle. It must point to an already allocated
- *  structure.
- *
- * @return LSUP_OK or an error code resulting from #LSUP_term_deserialize.
- */
-LSUP_rc
-LSUP_triple_deserialize (const LSUP_SerTriple *sspo, LSUP_Triple *spo);
-
-
 /** @brief Free the internal pointers of a triple.
  *
  * @param spo[in] Triple to be freed.
@@ -100,14 +49,6 @@ void
 LSUP_triple_done (LSUP_Triple *spo);
 
 
-/** @brief Free the internal pointers of a buffer triple.
- *
- * @param sspo[in] Buffer triple to be freed.
- */
-void
-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
@@ -119,27 +60,6 @@ void
 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.
- */
-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; \
@@ -173,28 +93,11 @@ LSUP_triple_pos (const LSUP_Triple *trp, LSUP_TriplePos n)
  * @return Corresponding serialized term or NULL if n is out of range.
  */
 inline LSUP_Buffer *
-LSUP_striple_pos (const LSUP_SerTriple *trp, LSUP_TriplePos n)
+LSUP_btriple_pos (const LSUP_BufferTriple *trp, LSUP_TriplePos n)
 { _FN_BODY }
 #undef _FN_BODY
 
 
-/** @brief Hash a buffer triple.
- *
- * TODO This doesn't handle blank nodes correctly.
- */
-inline LSUP_Key
-LSUP_striple_hash (const LSUP_SerTriple *strp)
-{
-    return XXH64 (
-        strp->s->addr, strp->s->size,
-        XXH64 (
-            strp->p->addr, strp->p->size,
-            XXH64 (strp->o->addr, strp->o->size, HASH_SEED)
-        )
-    );
-}
-
-
 /** @brief Hash a triple.
  *
  * TODO This doesn't handle blank nodes correctly.
@@ -202,9 +105,9 @@ LSUP_striple_hash (const LSUP_SerTriple *strp)
 inline LSUP_Key
 LSUP_triple_hash (const LSUP_Triple *trp)
 {
-    LSUP_SerTriple *strp = LSUP_striple_new_from_triple (trp);
-    LSUP_Key hash = LSUP_striple_hash (strp);
-    LSUP_striple_free (strp);
+    LSUP_BufferTriple *strp = LSUP_btriple_new_from_triple (trp);
+    LSUP_Key hash = LSUP_btriple_hash (strp);
+    LSUP_btriple_free (strp);
 
     return hash;
 }

+ 67 - 0
src/buffer.c

@@ -86,6 +86,73 @@ void LSUP_buffer_free (LSUP_Buffer *buf)
 }
 
 
+/*
+ * Buffer triples.
+ */
+
+LSUP_BufferTriple *
+LSUP_btriple_new(LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o)
+{
+    LSUP_BufferTriple *sspo = malloc (sizeof (*sspo));
+    if (!sspo) return NULL;
+
+    if (UNLIKELY (LSUP_btriple_init (sspo, s, p, o))) {
+        free (sspo);
+        return NULL;
+    }
+
+    return sspo;
+}
+
+
+LSUP_rc
+LSUP_btriple_init (
+        LSUP_BufferTriple *sspo, LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o)
+{
+    sspo->s = s;
+    sspo->p = p;
+    sspo->o = o;
+
+    return LSUP_OK;
+}
+
+
+void
+LSUP_btriple_done (LSUP_BufferTriple *sspo)
+{
+    if (UNLIKELY (!sspo)) return;
+
+    LSUP_buffer_done (sspo->s);
+    LSUP_buffer_done (sspo->p);
+    LSUP_buffer_done (sspo->o);
+}
+
+
+void
+LSUP_btriple_free (LSUP_BufferTriple *sspo)
+{
+    if (UNLIKELY (!sspo)) return;
+
+    LSUP_buffer_free (sspo->s);
+    LSUP_buffer_free (sspo->p);
+    LSUP_buffer_free (sspo->o);
+
+    free (sspo);
+}
+
+
+void
+LSUP_btriple_free_shallow (LSUP_BufferTriple *sspo)
+{
+    if (UNLIKELY (!sspo)) return;
+
+    sspo->s->addr = sspo->p->addr = sspo->o->addr = NULL;
+    LSUP_btriple_free (sspo);
+}
+
+
+
 /* Extern inline prototypes. */
 
 LSUP_Key LSUP_buffer_hash (const LSUP_Buffer *buf);
+LSUP_Key LSUP_btriple_hash (const LSUP_BufferTriple *strp);

+ 3 - 3
src/codec/nt_grammar.y

@@ -15,8 +15,8 @@
 %token_type { LSUP_Term * }
 %token_prefix "T_"
 
-%type triple            { LSUP_SerTriple * }
-%destructor triple      { LSUP_striple_free ($$); }
+%type triple            { LSUP_BufferTriple * }
+%destructor triple      { LSUP_btriple_free ($$); }
 %type subject           { LSUP_Term * }
 %destructor subject     { LSUP_term_free ($$); }
 %type predicate         { LSUP_Term * }
@@ -38,7 +38,7 @@ triples     ::= triples triple eol.
 
 triple(A)   ::= ws subject(S) ws predicate(P) ws object(O) ws DOT. {
 
-                A = LSUP_striple_new (
+                A = LSUP_btriple_new (
                     LSUP_buffer_new_from_term (S),
                     LSUP_buffer_new_from_term (P),
                     LSUP_buffer_new_from_term (O)

+ 20 - 19
src/codec/nt_lexer.re

@@ -219,31 +219,32 @@ loop:
         // Only unescape Unicode from data.
         size_t size = lit_data_e - it->tok - 2;
         YYCTYPE *data = unescape_unicode (it->tok + 1, size);
-        log_debug ("Literal data: %s", data);
+        log_trace ("Literal data: %s", data);
 
-        YYCTYPE *datatype = NULL, *lang = NULL;
+        char *metadata = NULL;
+        const YYCTYPE *md_marker;
+        LSUP_TermType type = LSUP_TERM_LITERAL;
 
         if (dtype_s) {
-            size = YYCURSOR - dtype_s - 1;
-            datatype = malloc (size);
-            memcpy (datatype, dtype_s + 1, size);
-            datatype [size - 1] = '\0';
-            log_debug ("datatype: %s", datatype);
+            md_marker = dtype_s;
+            size = YYCURSOR - md_marker - 1;
+        } else if (lang_s) {
+            type = LSUP_TERM_LT_LITERAL;
+            md_marker = lang_s;
+            size = YYCURSOR - md_marker;
+        } else md_marker = NULL;
+
+        if (md_marker) {
+            metadata = malloc (size);
+            memcpy (metadata, md_marker + 1, size);
+            metadata [size - 1] = '\0';
+            log_trace ("metadata: %s", metadata);
         }
 
-        if (lang_s) {
-            size = YYCURSOR - lang_s;
-            lang = malloc (size);
-            memcpy (lang, lang_s + 1, size);
-            lang [size - 1] = '\0';
-            log_debug ("lang: %s", lang);
-        }
-
-        *term = LSUP_term_new (LSUP_TERM_LITERAL, (char*)data, (char*)datatype, (char*)lang);
+        *term = LSUP_term_new (type, (char *) data, (char *) metadata);
 
         free (data);
-        free (datatype);
-        free (lang);
+        free (metadata);
 
         return T_LITERAL;
     }
@@ -253,7 +254,7 @@ loop:
 
         log_debug ("BNode data: %s", data);
 
-        *term = LSUP_term_new (LSUP_TERM_BNODE, (char*)data, NULL, NULL);
+        *term = LSUP_term_new (LSUP_TERM_BNODE, (char*)data, NULL);
         free (data);
 
         return T_BNODE;

+ 35 - 16
src/codec_nt.c

@@ -13,10 +13,6 @@
  */
 #define IRI_ECHAR_PTN "[\x00-\x20<>\"\\{\\}\\|\\^`\\\\]"
 
-/** @brief Default NT literal type.
- */
-#define XSD_STRING "http://www.w3.org/2001/XMLSchema#string"
-
 
 /* * * Static prototypes. * * */
 
@@ -30,13 +26,14 @@ term_to_nt (const LSUP_Term *term, const LSUP_NSMap *nsm, char **out_p)
 {
     LSUP_rc rc;
     char *out = NULL, *tmp, *escaped;
+    const char *metadata = NULL;
     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:
+        case LSUP_TERM_IRIREF:
             tmp = realloc (out, strlen (term->data) + 3);
             if (UNLIKELY (!tmp)) return LSUP_MEM_ERR;
             out = tmp;
@@ -51,13 +48,39 @@ term_to_nt (const LSUP_Term *term, const LSUP_NSMap *nsm, char **out_p)
                 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) + 4; // Room for ^^<>
+            if (
+                term->datatype != 0
+                && term->datatype != LSUP_default_dtype_key
+            ) {
+                metadata = LSUP_tcache_get_id (term->datatype);
+                buf_len += strlen (metadata) + 4; // Room for ^^<>
+            }
+
+            tmp = realloc (out, buf_len);
+            if (UNLIKELY (!tmp)) return LSUP_MEM_ERR;
+            out = tmp;
+
+            sprintf (out, "\"%s\"", escaped);
+            free (escaped);
 
-            if (strlen (term->lang) > 0)
-                buf_len += strlen(term->lang) + 1; // Room for @
+            // Add datatype.
+            if (metadata)
+                out = strcat (strcat (strcat (out, "^^<"), metadata), ">");
 
-            //TRACE ("nt rep length: %lu\n", buf_len);
+            rc = LSUP_OK;
+
+            break;
+
+        case LSUP_TERM_LT_LITERAL:
+            // 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->lang != 0) {
+                metadata = LSUP_tcache_get_id (term->lang);
+                buf_len += strlen(metadata) + 1; // Room for @
+            }
 
             tmp = realloc (out, buf_len);
             if (UNLIKELY (!tmp)) return LSUP_MEM_ERR;
@@ -66,12 +89,8 @@ term_to_nt (const LSUP_Term *term, const LSUP_NSMap *nsm, char **out_p)
             sprintf (out, "\"%s\"", escaped);
             free (escaped);
 
-            // Always suppress xsd:string data type.
-            if (term->datatype && strcmp (term->datatype, XSD_STRING) != 0)
-                out = strcat (strcat (strcat (out, "^^<"), term->datatype), ">");
-
-            if (strlen (term->lang) > 0)
-                out = strcat (strcat (out, "@"), term->lang);
+            // Add lang.
+            if (metadata) out = strcat (strcat (out, "@"), metadata);
 
             rc = LSUP_OK;
 

+ 1 - 1
src/core.c

@@ -20,7 +20,7 @@ char *warning_msg[] = {
  * err_msg[rc - LSUP_MIN_ERROR].
  */
 char *err_msg[] = {
-    "LSUP_ERROR: Geneic LSUP runtime error.",
+    "LSUP_ERROR: Runtime error.",
     "LSUP_PARSE_ERR: Error parsing input.",
     "LSUP_VALUE_ERR: Invalid input.",
     "LSUP_TXN_ERR: MDB transaction error.",

+ 25 - 9
src/environment.c

@@ -1,13 +1,6 @@
 #include "environment.h"
 
 
-// FIXME find a better cross-platform path.
-#define DEFAULT_ENV_PATH "./mdb_store"
-
-// RAMdisk path for MDB volatile store.
-#define MDB_RAMDISK_PATH TMPDIR "/lsup_mem_graph"
-
-
 /**
  * Static handles.
  */
@@ -62,13 +55,24 @@ LSUP_init (void)
     if (LSUP_default_env == NULL) {
 #ifdef DEBUG
         // In debug mode, always use max logging.
-        int loglevel = LOG_DEBUG;
+        int loglevel = LOG_TRACE;
 #else
         char *_loglevel = getenv ("LSUP_LOGLEVEL");
         int loglevel = (_loglevel == NULL) ? LOG_INFO : atoi (_loglevel);
 #endif
         log_set_level (loglevel);
 
+        // URI validation pattern.
+        MALLOC_GUARD (LSUP_uri_ptn, LSUP_MEM_ERR);
+        if (regcomp (LSUP_uri_ptn, LSUP_URI_REGEX_STR, REG_EXTENDED) != 0) {
+            log_error ("Error compiling regular expression pattern.");
+            return LSUP_ERROR;
+        }
+
+        // Default literal datatype key.
+        LSUP_default_dtype_key  = XXH32 (
+                DEFAULT_DTYPE, strlen (DEFAULT_DTYPE) + 1, HASH_SEED);
+
         // Default permanent store path.
         char *mdb_path = getenv ("LSUP_MDB_STORE_PATH");
         if (!mdb_path) {
@@ -100,10 +104,22 @@ LSUP_env_free (LSUP_Env *env)
     LSUP_mdbstore_free (env->mdb_store_ramdisk);
     LSUP_buffer_free (env->default_ctx);
     LSUP_nsmap_free (env->nsm);
+
+    IDCache *entry, *tmp;
+    HASH_ITER (hh, LSUP_id_cache, entry, tmp) {
+        HASH_DEL (LSUP_id_cache, entry);
+        free (entry->data);
+        free (entry);
+    }
+
     free (env);
 }
 
 
 void
 LSUP_done (void)
-{ LSUP_env_free (LSUP_default_env); }
+{
+    LSUP_env_free (LSUP_default_env);
+    regfree (LSUP_uri_ptn);
+    free (LSUP_uri_ptn);
+}

+ 15 - 13
src/graph.c

@@ -38,7 +38,7 @@ typedef struct GraphIterator {
  */
 
 inline static LSUP_rc
-graph_iter_next_buffer (GraphIterator *it, LSUP_SerTriple *sspo);
+graph_iter_next_buffer (GraphIterator *it, LSUP_BufferTriple *sspo);
 
 static LSUP_rc
 graph_copy_contents (const LSUP_Graph *src, LSUP_Graph *dest);
@@ -286,7 +286,7 @@ LSUP_graph_add_init (LSUP_Graph *gr)
 
 
 LSUP_rc
-LSUP_graph_add_iter (LSUP_GraphIterator *it, const LSUP_SerTriple *sspo)
+LSUP_graph_add_iter (LSUP_GraphIterator *it, const LSUP_BufferTriple *sspo)
 {
     if (it->graph->store_type == LSUP_STORE_MEM)
         return LSUP_htstore_add_iter (it->ht_iter, sspo);
@@ -310,7 +310,7 @@ LSUP_graph_add_done (LSUP_GraphIterator *it)
 LSUP_rc
 LSUP_graph_add (
         Graph *gr, const LSUP_Triple trp[],
-        const LSUP_SerTriple strp[], size_t *inserted)
+        const LSUP_BufferTriple strp[], size_t *inserted)
 {
     /*
      * NOTE It is possible to pass both sets of RDF triples and buffer triples.
@@ -322,21 +322,20 @@ LSUP_graph_add (
     LSUP_GraphIterator *it = LSUP_graph_add_init (gr);
 
     // Serialize and insert RDF triples.
-    LSUP_SerTriple *sspo = LSUP_striple_new (BUF_DUMMY, BUF_DUMMY, BUF_DUMMY);
-    if (UNLIKELY (!sspo)) return LSUP_MEM_ERR;
-
     if (trp) {
         for (size_t i = 0; trp[i].s != NULL; i++) {
             log_trace ("Inserting triple #%lu", i);
 
-            LSUP_triple_serialize (trp + i, sspo);
+            LSUP_BufferTriple *sspo = LSUP_btriple_new_from_triple (trp + i);
+            if (UNLIKELY (!sspo)) return LSUP_MEM_ERR;
             LSUP_rc db_rc = LSUP_graph_add_iter (it, sspo);
 
+            LSUP_btriple_free (sspo);
+
             if (db_rc == LSUP_OK) rc = LSUP_OK;
             if (UNLIKELY (db_rc < 0)) return db_rc;
         }
     }
-    LSUP_striple_free (sspo);
 
     // Insert serialized triples.
     if (strp) {
@@ -428,7 +427,7 @@ LSUP_graph_iter_next (GraphIterator *it, LSUP_Triple *spo)
      * the data. Therefore they must be initialized and freed differently.
      */
 
-    LSUP_SerTriple *sspo;
+    LSUP_BufferTriple *sspo;
     LSUP_Buffer *ss, *sp, *so;
 
     if (it->graph->store_type == LSUP_STORE_MEM) {
@@ -440,18 +439,21 @@ LSUP_graph_iter_next (GraphIterator *it, LSUP_Triple *spo)
         CALLOC_GUARD (sp, LSUP_MEM_ERR);
         CALLOC_GUARD (so, LSUP_MEM_ERR);
     }
-    sspo = LSUP_striple_new (ss, sp, so);
+    sspo = LSUP_btriple_new (ss, sp, so);
 
     LSUP_rc rc = graph_iter_next_buffer (it, sspo);
 
     if (rc == LSUP_OK) {
         spo->s = LSUP_term_new_from_buffer (sspo->s);
+        if (!spo->s) return LSUP_ERROR;
         spo->p = LSUP_term_new_from_buffer (sspo->p);
+        if (!spo->p) return LSUP_ERROR;
         spo->o = LSUP_term_new_from_buffer (sspo->o);
+        if (!spo->o) return LSUP_ERROR;
     }
 
     if (it->graph->store_type == LSUP_STORE_MEM) free (sspo);
-    else LSUP_striple_free_shallow (sspo);
+    else LSUP_btriple_free_shallow (sspo);
 
     return rc;
 }
@@ -499,7 +501,7 @@ LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo)
  * functions without serializing and deserializing triples.
  */
 inline static LSUP_rc
-graph_iter_next_buffer (GraphIterator *it, LSUP_SerTriple *sspo)
+graph_iter_next_buffer (GraphIterator *it, LSUP_BufferTriple *sspo)
 {
     LSUP_rc rc;
 
@@ -523,7 +525,7 @@ graph_copy_contents (const LSUP_Graph *src, LSUP_Graph *dest)
 
     GraphIterator *it = LSUP_graph_lookup (src, NULL, NULL, NULL, NULL);
 
-    LSUP_SerTriple sspo;
+    LSUP_BufferTriple sspo;
 
     LSUP_GraphIterator *add_it = LSUP_graph_add_init (dest);
     while (graph_iter_next_buffer (it, &sspo) != LSUP_END) {

+ 34 - 14
src/store_htable.c

@@ -108,7 +108,7 @@ static bool lookup_spok_eq_fn(
 /* * * Other prototypes. * * */
 
 static inline LSUP_rc tkey_to_strp (
-        const HTStore *store, const LSUP_Key spok[], LSUP_SerTriple *sspo);
+        const HTStore *store, const LSUP_Key spok[], LSUP_BufferTriple *sspo);
 
 
 /* * * API * * */
@@ -209,7 +209,7 @@ LSUP_htstore_bool_op(
         return dest;
     }
 
-    LSUP_SerTriple *sspo = STRP_DUMMY;
+    LSUP_BufferTriple *sspo = STRP_DUMMY;
 
     LSUP_HTIterator *it = LSUP_htstore_add_init(dest);
 
@@ -243,7 +243,7 @@ LSUP_htstore_bool_op(
             LSUP_htstore_add_iter (it, sspo);
         }
     }
-    LSUP_striple_free (sspo);
+    LSUP_btriple_free (sspo);
     LSUP_htstore_add_done (it);
 
     return dest;
@@ -295,7 +295,7 @@ LSUP_htstore_add_init (HTStore *store)
 
 
 LSUP_rc
-LSUP_htstore_add_iter (HTIterator *it, const LSUP_SerTriple *sspo)
+LSUP_htstore_add_iter (HTIterator *it, const LSUP_BufferTriple *sspo)
 {
     LSUP_TripleKey spok = {
         LSUP_buffer_hash (sspo->s),
@@ -323,17 +323,14 @@ LSUP_htstore_add_iter (HTIterator *it, const LSUP_SerTriple *sspo)
 
     // Add terms to index.
     for (int i = 0; i < 3; i++) {
-        spok[i] = LSUP_buffer_hash (LSUP_striple_pos (sspo, i));
-        //log_trace ("Indexing term key %lx\n", spok[i]);
-
         IndexEntry *ins = NULL;
         HASH_FIND (hh, it->store->idx, spok + i, KLEN, ins);
         if (ins == NULL) {
             MALLOC_GUARD (ins, LSUP_MEM_ERR);
             ins->key = spok[i];
             ins->sterm = LSUP_buffer_new (
-                    (LSUP_striple_pos (sspo, i))->size,
-                    (LSUP_striple_pos (sspo, i))->addr);
+                    (LSUP_btriple_pos (sspo, i))->size,
+                    (LSUP_btriple_pos (sspo, i))->addr);
             HASH_ADD (hh, it->store->idx, key, KLEN, ins);
         }
     }
@@ -456,7 +453,7 @@ LSUP_htiter_cur (LSUP_HTIterator *it)
 
 
 LSUP_rc
-LSUP_htiter_next (HTIterator *it, LSUP_SerTriple *sspo)
+LSUP_htiter_next (HTIterator *it, LSUP_BufferTriple *sspo)
 {
     if (UNLIKELY (!it)) return LSUP_VALUE_ERR;
 
@@ -464,6 +461,18 @@ LSUP_htiter_next (HTIterator *it, LSUP_SerTriple *sspo)
     if (it->rc != LSUP_OK) return it->rc;
 
     it->rc = LSUP_NORESULT;
+    /*
+#ifdef DEBUG
+    log_trace ("Lookup key: ");
+    IndexEntry *tmp = NULL;
+    HASH_FIND (hh, it->store->idx, it->luk + 0, KLEN, tmp);
+    if (tmp) LSUP_buffer_print(tmp->sterm); else printf("NULL\n");
+    HASH_FIND (hh, it->store->idx, it->luk + 1, KLEN, tmp);
+    if (tmp) LSUP_buffer_print(tmp->sterm); else printf("NULL\n");
+    HASH_FIND (hh, it->store->idx, it->luk + 2, KLEN, tmp);
+    if (tmp) LSUP_buffer_print(tmp->sterm); else printf("NULL\n");
+#endif
+    */
 
     do {
         if (!it->entry) it->rc = LSUP_END;
@@ -477,6 +486,17 @@ LSUP_htiter_next (HTIterator *it, LSUP_SerTriple *sspo)
                     "Found spok: {%lx, %lx, %lx}",
                     it->entry->key[0], it->entry->key[1], it->entry->key[2]
                 );
+                /*
+#ifdef DEBUG
+                IndexEntry *tmp = NULL;
+                HASH_FIND (hh, it->store->idx, it->entry->key + 0, KLEN, tmp);
+                LSUP_buffer_print(tmp->sterm);
+                HASH_FIND (hh, it->store->idx, it->entry->key + 1, KLEN, tmp);
+                LSUP_buffer_print(tmp->sterm);
+                HASH_FIND (hh, it->store->idx, it->entry->key + 2, KLEN, tmp);
+                LSUP_buffer_print(tmp->sterm);
+#endif
+                */
 
                 it->rc = LSUP_OK;
                 it->i++;
@@ -494,18 +514,18 @@ LSUP_htiter_next (HTIterator *it, LSUP_SerTriple *sspo)
 
 inline static LSUP_rc
 tkey_to_strp (
-        const HTStore *store, const LSUP_Key spok[], LSUP_SerTriple *sspo)
+        const HTStore *store, const LSUP_Key spok[], LSUP_BufferTriple *sspo)
 {
-    // owned by the store.
+    // Data owned by the store.
     IndexEntry *tmp = NULL;
 
     HASH_FIND (hh, store->idx, spok + 0, KLEN, tmp);
+    if (UNLIKELY (!tmp)) return LSUP_ERROR;
     sspo->s = tmp->sterm;
-    sspo->s->size = tmp->sterm->size;
 
     HASH_FIND (hh, store->idx, spok + 1, KLEN, tmp);
+    if (UNLIKELY (!tmp)) return LSUP_ERROR;
     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;

+ 8 - 4
src/store_mdb.c

@@ -49,6 +49,7 @@ typedef struct mdbstore_t {
     MDB_dbi             dbi[N_DB];      // DB handles. Refer to DbIdx enum.
     LSUP_Buffer *       default_ctx;    // Default ctx as a serialized URI.
     StoreState          state;          // Store state.
+    int                 features;       // Store feature flags.
 } MDBStore;
 
 /** @brief Iterator operation.
@@ -265,6 +266,9 @@ LSUP_mdbstore_new (const char *path, const LSUP_Buffer *default_ctx)
     int db_rc;
     LSUP_MDBStore *store;
     MALLOC_GUARD (store, NULL);
+    store->features = LSUP_STORE_CTX;
+    if (strcmp (path, MDB_RAMDISK_PATH) != 0)
+            store->features |= LSUP_STORE_PERM;
 
     db_rc = mdb_env_create (&store->env);
     log_trace ("create rc: %d", db_rc);
@@ -398,14 +402,14 @@ LSUP_mdbstore_add_init (LSUP_MDBStore *store, const LSUP_Buffer *sc)
 
 
 LSUP_rc
-LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_SerTriple *sspo)
+LSUP_mdbstore_add_iter (MDBIterator *it, const LSUP_BufferTriple *sspo)
 {
     int db_rc;
     LSUP_TripleKey spok = NULL_TRP;
 
     // Add triple.
     for (int i = 0; i < 3; i++) {
-        LSUP_Buffer *st = LSUP_striple_pos (sspo, i);
+        LSUP_Buffer *st = LSUP_btriple_pos (sspo, i);
 
         spok[i] = LSUP_buffer_hash (st);
 
@@ -486,7 +490,7 @@ LSUP_mdbstore_add_abort (MDBIterator *it)
 LSUP_rc
 LSUP_mdbstore_add (
         LSUP_MDBStore *store, const LSUP_Buffer *sc,
-        const LSUP_SerTriple strp[], const size_t ct, size_t *inserted)
+        const LSUP_BufferTriple strp[], const size_t ct, size_t *inserted)
 {
     MDBIterator *it = LSUP_mdbstore_add_init (store, sc);
     if (UNLIKELY (!it)) return LSUP_DB_ERR;
@@ -727,7 +731,7 @@ mdbiter_next_key (LSUP_MDBIterator *it, KeySet **ck_p)
 
 LSUP_rc
 LSUP_mdbiter_next (
-        LSUP_MDBIterator *it, LSUP_SerTriple *sspo, LSUP_Buffer **ctx_p)
+        LSUP_MDBIterator *it, LSUP_BufferTriple *sspo, LSUP_Buffer **ctx_p)
 {
     LSUP_rc rc;
     KeySet *ck = NULL;

+ 110 - 185
src/term.c

@@ -1,31 +1,36 @@
+#include "tpl.h"
+
 #include "term.h"
 
-// URI parsing regular expression. Conforms to RFC3986.
-#define URI_REGEX_STR \
-    "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"
-#define NLEN(str)   (str) == NULL ? 0 : strlen ((str))
-#define INVALID_URI_CHARS "<>\" {}|\\^`"
+/*
+ * tpl packing format for the term structure.
+ */
+#define TERM_PACK_FMT "S(suc)"
 
+/*
+ * Extern variables.
+ */
 
-static regex_t ptn;
-static bool ptn_init = false;
+IDCache *LSUP_id_cache = NULL;
+uint32_t LSUP_default_dtype_key = 0;
+regex_t *LSUP_uri_ptn;
 
 
-/* Global inline prototypes. */
+/*
+ * Static variables.
+ */
 
-LSUP_Term *LSUP_uri_new (const char *data);
-LSUP_rc LSUP_uri_init (LSUP_Term *term, const char *data);
+// Characters not allowed in a URI string.
+static const char *invalid_uri_chars = "<>\" {}|\\^`";
 
 
-/**
- * Free global regex struct. Register with atexit().
+/*
+ * API functions.
  */
-void term_cleanup() { if (ptn_init) regfree (&ptn); }
-
 
 LSUP_Term *
 LSUP_term_new (
-        LSUP_term_type type, const char *data, char *datatype, char *lang)
+        LSUP_TermType type, const char *data, const char *metadata)
 {
     LSUP_Term *term;
     CALLOC_GUARD (term, NULL);
@@ -34,7 +39,7 @@ LSUP_term_new (
     if (type == LSUP_TERM_UNDEFINED) term->type = type;
 
     else if (UNLIKELY (LSUP_term_init (
-                    term, type, data, datatype, lang) != LSUP_OK)) {
+                    term, type, data, metadata) != LSUP_OK)) {
         free (term);
         return NULL;
     }
@@ -46,26 +51,44 @@ LSUP_term_new (
 LSUP_Term *
 LSUP_term_new_from_buffer (const LSUP_Buffer *sterm)
 {
+    if (UNLIKELY (!sterm)) return NULL;
+
     LSUP_Term *term;
-    CALLOC_GUARD (term, NULL);
+    MALLOC_GUARD (term, NULL);
 
-    if (UNLIKELY (LSUP_term_deserialize (sterm, term) != LSUP_OK)) {
-        free (term);
-        return NULL;
-    }
+    tpl_node *tn;
+
+    tn = tpl_map (TERM_PACK_FMT, term);
+    if (UNLIKELY (!tn)) goto fail;
+
+    if (UNLIKELY (tpl_load (tn, TPL_MEM, sterm->addr, sterm->size) < 0))
+        goto fail;
+
+    if (UNLIKELY (tpl_unpack (tn, 0) < 0)) goto fail;
+
+    tpl_free (tn);
 
     return term;
+
+fail:
+    tpl_free (tn);
+    free (term);
+
+    return NULL;
 }
 
 
 LSUP_Buffer *
 LSUP_buffer_new_from_term (const LSUP_Term *term)
 {
+    if (UNLIKELY (!term)) return NULL;
+
     LSUP_Buffer *sterm;
-    CALLOC_GUARD (sterm, NULL);
-    sterm->addr = NULL;
+    MALLOC_GUARD (sterm, NULL);
 
-    if (LSUP_term_serialize (term, sterm) != LSUP_OK) {
+    int rc = tpl_jot (
+            TPL_MEM, &sterm->addr, &sterm->size, TERM_PACK_FMT, term);
+    if (rc != 0) {
         free (sterm);
         return NULL;
     }
@@ -76,32 +99,24 @@ LSUP_buffer_new_from_term (const LSUP_Term *term)
 
 LSUP_rc
 LSUP_term_init(
-        LSUP_Term *term, LSUP_term_type type,
-        const char *data, char *datatype, char *lang)
+        LSUP_Term *term, LSUP_TermType type,
+        const char *data, const char *metadata)
 {
     // This can never be LSUP_TERM_UNDEFINED.
     if (!data) return LSUP_VALUE_ERR;
     term->type = type;
 
     // Validate URI.
-    if (term->type == LSUP_TERM_URI) {
-        // TODO Cheap fix. Should url-encode all invalid chars.
-        if (strpbrk (data, INVALID_URI_CHARS) != NULL) {
-            fprintf (
-                    stderr, "Characters %s are not allowed. Got: %s\n",
-                    INVALID_URI_CHARS, data);
+    if (term->type == LSUP_TERM_IRIREF) {
+        if (strpbrk (data, invalid_uri_chars) != NULL) {
+            log_error (
+                    "Characters %s are not allowed. Got: %s\n",
+                    invalid_uri_chars, data);
 
             return LSUP_VALUE_ERR;
         }
 
-        if (UNLIKELY (!ptn_init)) {
-            int rc = regcomp (&ptn, URI_REGEX_STR, REG_EXTENDED);
-            if (rc != 0) return LSUP_ERROR;
-            ptn_init = true;
-            atexit (term_cleanup);
-        }
-
-        if (regexec (&ptn, data, 0, NULL, 0) != 0) {
+        if (regexec (LSUP_uri_ptn, data, 0, NULL, 0) != 0) {
             fprintf (stderr, "Error matching URI pattern.\n");
 
             return LSUP_VALUE_ERR;
@@ -113,107 +128,13 @@ LSUP_term_init(
     term->data = data_tmp;
     strcpy (term->data, data);
 
-    if (term->type == LSUP_TERM_LITERAL && !datatype)
-        datatype = DEFAULT_DTYPE;
-
-    if (datatype) {
-        data_tmp = realloc (term->datatype, strlen (datatype) + 1);
-        if (UNLIKELY (!data_tmp)) return LSUP_MEM_ERR;
-        term->datatype = data_tmp;
-        strcpy (term->datatype, datatype);
-    } else {
-        free (term->datatype);
-        term->datatype = NULL;
-    }
-    if (lang) {
-        // TODO validate language and country code
-        //char lsize = 5 ? lang[2] == "-" : 2;
-        memcpy (term->lang, lang, LANG_SIZE);
-    } else {
-        memset (term->lang, 0, LANG_SIZE);
-    }
-
-    return LSUP_OK;
-}
-
-
-/*
- * This function allocates and returns the following byte sequence:
- *
- * - `sizeof (char)` bytes for the term type;
- * - `LANG_SIZE` bytes for the language tag;
- * - Arbitrary bytes with NUL-terminated strings for data and datatype.
- *
- * The index for `data` is consistently `LANG_SIZE + sizeof (char)`. The
- * index for `datatype` is found by the terminating NULL for `data`.
- *
- * Serialized representations of some RDF terms:
- *
- * <http://hello.org>
- *
- * 0      1                size=19
- * | \x01 | http://hello.org\x00 |
- * type   data
- *
- * "hello"
- *
- * 0      1      size=7
- * | \x03 | hello\x00 |
- * type   data
- *
- * "hello"^^xsd:string
- *
- * 0      1           7          size=18
- * | \x03 | hello\x00 | xsd:string\x00 |
- * type   data        datatype
- *
- * (note: the "xsd:" prefix is used for simplification here, it would be
- * normally be a fully qualified URI)
- *
- * "hello"@en-US
- *
- * 0      1           7               18     size=24
- * | \x03 | hello\x00 | xsd:string\x00 | en-US\x00 |
- * type   data        datatype         lang
- */
-LSUP_rc
-LSUP_term_serialize (const LSUP_Term *term, LSUP_Buffer *sterm)
-{
-    size_t size, data_len, datatype_len = 0,
-           data_idx = 1, datatype_idx = 0, lang_idx = 0;
-
-    if (UNLIKELY (term == NULL)) return LSUP_NOACTION;
+    if (term->type == LSUP_TERM_LT_LITERAL) {
+        term->lang = XXH64 (metadata, strlen (metadata) + 1, HASH_SEED);
+        LSUP_tcache_add_id (term->lang, metadata);
 
-    data_len = strlen (term->data) + 1;
-
-    size = data_idx + data_len;
-
-    if (term->datatype != NULL) {
-        datatype_idx = size;
-        datatype_len = strlen (term->datatype) + 1;
-        size += datatype_len;
-
-        if (strlen (term->lang) > 0) {
-            lang_idx = size;
-            size += strlen (term->lang) + 1;
-        }
-    }
-
-    //log_debug ("Serialized term size: %lu", size);
-    LSUP_buffer_init (sterm, size, NULL);
-
-    // Copy type.
-    memcpy (sterm->addr, &term->type, 1);
-    // Copy data.
-    memcpy (sterm->addr + data_idx, term->data, data_len);
-
-    if (term->datatype != NULL) {
-        // Copy data type.
-        memcpy (sterm->addr + datatype_idx, term->datatype, datatype_len);
-
-        // Copy lang tag.
-        if (strlen (term->lang) > 0)
-            strcpy (sterm->addr + lang_idx, term->lang);
+    } else if (metadata && strcmp (metadata, DEFAULT_DTYPE) != 0) {
+        term->datatype = XXH64 (metadata, strlen (metadata) + 1, HASH_SEED);
+        LSUP_tcache_add_id (term->datatype, metadata);
     }
 
     return LSUP_OK;
@@ -221,29 +142,22 @@ LSUP_term_serialize (const LSUP_Term *term, LSUP_Buffer *sterm)
 
 
 LSUP_rc
-LSUP_term_deserialize (const LSUP_Buffer *sterm, LSUP_Term *term)
+LSUP_uri_init (LSUP_Term *term, const char *data)
 {
-    size_t cur;
-    char *data, *datatype = NULL;
-    langtag lang = "\00";
+    if (!data) {
+        uuid_t uuid;
+        uuid_generate_random (uuid);
 
-    char type = ((char*)(sterm->addr))[0];
+        uuid_str_t uuid_str;
+        uuid_unparse_lower (uuid, uuid_str);
 
-    cur = 1;
-    data = (char*)sterm->addr + cur;
-    cur += strlen (data) + 1;
+        char uri[UUIDSTR_SIZE + 10];
+        sprintf (uri, "urn:uuid4:%s", uuid_str);
 
-    if (type == LSUP_TERM_LITERAL && cur < sterm->size) {
-        datatype = (char*)sterm->addr + cur;
-        cur += strlen (datatype) + 1;
-        if (strlen (datatype) == 0)
-            datatype = NULL;
-
-        if (cur < sterm->size)
-            strcpy (lang, sterm->addr + cur);
+        data = uri;
     }
 
-    return LSUP_term_init (term, type, data, datatype, lang);
+    return LSUP_term_init (term, LSUP_TERM_IRIREF, data, NULL);
 }
 
 
@@ -255,23 +169,11 @@ bool LSUP_term_equals (const LSUP_Term *term1, const LSUP_Term *term2)
     if (strcmp (term1->data, term2->data) != 0)
         return false;
 
-    if (term1->type == LSUP_TERM_LITERAL) {
-        if ((term1->datatype == NULL) != (term2->datatype == NULL)) // XOR
-            return false;
-
-        if (
-                term1->datatype != NULL &&
-                strcmp (term1->datatype, term2->datatype) != 0)
-            return false;
+    if (term1->type == LSUP_TERM_LITERAL)
+        return term1->datatype == term2->datatype;
 
-        if ((term1->lang == NULL) != (term2->lang == NULL)) // XOR
-            return false;
-
-        if (
-                term1->lang != NULL &&
-                strcmp (term1->lang, term2->lang) != 0)
-            return false;
-    }
+    if (term1->type == LSUP_TERM_LT_LITERAL)
+        return term1->lang == term2->lang;
 
     return true;
 }
@@ -279,29 +181,52 @@ bool LSUP_term_equals (const LSUP_Term *term1, const LSUP_Term *term2)
 
 void LSUP_term_done (LSUP_Term *term)
 {
-    if (LIKELY (term->data != NULL)) {
-        free (term->data);
-        term->data = NULL;
-    }
-
-    if (term->datatype != NULL) {
-        free (term->datatype);
-        term->datatype = NULL;
-    }
+    free (term->data);
+    term->data = NULL;
 }
 
 
 void LSUP_term_free (LSUP_Term *term)
 {
     if (LIKELY (term != NULL)) {
-        LSUP_term_done (term);
+        free (term->data);
         free (term);
-        term = NULL;
     }
 }
 
 
+LSUP_rc
+LSUP_tcache_add_id (const uint32_t key, const char *data)
+{
+    struct id_cache_t *entry;
+
+    HASH_FIND_INT (LSUP_id_cache, &key, entry);
+    if (entry) return LSUP_NOACTION;
+
+    MALLOC_GUARD (entry, LSUP_MEM_ERR);
+    entry->key = key;
+    entry->data = strdup (data);
+    HASH_ADD_INT (LSUP_id_cache, key, entry);
+
+    return LSUP_OK;
+}
+
+
+const char *
+LSUP_tcache_get_id (const uint32_t key)
+{
+    struct id_cache_t *entry;
+
+    HASH_FIND_INT (LSUP_id_cache, &key, entry);
+    if (entry) log_trace ("Id found for key %d: %s", key, entry->data);
+    else log_trace ("No ID found for key %d.", key);
+
+    return (entry) ? entry->data : NULL;
+}
+
+
 // Extern inline functions.
 
 LSUP_Key LSUP_term_hash (const LSUP_Term *term);
-
+LSUP_Term *LSUP_uri_new (const char *data);
+LSUP_rc LSUP_uri_init (LSUP_Term *term, const char *data);

+ 5 - 103
src/triple.c

@@ -2,7 +2,7 @@
 
 // Extern inline prototypes.
 LSUP_Term *LSUP_triple_pos (const LSUP_Triple *trp, LSUP_TriplePos n);
-LSUP_Buffer *LSUP_striple_pos (const LSUP_SerTriple *trp, LSUP_TriplePos n);
+LSUP_Buffer *LSUP_btriple_pos (const LSUP_BufferTriple *trp, LSUP_TriplePos n);
 
 
 LSUP_Triple *
@@ -20,23 +20,8 @@ LSUP_triple_new(LSUP_Term *s, LSUP_Term *p, LSUP_Term *o)
 }
 
 
-LSUP_SerTriple *
-LSUP_striple_new(LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o)
-{
-    LSUP_SerTriple *sspo = malloc (sizeof (*sspo));
-    if (!sspo) return NULL;
-
-    if (UNLIKELY (LSUP_striple_init (sspo, s, p, o))) {
-        free (sspo);
-        return NULL;
-    }
-
-    return sspo;
-}
-
-
 LSUP_Triple *
-LSUP_triple_new_from_striple (const LSUP_SerTriple *sspo)
+LSUP_triple_new_from_btriple (const LSUP_BufferTriple *sspo)
 {
     LSUP_Triple *spo = malloc (sizeof (*spo));
     if (!spo) return NULL;
@@ -49,10 +34,10 @@ LSUP_triple_new_from_striple (const LSUP_SerTriple *sspo)
 }
 
 
-LSUP_SerTriple *
-LSUP_striple_new_from_triple (const LSUP_Triple *spo)
+LSUP_BufferTriple *
+LSUP_btriple_new_from_triple (const LSUP_Triple *spo)
 {
-    LSUP_SerTriple *sspo = malloc (sizeof (*sspo));
+    LSUP_BufferTriple *sspo = malloc (sizeof (*sspo));
     if (!sspo) return NULL;
 
     sspo->s = LSUP_buffer_new_from_term (spo->s);
@@ -74,54 +59,6 @@ LSUP_triple_init (LSUP_Triple *spo, LSUP_Term *s, LSUP_Term *p, LSUP_Term *o)
 }
 
 
-LSUP_rc
-LSUP_striple_init (
-        LSUP_SerTriple *sspo, LSUP_Buffer *s, LSUP_Buffer *p, LSUP_Buffer *o)
-{
-    sspo->s = s;
-    sspo->p = p;
-    sspo->o = o;
-
-    return LSUP_OK;
-}
-
-
-LSUP_rc
-LSUP_triple_serialize (const LSUP_Triple *spo, LSUP_SerTriple *sspo)
-{
-    LSUP_rc rc;
-
-    rc = LSUP_term_serialize (spo->s, sspo->s);
-    if (UNLIKELY (rc != LSUP_OK)) return rc;
-    rc = LSUP_term_serialize (spo->p, sspo->p);
-    if (UNLIKELY (rc != LSUP_OK)) return rc;
-    rc = LSUP_term_serialize (spo->o, sspo->o);
-    if (UNLIKELY (rc != LSUP_OK)) return rc;
-
-    return LSUP_OK;
-}
-
-
-LSUP_rc
-LSUP_triple_deserialize (const LSUP_SerTriple *sspo, LSUP_Triple *spo)
-{
-    LSUP_rc rc;
-
-    if (!spo->s) spo->s = malloc (sizeof (*spo->s));
-    if (!spo->p) spo->p = malloc (sizeof (*spo->p));
-    if (!spo->o) spo->o = malloc (sizeof (*spo->o));
-
-    rc = LSUP_term_deserialize (sspo->s, spo->s);
-    if (UNLIKELY (rc != LSUP_OK)) return rc;
-    rc = LSUP_term_deserialize (sspo->p, spo->p);
-    if (UNLIKELY (rc != LSUP_OK)) return rc;
-    rc = LSUP_term_deserialize (sspo->o, spo->o);
-    if (UNLIKELY (rc != LSUP_OK)) return rc;
-
-    return LSUP_OK;
-}
-
-
 void
 LSUP_triple_done (LSUP_Triple *spo)
 {
@@ -133,17 +70,6 @@ LSUP_triple_done (LSUP_Triple *spo)
 }
 
 
-void
-LSUP_striple_done (LSUP_SerTriple *sspo)
-{
-    if (UNLIKELY (!sspo)) return;
-
-    LSUP_buffer_done (sspo->s);
-    LSUP_buffer_done (sspo->p);
-    LSUP_buffer_done (sspo->o);
-}
-
-
 void
 LSUP_triple_free (LSUP_Triple *spo)
 {
@@ -157,30 +83,6 @@ LSUP_triple_free (LSUP_Triple *spo)
 }
 
 
-void
-LSUP_striple_free (LSUP_SerTriple *sspo)
-{
-    if (UNLIKELY (!sspo)) return;
-
-    LSUP_buffer_free (sspo->s);
-    LSUP_buffer_free (sspo->p);
-    LSUP_buffer_free (sspo->o);
-
-    free (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);
 LSUP_Key LSUP_triple_hash (const LSUP_Triple *trp);

+ 3 - 5
test/assets/triples.h

@@ -30,17 +30,15 @@ LSUP_Triple *create_triples()
 
     trp[4].s = LSUP_uri_new ("urn:s:0");
     trp[4].p = LSUP_uri_new ("urn:p:2");
-    trp[4].o = LSUP_term_new (LSUP_TERM_LITERAL, "String 1", NULL, NULL);
+    trp[4].o = LSUP_term_new (LSUP_TERM_LITERAL, "String 1", NULL);
 
     trp[5].s = LSUP_uri_new ("urn:s:0");
     trp[5].p = LSUP_uri_new ("urn:p:5");
-    trp[5].o = LSUP_term_new(
-            LSUP_TERM_LITERAL, "String 1", "xsd:string", NULL);
+    trp[5].o = LSUP_term_new(LSUP_TERM_LITERAL, "String 1", "xsd:string");
 
     trp[6].s = LSUP_uri_new ("urn:s:1");
     trp[6].p = LSUP_uri_new ("urn:p:6");
-    trp[6].o = LSUP_term_new(
-            LSUP_TERM_LITERAL, "String 1", "xsd:string", "es-ES");
+    trp[6].o = LSUP_term_new(LSUP_TERM_LT_LITERAL, "String 1", "es-ES");
 
     // Unique triple from reused pointers. Do not double-free.
     trp[7].s = trp[0].s; // <urn:s:0>

+ 8 - 8
test/test_codec_nt.c

@@ -11,18 +11,17 @@ init_terms (void)
     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", DEFAULT_DTYPE, "es-ES");
+    terms[2] = LSUP_term_new (LSUP_TERM_LITERAL, "hello", NULL);
+    terms[3] = LSUP_term_new (LSUP_TERM_LT_LITERAL, "hello", "en-US");
+    terms[4] = LSUP_term_new (LSUP_TERM_LT_LITERAL, "hello", "es-ES");
     terms[5] = LSUP_term_new (
             LSUP_TERM_LITERAL, "25",
-            "http://www.w3.org/2001/XMLSchema#integer", NULL);
+            "http://www.w3.org/2001/XMLSchema#integer");
     terms[6] = LSUP_term_new (
             LSUP_TERM_LITERAL, "This \\is\\ a \"multi-line\"\n'literal'\t.",
-            NULL, NULL);
-    terms[7] = LSUP_term_new (LSUP_TERM_BNODE, "bn1", NULL, NULL);
-    terms[8] = LSUP_term_new (LSUP_TERM_UNDEFINED, "bogus", NULL, NULL);
+            NULL);
+    terms[7] = LSUP_term_new (LSUP_TERM_BNODE, "bn1", NULL);
+    terms[8] = LSUP_term_new (LSUP_TERM_UNDEFINED, "bogus", NULL);
     terms[9] = TERM_DUMMY;
 
     return terms;
@@ -141,6 +140,7 @@ test_encode_nt_term()
     EXPECT_STR_EQ (out, end_nt[0]);
 
     for (int i = 0; i < TERM_CT - 2; i++) {
+        log_debug ("Test encoding term #%d of %d.", i, TERM_CT - 2);
         EXPECT_PASS (nt_codec.encode_term (terms[i], NULL, &out));
         EXPECT_STR_EQ (out, end_nt[i]);
     }

+ 4 - 4
test/test_store_ht.c

@@ -10,10 +10,10 @@ static int test_htstore()
     ASSERT (store != NULL, "Error initializing store!");
 
     LSUP_Triple *trp = create_triples();
-    LSUP_SerTriple *ser_trp[NUM_TRP];
+    LSUP_BufferTriple *ser_trp[NUM_TRP];
 
     for (int i = 0; i < NUM_TRP; i++)
-        ser_trp[i] = LSUP_striple_new_from_triple (trp + i);
+        ser_trp[i] = LSUP_btriple_new_from_triple (trp + i);
 
     // Test adding.
     LSUP_HTIterator *it = LSUP_htstore_add_init (store);
@@ -55,7 +55,7 @@ static int test_htstore()
         1, 0,
     };
 
-    LSUP_SerTriple *sspo = STRP_DUMMY;
+    LSUP_BufferTriple *sspo = STRP_DUMMY;
     for (int i = 0; i < NUM_TRP; i++) {
         size_t ct = 0;
         log_info ("Testing triple lookup #%d.", i);
@@ -74,7 +74,7 @@ static int test_htstore()
     free (sspo);
 
     for (int i = 0; i < NUM_TRP; i++)
-        LSUP_striple_free (ser_trp[i]);
+        LSUP_btriple_free (ser_trp[i]);
 
     LSUP_htstore_free (store);
     free_triples (trp);

+ 8 - 6
test/test_store_mdb.c

@@ -16,11 +16,12 @@ static int test_triple_store()
     ASSERT (store != NULL, "Error initializing store!");
 
     LSUP_Triple *trp = create_triples();
-    LSUP_SerTriple ser_trp[NUM_TRP];
+    LSUP_BufferTriple 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);
+        LSUP_BufferTriple *tmp = LSUP_btriple_new_from_triple (trp + i);
+        ser_trp[i] = *tmp;
+        free (tmp);
     }
 
     // Test adding.
@@ -113,11 +114,12 @@ static int test_quad_store()
     ASSERT (store != NULL, "Error initializing store!");
 
     LSUP_Triple *trp = create_triples();
-    LSUP_SerTriple ser_trp[NUM_TRP];
+    LSUP_BufferTriple 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);
+        LSUP_BufferTriple *tmp = LSUP_btriple_new_from_triple (trp + i);
+        ser_trp[i] = *tmp;
+        free (tmp);
     }
 
     // Only triples 0÷5 in default context.

+ 39 - 32
test/test_term.c

@@ -5,20 +5,18 @@ static int test_term_new()
 {
     char *data = "hello";
     char *datatype = "xsd:string";
-    char *lang = "en-US";
-
-    log_info ("Test term, heap-allocated.");
-    LSUP_Term *term = LSUP_term_new (LSUP_TERM_LITERAL, data, datatype, lang);
 
-    log_info ("Term data: %s", term->data);
+    LSUP_Term *term = LSUP_term_new (LSUP_TERM_LITERAL, data, datatype);
     EXPECT_STR_EQ (term->data, data);
-    EXPECT_STR_EQ (term->datatype, datatype);
-    EXPECT_STR_EQ (term->lang, lang);
+    EXPECT_STR_EQ (LSUP_tcache_get_id(term->datatype), datatype);
 
-    log_info ("Reset term.");
+    char *lang = "en-US";
+    LSUP_term_init (term, LSUP_TERM_LT_LITERAL, data, lang);
+    EXPECT_STR_EQ (term->data, data);
+    EXPECT_STR_EQ (LSUP_tcache_get_id(term->lang), lang);
 
     char *uri_data = "urn:id:2144564356";
-    LSUP_term_init (term, LSUP_TERM_URI, uri_data, NULL, NULL);
+    LSUP_uri_init (term, uri_data);
     EXPECT_STR_EQ (term->data, uri_data);
 
     LSUP_term_free (term);
@@ -29,47 +27,60 @@ static int test_term_new()
 static int test_term_serialize_deserialize()
 {
     LSUP_Term *uri = LSUP_uri_new ("http://hello.org");
-    LSUP_Term *lit = LSUP_term_new (LSUP_TERM_LITERAL, "hello", NULL, NULL);
-    LSUP_Term *tlit = LSUP_term_new (LSUP_TERM_LITERAL, "hello", "xsd:string", NULL);
-    LSUP_Term *tllit = LSUP_term_new (LSUP_TERM_LITERAL, "hello", "xsd:string", "en-US");
+    LSUP_Term *lit = LSUP_term_new (LSUP_TERM_LITERAL, "hello", NULL);
+    LSUP_Term *tlit = LSUP_term_new (LSUP_TERM_LITERAL, "hello", "xsd:string");
+    LSUP_Term *tllit = LSUP_term_new (LSUP_TERM_LT_LITERAL, "hello", "en-US");
 
-    LSUP_Buffer *sterm = BUF_DUMMY;
-    LSUP_Term *dsterm = TERM_DUMMY;
+    LSUP_Buffer *sterm;
+    LSUP_Term *dsterm;
 
-    LSUP_term_serialize (uri, sterm);
+    sterm = LSUP_buffer_new_from_term (uri);
+    ASSERT (sterm != NULL, "Error serializing term!");
     //log_info ("%s", "Serialized URI: ");
     //LSUP_buffer_print (sterm);
     //log_info ("%s", "\n");
-    LSUP_term_deserialize (sterm, dsterm);
+    dsterm = LSUP_term_new_from_buffer (sterm);
+    ASSERT (dsterm != NULL, "Error deserializing term!");
     ASSERT (LSUP_term_equals (dsterm, uri), "URI serialization error!");
     LSUP_term_free (uri);
+    LSUP_buffer_free (sterm);
+    LSUP_term_free (dsterm);
 
-    LSUP_term_serialize (lit, sterm);
+    sterm = LSUP_buffer_new_from_term (lit);
+    ASSERT (sterm != NULL, "Error serializing term!");
     //log_info ("%s", "Serialized literal: ");
     //LSUP_buffer_print (sterm);
     //log_info ("%s", "\n");
-    LSUP_term_deserialize (sterm, dsterm);
+    dsterm = LSUP_term_new_from_buffer (sterm);
+    ASSERT (dsterm != NULL, "Error deserializing term!");
     ASSERT (LSUP_term_equals (dsterm, lit), "lit serialization error!");
     LSUP_term_free (lit);
+    LSUP_buffer_free (sterm);
+    LSUP_term_free (dsterm);
 
-    LSUP_term_serialize (tlit, sterm);
+    sterm = LSUP_buffer_new_from_term (tlit);
+    ASSERT (sterm != NULL, "Error serializing term!");
     //log_info ("%s", "Serialized typed literal: ");
     //LSUP_buffer_print (sterm);
     //log_info ("%s", "\n");
-    LSUP_term_deserialize (sterm, dsterm);
+    dsterm = LSUP_term_new_from_buffer (sterm);
+    ASSERT (dsterm != NULL, "Error deserializing term!");
     ASSERT (LSUP_term_equals (dsterm, tlit), "tlit serialization error!");
     LSUP_term_free (tlit);
+    LSUP_buffer_free (sterm);
+    LSUP_term_free (dsterm);
 
-    LSUP_term_serialize (tllit, sterm);
+    sterm = LSUP_buffer_new_from_term (tllit);
+    ASSERT (sterm != NULL, "Error serializing term!");
     //log_info ("%s", "Serialized typed and language-tagged URI: ");
     //LSUP_buffer_print (sterm);
     //log_info ("%s", "\n");
-    LSUP_term_deserialize (sterm, dsterm);
+    dsterm = LSUP_term_new_from_buffer (sterm);
+    ASSERT (dsterm != NULL, "Error deserializing term!");
     ASSERT (LSUP_term_equals (dsterm, tllit), "URI serialization error!");
     LSUP_term_free (tllit);
-
-    LSUP_term_free (dsterm);
     LSUP_buffer_free (sterm);
+    LSUP_term_free (dsterm);
 
     return 0;
 }
@@ -78,14 +89,10 @@ static int test_term_serialize_deserialize()
 static int test_term_to_key()
 {
     LSUP_Term *uri = LSUP_uri_new ("http://hello.org");
-    LSUP_Term *lit = LSUP_term_new (LSUP_TERM_LITERAL, "hello", NULL, NULL);
-    LSUP_Term *tlit = LSUP_term_new(
-            LSUP_TERM_LITERAL, "hello", DEFAULT_DTYPE, NULL);
-    LSUP_Term *tllit1 = LSUP_term_new(
-            LSUP_TERM_LITERAL, "hello", NULL, "en-US");
-    LSUP_Term *tllit2 = LSUP_term_new(
-            LSUP_TERM_LITERAL, "hello",
-            "http://www.w3.org/2001/XMLSchema#string", "en-GB");
+    LSUP_Term *lit = LSUP_term_new (LSUP_TERM_LITERAL, "hello", NULL);
+    LSUP_Term *tlit = LSUP_term_new(LSUP_TERM_LITERAL, "hello", DEFAULT_DTYPE);
+    LSUP_Term *tllit1 = LSUP_term_new(LSUP_TERM_LT_LITERAL, "hello", "en-US");
+    LSUP_Term *tllit2 = LSUP_term_new(LSUP_TERM_LT_LITERAL, "hello", "en-GB");
 
     LSUP_Key uri_key = LSUP_term_hash (uri);
     LSUP_Key lit_key = LSUP_term_hash (lit);