Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lmdb: methods to support custom comparison Txn.SetCmp(), Txn.SetCmpDup() #27

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft
13 changes: 13 additions & 0 deletions exp/cmd/lmdb_cmp/compare.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "compare.h"

int lmdb_cmp_dyn(const MDB_val *a, const MDB_val *b) {
return lmdbCmpDyn(((lmdb_cmp_t) {a, b}), 2);
}

int lmdb_cmp_go(const MDB_val *a, const MDB_val *b) {
return lmdbCmp(((lmdb_cmp_t) {a, b}));
}

int lmdb_cmp_c(const MDB_val *a, const MDB_val *b) {
return -_cmp_baseline(a, b);
}
25 changes: 25 additions & 0 deletions exp/cmd/lmdb_cmp/compare.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef _MDB_CUSTOM_COMPARE_H
#define _MDB_CUSTOM_COMPARE_H

#include <stdlib.h>
#include <string.h>
#include "lmdb.h"

typedef struct{const MDB_val *a; const MDB_val *b;} lmdb_cmp_t;

extern int lmdbCmp(lmdb_cmp_t cmp);
extern int lmdbCmpDyn(lmdb_cmp_t cmp, size_t ctx);

int lmdb_cmp_dyn(const MDB_val *a, const MDB_val *b);
int lmdb_cmp_go(const MDB_val *a, const MDB_val *b);
int lmdb_cmp_c(const MDB_val *a, const MDB_val *b);

static inline int _cmp_baseline(const MDB_val *a, const MDB_val *b) {
int result;
if ((result = memcmp((a->mv_data), (b->mv_data), (a->mv_size) < (b->mv_size) ? (a->mv_size) : (b->mv_size)))) {
return result;
}
return a->mv_size - b->mv_size;
}

#endif
1 change: 1 addition & 0 deletions exp/cmd/lmdb_cmp/lmdb.h
178 changes: 178 additions & 0 deletions exp/cmd/lmdb_cmp/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package main

/*
#include "lmdb.h"
#include "compare.h"
*/
import "C"
import (
"bytes"
"flag"
"fmt"
"math/rand"
"os"
"reflect"
"sync"
"unsafe"

"github.com/PowerDNS/lmdb-go/lmdb"
)

// NumLoop is the number of times each update should loop.
const NumLoop = 1000000

// MaxID is the highest key id.
const MaxID = 100000

// RootDir contains the mdb database for this program.
const RootDir = "data"

func main() {
randSeed := flag.Int64("seed", 1, "random seed")
cmpfunc := flag.String("func", "c", "comparison func implementation to use")
flag.Parse()

err := os.RemoveAll(RootDir)
if err != nil {
panic(err)
}
err = os.MkdirAll(RootDir, 0755)
if err != nil {
panic(err)
}

env, err := lmdb.NewEnv()
if err != nil {
panic(err)
}
err = env.SetMapSize(100 << 20)
if err != nil {
panic(err)
}
err = env.Open(RootDir, 0, 0644)
if err != nil {
panic(err)
}
defer env.Close()

rand.Seed(*randSeed)

var dbi lmdb.DBI
err = env.Update(func(txn *lmdb.Txn) (err error) {
dbi, err = txn.OpenRoot(0)
if err != nil {
return err
}
switch *cmpfunc {
case "c":
err = txn.SetCmp(dbi, (*lmdb.CmpFunc)(unsafe.Pointer(C.lmdb_cmp_c)))
case "go":
err = txn.SetCmp(dbi, (*lmdb.CmpFunc)(unsafe.Pointer(C.lmdb_cmp_go)))
case "dyn":
err = txn.SetCmp(dbi, (*lmdb.CmpFunc)(unsafe.Pointer(C.lmdb_cmp_dyn)))
}
if err != nil {
return err
}
return nil
})
if err != nil {
panic(err)
}

err = env.Update(func(txn *lmdb.Txn) (err error) {
for i := 0; i < NumLoop; i++ {
kn := rand.Intn(MaxID)
k := fmt.Sprintf("k%d", kn)
v := fmt.Sprintf("v%d", i)
err = txn.Put(dbi, []byte(k), []byte(v), 0)
if err != nil {
return err
}
}
return nil
})
if err != nil {
panic(err)
}

err = env.Update(func(txn *lmdb.Txn) (err error) {
cur, err := txn.OpenCursor(dbi)
if err != nil {
return err
}
defer cur.Close()
for i := 0; i < NumLoop; i++ {
kn := rand.Intn(MaxID)
k := fmt.Sprintf("k%d", kn)
_, _, err = cur.Get([]byte(k), nil, lmdb.Set)
if lmdb.IsNotFound(err) {
continue
}
if err != nil {
return err
}
err = cur.Del(0)
if err != nil {
return err
}
}
return nil
})
if err != nil {
panic(err)
}

err = env.Update(func(txn *lmdb.Txn) (err error) {
cur, err := txn.OpenCursor(dbi)
if err != nil {
return err
}
defer cur.Close()
for i := 0; i < NumLoop; i++ {
kn := rand.Intn(MaxID)
k := fmt.Sprintf("k%d", kn)
v := fmt.Sprintf("v%d", i)
err = cur.Put([]byte(k), []byte(v), 0)
if err != nil {
return err
}
}
return nil
})
if err != nil {
panic(err)
}
}

var rwmut = &sync.RWMutex{}

var cmpMap = map[int]func(c C.lmdb_cmp_t) C.int{
0: nil,
1: nil,
2: lmdbCmp,
}

//export lmdbCmpDyn
func lmdbCmpDyn(c C.lmdb_cmp_t, ctx C.size_t) C.int {
rwmut.RLock()
fn := cmpMap[int(ctx)]
rwmut.RUnlock()
return fn(c)
}

//export lmdbCmp
func lmdbCmp(c C.lmdb_cmp_t) C.int {
p1 := mdbValBytes(c.a)
p2 := mdbValBytes(c.b)
return C.int(-bytes.Compare(p1, p2))
}

func mdbValBytes(val *C.MDB_val) []byte {
hdr := reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(val.mv_data)),
Len: int(val.mv_size),
Cap: int(val.mv_size),
}
return *(*[]byte)(unsafe.Pointer(&hdr))
}
16 changes: 16 additions & 0 deletions exp/cmd/lmdb_cmp_simple/compare.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include <stdlib.h>
#include <string.h>
#include "compare.h"

/* lmdb_cmp_c
* This function performs a simple reverse-string-comparison of two MDB_val
* data. This is obviously more complicated them simply using the MDB_REVERSE
* flag, but it demonstrates how to write a simple custom comparison function.
*/
int lmdb_cmp_c(const MDB_val *a, const MDB_val *b) {
int result;
if ((result = memcmp((a->mv_data), (b->mv_data), (a->mv_size) < (b->mv_size) ? (a->mv_size) : (b->mv_size)))) {
return result;
}
return a->mv_size - b->mv_size;
}
13 changes: 13 additions & 0 deletions exp/cmd/lmdb_cmp_simple/compare.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef _MDB_CUSTOM_COMPARE_H
#define _MDB_CUSTOM_COMPARE_H

#include "lmdb.h"

/* lmdb_cmp_c can be used by Go (after including this header) to set a
* comparison function for a database. For the following function to typecheck
* a copy (or link) of the lmdb.h file used by lmdb-go is needed in the local
* directory so it may be included.
* */
int lmdb_cmp_c(const MDB_val *a, const MDB_val *b);

#endif
1 change: 1 addition & 0 deletions exp/cmd/lmdb_cmp_simple/lmdb.h
Loading