| // +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) |
| } |
| |
| // 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() {} |