1package 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)
203	defer encoder.cfg.ReturnStream(tempStream)
204	encoder.elemEncoder.Encode(ptr, tempStream)
205	stream.WriteString(string(tempStream.Buffer()))
206}
207
208func (encoder *stringModeStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
209	return encoder.elemEncoder.IsEmpty(ptr)
210}
211