mirror of https://github.com/goccy/go-json.git
113 lines
2.5 KiB
Go
113 lines
2.5 KiB
Go
package json
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sync/atomic"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
|
|
)
|
|
|
|
var (
|
|
cachedOpcodeSets []*opcodeSet
|
|
cachedOpcodeMap unsafe.Pointer // map[uintptr]*opcodeSet
|
|
existsCachedOpcodeSets bool
|
|
cachedDecoder []decoder
|
|
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
|
|
existsCachedDecoder bool
|
|
baseTypeAddr uintptr
|
|
)
|
|
|
|
//go:linkname typelinks reflect.typelinks
|
|
func typelinks() ([]unsafe.Pointer, [][]int32)
|
|
|
|
//go:linkname rtypeOff reflect.rtypeOff
|
|
func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer
|
|
|
|
func setupCodec() error {
|
|
sections, offsets := typelinks()
|
|
if len(sections) != 1 {
|
|
return fmt.Errorf("failed to get sections")
|
|
}
|
|
if len(offsets) != 1 {
|
|
return fmt.Errorf("failed to get offsets")
|
|
}
|
|
section := sections[0]
|
|
offset := offsets[0]
|
|
var (
|
|
min uintptr = uintptr(^uint(0))
|
|
max uintptr = 0
|
|
)
|
|
for i := 0; i < len(offset); i++ {
|
|
typ := (*rtype)(rtypeOff(section, offset[i]))
|
|
addr := uintptr(unsafe.Pointer(typ))
|
|
if min > addr {
|
|
min = addr
|
|
}
|
|
if max < addr {
|
|
max = addr
|
|
}
|
|
if typ.Kind() == reflect.Ptr {
|
|
addr = uintptr(unsafe.Pointer(typ.Elem()))
|
|
if min > addr {
|
|
min = addr
|
|
}
|
|
if max < addr {
|
|
max = addr
|
|
}
|
|
}
|
|
}
|
|
addrRange := max - min
|
|
if addrRange == 0 {
|
|
return fmt.Errorf("failed to get address range of types")
|
|
}
|
|
if addrRange > maxAcceptableTypeAddrRange {
|
|
return fmt.Errorf("too big address range %d", addrRange)
|
|
}
|
|
cachedOpcodeSets = make([]*opcodeSet, addrRange)
|
|
existsCachedOpcodeSets = true
|
|
cachedDecoder = make([]decoder, addrRange)
|
|
existsCachedDecoder = true
|
|
baseTypeAddr = min
|
|
return nil
|
|
}
|
|
|
|
func init() {
|
|
_ = setupCodec()
|
|
}
|
|
|
|
func loadOpcodeMap() map[uintptr]*opcodeSet {
|
|
p := atomic.LoadPointer(&cachedOpcodeMap)
|
|
return *(*map[uintptr]*opcodeSet)(unsafe.Pointer(&p))
|
|
}
|
|
|
|
func storeOpcodeSet(typ uintptr, set *opcodeSet, m map[uintptr]*opcodeSet) {
|
|
newOpcodeMap := make(map[uintptr]*opcodeSet, len(m)+1)
|
|
newOpcodeMap[typ] = set
|
|
|
|
for k, v := range m {
|
|
newOpcodeMap[k] = v
|
|
}
|
|
|
|
atomic.StorePointer(&cachedOpcodeMap, *(*unsafe.Pointer)(unsafe.Pointer(&newOpcodeMap)))
|
|
}
|
|
|
|
func loadDecoderMap() map[uintptr]decoder {
|
|
p := atomic.LoadPointer(&cachedDecoderMap)
|
|
return *(*map[uintptr]decoder)(unsafe.Pointer(&p))
|
|
}
|
|
|
|
func storeDecoder(typ uintptr, dec decoder, m map[uintptr]decoder) {
|
|
newDecoderMap := make(map[uintptr]decoder, len(m)+1)
|
|
newDecoderMap[typ] = dec
|
|
|
|
for k, v := range m {
|
|
newDecoderMap[k] = v
|
|
}
|
|
|
|
atomic.StorePointer(&cachedDecoderMap, *(*unsafe.Pointer)(unsafe.Pointer(&newDecoderMap)))
|
|
}
|