Fix: Switch to lazy init() in decoder and encoder (#490)

* Switch to lazy init() in decoder and encoder

This will prevent go-json from consuming heap unless it is used.

* limit changes to initEncoder and initDecoder
This commit is contained in:
Todd Treece 2024-12-11 07:52:23 -05:00 committed by GitHub
parent 3e9769d637
commit 279389a781
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 26 additions and 12 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"strings" "strings"
"sync"
"sync/atomic" "sync/atomic"
"unicode" "unicode"
"unsafe" "unsafe"
@ -17,22 +18,27 @@ var (
typeAddr *runtime.TypeAddr typeAddr *runtime.TypeAddr
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
cachedDecoder []Decoder cachedDecoder []Decoder
initOnce sync.Once
) )
func init() { func initDecoder() {
typeAddr = runtime.AnalyzeTypeAddr() initOnce.Do(func() {
if typeAddr == nil { typeAddr = runtime.AnalyzeTypeAddr()
typeAddr = &runtime.TypeAddr{} if typeAddr == nil {
} typeAddr = &runtime.TypeAddr{}
cachedDecoder = make([]Decoder, typeAddr.AddrRange>>typeAddr.AddrShift+1) }
cachedDecoder = make([]Decoder, typeAddr.AddrRange>>typeAddr.AddrShift+1)
})
} }
func loadDecoderMap() map[uintptr]Decoder { func loadDecoderMap() map[uintptr]Decoder {
initDecoder()
p := atomic.LoadPointer(&cachedDecoderMap) p := atomic.LoadPointer(&cachedDecoderMap)
return *(*map[uintptr]Decoder)(unsafe.Pointer(&p)) return *(*map[uintptr]Decoder)(unsafe.Pointer(&p))
} }
func storeDecoder(typ uintptr, dec Decoder, m map[uintptr]Decoder) { func storeDecoder(typ uintptr, dec Decoder, m map[uintptr]Decoder) {
initDecoder()
newDecoderMap := make(map[uintptr]Decoder, len(m)+1) newDecoderMap := make(map[uintptr]Decoder, len(m)+1)
newDecoderMap[typ] = dec newDecoderMap[typ] = dec

View File

@ -10,6 +10,7 @@ import (
) )
func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) { func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) {
initDecoder()
typeptr := uintptr(unsafe.Pointer(typ)) typeptr := uintptr(unsafe.Pointer(typ))
if typeptr > typeAddr.MaxTypeAddr { if typeptr > typeAddr.MaxTypeAddr {
return compileToGetDecoderSlowPath(typeptr, typ) return compileToGetDecoderSlowPath(typeptr, typ)

View File

@ -13,6 +13,7 @@ import (
var decMu sync.RWMutex var decMu sync.RWMutex
func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) { func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) {
initDecoder()
typeptr := uintptr(unsafe.Pointer(typ)) typeptr := uintptr(unsafe.Pointer(typ))
if typeptr > typeAddr.MaxTypeAddr { if typeptr > typeAddr.MaxTypeAddr {
return compileToGetDecoderSlowPath(typeptr, typ) return compileToGetDecoderSlowPath(typeptr, typ)

View File

@ -5,6 +5,7 @@ import (
"encoding" "encoding"
"encoding/json" "encoding/json"
"reflect" "reflect"
"sync"
"sync/atomic" "sync/atomic"
"unsafe" "unsafe"
@ -24,14 +25,17 @@ var (
cachedOpcodeSets []*OpcodeSet cachedOpcodeSets []*OpcodeSet
cachedOpcodeMap unsafe.Pointer // map[uintptr]*OpcodeSet cachedOpcodeMap unsafe.Pointer // map[uintptr]*OpcodeSet
typeAddr *runtime.TypeAddr typeAddr *runtime.TypeAddr
initEncoderOnce sync.Once
) )
func init() { func initEncoder() {
typeAddr = runtime.AnalyzeTypeAddr() initEncoderOnce.Do(func() {
if typeAddr == nil { typeAddr = runtime.AnalyzeTypeAddr()
typeAddr = &runtime.TypeAddr{} if typeAddr == nil {
} typeAddr = &runtime.TypeAddr{}
cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange>>typeAddr.AddrShift+1) }
cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange>>typeAddr.AddrShift+1)
})
} }
func loadOpcodeMap() map[uintptr]*OpcodeSet { func loadOpcodeMap() map[uintptr]*OpcodeSet {

View File

@ -4,6 +4,7 @@
package encoder package encoder
func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) { func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) {
initEncoder()
if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr { if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr {
codeSet, err := compileToGetCodeSetSlowPath(typeptr) codeSet, err := compileToGetCodeSetSlowPath(typeptr)
if err != nil { if err != nil {

View File

@ -10,6 +10,7 @@ import (
var setsMu sync.RWMutex var setsMu sync.RWMutex
func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) { func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) {
initEncoder()
if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr { if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr {
codeSet, err := compileToGetCodeSetSlowPath(typeptr) codeSet, err := compileToGetCodeSetSlowPath(typeptr)
if err != nil { if err != nil {