go-jsonnet/c-bindings/handles.go

79 lines
2.2 KiB
Go

package main
import (
"errors"
"fmt"
)
const maxID = (1 << 63) - 1
// Because of Go GC, there are restrictions on keeping Go pointers in C.
// We cannot just pass *jsonnet.VM/JsonValue to C. So instead we use "handle" structs in C
// which refer to JsonnetVM/JsonnetJsonValue by a numeric id.
// The way it is implemented below is simple and has low overhead, but requires us to keep
// a list of used IDs. This results in a permanent "leak". I don't expect it to ever
// become a problem.
// The Handle IDs start with 1, so 0 is never a valid ID and the Handle's index in the array is (ID - 1).
// handlesTable is the set of active, valid Jsonnet allocated handles
type handlesTable struct {
objects []interface{}
freedIDs []uint64
}
// errMaxNumberOfOpenHandles tells that there was an attempt to create more than maxID open handles
var errMaxNumberOfOpenHandles = fmt.Errorf("maximum number of constructed Jsonnet handles exceeded (%d)", maxID)
// errInvalidHandle tells that there was an attempt to dereference invalid handle ID
var errInvalidHandle = errors.New("invalid handle ID was provided")
// make registers the new object as a handle and returns the corresponding ID
func (h *handlesTable) make(obj interface{}) (uint64, error) {
var id uint64
if len(h.freedIDs) > 0 {
id, h.freedIDs = h.freedIDs[len(h.freedIDs)-1], h.freedIDs[:len(h.freedIDs)-1]
h.objects[id-1] = obj
} else {
id = uint64(len(h.objects) + 1)
if id > maxID {
return 0, errMaxNumberOfOpenHandles
}
h.objects = append(h.objects, obj)
}
return id, nil
}
// free marks the given handle ID as unused
func (h *handlesTable) free(id uint64) error {
if err := h.ensureValidID(id); err != nil {
return err
}
h.objects[id-1] = nil
h.freedIDs = append(h.freedIDs, id)
return nil
}
// get returns the corresponding object for the provided ID
func (h *handlesTable) get(id uint64) (interface{}, error) {
if err := h.ensureValidID(id); err != nil {
return nil, err
}
return h.objects[id-1], nil
}
// ensureValidID returns an error if the given handle ID is invalid, otherwise returns nil
func (h *handlesTable) ensureValidID(id uint64) error {
if id == 0 || uint64(id) > uint64(len(h.objects)) {
return errInvalidHandle
}
return nil
}