1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
package sophia
import (
"reflect"
"sync"
"unsafe"
)
/*
#include <inttypes.h>
#include <stdio.h>
extern void goUpsertCall(int count,
char **src, uint32_t *src_size,
char **upsert, uint32_t *upsert_size,
char **result, uint32_t *result_size,
void *arg);
*/
import "C"
// keyUpsertTemplate template for upsert settings key
const (
keyUpsertTemplate = "db.%v.upsert"
keyUpsertArgTemplate = "db.%v.upsert_arg"
)
// upsertFunc golang binding to upsert_callback.
// It is a wrapper for UpsertFunc, that converts C types to golang ones
type upsertFunc func(count C.int,
src **C.char, srcSize *C.uint32_t,
upsert **C.char, upsertSize *C.uint32_t,
result **C.char, resultSize *C.uint32_t,
arg unsafe.Pointer) C.int
// UpsertFunc golang equivalent of upsert_callback.
// Should return 0 in case of success, otherwise -1.
type UpsertFunc func(count int,
src []unsafe.Pointer, srcSize uint32,
upsert []unsafe.Pointer, upsertSize uint32,
result []unsafe.Pointer, resultSize uint32,
arg unsafe.Pointer) int
//export goUpsertCall
func goUpsertCall(count C.int,
src **C.char, src_size *C.uint32_t,
upsert **C.char, upsert_size *C.uint32_t,
result **C.char, result_size *C.uint32_t,
arg unsafe.Pointer) {
index := (*int)(arg)
fn := getUpsert(index)
upsertArg := getUpsertArg(index)
fn(count, src, src_size, upsert, upsert_size, result, result_size, upsertArg)
}
var upsertMap = make(map[*int]upsertFunc)
var upsertMu sync.RWMutex
var upsertIndex int
var upsertArgMap = make(map[*int]unsafe.Pointer)
var upsertArgMu sync.RWMutex
func getUpsertArg(index *int) unsafe.Pointer {
upsertArgMu.RLock()
defer upsertArgMu.RUnlock()
return upsertArgMap[index]
}
func registerUpsertArg(index *int, arg interface{}) {
upsertArgMu.Lock()
defer upsertArgMu.Unlock()
if arg == nil {
return
}
val := reflect.ValueOf(arg)
if val.CanAddr() {
upsertArgMap[index] = unsafe.Pointer(val.Pointer())
return
}
switch val.Kind() {
case reflect.String:
str := val.String()
upsertArgMap[index] = unsafe.Pointer(&str)
case reflect.Int, reflect.Int64, reflect.Int8, reflect.Int16, reflect.Int32:
i := val.Int()
upsertArgMap[index] = unsafe.Pointer(&i)
case reflect.Uint, reflect.Uint64, reflect.Uint8, reflect.Uint16, reflect.Uint32:
i := val.Uint()
upsertArgMap[index] = unsafe.Pointer(&i)
}
}
func registerUpsert(upsertFunc UpsertFunc) (unsafe.Pointer, *int) {
upsertMu.Lock()
defer upsertMu.Unlock()
index := upsertIndex
upsertIndex++
indexPtr := &index
upsertMap[indexPtr] = func(count C.int,
src **C.char, srcSize *C.uint32_t,
upsert **C.char, upsertSize *C.uint32_t,
result **C.char, resultSize *C.uint32_t,
arg unsafe.Pointer) C.int {
if src == nil {
return C.int(0)
}
var sSize uint32
if srcSize != nil {
sSize = uint32(*srcSize)
}
uSize := uint32(*upsertSize)
rSize := uint32(*resultSize)
countN := int(count)
// We receive C pointer to pointer which can be interpreted as an array of pointers.
// Here we cast C pointer to pointer to Go slice of pointers.
slice1 := (*[1 << 4]unsafe.Pointer)(unsafe.Pointer(src))[:countN:countN]
slice2 := (*[1 << 4]unsafe.Pointer)(unsafe.Pointer(upsert))[:countN:countN]
slice3 := (*[1 << 4]unsafe.Pointer)(unsafe.Pointer(result))[:countN:countN]
res := upsertFunc(countN,
slice1, sSize,
slice2, uSize,
slice3, rSize,
arg)
return C.int(res)
}
ptr := C.goUpsertCall
return ptr, indexPtr
}
func getUpsert(index *int) upsertFunc {
upsertMu.RLock()
defer upsertMu.RUnlock()
return upsertMap[index]
}
func unregisterUpsert(index *int) {
upsertMu.Lock()
defer upsertMu.Unlock()
delete(upsertMap, index)
}
|