| 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 |
| } |