268 lines
5.1 KiB
Go
268 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"math/big"
|
|
"sort"
|
|
"strconv"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type encoder struct {
|
|
out io.Writer
|
|
w *bytes.Buffer
|
|
tab bool
|
|
indent int
|
|
depth int
|
|
buf [64]byte
|
|
}
|
|
|
|
func newEncoder(tab bool, indent int) *encoder {
|
|
// reuse the buffer in multiple calls of marshal
|
|
return &encoder{w: new(bytes.Buffer), tab: tab, indent: indent}
|
|
}
|
|
|
|
func (e *encoder) flush() error {
|
|
_, err := e.out.Write(e.w.Bytes())
|
|
e.w.Reset()
|
|
return err
|
|
}
|
|
|
|
func (e *encoder) marshal(v any, w io.Writer) error {
|
|
e.out = w
|
|
err := e.encode(v)
|
|
if ferr := e.flush(); ferr != nil && err == nil {
|
|
err = ferr
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (e *encoder) encode(v any) error {
|
|
switch v := v.(type) {
|
|
case nil:
|
|
e.write([]byte("null"), nullColor)
|
|
case bool:
|
|
if v {
|
|
e.write([]byte("true"), trueColor)
|
|
} else {
|
|
e.write([]byte("false"), falseColor)
|
|
}
|
|
case int:
|
|
e.write(strconv.AppendInt(e.buf[:0], int64(v), 10), numberColor)
|
|
case float64:
|
|
e.encodeFloat64(v)
|
|
case *big.Int:
|
|
e.write(v.Append(e.buf[:0], 10), numberColor)
|
|
case string:
|
|
e.encodeString(v, stringColor)
|
|
case []any:
|
|
if err := e.encodeArray(v); err != nil {
|
|
return err
|
|
}
|
|
case map[string]any:
|
|
if err := e.encodeObject(v); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v))
|
|
}
|
|
if e.w.Len() > 8*1024 {
|
|
return e.flush()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ref: floatEncoder in encoding/json
|
|
func (e *encoder) encodeFloat64(f float64) {
|
|
if math.IsNaN(f) {
|
|
e.write([]byte("null"), nullColor)
|
|
return
|
|
}
|
|
f = min(max(f, -math.MaxFloat64), math.MaxFloat64)
|
|
format := byte('f')
|
|
if x := math.Abs(f); x != 0 && x < 1e-6 || x >= 1e21 {
|
|
format = 'e'
|
|
}
|
|
buf := strconv.AppendFloat(e.buf[:0], f, format, -1, 64)
|
|
if format == 'e' {
|
|
// clean up e-09 to e-9
|
|
if n := len(buf); n >= 4 && buf[n-4] == 'e' && buf[n-3] == '-' && buf[n-2] == '0' {
|
|
buf[n-2] = buf[n-1]
|
|
buf = buf[:n-1]
|
|
}
|
|
}
|
|
e.write(buf, numberColor)
|
|
}
|
|
|
|
// ref: encodeState#string in encoding/json
|
|
func (e *encoder) encodeString(s string, color []byte) {
|
|
if color != nil {
|
|
setColor(e.w, color)
|
|
}
|
|
e.w.WriteByte('"')
|
|
start := 0
|
|
for i := 0; i < len(s); {
|
|
if b := s[i]; b < utf8.RuneSelf {
|
|
if ' ' <= b && b <= '~' && b != '"' && b != '\\' {
|
|
i++
|
|
continue
|
|
}
|
|
if start < i {
|
|
e.w.WriteString(s[start:i])
|
|
}
|
|
switch b {
|
|
case '"':
|
|
e.w.WriteString(`\"`)
|
|
case '\\':
|
|
e.w.WriteString(`\\`)
|
|
case '\b':
|
|
e.w.WriteString(`\b`)
|
|
case '\f':
|
|
e.w.WriteString(`\f`)
|
|
case '\n':
|
|
e.w.WriteString(`\n`)
|
|
case '\r':
|
|
e.w.WriteString(`\r`)
|
|
case '\t':
|
|
e.w.WriteString(`\t`)
|
|
default:
|
|
const hex = "0123456789abcdef"
|
|
e.w.WriteString(`\u00`)
|
|
e.w.WriteByte(hex[b>>4])
|
|
e.w.WriteByte(hex[b&0xF])
|
|
}
|
|
i++
|
|
start = i
|
|
continue
|
|
}
|
|
c, size := utf8.DecodeRuneInString(s[i:])
|
|
if c == utf8.RuneError && size == 1 {
|
|
if start < i {
|
|
e.w.WriteString(s[start:i])
|
|
}
|
|
e.w.WriteString(`\ufffd`)
|
|
i += size
|
|
start = i
|
|
continue
|
|
}
|
|
i += size
|
|
}
|
|
if start < len(s) {
|
|
e.w.WriteString(s[start:])
|
|
}
|
|
e.w.WriteByte('"')
|
|
if color != nil {
|
|
setColor(e.w, resetColor)
|
|
}
|
|
}
|
|
|
|
func (e *encoder) encodeArray(vs []any) error {
|
|
e.writeByte('[', arrayColor)
|
|
e.depth += e.indent
|
|
for i, v := range vs {
|
|
if i > 0 {
|
|
e.writeByte(',', arrayColor)
|
|
}
|
|
if e.indent != 0 {
|
|
e.writeIndent()
|
|
}
|
|
if err := e.encode(v); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
e.depth -= e.indent
|
|
if len(vs) > 0 && e.indent != 0 {
|
|
e.writeIndent()
|
|
}
|
|
e.writeByte(']', arrayColor)
|
|
return nil
|
|
}
|
|
|
|
func (e *encoder) encodeObject(vs map[string]any) error {
|
|
e.writeByte('{', objectColor)
|
|
e.depth += e.indent
|
|
type keyVal struct {
|
|
key string
|
|
val any
|
|
}
|
|
kvs := make([]keyVal, len(vs))
|
|
var i int
|
|
for k, v := range vs {
|
|
kvs[i] = keyVal{k, v}
|
|
i++
|
|
}
|
|
sort.Slice(kvs, func(i, j int) bool {
|
|
return kvs[i].key < kvs[j].key
|
|
})
|
|
for i, kv := range kvs {
|
|
if i > 0 {
|
|
e.writeByte(',', objectColor)
|
|
}
|
|
if e.indent != 0 {
|
|
e.writeIndent()
|
|
}
|
|
e.encodeString(kv.key, objectKeyColor)
|
|
e.writeByte(':', objectColor)
|
|
if e.indent != 0 {
|
|
e.w.WriteByte(' ')
|
|
}
|
|
if err := e.encode(kv.val); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
e.depth -= e.indent
|
|
if len(vs) > 0 && e.indent != 0 {
|
|
e.writeIndent()
|
|
}
|
|
e.writeByte('}', objectColor)
|
|
return nil
|
|
}
|
|
|
|
func (e *encoder) writeIndent() {
|
|
e.w.WriteByte('\n')
|
|
if n := e.depth; n > 0 {
|
|
if e.tab {
|
|
e.writeIndentInternal(n, "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t")
|
|
} else {
|
|
e.writeIndentInternal(n, " ")
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *encoder) writeIndentInternal(n int, spaces string) {
|
|
if l := len(spaces); n <= l {
|
|
e.w.WriteString(spaces[:n])
|
|
} else {
|
|
e.w.WriteString(spaces)
|
|
for n -= l; n > 0; n, l = n-l, l*2 {
|
|
if n < l {
|
|
l = n
|
|
}
|
|
e.w.Write(e.w.Bytes()[e.w.Len()-l:])
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *encoder) writeByte(b byte, color []byte) {
|
|
if color == nil {
|
|
e.w.WriteByte(b)
|
|
} else {
|
|
setColor(e.w, color)
|
|
e.w.WriteByte(b)
|
|
setColor(e.w, resetColor)
|
|
}
|
|
}
|
|
|
|
func (e *encoder) write(bs []byte, color []byte) {
|
|
if color == nil {
|
|
e.w.Write(bs)
|
|
} else {
|
|
setColor(e.w, color)
|
|
e.w.Write(bs)
|
|
setColor(e.w, resetColor)
|
|
}
|
|
}
|