Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
encode.go1346 linesDownload Raw Back to json
1// Copyright 2010 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5//go:build !goexperiment.jsonv26 7// Package json implements encoding and decoding of JSON as defined in RFC 7159.8// The mapping between JSON and Go values is described in the documentation for9// the Marshal and Unmarshal functions.10//11// See "JSON and Go" for an introduction to this package:12// https://golang.org/doc/articles/json_and_go.html13//14// # Security Considerations15//16// The JSON standard (RFC 7159) is lax in its definition of a number of parser17// behaviors. As such, many JSON parsers behave differently in various18// scenarios. These differences in parsers mean that systems that use multiple19// independent JSON parser implementations may parse the same JSON object in20// differing ways.21//22// Systems that rely on a JSON object being parsed consistently for security23// purposes should be careful to understand the behaviors of this parser, as24// well as how these behaviors may cause interoperability issues with other25// parser implementations.26//27// Due to the Go Backwards Compatibility promise (https://go.dev/doc/go1compat)28// there are a number of behaviors this package exhibits that may cause29// interopability issues, but cannot be changed. In particular the following30// parsing behaviors may cause issues:31//32//   - If a JSON object contains duplicate keys, keys are processed in the order33//     they are observed, meaning later values will replace or be merged into34//     prior values, depending on the field type (in particular maps and structs35//     will have values merged, while other types have values replaced).36//   - When parsing a JSON object into a Go struct, keys are considered in a37//     case-insensitive fashion.38//   - When parsing a JSON object into a Go struct, unknown keys in the JSON39//     object are ignored (unless a [Decoder] is used and40//     [Decoder.DisallowUnknownFields] has been called).41//   - Invalid UTF-8 bytes in JSON strings are replaced by the Unicode42//     replacement character.43//   - Large JSON number integers will lose precision when unmarshaled into44//     floating-point types.45package json46 47import (48	"bytes"49	"cmp"50	"encoding"51	"encoding/base64"52	"fmt"53	"math"54	"reflect"55	"slices"56	"strconv"57	"strings"58	"sync"59	"unicode"60	"unicode/utf8"61	_ "unsafe" // for linkname62)63 64// Marshal returns the JSON encoding of v.65//66// Marshal traverses the value v recursively.67// If an encountered value implements [Marshaler]68// and is not a nil pointer, Marshal calls [Marshaler.MarshalJSON]69// to produce JSON. If no [Marshaler.MarshalJSON] method is present but the70// value implements [encoding.TextMarshaler] instead, Marshal calls71// [encoding.TextMarshaler.MarshalText] and encodes the result as a JSON string.72// The nil pointer exception is not strictly necessary73// but mimics a similar, necessary exception in the behavior of74// [Unmarshaler.UnmarshalJSON].75//76// Otherwise, Marshal uses the following type-dependent default encodings:77//78// Boolean values encode as JSON booleans.79//80// Floating point, integer, and [Number] values encode as JSON numbers.81// NaN and +/-Inf values will return an [UnsupportedValueError].82//83// String values encode as JSON strings coerced to valid UTF-8,84// replacing invalid bytes with the Unicode replacement rune.85// So that the JSON will be safe to embed inside HTML <script> tags,86// the string is encoded using [HTMLEscape],87// which replaces "<", ">", "&", U+2028, and U+2029 are escaped88// to "\u003c","\u003e", "\u0026", "\u2028", and "\u2029".89// This replacement can be disabled when using an [Encoder],90// by calling [Encoder.SetEscapeHTML](false).91//92// Array and slice values encode as JSON arrays, except that93// []byte encodes as a base64-encoded string, and a nil slice94// encodes as the null JSON value.95//96// Struct values encode as JSON objects.97// Each exported struct field becomes a member of the object, using the98// field name as the object key, unless the field is omitted for one of the99// reasons given below.100//101// The encoding of each struct field can be customized by the format string102// stored under the "json" key in the struct field's tag.103// The format string gives the name of the field, possibly followed by a104// comma-separated list of options. The name may be empty in order to105// specify options without overriding the default field name.106//107// The "omitempty" option specifies that the field should be omitted108// from the encoding if the field has an empty value, defined as109// false, 0, a nil pointer, a nil interface value, and any array,110// slice, map, or string of length zero.111//112// As a special case, if the field tag is "-", the field is always omitted.113// Note that a field with name "-" can still be generated using the tag "-,".114//115// Examples of struct field tags and their meanings:116//117//	// Field appears in JSON as key "myName".118//	Field int `json:"myName"`119//120//	// Field appears in JSON as key "myName" and121//	// the field is omitted from the object if its value is empty,122//	// as defined above.123//	Field int `json:"myName,omitempty"`124//125//	// Field appears in JSON as key "Field" (the default), but126//	// the field is skipped if empty.127//	// Note the leading comma.128//	Field int `json:",omitempty"`129//130//	// Field is ignored by this package.131//	Field int `json:"-"`132//133//	// Field appears in JSON as key "-".134//	Field int `json:"-,"`135//136// The "omitzero" option specifies that the field should be omitted137// from the encoding if the field has a zero value, according to rules:138//139// 1) If the field type has an "IsZero() bool" method, that will be used to140// determine whether the value is zero.141//142// 2) Otherwise, the value is zero if it is the zero value for its type.143//144// If both "omitempty" and "omitzero" are specified, the field will be omitted145// if the value is either empty or zero (or both).146//147// The "string" option signals that a field is stored as JSON inside a148// JSON-encoded string. It applies only to fields of string, floating point,149// integer, or boolean types. This extra level of encoding is sometimes used150// when communicating with JavaScript programs:151//152//	Int64String int64 `json:",string"`153//154// The key name will be used if it's a non-empty string consisting of155// only Unicode letters, digits, and ASCII punctuation except quotation156// marks, backslash, and comma.157//158// Embedded struct fields are usually marshaled as if their inner exported fields159// were fields in the outer struct, subject to the usual Go visibility rules amended160// as described in the next paragraph.161// An anonymous struct field with a name given in its JSON tag is treated as162// having that name, rather than being anonymous.163// An anonymous struct field of interface type is treated the same as having164// that type as its name, rather than being anonymous.165//166// The Go visibility rules for struct fields are amended for JSON when167// deciding which field to marshal or unmarshal. If there are168// multiple fields at the same level, and that level is the least169// nested (and would therefore be the nesting level selected by the170// usual Go rules), the following extra rules apply:171//172// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered,173// even if there are multiple untagged fields that would otherwise conflict.174//175// 2) If there is exactly one field (tagged or not according to the first rule), that is selected.176//177// 3) Otherwise there are multiple fields, and all are ignored; no error occurs.178//179// Handling of anonymous struct fields is new in Go 1.1.180// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of181// an anonymous struct field in both current and earlier versions, give the field182// a JSON tag of "-".183//184// Map values encode as JSON objects. The map's key type must either be a185// string, an integer type, or implement [encoding.TextMarshaler]. The map keys186// are sorted and used as JSON object keys by applying the following rules,187// subject to the UTF-8 coercion described for string values above:188//   - keys of any string type are used directly189//   - keys that implement [encoding.TextMarshaler] are marshaled190//   - integer keys are converted to strings191//192// Pointer values encode as the value pointed to.193// A nil pointer encodes as the null JSON value.194//195// Interface values encode as the value contained in the interface.196// A nil interface value encodes as the null JSON value.197//198// Channel, complex, and function values cannot be encoded in JSON.199// Attempting to encode such a value causes Marshal to return200// an [UnsupportedTypeError].201//202// JSON cannot represent cyclic data structures and Marshal does not203// handle them. Passing cyclic structures to Marshal will result in204// an error.205func Marshal(v any) ([]byte, error) {206	e := newEncodeState()207	defer encodeStatePool.Put(e)208 209	err := e.marshal(v, encOpts{escapeHTML: true})210	if err != nil {211		return nil, err212	}213	buf := append([]byte(nil), e.Bytes()...)214 215	return buf, nil216}217 218// MarshalIndent is like [Marshal] but applies [Indent] to format the output.219// Each JSON element in the output will begin on a new line beginning with prefix220// followed by one or more copies of indent according to the indentation nesting.221func MarshalIndent(v any, prefix, indent string) ([]byte, error) {222	b, err := Marshal(v)223	if err != nil {224		return nil, err225	}226	b2 := make([]byte, 0, indentGrowthFactor*len(b))227	b2, err = appendIndent(b2, b, prefix, indent)228	if err != nil {229		return nil, err230	}231	return b2, nil232}233 234// Marshaler is the interface implemented by types that235// can marshal themselves into valid JSON.236type Marshaler interface {237	MarshalJSON() ([]byte, error)238}239 240// An UnsupportedTypeError is returned by [Marshal] when attempting241// to encode an unsupported value type.242type UnsupportedTypeError struct {243	Type reflect.Type244}245 246func (e *UnsupportedTypeError) Error() string {247	return "json: unsupported type: " + e.Type.String()248}249 250// An UnsupportedValueError is returned by [Marshal] when attempting251// to encode an unsupported value.252type UnsupportedValueError struct {253	Value reflect.Value254	Str   string255}256 257func (e *UnsupportedValueError) Error() string {258	return "json: unsupported value: " + e.Str259}260 261// Before Go 1.2, an InvalidUTF8Error was returned by [Marshal] when262// attempting to encode a string value with invalid UTF-8 sequences.263// As of Go 1.2, [Marshal] instead coerces the string to valid UTF-8 by264// replacing invalid bytes with the Unicode replacement rune U+FFFD.265//266// Deprecated: No longer used; kept for compatibility.267type InvalidUTF8Error struct {268	S string // the whole string value that caused the error269}270 271func (e *InvalidUTF8Error) Error() string {272	return "json: invalid UTF-8 in string: " + strconv.Quote(e.S)273}274 275// A MarshalerError represents an error from calling a276// [Marshaler.MarshalJSON] or [encoding.TextMarshaler.MarshalText] method.277type MarshalerError struct {278	Type       reflect.Type279	Err        error280	sourceFunc string281}282 283func (e *MarshalerError) Error() string {284	srcFunc := e.sourceFunc285	if srcFunc == "" {286		srcFunc = "MarshalJSON"287	}288	return "json: error calling " + srcFunc +289		" for type " + e.Type.String() +290		": " + e.Err.Error()291}292 293// Unwrap returns the underlying error.294func (e *MarshalerError) Unwrap() error { return e.Err }295 296const hex = "0123456789abcdef"297 298// An encodeState encodes JSON into a bytes.Buffer.299type encodeState struct {300	bytes.Buffer // accumulated output301 302	// Keep track of what pointers we've seen in the current recursive call303	// path, to avoid cycles that could lead to a stack overflow. Only do304	// the relatively expensive map operations if ptrLevel is larger than305	// startDetectingCyclesAfter, so that we skip the work if we're within a306	// reasonable amount of nested pointers deep.307	ptrLevel uint308	ptrSeen  map[any]struct{}309}310 311const startDetectingCyclesAfter = 1000312 313var encodeStatePool sync.Pool314 315func newEncodeState() *encodeState {316	if v := encodeStatePool.Get(); v != nil {317		e := v.(*encodeState)318		e.Reset()319		if len(e.ptrSeen) > 0 {320			panic("ptrEncoder.encode should have emptied ptrSeen via defers")321		}322		e.ptrLevel = 0323		return e324	}325	return &encodeState{ptrSeen: make(map[any]struct{})}326}327 328// jsonError is an error wrapper type for internal use only.329// Panics with errors are wrapped in jsonError so that the top-level recover330// can distinguish intentional panics from this package.331type jsonError struct{ error }332 333func (e *encodeState) marshal(v any, opts encOpts) (err error) {334	defer func() {335		if r := recover(); r != nil {336			if je, ok := r.(jsonError); ok {337				err = je.error338			} else {339				panic(r)340			}341		}342	}()343	e.reflectValue(reflect.ValueOf(v), opts)344	return nil345}346 347// error aborts the encoding by panicking with err wrapped in jsonError.348func (e *encodeState) error(err error) {349	panic(jsonError{err})350}351 352func isEmptyValue(v reflect.Value) bool {353	switch v.Kind() {354	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:355		return v.Len() == 0356	case reflect.Bool,357		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,358		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,359		reflect.Float32, reflect.Float64,360		reflect.Interface, reflect.Pointer:361		return v.IsZero()362	}363	return false364}365 366func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {367	valueEncoder(v)(e, v, opts)368}369 370type encOpts struct {371	// quoted causes primitive fields to be encoded inside JSON strings.372	quoted bool373	// escapeHTML causes '<', '>', and '&' to be escaped in JSON strings.374	escapeHTML bool375}376 377type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts)378 379var encoderCache sync.Map // map[reflect.Type]encoderFunc380 381func valueEncoder(v reflect.Value) encoderFunc {382	if !v.IsValid() {383		return invalidValueEncoder384	}385	return typeEncoder(v.Type())386}387 388func typeEncoder(t reflect.Type) encoderFunc {389	if fi, ok := encoderCache.Load(t); ok {390		return fi.(encoderFunc)391	}392 393	// To deal with recursive types, populate the map with an394	// indirect func before we build it. If the type is recursive,395	// the second lookup for the type will return the indirect func.396	//397	// This indirect func is only used for recursive types,398	// and briefly during racing calls to typeEncoder.399	indirect := sync.OnceValue(func() encoderFunc {400		return newTypeEncoder(t, true)401	})402	fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value, opts encOpts) {403		indirect()(e, v, opts)404	}))405	if loaded {406		return fi.(encoderFunc)407	}408 409	f := indirect()410	encoderCache.Store(t, f)411	return f412}413 414var (415	marshalerType     = reflect.TypeFor[Marshaler]()416	textMarshalerType = reflect.TypeFor[encoding.TextMarshaler]()417)418 419// newTypeEncoder constructs an encoderFunc for a type.420// The returned encoder only checks CanAddr when allowAddr is true.421func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {422	// If we have a non-pointer value whose type implements423	// Marshaler with a value receiver, then we're better off taking424	// the address of the value - otherwise we end up with an425	// allocation as we cast the value to an interface.426	if t.Kind() != reflect.Pointer && allowAddr && reflect.PointerTo(t).Implements(marshalerType) {427		return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))428	}429	if t.Implements(marshalerType) {430		return marshalerEncoder431	}432	if t.Kind() != reflect.Pointer && allowAddr && reflect.PointerTo(t).Implements(textMarshalerType) {433		return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))434	}435	if t.Implements(textMarshalerType) {436		return textMarshalerEncoder437	}438 439	switch t.Kind() {440	case reflect.Bool:441		return boolEncoder442	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:443		return intEncoder444	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:445		return uintEncoder446	case reflect.Float32:447		return float32Encoder448	case reflect.Float64:449		return float64Encoder450	case reflect.String:451		return stringEncoder452	case reflect.Interface:453		return interfaceEncoder454	case reflect.Struct:455		return newStructEncoder(t)456	case reflect.Map:457		return newMapEncoder(t)458	case reflect.Slice:459		return newSliceEncoder(t)460	case reflect.Array:461		return newArrayEncoder(t)462	case reflect.Pointer:463		return newPtrEncoder(t)464	default:465		return unsupportedTypeEncoder466	}467}468 469func invalidValueEncoder(e *encodeState, v reflect.Value, _ encOpts) {470	e.WriteString("null")471}472 473func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {474	if v.Kind() == reflect.Pointer && v.IsNil() {475		e.WriteString("null")476		return477	}478	m, ok := reflect.TypeAssert[Marshaler](v)479	if !ok {480		e.WriteString("null")481		return482	}483	b, err := m.MarshalJSON()484	if err == nil {485		e.Grow(len(b))486		out := e.AvailableBuffer()487		out, err = appendCompact(out, b, opts.escapeHTML)488		e.Buffer.Write(out)489	}490	if err != nil {491		e.error(&MarshalerError{v.Type(), err, "MarshalJSON"})492	}493}494 495func addrMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {496	va := v.Addr()497	if va.IsNil() {498		e.WriteString("null")499		return500	}501	m, _ := reflect.TypeAssert[Marshaler](va)502	b, err := m.MarshalJSON()503	if err == nil {504		e.Grow(len(b))505		out := e.AvailableBuffer()506		out, err = appendCompact(out, b, opts.escapeHTML)507		e.Buffer.Write(out)508	}509	if err != nil {510		e.error(&MarshalerError{v.Type(), err, "MarshalJSON"})511	}512}513 514func textMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {515	if v.Kind() == reflect.Pointer && v.IsNil() {516		e.WriteString("null")517		return518	}519	m, ok := reflect.TypeAssert[encoding.TextMarshaler](v)520	if !ok {521		e.WriteString("null")522		return523	}524	b, err := m.MarshalText()525	if err != nil {526		e.error(&MarshalerError{v.Type(), err, "MarshalText"})527	}528	e.Write(appendString(e.AvailableBuffer(), b, opts.escapeHTML))529}530 531func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {532	va := v.Addr()533	if va.IsNil() {534		e.WriteString("null")535		return536	}537	m, _ := reflect.TypeAssert[encoding.TextMarshaler](va)538	b, err := m.MarshalText()539	if err != nil {540		e.error(&MarshalerError{v.Type(), err, "MarshalText"})541	}542	e.Write(appendString(e.AvailableBuffer(), b, opts.escapeHTML))543}544 545func boolEncoder(e *encodeState, v reflect.Value, opts encOpts) {546	b := e.AvailableBuffer()547	b = mayAppendQuote(b, opts.quoted)548	b = strconv.AppendBool(b, v.Bool())549	b = mayAppendQuote(b, opts.quoted)550	e.Write(b)551}552 553func intEncoder(e *encodeState, v reflect.Value, opts encOpts) {554	b := e.AvailableBuffer()555	b = mayAppendQuote(b, opts.quoted)556	b = strconv.AppendInt(b, v.Int(), 10)557	b = mayAppendQuote(b, opts.quoted)558	e.Write(b)559}560 561func uintEncoder(e *encodeState, v reflect.Value, opts encOpts) {562	b := e.AvailableBuffer()563	b = mayAppendQuote(b, opts.quoted)564	b = strconv.AppendUint(b, v.Uint(), 10)565	b = mayAppendQuote(b, opts.quoted)566	e.Write(b)567}568 569type floatEncoder int // number of bits570 571func (bits floatEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {572	f := v.Float()573	if math.IsInf(f, 0) || math.IsNaN(f) {574		e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))})575	}576 577	// Convert as if by ES6 number to string conversion.578	// This matches most other JSON generators.579	// See golang.org/issue/6384 and golang.org/issue/14135.580	// Like fmt %g, but the exponent cutoffs are different581	// and exponents themselves are not padded to two digits.582	b := e.AvailableBuffer()583	b = mayAppendQuote(b, opts.quoted)584	abs := math.Abs(f)585	fmt := byte('f')586	// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.587	if abs != 0 {588		if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {589			fmt = 'e'590		}591	}592	b = strconv.AppendFloat(b, f, fmt, -1, int(bits))593	if fmt == 'e' {594		// clean up e-09 to e-9595		n := len(b)596		if n >= 4 && b[n-4] == 'e' && b[n-3] == '-' && b[n-2] == '0' {597			b[n-2] = b[n-1]598			b = b[:n-1]599		}600	}601	b = mayAppendQuote(b, opts.quoted)602	e.Write(b)603}604 605var (606	float32Encoder = (floatEncoder(32)).encode607	float64Encoder = (floatEncoder(64)).encode608)609 610func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {611	if v.Type() == numberType {612		numStr := v.String()613		// In Go1.5 the empty string encodes to "0", while this is not a valid number literal614		// we keep compatibility so check validity after this.615		if numStr == "" {616			numStr = "0" // Number's zero-val617		}618		if !isValidNumber(numStr) {619			e.error(fmt.Errorf("json: invalid number literal %q", numStr))620		}621		b := e.AvailableBuffer()622		b = mayAppendQuote(b, opts.quoted)623		b = append(b, numStr...)624		b = mayAppendQuote(b, opts.quoted)625		e.Write(b)626		return627	}628	if opts.quoted {629		b := appendString(nil, v.String(), opts.escapeHTML)630		e.Write(appendString(e.AvailableBuffer(), b, false)) // no need to escape again since it is already escaped631	} else {632		e.Write(appendString(e.AvailableBuffer(), v.String(), opts.escapeHTML))633	}634}635 636func isValidNumber(s string) bool {637	// This function implements the JSON numbers grammar.638	// See https://tools.ietf.org/html/rfc7159#section-6639	// and https://www.json.org/img/number.png640 641	if s == "" {642		return false643	}644 645	// Optional -646	if s[0] == '-' {647		s = s[1:]648		if s == "" {649			return false650		}651	}652 653	// Digits654	switch {655	default:656		return false657 658	case s[0] == '0':659		s = s[1:]660 661	case '1' <= s[0] && s[0] <= '9':662		s = s[1:]663		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {664			s = s[1:]665		}666	}667 668	// . followed by 1 or more digits.669	if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {670		s = s[2:]671		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {672			s = s[1:]673		}674	}675 676	// e or E followed by an optional - or + and677	// 1 or more digits.678	if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {679		s = s[1:]680		if s[0] == '+' || s[0] == '-' {681			s = s[1:]682			if s == "" {683				return false684			}685		}686		for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {687			s = s[1:]688		}689	}690 691	// Make sure we are at the end.692	return s == ""693}694 695func interfaceEncoder(e *encodeState, v reflect.Value, opts encOpts) {696	if v.IsNil() {697		e.WriteString("null")698		return699	}700	e.reflectValue(v.Elem(), opts)701}702 703func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) {704	e.error(&UnsupportedTypeError{v.Type()})705}706 707type structEncoder struct {708	fields structFields709}710 711type structFields struct {712	list         []field713	byExactName  map[string]*field714	byFoldedName map[string]*field715}716 717func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {718	next := byte('{')719FieldLoop:720	for i := range se.fields.list {721		f := &se.fields.list[i]722 723		// Find the nested struct field by following f.index.724		fv := v725		for _, i := range f.index {726			if fv.Kind() == reflect.Pointer {727				if fv.IsNil() {728					continue FieldLoop729				}730				fv = fv.Elem()731			}732			fv = fv.Field(i)733		}734 735		if (f.omitEmpty && isEmptyValue(fv)) ||736			(f.omitZero && (f.isZero == nil && fv.IsZero() || (f.isZero != nil && f.isZero(fv)))) {737			continue738		}739		e.WriteByte(next)740		next = ','741		if opts.escapeHTML {742			e.WriteString(f.nameEscHTML)743		} else {744			e.WriteString(f.nameNonEsc)745		}746		opts.quoted = f.quoted747		f.encoder(e, fv, opts)748	}749	if next == '{' {750		e.WriteString("{}")751	} else {752		e.WriteByte('}')753	}754}755 756func newStructEncoder(t reflect.Type) encoderFunc {757	se := structEncoder{fields: cachedTypeFields(t)}758	return se.encode759}760 761type mapEncoder struct {762	elemEnc encoderFunc763}764 765func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {766	if v.IsNil() {767		e.WriteString("null")768		return769	}770	if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter {771		// We're a large number of nested ptrEncoder.encode calls deep;772		// start checking if we've run into a pointer cycle.773		ptr := v.UnsafePointer()774		if _, ok := e.ptrSeen[ptr]; ok {775			e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())})776		}777		e.ptrSeen[ptr] = struct{}{}778		defer delete(e.ptrSeen, ptr)779	}780	e.WriteByte('{')781 782	// Extract and sort the keys.783	var (784		sv  = make([]reflectWithString, v.Len())785		mi  = v.MapRange()786		err error787	)788	for i := 0; mi.Next(); i++ {789		if sv[i].ks, err = resolveKeyName(mi.Key()); err != nil {790			e.error(fmt.Errorf("json: encoding error for type %q: %q", v.Type().String(), err.Error()))791		}792		sv[i].v = mi.Value()793	}794	slices.SortFunc(sv, func(i, j reflectWithString) int {795		return strings.Compare(i.ks, j.ks)796	})797 798	for i, kv := range sv {799		if i > 0 {800			e.WriteByte(',')801		}802		e.Write(appendString(e.AvailableBuffer(), kv.ks, opts.escapeHTML))803		e.WriteByte(':')804		me.elemEnc(e, kv.v, opts)805	}806	e.WriteByte('}')807	e.ptrLevel--808}809 810func newMapEncoder(t reflect.Type) encoderFunc {811	switch t.Key().Kind() {812	case reflect.String,813		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,814		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:815	default:816		if !t.Key().Implements(textMarshalerType) {817			return unsupportedTypeEncoder818		}819	}820	me := mapEncoder{typeEncoder(t.Elem())}821	return me.encode822}823 824func encodeByteSlice(e *encodeState, v reflect.Value, _ encOpts) {825	if v.IsNil() {826		e.WriteString("null")827		return828	}829 830	s := v.Bytes()831	b := e.AvailableBuffer()832	b = append(b, '"')833	b = base64.StdEncoding.AppendEncode(b, s)834	b = append(b, '"')835	e.Write(b)836}837 838// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil.839type sliceEncoder struct {840	arrayEnc encoderFunc841}842 843func (se sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {844	if v.IsNil() {845		e.WriteString("null")846		return847	}848	if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter {849		// We're a large number of nested ptrEncoder.encode calls deep;850		// start checking if we've run into a pointer cycle.851		// Here we use a struct to memorize the pointer to the first element of the slice852		// and its length.853		ptr := struct {854			ptr any // always an unsafe.Pointer, but avoids a dependency on package unsafe855			len int856		}{v.UnsafePointer(), v.Len()}857		if _, ok := e.ptrSeen[ptr]; ok {858			e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())})859		}860		e.ptrSeen[ptr] = struct{}{}861		defer delete(e.ptrSeen, ptr)862	}863	se.arrayEnc(e, v, opts)864	e.ptrLevel--865}866 867func newSliceEncoder(t reflect.Type) encoderFunc {868	// Byte slices get special treatment; arrays don't.869	if t.Elem().Kind() == reflect.Uint8 {870		p := reflect.PointerTo(t.Elem())871		if !p.Implements(marshalerType) && !p.Implements(textMarshalerType) {872			return encodeByteSlice873		}874	}875	enc := sliceEncoder{newArrayEncoder(t)}876	return enc.encode877}878 879type arrayEncoder struct {880	elemEnc encoderFunc881}882 883func (ae arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {884	e.WriteByte('[')885	n := v.Len()886	for i := 0; i < n; i++ {887		if i > 0 {888			e.WriteByte(',')889		}890		ae.elemEnc(e, v.Index(i), opts)891	}892	e.WriteByte(']')893}894 895func newArrayEncoder(t reflect.Type) encoderFunc {896	enc := arrayEncoder{typeEncoder(t.Elem())}897	return enc.encode898}899 900type ptrEncoder struct {901	elemEnc encoderFunc902}903 904func (pe ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {905	if v.IsNil() {906		e.WriteString("null")907		return908	}909	if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter {910		// We're a large number of nested ptrEncoder.encode calls deep;911		// start checking if we've run into a pointer cycle.912		ptr := v.Interface()913		if _, ok := e.ptrSeen[ptr]; ok {914			e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())})915		}916		e.ptrSeen[ptr] = struct{}{}917		defer delete(e.ptrSeen, ptr)918	}919	pe.elemEnc(e, v.Elem(), opts)920	e.ptrLevel--921}922 923func newPtrEncoder(t reflect.Type) encoderFunc {924	enc := ptrEncoder{typeEncoder(t.Elem())}925	return enc.encode926}927 928type condAddrEncoder struct {929	canAddrEnc, elseEnc encoderFunc930}931 932func (ce condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {933	if v.CanAddr() {934		ce.canAddrEnc(e, v, opts)935	} else {936		ce.elseEnc(e, v, opts)937	}938}939 940// newCondAddrEncoder returns an encoder that checks whether its value941// CanAddr and delegates to canAddrEnc if so, else to elseEnc.942func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc {943	enc := condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc}944	return enc.encode945}946 947func isValidTag(s string) bool {948	if s == "" {949		return false950	}951	for _, c := range s {952		switch {953		case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c):954			// Backslash and quote chars are reserved, but955			// otherwise any punctuation chars are allowed956			// in a tag name.957		case !unicode.IsLetter(c) && !unicode.IsDigit(c):958			return false959		}960	}961	return true962}963 964func typeByIndex(t reflect.Type, index []int) reflect.Type {965	for _, i := range index {966		if t.Kind() == reflect.Pointer {967			t = t.Elem()968		}969		t = t.Field(i).Type970	}971	return t972}973 974type reflectWithString struct {975	v  reflect.Value976	ks string977}978 979func resolveKeyName(k reflect.Value) (string, error) {980	if k.Kind() == reflect.String {981		return k.String(), nil982	}983	if tm, ok := reflect.TypeAssert[encoding.TextMarshaler](k); ok {984		if k.Kind() == reflect.Pointer && k.IsNil() {985			return "", nil986		}987		buf, err := tm.MarshalText()988		return string(buf), err989	}990	switch k.Kind() {991	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:992		return strconv.FormatInt(k.Int(), 10), nil993	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:994		return strconv.FormatUint(k.Uint(), 10), nil995	}996	panic("unexpected map key type")997}998 999func appendString[Bytes []byte | string](dst []byte, src Bytes, escapeHTML bool) []byte {1000	dst = append(dst, '"')1001	start := 01002	for i := 0; i < len(src); {1003		if b := src[i]; b < utf8.RuneSelf {1004			if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {1005				i++1006				continue1007			}1008			dst = append(dst, src[start:i]...)1009			switch b {1010			case '\\', '"':1011				dst = append(dst, '\\', b)1012			case '\b':1013				dst = append(dst, '\\', 'b')1014			case '\f':1015				dst = append(dst, '\\', 'f')1016			case '\n':1017				dst = append(dst, '\\', 'n')1018			case '\r':1019				dst = append(dst, '\\', 'r')1020			case '\t':1021				dst = append(dst, '\\', 't')1022			default:1023				// This encodes bytes < 0x20 except for \b, \f, \n, \r and \t.1024				// If escapeHTML is set, it also escapes <, >, and &1025				// because they can lead to security holes when1026				// user-controlled strings are rendered into JSON1027				// and served to some browsers.1028				dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])1029			}1030			i++1031			start = i1032			continue1033		}1034		// TODO(https://go.dev/issue/56948): Use generic utf8 functionality.1035		// For now, cast only a small portion of byte slices to a string1036		// so that it can be stack allocated. This slows down []byte slightly1037		// due to the extra copy, but keeps string performance roughly the same.1038		n := min(len(src)-i, utf8.UTFMax)1039		c, size := utf8.DecodeRuneInString(string(src[i : i+n]))1040		if c == utf8.RuneError && size == 1 {1041			dst = append(dst, src[start:i]...)1042			dst = append(dst, `\ufffd`...)1043			i += size1044			start = i1045			continue1046		}1047		// U+2028 is LINE SEPARATOR.1048		// U+2029 is PARAGRAPH SEPARATOR.1049		// They are both technically valid characters in JSON strings,1050		// but don't work in JSONP, which has to be evaluated as JavaScript,1051		// and can lead to security holes there. It is valid JSON to1052		// escape them, so we do so unconditionally.1053		// See https://en.wikipedia.org/wiki/JSON#Safety.1054		if c == '\u2028' || c == '\u2029' {1055			dst = append(dst, src[start:i]...)1056			dst = append(dst, '\\', 'u', '2', '0', '2', hex[c&0xF])1057			i += size1058			start = i1059			continue1060		}1061		i += size1062	}1063	dst = append(dst, src[start:]...)1064	dst = append(dst, '"')1065	return dst1066}1067 1068// A field represents a single field found in a struct.1069type field struct {1070	name      string1071	nameBytes []byte // []byte(name)1072 1073	nameNonEsc  string // `"` + name + `":`1074	nameEscHTML string // `"` + HTMLEscape(name) + `":`1075 1076	tag       bool1077	index     []int1078	typ       reflect.Type1079	omitEmpty bool1080	omitZero  bool1081	isZero    func(reflect.Value) bool1082	quoted    bool1083 1084	encoder encoderFunc1085}1086 1087type isZeroer interface {1088	IsZero() bool1089}1090 1091var isZeroerType = reflect.TypeFor[isZeroer]()1092 1093func typeFields(t reflect.Type) structFields {1094	// Anonymous fields to explore at the current level and the next.1095	current := []field{}1096	next := []field{{typ: t}}1097 1098	// Count of queued names for current level and the next.1099	var count, nextCount map[reflect.Type]int1100 1101	// Types already visited at an earlier level.1102	visited := map[reflect.Type]bool{}1103 1104	// Fields found.1105	var fields []field1106 1107	// Buffer to run appendHTMLEscape on field names.1108	var nameEscBuf []byte1109 1110	for len(next) > 0 {1111		current, next = next, current[:0]1112		count, nextCount = nextCount, map[reflect.Type]int{}1113 1114		for _, f := range current {1115			if visited[f.typ] {1116				continue1117			}1118			visited[f.typ] = true1119 1120			// Scan f.typ for fields to include.1121			for i := 0; i < f.typ.NumField(); i++ {1122				sf := f.typ.Field(i)1123				if sf.Anonymous {1124					t := sf.Type1125					if t.Kind() == reflect.Pointer {1126						t = t.Elem()1127					}1128					if !sf.IsExported() && t.Kind() != reflect.Struct {1129						// Ignore embedded fields of unexported non-struct types.1130						continue1131					}1132					// Do not ignore embedded fields of unexported struct types1133					// since they may have exported fields.1134				} else if !sf.IsExported() {1135					// Ignore unexported non-embedded fields.1136					continue1137				}1138				tag := sf.Tag.Get("json")1139				if tag == "-" {1140					continue1141				}1142				name, opts := parseTag(tag)1143				if !isValidTag(name) {1144					name = ""1145				}1146				index := make([]int, len(f.index)+1)1147				copy(index, f.index)1148				index[len(f.index)] = i1149 1150				ft := sf.Type1151				if ft.Name() == "" && ft.Kind() == reflect.Pointer {1152					// Follow pointer.1153					ft = ft.Elem()1154				}1155 1156				// Only strings, floats, integers, and booleans can be quoted.1157				quoted := false1158				if opts.Contains("string") {1159					switch ft.Kind() {1160					case reflect.Bool,1161						reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,1162						reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,1163						reflect.Float32, reflect.Float64,1164						reflect.String:1165						quoted = true1166					}1167				}1168 1169				// Record found field and index sequence.1170				if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {1171					tagged := name != ""1172					if name == "" {1173						name = sf.Name1174					}1175					field := field{1176						name:      name,1177						tag:       tagged,1178						index:     index,1179						typ:       ft,1180						omitEmpty: opts.Contains("omitempty"),1181						omitZero:  opts.Contains("omitzero"),1182						quoted:    quoted,1183					}1184					field.nameBytes = []byte(field.name)1185 1186					// Build nameEscHTML and nameNonEsc ahead of time.1187					nameEscBuf = appendHTMLEscape(nameEscBuf[:0], field.nameBytes)1188					field.nameEscHTML = `"` + string(nameEscBuf) + `":`1189					field.nameNonEsc = `"` + field.name + `":`1190 1191					if field.omitZero {1192						t := sf.Type1193						// Provide a function that uses a type's IsZero method.1194						switch {1195						case t.Kind() == reflect.Interface && t.Implements(isZeroerType):1196							field.isZero = func(v reflect.Value) bool {1197								// Avoid panics calling IsZero on a nil interface or1198								// non-nil interface with nil pointer.1199								return v.IsNil() ||1200									(v.Elem().Kind() == reflect.Pointer && v.Elem().IsNil()) ||

Showing the first 1,200 of 1346 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai