forked from github/multus-cni
gomodule is still in progress to migrate for now, hence multus team decide to keep vendor directory to support build without gomodule.
211 lines
5.1 KiB
Go
211 lines
5.1 KiB
Go
package jsoniter
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/modern-go/reflect2"
|
|
"io"
|
|
"reflect"
|
|
"unsafe"
|
|
)
|
|
|
|
func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder {
|
|
type bindingTo struct {
|
|
binding *Binding
|
|
toName string
|
|
ignored bool
|
|
}
|
|
orderedBindings := []*bindingTo{}
|
|
structDescriptor := describeStruct(ctx, typ)
|
|
for _, binding := range structDescriptor.Fields {
|
|
for _, toName := range binding.ToNames {
|
|
new := &bindingTo{
|
|
binding: binding,
|
|
toName: toName,
|
|
}
|
|
for _, old := range orderedBindings {
|
|
if old.toName != toName {
|
|
continue
|
|
}
|
|
old.ignored, new.ignored = resolveConflictBinding(ctx.frozenConfig, old.binding, new.binding)
|
|
}
|
|
orderedBindings = append(orderedBindings, new)
|
|
}
|
|
}
|
|
if len(orderedBindings) == 0 {
|
|
return &emptyStructEncoder{}
|
|
}
|
|
finalOrderedFields := []structFieldTo{}
|
|
for _, bindingTo := range orderedBindings {
|
|
if !bindingTo.ignored {
|
|
finalOrderedFields = append(finalOrderedFields, structFieldTo{
|
|
encoder: bindingTo.binding.Encoder.(*structFieldEncoder),
|
|
toName: bindingTo.toName,
|
|
})
|
|
}
|
|
}
|
|
return &structEncoder{typ, finalOrderedFields}
|
|
}
|
|
|
|
func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty {
|
|
encoder := createEncoderOfNative(ctx, typ)
|
|
if encoder != nil {
|
|
return encoder
|
|
}
|
|
kind := typ.Kind()
|
|
switch kind {
|
|
case reflect.Interface:
|
|
return &dynamicEncoder{typ}
|
|
case reflect.Struct:
|
|
return &structEncoder{typ: typ}
|
|
case reflect.Array:
|
|
return &arrayEncoder{}
|
|
case reflect.Slice:
|
|
return &sliceEncoder{}
|
|
case reflect.Map:
|
|
return encoderOfMap(ctx, typ)
|
|
case reflect.Ptr:
|
|
return &OptionalEncoder{}
|
|
default:
|
|
return &lazyErrorEncoder{err: fmt.Errorf("unsupported type: %v", typ)}
|
|
}
|
|
}
|
|
|
|
func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
|
|
newTagged := new.Field.Tag().Get(cfg.getTagKey()) != ""
|
|
oldTagged := old.Field.Tag().Get(cfg.getTagKey()) != ""
|
|
if newTagged {
|
|
if oldTagged {
|
|
if len(old.levels) > len(new.levels) {
|
|
return true, false
|
|
} else if len(new.levels) > len(old.levels) {
|
|
return false, true
|
|
} else {
|
|
return true, true
|
|
}
|
|
} else {
|
|
return true, false
|
|
}
|
|
} else {
|
|
if oldTagged {
|
|
return true, false
|
|
}
|
|
if len(old.levels) > len(new.levels) {
|
|
return true, false
|
|
} else if len(new.levels) > len(old.levels) {
|
|
return false, true
|
|
} else {
|
|
return true, true
|
|
}
|
|
}
|
|
}
|
|
|
|
type structFieldEncoder struct {
|
|
field reflect2.StructField
|
|
fieldEncoder ValEncoder
|
|
omitempty bool
|
|
}
|
|
|
|
func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
fieldPtr := encoder.field.UnsafeGet(ptr)
|
|
encoder.fieldEncoder.Encode(fieldPtr, stream)
|
|
if stream.Error != nil && stream.Error != io.EOF {
|
|
stream.Error = fmt.Errorf("%s: %s", encoder.field.Name(), stream.Error.Error())
|
|
}
|
|
}
|
|
|
|
func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
fieldPtr := encoder.field.UnsafeGet(ptr)
|
|
return encoder.fieldEncoder.IsEmpty(fieldPtr)
|
|
}
|
|
|
|
func (encoder *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
|
|
isEmbeddedPtrNil, converted := encoder.fieldEncoder.(IsEmbeddedPtrNil)
|
|
if !converted {
|
|
return false
|
|
}
|
|
fieldPtr := encoder.field.UnsafeGet(ptr)
|
|
return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
|
|
}
|
|
|
|
type IsEmbeddedPtrNil interface {
|
|
IsEmbeddedPtrNil(ptr unsafe.Pointer) bool
|
|
}
|
|
|
|
type structEncoder struct {
|
|
typ reflect2.Type
|
|
fields []structFieldTo
|
|
}
|
|
|
|
type structFieldTo struct {
|
|
encoder *structFieldEncoder
|
|
toName string
|
|
}
|
|
|
|
func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
stream.WriteObjectStart()
|
|
isNotFirst := false
|
|
for _, field := range encoder.fields {
|
|
if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
|
|
continue
|
|
}
|
|
if field.encoder.IsEmbeddedPtrNil(ptr) {
|
|
continue
|
|
}
|
|
if isNotFirst {
|
|
stream.WriteMore()
|
|
}
|
|
stream.WriteObjectField(field.toName)
|
|
field.encoder.Encode(ptr, stream)
|
|
isNotFirst = true
|
|
}
|
|
stream.WriteObjectEnd()
|
|
if stream.Error != nil && stream.Error != io.EOF {
|
|
stream.Error = fmt.Errorf("%v.%s", encoder.typ, stream.Error.Error())
|
|
}
|
|
}
|
|
|
|
func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
return false
|
|
}
|
|
|
|
type emptyStructEncoder struct {
|
|
}
|
|
|
|
func (encoder *emptyStructEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
stream.WriteEmptyObject()
|
|
}
|
|
|
|
func (encoder *emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
return false
|
|
}
|
|
|
|
type stringModeNumberEncoder struct {
|
|
elemEncoder ValEncoder
|
|
}
|
|
|
|
func (encoder *stringModeNumberEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
stream.writeByte('"')
|
|
encoder.elemEncoder.Encode(ptr, stream)
|
|
stream.writeByte('"')
|
|
}
|
|
|
|
func (encoder *stringModeNumberEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
return encoder.elemEncoder.IsEmpty(ptr)
|
|
}
|
|
|
|
type stringModeStringEncoder struct {
|
|
elemEncoder ValEncoder
|
|
cfg *frozenConfig
|
|
}
|
|
|
|
func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
tempStream := encoder.cfg.BorrowStream(nil)
|
|
defer encoder.cfg.ReturnStream(tempStream)
|
|
encoder.elemEncoder.Encode(ptr, tempStream)
|
|
stream.WriteString(string(tempStream.Buffer()))
|
|
}
|
|
|
|
func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
return encoder.elemEncoder.IsEmpty(ptr)
|
|
}
|