// An encodeState encodes JSON into a bytes.Buffer.
type encodeState struct {
bytes.Buffer // accumulated output
- scratch [64]byte
// Keep track of what pointers we've seen in the current recursive call
// path, to avoid cycles that could lead to a stack overflow. Only do
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
- return !v.Bool()
+ return v.Bool() == false
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
}
func boolEncoder(e *encodeState, v reflect.Value, opts encOpts) {
- if opts.quoted {
- e.WriteByte('"')
- }
- if v.Bool() {
- e.WriteString("true")
- } else {
- e.WriteString("false")
- }
- if opts.quoted {
- e.WriteByte('"')
- }
+ b := e.AvailableBuffer()
+ b = mayAppendQuote(b, opts.quoted)
+ b = strconv.AppendBool(b, v.Bool())
+ b = mayAppendQuote(b, opts.quoted)
+ e.Write(b)
}
func intEncoder(e *encodeState, v reflect.Value, opts encOpts) {
- b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
- if opts.quoted {
- e.WriteByte('"')
- }
+ b := e.AvailableBuffer()
+ b = mayAppendQuote(b, opts.quoted)
+ b = strconv.AppendInt(b, v.Int(), 10)
+ b = mayAppendQuote(b, opts.quoted)
e.Write(b)
- if opts.quoted {
- e.WriteByte('"')
- }
}
func uintEncoder(e *encodeState, v reflect.Value, opts encOpts) {
- b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
- if opts.quoted {
- e.WriteByte('"')
- }
+ b := e.AvailableBuffer()
+ b = mayAppendQuote(b, opts.quoted)
+ b = strconv.AppendUint(b, v.Uint(), 10)
+ b = mayAppendQuote(b, opts.quoted)
e.Write(b)
- if opts.quoted {
- e.WriteByte('"')
- }
}
type floatEncoder int // number of bits
// See golang.org/issue/6384 and golang.org/issue/14135.
// Like fmt %g, but the exponent cutoffs are different
// and exponents themselves are not padded to two digits.
- b := e.scratch[:0]
+ b := e.AvailableBuffer()
+ b = mayAppendQuote(b, opts.quoted)
abs := math.Abs(f)
fmt := byte('f')
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
b = b[:n-1]
}
}
-
- if opts.quoted {
- e.WriteByte('"')
- }
+ b = mayAppendQuote(b, opts.quoted)
e.Write(b)
- if opts.quoted {
- e.WriteByte('"')
- }
}
var (
if !isValidNumber(numStr) {
e.error(fmt.Errorf("json: invalid number literal %q", numStr))
}
- if opts.quoted {
- e.WriteByte('"')
- }
- e.WriteString(numStr)
- if opts.quoted {
- e.WriteByte('"')
- }
+ b := e.AvailableBuffer()
+ b = mayAppendQuote(b, opts.quoted)
+ b = append(b, numStr...)
+ b = mayAppendQuote(b, opts.quoted)
+ e.Write(b)
return
}
if opts.quoted {
return
}
s := v.Bytes()
- e.WriteByte('"')
encodedLen := base64.StdEncoding.EncodedLen(len(s))
- if encodedLen <= len(e.scratch) {
- // If the encoded bytes fit in e.scratch, avoid an extra
- // allocation and use the cheaper Encoding.Encode.
- dst := e.scratch[:encodedLen]
- base64.StdEncoding.Encode(dst, s)
- e.Write(dst)
- } else if encodedLen <= 1024 {
- // The encoded bytes are short enough to allocate for, and
- // Encoding.Encode is still cheaper.
- dst := make([]byte, encodedLen)
- base64.StdEncoding.Encode(dst, s)
- e.Write(dst)
- } else {
- // The encoded bytes are too long to cheaply allocate, and
- // Encoding.Encode is no longer noticeably cheaper.
- enc := base64.NewEncoder(base64.StdEncoding, e)
- enc.Write(s)
- enc.Close()
- }
- e.WriteByte('"')
+ e.Grow(len(`"`) + encodedLen + len(`"`))
+
+ // TODO(https://go.dev/issue/53693): Use base64.Encoding.AppendEncode.
+ b := e.AvailableBuffer()
+ b = append(b, '"')
+ base64.StdEncoding.Encode(b[len(b):][:encodedLen], s)
+ b = b[:len(b)+encodedLen]
+ b = append(b, '"')
+ e.Write(b)
}
// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil.
f, _ := fieldCache.LoadOrStore(t, typeFields(t))
return f.(structFields)
}
+
+func mayAppendQuote(b []byte, quoted bool) []byte {
+ if quoted {
+ b = append(b, '"')
+ }
+ return b
+}