.github
alerting
client
config
controller
core
docs
example
jsonpath
k8s
k8stest
metric
pattern
security
storage
util
vendor
cloud.google.com
github.com
TwinProduction
beorn7
cespare
davecgh
go-ping
gogo
golang
google
googleapis
gorilla
imdario
json-iterator
go
.codecov.yml
.gitignore
.travis.yml
Gopkg.lock
Gopkg.toml
LICENSE
README.md
adapter.go
any.go
any_array.go
any_bool.go
any_float.go
any_int32.go
any_int64.go
any_invalid.go
any_nil.go
any_number.go
any_object.go
any_str.go
any_uint32.go
any_uint64.go
build.sh
config.go
fuzzy_mode_convert_table.md
go.mod
go.sum
iter.go
iter_array.go
iter_float.go
iter_int.go
iter_object.go
iter_skip.go
iter_skip_sloppy.go
iter_skip_strict.go
iter_str.go
jsoniter.go
pool.go
reflect.go
reflect_array.go
reflect_dynamic.go
reflect_extension.go
reflect_json_number.go
reflect_json_raw_message.go
reflect_map.go
reflect_marshaler.go
reflect_native.go
reflect_optional.go
reflect_slice.go
reflect_struct_decoder.go
reflect_struct_encoder.go
stream.go
stream_float.go
stream_int.go
stream_str.go
test.sh
kballard
mattn
matttproud
miekg
modern-go
prometheus
remyoudompheng
spf13
go.etcd.io
golang.org
google.golang.org
gopkg.in
k8s.io
lukechampine.com
modernc.org
sigs.k8s.io
modules.txt
watchdog
web
.dockerignore
.gitattributes
.gitignore
Dockerfile
LICENSE.md
Makefile
README.md
config.yaml
go.mod
go.sum
main.go
376 lines
10 KiB
Go
376 lines
10 KiB
Go
package jsoniter
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"reflect"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"github.com/modern-go/concurrent"
|
|
"github.com/modern-go/reflect2"
|
|
)
|
|
|
|
// Config customize how the API should behave.
|
|
// The API is created from Config by Froze.
|
|
type Config struct {
|
|
IndentionStep int
|
|
MarshalFloatWith6Digits bool
|
|
EscapeHTML bool
|
|
SortMapKeys bool
|
|
UseNumber bool
|
|
DisallowUnknownFields bool
|
|
TagKey string
|
|
OnlyTaggedField bool
|
|
ValidateJsonRawMessage bool
|
|
ObjectFieldMustBeSimpleString bool
|
|
CaseSensitive bool
|
|
}
|
|
|
|
// API the public interface of this package.
|
|
// Primary Marshal and Unmarshal.
|
|
type API interface {
|
|
IteratorPool
|
|
StreamPool
|
|
MarshalToString(v interface{}) (string, error)
|
|
Marshal(v interface{}) ([]byte, error)
|
|
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
|
UnmarshalFromString(str string, v interface{}) error
|
|
Unmarshal(data []byte, v interface{}) error
|
|
Get(data []byte, path ...interface{}) Any
|
|
NewEncoder(writer io.Writer) *Encoder
|
|
NewDecoder(reader io.Reader) *Decoder
|
|
Valid(data []byte) bool
|
|
RegisterExtension(extension Extension)
|
|
DecoderOf(typ reflect2.Type) ValDecoder
|
|
EncoderOf(typ reflect2.Type) ValEncoder
|
|
}
|
|
|
|
// ConfigDefault the default API
|
|
var ConfigDefault = Config{
|
|
EscapeHTML: true,
|
|
}.Froze()
|
|
|
|
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
|
|
var ConfigCompatibleWithStandardLibrary = Config{
|
|
EscapeHTML: true,
|
|
SortMapKeys: true,
|
|
ValidateJsonRawMessage: true,
|
|
}.Froze()
|
|
|
|
// ConfigFastest marshals float with only 6 digits precision
|
|
var ConfigFastest = Config{
|
|
EscapeHTML: false,
|
|
MarshalFloatWith6Digits: true, // will lose precession
|
|
ObjectFieldMustBeSimpleString: true, // do not unescape object field
|
|
}.Froze()
|
|
|
|
type frozenConfig struct {
|
|
configBeforeFrozen Config
|
|
sortMapKeys bool
|
|
indentionStep int
|
|
objectFieldMustBeSimpleString bool
|
|
onlyTaggedField bool
|
|
disallowUnknownFields bool
|
|
decoderCache *concurrent.Map
|
|
encoderCache *concurrent.Map
|
|
encoderExtension Extension
|
|
decoderExtension Extension
|
|
extraExtensions []Extension
|
|
streamPool *sync.Pool
|
|
iteratorPool *sync.Pool
|
|
caseSensitive bool
|
|
}
|
|
|
|
func (cfg *frozenConfig) initCache() {
|
|
cfg.decoderCache = concurrent.NewMap()
|
|
cfg.encoderCache = concurrent.NewMap()
|
|
}
|
|
|
|
func (cfg *frozenConfig) addDecoderToCache(cacheKey uintptr, decoder ValDecoder) {
|
|
cfg.decoderCache.Store(cacheKey, decoder)
|
|
}
|
|
|
|
func (cfg *frozenConfig) addEncoderToCache(cacheKey uintptr, encoder ValEncoder) {
|
|
cfg.encoderCache.Store(cacheKey, encoder)
|
|
}
|
|
|
|
func (cfg *frozenConfig) getDecoderFromCache(cacheKey uintptr) ValDecoder {
|
|
decoder, found := cfg.decoderCache.Load(cacheKey)
|
|
if found {
|
|
return decoder.(ValDecoder)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (cfg *frozenConfig) getEncoderFromCache(cacheKey uintptr) ValEncoder {
|
|
encoder, found := cfg.encoderCache.Load(cacheKey)
|
|
if found {
|
|
return encoder.(ValEncoder)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var cfgCache = concurrent.NewMap()
|
|
|
|
func getFrozenConfigFromCache(cfg Config) *frozenConfig {
|
|
obj, found := cfgCache.Load(cfg)
|
|
if found {
|
|
return obj.(*frozenConfig)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func addFrozenConfigToCache(cfg Config, frozenConfig *frozenConfig) {
|
|
cfgCache.Store(cfg, frozenConfig)
|
|
}
|
|
|
|
// Froze forge API from config
|
|
func (cfg Config) Froze() API {
|
|
api := &frozenConfig{
|
|
sortMapKeys: cfg.SortMapKeys,
|
|
indentionStep: cfg.IndentionStep,
|
|
objectFieldMustBeSimpleString: cfg.ObjectFieldMustBeSimpleString,
|
|
onlyTaggedField: cfg.OnlyTaggedField,
|
|
disallowUnknownFields: cfg.DisallowUnknownFields,
|
|
caseSensitive: cfg.CaseSensitive,
|
|
}
|
|
api.streamPool = &sync.Pool{
|
|
New: func() interface{} {
|
|
return NewStream(api, nil, 512)
|
|
},
|
|
}
|
|
api.iteratorPool = &sync.Pool{
|
|
New: func() interface{} {
|
|
return NewIterator(api)
|
|
},
|
|
}
|
|
api.initCache()
|
|
encoderExtension := EncoderExtension{}
|
|
decoderExtension := DecoderExtension{}
|
|
if cfg.MarshalFloatWith6Digits {
|
|
api.marshalFloatWith6Digits(encoderExtension)
|
|
}
|
|
if cfg.EscapeHTML {
|
|
api.escapeHTML(encoderExtension)
|
|
}
|
|
if cfg.UseNumber {
|
|
api.useNumber(decoderExtension)
|
|
}
|
|
if cfg.ValidateJsonRawMessage {
|
|
api.validateJsonRawMessage(encoderExtension)
|
|
}
|
|
api.encoderExtension = encoderExtension
|
|
api.decoderExtension = decoderExtension
|
|
api.configBeforeFrozen = cfg
|
|
return api
|
|
}
|
|
|
|
func (cfg Config) frozeWithCacheReuse(extraExtensions []Extension) *frozenConfig {
|
|
api := getFrozenConfigFromCache(cfg)
|
|
if api != nil {
|
|
return api
|
|
}
|
|
api = cfg.Froze().(*frozenConfig)
|
|
for _, extension := range extraExtensions {
|
|
api.RegisterExtension(extension)
|
|
}
|
|
addFrozenConfigToCache(cfg, api)
|
|
return api
|
|
}
|
|
|
|
func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) {
|
|
encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) {
|
|
rawMessage := *(*json.RawMessage)(ptr)
|
|
iter := cfg.BorrowIterator([]byte(rawMessage))
|
|
defer cfg.ReturnIterator(iter)
|
|
iter.Read()
|
|
if iter.Error != nil && iter.Error != io.EOF {
|
|
stream.WriteRaw("null")
|
|
} else {
|
|
stream.WriteRaw(string(rawMessage))
|
|
}
|
|
}, func(ptr unsafe.Pointer) bool {
|
|
return len(*((*json.RawMessage)(ptr))) == 0
|
|
}}
|
|
extension[reflect2.TypeOfPtr((*json.RawMessage)(nil)).Elem()] = encoder
|
|
extension[reflect2.TypeOfPtr((*RawMessage)(nil)).Elem()] = encoder
|
|
}
|
|
|
|
func (cfg *frozenConfig) useNumber(extension DecoderExtension) {
|
|
extension[reflect2.TypeOfPtr((*interface{})(nil)).Elem()] = &funcDecoder{func(ptr unsafe.Pointer, iter *Iterator) {
|
|
exitingValue := *((*interface{})(ptr))
|
|
if exitingValue != nil && reflect.TypeOf(exitingValue).Kind() == reflect.Ptr {
|
|
iter.ReadVal(exitingValue)
|
|
return
|
|
}
|
|
if iter.WhatIsNext() == NumberValue {
|
|
*((*interface{})(ptr)) = json.Number(iter.readNumberAsString())
|
|
} else {
|
|
*((*interface{})(ptr)) = iter.Read()
|
|
}
|
|
}}
|
|
}
|
|
func (cfg *frozenConfig) getTagKey() string {
|
|
tagKey := cfg.configBeforeFrozen.TagKey
|
|
if tagKey == "" {
|
|
return "json"
|
|
}
|
|
return tagKey
|
|
}
|
|
|
|
func (cfg *frozenConfig) RegisterExtension(extension Extension) {
|
|
cfg.extraExtensions = append(cfg.extraExtensions, extension)
|
|
copied := cfg.configBeforeFrozen
|
|
cfg.configBeforeFrozen = copied
|
|
}
|
|
|
|
type lossyFloat32Encoder struct {
|
|
}
|
|
|
|
func (encoder *lossyFloat32Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
stream.WriteFloat32Lossy(*((*float32)(ptr)))
|
|
}
|
|
|
|
func (encoder *lossyFloat32Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
return *((*float32)(ptr)) == 0
|
|
}
|
|
|
|
type lossyFloat64Encoder struct {
|
|
}
|
|
|
|
func (encoder *lossyFloat64Encoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
stream.WriteFloat64Lossy(*((*float64)(ptr)))
|
|
}
|
|
|
|
func (encoder *lossyFloat64Encoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
return *((*float64)(ptr)) == 0
|
|
}
|
|
|
|
// EnableLossyFloatMarshalling keeps 10**(-6) precision
|
|
// for float variables for better performance.
|
|
func (cfg *frozenConfig) marshalFloatWith6Digits(extension EncoderExtension) {
|
|
// for better performance
|
|
extension[reflect2.TypeOfPtr((*float32)(nil)).Elem()] = &lossyFloat32Encoder{}
|
|
extension[reflect2.TypeOfPtr((*float64)(nil)).Elem()] = &lossyFloat64Encoder{}
|
|
}
|
|
|
|
type htmlEscapedStringEncoder struct {
|
|
}
|
|
|
|
func (encoder *htmlEscapedStringEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
|
|
str := *((*string)(ptr))
|
|
stream.WriteStringWithHTMLEscaped(str)
|
|
}
|
|
|
|
func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|
return *((*string)(ptr)) == ""
|
|
}
|
|
|
|
func (cfg *frozenConfig) escapeHTML(encoderExtension EncoderExtension) {
|
|
encoderExtension[reflect2.TypeOfPtr((*string)(nil)).Elem()] = &htmlEscapedStringEncoder{}
|
|
}
|
|
|
|
func (cfg *frozenConfig) cleanDecoders() {
|
|
typeDecoders = map[string]ValDecoder{}
|
|
fieldDecoders = map[string]ValDecoder{}
|
|
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
|
}
|
|
|
|
func (cfg *frozenConfig) cleanEncoders() {
|
|
typeEncoders = map[string]ValEncoder{}
|
|
fieldEncoders = map[string]ValEncoder{}
|
|
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
|
}
|
|
|
|
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
|
|
stream := cfg.BorrowStream(nil)
|
|
defer cfg.ReturnStream(stream)
|
|
stream.WriteVal(v)
|
|
if stream.Error != nil {
|
|
return "", stream.Error
|
|
}
|
|
return string(stream.Buffer()), nil
|
|
}
|
|
|
|
func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) {
|
|
stream := cfg.BorrowStream(nil)
|
|
defer cfg.ReturnStream(stream)
|
|
stream.WriteVal(v)
|
|
if stream.Error != nil {
|
|
return nil, stream.Error
|
|
}
|
|
result := stream.Buffer()
|
|
copied := make([]byte, len(result))
|
|
copy(copied, result)
|
|
return copied, nil
|
|
}
|
|
|
|
func (cfg *frozenConfig) MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
|
if prefix != "" {
|
|
panic("prefix is not supported")
|
|
}
|
|
for _, r := range indent {
|
|
if r != ' ' {
|
|
panic("indent can only be space")
|
|
}
|
|
}
|
|
newCfg := cfg.configBeforeFrozen
|
|
newCfg.IndentionStep = len(indent)
|
|
return newCfg.frozeWithCacheReuse(cfg.extraExtensions).Marshal(v)
|
|
}
|
|
|
|
func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error {
|
|
data := []byte(str)
|
|
iter := cfg.BorrowIterator(data)
|
|
defer cfg.ReturnIterator(iter)
|
|
iter.ReadVal(v)
|
|
c := iter.nextToken()
|
|
if c == 0 {
|
|
if iter.Error == io.EOF {
|
|
return nil
|
|
}
|
|
return iter.Error
|
|
}
|
|
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
|
return iter.Error
|
|
}
|
|
|
|
func (cfg *frozenConfig) Get(data []byte, path ...interface{}) Any {
|
|
iter := cfg.BorrowIterator(data)
|
|
defer cfg.ReturnIterator(iter)
|
|
return locatePath(iter, path)
|
|
}
|
|
|
|
func (cfg *frozenConfig) Unmarshal(data []byte, v interface{}) error {
|
|
iter := cfg.BorrowIterator(data)
|
|
defer cfg.ReturnIterator(iter)
|
|
iter.ReadVal(v)
|
|
c := iter.nextToken()
|
|
if c == 0 {
|
|
if iter.Error == io.EOF {
|
|
return nil
|
|
}
|
|
return iter.Error
|
|
}
|
|
iter.ReportError("Unmarshal", "there are bytes left after unmarshal")
|
|
return iter.Error
|
|
}
|
|
|
|
func (cfg *frozenConfig) NewEncoder(writer io.Writer) *Encoder {
|
|
stream := NewStream(cfg, writer, 512)
|
|
return &Encoder{stream}
|
|
}
|
|
|
|
func (cfg *frozenConfig) NewDecoder(reader io.Reader) *Decoder {
|
|
iter := Parse(cfg, reader, 512)
|
|
return &Decoder{iter}
|
|
}
|
|
|
|
func (cfg *frozenConfig) Valid(data []byte) bool {
|
|
iter := cfg.BorrowIterator(data)
|
|
defer cfg.ReturnIterator(iter)
|
|
iter.Skip()
|
|
return iter.Error == nil
|
|
}
|