blob: fea50719de9ea180c721e16fa6d455efd139b7ef [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001package jsoniter
2
3import (
4 "encoding"
5 "encoding/json"
6 "github.com/modern-go/reflect2"
7 "unsafe"
8)
9
10var marshalerType = reflect2.TypeOfPtr((*json.Marshaler)(nil)).Elem()
11var unmarshalerType = reflect2.TypeOfPtr((*json.Unmarshaler)(nil)).Elem()
12var textMarshalerType = reflect2.TypeOfPtr((*encoding.TextMarshaler)(nil)).Elem()
13var textUnmarshalerType = reflect2.TypeOfPtr((*encoding.TextUnmarshaler)(nil)).Elem()
14
15func createDecoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValDecoder {
16 ptrType := reflect2.PtrTo(typ)
17 if ptrType.Implements(unmarshalerType) {
18 return &referenceDecoder{
19 &unmarshalerDecoder{ptrType},
20 }
21 }
22 if ptrType.Implements(textUnmarshalerType) {
23 return &referenceDecoder{
24 &textUnmarshalerDecoder{ptrType},
25 }
26 }
27 return nil
28}
29
30func createEncoderOfMarshaler(ctx *ctx, typ reflect2.Type) ValEncoder {
31 if typ == marshalerType {
32 checkIsEmpty := createCheckIsEmpty(ctx, typ)
33 var encoder ValEncoder = &directMarshalerEncoder{
34 checkIsEmpty: checkIsEmpty,
35 }
36 return encoder
37 }
38 if typ.Implements(marshalerType) {
39 checkIsEmpty := createCheckIsEmpty(ctx, typ)
40 var encoder ValEncoder = &marshalerEncoder{
41 valType: typ,
42 checkIsEmpty: checkIsEmpty,
43 }
44 return encoder
45 }
46 ptrType := reflect2.PtrTo(typ)
47 if ctx.prefix != "" && ptrType.Implements(marshalerType) {
48 checkIsEmpty := createCheckIsEmpty(ctx, ptrType)
49 var encoder ValEncoder = &marshalerEncoder{
50 valType: ptrType,
51 checkIsEmpty: checkIsEmpty,
52 }
53 return &referenceEncoder{encoder}
54 }
55 if typ == textMarshalerType {
56 checkIsEmpty := createCheckIsEmpty(ctx, typ)
57 var encoder ValEncoder = &directTextMarshalerEncoder{
58 checkIsEmpty: checkIsEmpty,
59 stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
60 }
61 return encoder
62 }
63 if typ.Implements(textMarshalerType) {
64 checkIsEmpty := createCheckIsEmpty(ctx, typ)
65 var encoder ValEncoder = &textMarshalerEncoder{
66 valType: typ,
67 stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
68 checkIsEmpty: checkIsEmpty,
69 }
70 return encoder
71 }
72 // if prefix is empty, the type is the root type
73 if ctx.prefix != "" && ptrType.Implements(textMarshalerType) {
74 checkIsEmpty := createCheckIsEmpty(ctx, ptrType)
75 var encoder ValEncoder = &textMarshalerEncoder{
76 valType: ptrType,
77 stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")),
78 checkIsEmpty: checkIsEmpty,
79 }
80 return &referenceEncoder{encoder}
81 }
82 return nil
83}
84
85type marshalerEncoder struct {
86 checkIsEmpty checkIsEmpty
87 valType reflect2.Type
88}
89
90func (encoder *marshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
91 obj := encoder.valType.UnsafeIndirect(ptr)
92 if encoder.valType.IsNullable() && reflect2.IsNil(obj) {
93 stream.WriteNil()
94 return
95 }
96 bytes, err := json.Marshal(obj)
97 if err != nil {
98 stream.Error = err
99 } else {
100 stream.Write(bytes)
101 }
102}
103
104func (encoder *marshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
105 return encoder.checkIsEmpty.IsEmpty(ptr)
106}
107
108type directMarshalerEncoder struct {
109 checkIsEmpty checkIsEmpty
110}
111
112func (encoder *directMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
113 marshaler := *(*json.Marshaler)(ptr)
114 if marshaler == nil {
115 stream.WriteNil()
116 return
117 }
118 bytes, err := marshaler.MarshalJSON()
119 if err != nil {
120 stream.Error = err
121 } else {
122 stream.Write(bytes)
123 }
124}
125
126func (encoder *directMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
127 return encoder.checkIsEmpty.IsEmpty(ptr)
128}
129
130type textMarshalerEncoder struct {
131 valType reflect2.Type
132 stringEncoder ValEncoder
133 checkIsEmpty checkIsEmpty
134}
135
136func (encoder *textMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
137 obj := encoder.valType.UnsafeIndirect(ptr)
138 if encoder.valType.IsNullable() && reflect2.IsNil(obj) {
139 stream.WriteNil()
140 return
141 }
142 marshaler := (obj).(encoding.TextMarshaler)
143 bytes, err := marshaler.MarshalText()
144 if err != nil {
145 stream.Error = err
146 } else {
147 str := string(bytes)
148 encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream)
149 }
150}
151
152func (encoder *textMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
153 return encoder.checkIsEmpty.IsEmpty(ptr)
154}
155
156type directTextMarshalerEncoder struct {
157 stringEncoder ValEncoder
158 checkIsEmpty checkIsEmpty
159}
160
161func (encoder *directTextMarshalerEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
162 marshaler := *(*encoding.TextMarshaler)(ptr)
163 if marshaler == nil {
164 stream.WriteNil()
165 return
166 }
167 bytes, err := marshaler.MarshalText()
168 if err != nil {
169 stream.Error = err
170 } else {
171 str := string(bytes)
172 encoder.stringEncoder.Encode(unsafe.Pointer(&str), stream)
173 }
174}
175
176func (encoder *directTextMarshalerEncoder) IsEmpty(ptr unsafe.Pointer) bool {
177 return encoder.checkIsEmpty.IsEmpty(ptr)
178}
179
180type unmarshalerDecoder struct {
181 valType reflect2.Type
182}
183
184func (decoder *unmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
185 valType := decoder.valType
186 obj := valType.UnsafeIndirect(ptr)
187 unmarshaler := obj.(json.Unmarshaler)
188 iter.nextToken()
189 iter.unreadByte() // skip spaces
190 bytes := iter.SkipAndReturnBytes()
191 err := unmarshaler.UnmarshalJSON(bytes)
192 if err != nil {
193 iter.ReportError("unmarshalerDecoder", err.Error())
194 }
195}
196
197type textUnmarshalerDecoder struct {
198 valType reflect2.Type
199}
200
201func (decoder *textUnmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
202 valType := decoder.valType
203 obj := valType.UnsafeIndirect(ptr)
204 if reflect2.IsNil(obj) {
205 ptrType := valType.(*reflect2.UnsafePtrType)
206 elemType := ptrType.Elem()
207 elem := elemType.UnsafeNew()
208 ptrType.UnsafeSet(ptr, unsafe.Pointer(&elem))
209 obj = valType.UnsafeIndirect(ptr)
210 }
211 unmarshaler := (obj).(encoding.TextUnmarshaler)
212 str := iter.ReadString()
213 err := unmarshaler.UnmarshalText([]byte(str))
214 if err != nil {
215 iter.ReportError("textUnmarshalerDecoder", err.Error())
216 }
217}