gotosocial/vendor/github.com/vmihailenco/msgpack/v5/types.go

414 lines
8.6 KiB
Go

package msgpack
import (
"encoding"
"fmt"
"log"
"reflect"
"sync"
"github.com/vmihailenco/tagparser/v2"
)
var errorType = reflect.TypeOf((*error)(nil)).Elem()
var (
customEncoderType = reflect.TypeOf((*CustomEncoder)(nil)).Elem()
customDecoderType = reflect.TypeOf((*CustomDecoder)(nil)).Elem()
)
var (
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
)
var (
binaryMarshalerType = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()
binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
)
var (
textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
)
type (
encoderFunc func(*Encoder, reflect.Value) error
decoderFunc func(*Decoder, reflect.Value) error
)
var (
typeEncMap sync.Map
typeDecMap sync.Map
)
// Register registers encoder and decoder functions for a value.
// This is low level API and in most cases you should prefer implementing
// CustomEncoder/CustomDecoder or Marshaler/Unmarshaler interfaces.
func Register(value interface{}, enc encoderFunc, dec decoderFunc) {
typ := reflect.TypeOf(value)
if enc != nil {
typeEncMap.Store(typ, enc)
}
if dec != nil {
typeDecMap.Store(typ, dec)
}
}
//------------------------------------------------------------------------------
const defaultStructTag = "msgpack"
var structs = newStructCache()
type structCache struct {
m sync.Map
}
type structCacheKey struct {
typ reflect.Type
tag string
}
func newStructCache() *structCache {
return new(structCache)
}
func (m *structCache) Fields(typ reflect.Type, tag string) *fields {
key := structCacheKey{tag: tag, typ: typ}
if v, ok := m.m.Load(key); ok {
return v.(*fields)
}
fs := getFields(typ, tag)
m.m.Store(key, fs)
return fs
}
//------------------------------------------------------------------------------
type field struct {
encoder encoderFunc
decoder decoderFunc
name string
index []int
omitEmpty bool
}
func (f *field) Omit(e *Encoder, strct reflect.Value) bool {
v, ok := fieldByIndex(strct, f.index)
if !ok {
return true
}
forced := e.flags&omitEmptyFlag != 0
return (f.omitEmpty || forced) && e.isEmptyValue(v)
}
func (f *field) EncodeValue(e *Encoder, strct reflect.Value) error {
v, ok := fieldByIndex(strct, f.index)
if !ok {
return e.EncodeNil()
}
return f.encoder(e, v)
}
func (f *field) DecodeValue(d *Decoder, strct reflect.Value) error {
v := fieldByIndexAlloc(strct, f.index)
return f.decoder(d, v)
}
//------------------------------------------------------------------------------
type fields struct {
Type reflect.Type
Map map[string]*field
List []*field
AsArray bool
hasOmitEmpty bool
}
func newFields(typ reflect.Type) *fields {
return &fields{
Type: typ,
Map: make(map[string]*field, typ.NumField()),
List: make([]*field, 0, typ.NumField()),
}
}
func (fs *fields) Add(field *field) {
fs.warnIfFieldExists(field.name)
fs.Map[field.name] = field
fs.List = append(fs.List, field)
if field.omitEmpty {
fs.hasOmitEmpty = true
}
}
func (fs *fields) warnIfFieldExists(name string) {
if _, ok := fs.Map[name]; ok {
log.Printf("msgpack: %s already has field=%s", fs.Type, name)
}
}
func (fs *fields) OmitEmpty(e *Encoder, strct reflect.Value) []*field {
forced := e.flags&omitEmptyFlag != 0
if !fs.hasOmitEmpty && !forced {
return fs.List
}
fields := make([]*field, 0, len(fs.List))
for _, f := range fs.List {
if !f.Omit(e, strct) {
fields = append(fields, f)
}
}
return fields
}
func getFields(typ reflect.Type, fallbackTag string) *fields {
fs := newFields(typ)
var omitEmpty bool
for i := 0; i < typ.NumField(); i++ {
f := typ.Field(i)
tagStr := f.Tag.Get(defaultStructTag)
if tagStr == "" && fallbackTag != "" {
tagStr = f.Tag.Get(fallbackTag)
}
tag := tagparser.Parse(tagStr)
if tag.Name == "-" {
continue
}
if f.Name == "_msgpack" {
fs.AsArray = tag.HasOption("as_array") || tag.HasOption("asArray")
if tag.HasOption("omitempty") {
omitEmpty = true
}
}
if f.PkgPath != "" && !f.Anonymous {
continue
}
field := &field{
name: tag.Name,
index: f.Index,
omitEmpty: omitEmpty || tag.HasOption("omitempty"),
}
if tag.HasOption("intern") {
switch f.Type.Kind() {
case reflect.Interface:
field.encoder = encodeInternedInterfaceValue
field.decoder = decodeInternedInterfaceValue
case reflect.String:
field.encoder = encodeInternedStringValue
field.decoder = decodeInternedStringValue
default:
err := fmt.Errorf("msgpack: intern strings are not supported on %s", f.Type)
panic(err)
}
} else {
field.encoder = getEncoder(f.Type)
field.decoder = getDecoder(f.Type)
}
if field.name == "" {
field.name = f.Name
}
if f.Anonymous && !tag.HasOption("noinline") {
inline := tag.HasOption("inline")
if inline {
inlineFields(fs, f.Type, field, fallbackTag)
} else {
inline = shouldInline(fs, f.Type, field, fallbackTag)
}
if inline {
if _, ok := fs.Map[field.name]; ok {
log.Printf("msgpack: %s already has field=%s", fs.Type, field.name)
}
fs.Map[field.name] = field
continue
}
}
fs.Add(field)
if alias, ok := tag.Options["alias"]; ok {
fs.warnIfFieldExists(alias)
fs.Map[alias] = field
}
}
return fs
}
var (
encodeStructValuePtr uintptr
decodeStructValuePtr uintptr
)
//nolint:gochecknoinits
func init() {
encodeStructValuePtr = reflect.ValueOf(encodeStructValue).Pointer()
decodeStructValuePtr = reflect.ValueOf(decodeStructValue).Pointer()
}
func inlineFields(fs *fields, typ reflect.Type, f *field, tag string) {
inlinedFields := getFields(typ, tag).List
for _, field := range inlinedFields {
if _, ok := fs.Map[field.name]; ok {
// Don't inline shadowed fields.
continue
}
field.index = append(f.index, field.index...)
fs.Add(field)
}
}
func shouldInline(fs *fields, typ reflect.Type, f *field, tag string) 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, tag).List
for _, field := range inlinedFields {
if _, ok := fs.Map[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
}
type isZeroer interface {
IsZero() bool
}
func (e *Encoder) isEmptyValue(v reflect.Value) bool {
kind := v.Kind()
for kind == reflect.Interface {
if v.IsNil() {
return true
}
v = v.Elem()
kind = v.Kind()
}
if z, ok := v.Interface().(isZeroer); ok {
return nilable(kind) && v.IsNil() || z.IsZero()
}
switch kind {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Struct:
structFields := structs.Fields(v.Type(), e.structTag)
fields := structFields.OmitEmpty(e, v)
return len(fields) == 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.Ptr:
return v.IsNil()
default:
return false
}
}
func fieldByIndex(v reflect.Value, index []int) (_ reflect.Value, ok bool) {
if len(index) == 1 {
return v.Field(index[0]), true
}
for i, idx := range index {
if i > 0 {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return v, false
}
v = v.Elem()
}
}
v = v.Field(idx)
}
return v, true
}
func fieldByIndexAlloc(v reflect.Value, index []int) reflect.Value {
if len(index) == 1 {
return v.Field(index[0])
}
for i, idx := range index {
if i > 0 {
var ok bool
v, ok = indirectNil(v)
if !ok {
return v
}
}
v = v.Field(idx)
}
return v
}
func indirectNil(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(cachedValue(elemType))
}
v = v.Elem()
}
return v, true
}