diff --git a/encode_compile.go b/encode_compile.go index 04e256c..06f21a0 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -22,11 +22,12 @@ type opcodeSet struct { } var ( - marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem() - marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() - cachedOpcode unsafe.Pointer // map[uintptr]*opcodeSet - baseTypeAddr uintptr - cachedOpcodeSets []*opcodeSet + marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem() + marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + cachedOpcode unsafe.Pointer // map[uintptr]*opcodeSet + baseTypeAddr uintptr + cachedOpcodeSets []*opcodeSet + existsCachedOpcodeSets bool ) const ( @@ -80,6 +81,7 @@ func setupOpcodeSets() error { return fmt.Errorf("too big address range %d", addrRange) } cachedOpcodeSets = make([]*opcodeSet, addrRange) + existsCachedOpcodeSets = true baseTypeAddr = min return nil } @@ -90,35 +92,6 @@ func init() { } } -func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) { - if cachedOpcodeSets == nil { - return encodeCompileToGetCodeSetSlowPath(typeptr) - } - if codeSet := cachedOpcodeSets[typeptr-baseTypeAddr]; codeSet != nil { - return codeSet, nil - } - - // noescape trick for header.typ ( reflect.*rtype ) - copiedType := *(**rtype)(unsafe.Pointer(&typeptr)) - - code, err := encodeCompileHead(&encodeCompileContext{ - typ: copiedType, - root: true, - structTypeToCompiledCode: map[uintptr]*compiledCode{}, - }) - if err != nil { - return nil, err - } - code = copyOpcode(code) - codeLength := code.totalLength() - codeSet := &opcodeSet{ - code: code, - codeLength: codeLength, - } - cachedOpcodeSets[int(typeptr-baseTypeAddr)] = codeSet - return codeSet, nil -} - func encodeCompileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) { opcodeMap := loadOpcodeMap() if codeSet, exists := opcodeMap[typeptr]; exists { diff --git a/encode_compile_norace.go b/encode_compile_norace.go new file mode 100644 index 0000000..209f60c --- /dev/null +++ b/encode_compile_norace.go @@ -0,0 +1,34 @@ +// +build !race + +package json + +import "unsafe" + +func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) { + if !existsCachedOpcodeSets { + return encodeCompileToGetCodeSetSlowPath(typeptr) + } + if codeSet := cachedOpcodeSets[typeptr-baseTypeAddr]; codeSet != nil { + return codeSet, nil + } + + // noescape trick for header.typ ( reflect.*rtype ) + copiedType := *(**rtype)(unsafe.Pointer(&typeptr)) + + code, err := encodeCompileHead(&encodeCompileContext{ + typ: copiedType, + root: true, + structTypeToCompiledCode: map[uintptr]*compiledCode{}, + }) + if err != nil { + return nil, err + } + code = copyOpcode(code) + codeLength := code.totalLength() + codeSet := &opcodeSet{ + code: code, + codeLength: codeLength, + } + cachedOpcodeSets[int(typeptr-baseTypeAddr)] = codeSet + return codeSet, nil +} diff --git a/encode_compile_race.go b/encode_compile_race.go new file mode 100644 index 0000000..a2fcb73 --- /dev/null +++ b/encode_compile_race.go @@ -0,0 +1,44 @@ +// +build race + +package json + +import ( + "sync" + "unsafe" +) + +var setsMu sync.RWMutex + +func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) { + if !existsCachedOpcodeSets { + return encodeCompileToGetCodeSetSlowPath(typeptr) + } + setsMu.RLock() + if codeSet := cachedOpcodeSets[typeptr-baseTypeAddr]; codeSet != nil { + setsMu.RUnlock() + return codeSet, nil + } + setsMu.RUnlock() + + // noescape trick for header.typ ( reflect.*rtype ) + copiedType := *(**rtype)(unsafe.Pointer(&typeptr)) + + code, err := encodeCompileHead(&encodeCompileContext{ + typ: copiedType, + root: true, + structTypeToCompiledCode: map[uintptr]*compiledCode{}, + }) + if err != nil { + return nil, err + } + code = copyOpcode(code) + codeLength := code.totalLength() + codeSet := &opcodeSet{ + code: code, + codeLength: codeLength, + } + setsMu.Lock() + cachedOpcodeSets[int(typeptr-baseTypeAddr)] = codeSet + setsMu.Unlock() + return codeSet, nil +}