diff options
Diffstat (limited to 'cshared/objects.go')
-rw-r--r-- | cshared/objects.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/cshared/objects.go b/cshared/objects.go new file mode 100644 index 0000000..0517449 --- /dev/null +++ b/cshared/objects.go @@ -0,0 +1,182 @@ +// +build ignore +package main + +import ( + "C" + "fmt" + "math" + "sync" + "reflect" +) + +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) +} + +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() {} |