aboutsummaryrefslogblamecommitdiffstats
path: root/cshared/objects.go
blob: ae3440de3c20261e222225146d4d2be164f133de (plain) (tree)













































































































































                                                                                                    






                                             







































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