Browse Source

Kinda working Python bindings.

Stefano Cossu 3 years ago
parent
commit
d8d2ce73ff
12 changed files with 552 additions and 167 deletions
  1. 46 2
      .gitignore
  2. 262 0
      cpython/graph_obj.h
  3. 29 0
      cpython/lsup_rdf/__init__.py
  4. 120 0
      cpython/lsup_rdf_mod.c
  5. 0 42
      cpython/term_mod.c
  6. 0 37
      cpython/triple_mod.c
  7. 22 40
      cpython/triple_obj.h
  8. 18 5
      include/graph.h
  9. 2 2
      include/term.h
  10. 12 23
      setup.py
  11. 37 16
      src/graph.c
  12. 4 0
      src/store_mdb.c

+ 46 - 2
.gitignore

@@ -61,6 +61,50 @@ bin/*
 vgcore.*
 callgrind.out.*
 
-# Local IDE
-.vimrc
+
+## PYTHON ##
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+## LOCAL ##
+
 sandbox.c
+
+# IDE
+.syntastic*
+.vimrc
+tags

+ 262 - 0
cpython/graph_obj.h

@@ -0,0 +1,262 @@
+#ifndef _PY_GRAPH_MOD_H
+#define _PY_GRAPH_MOD_H
+
+#define PY_SSIZE_T_CLEAN
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "graph.h"
+#include "triple_obj.h"
+
+
+typedef struct {
+    PyObject_HEAD
+    LSUP_Graph *ob_struct;
+} GraphObject;
+
+
+static int
+Graph_init (GraphObject *self, PyObject *args, PyObject *kwargs)
+{
+    unsigned char store_type;
+
+    if (!PyArg_ParseTuple (args, "b", &store_type))
+        return -1;
+
+    self->ob_struct = LSUP_graph_new ((LSUP_store_type)store_type);
+    if (!self->ob_struct) {
+        PyErr_SetString (PyExc_ValueError, "Could not create graph.");
+        return -1;
+    }
+
+    return 0;
+}
+
+static void
+Graph_dealloc (GraphObject *self)
+{
+    LSUP_graph_free (self->ob_struct);
+    Py_TYPE (self)->tp_free ((PyObject *) self);
+}
+
+
+static PyObject *
+Graph_get_size (GraphObject *self, void *closure)
+{
+    size_t gr_size = LSUP_graph_size (self->ob_struct);
+    PyObject *size = PyLong_FromSize_t (gr_size);
+
+    Py_INCREF (size);
+    return size;
+}
+
+/*
+ * FIXME This should be deleted and the setter set to NULL or omitted, but
+ * in doing so Python 3.8 raises a SystemError: bad call flags. See
+ * https://bugs.python.org/issue39884 No resolution is clear yet.
+ */
+static int
+Graph_set_size (GraphObject *self, PyObject value, void *closure)
+{ return -1; }
+
+
+static PyObject *
+Graph_get_uri (GraphObject *self, void *closure)
+{
+    PyObject *uri = PyUnicode_FromString (
+            LSUP_graph_uri (self->ob_struct)->data);
+
+    Py_INCREF (uri);
+    return uri;
+}
+
+
+static int
+Graph_set_uri (GraphObject *self, PyObject *uri, void *closure)
+{
+    if (PyUnicode_READY (uri) != 0) return -1; // TODO redundant?
+    if (PyUnicode_KIND (uri) != PyUnicode_1BYTE_KIND) return -1;
+
+    LSUP_rc rc = LSUP_graph_set_uri (self->ob_struct, PyUnicode_DATA (uri));
+
+    return rc == LSUP_OK ? 0 : -1;
+}
+
+
+static PyGetSetDef Graph_getsetters[] = {
+    {
+        .name = "__len__",
+        .get = (getter) Graph_get_size,
+        .set = (setter) Graph_set_size,
+        .doc = "Number of triples in the graph.",
+        //.closure = NULL,
+    },
+    {
+        "uri", (getter) Graph_get_uri, (setter) Graph_set_uri,
+        "Graph URI.", NULL
+    },
+    {NULL}
+};
+
+
+static PyObject *
+Graph_copy (PyTypeObject *cls, PyObject *src)
+{
+    if (! PyObject_TypeCheck (src, cls)) return NULL;
+
+    GraphObject *res = (GraphObject *) cls->tp_alloc(cls, 0);
+    if (!res) return NULL;
+
+    res->ob_struct = LSUP_graph_copy (((GraphObject *) src)->ob_struct);
+
+    Py_INCREF(res);
+    return (PyObject *) res;
+}
+
+
+/* TODO implement LSUP_graph_equals
+static PyObject *
+Graph_richcmp (PyObject *gr1, PyObject *gr2, int op)
+{
+    if (op != Py_EQ && op != Py_NE) Py_RETURN_NOTIMPLEMENTED;
+
+    PyObject *res = NULL;
+
+    LSUP_Graph *t1 = ((GraphObject *) gr1)->ob_struct;
+    LSUP_Graph *t2 = ((GraphObject *) gr2)->ob_struct;
+
+    if (LSUP_graph_equals (t1, t2) && op == Py_EQ) Py_RETURN_TRUE;
+    Py_RETURN_FALSE;
+ }
+*/
+
+
+/* TODO Implement mdbstore bool ops
+static inline PyObject *
+Graph_bool_op (
+        PyTypeObject *cls, LSUP_bool_op op, PyObject *gr1, PyObject *gr2)
+{
+    if (! PyObject_TypeCheck (gr1, cls) || ! PyObject_TypeCheck (gr2, cls))
+        return NULL;
+
+    GraphObject *res = (GraphObject *) cls->tp_alloc (cls, 0);
+    if (!res) return NULL;
+
+    res->ob_struct = LSUP_graph_bool_op (
+            op, ((GraphObject *) gr1)->ob_struct,
+            ((GraphObject *) gr2)->ob_struct);
+
+    Py_INCREF(res);
+    return (PyObject *) res;
+}
+*/
+
+
+static int Graph_add (PyObject *self, PyObject *triples)
+{
+    // Triple may be any iterable.
+    if (! PyIter_Check (triples)) return -1;
+
+    PyObject *trp_obj = NULL;
+    int rc = 0;
+    size_t i;
+    LSUP_SerTriple *sspo = STRP_DUMMY;
+    LSUP_GraphIterator *it = LSUP_graph_add_stream_init (
+            ((GraphObject *)self)->ob_struct);
+
+    for (i = 0; (trp_obj = PyIter_Next (triples)); i++) {
+        if (!PyObject_TypeCheck (trp_obj, &TripleType)) {
+            rc = -1;
+            goto finalize;
+        }
+
+        TRACE ("Inserting triple #%lu\n", i);
+
+        LSUP_triple_serialize (((TripleObject *)trp_obj)->ob_struct, sspo);
+        LSUP_rc db_rc = LSUP_graph_add_stream_iter (it, sspo);
+
+        if (db_rc == LSUP_OK) rc = LSUP_OK;
+        if (UNLIKELY (db_rc < 0)) {
+            rc = -1;
+            goto finalize;
+        }
+    }
+    LSUP_striple_free (sspo);
+
+finalize:
+    LSUP_graph_add_stream_done (it);
+    LSUP_striple_free (sspo);
+
+    return rc;
+}
+
+
+static int Graph_remove (PyObject *self, PyObject *s, PyObject *p, PyObject *o)
+{
+    return 0;
+}
+
+static PyObject *Graph_lookup (
+        PyObject *self, PyObject *s, PyObject *p, PyObject *o)
+{
+    return NULL;
+}
+
+
+static PyMethodDef Graph_methods[] = {
+    {"copy", (PyCFunction) Graph_copy, METH_CLASS, "Copy a graph."},
+    {"add", (PyCFunction) Graph_add, METH_O, "Add triples to a graph."},
+    {
+        "remove", (PyCFunction) Graph_remove, METH_VARARGS,
+        "Remove triples from a graph by matching a pattern."
+    },
+    {
+        "lookup", (PyCFunction) Graph_lookup, METH_VARARGS,
+        "Look triples in a graph by matching a pattern."
+    },
+};
+
+
+/* TODO Implement mdbstore bool ops
+static inline PyObject *Graph_bool_and (
+        PyTypeObject *cls, PyObject *gr1, PyObject *gr2)
+{ return Graph_bool_op (cls, LSUP_BOOL_INTERSECTION, gr1, gr2); }
+
+static inline PyObject *Graph_bool_or (
+        PyTypeObject *cls, PyObject *gr1, PyObject *gr2)
+{ return Graph_bool_op (cls, LSUP_BOOL_UNION, gr1, gr2); }
+
+static inline PyObject *Graph_bool_subtract (
+        PyTypeObject *cls, PyObject *gr1, PyObject *gr2)
+{ return Graph_bool_op (cls, LSUP_BOOL_SUBTRACTION, gr1, gr2); }
+
+static inline PyObject *Graph_bool_xor (
+        PyTypeObject *cls, PyObject *gr1, PyObject *gr2)
+{ return Graph_bool_op (cls, LSUP_BOOL_XOR, gr1, gr2); }
+
+static PyNumberMethods Graph_number_methods = {
+    .nb_and = (binaryfunc) Graph_bool_and,
+    .nb_or = (binaryfunc) Graph_bool_or,
+    .nb_subtract = (binaryfunc) Graph_bool_subtract,
+    .nb_xor = (binaryfunc) Graph_bool_xor,
+};
+*/
+
+PyTypeObject GraphType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "graph.Graph",
+    .tp_doc = "RDF graph",
+    .tp_basicsize = sizeof(GraphObject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_new = PyType_GenericNew,
+    .tp_init = (initproc) Graph_init,
+    .tp_dealloc = (destructor) Graph_dealloc,
+    .tp_methods = Graph_methods,
+    .tp_getset = Graph_getsetters,
+    //.tp_richcompare = Graph_richcmp, TODO implement LSUP_graph_equals
+    //.tp_as_number = &Graph_number_methods,
+};
+
+#endif

+ 29 - 0
cpython/lsup_rdf/__init__.py

@@ -0,0 +1,29 @@
+# lsup_rdf package.
+# All "modules" are inside the monolythic lsup_rdf extension because they have
+# cross-linked symbols, so they need a bit of work to be exposed at the main
+# level.
+
+import importlib.util
+import sys
+
+import _lsup_rdf
+
+
+pkg_path = _lsup_rdf.__file__
+
+
+def _load_module(mod_name, path):
+    """
+    Replacement of deprecated imp.load_dynamic(). See
+    https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
+    """
+    spec = importlib.util.spec_from_file_location(mod_name, path)
+    module = importlib.util.module_from_spec(spec)
+    sys.modules[mod_name] = module
+    spec.loader.exec_module(module)
+
+    return module
+
+
+for mod_name in ('term', 'triple', 'graph'):
+    locals()[mod_name] = _load_module(mod_name, pkg_path)

+ 120 - 0
cpython/lsup_rdf_mod.c

@@ -0,0 +1,120 @@
+/** @file lsup_rdf_mod.c
+ *
+ * @brief LSUP_RDF package.
+ *
+ * This "package" module includes all RDF modules in a monolithic compiled
+ * object, in order to resolve internal symbol dependencies. Individual modules
+ * are extracted and exposed by the package __init__.py .
+ */
+
+#define PY_SSIZE_T_CLEAN
+
+#include <Python.h>
+
+#include "term_obj.h"
+#include "triple_obj.h"
+#include "graph_obj.h"
+
+
+static PyModuleDef lsup_rdf_pkg = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "lsup_rdf",
+    .m_doc = "Lakesuperior RDF package.",
+    .m_size = -1,
+};
+
+PyMODINIT_FUNC
+PyInit__lsup_rdf (void)
+{ return PyModule_Create (&lsup_rdf_pkg); }
+
+
+PyModuleDef term_mod = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "term",
+    .m_doc = "RDF term module.",
+    .m_size = -1,
+};
+
+
+PyMODINIT_FUNC
+PyInit_term()
+{
+    if (PyType_Ready (&TermType) < 0) return NULL;
+
+    PyObject *m = PyModule_Create(&term_mod);
+    if (m == NULL) return NULL;
+
+#define ENTRY(a, b) \
+    if (PyModule_AddIntConstant (m, "TERM_" #a, b) < 0) return NULL;
+    TTYPE_TBL
+#undef ENTRY
+
+    Py_INCREF(&TermType);
+    if (PyModule_AddObject(m, "Term", (PyObject *) &TermType) < 0) {
+        Py_DECREF(&TermType);
+        Py_DECREF(m);
+        return NULL;
+    }
+
+    return m;
+}
+
+
+PyModuleDef triple_mod = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "triple",
+    .m_doc = "RDF triple module.",
+    .m_size = -1,
+};
+
+
+PyMODINIT_FUNC
+PyInit_triple()
+{
+    if (PyType_Ready (&TripleType) < 0) return NULL;
+
+    PyObject *m = PyModule_Create(&triple_mod);
+    if (m == NULL) return NULL;
+
+    Py_INCREF(&TripleType);
+    if (PyModule_AddObject(m, "Triple", (PyObject *) &TripleType) < 0) {
+        Py_DECREF(&TripleType);
+        Py_DECREF(m);
+        return NULL;
+    }
+
+    return m;
+}
+
+
+static PyModuleDef graph_mod = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "graph",
+    .m_doc = "RDF graph module.",
+    .m_size = -1,
+};
+
+
+PyMODINIT_FUNC
+PyInit_graph()
+{
+    if (PyType_Ready (&GraphType) < 0) return NULL;
+
+    PyObject *m = PyModule_Create(&graph_mod);
+    if (m == NULL) return NULL;
+
+#define ENTRY(a, b) \
+    if (PyModule_AddIntConstant (m, "STORE_" #a, b) < 0) return NULL;
+    BACKEND_TBL
+#undef ENTRY
+
+    Py_INCREF(&GraphType);
+    if (PyModule_AddObject(m, "Graph", (PyObject *) &GraphType) < 0) {
+        Py_DECREF(&GraphType);
+        Py_DECREF(m);
+        return NULL;
+    }
+
+    return m;
+}
+

+ 0 - 42
cpython/term_mod.c

@@ -1,42 +0,0 @@
-#ifndef _PY_TERM_MOD_H
-#define _PY_TERM_MOD_H
-
-#define PY_SSIZE_T_CLEAN
-
-#include <Python.h>
-
-#include "term_obj.h"
-
-
-PyModuleDef term_mod = {
-    PyModuleDef_HEAD_INIT,
-    .m_name = "term",
-    .m_doc = "RDF term module.",
-    .m_size = -1,
-};
-
-
-PyMODINIT_FUNC
-PyInit_term(void)
-{
-    if (PyType_Ready (&TermType) < 0) return NULL;
-
-    PyObject *m = PyModule_Create(&term_mod);
-    if (m == NULL) return NULL;
-
-#define ENTRY(a, b) \
-    if (PyModule_AddIntConstant (m, "TERM_" #a, b) < 0) return NULL;
-    TTYPE_TABLE
-#undef ENTRY
-
-    Py_INCREF(&TermType);
-    if (PyModule_AddObject(m, "Term", (PyObject *) &TermType) < 0) {
-        Py_DECREF(&TermType);
-        Py_DECREF(m);
-        return NULL;
-    }
-
-    return m;
-}
-
-#endif

+ 0 - 37
cpython/triple_mod.c

@@ -1,37 +0,0 @@
-#ifndef _PY_TRIPLE_MOD_H
-#define _PY_TRIPLE_MOD_H
-
-#define PY_SSIZE_T_CLEAN
-
-#include <Python.h>
-
-#include "triple_obj.h"
-
-
-PyModuleDef triple_mod = {
-    PyModuleDef_HEAD_INIT,
-    .m_name = "triple",
-    .m_doc = "RDF triple module.",
-    .m_size = -1,
-};
-
-
-PyMODINIT_FUNC
-PyInit_triple(void)
-{
-    if (PyType_Ready (&TripleType) < 0) return NULL;
-
-    PyObject *m = PyModule_Create(&triple_mod);
-    if (m == NULL) return NULL;
-
-    Py_INCREF(&TripleType);
-    if (PyModule_AddObject(m, "Triple", (PyObject *) &TripleType) < 0) {
-        Py_DECREF(&TripleType);
-        Py_DECREF(m);
-        return NULL;
-    }
-
-    return m;
-}
-
-#endif

+ 22 - 40
cpython/triple_obj.h

@@ -22,40 +22,31 @@ typedef struct {
 static int
 Triple_init (TripleObject *self, PyObject *args)
 {
-    TermObject *s = NULL, *p = NULL, *o = NULL, *tmp;
+    PyObject *s = NULL, *p = NULL, *o = NULL, *tmp;
 
     if (! PyArg_ParseTuple (
-    /*
-                args, "O!O!O!", &s, &TermType,
-                &p, &TermType, &o, &TermType))
-    */
-                args, "OOO", &s, &p, &o))
+                args, "O!O!O!", &TermType, &s,
+                &TermType, &p, &TermType, &o))
         return -1;
 
     // TODO check for null s, p, o
-    if (
-        PyObject_TypeCheck (s, &TermType) ||
-        PyObject_TypeCheck (p, &TermType) ||
-        PyObject_TypeCheck (o, &TermType)
-    ) return -1;
-
     tmp = self->s;
     Py_INCREF(s);
-    self->s = s;
+    self->s = (TermObject *) s;
     Py_XDECREF(tmp);
 
     tmp = self->p;
     Py_INCREF(p);
-    self->p = p;
+    self->p = (TermObject *) p;
     Py_XDECREF(tmp);
 
     tmp = self->o;
     Py_INCREF(o);
-    self->o = o;
+    self->o = (TermObject *) o;
     Py_XDECREF(tmp);
 
     self->ob_struct = LSUP_triple_new (
-            s->ob_struct, p->ob_struct, o->ob_struct);
+            self->s->ob_struct, self->p->ob_struct, self->o->ob_struct);
     if (!self->ob_struct) {
         PyErr_SetString (PyExc_ValueError, "Could not create triple.");
         return -1;
@@ -84,35 +75,11 @@ static PyMemberDef Triple_members[] = {
 };
 
 
-static PyObject *
-Triple_richcmp (PyObject *obj1, PyObject *obj2, int op);
-
-
-PyTypeObject TripleType = {
-    PyVarObject_HEAD_INIT(NULL, 0)
-    .tp_name = "triple.Triple",
-    .tp_doc = "RDF triple",
-    .tp_basicsize = sizeof(TripleObject),
-    .tp_itemsize = 0,
-    .tp_flags = Py_TPFLAGS_DEFAULT,
-    .tp_new = PyType_GenericNew,
-    .tp_init = (initproc) Triple_init,
-    .tp_dealloc = (destructor) Triple_dealloc,
-    .tp_members = Triple_members,
-    .tp_richcompare = Triple_richcmp,
-};
-
-
 static PyObject *
 Triple_richcmp (PyObject *obj1, PyObject *obj2, int op)
 {
     PyObject *result = NULL;
 
-    if (
-        ! PyObject_TypeCheck (obj1, &TripleType) ||
-        ! PyObject_TypeCheck (obj2, &TripleType)
-    ) return NULL;
-
     int c = 0;
     LSUP_Triple *t1 = ((TripleObject *) obj1)->ob_struct;
     LSUP_Triple *t2 = ((TripleObject *) obj2)->ob_struct;
@@ -138,4 +105,19 @@ Triple_richcmp (PyObject *obj1, PyObject *obj2, int op)
     return result;
  }
 
+
+PyTypeObject TripleType = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    .tp_name = "triple.Triple",
+    .tp_doc = "RDF triple",
+    .tp_basicsize = sizeof(TripleObject),
+    .tp_itemsize = 0,
+    .tp_flags = Py_TPFLAGS_DEFAULT,
+    .tp_new = PyType_GenericNew,
+    .tp_init = (initproc) Triple_init,
+    .tp_dealloc = (destructor) Triple_dealloc,
+    .tp_members = Triple_members,
+    .tp_richcompare = Triple_richcmp,
+};
+
 #endif

+ 18 - 5
include/graph.h

@@ -7,13 +7,13 @@
  * Define backend types and checks.
  */
 
-#define BACKEND_TBL                                                         \
-    ENTRY(  LSUP_STORE_MEM         )/* Memory backend, hash map. */         \
-    ENTRY(  LSUP_STORE_MDB         )/* LMDB back end on persistent disk. */ \
-    ENTRY(  LSUP_STORE_MDB_TMP     )/* LMDB back end on RAM disk. */        \
+#define BACKEND_TBL                                              \
+    ENTRY(  MEM,       0)/* Memory backend, hash map. */         \
+    ENTRY(  MDB,       1)/* LMDB back end on persistent disk. */ \
+    ENTRY(  MDB_TMP,   2)/* LMDB back end on RAM disk. */        \
 
 typedef enum LSUP_store_type {
-#define ENTRY(x) x,
+#define ENTRY(a, b) LSUP_STORE_##a = b,
     BACKEND_TBL
 #undef ENTRY
 } LSUP_store_type;
@@ -145,6 +145,19 @@ bool
 LSUP_graph_contains (const LSUP_Graph *gr, const LSUP_Triple *spo);
 
 
+LSUP_GraphIterator *
+LSUP_graph_add_stream_init (LSUP_Graph *gr);
+
+
+LSUP_rc
+LSUP_graph_add_stream_iter (
+        LSUP_GraphIterator *it, const LSUP_SerTriple *sspo);
+
+
+void
+LSUP_graph_add_stream_done (LSUP_GraphIterator *it);
+
+
 /** @brief Add triples and/or serialized triples to a graph.
  *
  * For API users it may be more convenient to use the more specialized

+ 2 - 2
include/term.h

@@ -28,7 +28,7 @@
 typedef XXH64_hash_t LSUP_TermHash64;
 typedef char langtag[LANG_SIZE];
 
-#define TTYPE_TABLE \
+#define TTYPE_TBL \
     ENTRY (UNDEFINED,     0) \
     ENTRY (URI,           1) \
     ENTRY (BNODE,         2) \
@@ -36,7 +36,7 @@ typedef char langtag[LANG_SIZE];
 
 typedef enum LSUP_term_type {
 #define ENTRY(a, b) LSUP_TERM_##a = b,
-    TTYPE_TABLE
+    TTYPE_TBL
 #undef ENTRY
 } LSUP_term_type;
 

+ 12 - 23
setup.py

@@ -1,4 +1,4 @@
-# from glob import glob
+from glob import glob
 from os import path
 from setuptools import Extension, setup
 
@@ -10,8 +10,8 @@ INCL_DIR = path.join(ROOT_DIR, 'include')
 EXT_DIR = path.join(ROOT_DIR, 'ext')
 
 sources = (
-    # glob(path.join(SRC_DIR, '*.c')) +
-    # glob(path.join(MOD_DIR, '*.c')) +
+    glob(path.join(SRC_DIR, '*.c')) +
+    glob(path.join(MOD_DIR, '*.c')) +
     [
         path.join(EXT_DIR, 'xxHash', 'xxhash.c'),
         path.join(EXT_DIR, 'openldap', 'libraries', 'liblmdb', 'mdb.c'),
@@ -21,31 +21,20 @@ sources = (
 
 compile_args = ['-std=c99', '-DDEBUG', '-g3']
 
+
 setup(
     name="lsup_rdf",
     version="1.0a1",
+    description='Ultra-compact RDF library.',
+    author='Stefano Cossu <https://notabug.org/scossu>',
+    url='https://notabug.org/scossu/lsup_rdf',
+    license='https://notabug.org/scossu/lsup_rdf/src/master/LICENSE',
+    package_dir={'lsup_rdf': path.join(MOD_DIR, 'lsup_rdf')},
+    packages=['lsup_rdf'],
     ext_modules=[
         Extension(
-            "lsup_rdf.term",
-            [
-                path.join(EXT_DIR, 'xxHash', 'xxhash.c'),
-                path.join(SRC_DIR, 'buffer.c'),
-                path.join(SRC_DIR, 'term.c'),
-                path.join(MOD_DIR, 'term_mod.c'),
-            ],
-            include_dirs=[INCL_DIR],
-            libraries=['uuid'],
-            extra_compile_args=compile_args,
-        ),
-        Extension(
-            "lsup_rdf.triple",
-            [
-                path.join(EXT_DIR, 'xxHash', 'xxhash.c'),
-                path.join(SRC_DIR, 'buffer.c'),
-                path.join(SRC_DIR, 'term.c'),
-                path.join(SRC_DIR, 'triple.c'),
-                path.join(MOD_DIR, 'triple_mod.c'),
-            ],
+            "_lsup_rdf",
+            sources,
             include_dirs=[INCL_DIR],
             libraries=['uuid'],
             extra_compile_args=compile_args,

+ 37 - 16
src/graph.c

@@ -87,7 +87,7 @@ static inline bool is_null_trp (const LSUP_TripleKey *trp)
             *trp[2] == NULL_KEY);
 }
 
-#define ENTRY(x) (be) == (x) ||
+#define ENTRY(a, b) (be) == (LSUP_STORE_##a) ||
 static inline bool
 check_backend (LSUP_store_type be)
 { return (BACKEND_TBL false); }
@@ -255,6 +255,33 @@ LSUP_graph_size (const Graph *gr)
 // TODO Add add_stream_init, add_stream_iter and add_stream_done for streaming
 // functions.
 
+GraphIterator *
+LSUP_graph_add_stream_init (LSUP_Graph *gr)
+{
+    GraphIterator *it = malloc (sizeof (*it));
+    if (!it) return NULL;
+
+    LSUP_Buffer *sc = LSUP_buffer_new_from_term (gr->uri);
+    it->mdb_iter = LSUP_mdbstore_add_init (gr->mdb_store, sc);
+    LSUP_buffer_free (sc);
+
+    it->graph = gr;
+    it->i = 0;
+
+    return it;
+}
+
+
+LSUP_rc
+LSUP_graph_add_stream_iter (LSUP_GraphIterator *it, const LSUP_SerTriple *sspo)
+{ return LSUP_mdbstore_add_iter (it->mdb_iter, sspo); }
+
+
+void
+LSUP_graph_add_stream_done (LSUP_GraphIterator *it)
+{ LSUP_mdbstore_add_done(it->mdb_iter); }
+
+
 LSUP_rc
 LSUP_graph_add(
         LSUP_Graph *gr,
@@ -318,9 +345,8 @@ LSUP_graph_add(
     } else {
         rc = LSUP_NOACTION;
 
-        LSUP_Buffer *sc = LSUP_buffer_new_from_term (gr->uri);
-        LSUP_MDBIterator *it = LSUP_mdbstore_add_init (gr->mdb_store, sc);
-        LSUP_buffer_free (sc);
+        // Initialize iterator.
+        LSUP_GraphIterator *it = LSUP_graph_add_stream_init (gr);
 
         // Serialize and insert RDF triples.
         if (trp_ct > 0) {
@@ -330,13 +356,9 @@ LSUP_graph_add(
                 TRACE ("Inserting triple #%lu\n", i);
 
                 LSUP_triple_serialize (trp + i, sspo);
-                LSUP_rc db_rc = LSUP_mdbstore_add_iter (it, sspo);
-
-                if (LIKELY (db_rc == LSUP_OK)) {
-                    rc = LSUP_OK;
-                    if (inserted) (*inserted) ++;
-                }
+                LSUP_rc db_rc = LSUP_graph_add_stream_iter (it, sspo);
 
+                if (db_rc == LSUP_OK) rc = LSUP_OK;
                 if (UNLIKELY (db_rc < 0)) return db_rc;
             }
 
@@ -346,16 +368,15 @@ LSUP_graph_add(
         // Insert serialized triples.
         for (size_t i = 0; i < strp_ct; i++) {
             TRACE ("Inserting triple #%lu\n", i);
-            LSUP_rc db_rc = LSUP_mdbstore_add_iter (it, strp + i);
+            LSUP_rc db_rc = LSUP_graph_add_stream_iter (it, strp + i);
 
-            if (LIKELY (db_rc == LSUP_OK)) {
-                rc = LSUP_OK;
-                if (inserted) (*inserted) ++;
-            }
+            if (db_rc == LSUP_OK) rc = LSUP_OK;
             if (UNLIKELY (db_rc < 0)) return db_rc;
         }
 
-        LSUP_mdbstore_add_done (it);
+        LSUP_graph_add_stream_done (it);
+
+        if (inserted) *inserted = it->ct;
 
         return rc;
     }

+ 4 - 0
src/store_mdb.c

@@ -1084,8 +1084,10 @@ lookup_1bound (MDBStore *store, uint8_t idx0, MDBIterator *it, size_t *ct)
             CRITICAL (ct_it = malloc (sizeof (MDBIterator)));
 
             ct_it->luk[0] = it->luk[0];
+            /*
             LSUP_TripleKey ct_spok;
             memcpy (ct_it->spok, ct_spok, sizeof (LSUP_TripleKey));
+            */
             ct_it->ck = it->ck;
             ct_it->store = it->store;
             ct_it->txn = it->txn;
@@ -1195,8 +1197,10 @@ lookup_2bound(
 
             ct_it->luk[0] = it->luk[0];
             ct_it->luk[1] = it->luk[1];
+            /*
             LSUP_TripleKey ct_spok;
             memcpy (ct_it->spok, ct_spok, sizeof (LSUP_TripleKey));
+            */
             ct_it->ck = it->ck;
             ct_it->store = it->store;
             ct_it->txn = it->txn;