Switch to lazy init() in decoder and encoder

This will prevent go-json from consuming heap unless it is used.
This commit is contained in:
Todd Treece 2023-12-18 21:32:16 -05:00 committed by Todd Treece
parent 3e9769d637
commit f14426c40f
No known key found for this signature in database
GPG Key ID: 88EC0616B7F26C88
10 changed files with 918 additions and 863 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,12 +25,20 @@
package encoder
import (
"sync"
"unsafe"
)
var endianness int
var (
endianness int
initIntOnce sync.Once
intLELookup *[100]uint16
intBELookup *[100]uint16
intLookup [2]*[100]uint16
)
func init() {
func initInt() {
initIntOnce.Do(func() {
var b [2]byte
*(*uint16)(unsafe.Pointer(&b)) = uint16(0xABCD)
@ -42,10 +50,9 @@ func init() {
default:
panic("could not determine endianness")
}
}
// "00010203...96979899" cast to []uint16
var intLELookup = [100]uint16{
// "00010203...96979899" cast to []uint16
intLELookup = &[100]uint16{
0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930,
0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931,
0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932,
@ -56,9 +63,9 @@ var intLELookup = [100]uint16{
0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937,
0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938,
0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939,
}
}
var intBELookup = [100]uint16{
intBELookup = &[100]uint16{
0x3030, 0x3031, 0x3032, 0x3033, 0x3034, 0x3035, 0x3036, 0x3037, 0x3038, 0x3039,
0x3130, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3138, 0x3139,
0x3230, 0x3231, 0x3232, 0x3233, 0x3234, 0x3235, 0x3236, 0x3237, 0x3238, 0x3239,
@ -69,15 +76,19 @@ var intBELookup = [100]uint16{
0x3730, 0x3731, 0x3732, 0x3733, 0x3734, 0x3735, 0x3736, 0x3737, 0x3738, 0x3739,
0x3830, 0x3831, 0x3832, 0x3833, 0x3834, 0x3835, 0x3836, 0x3837, 0x3838, 0x3839,
0x3930, 0x3931, 0x3932, 0x3933, 0x3934, 0x3935, 0x3936, 0x3937, 0x3938, 0x3939,
}
}
var intLookup = [2]*[100]uint16{&intLELookup, &intBELookup}
intLookup = [2]*[100]uint16{intLELookup, intBELookup}
})
}
func numMask(numBitSize uint8) uint64 {
return 1<<numBitSize - 1
}
func AppendInt(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte {
initInt() // lazy init
var u64 uint64
switch code.NumBitSize {
case 8:
@ -132,6 +143,8 @@ func AppendInt(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte {
}
func AppendUint(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte {
initInt() // lazy init
var u64 uint64
switch code.NumBitSize {
case 8:

View File

@ -3,6 +3,7 @@ package encoder
import (
"strings"
"sync"
)
type CodeType int
@ -22,7 +23,14 @@ const (
CodeStructEnd CodeType = 11
)
var opTypeStrings = [400]string{
var (
opTypeStrings *[400]string
initOpTypeOnce sync.Once
)
func initOpType() {
initOpTypeOnce.Do(func() {
opTypeStrings = &[400]string{
"End",
"Interface",
"Ptr",
@ -423,6 +431,8 @@ var opTypeStrings = [400]string{
"StructFieldOmitEmpty",
"StructEnd",
"StructEndOmitEmpty",
}
})
}
type OpType uint16
@ -831,6 +841,8 @@ const (
)
func (t OpType) String() string {
initOpType() // lazy initialization
if int(t) >= 400 {
return ""
}

View File

@ -47,6 +47,8 @@ func stringToUint64Slice(s string) []uint64 {
}
func AppendString(ctx *RuntimeContext, buf []byte, s string) []byte {
initStringTable() // lazy initialization
if ctx.Option.Flag&HTMLEscapeOption != 0 {
if ctx.Option.Flag&NormalizeUTF8Option != 0 {
return appendNormalizedHTMLString(buf, s)

View File

@ -1,6 +1,18 @@
package encoder
var needEscapeHTMLNormalizeUTF8 = [256]bool{
import "sync"
var (
needEscapeHTMLNormalizeUTF8 *[256]bool
needEscapeNormalizeUTF8 *[256]bool
needEscapeHTML *[256]bool
needEscape *[256]bool
initStringTableOnce sync.Once
)
func initStringTable() {
initStringTableOnce.Do(func() {
needEscapeHTMLNormalizeUTF8 = &[256]bool{
'"': true,
'&': true,
'<': true,
@ -167,9 +179,9 @@ var needEscapeHTMLNormalizeUTF8 = [256]bool{
0xfd: true,
0xfe: true,
0xff: true,
}
}
var needEscapeNormalizeUTF8 = [256]bool{
needEscapeNormalizeUTF8 = &[256]bool{
'"': true,
'\\': true,
0x00: true,
@ -333,9 +345,9 @@ var needEscapeNormalizeUTF8 = [256]bool{
0xfd: true,
0xfe: true,
0xff: true,
}
}
var needEscapeHTML = [256]bool{
needEscapeHTML = &[256]bool{
'"': true,
'&': true,
'<': true,
@ -374,9 +386,9 @@ var needEscapeHTML = [256]bool{
0x1e: true,
0x1f: true,
/* 0x20 - 0xff */
}
}
var needEscape = [256]bool{
needEscape = &[256]bool{
'"': true,
'\\': true,
0x00: true,
@ -412,4 +424,6 @@ var needEscape = [256]bool{
0x1e: true,
0x1f: true,
/* 0x20 - 0xff */
}
})
}