mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-05-20 09:18:13 +00:00
414 lines
8.6 KiB
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
|
|
}
|