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)) iter.Read() if iter.Error != nil { stream.WriteRaw("null") } else { cfg.ReturnIterator(iter) 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 }