mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-06 17:16:44 +00:00
311 lines
6.5 KiB
Go
311 lines
6.5 KiB
Go
package msgpack
|
|
|
|
import (
|
|
"reflect"
|
|
"sync"
|
|
)
|
|
|
|
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
|
|
|
var customEncoderType = reflect.TypeOf((*CustomEncoder)(nil)).Elem()
|
|
var customDecoderType = reflect.TypeOf((*CustomDecoder)(nil)).Elem()
|
|
|
|
var marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
|
var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
|
|
|
type encoderFunc func(*Encoder, reflect.Value) error
|
|
type decoderFunc func(*Decoder, reflect.Value) error
|
|
|
|
var typEncMap = make(map[reflect.Type]encoderFunc)
|
|
var typDecMap = make(map[reflect.Type]decoderFunc)
|
|
|
|
// Register registers encoder and decoder functions for a value.
|
|
// This is low level API and in most cases you should prefer implementing
|
|
// Marshaler/CustomEncoder and Unmarshaler/CustomDecoder interfaces.
|
|
func Register(value interface{}, enc encoderFunc, dec decoderFunc) {
|
|
typ := reflect.TypeOf(value)
|
|
if enc != nil {
|
|
typEncMap[typ] = enc
|
|
}
|
|
if dec != nil {
|
|
typDecMap[typ] = dec
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
var structs = newStructCache(false)
|
|
var jsonStructs = newStructCache(true)
|
|
|
|
type structCache struct {
|
|
mu sync.RWMutex
|
|
m map[reflect.Type]*fields
|
|
|
|
useJSONTag bool
|
|
}
|
|
|
|
func newStructCache(useJSONTag bool) *structCache {
|
|
return &structCache{
|
|
m: make(map[reflect.Type]*fields),
|
|
|
|
useJSONTag: useJSONTag,
|
|
}
|
|
}
|
|
|
|
func (m *structCache) Fields(typ reflect.Type) *fields {
|
|
m.mu.RLock()
|
|
fs, ok := m.m[typ]
|
|
m.mu.RUnlock()
|
|
if ok {
|
|
return fs
|
|
}
|
|
|
|
m.mu.Lock()
|
|
fs, ok = m.m[typ]
|
|
if !ok {
|
|
fs = getFields(typ, m.useJSONTag)
|
|
m.m[typ] = fs
|
|
}
|
|
m.mu.Unlock()
|
|
|
|
return fs
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
type field struct {
|
|
name string
|
|
index []int
|
|
omitEmpty bool
|
|
encoder encoderFunc
|
|
decoder decoderFunc
|
|
}
|
|
|
|
func (f *field) value(v reflect.Value) reflect.Value {
|
|
return fieldByIndex(v, f.index)
|
|
}
|
|
|
|
func (f *field) Omit(strct reflect.Value) bool {
|
|
return f.omitEmpty && isEmptyValue(f.value(strct))
|
|
}
|
|
|
|
func (f *field) EncodeValue(e *Encoder, strct reflect.Value) error {
|
|
return f.encoder(e, f.value(strct))
|
|
}
|
|
|
|
func (f *field) DecodeValue(d *Decoder, strct reflect.Value) error {
|
|
return f.decoder(d, f.value(strct))
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
type fields struct {
|
|
Table map[string]*field
|
|
List []*field
|
|
AsArray bool
|
|
|
|
hasOmitEmpty bool
|
|
}
|
|
|
|
func newFields(numField int) *fields {
|
|
return &fields{
|
|
Table: make(map[string]*field, numField),
|
|
List: make([]*field, 0, numField),
|
|
}
|
|
}
|
|
|
|
func (fs *fields) Add(field *field) {
|
|
fs.Table[field.name] = field
|
|
fs.List = append(fs.List, field)
|
|
if field.omitEmpty {
|
|
fs.hasOmitEmpty = true
|
|
}
|
|
}
|
|
|
|
func (fs *fields) OmitEmpty(strct reflect.Value) []*field {
|
|
if !fs.hasOmitEmpty {
|
|
return fs.List
|
|
}
|
|
|
|
fields := make([]*field, 0, len(fs.List))
|
|
for _, f := range fs.List {
|
|
if !f.Omit(strct) {
|
|
fields = append(fields, f)
|
|
}
|
|
}
|
|
return fields
|
|
}
|
|
|
|
func getFields(typ reflect.Type, useJSONTag bool) *fields {
|
|
numField := typ.NumField()
|
|
fs := newFields(numField)
|
|
|
|
var omitEmpty bool
|
|
for i := 0; i < numField; i++ {
|
|
f := typ.Field(i)
|
|
|
|
tag := f.Tag.Get("msgpack")
|
|
if useJSONTag && tag == "" {
|
|
tag = f.Tag.Get("json")
|
|
}
|
|
|
|
name, opt := parseTag(tag)
|
|
if name == "-" {
|
|
continue
|
|
}
|
|
|
|
if f.Name == "_msgpack" {
|
|
if opt.Contains("asArray") {
|
|
fs.AsArray = true
|
|
}
|
|
if opt.Contains("omitempty") {
|
|
omitEmpty = true
|
|
}
|
|
}
|
|
|
|
if f.PkgPath != "" && !f.Anonymous {
|
|
continue
|
|
}
|
|
|
|
field := &field{
|
|
name: name,
|
|
index: f.Index,
|
|
omitEmpty: omitEmpty || opt.Contains("omitempty"),
|
|
encoder: getEncoder(f.Type),
|
|
decoder: getDecoder(f.Type),
|
|
}
|
|
|
|
if field.name == "" {
|
|
field.name = f.Name
|
|
}
|
|
|
|
if f.Anonymous && !opt.Contains("noinline") {
|
|
inline := opt.Contains("inline")
|
|
if inline {
|
|
inlineFields(fs, f.Type, field, useJSONTag)
|
|
} else {
|
|
inline = autoinlineFields(fs, f.Type, field, useJSONTag)
|
|
}
|
|
if inline {
|
|
fs.Table[field.name] = field
|
|
continue
|
|
}
|
|
}
|
|
|
|
fs.Add(field)
|
|
}
|
|
return fs
|
|
}
|
|
|
|
var encodeStructValuePtr uintptr
|
|
var decodeStructValuePtr uintptr
|
|
|
|
func init() {
|
|
encodeStructValuePtr = reflect.ValueOf(encodeStructValue).Pointer()
|
|
decodeStructValuePtr = reflect.ValueOf(decodeStructValue).Pointer()
|
|
}
|
|
|
|
func inlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) {
|
|
inlinedFields := getFields(typ, useJSONTag).List
|
|
for _, field := range inlinedFields {
|
|
if _, ok := fs.Table[field.name]; ok {
|
|
// Don't inline shadowed fields.
|
|
continue
|
|
}
|
|
field.index = append(f.index, field.index...)
|
|
fs.Add(field)
|
|
}
|
|
}
|
|
|
|
func autoinlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) bool {
|
|
var encoder encoderFunc
|
|
var decoder decoderFunc
|
|
|
|
if typ.Kind() == reflect.Struct {
|
|
encoder = f.encoder
|
|
decoder = f.decoder
|
|
} else {
|
|
for typ.Kind() == reflect.Ptr {
|
|
typ = typ.Elem()
|
|
encoder = getEncoder(typ)
|
|
decoder = getDecoder(typ)
|
|
}
|
|
if typ.Kind() != reflect.Struct {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if reflect.ValueOf(encoder).Pointer() != encodeStructValuePtr {
|
|
return false
|
|
}
|
|
if reflect.ValueOf(decoder).Pointer() != decodeStructValuePtr {
|
|
return false
|
|
}
|
|
|
|
inlinedFields := getFields(typ, useJSONTag).List
|
|
for _, field := range inlinedFields {
|
|
if _, ok := fs.Table[field.name]; ok {
|
|
// Don't auto inline if there are shadowed fields.
|
|
return false
|
|
}
|
|
}
|
|
|
|
for _, field := range inlinedFields {
|
|
field.index = append(f.index, field.index...)
|
|
fs.Add(field)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func isEmptyValue(v reflect.Value) bool {
|
|
switch v.Kind() {
|
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
|
return v.Len() == 0
|
|
case reflect.Bool:
|
|
return !v.Bool()
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
return v.Int() == 0
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
return v.Uint() == 0
|
|
case reflect.Float32, reflect.Float64:
|
|
return v.Float() == 0
|
|
case reflect.Interface, reflect.Ptr:
|
|
return v.IsNil()
|
|
}
|
|
return false
|
|
}
|
|
|
|
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
|
if len(index) == 1 {
|
|
return v.Field(index[0])
|
|
}
|
|
for i, x := range index {
|
|
if i > 0 {
|
|
var ok bool
|
|
v, ok = indirectNew(v)
|
|
if !ok {
|
|
return v
|
|
}
|
|
}
|
|
v = v.Field(x)
|
|
}
|
|
return v
|
|
}
|
|
|
|
func indirectNew(v reflect.Value) (reflect.Value, bool) {
|
|
if v.Kind() == reflect.Ptr {
|
|
if v.IsNil() {
|
|
if !v.CanSet() {
|
|
return v, false
|
|
}
|
|
elemType := v.Type().Elem()
|
|
if elemType.Kind() != reflect.Struct {
|
|
return v, false
|
|
}
|
|
v.Set(reflect.New(elemType))
|
|
}
|
|
v = v.Elem()
|
|
}
|
|
return v, true
|
|
}
|