summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--c_src/Makefile33
-rw-r--r--c_src/permdbpy.c271
-rw-r--r--c_src/permdbtest.py103
3 files changed, 404 insertions, 3 deletions
diff --git a/c_src/Makefile b/c_src/Makefile
index d45e601..e9b0394 100644
--- a/c_src/Makefile
+++ b/c_src/Makefile
@@ -1,6 +1,28 @@
+UNAME = $(shell uname)
+
+ifeq ($(UNAME),Darwin)
+# Mac OS X 10.10
+OS_CFLAGS = -arch x86_64 -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -DHAVE_COMMON_CRYPTO
+OS_SHLIB_LINKFLAGS = -Wl,-F. -bundle -undefined dynamic_lookup
+OS_LDFLAGS =
+PYTHONLIB = -lpython
+else ifeq ($(UNAME),Linux)
+# Ubuntu 12
+OS_CFLAGS = -I/usr/include/python2.7 -std=gnu99 -fPIC -DHAVE_BSD_COMPAT_LIB
+OS_SHLIB_LINKFLAGS = -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro
+OS_LDFLAGS = -lbsd -lrhash
+PYTHONLIB = -L/usr/lib/x86_64-linux-gnu -lpython2.7
+else
+error:
+ echo $(UNAME) not supported
+endif
+
+LOCAL_CFLAGS =
+LOCAL_LDFLAGS =
+
CC = gcc
-CFLAGS = -Wall -Werror -std=gnu99
-LDFLAGS = -lnettle
+CFLAGS = -Wall -Werror -std=gnu99 $(LOCAL_CFLAGS) $(OS_CFLAGS)
+LDFLAGS = $(LOCAL_CFLAGS) -lnettle $(OS_LDFLAGS)
PORTS = fsynchelper hsmhelper permdbport
@@ -8,8 +30,10 @@ common_OBJS = erlport.o net_read_write.o
fsynchelper_OBJS = fsynchelper.o $(common_OBJS)
hsmhelper_OBJS = hsmhelper.o pkcs11.o $(common_OBJS)
permdbport_OBJS = permdb.o permdbport.o arlamath.o hash.o $(common_OBJS)
+permdbso_OBJS = permdb.o arlamath.o hash.o permdbpy.o $(common_OBJS)
-all: $(PORTS)
+
+all: $(PORTS) permdb.so
clean:
rm -f $(fsynchelper_OBJS) $(hsmhelper_OBJS) $(permdbport_OBJS) $(PORTS)
@@ -22,3 +46,6 @@ hsmhelper: $(hsmhelper_OBJS)
permdbport: $(permdbport_OBJS)
$(CC) -o permdbport $(permdbport_OBJS) $(LDFLAGS)
+
+permdb.so: $(permdbso_OBJS)
+ $(CC) $(LDFLAGS) $(OS_SHLIB_LINKFLAGS) $(permdbso_OBJS) -o permdb.so
diff --git a/c_src/permdbpy.c b/c_src/permdbpy.c
new file mode 100644
index 0000000..a98c83a
--- /dev/null
+++ b/c_src/permdbpy.c
@@ -0,0 +1,271 @@
+#include <Python.h>
+#include "permdb.h"
+
+typedef struct permdb_object_py {
+ PyObject_HEAD
+ struct permdb_object *permdb;
+} permdb_object_py;
+
+static void
+permdb_dealloc(permdb_object_py *state)
+{
+ permdb_free(state->permdb);
+}
+
+PyTypeObject permdb_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "permdb.permdb", /*tp_name*/
+ sizeof(permdb_object_py), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)permdb_dealloc,/*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "PermDB state", /*tp_doc*/
+};
+
+permdb_object_py *
+permdb_alloc_py(const char *dbpath)
+{
+ permdb_object_py *state = PyObject_New(permdb_object_py, &permdb_type);
+ state->permdb = permdb_alloc(dbpath);
+ return state;
+}
+
+typedef struct node_object_py {
+ PyObject_HEAD
+ node_object nodeobj;
+} node_object_py;
+
+static void
+pynode_dealloc(node_object_py *node)
+{
+
+}
+
+static PyObject*
+node_getitem(PyObject *o, PyObject *key)
+{
+ unsigned int n = PyLong_AsUnsignedLong(key);
+ node_object_py *node = (node_object_py *)o;
+
+ return PyLong_FromUnsignedLongLong(get_entry_in_node(node->nodeobj, n));
+}
+
+static PyMappingMethods node_as_mapping = {
+ 0, /* mp_length */
+ node_getitem, /* mp_subscript */
+ 0, /* mp_ass_subscript */
+};
+
+static PyTypeObject node_type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "permdb.node", /*tp_name*/
+ sizeof(node_object_py), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)pynode_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ &node_as_mapping, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "PermDB node", /*tp_doc*/
+};
+
+
+static node_object_py *
+node_alloc(node_object data)
+{
+ node_object_py *node = PyObject_New(node_object_py, &node_type);
+ node->nodeobj = data;
+ return node;
+}
+
+
+static PyObject *
+data_pread(PyObject *self, PyObject *args)
+{
+ unsigned long long offset;
+ unsigned int length;
+ permdb_object_py *state;
+
+ if (!PyArg_ParseTuple(args, "O!KI", &permdb_type, &state, &offset, &length)) {
+ return NULL;
+ }
+
+ unsigned char *result = read_internal_data(state->permdb, offset, length);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ PyObject* resultObj = PyString_FromStringAndSize((char *)result, length);
+ free(result);
+ return resultObj;
+}
+
+static PyObject *
+permdb_alloc_wrapper(PyObject *self, PyObject *args)
+{
+ const char *dbpath = NULL;
+
+ if (!PyArg_ParseTuple(args, "s", &dbpath)) {
+ return NULL;
+ }
+
+ return (PyObject*)permdb_alloc_py(dbpath);
+}
+
+static PyObject *
+readnode_wrapper(PyObject *self, PyObject *args)
+{
+ permdb_object_py *state;
+ unsigned long long offset;
+ const char *key = NULL;
+
+ if (!PyArg_ParseTuple(args, "O!K|s", &permdb_type, &state, &offset, &key)) {
+ return NULL;
+ }
+
+ return (PyObject *)node_alloc(readnode(state->permdb, offset, key));
+}
+
+static PyObject *
+datasize_wrapper(PyObject *self, PyObject *args)
+{
+ permdb_object_py *state;
+
+ if (!PyArg_ParseTuple(args, "O!", &permdb_type, &state)) {
+ return NULL;
+ }
+
+ return PyInt_FromLong(datasize(state->permdb));
+}
+
+static PyObject *
+addvalue_wrapper(PyObject *self, PyObject *args)
+{
+ permdb_object_py *state;
+ const char *key;
+ unsigned int keylength;
+ const char *data;
+ unsigned int datalength;
+
+ if (!PyArg_ParseTuple(args, "O!s#s#", &permdb_type, &state, &key, &keylength, &data, &datalength)) {
+ return NULL;
+ }
+
+ int result = addvalue(state->permdb, (unsigned char *) key, keylength, (unsigned char *) data, datalength);
+
+ if (result < 0) {
+ return NULL;
+ } else if (result == 0) {
+ Py_INCREF(Py_False);
+ return Py_False;
+ } else {
+ Py_INCREF(Py_True);
+ return Py_True;
+ }
+}
+
+static PyObject *
+getvalue_wrapper(PyObject *self, PyObject *args)
+{
+ permdb_object_py *state;
+
+ const char *key;
+ int keylen;
+
+ if (!PyArg_ParseTuple(args, "O!s#", &permdb_type, &state, &key, &keylen)) {
+ return NULL;
+ }
+
+ size_t datalen;
+ unsigned char *result = getvalue(state->permdb, (unsigned char *) key, keylen, &datalen);
+
+ if (result == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ PyObject* resultObj = PyString_FromStringAndSize((char *) result, datalen);
+ free(result);
+ return resultObj;
+}
+
+static PyObject *
+clear_nodecache(PyObject *self, PyObject *args)
+{
+ permdb_object_py *state;
+
+ if (!PyArg_ParseTuple(args, "O!", &permdb_type, &state)) {
+ return NULL;
+ }
+
+ delete_all_nodes_in_cache(state->permdb);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+committree_wrapper(PyObject *self, PyObject *args)
+{
+ permdb_object_py *state;
+
+ fprintf(stderr, "starting commit\n");
+
+ if (!PyArg_ParseTuple(args, "O!", &permdb_type, &state)) {
+ return NULL;
+ }
+
+ int result = committree(state->permdb);
+
+ if (result < 0) {
+ return NULL;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+}
+
+static PyMethodDef UtilMethods[] = {
+ {"data_pread", data_pread, METH_VARARGS},
+ {"alloc", permdb_alloc_wrapper, METH_VARARGS},
+ {"readnode", readnode_wrapper, METH_VARARGS},
+ {"datasize", datasize_wrapper, METH_VARARGS},
+ {"addvalue", addvalue_wrapper, METH_VARARGS},
+ {"getvalue", getvalue_wrapper, METH_VARARGS},
+ {"committree", committree_wrapper, METH_VARARGS},
+ {"clear_nodecache", clear_nodecache, METH_VARARGS},
+ {NULL, NULL}
+};
+
+void
+initpermdb()
+{
+ (void) Py_InitModule("permdb", UtilMethods);
+}
diff --git a/c_src/permdbtest.py b/c_src/permdbtest.py
new file mode 100644
index 0000000..58debe7
--- /dev/null
+++ b/c_src/permdbtest.py
@@ -0,0 +1,103 @@
+import argparse
+import struct
+import sys
+import os
+import random
+import datetime
+import permdb
+import hashlib
+
+parser = argparse.ArgumentParser(description="")
+parser.add_argument('--store', help="Store", required=True)
+parser.add_argument('--fsync', metavar="n", type=int, help="Fsync every n adds", required=False)
+parser.add_argument('--startrand', metavar="n", type=int, default=0, help="Start position in random sequence", required=False)
+parser.add_argument("--remove", action='store_true', help="Remove database before starting test")
+parser.add_argument('--datasize', metavar="n", type=int, default=32, help="Start position in random sequence", required=False)
+parser.add_argument('testentries', type=int, help="Number of entries to insert")
+args = parser.parse_args()
+
+if args.remove:
+ os.remove(args.store)
+ os.remove(args.store+".idx")
+permdbobj = permdb.alloc(args.store)
+q = 2
+
+def timing_point(timer_dict=None, name=None):
+ t = datetime.datetime.now()
+ if timer_dict:
+ starttime = timer_dict["lasttime"]
+ stoptime = t
+ deltatime = stoptime - starttime
+ deltaseconds = deltatime.seconds + float(deltatime.microseconds) / 1000000
+ print name, deltaseconds
+ timer_dict["deltatimes"].append((name, deltaseconds))
+ timer_dict["lasttime"] = t
+ return None
+ else:
+ timer_dict = {"deltatimes":[], "lasttime":t}
+ return timer_dict
+
+def getsize():
+ return permdb.datasize(permdbobj)
+
+
+def main():
+ permdb.addvalue(permdbobj, b"\xAB\xCDABCDEFGHIJKLMNOPQRSTUVWXYZ1234", b"hej")
+ permdb.clear_nodecache(permdbobj)
+ permdb.addvalue(permdbobj, b"\xAB\xCDABCDEFGHIJKLMNOPQRSTUVWXYZ1234", b"hej")
+ assert permdb.getvalue(permdbobj, b"\xAB\xCDABCDEFGHIJKLMNOPQRSTUVWXYZ1234") == b"hej"
+ assert permdb.getvalue(permdbobj, b"\xAB\xCEABCDEFGHIJKLMNOPQRSTUVWXYZ1234") == None
+ permdb.addvalue(permdbobj, b"\xAB\x12ABCDEFGHIJKLMNOPQRSTUVWXYZ1234", b"hej1")
+ permdb.addvalue(permdbobj, b"\xAC\x52ABCDEFGHIJKLMNOPQRSTUVWXYZ1234", b"hej2")
+ permdb.addvalue(permdbobj, b"\x9A\x43ABCDEFGHIJKLMNOPQRSTUVWXYZ1234", b"hej3")
+ permdb.committree(permdbobj)
+ permdb.clear_nodecache(permdbobj)
+ assert permdb.getvalue(permdbobj, b"\xAB\xCDABCDEFGHIJKLMNOPQRSTUVWXYZ1234") == b"hej"
+ assert permdb.getvalue(permdbobj, b"\xAB\xCEABCDEFGHIJKLMNOPQRSTUVWXYZ1234") == None
+ assert permdb.getvalue(permdbobj, b"\xAB\x12ABCDEFGHIJKLMNOPQRSTUVWXYZ1234") == b"hej1"
+ assert permdb.getvalue(permdbobj, b"\xAC\x52ABCDEFGHIJKLMNOPQRSTUVWXYZ1234") == b"hej2"
+ assert permdb.getvalue(permdbobj, b"\x9A\x43ABCDEFGHIJKLMNOPQRSTUVWXYZ1234") == b"hej3"
+
+ print "generating test data"
+ timing = timing_point()
+ testdata = [(hashlib.sha256(struct.pack(">II", i, 0)).digest(), hashlib.sha256(struct.pack(">II", i, 1)).digest()) for i in range(args.startrand, args.testentries+args.startrand)]
+ timing_point(timing, "gendata2")
+ print "inserting test data"
+ written_since_fsync = 0
+ datamultiplier = args.datasize // 32
+ if args.datasize % 32 != 0:
+ print "datasize", args.datasize, "not multiple of 32, truncating to", datamultiplier * 32
+ for (k, v) in testdata:
+ permdb.addvalue(permdbobj, k, v * datamultiplier)
+ written_since_fsync += 1
+ if args.fsync and written_since_fsync >= args.fsync:
+ permdb.committree(permdbobj)
+ written_since_fsync = 0
+ permdb.committree(permdbobj)
+ timing_point(timing, "insert")
+ nentries = args.testentries
+ print "reading test data"
+ for (k, v) in testdata:
+ assert permdb.getvalue(permdbobj, k) == v * datamultiplier
+ timing_point(timing, "read1")
+ random.shuffle(testdata)
+ timing_point(timing, "shuffle")
+ permdb.clear_nodecache(permdbobj)
+ for (k, v) in testdata:
+ assert permdb.getvalue(permdbobj, k) == v * datamultiplier
+ timing_point(timing, "read2")
+ if getsize() > 1024*1024:
+ print "db size %sM" % (getsize() / (1024*1024),)
+ else:
+ print "db size", getsize()
+ print "db size/entry", getsize()/nentries
+ print "data file size %sM, entry size %d" % (os.stat(args.store).st_size / (1024*1024), datamultiplier * 32)
+ print "q", q, "entries", nentries, "fsync", args.fsync
+ print timing["deltatimes"]
+ timingdict = dict(timing["deltatimes"])
+ print len(testdata)/timingdict["insert"], "writeops/s", "(%f microseconds)" % (timingdict["insert"]*1000000/len(testdata))
+ print len(testdata)/timingdict["read1"], "cached readops/s", "(%f microseconds)" % (timingdict["read1"]*1000000/len(testdata))
+ print len(testdata)/timingdict["read2"], "uncached readops/s", "(%f microseconds)" % (timingdict["read2"]*1000000/len(testdata))
+ permdb.clear_nodecache(permdbobj)
+
+main()