diff --git a/feature_adapter.go b/feature_adapter.go index 25169b0..9dbabb9 100644 --- a/feature_adapter.go +++ b/feature_adapter.go @@ -172,7 +172,7 @@ func (decoder *AdaptedDecoder) UseNumber() { } func NewEncoder(writer io.Writer) *AdaptedEncoder { - stream := NewStream(&Config{}, writer, 512) + stream := NewStream(Config{}.Froze(), writer, 512) return &AdaptedEncoder{stream} } @@ -187,5 +187,5 @@ func (adapter *AdaptedEncoder) Encode(val interface{}) error { } func (adapter *AdaptedEncoder) SetIndent(prefix, indent string) { - adapter.stream.cfg.IndentionStep = len(indent) + adapter.stream.cfg.indentionStep = len(indent) } diff --git a/feature_config.go b/feature_config.go index 9aa5e91..a84e86b 100644 --- a/feature_config.go +++ b/feature_config.go @@ -11,37 +11,38 @@ type Config struct { IndentionStep int MarshalFloatWith6Digits bool SupportUnexportedStructFields bool +} + +type frozenConfig struct { + indentionStep int decoderCache unsafe.Pointer encoderCache unsafe.Pointer extensions []ExtensionFunc } -var DEFAULT_CONFIG = &Config{} +var DEFAULT_CONFIG = Config{}.Froze() -func init() { - DEFAULT_CONFIG.init() -} - -func (cfg *Config) init() *Config { - if cfg.encoderCache == nil { - atomic.StorePointer(&cfg.decoderCache, unsafe.Pointer(&map[string]Decoder{})) - atomic.StorePointer(&cfg.encoderCache, unsafe.Pointer(&map[string]Encoder{})) - if cfg.MarshalFloatWith6Digits { - cfg.marshalFloatWith6Digits() - } - if cfg.SupportUnexportedStructFields { - cfg.supportUnexportedStructFields() - } +func (cfg Config) Froze() *frozenConfig { + frozenConfig := &frozenConfig{ + indentionStep: cfg.IndentionStep, } - return cfg + atomic.StorePointer(&frozenConfig.decoderCache, unsafe.Pointer(&map[string]Decoder{})) + atomic.StorePointer(&frozenConfig.encoderCache, unsafe.Pointer(&map[string]Encoder{})) + if cfg.MarshalFloatWith6Digits { + frozenConfig.marshalFloatWith6Digits() + } + if cfg.SupportUnexportedStructFields { + frozenConfig.supportUnexportedStructFields() + } + return frozenConfig } // RegisterExtension can register a custom extension -func (cfg *Config) RegisterExtension(extension ExtensionFunc) { +func (cfg *frozenConfig) RegisterExtension(extension ExtensionFunc) { cfg.extensions = append(cfg.extensions, extension) } -func (cfg *Config) supportUnexportedStructFields() { +func (cfg *frozenConfig) supportUnexportedStructFields() { cfg.RegisterExtension(func(type_ reflect.Type, field *reflect.StructField) ([]string, EncoderFunc, DecoderFunc) { return []string{field.Name}, nil, nil }) @@ -49,7 +50,7 @@ func (cfg *Config) supportUnexportedStructFields() { // EnableLossyFloatMarshalling keeps 10**(-6) precision // for float variables for better performance. -func (cfg *Config) marshalFloatWith6Digits() { +func (cfg *frozenConfig) marshalFloatWith6Digits() { // for better performance cfg.addEncoderToCache(reflect.TypeOf((*float32)(nil)).Elem(), &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) { val := *((*float32)(ptr)) @@ -61,7 +62,7 @@ func (cfg *Config) marshalFloatWith6Digits() { }}) } -func (cfg *Config) addDecoderToCache(cacheKey reflect.Type, decoder Decoder) { +func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder Decoder) { done := false for !done { ptr := atomic.LoadPointer(&cfg.decoderCache) @@ -75,7 +76,7 @@ func (cfg *Config) addDecoderToCache(cacheKey reflect.Type, decoder Decoder) { } } -func (cfg *Config) addEncoderToCache(cacheKey reflect.Type, encoder Encoder) { +func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder Encoder) { done := false for !done { ptr := atomic.LoadPointer(&cfg.encoderCache) @@ -89,33 +90,33 @@ func (cfg *Config) addEncoderToCache(cacheKey reflect.Type, encoder Encoder) { } } -func (cfg *Config) getDecoderFromCache(cacheKey reflect.Type) Decoder { +func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) Decoder { ptr := atomic.LoadPointer(&cfg.decoderCache) cache := *(*map[reflect.Type]Decoder)(ptr) return cache[cacheKey] } -func (cfg *Config) getEncoderFromCache(cacheKey reflect.Type) Encoder { +func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) Encoder { ptr := atomic.LoadPointer(&cfg.encoderCache) cache := *(*map[reflect.Type]Encoder)(ptr) return cache[cacheKey] } // CleanDecoders cleans decoders registered or cached -func (cfg *Config) CleanDecoders() { +func (cfg *frozenConfig) CleanDecoders() { typeDecoders = map[string]Decoder{} fieldDecoders = map[string]Decoder{} atomic.StorePointer(&cfg.decoderCache, unsafe.Pointer(&map[string]Decoder{})) } // CleanEncoders cleans encoders registered or cached -func (cfg *Config) CleanEncoders() { +func (cfg *frozenConfig) CleanEncoders() { typeEncoders = map[string]Encoder{} fieldEncoders = map[string]Encoder{} atomic.StorePointer(&cfg.encoderCache, unsafe.Pointer(&map[string]Encoder{})) } -func (cfg *Config) MarshalToString(v interface{}) (string, error) { +func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) { buf, err := cfg.Marshal(v) if err != nil { return "", err @@ -123,8 +124,7 @@ func (cfg *Config) MarshalToString(v interface{}) (string, error) { return string(buf), nil } -func (cfg *Config) Marshal(v interface{}) ([]byte, error) { - cfg.init() +func (cfg *frozenConfig) Marshal(v interface{}) ([]byte, error) { stream := NewStream(cfg, nil, 256) stream.WriteVal(v) if stream.Error != nil { @@ -133,7 +133,7 @@ func (cfg *Config) Marshal(v interface{}) ([]byte, error) { return stream.Buffer(), nil } -func (cfg *Config) UnmarshalFromString(str string, v interface{}) error { +func (cfg *frozenConfig) UnmarshalFromString(str string, v interface{}) error { data := []byte(str) data = data[:lastNotSpacePos(data)] iter := ParseBytes(cfg, data) diff --git a/feature_iter.go b/feature_iter.go index 7b5b238..85e6275 100644 --- a/feature_iter.go +++ b/feature_iter.go @@ -66,7 +66,7 @@ func init() { // Iterator is a fast and flexible JSON parser type Iterator struct { - cfg *Config + cfg *frozenConfig reader io.Reader buf []byte head int @@ -75,8 +75,7 @@ type Iterator struct { } // Create creates an empty Iterator instance -func NewIterator(cfg *Config) *Iterator { - cfg.init() +func NewIterator(cfg *frozenConfig) *Iterator { return &Iterator{ cfg: cfg, reader: nil, @@ -87,8 +86,7 @@ func NewIterator(cfg *Config) *Iterator { } // Parse parses a json buffer in io.Reader into an Iterator instance -func Parse(cfg *Config, reader io.Reader, bufSize int) *Iterator { - cfg.init() +func Parse(cfg *frozenConfig, reader io.Reader, bufSize int) *Iterator { return &Iterator{ cfg: cfg, reader: reader, @@ -99,8 +97,7 @@ func Parse(cfg *Config, reader io.Reader, bufSize int) *Iterator { } // ParseBytes parses a json byte slice into an Iterator instance -func ParseBytes(cfg *Config, input []byte) *Iterator { - cfg.init() +func ParseBytes(cfg *frozenConfig, input []byte) *Iterator { return &Iterator{ cfg: cfg, reader: nil, @@ -111,7 +108,7 @@ func ParseBytes(cfg *Config, input []byte) *Iterator { } // ParseString parses a json string into an Iterator instance -func ParseString(cfg *Config, input string) *Iterator { +func ParseString(cfg *frozenConfig, input string) *Iterator { return ParseBytes(cfg, []byte(input)) } diff --git a/feature_reflect.go b/feature_reflect.go index 5723cc7..bd016e3 100644 --- a/feature_reflect.go +++ b/feature_reflect.go @@ -264,7 +264,7 @@ func (p prefix) addToEncoder(encoder Encoder, err error) (Encoder, error) { return encoder, err } -func decoderOfType(cfg *Config, typ reflect.Type) (Decoder, error) { +func decoderOfType(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { typeName := typ.String() typeDecoder := typeDecoders[typeName] if typeDecoder != nil { @@ -289,7 +289,7 @@ func decoderOfType(cfg *Config, typ reflect.Type) (Decoder, error) { return newDecoder, err } -func createDecoderOfType(cfg *Config, typ reflect.Type) (Decoder, error) { +func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { if typ.String() == "[]uint8" { return &base64Codec{}, nil } @@ -354,7 +354,7 @@ func createDecoderOfType(cfg *Config, typ reflect.Type) (Decoder, error) { } } -func encoderOfType(cfg *Config, typ reflect.Type) (Encoder, error) { +func encoderOfType(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { typeName := typ.String() typeEncoder := typeEncoders[typeName] if typeEncoder != nil { @@ -379,7 +379,7 @@ func encoderOfType(cfg *Config, typ reflect.Type) (Encoder, error) { return newEncoder, err } -func createEncoderOfType(cfg *Config, typ reflect.Type) (Encoder, error) { +func createEncoderOfType(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { if typ.String() == "[]uint8" { return &base64Codec{}, nil } @@ -445,7 +445,7 @@ func createEncoderOfType(cfg *Config, typ reflect.Type) (Encoder, error) { } } -func decoderOfOptional(cfg *Config, typ reflect.Type) (Decoder, error) { +func decoderOfOptional(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { elemType := typ.Elem() decoder, err := decoderOfType(cfg, elemType) if err != nil { @@ -454,7 +454,7 @@ func decoderOfOptional(cfg *Config, typ reflect.Type) (Decoder, error) { return &optionalDecoder{elemType, decoder}, nil } -func encoderOfOptional(cfg *Config, typ reflect.Type) (Encoder, error) { +func encoderOfOptional(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { elemType := typ.Elem() elemEncoder, err := encoderOfType(cfg, elemType) if err != nil { @@ -467,7 +467,7 @@ func encoderOfOptional(cfg *Config, typ reflect.Type) (Encoder, error) { return encoder, nil } -func decoderOfMap(cfg *Config, typ reflect.Type) (Decoder, error) { +func decoderOfMap(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { decoder, err := decoderOfType(cfg, typ.Elem()) if err != nil { return nil, err @@ -480,7 +480,7 @@ func extractInterface(val interface{}) emptyInterface { return *((*emptyInterface)(unsafe.Pointer(&val))) } -func encoderOfMap(cfg *Config, typ reflect.Type) (Encoder, error) { +func encoderOfMap(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { elemType := typ.Elem() encoder, err := encoderOfType(cfg, elemType) if err != nil { diff --git a/feature_reflect_array.go b/feature_reflect_array.go index 3499538..abf5127 100644 --- a/feature_reflect_array.go +++ b/feature_reflect_array.go @@ -7,7 +7,7 @@ import ( "unsafe" ) -func decoderOfSlice(cfg *Config, typ reflect.Type) (Decoder, error) { +func decoderOfSlice(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { decoder, err := decoderOfType(cfg, typ.Elem()) if err != nil { return nil, err @@ -15,7 +15,7 @@ func decoderOfSlice(cfg *Config, typ reflect.Type) (Decoder, error) { return &sliceDecoder{typ, typ.Elem(), decoder}, nil } -func encoderOfSlice(cfg *Config, typ reflect.Type) (Encoder, error) { +func encoderOfSlice(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { encoder, err := encoderOfType(cfg, typ.Elem()) if err != nil { return nil, err diff --git a/feature_reflect_object.go b/feature_reflect_object.go index 9bbdb12..9ea375f 100644 --- a/feature_reflect_object.go +++ b/feature_reflect_object.go @@ -9,7 +9,7 @@ import ( "unsafe" ) -func encoderOfStruct(cfg *Config, typ reflect.Type) (Encoder, error) { +func encoderOfStruct(cfg *frozenConfig, typ reflect.Type) (Encoder, error) { structEncoder_ := &structEncoder{} fields := map[string]*structFieldEncoder{} for _, field := range listStructFields(typ) { @@ -80,7 +80,7 @@ func listStructFields(typ reflect.Type) []*reflect.StructField { return fields } -func decoderOfStruct(cfg *Config, typ reflect.Type) (Decoder, error) { +func decoderOfStruct(cfg *frozenConfig, typ reflect.Type) (Decoder, error) { fields := map[string]*structFieldDecoder{} for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) diff --git a/feature_stream.go b/feature_stream.go index c30355a..7ad767a 100644 --- a/feature_stream.go +++ b/feature_stream.go @@ -5,7 +5,7 @@ import ( ) type Stream struct { - cfg *Config + cfg *frozenConfig out io.Writer buf []byte n int @@ -13,8 +13,7 @@ type Stream struct { indention int } -func NewStream(cfg *Config, out io.Writer, bufSize int) *Stream { - cfg.init() +func NewStream(cfg *frozenConfig, out io.Writer, bufSize int) *Stream { return &Stream{ cfg: cfg, out: out, @@ -277,7 +276,7 @@ func (stream *Stream) WriteBool(val bool) { } func (stream *Stream) WriteObjectStart() { - stream.indention += stream.cfg.IndentionStep + stream.indention += stream.cfg.indentionStep stream.writeByte('{') stream.writeIndention(0) } @@ -288,8 +287,8 @@ func (stream *Stream) WriteObjectField(field string) { } func (stream *Stream) WriteObjectEnd() { - stream.writeIndention(stream.cfg.IndentionStep) - stream.indention -= stream.cfg.IndentionStep + stream.writeIndention(stream.cfg.indentionStep) + stream.indention -= stream.cfg.indentionStep stream.writeByte('}') } @@ -304,7 +303,7 @@ func (stream *Stream) WriteMore() { } func (stream *Stream) WriteArrayStart() { - stream.indention += stream.cfg.IndentionStep + stream.indention += stream.cfg.indentionStep stream.writeByte('[') stream.writeIndention(0) } @@ -315,8 +314,8 @@ func (stream *Stream) WriteEmptyArray() { } func (stream *Stream) WriteArrayEnd() { - stream.writeIndention(stream.cfg.IndentionStep) - stream.indention -= stream.cfg.IndentionStep + stream.writeIndention(stream.cfg.indentionStep) + stream.indention -= stream.cfg.indentionStep stream.writeByte(']') } diff --git a/jsoniter_array_test.go b/jsoniter_array_test.go index 85c1d69..d69470b 100644 --- a/jsoniter_array_test.go +++ b/jsoniter_array_test.go @@ -213,7 +213,7 @@ func Test_whitespace_before_comma(t *testing.T) { func Test_write_array(t *testing.T) { should := require.New(t) buf := &bytes.Buffer{} - stream := NewStream(&Config{IndentionStep: 2}, buf, 4096) + stream := NewStream(Config{IndentionStep: 2}.Froze(), buf, 4096) stream.WriteArrayStart() stream.WriteInt(1) stream.WriteMore() diff --git a/jsoniter_customize_test.go b/jsoniter_customize_test.go index 619aa9a..1bc1f34 100644 --- a/jsoniter_customize_test.go +++ b/jsoniter_customize_test.go @@ -60,7 +60,7 @@ func Test_customize_byte_array_encoder(t *testing.T) { func Test_customize_float_marshal(t *testing.T) { should := require.New(t) - json := Config{MarshalFloatWith6Digits: true} + json := Config{MarshalFloatWith6Digits: true}.Froze() str, err := json.MarshalToString(float32(1.23456789)) should.Nil(err) should.Equal("1.234568", str) @@ -112,7 +112,7 @@ func Test_customize_field_by_extension(t *testing.T) { } func Test_unexported_fields(t *testing.T) { - jsoniter := &Config{SupportUnexportedStructFields: true} + jsoniter := Config{SupportUnexportedStructFields: true}.Froze() should := require.New(t) type TestObject struct { field1 string diff --git a/jsoniter_object_test.go b/jsoniter_object_test.go index a988351..75d6188 100644 --- a/jsoniter_object_test.go +++ b/jsoniter_object_test.go @@ -210,7 +210,7 @@ func Test_object_wrapper_any_get_all(t *testing.T) { func Test_write_object(t *testing.T) { should := require.New(t) buf := &bytes.Buffer{} - stream := NewStream(&Config{IndentionStep: 2}, buf, 4096) + stream := NewStream(Config{IndentionStep: 2}.Froze(), buf, 4096) stream.WriteObjectStart() stream.WriteObjectField("hello") stream.WriteInt(1) diff --git a/jsoniter_stream_test.go b/jsoniter_stream_test.go index 5d01252..541cc8e 100644 --- a/jsoniter_stream_test.go +++ b/jsoniter_stream_test.go @@ -31,7 +31,7 @@ func Test_writeBytes_should_grow_buffer(t *testing.T) { func Test_writeIndention_should_grow_buffer(t *testing.T) { should := require.New(t) - stream := NewStream(&Config{IndentionStep: 2}, nil, 1) + stream := NewStream(Config{IndentionStep: 2}.Froze(), nil, 1) stream.WriteVal([]int{1, 2, 3}) should.Equal("[\n 1,\n 2,\n 3\n]", string(stream.Buffer())) }