You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
424 lines
11 KiB
424 lines
11 KiB
package core |
|
|
|
import ( |
|
"encoding/base64" |
|
"encoding/json" |
|
"math" |
|
"sync" |
|
"time" |
|
"unicode/utf8" |
|
) |
|
|
|
// For JSON-escaping; see jsonEncoder.safeAddString below. |
|
const _hex = "0123456789abcdef" |
|
|
|
var _ ObjectEncoder = &jsonEncoder{} |
|
var _jsonPool = sync.Pool{New: func() interface{} { |
|
return &jsonEncoder{} |
|
}} |
|
|
|
func getJSONEncoder() *jsonEncoder { |
|
return _jsonPool.Get().(*jsonEncoder) |
|
} |
|
|
|
func putJSONEncoder(enc *jsonEncoder) { |
|
if enc.reflectBuf != nil { |
|
enc.reflectBuf.Free() |
|
} |
|
enc.EncoderConfig = nil |
|
enc.buf = nil |
|
enc.spaced = false |
|
enc.openNamespaces = 0 |
|
enc.reflectBuf = nil |
|
enc.reflectEnc = nil |
|
_jsonPool.Put(enc) |
|
} |
|
|
|
type jsonEncoder struct { |
|
*EncoderConfig |
|
buf *Buffer |
|
spaced bool // include spaces after colons and commas |
|
openNamespaces int |
|
|
|
// for encoding generic values by reflection |
|
reflectBuf *Buffer |
|
reflectEnc *json.Encoder |
|
} |
|
|
|
// NewJSONEncoder creates a fast, low-allocation JSON encoder. The encoder |
|
// appropriately escapes all field keys and values. |
|
// |
|
// Note that the encoder doesn't deduplicate keys, so it's possible to produce |
|
// a message like |
|
// {"foo":"bar","foo":"baz"} |
|
// This is permitted by the JSON specification, but not encouraged. Many |
|
// libraries will ignore duplicate key-value pairs (typically keeping the last |
|
// pair) when unmarshaling, but users should attempt to avoid adding duplicate |
|
// keys. |
|
func NewJSONEncoder(cfg EncoderConfig, buf *Buffer) Encoder { |
|
return newJSONEncoder(cfg, false, buf) |
|
} |
|
|
|
func newJSONEncoder(cfg EncoderConfig, spaced bool, buf *Buffer) *jsonEncoder { |
|
return &jsonEncoder{ |
|
EncoderConfig: &cfg, |
|
buf: buf, |
|
spaced: spaced, |
|
} |
|
} |
|
|
|
func (enc *jsonEncoder) AddArray(key string, arr ArrayMarshaler) error { |
|
enc.addKey(key) |
|
return enc.AppendArray(arr) |
|
} |
|
|
|
func (enc *jsonEncoder) AddObject(key string, obj ObjectMarshaler) error { |
|
enc.addKey(key) |
|
return enc.AppendObject(obj) |
|
} |
|
|
|
func (enc *jsonEncoder) AddBinary(key string, val []byte) { |
|
enc.AddString(key, base64.StdEncoding.EncodeToString(val)) |
|
} |
|
|
|
func (enc *jsonEncoder) AddByteString(key string, val []byte) { |
|
enc.addKey(key) |
|
enc.AppendByteString(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AddBool(key string, val bool) { |
|
enc.addKey(key) |
|
enc.AppendBool(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AddComplex128(key string, val complex128) { |
|
enc.addKey(key) |
|
enc.AppendComplex128(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AddDuration(key string, val time.Duration) { |
|
enc.addKey(key) |
|
enc.AppendDuration(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AddFloat64(key string, val float64) { |
|
enc.addKey(key) |
|
enc.AppendFloat64(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AddInt64(key string, val int64) { |
|
enc.addKey(key) |
|
enc.AppendInt64(val) |
|
} |
|
|
|
func (enc *jsonEncoder) resetReflectBuf() { |
|
if enc.reflectBuf == nil { |
|
enc.reflectBuf = GetPool() |
|
enc.reflectEnc = json.NewEncoder(enc.reflectBuf) |
|
} else { |
|
enc.reflectBuf.Reset() |
|
} |
|
} |
|
|
|
func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error { |
|
enc.resetReflectBuf() |
|
err := enc.reflectEnc.Encode(obj) |
|
if err != nil { |
|
return err |
|
} |
|
enc.reflectBuf.TrimNewline() |
|
enc.addKey(key) |
|
_, err = enc.buf.Write(enc.reflectBuf.Bytes()) |
|
return err |
|
} |
|
|
|
func (enc *jsonEncoder) OpenNamespace(key string) { |
|
enc.addKey(key) |
|
enc.buf.AppendByte('{') |
|
enc.openNamespaces++ |
|
} |
|
|
|
func (enc *jsonEncoder) AddString(key, val string) { |
|
enc.addKey(key) |
|
enc.AppendString(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AddTime(key string, val time.Time) { |
|
enc.addKey(key) |
|
enc.AppendTime(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AddUint64(key string, val uint64) { |
|
enc.addKey(key) |
|
enc.AppendUint64(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AppendArray(arr ArrayMarshaler) error { |
|
enc.addElementSeparator() |
|
enc.buf.AppendByte('[') |
|
err := arr.MarshalLogArray(enc) |
|
enc.buf.AppendByte(']') |
|
return err |
|
} |
|
|
|
func (enc *jsonEncoder) AppendObject(obj ObjectMarshaler) error { |
|
enc.addElementSeparator() |
|
enc.buf.AppendByte('{') |
|
err := obj.MarshalLogObject(enc) |
|
enc.buf.AppendByte('}') |
|
return err |
|
} |
|
|
|
func (enc *jsonEncoder) AppendBool(val bool) { |
|
enc.addElementSeparator() |
|
enc.buf.AppendBool(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AppendByteString(val []byte) { |
|
enc.addElementSeparator() |
|
enc.buf.AppendByte('"') |
|
enc.safeAddByteString(val) |
|
enc.buf.AppendByte('"') |
|
} |
|
|
|
func (enc *jsonEncoder) AppendComplex128(val complex128) { |
|
enc.addElementSeparator() |
|
// Cast to a platform-independent, fixed-size type. |
|
r, i := float64(real(val)), float64(imag(val)) |
|
enc.buf.AppendByte('"') |
|
// Because we're always in a quoted string, we can use strconv without |
|
// special-casing NaN and +/-Inf. |
|
enc.buf.AppendFloat(r, 64) |
|
enc.buf.AppendByte('+') |
|
enc.buf.AppendFloat(i, 64) |
|
enc.buf.AppendByte('i') |
|
enc.buf.AppendByte('"') |
|
} |
|
|
|
func (enc *jsonEncoder) AppendDuration(val time.Duration) { |
|
cur := enc.buf.Len() |
|
enc.EncodeDuration(val, enc) |
|
if cur == enc.buf.Len() { |
|
// User-supplied EncodeDuration is a no-op. Fall back to nanoseconds to keep |
|
// JSON valid. |
|
enc.AppendInt64(int64(val)) |
|
} |
|
} |
|
|
|
func (enc *jsonEncoder) AppendInt64(val int64) { |
|
enc.addElementSeparator() |
|
enc.buf.AppendInt(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AppendReflected(val interface{}) error { |
|
enc.resetReflectBuf() |
|
err := enc.reflectEnc.Encode(val) |
|
if err != nil { |
|
return err |
|
} |
|
enc.reflectBuf.TrimNewline() |
|
enc.addElementSeparator() |
|
_, err = enc.buf.Write(enc.reflectBuf.Bytes()) |
|
return err |
|
} |
|
|
|
func (enc *jsonEncoder) AppendString(val string) { |
|
enc.addElementSeparator() |
|
enc.buf.AppendByte('"') |
|
enc.safeAddString(val) |
|
enc.buf.AppendByte('"') |
|
} |
|
|
|
func (enc *jsonEncoder) AppendTime(val time.Time) { |
|
cur := enc.buf.Len() |
|
enc.EncodeTime(val, enc) |
|
if cur == enc.buf.Len() { |
|
// User-supplied EncodeTime is a no-op. Fall back to nanos since epoch to keep |
|
// output JSON valid. |
|
enc.AppendInt64(val.UnixNano()) |
|
} |
|
} |
|
|
|
func (enc *jsonEncoder) AppendUint64(val uint64) { |
|
enc.addElementSeparator() |
|
enc.buf.AppendUint(val) |
|
} |
|
|
|
func (enc *jsonEncoder) AddComplex64(k string, v complex64) { enc.AddComplex128(k, complex128(v)) } |
|
func (enc *jsonEncoder) AddFloat32(k string, v float32) { enc.AddFloat64(k, float64(v)) } |
|
func (enc *jsonEncoder) AddInt(k string, v int) { enc.AddInt64(k, int64(v)) } |
|
func (enc *jsonEncoder) AddInt32(k string, v int32) { enc.AddInt64(k, int64(v)) } |
|
func (enc *jsonEncoder) AddInt16(k string, v int16) { enc.AddInt64(k, int64(v)) } |
|
func (enc *jsonEncoder) AddInt8(k string, v int8) { enc.AddInt64(k, int64(v)) } |
|
func (enc *jsonEncoder) AddUint(k string, v uint) { enc.AddUint64(k, uint64(v)) } |
|
func (enc *jsonEncoder) AddUint32(k string, v uint32) { enc.AddUint64(k, uint64(v)) } |
|
func (enc *jsonEncoder) AddUint16(k string, v uint16) { enc.AddUint64(k, uint64(v)) } |
|
func (enc *jsonEncoder) AddUint8(k string, v uint8) { enc.AddUint64(k, uint64(v)) } |
|
func (enc *jsonEncoder) AddUintptr(k string, v uintptr) { enc.AddUint64(k, uint64(v)) } |
|
func (enc *jsonEncoder) AppendComplex64(v complex64) { enc.AppendComplex128(complex128(v)) } |
|
func (enc *jsonEncoder) AppendFloat64(v float64) { enc.appendFloat(v, 64) } |
|
func (enc *jsonEncoder) AppendFloat32(v float32) { enc.appendFloat(float64(v), 32) } |
|
func (enc *jsonEncoder) AppendInt(v int) { enc.AppendInt64(int64(v)) } |
|
func (enc *jsonEncoder) AppendInt32(v int32) { enc.AppendInt64(int64(v)) } |
|
func (enc *jsonEncoder) AppendInt16(v int16) { enc.AppendInt64(int64(v)) } |
|
func (enc *jsonEncoder) AppendInt8(v int8) { enc.AppendInt64(int64(v)) } |
|
func (enc *jsonEncoder) AppendUint(v uint) { enc.AppendUint64(uint64(v)) } |
|
func (enc *jsonEncoder) AppendUint32(v uint32) { enc.AppendUint64(uint64(v)) } |
|
func (enc *jsonEncoder) AppendUint16(v uint16) { enc.AppendUint64(uint64(v)) } |
|
func (enc *jsonEncoder) AppendUint8(v uint8) { enc.AppendUint64(uint64(v)) } |
|
func (enc *jsonEncoder) AppendUintptr(v uintptr) { enc.AppendUint64(uint64(v)) } |
|
|
|
func (enc *jsonEncoder) Clone() Encoder { |
|
clone := enc.clone() |
|
return clone |
|
} |
|
|
|
func (enc *jsonEncoder) clone() *jsonEncoder { |
|
clone := getJSONEncoder() |
|
clone.EncoderConfig = enc.EncoderConfig |
|
clone.spaced = enc.spaced |
|
clone.openNamespaces = enc.openNamespaces |
|
clone.buf = GetPool() |
|
return clone |
|
} |
|
|
|
func (enc *jsonEncoder) Encode(buf *Buffer, fields ...Field) error { |
|
final := enc.clone() |
|
final.buf = buf |
|
final.buf.AppendByte('{') |
|
if enc.buf.Len() > 0 { |
|
final.addElementSeparator() |
|
final.buf.Write(enc.buf.Bytes()) |
|
} |
|
|
|
for i := range fields { |
|
fields[i].AddTo(final) |
|
} |
|
|
|
final.closeOpenNamespaces() |
|
final.buf.AppendString("}\n") |
|
putJSONEncoder(final) |
|
return nil |
|
} |
|
|
|
func (enc *jsonEncoder) closeOpenNamespaces() { |
|
for i := 0; i < enc.openNamespaces; i++ { |
|
enc.buf.AppendByte('}') |
|
} |
|
} |
|
|
|
func (enc *jsonEncoder) addKey(key string) { |
|
enc.addElementSeparator() |
|
enc.buf.AppendByte('"') |
|
enc.safeAddString(key) |
|
enc.buf.AppendByte('"') |
|
enc.buf.AppendByte(':') |
|
if enc.spaced { |
|
enc.buf.AppendByte(' ') |
|
} |
|
} |
|
|
|
func (enc *jsonEncoder) addElementSeparator() { |
|
last := enc.buf.Len() - 1 |
|
if last < 0 { |
|
return |
|
} |
|
switch enc.buf.Bytes()[last] { |
|
case '{', '[', ':', ',', ' ': |
|
return |
|
default: |
|
enc.buf.AppendByte(',') |
|
if enc.spaced { |
|
enc.buf.AppendByte(' ') |
|
} |
|
} |
|
} |
|
|
|
func (enc *jsonEncoder) appendFloat(val float64, bitSize int) { |
|
enc.addElementSeparator() |
|
switch { |
|
case math.IsNaN(val): |
|
enc.buf.AppendString(`"NaN"`) |
|
case math.IsInf(val, 1): |
|
enc.buf.AppendString(`"+Inf"`) |
|
case math.IsInf(val, -1): |
|
enc.buf.AppendString(`"-Inf"`) |
|
default: |
|
enc.buf.AppendFloat(val, bitSize) |
|
} |
|
} |
|
|
|
// safeAddString JSON-escapes a string and appends it to the internal buffer. |
|
// Unlike the standard library's encoder, it doesn't attempt to protect the |
|
// user from browser vulnerabilities or JSONP-related problems. |
|
func (enc *jsonEncoder) safeAddString(s string) { |
|
for i := 0; i < len(s); { |
|
if enc.tryAddRuneSelf(s[i]) { |
|
i++ |
|
continue |
|
} |
|
r, size := utf8.DecodeRuneInString(s[i:]) |
|
if enc.tryAddRuneError(r, size) { |
|
i++ |
|
continue |
|
} |
|
enc.buf.AppendString(s[i : i+size]) |
|
i += size |
|
} |
|
} |
|
|
|
// safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte. |
|
func (enc *jsonEncoder) safeAddByteString(s []byte) { |
|
for i := 0; i < len(s); { |
|
if enc.tryAddRuneSelf(s[i]) { |
|
i++ |
|
continue |
|
} |
|
r, size := utf8.DecodeRune(s[i:]) |
|
if enc.tryAddRuneError(r, size) { |
|
i++ |
|
continue |
|
} |
|
enc.buf.Write(s[i : i+size]) |
|
i += size |
|
} |
|
} |
|
|
|
// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte. |
|
func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool { |
|
if b >= utf8.RuneSelf { |
|
return false |
|
} |
|
if 0x20 <= b && b != '\\' && b != '"' { |
|
enc.buf.AppendByte(b) |
|
return true |
|
} |
|
switch b { |
|
case '\\', '"': |
|
enc.buf.AppendByte('\\') |
|
enc.buf.AppendByte(b) |
|
case '\n': |
|
enc.buf.AppendByte('\\') |
|
enc.buf.AppendByte('n') |
|
case '\r': |
|
enc.buf.AppendByte('\\') |
|
enc.buf.AppendByte('r') |
|
case '\t': |
|
enc.buf.AppendByte('\\') |
|
enc.buf.AppendByte('t') |
|
default: |
|
// Encode bytes < 0x20, except for the escape sequences above. |
|
enc.buf.AppendString(`\u00`) |
|
enc.buf.AppendByte(_hex[b>>4]) |
|
enc.buf.AppendByte(_hex[b&0xF]) |
|
} |
|
return true |
|
} |
|
|
|
func (enc *jsonEncoder) tryAddRuneError(r rune, size int) bool { |
|
if r == utf8.RuneError && size == 1 { |
|
enc.buf.AppendString(`\ufffd`) |
|
return true |
|
} |
|
return false |
|
}
|
|
|