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 )
tempStream .Attachment = stream .Attachment
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 )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .