blob: 152e3ef5a93c6e375ed1c3ea34e66e821ef5b9ac [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001package jsoniter
2
3import (
4 "fmt"
5 "github.com/modern-go/reflect2"
6 "io"
7 "reflect"
8 "unsafe"
9)
10
11func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder {
12 type bindingTo struct {
13 binding *Binding
14 toName string
15 ignored bool
16 }
17 orderedBindings := []*bindingTo{}
18 structDescriptor := describeStruct(ctx, typ)
19 for _, binding := range structDescriptor.Fields {
20 for _, toName := range binding.ToNames {
21 new := &bindingTo{
22 binding: binding,
23 toName: toName,
24 }
25 for _, old := range orderedBindings {
26 if old.toName != toName {
27 continue
28 }
29 old.ignored, new.ignored = resolveConflictBinding(ctx.frozenConfig, old.binding, new.binding)
30 }
31 orderedBindings = append(orderedBindings, new)
32 }
33 }
34 if len(orderedBindings) == 0 {
35 return &emptyStructEncoder{}
36 }
37 finalOrderedFields := []structFieldTo{}
38 for _, bindingTo := range orderedBindings {
39 if !bindingTo.ignored {
40 finalOrderedFields = append(finalOrderedFields, structFieldTo{
41 encoder: bindingTo.binding.Encoder.(*structFieldEncoder),
42 toName: bindingTo.toName,
43 })
44 }
45 }
46 return &structEncoder{typ, finalOrderedFields}
47}
48
49func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty {
50 encoder := createEncoderOfNative(ctx, typ)
51 if encoder != nil {
52 return encoder
53 }
54 kind := typ.Kind()
55 switch kind {
56 case reflect.Interface:
57 return &dynamicEncoder{typ}
58 case reflect.Struct:
59 return &structEncoder{typ: typ}
60 case reflect.Array:
61 return &arrayEncoder{}
62 case reflect.Slice:
63 return &sliceEncoder{}
64 case reflect.Map:
65 return encoderOfMap(ctx, typ)
66 case reflect.Ptr:
67 return &OptionalEncoder{}
68 default:
69 return &lazyErrorEncoder{err: fmt.Errorf("unsupported type: %v", typ)}
70 }
71}
72
73func resolveConflictBinding(cfg *frozenConfig, old, new *Binding) (ignoreOld, ignoreNew bool) {
74 newTagged := new.Field.Tag().Get(cfg.getTagKey()) != ""
75 oldTagged := old.Field.Tag().Get(cfg.getTagKey()) != ""
76 if newTagged {
77 if oldTagged {
78 if len(old.levels) > len(new.levels) {
79 return true, false
80 } else if len(new.levels) > len(old.levels) {
81 return false, true
82 } else {
83 return true, true
84 }
85 } else {
86 return true, false
87 }
88 } else {
89 if oldTagged {
90 return true, false
91 }
92 if len(old.levels) > len(new.levels) {
93 return true, false
94 } else if len(new.levels) > len(old.levels) {
95 return false, true
96 } else {
97 return true, true
98 }
99 }
100}
101
102type structFieldEncoder struct {
103 field reflect2.StructField
104 fieldEncoder ValEncoder
105 omitempty bool
106}
107
108func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
109 fieldPtr := encoder.field.UnsafeGet(ptr)
110 encoder.fieldEncoder.Encode(fieldPtr, stream)
111 if stream.Error != nil && stream.Error != io.EOF {
112 stream.Error = fmt.Errorf("%s: %s", encoder.field.Name(), stream.Error.Error())
113 }
114}
115
116func (encoder *structFieldEncoder) IsEmpty(ptr unsafe.Pointer) bool {
117 fieldPtr := encoder.field.UnsafeGet(ptr)
118 return encoder.fieldEncoder.IsEmpty(fieldPtr)
119}
120
121func (encoder *structFieldEncoder) IsEmbeddedPtrNil(ptr unsafe.Pointer) bool {
122 isEmbeddedPtrNil, converted := encoder.fieldEncoder.(IsEmbeddedPtrNil)
123 if !converted {
124 return false
125 }
126 fieldPtr := encoder.field.UnsafeGet(ptr)
127 return isEmbeddedPtrNil.IsEmbeddedPtrNil(fieldPtr)
128}
129
130type IsEmbeddedPtrNil interface {
131 IsEmbeddedPtrNil(ptr unsafe.Pointer) bool
132}
133
134type structEncoder struct {
135 typ reflect2.Type
136 fields []structFieldTo
137}
138
139type structFieldTo struct {
140 encoder *structFieldEncoder
141 toName string
142}
143
144func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
145 stream.WriteObjectStart()
146 isNotFirst := false
147 for _, field := range encoder.fields {
148 if field.encoder.omitempty && field.encoder.IsEmpty(ptr) {
149 continue
150 }
151 if field.encoder.IsEmbeddedPtrNil(ptr) {
152 continue
153 }
154 if isNotFirst {
155 stream.WriteMore()
156 }
157 stream.WriteObjectField(field.toName)
158 field.encoder.Encode(ptr, stream)
159 isNotFirst = true
160 }
161 stream.WriteObjectEnd()
162 if stream.Error != nil && stream.Error != io.EOF {
163 stream.Error = fmt.Errorf("%v.%s", encoder.typ, stream.Error.Error())
164 }
165}
166
167func (encoder *structEncoder) IsEmpty(ptr unsafe.Pointer) bool {
168 return false
169}
170
171type emptyStructEncoder struct {
172}
173
174func (encoder *emptyStructEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
175 stream.WriteEmptyObject()
176}
177
178func (encoder *emptyStructEncoder) IsEmpty(ptr unsafe.Pointer) bool {
179 return false
180}
181
182type stringModeNumberEncoder struct {
183 elemEncoder ValEncoder
184}
185
186func (encoder *stringModeNumberEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
187 stream.writeByte('"')
188 encoder.elemEncoder.Encode(ptr, stream)
189 stream.writeByte('"')
190}
191
192func (encoder *stringModeNumberEncoder) IsEmpty(ptr unsafe.Pointer) bool {
193 return encoder.elemEncoder.IsEmpty(ptr)
194}
195
196type stringModeStringEncoder struct {
197 elemEncoder ValEncoder
198 cfg *frozenConfig
199}
200
201func (encoder *stringModeStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
202 tempStream := encoder.cfg.BorrowStream(nil)
David K. Bainbridgebd6b2882021-08-26 13:31:02 +0000203 tempStream.Attachment = stream.Attachment
Zack Williamse940c7a2019-08-21 14:25:39 -0700204 defer encoder.cfg.ReturnStream(tempStream)
205 encoder.elemEncoder.Encode(ptr, tempStream)
206 stream.WriteString(string(tempStream.Buffer()))
207}
208
209func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
210 return encoder.elemEncoder.IsEmpty(ptr)
211}