package json
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"sync"
|
|
)
|
|
|
|
var (
|
|
// typeRegistry contains globally registered types for JSON encoding/decoding.
|
|
typeRegistry = newTypes()
|
|
)
|
|
|
|
// RegisterType registers a type for Amino-compatible interface encoding in the global type
|
|
// registry. These types will be encoded with a type wrapper `{"type":"<type>","value":<value>}`
|
|
// regardless of which interface they are wrapped in (if any). If the type is a pointer, it will
|
|
// still be valid both for value and pointer types, but decoding into an interface will generate
|
|
// the a value or pointer based on the registered type.
|
|
//
|
|
// Should only be called in init() functions, as it panics on error.
|
|
func RegisterType(_type interface{}, name string) {
|
|
if _type == nil {
|
|
panic("cannot register nil type")
|
|
}
|
|
err := typeRegistry.register(name, reflect.ValueOf(_type).Type())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// typeInfo contains type information.
|
|
type typeInfo struct {
|
|
name string
|
|
rt reflect.Type
|
|
returnPtr bool
|
|
}
|
|
|
|
// types is a type registry. It is safe for concurrent use.
|
|
type types struct {
|
|
sync.RWMutex
|
|
byType map[reflect.Type]*typeInfo
|
|
byName map[string]*typeInfo
|
|
}
|
|
|
|
// newTypes creates a new type registry.
|
|
func newTypes() types {
|
|
return types{
|
|
byType: map[reflect.Type]*typeInfo{},
|
|
byName: map[string]*typeInfo{},
|
|
}
|
|
}
|
|
|
|
// registers the given type with the given name. The name and type must not be registered already.
|
|
func (t *types) register(name string, rt reflect.Type) error {
|
|
if name == "" {
|
|
return errors.New("name cannot be empty")
|
|
}
|
|
// If this is a pointer type, we recursively resolve until we get a bare type, but register that
|
|
// we should return pointers.
|
|
returnPtr := false
|
|
for rt.Kind() == reflect.Ptr {
|
|
returnPtr = true
|
|
rt = rt.Elem()
|
|
}
|
|
tInfo := &typeInfo{
|
|
name: name,
|
|
rt: rt,
|
|
returnPtr: returnPtr,
|
|
}
|
|
|
|
t.Lock()
|
|
defer t.Unlock()
|
|
if _, ok := t.byName[tInfo.name]; ok {
|
|
return fmt.Errorf("a type with name %q is already registered", name)
|
|
}
|
|
if _, ok := t.byType[tInfo.rt]; ok {
|
|
return fmt.Errorf("the type %v is already registered", rt)
|
|
}
|
|
t.byName[name] = tInfo
|
|
t.byType[rt] = tInfo
|
|
return nil
|
|
}
|
|
|
|
// lookup looks up a type from a name, or nil if not registered.
|
|
func (t *types) lookup(name string) (reflect.Type, bool) {
|
|
t.RLock()
|
|
defer t.RUnlock()
|
|
tInfo := t.byName[name]
|
|
if tInfo == nil {
|
|
return nil, false
|
|
}
|
|
return tInfo.rt, tInfo.returnPtr
|
|
}
|
|
|
|
// name looks up the name of a type, or empty if not registered. Unwraps pointers as necessary.
|
|
func (t *types) name(rt reflect.Type) string {
|
|
for rt.Kind() == reflect.Ptr {
|
|
rt = rt.Elem()
|
|
}
|
|
t.RLock()
|
|
defer t.RUnlock()
|
|
tInfo := t.byType[rt]
|
|
if tInfo == nil {
|
|
return ""
|
|
}
|
|
return tInfo.name
|
|
}
|