package jsoniter
import (
"encoding/json"
"io"
"reflect"
"sync"
"unsafe"
"github.com/modern-go/concurrent"
"github.com/modern-go/reflect2"
)
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
}
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
}
var ConfigDefault = Config {
EscapeHTML : true ,
}.Froze ()
var ConfigCompatibleWithStandardLibrary = Config {
EscapeHTML : true ,
SortMapKeys : true ,
ValidateJsonRawMessage : true ,
}.Froze ()
var ConfigFastest = Config {
EscapeHTML : false ,
MarshalFloatWith6Digits : true ,
ObjectFieldMustBeSimpleString : true ,
}.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 )
}
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
}
func (cfg *frozenConfig ) marshalFloatWith6Digits (extension EncoderExtension ) {
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
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .