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