Skip to content
This repository was archived by the owner on Dec 15, 2025. It is now read-only.

Commit 28452fc

Browse files
committed
cow cache is not same, as map read will modify the underlying map. use sync.Map for 1.9 and above, and mutex if sync.Map not available
1 parent ea8c330 commit 28452fc

File tree

3 files changed

+106
-56
lines changed

3 files changed

+106
-56
lines changed

feature_config.go

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"errors"
66
"io"
77
"reflect"
8-
"sync/atomic"
98
"unsafe"
109
)
1110

@@ -23,19 +22,6 @@ type Config struct {
2322
ObjectFieldMustBeSimpleString bool
2423
}
2524

26-
type frozenConfig struct {
27-
configBeforeFrozen Config
28-
sortMapKeys bool
29-
indentionStep int
30-
objectFieldMustBeSimpleString bool
31-
onlyTaggedField bool
32-
decoderCache unsafe.Pointer
33-
encoderCache unsafe.Pointer
34-
extensions []Extension
35-
streamPool chan *Stream
36-
iteratorPool chan *Iterator
37-
}
38-
3925
// API the public interface of this package.
4026
// Primary Marshal and Unmarshal.
4127
type API interface {
@@ -83,8 +69,7 @@ func (cfg Config) Froze() API {
8369
streamPool: make(chan *Stream, 16),
8470
iteratorPool: make(chan *Iterator, 16),
8571
}
86-
atomic.StorePointer(&frozenConfig.decoderCache, unsafe.Pointer(&map[string]ValDecoder{}))
87-
atomic.StorePointer(&frozenConfig.encoderCache, unsafe.Pointer(&map[string]ValEncoder{}))
72+
frozenConfig.initCache()
8873
if cfg.MarshalFloatWith6Digits {
8974
frozenConfig.marshalFloatWith6Digits()
9075
}
@@ -198,46 +183,6 @@ func (cfg *frozenConfig) escapeHTML() {
198183
cfg.addEncoderToCache(reflect.TypeOf((*string)(nil)).Elem(), &htmlEscapedStringEncoder{})
199184
}
200185

201-
func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
202-
done := false
203-
for !done {
204-
ptr := atomic.LoadPointer(&cfg.decoderCache)
205-
cache := *(*map[reflect.Type]ValDecoder)(ptr)
206-
copied := map[reflect.Type]ValDecoder{}
207-
for k, v := range cache {
208-
copied[k] = v
209-
}
210-
copied[cacheKey] = decoder
211-
done = atomic.CompareAndSwapPointer(&cfg.decoderCache, ptr, unsafe.Pointer(&copied))
212-
}
213-
}
214-
215-
func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
216-
done := false
217-
for !done {
218-
ptr := atomic.LoadPointer(&cfg.encoderCache)
219-
cache := *(*map[reflect.Type]ValEncoder)(ptr)
220-
copied := map[reflect.Type]ValEncoder{}
221-
for k, v := range cache {
222-
copied[k] = v
223-
}
224-
copied[cacheKey] = encoder
225-
done = atomic.CompareAndSwapPointer(&cfg.encoderCache, ptr, unsafe.Pointer(&copied))
226-
}
227-
}
228-
229-
func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
230-
ptr := atomic.LoadPointer(&cfg.decoderCache)
231-
cache := *(*map[reflect.Type]ValDecoder)(ptr)
232-
return cache[cacheKey]
233-
}
234-
235-
func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
236-
ptr := atomic.LoadPointer(&cfg.encoderCache)
237-
cache := *(*map[reflect.Type]ValEncoder)(ptr)
238-
return cache[cacheKey]
239-
}
240-
241186
func (cfg *frozenConfig) cleanDecoders() {
242187
typeDecoders = map[string]ValDecoder{}
243188
fieldDecoders = map[string]ValDecoder{}

feature_config_with_sync_map.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//+build go1.9
2+
3+
package jsoniter
4+
5+
import (
6+
"reflect"
7+
"sync"
8+
)
9+
10+
type frozenConfig struct {
11+
configBeforeFrozen Config
12+
sortMapKeys bool
13+
indentionStep int
14+
objectFieldMustBeSimpleString bool
15+
onlyTaggedField bool
16+
decoderCache sync.Map
17+
encoderCache sync.Map
18+
extensions []Extension
19+
streamPool chan *Stream
20+
iteratorPool chan *Iterator
21+
}
22+
23+
func (cfg *frozenConfig) initCache() {
24+
cfg.decoderCache = sync.Map{}
25+
cfg.encoderCache = sync.Map{}
26+
}
27+
28+
29+
func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
30+
cfg.decoderCache.Store(cacheKey, decoder)
31+
}
32+
33+
func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
34+
cfg.encoderCache.Store(cacheKey, encoder)
35+
}
36+
37+
func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
38+
decoder, found := cfg.decoderCache.Load(cacheKey)
39+
if found {
40+
return decoder.(ValDecoder)
41+
}
42+
return nil
43+
}
44+
45+
func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
46+
encoder, found := cfg.encoderCache.Load(cacheKey)
47+
if found {
48+
return encoder.(ValEncoder)
49+
}
50+
return nil
51+
}

feature_config_without_sync_map.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//+build !go1.9
2+
3+
package jsoniter
4+
5+
import (
6+
"reflect"
7+
"sync"
8+
)
9+
10+
type frozenConfig struct {
11+
configBeforeFrozen Config
12+
sortMapKeys bool
13+
indentionStep int
14+
objectFieldMustBeSimpleString bool
15+
onlyTaggedField bool
16+
cacheLock *sync.RWMutex
17+
decoderCache map[reflect.Type]ValDecoder
18+
encoderCache map[reflect.Type]ValEncoder
19+
extensions []Extension
20+
streamPool chan *Stream
21+
iteratorPool chan *Iterator
22+
}
23+
24+
func (cfg *frozenConfig) initCache() {
25+
cfg.cacheLock = &sync.RWMutex{}
26+
cfg.decoderCache = map[reflect.Type]ValDecoder{}
27+
cfg.encoderCache = map[reflect.Type]ValEncoder{}
28+
}
29+
30+
func (cfg *frozenConfig) addDecoderToCache(cacheKey reflect.Type, decoder ValDecoder) {
31+
cfg.cacheLock.Lock()
32+
cfg.decoderCache[cacheKey] = decoder
33+
cfg.cacheLock.Unlock()
34+
}
35+
36+
func (cfg *frozenConfig) addEncoderToCache(cacheKey reflect.Type, encoder ValEncoder) {
37+
cfg.cacheLock.Lock()
38+
cfg.encoderCache[cacheKey] = encoder
39+
cfg.cacheLock.Unlock()
40+
}
41+
42+
func (cfg *frozenConfig) getDecoderFromCache(cacheKey reflect.Type) ValDecoder {
43+
cfg.cacheLock.RLock()
44+
decoder, _ := cfg.decoderCache[cacheKey].(ValDecoder)
45+
cfg.cacheLock.RUnlock()
46+
return decoder
47+
}
48+
49+
func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
50+
cfg.cacheLock.RLock()
51+
encoder, _ := cfg.encoderCache[cacheKey].(ValEncoder)
52+
cfg.cacheLock.RUnlock()
53+
return encoder
54+
}

0 commit comments

Comments
 (0)