// +build ignore
package main
import (
"C"
"fmt"
"math"
"reflect"
"sync"
)
type Handle uint64
const (
ErrorCodeSuccess = iota
ErrorCodeNotFound = -iota
ErrorCodeInternal = -iota
)
const MessageNotFound string = "object not found"
const InvalidHandle Handle = 0
const IH uint64 = uint64(InvalidHandle)
var counter Handle = InvalidHandle
var opMutex sync.Mutex
var registryHandle2Obj map[Handle]interface{} = map[Handle]interface{}{}
var registryObj2Handle map[uintptr][]Handle = map[uintptr][]Handle{}
var trace bool = false
func getNewHandle() Handle {
counter++
if counter == math.MaxUint64 {
panic("Handle cache is exhausted")
}
return counter
}
func RegisterObject(obj interface{}) Handle {
data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1]
if trace {
fmt.Printf("RegisterObject 0x%x\t%v\n", data_ptr, obj)
}
opMutex.Lock()
defer opMutex.Unlock()
handles, ok := registryObj2Handle[data_ptr]
if ok {
for _, h := range handles {
other, ok := registryHandle2Obj[h]
if !ok {
panic("Inconsistent internal object mapping state (1)")
}
if other == obj {
if trace {
fmt.Printf("RegisterObject 0x%x reused %d\n", data_ptr, h)
}
return h
}
}
}
handle := getNewHandle()
registryHandle2Obj[handle] = obj
registryObj2Handle[data_ptr] = append(registryObj2Handle[data_ptr], handle)
if trace {
c_dump_objects()
}
return handle
}
func UnregisterObject(handle Handle) int {
if trace {
fmt.Printf("UnregisterObject %d\n", handle)
}
if handle == InvalidHandle {
return ErrorCodeNotFound
}
opMutex.Lock()
defer opMutex.Unlock()
obj, ok := registryHandle2Obj[handle]
if !ok {
return ErrorCodeNotFound
}
delete(registryHandle2Obj, handle)
data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1]
other_handles, ok := registryObj2Handle[data_ptr]
if !ok {
panic(fmt.Sprintf("Inconsistent internal object mapping state (2): %d",
handle))
}
hi := -1
for i, h := range other_handles {
if h == handle {
hi = i
break
}
}
if hi < 0 {
panic(fmt.Sprintf("Inconsistent internal object mapping state (3): %d",
handle))
}
if len(other_handles) == 1 {
delete(registryObj2Handle, data_ptr)
} else {
registryObj2Handle[data_ptr] = append(other_handles[:hi], other_handles[hi+1:]...)
}
if trace {
c_dump_objects()
}
return ErrorCodeSuccess
}
func GetObject(handle Handle) (interface{}, bool) {
if handle == InvalidHandle {
return nil, false
}
opMutex.Lock()
defer opMutex.Unlock()
a, b := registryHandle2Obj[handle]
return a, b
}
func GetHandle(obj interface{}) (Handle, bool) {
data_ptr := reflect.ValueOf(&obj).Elem().InterfaceData()[1]
opMutex.Lock()
defer opMutex.Unlock()
handles, ok := registryObj2Handle[data_ptr]
if !ok {
return InvalidHandle, false
}
for _, h := range handles {
candidate := registryHandle2Obj[h]
if candidate == obj {
return h, true
}
}
return InvalidHandle, false
}
func CopyString(str string) string {
buf := make([]byte, len(str))
copy(buf, []byte(str))
return string(buf)
}
// https://github.com/golang/go/issues/14838
func CBytes(bytes []byte) *C.char {
ptr := C.malloc(C.size_t(len(bytes)))
copy((*[1 << 30]byte)(ptr)[:], bytes)
return (*C.char)(ptr)
}
func SafeIsNil(v reflect.Value) bool {
defer func() { recover() }()
return v.IsNil()
}
//export c_dispose
func c_dispose(handle uint64) {
UnregisterObject(Handle(handle))
}
//export c_objects_size
func c_objects_size() int {
return len(registryHandle2Obj)
}
//export c_dump_objects
func c_dump_objects() {
fmt.Printf("handles (%d):\n", len(registryHandle2Obj))
for h, obj := range registryHandle2Obj {
fmt.Printf("0x%x\t0x%x %v\n", h,
reflect.ValueOf(&obj).Elem().InterfaceData()[1], obj)
}
fmt.Println()
phs := 0
for _, h := range registryObj2Handle {
phs += len(h)
}
fmt.Printf("pointers (%d):\n", phs)
for ptr, h := range registryObj2Handle {
fmt.Printf("0x%x\t%v\n", ptr, h)
}
}
//export c_set_trace
func c_set_trace(val bool) {
trace = val
}
// dummy main() is needed by the linker
func main() {}