mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
5.1 KiB
210 lines
5.1 KiB
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) |
|
}
|
|
|