json.mx raw
1 package json
2
3 import (
4 "bytes"
5 "errors"
6 "math"
7 "strconv"
8 "unsafe"
9 )
10
11 type Marshaler interface {
12 MarshalJSON() ([]byte, error)
13 }
14
15 type Unmarshaler interface {
16 UnmarshalJSON([]byte) error
17 }
18
19 func Marshal(v any) ([]byte, error) {
20 if v == nil {
21 return []byte("null"), nil
22 }
23 if m, ok := v.(Marshaler); ok {
24 return m.MarshalJSON()
25 }
26 tc := typeCodeOf(v)
27 if tc == nil {
28 return nil, errors.New("json: cannot marshal nil")
29 }
30 dp := dataOf(v)
31 buf := []byte{:0:256}
32 return appendValue(buf, tc, dp)
33 }
34
35 func Unmarshal(data []byte, v any) error {
36 if v == nil {
37 return errors.New("json: Unmarshal requires non-nil pointer")
38 }
39 if u, ok := v.(Unmarshaler); ok {
40 return u.UnmarshalJSON(data)
41 }
42 elemType, dataPtr := derefPtr(v)
43 if elemType == nil {
44 return errors.New("json: Unmarshal requires pointer argument")
45 }
46 if dataPtr == nil {
47 return errors.New("json: Unmarshal into nil pointer")
48 }
49 pos := skipWS(data, 0)
50 _, err := parseValue(data, pos, elemType, dataPtr)
51 return err
52 }
53
54 // --- Marshal internals ---
55
56 func appendValue(buf []byte, typ *_rawType, ptr unsafe.Pointer) ([]byte, error) {
57 // Check Marshaler interface on pointer-to-type.
58 iface := ptrToIface(typ, ptr)
59 if iface != nil {
60 if m, ok := iface.(Marshaler); ok {
61 b, err := m.MarshalJSON()
62 if err != nil {
63 return nil, err
64 }
65 return append(buf, b...), nil
66 }
67 }
68
69 switch typ.kind() {
70 case jkBool:
71 if *(*bool)(ptr) {
72 return append(buf, "true"...), nil
73 }
74 return append(buf, "false"...), nil
75
76 case jkInt:
77 return strconv.AppendInt(buf, int64(*(*int32)(ptr)), 10), nil
78 case jkInt8:
79 return strconv.AppendInt(buf, int64(*(*int8)(ptr)), 10), nil
80 case jkInt16:
81 return strconv.AppendInt(buf, int64(*(*int16)(ptr)), 10), nil
82 case jkInt32:
83 return strconv.AppendInt(buf, int64(*(*int32)(ptr)), 10), nil
84 case jkInt64:
85 return strconv.AppendInt(buf, *(*int64)(ptr), 10), nil
86
87 case jkUint:
88 return strconv.AppendUint(buf, uint64(*(*uint32)(ptr)), 10), nil
89 case jkUint8:
90 return strconv.AppendUint(buf, uint64(*(*uint8)(ptr)), 10), nil
91 case jkUint16:
92 return strconv.AppendUint(buf, uint64(*(*uint16)(ptr)), 10), nil
93 case jkUint32:
94 return strconv.AppendUint(buf, uint64(*(*uint32)(ptr)), 10), nil
95 case jkUint64:
96 return strconv.AppendUint(buf, *(*uint64)(ptr), 10), nil
97
98 case jkFloat32:
99 f := float64(*(*float32)(ptr))
100 if math.IsInf(f, 0) || math.IsNaN(f) {
101 return nil, errors.New("json: unsupported float value")
102 }
103 return strconv.AppendFloat(buf, f, 'f', -1, 32), nil
104 case jkFloat64:
105 f := *(*float64)(ptr)
106 if math.IsInf(f, 0) || math.IsNaN(f) {
107 return nil, errors.New("json: unsupported float value")
108 }
109 return strconv.AppendFloat(buf, f, 'f', -1, 64), nil
110
111 case jkBytes:
112 s := *(*[]byte)(ptr)
113 return appendString(buf, s), nil
114
115 case jkPointer:
116 elemType := typ.elem()
117 elemPtr := *(*unsafe.Pointer)(ptr)
118 if elemPtr == nil {
119 return append(buf, "null"...), nil
120 }
121 return appendValue(buf, elemType, elemPtr)
122
123 case jkStruct:
124 return appendStruct(buf, typ, ptr)
125
126 case jkSlice:
127 return appendSlice(buf, typ, ptr)
128
129 case jkMap:
130 return nil, errors.New("json: map types not supported")
131 case jkInterface:
132 return nil, errors.New("json: interface{} field values not supported")
133 case jkFunc:
134 return nil, errors.New("json: func types not supported")
135 case jkChan:
136 return nil, errors.New("json: chan types not supported")
137 }
138
139 return nil, errors.New("json: unsupported type")
140 }
141
142 func appendStruct(buf []byte, typ *_rawType, ptr unsafe.Pointer) ([]byte, error) {
143 sf := structFieldsFor(typ)
144 buf = append(buf, '{')
145 first := true
146 for i := 0; i < len(sf.params); i++ {
147 p := &sf.params[i]
148 if p.skip {
149 continue
150 }
151 ft := typ.fieldType(i)
152 fp := unsafe.Add(ptr, typ.fieldOffset(i))
153 if p.omitEmpty && isZeroValue(ft, fp) {
154 continue
155 }
156 if !first {
157 buf = append(buf, ',')
158 }
159 first = false
160 buf = appendString(buf, p.jsonName)
161 buf = append(buf, ':')
162 var err error
163 buf, err = appendValue(buf, ft, fp)
164 if err != nil {
165 return nil, err
166 }
167 }
168 buf = append(buf, '}')
169 return buf, nil
170 }
171
172 func appendSlice(buf []byte, typ *_rawType, ptr unsafe.Pointer) ([]byte, error) {
173 hdr := (*_sliceHeader)(ptr)
174 if hdr.data == nil {
175 return append(buf, "null"...), nil
176 }
177 elemType := typ.elem()
178 buf = append(buf, '[')
179 for i := 0; i < hdr.len; i++ {
180 if i > 0 {
181 buf = append(buf, ',')
182 }
183 elemPtr := unsafe.Add(hdr.data, elemType.size()*uintptr(i))
184 var err error
185 buf, err = appendValue(buf, elemType, elemPtr)
186 if err != nil {
187 return nil, err
188 }
189 }
190 buf = append(buf, ']')
191 return buf, nil
192 }
193
194 func appendString(buf []byte, s []byte) []byte {
195 buf = append(buf, '"')
196 start := 0
197 for i := 0; i < len(s); i++ {
198 c := s[i]
199 var esc []byte
200 switch c {
201 case '"':
202 esc = []byte(`\"`)
203 case '\\':
204 esc = []byte(`\\`)
205 case '\n':
206 esc = []byte(`\n`)
207 case '\r':
208 esc = []byte(`\r`)
209 case '\t':
210 esc = []byte(`\t`)
211 case '\b':
212 esc = []byte(`\b`)
213 case '\f':
214 esc = []byte(`\f`)
215 default:
216 if c < 0x20 {
217 hc := _hexChars()
218 esc = []byte{'\\', 'u', '0', '0', hc[c>>4], hc[c&0x0f]}
219 } else {
220 continue
221 }
222 }
223 buf = append(buf, s[start:i]...)
224 buf = append(buf, esc...)
225 start = i + 1
226 }
227 buf = append(buf, s[start:]...)
228 buf = append(buf, '"')
229 return buf
230 }
231
232 func _hexChars() [16]byte {
233 return [16]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}
234 }
235
236 // --- Tag parsing ---
237
238 type fieldParams struct {
239 jsonName []byte
240 omitEmpty bool
241 skip bool
242 asString bool
243 }
244
245 type structFields struct {
246 byName map[string]int
247 params []fieldParams
248 }
249
250 var _fieldCacheMapDone bool
251 var _fieldCacheMap map[uintptr]*structFields
252
253 func fieldCache() map[uintptr]*structFields {
254 if !_fieldCacheMapDone {
255 _fieldCacheMapDone = true
256 _fieldCacheMap = map[uintptr]*structFields{}
257 }
258 return _fieldCacheMap
259 }
260
261 func structFieldsFor(typ *_rawType) *structFields {
262 key := uintptr(unsafe.Pointer(typ))
263 fc := fieldCache()
264 if sf, ok := fc[key]; ok {
265 return sf
266 }
267 u := typ.underlying()
268 n := u.numField()
269 sf := &structFields{
270 byName: map[string]int{},
271 params: []fieldParams{:n},
272 }
273 for i := 0; i < n; i++ {
274 if !u.fieldExported(i) {
275 continue
276 }
277 tag := u.fieldTag(i)
278 name := u.fieldName(i)
279 p := parseJSONTag(tag, name)
280 sf.params[i] = p
281 if !p.skip {
282 sf.byName[string(p.jsonName)] = i
283 }
284 }
285 fc[key] = sf
286 return sf
287 }
288
289 func parseJSONTag(tag []byte, fieldName []byte) fieldParams {
290 var p fieldParams
291 jsonTag := getJSONTag(tag)
292 if jsonTag == nil {
293 p.jsonName = SnakeCase(fieldName)
294 return p
295 }
296 name, rest, _ := bytes.Cut(jsonTag, ",")
297 if string(name) == "-" && len(rest) == 0 {
298 p.skip = true
299 return p
300 }
301 if len(name) > 0 {
302 p.jsonName = name
303 } else {
304 p.jsonName = SnakeCase(fieldName)
305 }
306 for len(rest) > 0 {
307 var opt []byte
308 opt, rest, _ = bytes.Cut(rest, ",")
309 switch {
310 case string(opt) == "omitempty":
311 p.omitEmpty = true
312 case string(opt) == "string":
313 p.asString = true
314 }
315 }
316 return p
317 }
318
319 func getJSONTag(tag []byte) []byte {
320 key := []byte(`json:"`)
321 i := bytes.Index(tag, key)
322 if i < 0 {
323 return nil
324 }
325 tag = tag[i+len(key):]
326 j := bytes.IndexByte(tag, '"')
327 if j < 0 {
328 return tag
329 }
330 return tag[:j]
331 }
332
333 // --- snake_case ---
334
335 func SnakeCase(s []byte) []byte {
336 if len(s) == 0 {
337 return s
338 }
339 buf := []byte{:0:len(s)+4}
340 wordStart := 0
341 i := 0
342 for i < len(s) {
343 if !isUpper(s[i]) {
344 i++
345 continue
346 }
347 // Start of an uppercase run.
348 runStart := i
349 for i < len(s) && isUpper(s[i]) {
350 i++
351 }
352 runLen := i - runStart
353
354 if runStart > wordStart {
355 // Flush lowercase word before the run.
356 if len(buf) > 0 {
357 buf = append(buf, '_')
358 }
359 buf = appendLower(buf, s[wordStart:runStart])
360 }
361
362 if i < len(s) && isLower(s[i]) {
363 // Lookahead: last uppercase of run belongs to next word.
364 if runLen > 1 {
365 if len(buf) > 0 {
366 buf = append(buf, '_')
367 }
368 buf = appendLower(buf, s[runStart:i-1])
369 }
370 // The last uppercase char starts the next word.
371 wordStart = i - 1
372 } else {
373 // End of string or next char is also uppercase - entire run is one word.
374 if len(buf) > 0 {
375 buf = append(buf, '_')
376 }
377 buf = appendLower(buf, s[runStart:i])
378 wordStart = i
379 }
380 }
381 if wordStart < len(s) {
382 if len(buf) > 0 {
383 buf = append(buf, '_')
384 }
385 buf = appendLower(buf, s[wordStart:])
386 }
387 return buf
388 }
389
390 func isUpper(c byte) bool { return c >= 'A' && c <= 'Z' }
391 func isLower(c byte) bool { return c >= 'a' && c <= 'z' }
392
393 func appendLower(buf []byte, s []byte) []byte {
394 for _, c := range s {
395 if isUpper(c) {
396 buf = append(buf, c+32)
397 } else {
398 buf = append(buf, c)
399 }
400 }
401 return buf
402 }
403