991 lines
20 KiB
Go
991 lines
20 KiB
Go
package json
|
|
|
|
import (
|
|
"encoding"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
"unicode/utf8"
|
|
"unsafe"
|
|
|
|
"github.com/segmentio/asm/base64"
|
|
)
|
|
|
|
const hex = "0123456789abcdef"
|
|
|
|
func (e encoder) encodeNull(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
func (e encoder) encodeBool(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
if *(*bool)(p) {
|
|
return append(b, "true"...), nil
|
|
}
|
|
return append(b, "false"...), nil
|
|
}
|
|
|
|
func (e encoder) encodeInt(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendInt(b, int64(*(*int)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeInt8(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendInt(b, int64(*(*int8)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeInt16(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendInt(b, int64(*(*int16)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeInt32(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendInt(b, int64(*(*int32)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeInt64(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendInt(b, *(*int64)(p)), nil
|
|
}
|
|
|
|
func (e encoder) encodeUint(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendUint(b, uint64(*(*uint)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeUintptr(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendUint(b, uint64(*(*uintptr)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeUint8(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendUint(b, uint64(*(*uint8)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeUint16(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendUint(b, uint64(*(*uint16)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeUint32(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendUint(b, uint64(*(*uint32)(p))), nil
|
|
}
|
|
|
|
func (e encoder) encodeUint64(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return appendUint(b, *(*uint64)(p)), nil
|
|
}
|
|
|
|
func (e encoder) encodeFloat32(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return e.encodeFloat(b, float64(*(*float32)(p)), 32)
|
|
}
|
|
|
|
func (e encoder) encodeFloat64(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return e.encodeFloat(b, *(*float64)(p), 64)
|
|
}
|
|
|
|
func (e encoder) encodeFloat(b []byte, f float64, bits int) ([]byte, error) {
|
|
switch {
|
|
case math.IsNaN(f):
|
|
return b, &UnsupportedValueError{Value: reflect.ValueOf(f), Str: "NaN"}
|
|
case math.IsInf(f, 0):
|
|
return b, &UnsupportedValueError{Value: reflect.ValueOf(f), Str: "inf"}
|
|
}
|
|
|
|
// Convert as if by ES6 number to string conversion.
|
|
// This matches most other JSON generators.
|
|
// 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.
|
|
abs := math.Abs(f)
|
|
fmt := byte('f')
|
|
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
|
if abs != 0 {
|
|
if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
|
|
fmt = 'e'
|
|
}
|
|
}
|
|
|
|
b = strconv.AppendFloat(b, f, fmt, -1, int(bits))
|
|
|
|
if fmt == 'e' {
|
|
// clean up e-09 to e-9
|
|
n := len(b)
|
|
if n >= 4 && b[n-4] == 'e' && b[n-3] == '-' && b[n-2] == '0' {
|
|
b[n-2] = b[n-1]
|
|
b = b[:n-1]
|
|
}
|
|
}
|
|
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeNumber(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
n := *(*Number)(p)
|
|
if n == "" {
|
|
n = "0"
|
|
}
|
|
|
|
d := decoder{}
|
|
_, _, _, err := d.parseNumber(stringToBytes(string(n)))
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
|
|
return append(b, n...), nil
|
|
}
|
|
|
|
func (e encoder) encodeString(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
s := *(*string)(p)
|
|
if len(s) == 0 {
|
|
return append(b, `""`...), nil
|
|
}
|
|
i := 0
|
|
j := 0
|
|
escapeHTML := (e.flags & EscapeHTML) != 0
|
|
|
|
b = append(b, '"')
|
|
|
|
if len(s) >= 8 {
|
|
if j = escapeIndex(s, escapeHTML); j < 0 {
|
|
return append(append(b, s...), '"'), nil
|
|
}
|
|
}
|
|
|
|
for j < len(s) {
|
|
c := s[j]
|
|
|
|
if c >= 0x20 && c <= 0x7f && c != '\\' && c != '"' && (!escapeHTML || (c != '<' && c != '>' && c != '&')) {
|
|
// fast path: most of the time, printable ascii characters are used
|
|
j++
|
|
continue
|
|
}
|
|
|
|
switch c {
|
|
case '\\', '"':
|
|
b = append(b, s[i:j]...)
|
|
b = append(b, '\\', c)
|
|
i = j + 1
|
|
j = j + 1
|
|
continue
|
|
|
|
case '\n':
|
|
b = append(b, s[i:j]...)
|
|
b = append(b, '\\', 'n')
|
|
i = j + 1
|
|
j = j + 1
|
|
continue
|
|
|
|
case '\r':
|
|
b = append(b, s[i:j]...)
|
|
b = append(b, '\\', 'r')
|
|
i = j + 1
|
|
j = j + 1
|
|
continue
|
|
|
|
case '\t':
|
|
b = append(b, s[i:j]...)
|
|
b = append(b, '\\', 't')
|
|
i = j + 1
|
|
j = j + 1
|
|
continue
|
|
|
|
case '<', '>', '&':
|
|
b = append(b, s[i:j]...)
|
|
b = append(b, `\u00`...)
|
|
b = append(b, hex[c>>4], hex[c&0xF])
|
|
i = j + 1
|
|
j = j + 1
|
|
continue
|
|
}
|
|
|
|
// This encodes bytes < 0x20 except for \t, \n and \r.
|
|
if c < 0x20 {
|
|
b = append(b, s[i:j]...)
|
|
b = append(b, `\u00`...)
|
|
b = append(b, hex[c>>4], hex[c&0xF])
|
|
i = j + 1
|
|
j = j + 1
|
|
continue
|
|
}
|
|
|
|
r, size := utf8.DecodeRuneInString(s[j:])
|
|
|
|
if r == utf8.RuneError && size == 1 {
|
|
b = append(b, s[i:j]...)
|
|
b = append(b, `\ufffd`...)
|
|
i = j + size
|
|
j = j + size
|
|
continue
|
|
}
|
|
|
|
switch r {
|
|
case '\u2028', '\u2029':
|
|
// U+2028 is LINE SEPARATOR.
|
|
// U+2029 is PARAGRAPH SEPARATOR.
|
|
// They are both technically valid characters in JSON strings,
|
|
// but don't work in JSONP, which has to be evaluated as JavaScript,
|
|
// and can lead to security holes there. It is valid JSON to
|
|
// escape them, so we do so unconditionally.
|
|
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
|
|
b = append(b, s[i:j]...)
|
|
b = append(b, `\u202`...)
|
|
b = append(b, hex[r&0xF])
|
|
i = j + size
|
|
j = j + size
|
|
continue
|
|
}
|
|
|
|
j += size
|
|
}
|
|
|
|
b = append(b, s[i:]...)
|
|
b = append(b, '"')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeToString(b []byte, p unsafe.Pointer, encode encodeFunc) ([]byte, error) {
|
|
i := len(b)
|
|
|
|
b, err := encode(e, b, p)
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
|
|
j := len(b)
|
|
s := b[i:]
|
|
|
|
if b, err = e.encodeString(b, unsafe.Pointer(&s)); err != nil {
|
|
return b, err
|
|
}
|
|
|
|
n := copy(b[i:], b[j:])
|
|
return b[:i+n], nil
|
|
}
|
|
|
|
func (e encoder) encodeBytes(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
v := *(*[]byte)(p)
|
|
if v == nil {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
n := base64.StdEncoding.EncodedLen(len(v)) + 2
|
|
|
|
if avail := cap(b) - len(b); avail < n {
|
|
newB := make([]byte, cap(b)+(n-avail))
|
|
copy(newB, b)
|
|
b = newB[:len(b)]
|
|
}
|
|
|
|
i := len(b)
|
|
j := len(b) + n
|
|
|
|
b = b[:j]
|
|
b[i] = '"'
|
|
base64.StdEncoding.Encode(b[i+1:j-1], v)
|
|
b[j-1] = '"'
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeDuration(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
b = append(b, '"')
|
|
b = appendDuration(b, *(*time.Duration)(p))
|
|
b = append(b, '"')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeTime(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
t := *(*time.Time)(p)
|
|
b = append(b, '"')
|
|
b = t.AppendFormat(b, time.RFC3339Nano)
|
|
b = append(b, '"')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeArray(b []byte, p unsafe.Pointer, n int, size uintptr, t reflect.Type, encode encodeFunc) ([]byte, error) {
|
|
var start = len(b)
|
|
var err error
|
|
b = append(b, '[')
|
|
|
|
for i := 0; i < n; i++ {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
if b, err = encode(e, b, unsafe.Pointer(uintptr(p)+(uintptr(i)*size))); err != nil {
|
|
return b[:start], err
|
|
}
|
|
}
|
|
|
|
b = append(b, ']')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeSlice(b []byte, p unsafe.Pointer, size uintptr, t reflect.Type, encode encodeFunc) ([]byte, error) {
|
|
s := (*slice)(p)
|
|
|
|
if s.data == nil && s.len == 0 && s.cap == 0 {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
return e.encodeArray(b, s.data, s.len, size, t, encode)
|
|
}
|
|
|
|
func (e encoder) encodeMap(b []byte, p unsafe.Pointer, t reflect.Type, encodeKey, encodeValue encodeFunc, sortKeys sortFunc) ([]byte, error) {
|
|
m := reflect.NewAt(t, p).Elem()
|
|
if m.IsNil() {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
keys := m.MapKeys()
|
|
if sortKeys != nil && (e.flags&SortMapKeys) != 0 {
|
|
sortKeys(keys)
|
|
}
|
|
|
|
var start = len(b)
|
|
var err error
|
|
b = append(b, '{')
|
|
|
|
for i, k := range keys {
|
|
v := m.MapIndex(k)
|
|
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
if b, err = encodeKey(e, b, (*iface)(unsafe.Pointer(&k)).ptr); err != nil {
|
|
return b[:start], err
|
|
}
|
|
|
|
b = append(b, ':')
|
|
|
|
if b, err = encodeValue(e, b, (*iface)(unsafe.Pointer(&v)).ptr); err != nil {
|
|
return b[:start], err
|
|
}
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
type element struct {
|
|
key string
|
|
val interface{}
|
|
raw RawMessage
|
|
}
|
|
|
|
type mapslice struct {
|
|
elements []element
|
|
}
|
|
|
|
func (m *mapslice) Len() int { return len(m.elements) }
|
|
func (m *mapslice) Less(i, j int) bool { return m.elements[i].key < m.elements[j].key }
|
|
func (m *mapslice) Swap(i, j int) { m.elements[i], m.elements[j] = m.elements[j], m.elements[i] }
|
|
|
|
var mapslicePool = sync.Pool{
|
|
New: func() interface{} { return new(mapslice) },
|
|
}
|
|
|
|
func (e encoder) encodeMapStringInterface(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
m := *(*map[string]interface{})(p)
|
|
if m == nil {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
if (e.flags & SortMapKeys) == 0 {
|
|
// Optimized code path when the program does not need the map keys to be
|
|
// sorted.
|
|
b = append(b, '{')
|
|
|
|
if len(m) != 0 {
|
|
var err error
|
|
var i = 0
|
|
|
|
for k, v := range m {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&k))
|
|
b = append(b, ':')
|
|
|
|
b, err = Append(b, v, e.flags)
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
|
|
i++
|
|
}
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
s := mapslicePool.Get().(*mapslice)
|
|
if cap(s.elements) < len(m) {
|
|
s.elements = make([]element, 0, align(10, uintptr(len(m))))
|
|
}
|
|
for key, val := range m {
|
|
s.elements = append(s.elements, element{key: key, val: val})
|
|
}
|
|
sort.Sort(s)
|
|
|
|
var start = len(b)
|
|
var err error
|
|
b = append(b, '{')
|
|
|
|
for i, elem := range s.elements {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&elem.key))
|
|
b = append(b, ':')
|
|
|
|
b, err = Append(b, elem.val, e.flags)
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
for i := range s.elements {
|
|
s.elements[i] = element{}
|
|
}
|
|
|
|
s.elements = s.elements[:0]
|
|
mapslicePool.Put(s)
|
|
|
|
if err != nil {
|
|
return b[:start], err
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeMapStringRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
m := *(*map[string]RawMessage)(p)
|
|
if m == nil {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
if (e.flags & SortMapKeys) == 0 {
|
|
// Optimized code path when the program does not need the map keys to be
|
|
// sorted.
|
|
b = append(b, '{')
|
|
|
|
if len(m) != 0 {
|
|
var err error
|
|
var i = 0
|
|
|
|
for k, v := range m {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
// encodeString doesn't return errors so we ignore it here
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&k))
|
|
b = append(b, ':')
|
|
|
|
b, err = e.encodeRawMessage(b, unsafe.Pointer(&v))
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
i++
|
|
}
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
s := mapslicePool.Get().(*mapslice)
|
|
if cap(s.elements) < len(m) {
|
|
s.elements = make([]element, 0, align(10, uintptr(len(m))))
|
|
}
|
|
for key, raw := range m {
|
|
s.elements = append(s.elements, element{key: key, raw: raw})
|
|
}
|
|
sort.Sort(s)
|
|
|
|
var start = len(b)
|
|
var err error
|
|
b = append(b, '{')
|
|
|
|
for i, elem := range s.elements {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&elem.key))
|
|
b = append(b, ':')
|
|
|
|
b, err = e.encodeRawMessage(b, unsafe.Pointer(&elem.raw))
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
for i := range s.elements {
|
|
s.elements[i] = element{}
|
|
}
|
|
|
|
s.elements = s.elements[:0]
|
|
mapslicePool.Put(s)
|
|
|
|
if err != nil {
|
|
return b[:start], err
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeMapStringString(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
m := *(*map[string]string)(p)
|
|
if m == nil {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
if (e.flags & SortMapKeys) == 0 {
|
|
// Optimized code path when the program does not need the map keys to be
|
|
// sorted.
|
|
b = append(b, '{')
|
|
|
|
if len(m) != 0 {
|
|
var i = 0
|
|
|
|
for k, v := range m {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
// encodeString never returns an error so we ignore it here
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&k))
|
|
b = append(b, ':')
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&v))
|
|
|
|
i++
|
|
}
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
s := mapslicePool.Get().(*mapslice)
|
|
if cap(s.elements) < len(m) {
|
|
s.elements = make([]element, 0, align(10, uintptr(len(m))))
|
|
}
|
|
for key, val := range m {
|
|
v := val
|
|
s.elements = append(s.elements, element{key: key, val: &v})
|
|
}
|
|
sort.Sort(s)
|
|
|
|
b = append(b, '{')
|
|
|
|
for i, elem := range s.elements {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
// encodeString never returns an error so we ignore it here
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&elem.key))
|
|
b = append(b, ':')
|
|
b, _ = e.encodeString(b, unsafe.Pointer(elem.val.(*string)))
|
|
}
|
|
|
|
for i := range s.elements {
|
|
s.elements[i] = element{}
|
|
}
|
|
|
|
s.elements = s.elements[:0]
|
|
mapslicePool.Put(s)
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeMapStringStringSlice(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
m := *(*map[string][]string)(p)
|
|
if m == nil {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
var stringSize = unsafe.Sizeof("")
|
|
|
|
if (e.flags & SortMapKeys) == 0 {
|
|
// Optimized code path when the program does not need the map keys to be
|
|
// sorted.
|
|
b = append(b, '{')
|
|
|
|
if len(m) != 0 {
|
|
var err error
|
|
var i = 0
|
|
|
|
for k, v := range m {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&k))
|
|
b = append(b, ':')
|
|
|
|
b, err = e.encodeSlice(b, unsafe.Pointer(&v), stringSize, sliceStringType, encoder.encodeString)
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
|
|
i++
|
|
}
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
s := mapslicePool.Get().(*mapslice)
|
|
if cap(s.elements) < len(m) {
|
|
s.elements = make([]element, 0, align(10, uintptr(len(m))))
|
|
}
|
|
for key, val := range m {
|
|
v := val
|
|
s.elements = append(s.elements, element{key: key, val: &v})
|
|
}
|
|
sort.Sort(s)
|
|
|
|
var start = len(b)
|
|
var err error
|
|
b = append(b, '{')
|
|
|
|
for i, elem := range s.elements {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&elem.key))
|
|
b = append(b, ':')
|
|
|
|
b, err = e.encodeSlice(b, unsafe.Pointer(elem.val.(*[]string)), stringSize, sliceStringType, encoder.encodeString)
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
for i := range s.elements {
|
|
s.elements[i] = element{}
|
|
}
|
|
|
|
s.elements = s.elements[:0]
|
|
mapslicePool.Put(s)
|
|
|
|
if err != nil {
|
|
return b[:start], err
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeMapStringBool(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
m := *(*map[string]bool)(p)
|
|
if m == nil {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
if (e.flags & SortMapKeys) == 0 {
|
|
// Optimized code path when the program does not need the map keys to be
|
|
// sorted.
|
|
b = append(b, '{')
|
|
|
|
if len(m) != 0 {
|
|
var i = 0
|
|
|
|
for k, v := range m {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
// encodeString never returns an error so we ignore it here
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&k))
|
|
if v {
|
|
b = append(b, ":true"...)
|
|
} else {
|
|
b = append(b, ":false"...)
|
|
}
|
|
|
|
i++
|
|
}
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
s := mapslicePool.Get().(*mapslice)
|
|
if cap(s.elements) < len(m) {
|
|
s.elements = make([]element, 0, align(10, uintptr(len(m))))
|
|
}
|
|
for key, val := range m {
|
|
s.elements = append(s.elements, element{key: key, val: val})
|
|
}
|
|
sort.Sort(s)
|
|
|
|
b = append(b, '{')
|
|
|
|
for i, elem := range s.elements {
|
|
if i != 0 {
|
|
b = append(b, ',')
|
|
}
|
|
|
|
// encodeString never returns an error so we ignore it here
|
|
b, _ = e.encodeString(b, unsafe.Pointer(&elem.key))
|
|
if elem.val.(bool) {
|
|
b = append(b, ":true"...)
|
|
} else {
|
|
b = append(b, ":false"...)
|
|
}
|
|
}
|
|
|
|
for i := range s.elements {
|
|
s.elements[i] = element{}
|
|
}
|
|
|
|
s.elements = s.elements[:0]
|
|
mapslicePool.Put(s)
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
func (e encoder) encodeStruct(b []byte, p unsafe.Pointer, st *structType) ([]byte, error) {
|
|
var start = len(b)
|
|
var err error
|
|
var k string
|
|
var n int
|
|
b = append(b, '{')
|
|
|
|
escapeHTML := (e.flags & EscapeHTML) != 0
|
|
|
|
for i := range st.fields {
|
|
f := &st.fields[i]
|
|
v := unsafe.Pointer(uintptr(p) + f.offset)
|
|
|
|
if f.omitempty && f.empty(v) {
|
|
continue
|
|
}
|
|
|
|
if escapeHTML {
|
|
k = f.html
|
|
} else {
|
|
k = f.json
|
|
}
|
|
|
|
lengthBeforeKey := len(b)
|
|
|
|
if n != 0 {
|
|
b = append(b, k...)
|
|
} else {
|
|
b = append(b, k[1:]...)
|
|
}
|
|
|
|
if b, err = f.codec.encode(e, b, v); err != nil {
|
|
if err == (rollback{}) {
|
|
b = b[:lengthBeforeKey]
|
|
continue
|
|
}
|
|
return b[:start], err
|
|
}
|
|
|
|
n++
|
|
}
|
|
|
|
b = append(b, '}')
|
|
return b, nil
|
|
}
|
|
|
|
type rollback struct{}
|
|
|
|
func (rollback) Error() string { return "rollback" }
|
|
|
|
func (e encoder) encodeEmbeddedStructPointer(b []byte, p unsafe.Pointer, t reflect.Type, unexported bool, offset uintptr, encode encodeFunc) ([]byte, error) {
|
|
p = *(*unsafe.Pointer)(p)
|
|
if p == nil {
|
|
return b, rollback{}
|
|
}
|
|
return encode(e, b, unsafe.Pointer(uintptr(p)+offset))
|
|
}
|
|
|
|
func (e encoder) encodePointer(b []byte, p unsafe.Pointer, t reflect.Type, encode encodeFunc) ([]byte, error) {
|
|
if p = *(*unsafe.Pointer)(p); p != nil {
|
|
if e.ptrDepth++; e.ptrDepth >= startDetectingCyclesAfter {
|
|
if _, seen := e.ptrSeen[p]; seen {
|
|
// TODO: reconstruct the reflect.Value from p + t so we can set
|
|
// the erorr's Value field?
|
|
return b, &UnsupportedValueError{Str: fmt.Sprintf("encountered a cycle via %s", t)}
|
|
}
|
|
if e.ptrSeen == nil {
|
|
e.ptrSeen = make(map[unsafe.Pointer]struct{})
|
|
}
|
|
e.ptrSeen[p] = struct{}{}
|
|
defer delete(e.ptrSeen, p)
|
|
}
|
|
return encode(e, b, p)
|
|
}
|
|
return e.encodeNull(b, nil)
|
|
}
|
|
|
|
func (e encoder) encodeInterface(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
return Append(b, *(*interface{})(p), e.flags)
|
|
}
|
|
|
|
func (e encoder) encodeMaybeEmptyInterface(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {
|
|
return Append(b, reflect.NewAt(t, p).Elem().Interface(), e.flags)
|
|
}
|
|
|
|
func (e encoder) encodeUnsupportedTypeError(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {
|
|
return b, &UnsupportedTypeError{Type: t}
|
|
}
|
|
|
|
func (e encoder) encodeRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
|
|
v := *(*RawMessage)(p)
|
|
|
|
if v == nil {
|
|
return append(b, "null"...), nil
|
|
}
|
|
|
|
var s []byte
|
|
|
|
if (e.flags & TrustRawMessage) != 0 {
|
|
s = v
|
|
} else {
|
|
var err error
|
|
d := decoder{}
|
|
s, _, _, err = d.parseValue(v)
|
|
if err != nil {
|
|
return b, &UnsupportedValueError{Value: reflect.ValueOf(v), Str: err.Error()}
|
|
}
|
|
}
|
|
|
|
if (e.flags & EscapeHTML) != 0 {
|
|
return appendCompactEscapeHTML(b, s), nil
|
|
}
|
|
|
|
return append(b, s...), nil
|
|
}
|
|
|
|
func (e encoder) encodeJSONMarshaler(b []byte, p unsafe.Pointer, t reflect.Type, pointer bool) ([]byte, error) {
|
|
v := reflect.NewAt(t, p)
|
|
|
|
if !pointer {
|
|
v = v.Elem()
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Ptr, reflect.Interface:
|
|
if v.IsNil() {
|
|
return append(b, "null"...), nil
|
|
}
|
|
}
|
|
|
|
j, err := v.Interface().(Marshaler).MarshalJSON()
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
|
|
d := decoder{}
|
|
s, _, _, err := d.parseValue(j)
|
|
if err != nil {
|
|
return b, &MarshalerError{Type: t, Err: err}
|
|
}
|
|
|
|
if (e.flags & EscapeHTML) != 0 {
|
|
return appendCompactEscapeHTML(b, s), nil
|
|
}
|
|
|
|
return append(b, s...), nil
|
|
}
|
|
|
|
func (e encoder) encodeTextMarshaler(b []byte, p unsafe.Pointer, t reflect.Type, pointer bool) ([]byte, error) {
|
|
v := reflect.NewAt(t, p)
|
|
|
|
if !pointer {
|
|
v = v.Elem()
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Ptr, reflect.Interface:
|
|
if v.IsNil() {
|
|
return append(b, `null`...), nil
|
|
}
|
|
}
|
|
|
|
s, err := v.Interface().(encoding.TextMarshaler).MarshalText()
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
|
|
return e.encodeString(b, unsafe.Pointer(&s))
|
|
}
|
|
|
|
func appendCompactEscapeHTML(dst []byte, src []byte) []byte {
|
|
start := 0
|
|
escape := false
|
|
inString := false
|
|
|
|
for i, c := range src {
|
|
if !inString {
|
|
switch c {
|
|
case '"': // enter string
|
|
inString = true
|
|
case ' ', '\n', '\r', '\t': // skip space
|
|
if start < i {
|
|
dst = append(dst, src[start:i]...)
|
|
}
|
|
start = i + 1
|
|
}
|
|
continue
|
|
}
|
|
|
|
if escape {
|
|
escape = false
|
|
continue
|
|
}
|
|
|
|
if c == '\\' {
|
|
escape = true
|
|
continue
|
|
}
|
|
|
|
if c == '"' {
|
|
inString = false
|
|
continue
|
|
}
|
|
|
|
if c == '<' || c == '>' || c == '&' {
|
|
if start < i {
|
|
dst = append(dst, src[start:i]...)
|
|
}
|
|
dst = append(dst, `\u00`...)
|
|
dst = append(dst, hex[c>>4], hex[c&0xF])
|
|
start = i + 1
|
|
continue
|
|
}
|
|
|
|
// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
|
|
if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
|
|
if start < i {
|
|
dst = append(dst, src[start:i]...)
|
|
}
|
|
dst = append(dst, `\u202`...)
|
|
dst = append(dst, hex[src[i+2]&0xF])
|
|
start = i + 3
|
|
continue
|
|
}
|
|
}
|
|
|
|
if start < len(src) {
|
|
dst = append(dst, src[start:]...)
|
|
}
|
|
|
|
return dst
|
|
}
|