153 lines
3.3 KiB
Go
153 lines
3.3 KiB
Go
|
package encoding
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"reflect"
|
||
|
"runtime"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
var byteSliceType = reflect.TypeOf([]byte(nil))
|
||
|
|
||
|
type decoderFunc func(dv reflect.Value, sv reflect.Value)
|
||
|
|
||
|
// Decode decodes map[string]interface{} into a struct. The first parameter
|
||
|
// must be a pointer.
|
||
|
func Decode(dst interface{}, src interface{}) (err error) {
|
||
|
return decode(dst, src, true)
|
||
|
}
|
||
|
|
||
|
func Merge(dst interface{}, src interface{}) (err error) {
|
||
|
return decode(dst, src, false)
|
||
|
}
|
||
|
|
||
|
func decode(dst interface{}, src interface{}, blank bool) (err error) {
|
||
|
defer func() {
|
||
|
if r := recover(); r != nil {
|
||
|
if _, ok := r.(runtime.Error); ok {
|
||
|
panic(r)
|
||
|
}
|
||
|
if v, ok := r.(string); ok {
|
||
|
err = errors.New(v)
|
||
|
} else {
|
||
|
err = r.(error)
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
dv := reflect.ValueOf(dst)
|
||
|
sv := reflect.ValueOf(src)
|
||
|
if dv.Kind() != reflect.Ptr {
|
||
|
return &DecodeTypeError{
|
||
|
DestType: dv.Type(),
|
||
|
SrcType: sv.Type(),
|
||
|
Reason: "must be a pointer",
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dv = dv.Elem()
|
||
|
if !dv.CanAddr() {
|
||
|
return &DecodeTypeError{
|
||
|
DestType: dv.Type(),
|
||
|
SrcType: sv.Type(),
|
||
|
Reason: "must be addressable",
|
||
|
}
|
||
|
}
|
||
|
|
||
|
decodeValue(dv, sv, blank)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// decodeValue decodes the source value into the destination value
|
||
|
func decodeValue(dv, sv reflect.Value, blank bool) {
|
||
|
valueDecoder(dv, sv, blank)(dv, sv)
|
||
|
}
|
||
|
|
||
|
type decoderCacheKey struct {
|
||
|
dt, st reflect.Type
|
||
|
blank bool
|
||
|
}
|
||
|
|
||
|
var decoderCache struct {
|
||
|
sync.RWMutex
|
||
|
m map[decoderCacheKey]decoderFunc
|
||
|
}
|
||
|
|
||
|
func valueDecoder(dv, sv reflect.Value, blank bool) decoderFunc {
|
||
|
if !sv.IsValid() {
|
||
|
return invalidValueDecoder
|
||
|
}
|
||
|
|
||
|
if dv.IsValid() {
|
||
|
dv = indirect(dv, false)
|
||
|
if blank {
|
||
|
dv.Set(reflect.Zero(dv.Type()))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return typeDecoder(dv.Type(), sv.Type(), blank)
|
||
|
}
|
||
|
|
||
|
func typeDecoder(dt, st reflect.Type, blank bool) decoderFunc {
|
||
|
decoderCache.RLock()
|
||
|
f := decoderCache.m[decoderCacheKey{dt, st, blank}]
|
||
|
decoderCache.RUnlock()
|
||
|
if f != nil {
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
// To deal with recursive types, populate the map with an
|
||
|
// indirect func before we build it. This type waits on the
|
||
|
// real func (f) to be ready and then calls it. This indirect
|
||
|
// func is only used for recursive types.
|
||
|
decoderCache.Lock()
|
||
|
var wg sync.WaitGroup
|
||
|
wg.Add(1)
|
||
|
decoderCache.m[decoderCacheKey{dt, st, blank}] = func(dv, sv reflect.Value) {
|
||
|
wg.Wait()
|
||
|
f(dv, sv)
|
||
|
}
|
||
|
decoderCache.Unlock()
|
||
|
|
||
|
// Compute fields without lock.
|
||
|
// Might duplicate effort but won't hold other computations back.
|
||
|
f = newTypeDecoder(dt, st, blank)
|
||
|
wg.Done()
|
||
|
decoderCache.Lock()
|
||
|
decoderCache.m[decoderCacheKey{dt, st, blank}] = f
|
||
|
decoderCache.Unlock()
|
||
|
return f
|
||
|
}
|
||
|
|
||
|
// indirect walks down v allocating pointers as needed,
|
||
|
// until it gets to a non-pointer.
|
||
|
func indirect(v reflect.Value, decodeNull bool) reflect.Value {
|
||
|
// If v is a named type and is addressable,
|
||
|
// start with its address, so that if the type has pointer methods,
|
||
|
// we find them.
|
||
|
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
|
||
|
v = v.Addr()
|
||
|
}
|
||
|
for {
|
||
|
// Load value from interface, but only if the result will be
|
||
|
// usefully addressable.
|
||
|
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||
|
e := v.Elem()
|
||
|
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodeNull || e.Elem().Kind() == reflect.Ptr) {
|
||
|
v = e
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if v.Kind() != reflect.Ptr {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if v.IsNil() {
|
||
|
v.Set(reflect.New(v.Type().Elem()))
|
||
|
}
|
||
|
v = v.Elem()
|
||
|
}
|
||
|
return v
|
||
|
}
|