blob: 6a1bf7f91343f96f81cf127500d457d146c8f518 [file] [log] [blame]
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
}