forked from mirror/go-json
Merge pull request #152 from goccy/feature/reduce-memory-usage-at-compile
Reduce memory usage at compile time
This commit is contained in:
commit
141e3992af
|
@ -42,7 +42,7 @@ jobs:
|
|||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: measure coverage
|
||||
run: go test -v -coverprofile=coverage.out ./ -count=1
|
||||
run: make cover
|
||||
- name: report coverage
|
||||
run: |
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
|
|
|
@ -62,6 +62,10 @@ issues:
|
|||
- path: rtype.go
|
||||
linters:
|
||||
- golint
|
||||
- stylecheck
|
||||
- path: error.go
|
||||
linters:
|
||||
- staticcheck
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-issues-per-linter: 0
|
||||
|
|
17
Makefile
17
Makefile
|
@ -1,13 +1,20 @@
|
|||
PKG := github.com/goccy/go-json
|
||||
|
||||
BIN_DIR := $(CURDIR)/bin
|
||||
PKGS := $(shell go list ./... | grep -v internal/cmd)
|
||||
COVER_PKGS := $(foreach pkg,$(PKGS),$(subst $(PKG),.,$(pkg)))
|
||||
|
||||
COMMA := ,
|
||||
EMPTY :=
|
||||
SPACE := $(EMPTY) $(EMPTY)
|
||||
COVERPKG_OPT := $(subst $(SPACE),$(COMMA),$(COVER_PKGS))
|
||||
|
||||
$(BIN_DIR):
|
||||
@mkdir -p $(BIN_DIR)
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
@ go test -coverprofile=cover.tmp.out . ; \
|
||||
cat cover.tmp.out | grep -v "encode_optype.go" > cover.out; \
|
||||
rm cover.tmp.out
|
||||
go test -coverpkg=$(COVERPKG_OPT) -coverprofile=cover.out .
|
||||
|
||||
.PHONY: cover-html
|
||||
cover-html: cover
|
||||
|
@ -26,3 +33,7 @@ golangci-lint: | $(BIN_DIR)
|
|||
GOBIN=$(BIN_DIR) go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.36.0; \
|
||||
rm -rf $$GOLANGCI_LINT_TMP_DIR; \
|
||||
}
|
||||
|
||||
.PHONY: generate
|
||||
generate:
|
||||
go generate ./internal/...
|
||||
|
|
19
codec.go
19
codec.go
|
@ -12,8 +12,6 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
cachedOpcodeSets []*opcodeSet
|
||||
cachedOpcodeMap unsafe.Pointer // map[uintptr]*opcodeSet
|
||||
cachedDecoder []decoder
|
||||
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
|
||||
baseTypeAddr uintptr
|
||||
|
@ -66,7 +64,6 @@ func setupCodec() error {
|
|||
if addrRange > maxAcceptableTypeAddrRange {
|
||||
return fmt.Errorf("too big address range %d", addrRange)
|
||||
}
|
||||
cachedOpcodeSets = make([]*opcodeSet, addrRange)
|
||||
cachedDecoder = make([]decoder, addrRange)
|
||||
baseTypeAddr = min
|
||||
maxTypeAddr = max
|
||||
|
@ -77,22 +74,6 @@ 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))
|
||||
|
|
|
@ -37,7 +37,7 @@ func unmarshal(data []byte, v interface{}) error {
|
|||
src := make([]byte, len(data)+1) // append nul byte to the end
|
||||
copy(src, data)
|
||||
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
|
||||
if err := validateType(header.typ, uintptr(header.ptr)); err != nil {
|
||||
return err
|
||||
|
@ -56,7 +56,7 @@ func unmarshalNoEscape(data []byte, v interface{}) error {
|
|||
src := make([]byte, len(data)+1) // append nul byte to the end
|
||||
copy(src, data)
|
||||
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
|
||||
if err := validateType(header.typ, uintptr(header.ptr)); err != nil {
|
||||
return err
|
||||
|
@ -129,7 +129,7 @@ func (d *Decoder) prepareForDecode() error {
|
|||
// See the documentation for Unmarshal for details about
|
||||
// the conversion of JSON into a Go value.
|
||||
func (d *Decoder) Decode(v interface{}) error {
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
typ := header.typ
|
||||
ptr := uintptr(header.ptr)
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
jsonNumberType = reflect.TypeOf(json.Number(""))
|
||||
)
|
||||
|
||||
func decodeCompileToGetDecoderSlowPath(typeptr uintptr, typ *rtype) (decoder, error) {
|
||||
|
@ -322,16 +329,16 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
|
|||
structName = typ.Name()
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
field := typ.Field(i)
|
||||
if isIgnoredStructField(field) {
|
||||
if runtime.IsIgnoredStructField(field) {
|
||||
continue
|
||||
}
|
||||
isUnexportedField := unicode.IsLower([]rune(field.Name)[0])
|
||||
tag := structTagFromField(field)
|
||||
tag := runtime.StructTagFromField(field)
|
||||
dec, err := decodeCompile(type2rtype(field.Type), structName, field.Name, structTypeToDecoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field.Anonymous && !tag.isTaggedKey {
|
||||
if field.Anonymous && !tag.IsTaggedKey {
|
||||
if stDec, ok := dec.(*structDecoder); ok {
|
||||
if type2rtype(field.Type) == typ {
|
||||
// recursive definition
|
||||
|
@ -409,19 +416,19 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if tag.isString {
|
||||
if tag.IsString {
|
||||
dec = newWrappedStringDecoder(type2rtype(field.Type), dec, structName, field.Name)
|
||||
}
|
||||
var key string
|
||||
if tag.key != "" {
|
||||
key = tag.key
|
||||
if tag.Key != "" {
|
||||
key = tag.Key
|
||||
} else {
|
||||
key = field.Name
|
||||
}
|
||||
fieldSet := &structFieldSet{
|
||||
dec: dec,
|
||||
offset: field.Offset,
|
||||
isTaggedKey: tag.isTaggedKey,
|
||||
isTaggedKey: tag.IsTaggedKey,
|
||||
key: key,
|
||||
keyLen: int64(len(key)),
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ func (d *floatDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
|
|||
str := *(*string)(unsafe.Pointer(&bytes))
|
||||
f64, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return &SyntaxError{msg: err.Error(), Offset: s.totalOffset()}
|
||||
return errSyntax(err.Error(), s.totalOffset())
|
||||
}
|
||||
d.op(p, f64)
|
||||
return nil
|
||||
|
@ -161,7 +161,7 @@ func (d *floatDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
|
|||
s := *(*string)(unsafe.Pointer(&bytes))
|
||||
f64, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return 0, &SyntaxError{msg: err.Error(), Offset: cursor}
|
||||
return 0, errSyntax(err.Error(), cursor)
|
||||
}
|
||||
d.op(p, f64)
|
||||
return cursor, nil
|
||||
|
|
|
@ -202,7 +202,7 @@ func (d *interfaceDecoder) decodeStreamEmptyInterface(s *stream, depth int64, p
|
|||
}
|
||||
|
||||
func (d *interfaceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
|
||||
runtimeInterfaceValue := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
runtimeInterfaceValue := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
|
@ -217,7 +217,7 @@ func (d *interfaceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer
|
|||
return d.errUnmarshalType(rv.Type(), s.totalOffset())
|
||||
}
|
||||
iface := rv.Interface()
|
||||
ifaceHeader := (*interfaceHeader)(unsafe.Pointer(&iface))
|
||||
ifaceHeader := (*emptyInterface)(unsafe.Pointer(&iface))
|
||||
typ := ifaceHeader.typ
|
||||
if ifaceHeader.ptr == nil || d.typ == typ || typ == nil {
|
||||
// concrete type is empty interface
|
||||
|
@ -252,7 +252,7 @@ func (d *interfaceDecoder) errUnmarshalType(typ reflect.Type, offset int64) *Unm
|
|||
}
|
||||
|
||||
func (d *interfaceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
runtimeInterfaceValue := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
runtimeInterfaceValue := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
|
@ -268,7 +268,7 @@ func (d *interfaceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Poin
|
|||
}
|
||||
|
||||
iface := rv.Interface()
|
||||
ifaceHeader := (*interfaceHeader)(unsafe.Pointer(&iface))
|
||||
ifaceHeader := (*emptyInterface)(unsafe.Pointer(&iface))
|
||||
typ := ifaceHeader.typ
|
||||
if ifaceHeader.ptr == nil || d.typ == typ || typ == nil {
|
||||
// concrete type is empty interface
|
||||
|
|
|
@ -27,7 +27,7 @@ func (d *numberDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) e
|
|||
return err
|
||||
}
|
||||
if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil {
|
||||
return &SyntaxError{msg: err.Error(), Offset: s.totalOffset()}
|
||||
return errSyntax(err.Error(), s.totalOffset())
|
||||
}
|
||||
d.op(p, Number(string(bytes)))
|
||||
s.reset()
|
||||
|
@ -40,7 +40,7 @@ func (d *numberDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer
|
|||
return 0, err
|
||||
}
|
||||
if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil {
|
||||
return 0, &SyntaxError{msg: err.Error(), Offset: c}
|
||||
return 0, errSyntax(err.Error(), c)
|
||||
}
|
||||
cursor = c
|
||||
s := *(*string)(unsafe.Pointer(&bytes))
|
||||
|
|
136
decode_test.go
136
decode_test.go
|
@ -1786,80 +1786,82 @@ func equalError(a, b error) bool {
|
|||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
for i, tt := range unmarshalTests {
|
||||
in := []byte(tt.in)
|
||||
if tt.ptr == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
typ := reflect.TypeOf(tt.ptr)
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
t.Errorf("#%d: unmarshalTest.ptr %T is not a pointer type", i, tt.ptr)
|
||||
continue
|
||||
}
|
||||
typ = typ.Elem()
|
||||
|
||||
// v = new(right-type)
|
||||
v := reflect.New(typ)
|
||||
|
||||
if !reflect.DeepEqual(tt.ptr, v.Interface()) {
|
||||
// There's no reason for ptr to point to non-zero data,
|
||||
// as we decode into new(right-type), so the data is
|
||||
// discarded.
|
||||
// This can easily mean tests that silently don't test
|
||||
// what they should. To test decoding into existing
|
||||
// data, see TestPrefilled.
|
||||
t.Errorf("#%d: unmarshalTest.ptr %#v is not a pointer to a zero value", i, tt.ptr)
|
||||
continue
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(in))
|
||||
if tt.useNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
if tt.disallowUnknownFields {
|
||||
dec.DisallowUnknownFields()
|
||||
}
|
||||
if err := dec.Decode(v.Interface()); !equalError(err, tt.err) {
|
||||
t.Errorf("#%d: %v, want %v", i, err, tt.err)
|
||||
continue
|
||||
} else if err != nil {
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {
|
||||
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out)
|
||||
data, _ := json.Marshal(v.Elem().Interface())
|
||||
println(string(data))
|
||||
data, _ = json.Marshal(tt.out)
|
||||
println(string(data))
|
||||
continue
|
||||
}
|
||||
|
||||
// Check round trip also decodes correctly.
|
||||
if tt.err == nil {
|
||||
enc, err := json.Marshal(v.Interface())
|
||||
if err != nil {
|
||||
t.Errorf("#%d: error re-marshaling: %v", i, err)
|
||||
continue
|
||||
t.Run(fmt.Sprintf("%d_%q", i, tt.in), func(t *testing.T) {
|
||||
in := []byte(tt.in)
|
||||
if tt.ptr == nil {
|
||||
return
|
||||
}
|
||||
if tt.golden && !bytes.Equal(enc, in) {
|
||||
t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, enc, in)
|
||||
|
||||
typ := reflect.TypeOf(tt.ptr)
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
t.Errorf("#%d: unmarshalTest.ptr %T is not a pointer type", i, tt.ptr)
|
||||
return
|
||||
}
|
||||
vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
|
||||
dec = json.NewDecoder(bytes.NewReader(enc))
|
||||
typ = typ.Elem()
|
||||
|
||||
// v = new(right-type)
|
||||
v := reflect.New(typ)
|
||||
|
||||
if !reflect.DeepEqual(tt.ptr, v.Interface()) {
|
||||
// There's no reason for ptr to point to non-zero data,
|
||||
// as we decode into new(right-type), so the data is
|
||||
// discarded.
|
||||
// This can easily mean tests that silently don't test
|
||||
// what they should. To test decoding into existing
|
||||
// data, see TestPrefilled.
|
||||
t.Errorf("#%d: unmarshalTest.ptr %#v is not a pointer to a zero value", i, tt.ptr)
|
||||
return
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(bytes.NewReader(in))
|
||||
if tt.useNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
if err := dec.Decode(vv.Interface()); err != nil {
|
||||
t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
|
||||
continue
|
||||
if tt.disallowUnknownFields {
|
||||
dec.DisallowUnknownFields()
|
||||
}
|
||||
if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
|
||||
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
|
||||
t.Errorf(" In: %q", strings.Map(noSpace, string(in)))
|
||||
t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
|
||||
continue
|
||||
if err := dec.Decode(v.Interface()); !equalError(err, tt.err) {
|
||||
t.Errorf("#%d: %v, want %v", i, err, tt.err)
|
||||
return
|
||||
} else if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {
|
||||
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out)
|
||||
data, _ := json.Marshal(v.Elem().Interface())
|
||||
println(string(data))
|
||||
data, _ = json.Marshal(tt.out)
|
||||
println(string(data))
|
||||
return
|
||||
}
|
||||
|
||||
// Check round trip also decodes correctly.
|
||||
if tt.err == nil {
|
||||
enc, err := json.Marshal(v.Interface())
|
||||
if err != nil {
|
||||
t.Errorf("#%d: error re-marshaling: %v", i, err)
|
||||
return
|
||||
}
|
||||
if tt.golden && !bytes.Equal(enc, in) {
|
||||
t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, enc, in)
|
||||
}
|
||||
vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
|
||||
dec = json.NewDecoder(bytes.NewReader(enc))
|
||||
if tt.useNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
if err := dec.Decode(vv.Interface()); err != nil {
|
||||
t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
|
||||
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
|
||||
t.Errorf(" In: %q", strings.Map(noSpace, string(in)))
|
||||
t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ func (d *unmarshalJSONDecoder) decodeStream(s *stream, depth int64, p unsafe.Poi
|
|||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
|
@ -60,7 +60,7 @@ func (d *unmarshalJSONDecoder) decode(buf []byte, cursor, depth int64, p unsafe.
|
|||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
|
|
|
@ -77,7 +77,7 @@ func (d *unmarshalTextDecoder) decodeStream(s *stream, depth int64, p unsafe.Poi
|
|||
if b, ok := unquoteBytes(dst); ok {
|
||||
dst = b
|
||||
}
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
|
@ -127,7 +127,7 @@ func (d *unmarshalTextDecoder) decode(buf []byte, cursor, depth int64, p unsafe.
|
|||
if s, ok := unquoteBytes(src); ok {
|
||||
src = s
|
||||
}
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
|
||||
}))
|
||||
|
|
|
@ -7,7 +7,7 @@ services:
|
|||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2048M
|
||||
memory: 600M
|
||||
working_dir: /go/src/go-json
|
||||
command: |
|
||||
sh -c "go test -c . && ls go-json.test"
|
||||
|
|
318
encode.go
318
encode.go
|
@ -1,17 +1,15 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/encoder/vm"
|
||||
"github.com/goccy/go-json/internal/encoder/vm_escaped"
|
||||
"github.com/goccy/go-json/internal/encoder/vm_escaped_indent"
|
||||
"github.com/goccy/go-json/internal/encoder/vm_indent"
|
||||
)
|
||||
|
||||
// An Encoder writes JSON values to an output stream.
|
||||
|
@ -38,20 +36,20 @@ const (
|
|||
var (
|
||||
encRuntimeContextPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &encodeRuntimeContext{
|
||||
buf: make([]byte, 0, bufSize),
|
||||
ptrs: make([]uintptr, 128),
|
||||
keepRefs: make([]unsafe.Pointer, 0, 8),
|
||||
return &encoder.RuntimeContext{
|
||||
Buf: make([]byte, 0, bufSize),
|
||||
Ptrs: make([]uintptr, 128),
|
||||
KeepRefs: make([]unsafe.Pointer, 0, 8),
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func takeEncodeRuntimeContext() *encodeRuntimeContext {
|
||||
return encRuntimeContextPool.Get().(*encodeRuntimeContext)
|
||||
func takeEncodeRuntimeContext() *encoder.RuntimeContext {
|
||||
return encRuntimeContextPool.Get().(*encoder.RuntimeContext)
|
||||
}
|
||||
|
||||
func releaseEncodeRuntimeContext(ctx *encodeRuntimeContext) {
|
||||
func releaseEncodeRuntimeContext(ctx *encoder.RuntimeContext) {
|
||||
encRuntimeContextPool.Put(ctx)
|
||||
}
|
||||
|
||||
|
@ -77,7 +75,7 @@ func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc)
|
|||
return err
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeWithOption(ctx *encodeRuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
|
||||
func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
|
||||
var opt EncodeOption
|
||||
if e.enabledHTMLEscape {
|
||||
opt |= EncodeOptionHTMLEscape
|
||||
|
@ -188,321 +186,103 @@ func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]by
|
|||
return copied, nil
|
||||
}
|
||||
|
||||
func encode(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
|
||||
b := ctx.buf[:0]
|
||||
func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
|
||||
b := ctx.Buf[:0]
|
||||
if v == nil {
|
||||
b = encodeNull(b)
|
||||
b = encodeComma(b)
|
||||
b = encoder.AppendNull(b)
|
||||
b = encoder.AppendComma(b)
|
||||
return b, nil
|
||||
}
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
typ := header.typ
|
||||
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
codeSet, err := encodeCompileToGetCodeSet(typeptr)
|
||||
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := uintptr(header.ptr)
|
||||
ctx.init(p, codeSet.codeLength)
|
||||
ctx.Init(p, codeSet.CodeLength)
|
||||
ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
|
||||
|
||||
buf, err := encodeRunCode(ctx, b, codeSet, opt)
|
||||
|
||||
ctx.keepRefs = append(ctx.keepRefs, header.ptr)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.buf = buf
|
||||
ctx.Buf = buf
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func encodeNoEscape(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
|
||||
b := ctx.buf[:0]
|
||||
func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
|
||||
b := ctx.Buf[:0]
|
||||
if v == nil {
|
||||
b = encodeNull(b)
|
||||
b = encodeComma(b)
|
||||
b = encoder.AppendNull(b)
|
||||
b = encoder.AppendComma(b)
|
||||
return b, nil
|
||||
}
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
typ := header.typ
|
||||
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
codeSet, err := encodeCompileToGetCodeSet(typeptr)
|
||||
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := uintptr(header.ptr)
|
||||
ctx.init(p, codeSet.codeLength)
|
||||
ctx.Init(p, codeSet.CodeLength)
|
||||
buf, err := encodeRunCode(ctx, b, codeSet, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.buf = buf
|
||||
ctx.Buf = buf
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func encodeIndent(ctx *encodeRuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||
b := ctx.buf[:0]
|
||||
func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||
b := ctx.Buf[:0]
|
||||
if v == nil {
|
||||
b = encodeNull(b)
|
||||
b = encodeIndentComma(b)
|
||||
b = encoder.AppendNull(b)
|
||||
b = encoder.AppendCommaIndent(b)
|
||||
return b, nil
|
||||
}
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
typ := header.typ
|
||||
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
codeSet, err := encodeCompileToGetCodeSet(typeptr)
|
||||
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := uintptr(header.ptr)
|
||||
ctx.init(p, codeSet.codeLength)
|
||||
ctx.Init(p, codeSet.CodeLength)
|
||||
buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent, opt)
|
||||
|
||||
ctx.keepRefs = append(ctx.keepRefs, header.ptr)
|
||||
ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.buf = buf
|
||||
ctx.Buf = buf
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func encodeRunCode(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt EncodeOption) ([]byte, error) {
|
||||
func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) {
|
||||
if (opt & EncodeOptionHTMLEscape) != 0 {
|
||||
return encodeRunEscaped(ctx, b, codeSet, opt)
|
||||
return vm_escaped.Run(ctx, b, codeSet, encoder.Option(opt))
|
||||
}
|
||||
return encodeRun(ctx, b, codeSet, opt)
|
||||
return vm.Run(ctx, b, codeSet, encoder.Option(opt))
|
||||
}
|
||||
|
||||
func encodeRunIndentCode(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||
ctx.prefix = []byte(prefix)
|
||||
ctx.indentStr = []byte(indent)
|
||||
func encodeRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||
ctx.Prefix = []byte(prefix)
|
||||
ctx.IndentStr = []byte(indent)
|
||||
if (opt & EncodeOptionHTMLEscape) != 0 {
|
||||
return encodeRunEscapedIndent(ctx, b, codeSet, opt)
|
||||
return vm_escaped_indent.Run(ctx, b, codeSet, encoder.Option(opt))
|
||||
}
|
||||
return encodeRunIndent(ctx, b, codeSet, opt)
|
||||
}
|
||||
|
||||
func encodeFloat32(b []byte, v float32) []byte {
|
||||
f64 := float64(v)
|
||||
abs := math.Abs(f64)
|
||||
fmt := byte('f')
|
||||
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
||||
if abs != 0 {
|
||||
f32 := float32(abs)
|
||||
if f32 < 1e-6 || f32 >= 1e21 {
|
||||
fmt = 'e'
|
||||
}
|
||||
}
|
||||
return strconv.AppendFloat(b, f64, fmt, -1, 32)
|
||||
}
|
||||
|
||||
func encodeFloat64(b []byte, v float64) []byte {
|
||||
abs := math.Abs(v)
|
||||
fmt := byte('f')
|
||||
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
||||
if abs != 0 {
|
||||
if abs < 1e-6 || abs >= 1e21 {
|
||||
fmt = 'e'
|
||||
}
|
||||
}
|
||||
return strconv.AppendFloat(b, v, fmt, -1, 64)
|
||||
}
|
||||
|
||||
func encodeBool(b []byte, v bool) []byte {
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
}
|
||||
|
||||
func encodeNull(b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func encodeComma(b []byte) []byte {
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func encodeIndentComma(b []byte) []byte {
|
||||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func appendStructEnd(b []byte) []byte {
|
||||
return append(b, '}', ',')
|
||||
}
|
||||
|
||||
func appendStructEndIndent(ctx *encodeRuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, '\n')
|
||||
b = append(b, ctx.prefix...)
|
||||
b = append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func encodeByteSlice(b []byte, src []byte) []byte {
|
||||
encodedLen := base64.StdEncoding.EncodedLen(len(src))
|
||||
b = append(b, '"')
|
||||
pos := len(b)
|
||||
remainLen := cap(b[pos:])
|
||||
var buf []byte
|
||||
if remainLen > encodedLen {
|
||||
buf = b[pos : pos+encodedLen]
|
||||
} else {
|
||||
buf = make([]byte, encodedLen)
|
||||
}
|
||||
base64.StdEncoding.Encode(buf, src)
|
||||
return append(append(b, buf...), '"')
|
||||
}
|
||||
|
||||
func encodeNumber(b []byte, n Number) ([]byte, error) {
|
||||
if len(n) == 0 {
|
||||
return append(b, '0'), nil
|
||||
}
|
||||
for i := 0; i < len(n); i++ {
|
||||
if !floatTable[n[i]] {
|
||||
return nil, fmt.Errorf("json: invalid number literal %q", n)
|
||||
}
|
||||
}
|
||||
b = append(b, n...)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func appendIndent(ctx *encodeRuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, ctx.prefix...)
|
||||
return append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
|
||||
}
|
||||
|
||||
func encodeIsNilForMarshaler(v interface{}) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Interface, reflect.Map, reflect.Ptr:
|
||||
return rv.IsNil()
|
||||
case reflect.Slice:
|
||||
return rv.IsNil() || rv.Len() == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func encodeMarshalJSON(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.addrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(Marshaler)
|
||||
if !ok {
|
||||
return encodeNull(b), nil
|
||||
}
|
||||
bb, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
buf := bytes.NewBuffer(b)
|
||||
//TODO: we should validate buffer with `compact`
|
||||
if err := compact(buf, bb, escape); err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func encodeMarshalJSONIndent(ctx *encodeRuntimeContext, code *opcode, b []byte, v interface{}, indent int, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.addrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(Marshaler)
|
||||
if !ok {
|
||||
return encodeNull(b), nil
|
||||
}
|
||||
bb, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
var compactBuf bytes.Buffer
|
||||
if err := compact(&compactBuf, bb, escape); err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
var indentBuf bytes.Buffer
|
||||
if err := encodeWithIndent(
|
||||
&indentBuf,
|
||||
compactBuf.Bytes(),
|
||||
string(ctx.prefix)+strings.Repeat(string(ctx.indentStr), ctx.baseIndent+indent+1),
|
||||
string(ctx.indentStr),
|
||||
); err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
return append(b, indentBuf.Bytes()...), nil
|
||||
}
|
||||
|
||||
func encodeMarshalText(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.addrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
return encodeNull(b), nil
|
||||
}
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
if escape {
|
||||
return encodeEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
return encodeNoEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
|
||||
func encodeMarshalTextIndent(code *opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.addrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
return encodeNull(b), nil
|
||||
}
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
if escape {
|
||||
return encodeEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
return encodeNoEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
return vm_indent.Run(ctx, b, codeSet, encoder.Option(opt))
|
||||
}
|
||||
|
|
1424
encode_compile.go
1424
encode_compile.go
File diff suppressed because it is too large
Load Diff
|
@ -1,34 +0,0 @@
|
|||
// +build !race
|
||||
|
||||
package json
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
||||
if typeptr > maxTypeAddr {
|
||||
return encodeCompileToGetCodeSetSlowPath(typeptr)
|
||||
}
|
||||
index := typeptr - baseTypeAddr
|
||||
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
|
||||
return codeSet, nil
|
||||
}
|
||||
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
|
||||
|
||||
code, err := encodeCompileHead(&encodeCompileContext{
|
||||
typ: copiedType,
|
||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code = copyOpcode(code)
|
||||
codeLength := code.totalLength()
|
||||
codeSet := &opcodeSet{
|
||||
code: code,
|
||||
codeLength: codeLength,
|
||||
}
|
||||
cachedOpcodeSets[index] = codeSet
|
||||
return codeSet, nil
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// +build race
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var setsMu sync.RWMutex
|
||||
|
||||
func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
||||
if typeptr > maxTypeAddr {
|
||||
return encodeCompileToGetCodeSetSlowPath(typeptr)
|
||||
}
|
||||
index := typeptr - baseTypeAddr
|
||||
setsMu.RLock()
|
||||
if codeSet := cachedOpcodeSets[index]; 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,
|
||||
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[index] = codeSet
|
||||
setsMu.Unlock()
|
||||
return codeSet, nil
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type mapItem struct {
|
||||
key []byte
|
||||
value []byte
|
||||
}
|
||||
|
||||
type mapslice struct {
|
||||
items []mapItem
|
||||
}
|
||||
|
||||
func (m *mapslice) Len() int {
|
||||
return len(m.items)
|
||||
}
|
||||
|
||||
func (m *mapslice) Less(i, j int) bool {
|
||||
return bytes.Compare(m.items[i].key, m.items[j].key) < 0
|
||||
}
|
||||
|
||||
func (m *mapslice) Swap(i, j int) {
|
||||
m.items[i], m.items[j] = m.items[j], m.items[i]
|
||||
}
|
||||
|
||||
type encodeMapContext struct {
|
||||
pos []int
|
||||
slice *mapslice
|
||||
buf []byte
|
||||
}
|
||||
|
||||
var mapContextPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &encodeMapContext{}
|
||||
},
|
||||
}
|
||||
|
||||
func newMapContext(mapLen int) *encodeMapContext {
|
||||
ctx := mapContextPool.Get().(*encodeMapContext)
|
||||
if ctx.slice == nil {
|
||||
ctx.slice = &mapslice{
|
||||
items: make([]mapItem, 0, mapLen),
|
||||
}
|
||||
}
|
||||
if cap(ctx.pos) < (mapLen*2 + 1) {
|
||||
ctx.pos = make([]int, 0, mapLen*2+1)
|
||||
ctx.slice.items = make([]mapItem, 0, mapLen)
|
||||
} else {
|
||||
ctx.pos = ctx.pos[:0]
|
||||
ctx.slice.items = ctx.slice.items[:0]
|
||||
}
|
||||
ctx.buf = ctx.buf[:0]
|
||||
return ctx
|
||||
}
|
||||
|
||||
func releaseMapContext(c *encodeMapContext) {
|
||||
mapContextPool.Put(c)
|
||||
}
|
||||
|
||||
type encodeCompileContext struct {
|
||||
typ *rtype
|
||||
opcodeIndex int
|
||||
ptrIndex int
|
||||
indent int
|
||||
structTypeToCompiledCode map[uintptr]*compiledCode
|
||||
|
||||
parent *encodeCompileContext
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) context() *encodeCompileContext {
|
||||
return &encodeCompileContext{
|
||||
typ: c.typ,
|
||||
opcodeIndex: c.opcodeIndex,
|
||||
ptrIndex: c.ptrIndex,
|
||||
indent: c.indent,
|
||||
structTypeToCompiledCode: c.structTypeToCompiledCode,
|
||||
parent: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) withType(typ *rtype) *encodeCompileContext {
|
||||
ctx := c.context()
|
||||
ctx.typ = typ
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) incIndent() *encodeCompileContext {
|
||||
ctx := c.context()
|
||||
ctx.indent++
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) decIndent() *encodeCompileContext {
|
||||
ctx := c.context()
|
||||
ctx.indent--
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) incIndex() {
|
||||
c.incOpcodeIndex()
|
||||
c.incPtrIndex()
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) decIndex() {
|
||||
c.decOpcodeIndex()
|
||||
c.decPtrIndex()
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) incOpcodeIndex() {
|
||||
c.opcodeIndex++
|
||||
if c.parent != nil {
|
||||
c.parent.incOpcodeIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) decOpcodeIndex() {
|
||||
c.opcodeIndex--
|
||||
if c.parent != nil {
|
||||
c.parent.decOpcodeIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) incPtrIndex() {
|
||||
c.ptrIndex++
|
||||
if c.parent != nil {
|
||||
c.parent.incPtrIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *encodeCompileContext) decPtrIndex() {
|
||||
c.ptrIndex--
|
||||
if c.parent != nil {
|
||||
c.parent.decPtrIndex()
|
||||
}
|
||||
}
|
||||
|
||||
type encodeRuntimeContext struct {
|
||||
buf []byte
|
||||
ptrs []uintptr
|
||||
keepRefs []unsafe.Pointer
|
||||
seenPtr []uintptr
|
||||
baseIndent int
|
||||
prefix []byte
|
||||
indentStr []byte
|
||||
}
|
||||
|
||||
func (c *encodeRuntimeContext) init(p uintptr, codelen int) {
|
||||
if len(c.ptrs) < codelen {
|
||||
c.ptrs = make([]uintptr, codelen)
|
||||
}
|
||||
c.ptrs[0] = p
|
||||
c.keepRefs = c.keepRefs[:0]
|
||||
c.seenPtr = c.seenPtr[:0]
|
||||
c.baseIndent = 0
|
||||
}
|
||||
|
||||
func (c *encodeRuntimeContext) ptr() uintptr {
|
||||
header := (*sliceHeader)(unsafe.Pointer(&c.ptrs))
|
||||
return uintptr(header.data)
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
// +build !go1.13
|
||||
|
||||
package json
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:linkname mapitervalue reflect.mapitervalue
|
||||
func mapitervalue(it unsafe.Pointer) unsafe.Pointer
|
|
@ -1,8 +0,0 @@
|
|||
// +build go1.13
|
||||
|
||||
package json
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:linkname mapitervalue reflect.mapiterelem
|
||||
func mapitervalue(it unsafe.Pointer) unsafe.Pointer
|
512
encode_opcode.go
512
encode_opcode.go
|
@ -1,512 +0,0 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const uintptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
|
||||
|
||||
type opcode struct {
|
||||
op opType // operation type
|
||||
typ *rtype // go type
|
||||
displayIdx int // opcode index
|
||||
key []byte // struct field key
|
||||
escapedKey []byte // struct field key ( HTML escaped )
|
||||
ptrNum int // pointer number: e.g. double pointer is 2.
|
||||
displayKey string // key text to display
|
||||
isTaggedKey bool // whether tagged key
|
||||
anonymousKey bool // whether anonymous key
|
||||
anonymousHead bool // whether anonymous head or not
|
||||
indirect bool // whether indirect or not
|
||||
nilcheck bool // whether needs to nilcheck or not
|
||||
addrForMarshaler bool // whether needs to addr for marshaler or not
|
||||
rshiftNum uint8 // use to take bit for judging whether negative integer or not
|
||||
mask uint64 // mask for number
|
||||
indent int // indent number
|
||||
|
||||
idx uintptr // offset to access ptr
|
||||
headIdx uintptr // offset to access slice/struct head
|
||||
elemIdx uintptr // offset to access array/slice/map elem
|
||||
length uintptr // offset to access slice/map length or array length
|
||||
mapIter uintptr // offset to access map iterator
|
||||
mapPos uintptr // offset to access position list for sorted map
|
||||
offset uintptr // offset size from struct header
|
||||
size uintptr // array/slice elem size
|
||||
|
||||
mapKey *opcode // map key
|
||||
mapValue *opcode // map value
|
||||
elem *opcode // array/slice elem
|
||||
end *opcode // array/slice/struct/map end
|
||||
prevField *opcode // prev struct field
|
||||
nextField *opcode // next struct field
|
||||
next *opcode // next opcode
|
||||
jmp *compiledCode // for recursive call
|
||||
}
|
||||
|
||||
func newOpCode(ctx *encodeCompileContext, op opType) *opcode {
|
||||
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
|
||||
}
|
||||
|
||||
func opcodeOffset(idx int) uintptr {
|
||||
return uintptr(idx) * uintptrSize
|
||||
}
|
||||
|
||||
func copyOpcode(code *opcode) *opcode {
|
||||
codeMap := map[uintptr]*opcode{}
|
||||
return code.copy(codeMap)
|
||||
}
|
||||
|
||||
func newOpCodeWithNext(ctx *encodeCompileContext, op opType, next *opcode) *opcode {
|
||||
return &opcode{
|
||||
op: op,
|
||||
typ: ctx.typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
indent: ctx.indent,
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
next: next,
|
||||
}
|
||||
}
|
||||
|
||||
func newEndOp(ctx *encodeCompileContext) *opcode {
|
||||
return newOpCodeWithNext(ctx, opEnd, nil)
|
||||
}
|
||||
|
||||
func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
addr := uintptr(unsafe.Pointer(c))
|
||||
if code, exists := codeMap[addr]; exists {
|
||||
return code
|
||||
}
|
||||
copied := &opcode{
|
||||
op: c.op,
|
||||
typ: c.typ,
|
||||
displayIdx: c.displayIdx,
|
||||
key: c.key,
|
||||
escapedKey: c.escapedKey,
|
||||
displayKey: c.displayKey,
|
||||
ptrNum: c.ptrNum,
|
||||
mask: c.mask,
|
||||
rshiftNum: c.rshiftNum,
|
||||
isTaggedKey: c.isTaggedKey,
|
||||
anonymousKey: c.anonymousKey,
|
||||
anonymousHead: c.anonymousHead,
|
||||
indirect: c.indirect,
|
||||
nilcheck: c.nilcheck,
|
||||
addrForMarshaler: c.addrForMarshaler,
|
||||
indent: c.indent,
|
||||
idx: c.idx,
|
||||
headIdx: c.headIdx,
|
||||
elemIdx: c.elemIdx,
|
||||
length: c.length,
|
||||
mapIter: c.mapIter,
|
||||
mapPos: c.mapPos,
|
||||
offset: c.offset,
|
||||
size: c.size,
|
||||
}
|
||||
codeMap[addr] = copied
|
||||
copied.mapKey = c.mapKey.copy(codeMap)
|
||||
copied.mapValue = c.mapValue.copy(codeMap)
|
||||
copied.elem = c.elem.copy(codeMap)
|
||||
copied.end = c.end.copy(codeMap)
|
||||
copied.prevField = c.prevField.copy(codeMap)
|
||||
copied.nextField = c.nextField.copy(codeMap)
|
||||
copied.next = c.next.copy(codeMap)
|
||||
copied.jmp = c.jmp
|
||||
return copied
|
||||
}
|
||||
|
||||
func (c *opcode) beforeLastCode() *opcode {
|
||||
code := c
|
||||
for {
|
||||
var nextCode *opcode
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem, codeSliceElem, codeMapKey:
|
||||
nextCode = code.end
|
||||
default:
|
||||
nextCode = code.next
|
||||
}
|
||||
if nextCode.op == opEnd {
|
||||
return code
|
||||
}
|
||||
code = nextCode
|
||||
}
|
||||
}
|
||||
|
||||
func (c *opcode) totalLength() int {
|
||||
var idx int
|
||||
for code := c; code.op != opEnd; {
|
||||
idx = int(code.idx / uintptrSize)
|
||||
if code.op == opStructFieldRecursiveEnd {
|
||||
break
|
||||
}
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem, codeSliceElem, codeMapKey:
|
||||
code = code.end
|
||||
default:
|
||||
code = code.next
|
||||
}
|
||||
}
|
||||
return idx + 2 // opEnd + 1
|
||||
}
|
||||
|
||||
func (c *opcode) decOpcodeIndex() {
|
||||
for code := c; code.op != opEnd; {
|
||||
code.displayIdx--
|
||||
code.idx -= uintptrSize
|
||||
if code.headIdx > 0 {
|
||||
code.headIdx -= uintptrSize
|
||||
}
|
||||
if code.elemIdx > 0 {
|
||||
code.elemIdx -= uintptrSize
|
||||
}
|
||||
if code.mapIter > 0 {
|
||||
code.mapIter -= uintptrSize
|
||||
}
|
||||
if code.length > 0 && code.op.codeType() != codeArrayHead && code.op.codeType() != codeArrayElem {
|
||||
code.length -= uintptrSize
|
||||
}
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem, codeSliceElem, codeMapKey:
|
||||
code = code.end
|
||||
default:
|
||||
code = code.next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *opcode) decIndent() {
|
||||
for code := c; code.op != opEnd; {
|
||||
code.indent--
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem, codeSliceElem, codeMapKey:
|
||||
code = code.end
|
||||
default:
|
||||
code = code.next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *opcode) dumpHead(code *opcode) string {
|
||||
var length uintptr
|
||||
if code.op.codeType() == codeArrayHead {
|
||||
length = code.length
|
||||
} else {
|
||||
length = code.length / uintptrSize
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
code.headIdx/uintptrSize,
|
||||
code.elemIdx/uintptrSize,
|
||||
length,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *opcode) dumpMapHead(code *opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
code.headIdx/uintptrSize,
|
||||
code.elemIdx/uintptrSize,
|
||||
code.length/uintptrSize,
|
||||
code.mapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *opcode) dumpMapEnd(code *opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
code.mapPos/uintptrSize,
|
||||
code.length/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *opcode) dumpElem(code *opcode) string {
|
||||
var length uintptr
|
||||
if code.op.codeType() == codeArrayElem {
|
||||
length = code.length
|
||||
} else {
|
||||
length = code.length / uintptrSize
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][size:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
code.headIdx/uintptrSize,
|
||||
code.elemIdx/uintptrSize,
|
||||
length,
|
||||
code.size,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *opcode) dumpField(code *opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][key:%s][offset:%d][headIdx:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
code.displayKey,
|
||||
code.offset,
|
||||
code.headIdx/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *opcode) dumpKey(code *opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
code.elemIdx/uintptrSize,
|
||||
code.length/uintptrSize,
|
||||
code.mapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *opcode) dumpValue(code *opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][mapIter:%d])`,
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
code.mapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *opcode) dump() string {
|
||||
codes := []string{}
|
||||
for code := c; code.op != opEnd; {
|
||||
switch code.op.codeType() {
|
||||
case codeSliceHead:
|
||||
codes = append(codes, c.dumpHead(code))
|
||||
code = code.next
|
||||
case codeMapHead:
|
||||
codes = append(codes, c.dumpMapHead(code))
|
||||
code = code.next
|
||||
case codeArrayElem, codeSliceElem:
|
||||
codes = append(codes, c.dumpElem(code))
|
||||
code = code.end
|
||||
case codeMapKey:
|
||||
codes = append(codes, c.dumpKey(code))
|
||||
code = code.end
|
||||
case codeMapValue:
|
||||
codes = append(codes, c.dumpValue(code))
|
||||
code = code.next
|
||||
case codeMapEnd:
|
||||
codes = append(codes, c.dumpMapEnd(code))
|
||||
code = code.next
|
||||
case codeStructField:
|
||||
codes = append(codes, c.dumpField(code))
|
||||
code = code.next
|
||||
case codeStructEnd:
|
||||
codes = append(codes, c.dumpField(code))
|
||||
code = code.next
|
||||
default:
|
||||
codes = append(codes, fmt.Sprintf(
|
||||
"[%d]%s%s ([idx:%d])",
|
||||
code.displayIdx,
|
||||
strings.Repeat("-", code.indent),
|
||||
code.op,
|
||||
code.idx/uintptrSize,
|
||||
))
|
||||
code = code.next
|
||||
}
|
||||
}
|
||||
return strings.Join(codes, "\n")
|
||||
}
|
||||
|
||||
func prevField(code *opcode, removedFields map[*opcode]struct{}) *opcode {
|
||||
if _, exists := removedFields[code]; exists {
|
||||
return prevField(code.prevField, removedFields)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func nextField(code *opcode, removedFields map[*opcode]struct{}) *opcode {
|
||||
if _, exists := removedFields[code]; exists {
|
||||
return nextField(code.nextField, removedFields)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func encodeLinkPrevToNextField(cur *opcode, removedFields map[*opcode]struct{}) {
|
||||
prev := prevField(cur.prevField, removedFields)
|
||||
prev.nextField = nextField(cur.nextField, removedFields)
|
||||
code := prev
|
||||
fcode := cur
|
||||
for {
|
||||
var nextCode *opcode
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem, codeSliceElem, codeMapKey:
|
||||
nextCode = code.end
|
||||
default:
|
||||
nextCode = code.next
|
||||
}
|
||||
if nextCode == fcode {
|
||||
code.next = fcode.next
|
||||
break
|
||||
} else if nextCode.op == opEnd {
|
||||
break
|
||||
}
|
||||
code = nextCode
|
||||
}
|
||||
}
|
||||
|
||||
func newSliceHeaderCode(ctx *encodeCompileContext) *opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
length := opcodeOffset(ctx.ptrIndex)
|
||||
return &opcode{
|
||||
op: opSlice,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: idx,
|
||||
headIdx: idx,
|
||||
elemIdx: elemIdx,
|
||||
length: length,
|
||||
indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newSliceElemCode(ctx *encodeCompileContext, head *opcode, size uintptr) *opcode {
|
||||
return &opcode{
|
||||
op: opSliceElem,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
headIdx: head.idx,
|
||||
elemIdx: head.elemIdx,
|
||||
length: head.length,
|
||||
indent: ctx.indent,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func newArrayHeaderCode(ctx *encodeCompileContext, alen int) *opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
return &opcode{
|
||||
op: opArray,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: idx,
|
||||
headIdx: idx,
|
||||
elemIdx: elemIdx,
|
||||
indent: ctx.indent,
|
||||
length: uintptr(alen),
|
||||
}
|
||||
}
|
||||
|
||||
func newArrayElemCode(ctx *encodeCompileContext, head *opcode, length int, size uintptr) *opcode {
|
||||
return &opcode{
|
||||
op: opArrayElem,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
elemIdx: head.elemIdx,
|
||||
headIdx: head.headIdx,
|
||||
length: uintptr(length),
|
||||
indent: ctx.indent,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapHeaderCode(ctx *encodeCompileContext) *opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
length := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
mapIter := opcodeOffset(ctx.ptrIndex)
|
||||
return &opcode{
|
||||
op: opMap,
|
||||
typ: ctx.typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: idx,
|
||||
elemIdx: elemIdx,
|
||||
length: length,
|
||||
mapIter: mapIter,
|
||||
indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||
return &opcode{
|
||||
op: opMapKey,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
elemIdx: head.elemIdx,
|
||||
length: head.length,
|
||||
mapIter: head.mapIter,
|
||||
indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||
return &opcode{
|
||||
op: opMapValue,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
elemIdx: head.elemIdx,
|
||||
length: head.length,
|
||||
mapIter: head.mapIter,
|
||||
indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapEndCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||
mapPos := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
return &opcode{
|
||||
op: opMapEnd,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: idx,
|
||||
length: head.length,
|
||||
mapPos: mapPos,
|
||||
indent: ctx.indent,
|
||||
next: newEndOp(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func newInterfaceCode(ctx *encodeCompileContext) *opcode {
|
||||
return &opcode{
|
||||
op: opInterface,
|
||||
typ: ctx.typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
indent: ctx.indent,
|
||||
next: newEndOp(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func newRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
|
||||
return &opcode{
|
||||
op: opStructFieldRecursive,
|
||||
typ: ctx.typ,
|
||||
displayIdx: ctx.opcodeIndex,
|
||||
idx: opcodeOffset(ctx.ptrIndex),
|
||||
indent: ctx.indent,
|
||||
next: newEndOp(ctx),
|
||||
jmp: jmp,
|
||||
}
|
||||
}
|
1186
encode_optype.go
1186
encode_optype.go
File diff suppressed because it is too large
Load Diff
4116
encode_vm.go
4116
encode_vm.go
File diff suppressed because it is too large
Load Diff
4091
encode_vm_escaped.go
4091
encode_vm_escaped.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4567
encode_vm_indent.go
4567
encode_vm_indent.go
File diff suppressed because it is too large
Load Diff
140
error.go
140
error.go
|
@ -1,9 +1,7 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
// Before Go 1.2, an InvalidUTF8Error was returned by Marshal when
|
||||
|
@ -12,142 +10,40 @@ import (
|
|||
// replacing invalid bytes with the Unicode replacement rune U+FFFD.
|
||||
//
|
||||
// Deprecated: No longer used; kept for compatibility.
|
||||
type InvalidUTF8Error struct {
|
||||
S string // the whole string value that caused the error
|
||||
}
|
||||
|
||||
func (e *InvalidUTF8Error) Error() string {
|
||||
return fmt.Sprintf("json: invalid UTF-8 in string: %s", strconv.Quote(e.S))
|
||||
}
|
||||
type InvalidUTF8Error = errors.InvalidUTF8Error
|
||||
|
||||
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
|
||||
// (The argument to Unmarshal must be a non-nil pointer.)
|
||||
type InvalidUnmarshalError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *InvalidUnmarshalError) Error() string {
|
||||
if e.Type == nil {
|
||||
return "json: Unmarshal(nil)"
|
||||
}
|
||||
|
||||
if e.Type.Kind() != reflect.Ptr {
|
||||
return fmt.Sprintf("json: Unmarshal(non-pointer %s)", e.Type)
|
||||
}
|
||||
return fmt.Sprintf("json: Unmarshal(nil %s)", e.Type)
|
||||
}
|
||||
type InvalidUnmarshalError = errors.InvalidUnmarshalError
|
||||
|
||||
// A MarshalerError represents an error from calling a MarshalJSON or MarshalText method.
|
||||
type MarshalerError struct {
|
||||
Type reflect.Type
|
||||
Err error
|
||||
sourceFunc string
|
||||
}
|
||||
|
||||
func (e *MarshalerError) Error() string {
|
||||
srcFunc := e.sourceFunc
|
||||
if srcFunc == "" {
|
||||
srcFunc = "MarshalJSON"
|
||||
}
|
||||
return fmt.Sprintf("json: error calling %s for type %s: %s", srcFunc, e.Type, e.Err.Error())
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error.
|
||||
func (e *MarshalerError) Unwrap() error { return e.Err }
|
||||
type MarshalerError = errors.MarshalerError
|
||||
|
||||
// A SyntaxError is a description of a JSON syntax error.
|
||||
type SyntaxError struct {
|
||||
msg string // description of error
|
||||
Offset int64 // error occurred after reading Offset bytes
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string { return e.msg }
|
||||
type SyntaxError = errors.SyntaxError
|
||||
|
||||
// An UnmarshalFieldError describes a JSON object key that
|
||||
// led to an unexported (and therefore unwritable) struct field.
|
||||
//
|
||||
// Deprecated: No longer used; kept for compatibility.
|
||||
type UnmarshalFieldError struct {
|
||||
Key string
|
||||
Type reflect.Type
|
||||
Field reflect.StructField
|
||||
}
|
||||
|
||||
func (e *UnmarshalFieldError) Error() string {
|
||||
return fmt.Sprintf("json: cannot unmarshal object key %s into unexported field %s of type %s",
|
||||
strconv.Quote(e.Key), e.Field.Name, e.Type.String(),
|
||||
)
|
||||
}
|
||||
type UnmarshalFieldError = errors.UnmarshalFieldError
|
||||
|
||||
// An UnmarshalTypeError describes a JSON value that was
|
||||
// not appropriate for a value of a specific Go type.
|
||||
type UnmarshalTypeError struct {
|
||||
Value string // description of JSON value - "bool", "array", "number -5"
|
||||
Type reflect.Type // type of Go value it could not be assigned to
|
||||
Offset int64 // error occurred after reading Offset bytes
|
||||
Struct string // name of the struct type containing the field
|
||||
Field string // the full path from root node to the field
|
||||
}
|
||||
|
||||
func (e *UnmarshalTypeError) Error() string {
|
||||
if e.Struct != "" || e.Field != "" {
|
||||
return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s.%s of type %s",
|
||||
e.Value, e.Struct, e.Field, e.Type,
|
||||
)
|
||||
}
|
||||
return fmt.Sprintf("json: cannot unmarshal %s into Go value of type %s", e.Value, e.Type)
|
||||
}
|
||||
type UnmarshalTypeError = errors.UnmarshalTypeError
|
||||
|
||||
// An UnsupportedTypeError is returned by Marshal when attempting
|
||||
// to encode an unsupported value type.
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
type UnsupportedTypeError = errors.UnsupportedTypeError
|
||||
|
||||
func (e *UnsupportedTypeError) Error() string {
|
||||
return fmt.Sprintf("json: unsupported type: %s", e.Type)
|
||||
}
|
||||
type UnsupportedValueError = errors.UnsupportedValueError
|
||||
|
||||
type UnsupportedValueError struct {
|
||||
Value reflect.Value
|
||||
Str string
|
||||
}
|
||||
|
||||
func (e *UnsupportedValueError) Error() string {
|
||||
return fmt.Sprintf("json: unsupported value: %s", e.Str)
|
||||
}
|
||||
|
||||
func errExceededMaxDepth(c byte, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf(`invalid character "%c" exceeded max depth`, c),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
|
||||
func errNotAtBeginningOfValue(cursor int64) *SyntaxError {
|
||||
return &SyntaxError{msg: "not at beginning of value", Offset: cursor}
|
||||
}
|
||||
|
||||
func errUnexpectedEndOfJSON(msg string, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("json: %s unexpected end of JSON input", msg),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
|
||||
func errExpected(msg string, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{msg: fmt.Sprintf("expected %s", msg), Offset: cursor}
|
||||
}
|
||||
|
||||
func errInvalidCharacter(c byte, context string, cursor int64) *SyntaxError {
|
||||
if c == 0 {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("json: invalid character as %s", context),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("json: invalid character %c as %s", c, context),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
var (
|
||||
errExceededMaxDepth = errors.ErrExceededMaxDepth
|
||||
errNotAtBeginningOfValue = errors.ErrNotAtBeginningOfValue
|
||||
errUnexpectedEndOfJSON = errors.ErrUnexpectedEndOfJSON
|
||||
errExpected = errors.ErrExpected
|
||||
errInvalidCharacter = errors.ErrInvalidCharacter
|
||||
errSyntax = errors.ErrSyntax
|
||||
errMarshaler = errors.ErrMarshaler
|
||||
)
|
||||
|
|
|
@ -1,18 +1,6 @@
|
|||
package json
|
||||
|
||||
import "reflect"
|
||||
|
||||
func NewSyntaxError(msg string, offset int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: msg,
|
||||
Offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMarshalerError(typ reflect.Type, err error, msg string) *MarshalerError {
|
||||
return &MarshalerError{
|
||||
Type: typ,
|
||||
Err: err,
|
||||
sourceFunc: msg,
|
||||
}
|
||||
}
|
||||
var (
|
||||
NewSyntaxError = errSyntax
|
||||
NewMarshalerError = errMarshaler
|
||||
)
|
||||
|
|
|
@ -66,18 +66,18 @@ func createOpType(op, code string) opType {
|
|||
}
|
||||
|
||||
func _main() error {
|
||||
tmpl, err := template.New("").Parse(`// Code generated by cmd/generator. DO NOT EDIT!
|
||||
package json
|
||||
tmpl, err := template.New("").Parse(`// Code generated by internal/cmd/generator. DO NOT EDIT!
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type codeType int
|
||||
type CodeType int
|
||||
|
||||
const (
|
||||
{{- range $index, $type := .CodeTypes }}
|
||||
code{{ $type }} codeType = {{ $index }}
|
||||
Code{{ $type }} CodeType = {{ $index }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
|
@ -87,92 +87,86 @@ var opTypeStrings = [{{ .OpLen }}]string{
|
|||
{{- end }}
|
||||
}
|
||||
|
||||
type opType int
|
||||
type OpType int
|
||||
|
||||
const (
|
||||
{{- range $index, $type := .OpTypes }}
|
||||
op{{ $type.Op }} opType = {{ $index }}
|
||||
Op{{ $type.Op }} OpType = {{ $index }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
func (t opType) String() string {
|
||||
func (t OpType) String() string {
|
||||
if int(t) >= {{ .OpLen }} {
|
||||
return ""
|
||||
}
|
||||
return opTypeStrings[int(t)]
|
||||
}
|
||||
|
||||
func (t opType) codeType() codeType {
|
||||
func (t OpType) CodeType() CodeType {
|
||||
if strings.Contains(t.String(), "Struct") {
|
||||
if strings.Contains(t.String(), "End") {
|
||||
return codeStructEnd
|
||||
return CodeStructEnd
|
||||
}
|
||||
return codeStructField
|
||||
return CodeStructField
|
||||
}
|
||||
if t.String() == "Array" || t.String() == "ArrayPtr" {
|
||||
return codeArrayHead
|
||||
}
|
||||
if strings.Contains(t.String(), "ArrayElem") {
|
||||
return codeArrayElem
|
||||
}
|
||||
if t.String() == "Slice" || t.String() == "SlicePtr" {
|
||||
return codeSliceHead
|
||||
}
|
||||
if strings.Contains(t.String(), "SliceElem") {
|
||||
return codeSliceElem
|
||||
}
|
||||
if t.String() == "Map" || t.String() == "MapPtr" {
|
||||
return codeMapHead
|
||||
}
|
||||
if strings.Contains(t.String(), "MapKey") {
|
||||
return codeMapKey
|
||||
}
|
||||
if strings.Contains(t.String(), "MapValue") {
|
||||
return codeMapValue
|
||||
}
|
||||
if strings.Contains(t.String(), "MapEnd") {
|
||||
return codeMapEnd
|
||||
switch t {
|
||||
case OpArray, OpArrayPtr:
|
||||
return CodeArrayHead
|
||||
case OpArrayElem:
|
||||
return CodeArrayElem
|
||||
case OpSlice, OpSlicePtr:
|
||||
return CodeSliceHead
|
||||
case OpSliceElem:
|
||||
return CodeSliceElem
|
||||
case OpMap, OpMapPtr:
|
||||
return CodeMapHead
|
||||
case OpMapKey:
|
||||
return CodeMapKey
|
||||
case OpMapValue:
|
||||
return CodeMapValue
|
||||
case OpMapEnd:
|
||||
return CodeMapEnd
|
||||
}
|
||||
|
||||
return codeOp
|
||||
return CodeOp
|
||||
}
|
||||
|
||||
func (t opType) headToPtrHead() opType {
|
||||
func (t OpType) HeadToPtrHead() OpType {
|
||||
if strings.Index(t.String(), "PtrHead") > 0 {
|
||||
return t
|
||||
}
|
||||
|
||||
idx := strings.Index(t.String(), "Field")
|
||||
idx := strings.Index(t.String(), "Head")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
suffix := "Ptr"+t.String()[idx+len("Field"):]
|
||||
suffix := "PtrHead"+t.String()[idx+len("Head"):]
|
||||
|
||||
const toPtrOffset = 3
|
||||
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
|
||||
return opType(int(t) + toPtrOffset)
|
||||
if strings.Contains(OpType(int(t) + toPtrOffset).String(), suffix) {
|
||||
return OpType(int(t) + toPtrOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToOmitEmptyHead() opType {
|
||||
func (t OpType) HeadToOmitEmptyHead() OpType {
|
||||
const toOmitEmptyOffset = 1
|
||||
if strings.Contains(opType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return opType(int(t) + toOmitEmptyOffset)
|
||||
if strings.Contains(OpType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return OpType(int(t) + toOmitEmptyOffset)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToStringTagHead() opType {
|
||||
func (t OpType) HeadToStringTagHead() OpType {
|
||||
const toStringTagOffset = 2
|
||||
if strings.Contains(opType(int(t) + toStringTagOffset).String(), "StringTag") {
|
||||
return opType(int(t) + toStringTagOffset)
|
||||
if strings.Contains(OpType(int(t) + toStringTagOffset).String(), "StringTag") {
|
||||
return OpType(int(t) + toStringTagOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) ptrHeadToHead() opType {
|
||||
func (t OpType) PtrHeadToHead() OpType {
|
||||
idx := strings.Index(t.String(), "Ptr")
|
||||
if idx == -1 {
|
||||
return t
|
||||
|
@ -180,36 +174,40 @@ func (t opType) ptrHeadToHead() opType {
|
|||
suffix := t.String()[idx+len("Ptr"):]
|
||||
|
||||
const toPtrOffset = 3
|
||||
if strings.Contains(opType(int(t) - toPtrOffset).String(), suffix) {
|
||||
return opType(int(t) - toPtrOffset)
|
||||
if strings.Contains(OpType(int(t) - toPtrOffset).String(), suffix) {
|
||||
return OpType(int(t) - toPtrOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) fieldToEnd() opType {
|
||||
switch t {
|
||||
{{- range $type := .OpTypes }}
|
||||
{{- if $type.IsFieldToEnd }}
|
||||
case op{{ $type.Op }}:
|
||||
return op{{ call $type.FieldToEnd }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
func (t OpType) FieldToEnd() OpType {
|
||||
idx := strings.Index(t.String(), "Field")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
suffix := t.String()[idx+len("Field"):]
|
||||
if suffix == "" || suffix == "OmitEmpty" || suffix == "StringTag" {
|
||||
return t
|
||||
}
|
||||
const toEndOffset = 3
|
||||
if strings.Contains(OpType(int(t) + toEndOffset).String(), "End"+suffix) {
|
||||
return OpType(int(t) + toEndOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) fieldToOmitEmptyField() opType {
|
||||
func (t OpType) FieldToOmitEmptyField() OpType {
|
||||
const toOmitEmptyOffset = 1
|
||||
if strings.Contains(opType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return opType(int(t) + toOmitEmptyOffset)
|
||||
if strings.Contains(OpType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return OpType(int(t) + toOmitEmptyOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) fieldToStringTagField() opType {
|
||||
func (t OpType) FieldToStringTagField() OpType {
|
||||
const toStringTagOffset = 2
|
||||
if strings.Contains(opType(int(t) + toStringTagOffset).String(), "StringTag") {
|
||||
return opType(int(t) + toStringTagOffset)
|
||||
if strings.Contains(OpType(int(t) + toStringTagOffset).String(), "StringTag") {
|
||||
return OpType(int(t) + toStringTagOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
@ -269,7 +267,7 @@ func (t opType) fieldToStringTagField() opType {
|
|||
typ := typ
|
||||
|
||||
op := fmt.Sprintf(
|
||||
"StructField%sHead%s%s",
|
||||
"Struct%sHead%s%s",
|
||||
ptrOrNot,
|
||||
opt,
|
||||
typ,
|
||||
|
@ -279,28 +277,28 @@ func (t opType) fieldToStringTagField() opType {
|
|||
Code: "StructField",
|
||||
HeadToPtrHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructFieldPtrHead%s%s",
|
||||
"StructPtrHead%s%s",
|
||||
opt,
|
||||
typ,
|
||||
)
|
||||
},
|
||||
HeadToOmitEmptyHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%sHeadOmitEmpty%s",
|
||||
"Struct%sHeadOmitEmpty%s",
|
||||
ptrOrNot,
|
||||
typ,
|
||||
)
|
||||
},
|
||||
HeadToStringTagHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructField%sHeadStringTag%s",
|
||||
"Struct%sHeadStringTag%s",
|
||||
ptrOrNot,
|
||||
typ,
|
||||
)
|
||||
},
|
||||
PtrHeadToHead: func() string {
|
||||
return fmt.Sprintf(
|
||||
"StructFieldHead%s%s",
|
||||
"StructHead%s%s",
|
||||
opt,
|
||||
typ,
|
||||
)
|
||||
|
@ -354,8 +352,6 @@ func (t opType) fieldToStringTagField() opType {
|
|||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, typ := range append(primitiveTypesUpper, "") {
|
||||
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
|
||||
opt := opt
|
||||
typ := typ
|
||||
|
@ -390,7 +386,7 @@ func (t opType) fieldToStringTagField() opType {
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(repoRoot(), "encode_optype.go")
|
||||
path := filepath.Join(repoRoot(), "internal", "encoder", "optype.go")
|
||||
buf, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -400,10 +396,11 @@ func (t opType) fieldToStringTagField() opType {
|
|||
|
||||
func repoRoot() string {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
relativePathFromRepoRoot := filepath.Join("cmd", "generator")
|
||||
relativePathFromRepoRoot := filepath.Join("internal", "cmd", "generator")
|
||||
return strings.TrimSuffix(filepath.Dir(file), relativePathFromRepoRoot)
|
||||
}
|
||||
|
||||
//go:generate go run main.go
|
||||
func main() {
|
||||
if err := _main(); err != nil {
|
||||
panic(err)
|
|
@ -1,12 +1,14 @@
|
|||
package json
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
func compact(dst *bytes.Buffer, src []byte, escape bool) error {
|
||||
func Compact(dst *bytes.Buffer, src []byte, escape bool) error {
|
||||
if len(src) == 0 {
|
||||
return errUnexpectedEndOfJSON("", 0)
|
||||
return errors.ErrUnexpectedEndOfJSON("", 0)
|
||||
}
|
||||
length := len(src)
|
||||
for cursor := 0; cursor < length; cursor++ {
|
||||
|
@ -39,8 +41,8 @@ func compact(dst *bytes.Buffer, src []byte, escape bool) error {
|
|||
}
|
||||
case '"':
|
||||
goto LOOP_END
|
||||
case nul:
|
||||
return errUnexpectedEndOfJSON("string", int64(length))
|
||||
case '\000':
|
||||
return errors.ErrUnexpectedEndOfJSON("string", int64(length))
|
||||
}
|
||||
}
|
||||
default:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,38 @@
|
|||
// +build !race
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
|
||||
if typeptr > typeAddr.MaxTypeAddr {
|
||||
return compileToGetCodeSetSlowPath(typeptr)
|
||||
}
|
||||
index := typeptr - typeAddr.BaseTypeAddr
|
||||
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
|
||||
return codeSet, nil
|
||||
}
|
||||
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
|
||||
|
||||
code, err := compileHead(&compileContext{
|
||||
typ: copiedType,
|
||||
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code = copyOpcode(code)
|
||||
codeLength := code.TotalLength()
|
||||
codeSet := &OpcodeSet{
|
||||
Code: code,
|
||||
CodeLength: codeLength,
|
||||
}
|
||||
cachedOpcodeSets[index] = codeSet
|
||||
return codeSet, nil
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// +build race
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
var setsMu sync.RWMutex
|
||||
|
||||
func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
|
||||
if typeptr > typeAddr.MaxTypeAddr {
|
||||
return compileToGetCodeSetSlowPath(typeptr)
|
||||
}
|
||||
index := typeptr - typeAddr.BaseTypeAddr
|
||||
setsMu.RLock()
|
||||
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
|
||||
setsMu.RUnlock()
|
||||
return codeSet, nil
|
||||
}
|
||||
setsMu.RUnlock()
|
||||
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
|
||||
|
||||
code, err := compileHead(&compileContext{
|
||||
typ: copiedType,
|
||||
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[index] = codeSet
|
||||
setsMu.Unlock()
|
||||
return codeSet, nil
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type compileContext struct {
|
||||
typ *runtime.Type
|
||||
opcodeIndex int
|
||||
ptrIndex int
|
||||
indent int
|
||||
structTypeToCompiledCode map[uintptr]*CompiledCode
|
||||
|
||||
parent *compileContext
|
||||
}
|
||||
|
||||
func (c *compileContext) context() *compileContext {
|
||||
return &compileContext{
|
||||
typ: c.typ,
|
||||
opcodeIndex: c.opcodeIndex,
|
||||
ptrIndex: c.ptrIndex,
|
||||
indent: c.indent,
|
||||
structTypeToCompiledCode: c.structTypeToCompiledCode,
|
||||
parent: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compileContext) withType(typ *runtime.Type) *compileContext {
|
||||
ctx := c.context()
|
||||
ctx.typ = typ
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *compileContext) incIndent() *compileContext {
|
||||
ctx := c.context()
|
||||
ctx.indent++
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *compileContext) decIndent() *compileContext {
|
||||
ctx := c.context()
|
||||
ctx.indent--
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *compileContext) incIndex() {
|
||||
c.incOpcodeIndex()
|
||||
c.incPtrIndex()
|
||||
}
|
||||
|
||||
func (c *compileContext) decIndex() {
|
||||
c.decOpcodeIndex()
|
||||
c.decPtrIndex()
|
||||
}
|
||||
|
||||
func (c *compileContext) incOpcodeIndex() {
|
||||
c.opcodeIndex++
|
||||
if c.parent != nil {
|
||||
c.parent.incOpcodeIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compileContext) decOpcodeIndex() {
|
||||
c.opcodeIndex--
|
||||
if c.parent != nil {
|
||||
c.parent.decOpcodeIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compileContext) incPtrIndex() {
|
||||
c.ptrIndex++
|
||||
if c.parent != nil {
|
||||
c.parent.incPtrIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compileContext) decPtrIndex() {
|
||||
c.ptrIndex--
|
||||
if c.parent != nil {
|
||||
c.parent.decPtrIndex()
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package json
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
@ -7,12 +7,12 @@ import (
|
|||
|
||||
func TestDumpOpcode(t *testing.T) {
|
||||
var v interface{} = 1
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
typ := header.typ
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
codeSet, err := encodeCompileToGetCodeSet(typeptr)
|
||||
codeSet, err := CompileToGetCodeSet(typeptr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codeSet.code.dump()
|
||||
codeSet.Code.Dump()
|
||||
}
|
|
@ -0,0 +1,568 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type Option int
|
||||
|
||||
const (
|
||||
HTMLEscapeOption Option = 1 << iota
|
||||
IndentOption
|
||||
UnorderedMapOption
|
||||
)
|
||||
|
||||
func (t OpType) IsMultipleOpHead() bool {
|
||||
switch t {
|
||||
case OpStructHead:
|
||||
return true
|
||||
case OpStructHeadSlice:
|
||||
return true
|
||||
case OpStructHeadArray:
|
||||
return true
|
||||
case OpStructHeadMap:
|
||||
return true
|
||||
case OpStructHeadStruct:
|
||||
return true
|
||||
case OpStructHeadOmitEmpty:
|
||||
return true
|
||||
case OpStructHeadOmitEmptySlice:
|
||||
return true
|
||||
case OpStructHeadStringTagSlice:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyArray:
|
||||
return true
|
||||
case OpStructHeadStringTagArray:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyMap:
|
||||
return true
|
||||
case OpStructHeadStringTagMap:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyStruct:
|
||||
return true
|
||||
case OpStructHeadStringTag:
|
||||
return true
|
||||
case OpStructHeadSlicePtr:
|
||||
return true
|
||||
case OpStructHeadOmitEmptySlicePtr:
|
||||
return true
|
||||
case OpStructHeadStringTagSlicePtr:
|
||||
return true
|
||||
case OpStructHeadArrayPtr:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyArrayPtr:
|
||||
return true
|
||||
case OpStructHeadStringTagArrayPtr:
|
||||
return true
|
||||
case OpStructHeadMapPtr:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyMapPtr:
|
||||
return true
|
||||
case OpStructHeadStringTagMapPtr:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t OpType) IsMultipleOpField() bool {
|
||||
switch t {
|
||||
case OpStructField:
|
||||
return true
|
||||
case OpStructFieldSlice:
|
||||
return true
|
||||
case OpStructFieldArray:
|
||||
return true
|
||||
case OpStructFieldMap:
|
||||
return true
|
||||
case OpStructFieldStruct:
|
||||
return true
|
||||
case OpStructFieldOmitEmpty:
|
||||
return true
|
||||
case OpStructFieldOmitEmptySlice:
|
||||
return true
|
||||
case OpStructFieldStringTagSlice:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyArray:
|
||||
return true
|
||||
case OpStructFieldStringTagArray:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyMap:
|
||||
return true
|
||||
case OpStructFieldStringTagMap:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyStruct:
|
||||
return true
|
||||
case OpStructFieldStringTag:
|
||||
return true
|
||||
case OpStructFieldSlicePtr:
|
||||
return true
|
||||
case OpStructFieldOmitEmptySlicePtr:
|
||||
return true
|
||||
case OpStructFieldStringTagSlicePtr:
|
||||
return true
|
||||
case OpStructFieldArrayPtr:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyArrayPtr:
|
||||
return true
|
||||
case OpStructFieldStringTagArrayPtr:
|
||||
return true
|
||||
case OpStructFieldMapPtr:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyMapPtr:
|
||||
return true
|
||||
case OpStructFieldStringTagMapPtr:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type OpcodeSet struct {
|
||||
Code *Opcode
|
||||
CodeLength int
|
||||
}
|
||||
|
||||
type CompiledCode struct {
|
||||
Code *Opcode
|
||||
Linked bool // whether recursive code already have linked
|
||||
CurLen uintptr
|
||||
NextLen uintptr
|
||||
}
|
||||
|
||||
const StartDetectingCyclesAfter = 1000
|
||||
|
||||
func Load(base uintptr, idx uintptr) uintptr {
|
||||
addr := base + idx
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func Store(base uintptr, idx uintptr, p uintptr) {
|
||||
addr := base + idx
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func LoadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
|
||||
addr := base + idx
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
return PtrToPtr(p)
|
||||
/*
|
||||
for i := 0; i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return p
|
||||
}
|
||||
p = PtrToPtr(p)
|
||||
}
|
||||
return p
|
||||
*/
|
||||
}
|
||||
|
||||
func PtrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func PtrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func PtrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func PtrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
func PtrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
|
||||
func PtrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) }
|
||||
func PtrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
|
||||
func PtrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) }
|
||||
func PtrToPtr(p uintptr) uintptr {
|
||||
return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
|
||||
}
|
||||
func PtrToNPtr(p uintptr, ptrNum int) uintptr {
|
||||
for i := 0; i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = PtrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func PtrToUnsafePtr(p uintptr) unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
|
||||
}
|
||||
func PtrToInterface(code *Opcode, p uintptr) interface{} {
|
||||
return *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: code.Type,
|
||||
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
|
||||
}))
|
||||
}
|
||||
|
||||
func ErrUnsupportedValue(code *Opcode, ptr uintptr) *errors.UnsupportedValueError {
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: code.Type,
|
||||
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&ptr)),
|
||||
}))
|
||||
return &errors.UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: fmt.Sprintf("encountered a cycle via %s", code.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrUnsupportedFloat(v float64) *errors.UnsupportedValueError {
|
||||
return &errors.UnsupportedValueError{
|
||||
Value: reflect.ValueOf(v),
|
||||
Str: strconv.FormatFloat(v, 'g', -1, 64),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrMarshalerWithCode(code *Opcode, err error) *errors.MarshalerError {
|
||||
return &errors.MarshalerError{
|
||||
Type: runtime.RType2Type(code.Type),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
type emptyInterface struct {
|
||||
typ *runtime.Type
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type MapItem struct {
|
||||
Key []byte
|
||||
Value []byte
|
||||
}
|
||||
|
||||
type Mapslice struct {
|
||||
Items []MapItem
|
||||
}
|
||||
|
||||
func (m *Mapslice) Len() int {
|
||||
return len(m.Items)
|
||||
}
|
||||
|
||||
func (m *Mapslice) Less(i, j int) bool {
|
||||
return bytes.Compare(m.Items[i].Key, m.Items[j].Key) < 0
|
||||
}
|
||||
|
||||
func (m *Mapslice) Swap(i, j int) {
|
||||
m.Items[i], m.Items[j] = m.Items[j], m.Items[i]
|
||||
}
|
||||
|
||||
type MapContext struct {
|
||||
Pos []int
|
||||
Slice *Mapslice
|
||||
Buf []byte
|
||||
}
|
||||
|
||||
var mapContextPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &MapContext{}
|
||||
},
|
||||
}
|
||||
|
||||
func NewMapContext(mapLen int) *MapContext {
|
||||
ctx := mapContextPool.Get().(*MapContext)
|
||||
if ctx.Slice == nil {
|
||||
ctx.Slice = &Mapslice{
|
||||
Items: make([]MapItem, 0, mapLen),
|
||||
}
|
||||
}
|
||||
if cap(ctx.Pos) < (mapLen*2 + 1) {
|
||||
ctx.Pos = make([]int, 0, mapLen*2+1)
|
||||
ctx.Slice.Items = make([]MapItem, 0, mapLen)
|
||||
} else {
|
||||
ctx.Pos = ctx.Pos[:0]
|
||||
ctx.Slice.Items = ctx.Slice.Items[:0]
|
||||
}
|
||||
ctx.Buf = ctx.Buf[:0]
|
||||
return ctx
|
||||
}
|
||||
|
||||
func ReleaseMapContext(c *MapContext) {
|
||||
mapContextPool.Put(c)
|
||||
}
|
||||
|
||||
//go:linkname MapIterInit reflect.mapiterinit
|
||||
//go:noescape
|
||||
func MapIterInit(mapType *runtime.Type, m unsafe.Pointer) unsafe.Pointer
|
||||
|
||||
//go:linkname MapIterKey reflect.mapiterkey
|
||||
//go:noescape
|
||||
func MapIterKey(it unsafe.Pointer) unsafe.Pointer
|
||||
|
||||
//go:linkname MapIterNext reflect.mapiternext
|
||||
//go:noescape
|
||||
func MapIterNext(it unsafe.Pointer)
|
||||
|
||||
//go:linkname MapLen reflect.maplen
|
||||
//go:noescape
|
||||
func MapLen(m unsafe.Pointer) int
|
||||
|
||||
type RuntimeContext struct {
|
||||
Buf []byte
|
||||
Ptrs []uintptr
|
||||
KeepRefs []unsafe.Pointer
|
||||
SeenPtr []uintptr
|
||||
BaseIndent int
|
||||
Prefix []byte
|
||||
IndentStr []byte
|
||||
}
|
||||
|
||||
func (c *RuntimeContext) Init(p uintptr, codelen int) {
|
||||
if len(c.Ptrs) < codelen {
|
||||
c.Ptrs = make([]uintptr, codelen)
|
||||
}
|
||||
c.Ptrs[0] = p
|
||||
c.KeepRefs = c.KeepRefs[:0]
|
||||
c.SeenPtr = c.SeenPtr[:0]
|
||||
c.BaseIndent = 0
|
||||
}
|
||||
|
||||
func (c *RuntimeContext) Ptr() uintptr {
|
||||
header := (*runtime.SliceHeader)(unsafe.Pointer(&c.Ptrs))
|
||||
return uintptr(header.Data)
|
||||
}
|
||||
|
||||
func AppendByteSlice(b []byte, src []byte) []byte {
|
||||
if src == nil {
|
||||
return append(b, `null`...)
|
||||
}
|
||||
encodedLen := base64.StdEncoding.EncodedLen(len(src))
|
||||
b = append(b, '"')
|
||||
pos := len(b)
|
||||
remainLen := cap(b[pos:])
|
||||
var buf []byte
|
||||
if remainLen > encodedLen {
|
||||
buf = b[pos : pos+encodedLen]
|
||||
} else {
|
||||
buf = make([]byte, encodedLen)
|
||||
}
|
||||
base64.StdEncoding.Encode(buf, src)
|
||||
return append(append(b, buf...), '"')
|
||||
}
|
||||
|
||||
func AppendFloat32(b []byte, v float32) []byte {
|
||||
f64 := float64(v)
|
||||
abs := math.Abs(f64)
|
||||
fmt := byte('f')
|
||||
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
||||
if abs != 0 {
|
||||
f32 := float32(abs)
|
||||
if f32 < 1e-6 || f32 >= 1e21 {
|
||||
fmt = 'e'
|
||||
}
|
||||
}
|
||||
return strconv.AppendFloat(b, f64, fmt, -1, 32)
|
||||
}
|
||||
|
||||
func AppendFloat64(b []byte, v float64) []byte {
|
||||
abs := math.Abs(v)
|
||||
fmt := byte('f')
|
||||
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
|
||||
if abs != 0 {
|
||||
if abs < 1e-6 || abs >= 1e21 {
|
||||
fmt = 'e'
|
||||
}
|
||||
}
|
||||
return strconv.AppendFloat(b, v, fmt, -1, 64)
|
||||
}
|
||||
|
||||
func AppendBool(b []byte, v bool) []byte {
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
}
|
||||
|
||||
var (
|
||||
floatTable = [256]bool{
|
||||
'0': true,
|
||||
'1': true,
|
||||
'2': true,
|
||||
'3': true,
|
||||
'4': true,
|
||||
'5': true,
|
||||
'6': true,
|
||||
'7': true,
|
||||
'8': true,
|
||||
'9': true,
|
||||
'.': true,
|
||||
'e': true,
|
||||
'E': true,
|
||||
'+': true,
|
||||
'-': true,
|
||||
}
|
||||
)
|
||||
|
||||
func AppendNumber(b []byte, n json.Number) ([]byte, error) {
|
||||
if len(n) == 0 {
|
||||
return append(b, '0'), nil
|
||||
}
|
||||
for i := 0; i < len(n); i++ {
|
||||
if !floatTable[n[i]] {
|
||||
return nil, fmt.Errorf("json: invalid number literal %q", n)
|
||||
}
|
||||
}
|
||||
b = append(b, n...)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func AppendMarshalJSON(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.AddrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(json.Marshaler)
|
||||
if !ok {
|
||||
return AppendNull(b), nil
|
||||
}
|
||||
bb, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
buf := bytes.NewBuffer(b)
|
||||
// TODO: we should validate buffer with `compact`
|
||||
if err := Compact(buf, bb, escape); err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}, indent int, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.AddrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(json.Marshaler)
|
||||
if !ok {
|
||||
return AppendNull(b), nil
|
||||
}
|
||||
bb, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
var compactBuf bytes.Buffer
|
||||
if err := Compact(&compactBuf, bb, escape); err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
var indentBuf bytes.Buffer
|
||||
if err := Indent(
|
||||
&indentBuf,
|
||||
compactBuf.Bytes(),
|
||||
string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), ctx.BaseIndent+indent+1),
|
||||
string(ctx.IndentStr),
|
||||
); err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
return append(b, indentBuf.Bytes()...), nil
|
||||
}
|
||||
|
||||
func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.AddrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
return AppendNull(b), nil
|
||||
}
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
if escape {
|
||||
return AppendEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
return AppendString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
|
||||
func AppendMarshalTextIndent(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if code.AddrForMarshaler {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
marshaler, ok := v.(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
return AppendNull(b), nil
|
||||
}
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
if escape {
|
||||
return AppendEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
return AppendString(b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
|
||||
func AppendNull(b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func AppendComma(b []byte) []byte {
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func AppendCommaIndent(b []byte) []byte {
|
||||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func AppendStructEnd(b []byte) []byte {
|
||||
return append(b, '}', ',')
|
||||
}
|
||||
|
||||
func AppendStructEndIndent(ctx *RuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, '\n')
|
||||
b = append(b, ctx.Prefix...)
|
||||
b = append(b, bytes.Repeat(ctx.IndentStr, ctx.BaseIndent+indent)...)
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func AppendIndent(ctx *RuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, ctx.Prefix...)
|
||||
return append(b, bytes.Repeat(ctx.IndentStr, ctx.BaseIndent+indent)...)
|
||||
}
|
||||
|
||||
func IsNilForMarshaler(v interface{}) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Interface, reflect.Map, reflect.Ptr:
|
||||
return rv.IsNil()
|
||||
case reflect.Slice:
|
||||
return rv.IsNil() || rv.Len() == 0
|
||||
}
|
||||
return false
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
package json
|
||||
package encoder
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
func encodeWithIndent(dst *bytes.Buffer, src []byte, prefix, indentStr string) error {
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
func Indent(dst *bytes.Buffer, src []byte, prefix, indentStr string) error {
|
||||
length := int64(len(src))
|
||||
indentNum := 0
|
||||
indentBytes := []byte(indentStr)
|
||||
|
@ -28,8 +32,8 @@ func encodeWithIndent(dst *bytes.Buffer, src []byte, prefix, indentStr string) e
|
|||
}
|
||||
case '"':
|
||||
goto LOOP_END
|
||||
case nul:
|
||||
return errUnexpectedEndOfJSON("string", length)
|
||||
case '\000':
|
||||
return errors.ErrUnexpectedEndOfJSON("string", length)
|
||||
}
|
||||
}
|
||||
case '{':
|
||||
|
@ -50,7 +54,7 @@ func encodeWithIndent(dst *bytes.Buffer, src []byte, prefix, indentStr string) e
|
|||
case '}':
|
||||
indentNum--
|
||||
if indentNum < 0 {
|
||||
return errInvalidCharacter('}', "}", cursor)
|
||||
return errors.ErrInvalidCharacter('}', "}", cursor)
|
||||
}
|
||||
b := []byte{'\n'}
|
||||
b = append(b, prefix...)
|
||||
|
@ -77,7 +81,7 @@ func encodeWithIndent(dst *bytes.Buffer, src []byte, prefix, indentStr string) e
|
|||
case ']':
|
||||
indentNum--
|
||||
if indentNum < 0 {
|
||||
return errInvalidCharacter(']', "]", cursor)
|
||||
return errors.ErrInvalidCharacter(']', "]", cursor)
|
||||
}
|
||||
b := []byte{'\n'}
|
||||
b = append(b, prefix...)
|
|
@ -1,4 +1,4 @@
|
|||
package json
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
@ -49,9 +49,9 @@ var intBELookup = [100]uint16{
|
|||
|
||||
var intLookup = [2]*[100]uint16{&intLELookup, &intBELookup}
|
||||
|
||||
func appendInt(out []byte, u64 uint64, code *opcode) []byte {
|
||||
n := u64 & code.mask
|
||||
negative := (u64>>code.rshiftNum)&1 == 1
|
||||
func AppendInt(out []byte, u64 uint64, code *Opcode) []byte {
|
||||
n := u64 & code.Mask
|
||||
negative := (u64>>code.RshiftNum)&1 == 1
|
||||
if !negative {
|
||||
if n < 10 {
|
||||
return append(out, byte(n+'0'))
|
||||
|
@ -60,7 +60,7 @@ func appendInt(out []byte, u64 uint64, code *opcode) []byte {
|
|||
return append(out, byte(u), byte(u>>8))
|
||||
}
|
||||
} else {
|
||||
n = -n & code.mask
|
||||
n = -n & code.Mask
|
||||
}
|
||||
|
||||
lookup := intLookup[endianness]
|
||||
|
@ -91,8 +91,8 @@ func appendInt(out []byte, u64 uint64, code *opcode) []byte {
|
|||
return append(out, b[i:]...)
|
||||
}
|
||||
|
||||
func appendUint(out []byte, u64 uint64, code *opcode) []byte {
|
||||
n := u64 & code.mask
|
||||
func AppendUint(out []byte, u64 uint64, code *Opcode) []byte {
|
||||
n := u64 & code.Mask
|
||||
if n < 10 {
|
||||
return append(out, byte(n+'0'))
|
||||
} else if n < 100 {
|
|
@ -0,0 +1,8 @@
|
|||
// +build !go1.13
|
||||
|
||||
package encoder
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:linkname MapIterValue reflect.mapitervalue
|
||||
func MapIterValue(it unsafe.Pointer) unsafe.Pointer
|
|
@ -0,0 +1,8 @@
|
|||
// +build go1.13
|
||||
|
||||
package encoder
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:linkname MapIterValue reflect.mapiterelem
|
||||
func MapIterValue(it unsafe.Pointer) unsafe.Pointer
|
|
@ -0,0 +1,647 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||
|
||||
type Opcode struct {
|
||||
Op OpType // operation type
|
||||
Type *runtime.Type // go type
|
||||
DisplayIdx int // opcode index
|
||||
Key []byte // struct field key
|
||||
EscapedKey []byte // struct field key ( HTML escaped )
|
||||
PtrNum int // pointer number: e.g. double pointer is 2.
|
||||
DisplayKey string // key text to display
|
||||
IsTaggedKey bool // whether tagged key
|
||||
AnonymousKey bool // whether anonymous key
|
||||
AnonymousHead bool // whether anonymous head or not
|
||||
Indirect bool // whether indirect or not
|
||||
Nilcheck bool // whether needs to nilcheck or not
|
||||
AddrForMarshaler bool // whether needs to addr for marshaler or not
|
||||
RshiftNum uint8 // use to take bit for judging whether negative integer or not
|
||||
Mask uint64 // mask for number
|
||||
Indent int // indent number
|
||||
|
||||
Idx uintptr // offset to access ptr
|
||||
HeadIdx uintptr // offset to access slice/struct head
|
||||
ElemIdx uintptr // offset to access array/slice/map elem
|
||||
Length uintptr // offset to access slice/map length or array length
|
||||
MapIter uintptr // offset to access map iterator
|
||||
MapPos uintptr // offset to access position list for sorted map
|
||||
Offset uintptr // offset size from struct header
|
||||
Size uintptr // array/slice elem size
|
||||
|
||||
MapKey *Opcode // map key
|
||||
MapValue *Opcode // map value
|
||||
Elem *Opcode // array/slice elem
|
||||
End *Opcode // array/slice/struct/map end
|
||||
PrevField *Opcode // prev struct field
|
||||
NextField *Opcode // next struct field
|
||||
Next *Opcode // next opcode
|
||||
Jmp *CompiledCode // for recursive call
|
||||
}
|
||||
|
||||
func rshitNum(bitSize uint8) uint8 {
|
||||
return bitSize - 1
|
||||
}
|
||||
|
||||
func (c *Opcode) setMaskAndRshiftNum(bitSize uint8) {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
c.Mask = math.MaxUint8
|
||||
case 16:
|
||||
c.Mask = math.MaxUint16
|
||||
case 32:
|
||||
c.Mask = math.MaxUint32
|
||||
case 64:
|
||||
c.Mask = math.MaxUint64
|
||||
}
|
||||
c.RshiftNum = rshitNum(bitSize)
|
||||
}
|
||||
|
||||
func (c *Opcode) ToHeaderType() OpType {
|
||||
switch c.Op {
|
||||
case OpInt:
|
||||
return OpStructHeadInt
|
||||
case OpIntPtr:
|
||||
return OpStructHeadIntPtr
|
||||
case OpUint:
|
||||
return OpStructHeadUint
|
||||
case OpUintPtr:
|
||||
return OpStructHeadUintPtr
|
||||
case OpFloat32:
|
||||
return OpStructHeadFloat32
|
||||
case OpFloat32Ptr:
|
||||
return OpStructHeadFloat32Ptr
|
||||
case OpFloat64:
|
||||
return OpStructHeadFloat64
|
||||
case OpFloat64Ptr:
|
||||
return OpStructHeadFloat64Ptr
|
||||
case OpString:
|
||||
return OpStructHeadString
|
||||
case OpStringPtr:
|
||||
return OpStructHeadStringPtr
|
||||
case OpNumber:
|
||||
return OpStructHeadNumber
|
||||
case OpNumberPtr:
|
||||
return OpStructHeadNumberPtr
|
||||
case OpBool:
|
||||
return OpStructHeadBool
|
||||
case OpBoolPtr:
|
||||
return OpStructHeadBoolPtr
|
||||
case OpMap:
|
||||
return OpStructHeadMap
|
||||
case OpMapPtr:
|
||||
c.Op = OpMap
|
||||
return OpStructHeadMapPtr
|
||||
case OpArray:
|
||||
return OpStructHeadArray
|
||||
case OpArrayPtr:
|
||||
c.Op = OpArray
|
||||
return OpStructHeadArrayPtr
|
||||
case OpSlice:
|
||||
return OpStructHeadSlice
|
||||
case OpSlicePtr:
|
||||
c.Op = OpSlice
|
||||
return OpStructHeadSlicePtr
|
||||
case OpMarshalJSON:
|
||||
return OpStructHeadMarshalJSON
|
||||
case OpMarshalJSONPtr:
|
||||
return OpStructHeadMarshalJSONPtr
|
||||
case OpMarshalText:
|
||||
return OpStructHeadMarshalText
|
||||
case OpMarshalTextPtr:
|
||||
return OpStructHeadMarshalTextPtr
|
||||
}
|
||||
return OpStructHead
|
||||
}
|
||||
|
||||
func (c *Opcode) ToFieldType() OpType {
|
||||
switch c.Op {
|
||||
case OpInt:
|
||||
return OpStructFieldInt
|
||||
case OpIntPtr:
|
||||
return OpStructFieldIntPtr
|
||||
case OpUint:
|
||||
return OpStructFieldUint
|
||||
case OpUintPtr:
|
||||
return OpStructFieldUintPtr
|
||||
case OpFloat32:
|
||||
return OpStructFieldFloat32
|
||||
case OpFloat32Ptr:
|
||||
return OpStructFieldFloat32Ptr
|
||||
case OpFloat64:
|
||||
return OpStructFieldFloat64
|
||||
case OpFloat64Ptr:
|
||||
return OpStructFieldFloat64Ptr
|
||||
case OpString:
|
||||
return OpStructFieldString
|
||||
case OpStringPtr:
|
||||
return OpStructFieldStringPtr
|
||||
case OpNumber:
|
||||
return OpStructFieldNumber
|
||||
case OpNumberPtr:
|
||||
return OpStructFieldNumberPtr
|
||||
case OpBool:
|
||||
return OpStructFieldBool
|
||||
case OpBoolPtr:
|
||||
return OpStructFieldBoolPtr
|
||||
case OpMap:
|
||||
return OpStructFieldMap
|
||||
case OpMapPtr:
|
||||
c.Op = OpMap
|
||||
return OpStructFieldMapPtr
|
||||
case OpArray:
|
||||
return OpStructFieldArray
|
||||
case OpArrayPtr:
|
||||
c.Op = OpArray
|
||||
return OpStructFieldArrayPtr
|
||||
case OpSlice:
|
||||
return OpStructFieldSlice
|
||||
case OpSlicePtr:
|
||||
c.Op = OpSlice
|
||||
return OpStructFieldSlicePtr
|
||||
case OpMarshalJSON:
|
||||
return OpStructFieldMarshalJSON
|
||||
case OpMarshalJSONPtr:
|
||||
return OpStructFieldMarshalJSONPtr
|
||||
case OpMarshalText:
|
||||
return OpStructFieldMarshalText
|
||||
case OpMarshalTextPtr:
|
||||
return OpStructFieldMarshalTextPtr
|
||||
}
|
||||
return OpStructField
|
||||
}
|
||||
|
||||
func newOpCode(ctx *compileContext, op OpType) *Opcode {
|
||||
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
|
||||
}
|
||||
|
||||
func opcodeOffset(idx int) uintptr {
|
||||
return uintptr(idx) * uintptrSize
|
||||
}
|
||||
|
||||
func copyOpcode(code *Opcode) *Opcode {
|
||||
codeMap := map[uintptr]*Opcode{}
|
||||
return code.copy(codeMap)
|
||||
}
|
||||
|
||||
func newOpCodeWithNext(ctx *compileContext, op OpType, next *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: op,
|
||||
Type: ctx.typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
Next: next,
|
||||
}
|
||||
}
|
||||
|
||||
func newEndOp(ctx *compileContext) *Opcode {
|
||||
return newOpCodeWithNext(ctx, OpEnd, nil)
|
||||
}
|
||||
|
||||
func (c *Opcode) copy(codeMap map[uintptr]*Opcode) *Opcode {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
addr := uintptr(unsafe.Pointer(c))
|
||||
if code, exists := codeMap[addr]; exists {
|
||||
return code
|
||||
}
|
||||
copied := &Opcode{
|
||||
Op: c.Op,
|
||||
Type: c.Type,
|
||||
DisplayIdx: c.DisplayIdx,
|
||||
Key: c.Key,
|
||||
EscapedKey: c.EscapedKey,
|
||||
DisplayKey: c.DisplayKey,
|
||||
PtrNum: c.PtrNum,
|
||||
Mask: c.Mask,
|
||||
RshiftNum: c.RshiftNum,
|
||||
IsTaggedKey: c.IsTaggedKey,
|
||||
AnonymousKey: c.AnonymousKey,
|
||||
AnonymousHead: c.AnonymousHead,
|
||||
Indirect: c.Indirect,
|
||||
Nilcheck: c.Nilcheck,
|
||||
AddrForMarshaler: c.AddrForMarshaler,
|
||||
Indent: c.Indent,
|
||||
Idx: c.Idx,
|
||||
HeadIdx: c.HeadIdx,
|
||||
ElemIdx: c.ElemIdx,
|
||||
Length: c.Length,
|
||||
MapIter: c.MapIter,
|
||||
MapPos: c.MapPos,
|
||||
Offset: c.Offset,
|
||||
Size: c.Size,
|
||||
}
|
||||
codeMap[addr] = copied
|
||||
copied.MapKey = c.MapKey.copy(codeMap)
|
||||
copied.MapValue = c.MapValue.copy(codeMap)
|
||||
copied.Elem = c.Elem.copy(codeMap)
|
||||
copied.End = c.End.copy(codeMap)
|
||||
copied.PrevField = c.PrevField.copy(codeMap)
|
||||
copied.NextField = c.NextField.copy(codeMap)
|
||||
copied.Next = c.Next.copy(codeMap)
|
||||
copied.Jmp = c.Jmp
|
||||
return copied
|
||||
}
|
||||
|
||||
func (c *Opcode) BeforeLastCode() *Opcode {
|
||||
code := c
|
||||
for {
|
||||
var nextCode *Opcode
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
nextCode = code.End
|
||||
default:
|
||||
nextCode = code.Next
|
||||
}
|
||||
if nextCode.Op == OpEnd {
|
||||
return code
|
||||
}
|
||||
code = nextCode
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Opcode) TotalLength() int {
|
||||
var idx int
|
||||
for code := c; code.Op != OpEnd; {
|
||||
idx = int(code.Idx / uintptrSize)
|
||||
if code.Op == OpStructFieldRecursiveEnd {
|
||||
break
|
||||
}
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
code = code.End
|
||||
default:
|
||||
code = code.Next
|
||||
}
|
||||
}
|
||||
return idx + 2 // opEnd + 1
|
||||
}
|
||||
|
||||
func (c *Opcode) decOpcodeIndex() {
|
||||
for code := c; code.Op != OpEnd; {
|
||||
code.DisplayIdx--
|
||||
code.Idx -= uintptrSize
|
||||
if code.HeadIdx > 0 {
|
||||
code.HeadIdx -= uintptrSize
|
||||
}
|
||||
if code.ElemIdx > 0 {
|
||||
code.ElemIdx -= uintptrSize
|
||||
}
|
||||
if code.MapIter > 0 {
|
||||
code.MapIter -= uintptrSize
|
||||
}
|
||||
if code.Length > 0 && code.Op.CodeType() != CodeArrayHead && code.Op.CodeType() != CodeArrayElem {
|
||||
code.Length -= uintptrSize
|
||||
}
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
code = code.End
|
||||
default:
|
||||
code = code.Next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Opcode) decIndent() {
|
||||
for code := c; code.Op != OpEnd; {
|
||||
code.Indent--
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
code = code.End
|
||||
default:
|
||||
code = code.Next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpHead(code *Opcode) string {
|
||||
var length uintptr
|
||||
if code.Op.CodeType() == CodeArrayHead {
|
||||
length = code.Length
|
||||
} else {
|
||||
length = code.Length / uintptrSize
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", code.Indent),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.HeadIdx/uintptrSize,
|
||||
code.ElemIdx/uintptrSize,
|
||||
length,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpMapHead(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", code.Indent),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.HeadIdx/uintptrSize,
|
||||
code.ElemIdx/uintptrSize,
|
||||
code.Length/uintptrSize,
|
||||
code.MapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpMapEnd(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", code.Indent),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.MapPos/uintptrSize,
|
||||
code.Length/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpElem(code *Opcode) string {
|
||||
var length uintptr
|
||||
if code.Op.CodeType() == CodeArrayElem {
|
||||
length = code.Length
|
||||
} else {
|
||||
length = code.Length / uintptrSize
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][size:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", code.Indent),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.HeadIdx/uintptrSize,
|
||||
code.ElemIdx/uintptrSize,
|
||||
length,
|
||||
code.Size,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpField(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][key:%s][offset:%d][headIdx:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", code.Indent),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.DisplayKey,
|
||||
code.Offset,
|
||||
code.HeadIdx/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpKey(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", code.Indent),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.ElemIdx/uintptrSize,
|
||||
code.Length/uintptrSize,
|
||||
code.MapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpValue(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%d]%s%s ([idx:%d][mapIter:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", code.Indent),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.MapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) Dump() string {
|
||||
codes := []string{}
|
||||
for code := c; code.Op != OpEnd; {
|
||||
switch code.Op.CodeType() {
|
||||
case CodeSliceHead:
|
||||
codes = append(codes, c.dumpHead(code))
|
||||
code = code.Next
|
||||
case CodeMapHead:
|
||||
codes = append(codes, c.dumpMapHead(code))
|
||||
code = code.Next
|
||||
case CodeArrayElem, CodeSliceElem:
|
||||
codes = append(codes, c.dumpElem(code))
|
||||
code = code.End
|
||||
case CodeMapKey:
|
||||
codes = append(codes, c.dumpKey(code))
|
||||
code = code.End
|
||||
case CodeMapValue:
|
||||
codes = append(codes, c.dumpValue(code))
|
||||
code = code.Next
|
||||
case CodeMapEnd:
|
||||
codes = append(codes, c.dumpMapEnd(code))
|
||||
code = code.Next
|
||||
case CodeStructField:
|
||||
codes = append(codes, c.dumpField(code))
|
||||
code = code.Next
|
||||
case CodeStructEnd:
|
||||
codes = append(codes, c.dumpField(code))
|
||||
code = code.Next
|
||||
default:
|
||||
codes = append(codes, fmt.Sprintf(
|
||||
"[%d]%s%s ([idx:%d])",
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", code.Indent),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
))
|
||||
code = code.Next
|
||||
}
|
||||
}
|
||||
return strings.Join(codes, "\n")
|
||||
}
|
||||
|
||||
func prevField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
|
||||
if _, exists := removedFields[code]; exists {
|
||||
return prevField(code.PrevField, removedFields)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func nextField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
|
||||
if _, exists := removedFields[code]; exists {
|
||||
return nextField(code.NextField, removedFields)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func linkPrevToNextField(cur *Opcode, removedFields map[*Opcode]struct{}) {
|
||||
prev := prevField(cur.PrevField, removedFields)
|
||||
prev.NextField = nextField(cur.NextField, removedFields)
|
||||
code := prev
|
||||
fcode := cur
|
||||
for {
|
||||
var nextCode *Opcode
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
nextCode = code.End
|
||||
default:
|
||||
nextCode = code.Next
|
||||
}
|
||||
if nextCode == fcode {
|
||||
code.Next = fcode.Next
|
||||
break
|
||||
} else if nextCode.Op == OpEnd {
|
||||
break
|
||||
}
|
||||
code = nextCode
|
||||
}
|
||||
}
|
||||
|
||||
func newSliceHeaderCode(ctx *compileContext) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
length := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpSlice,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: idx,
|
||||
HeadIdx: idx,
|
||||
ElemIdx: elemIdx,
|
||||
Length: length,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newSliceElemCode(ctx *compileContext, head *Opcode, size uintptr) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpSliceElem,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
HeadIdx: head.Idx,
|
||||
ElemIdx: head.ElemIdx,
|
||||
Length: head.Length,
|
||||
Indent: ctx.indent,
|
||||
Size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func newArrayHeaderCode(ctx *compileContext, alen int) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpArray,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: idx,
|
||||
HeadIdx: idx,
|
||||
ElemIdx: elemIdx,
|
||||
Indent: ctx.indent,
|
||||
Length: uintptr(alen),
|
||||
}
|
||||
}
|
||||
|
||||
func newArrayElemCode(ctx *compileContext, head *Opcode, length int, size uintptr) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpArrayElem,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
ElemIdx: head.ElemIdx,
|
||||
HeadIdx: head.HeadIdx,
|
||||
Length: uintptr(length),
|
||||
Indent: ctx.indent,
|
||||
Size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapHeaderCode(ctx *compileContext) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
length := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
mapIter := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpMap,
|
||||
Type: ctx.typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: idx,
|
||||
ElemIdx: elemIdx,
|
||||
Length: length,
|
||||
MapIter: mapIter,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpMapKey,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
ElemIdx: head.ElemIdx,
|
||||
Length: head.Length,
|
||||
MapIter: head.MapIter,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapValueCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpMapValue,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
ElemIdx: head.ElemIdx,
|
||||
Length: head.Length,
|
||||
MapIter: head.MapIter,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||
mapPos := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpMapEnd,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: idx,
|
||||
Length: head.Length,
|
||||
MapPos: mapPos,
|
||||
Indent: ctx.indent,
|
||||
Next: newEndOp(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func newInterfaceCode(ctx *compileContext) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpInterface,
|
||||
Type: ctx.typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
Indent: ctx.indent,
|
||||
Next: newEndOp(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
func newRecursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpStructFieldRecursive,
|
||||
Type: ctx.typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
Indent: ctx.indent,
|
||||
Next: newEndOp(ctx),
|
||||
Jmp: jmp,
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
package json
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
|
@ -405,7 +405,7 @@ func stringToUint64Slice(s string) []uint64 {
|
|||
}))
|
||||
}
|
||||
|
||||
func encodeEscapedString(buf []byte, s string) []byte {
|
||||
func AppendEscapedString(buf []byte, s string) []byte {
|
||||
valLen := len(s)
|
||||
if valLen == 0 {
|
||||
return append(buf, `""`...)
|
||||
|
@ -531,7 +531,7 @@ ESCAPE_END:
|
|||
return append(append(buf, s[i:]...), '"')
|
||||
}
|
||||
|
||||
func encodeNoEscapedString(buf []byte, s string) []byte {
|
||||
func AppendString(buf []byte, s string) []byte {
|
||||
valLen := len(s)
|
||||
if valLen == 0 {
|
||||
return append(buf, `""`...)
|
|
@ -0,0 +1,78 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
func load(base uintptr, idx uintptr) uintptr {
|
||||
addr := base + idx
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func store(base uintptr, idx uintptr, p uintptr) {
|
||||
addr := base + idx
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func loadNPtr(base uintptr, idx uintptr, _ int) uintptr {
|
||||
addr := base + idx
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
return ptrToPtr(p)
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
|
||||
func ptrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) }
|
||||
func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
|
||||
func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) }
|
||||
func ptrToPtr(p uintptr) uintptr {
|
||||
return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
|
||||
}
|
||||
func ptrToNPtr(p uintptr, ptrNum int) uintptr {
|
||||
for i := 0; i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = ptrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func ptrToUnsafePtr(p uintptr) unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
|
||||
}
|
||||
func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
|
||||
return *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: code.Type,
|
||||
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
|
||||
}))
|
||||
}
|
||||
|
||||
func appendBool(b []byte, v bool) []byte {
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
}
|
||||
|
||||
func appendNull(b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func appendComma(b []byte) []byte {
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendStructEnd(b []byte) []byte {
|
||||
return append(b, '}', ',')
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,78 @@
|
|||
package vm_escaped
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
func load(base uintptr, idx uintptr) uintptr {
|
||||
addr := base + idx
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func store(base uintptr, idx uintptr, p uintptr) {
|
||||
addr := base + idx
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func loadNPtr(base uintptr, idx uintptr, _ int) uintptr {
|
||||
addr := base + idx
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
return ptrToPtr(p)
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
|
||||
func ptrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) }
|
||||
func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
|
||||
func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) }
|
||||
func ptrToPtr(p uintptr) uintptr {
|
||||
return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
|
||||
}
|
||||
func ptrToNPtr(p uintptr, ptrNum int) uintptr {
|
||||
for i := 0; i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = ptrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func ptrToUnsafePtr(p uintptr) unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
|
||||
}
|
||||
func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
|
||||
return *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: code.Type,
|
||||
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
|
||||
}))
|
||||
}
|
||||
|
||||
func appendBool(b []byte, v bool) []byte {
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
}
|
||||
|
||||
func appendNull(b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func appendComma(b []byte) []byte {
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendStructEnd(b []byte) []byte {
|
||||
return append(b, '}', ',')
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,87 @@
|
|||
package vm_escaped_indent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
func load(base uintptr, idx uintptr) uintptr {
|
||||
addr := base + idx
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func store(base uintptr, idx uintptr, p uintptr) {
|
||||
addr := base + idx
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func loadNPtr(base uintptr, idx uintptr, _ int) uintptr {
|
||||
addr := base + idx
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
return ptrToPtr(p)
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
|
||||
func ptrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) }
|
||||
func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
|
||||
func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) }
|
||||
func ptrToPtr(p uintptr) uintptr {
|
||||
return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
|
||||
}
|
||||
func ptrToNPtr(p uintptr, ptrNum int) uintptr {
|
||||
for i := 0; i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = ptrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func ptrToUnsafePtr(p uintptr) unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
|
||||
}
|
||||
func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
|
||||
return *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: code.Type,
|
||||
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
|
||||
}))
|
||||
}
|
||||
|
||||
func appendBool(b []byte, v bool) []byte {
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
}
|
||||
|
||||
func appendNull(b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func appendComma(b []byte) []byte {
|
||||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func appendStructEnd(ctx *encoder.RuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, '\n')
|
||||
b = append(b, ctx.Prefix...)
|
||||
b = append(b, bytes.Repeat(ctx.IndentStr, ctx.BaseIndent+indent)...)
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func appendIndent(ctx *encoder.RuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, ctx.Prefix...)
|
||||
return append(b, bytes.Repeat(ctx.IndentStr, ctx.BaseIndent+indent)...)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,87 @@
|
|||
package vm_indent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
func load(base uintptr, idx uintptr) uintptr {
|
||||
addr := base + idx
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func store(base uintptr, idx uintptr, p uintptr) {
|
||||
addr := base + idx
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func loadNPtr(base uintptr, idx uintptr, _ int) uintptr {
|
||||
addr := base + idx
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
return ptrToPtr(p)
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
|
||||
func ptrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) }
|
||||
func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
|
||||
func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) }
|
||||
func ptrToPtr(p uintptr) uintptr {
|
||||
return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
|
||||
}
|
||||
func ptrToNPtr(p uintptr, ptrNum int) uintptr {
|
||||
for i := 0; i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = ptrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func ptrToUnsafePtr(p uintptr) unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
|
||||
}
|
||||
func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
|
||||
return *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: code.Type,
|
||||
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
|
||||
}))
|
||||
}
|
||||
|
||||
func appendBool(b []byte, v bool) []byte {
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
}
|
||||
|
||||
func appendNull(b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func appendComma(b []byte) []byte {
|
||||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func appendStructEnd(ctx *encoder.RuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, '\n')
|
||||
b = append(b, ctx.Prefix...)
|
||||
b = append(b, bytes.Repeat(ctx.IndentStr, ctx.BaseIndent+indent)...)
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func appendIndent(ctx *encoder.RuntimeContext, b []byte, indent int) []byte {
|
||||
b = append(b, ctx.Prefix...)
|
||||
return append(b, bytes.Repeat(ctx.IndentStr, ctx.BaseIndent+indent)...)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,157 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type InvalidUTF8Error struct {
|
||||
S string // the whole string value that caused the error
|
||||
}
|
||||
|
||||
func (e *InvalidUTF8Error) Error() string {
|
||||
return fmt.Sprintf("json: invalid UTF-8 in string: %s", strconv.Quote(e.S))
|
||||
}
|
||||
|
||||
type InvalidUnmarshalError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *InvalidUnmarshalError) Error() string {
|
||||
if e.Type == nil {
|
||||
return "json: Unmarshal(nil)"
|
||||
}
|
||||
|
||||
if e.Type.Kind() != reflect.Ptr {
|
||||
return fmt.Sprintf("json: Unmarshal(non-pointer %s)", e.Type)
|
||||
}
|
||||
return fmt.Sprintf("json: Unmarshal(nil %s)", e.Type)
|
||||
}
|
||||
|
||||
// A MarshalerError represents an error from calling a MarshalJSON or MarshalText method.
|
||||
type MarshalerError struct {
|
||||
Type reflect.Type
|
||||
Err error
|
||||
sourceFunc string
|
||||
}
|
||||
|
||||
func (e *MarshalerError) Error() string {
|
||||
srcFunc := e.sourceFunc
|
||||
if srcFunc == "" {
|
||||
srcFunc = "MarshalJSON"
|
||||
}
|
||||
return fmt.Sprintf("json: error calling %s for type %s: %s", srcFunc, e.Type, e.Err.Error())
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error.
|
||||
func (e *MarshalerError) Unwrap() error { return e.Err }
|
||||
|
||||
// A SyntaxError is a description of a JSON syntax error.
|
||||
type SyntaxError struct {
|
||||
msg string // description of error
|
||||
Offset int64 // error occurred after reading Offset bytes
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string { return e.msg }
|
||||
|
||||
// An UnmarshalFieldError describes a JSON object key that
|
||||
// led to an unexported (and therefore unwritable) struct field.
|
||||
//
|
||||
// Deprecated: No longer used; kept for compatibility.
|
||||
type UnmarshalFieldError struct {
|
||||
Key string
|
||||
Type reflect.Type
|
||||
Field reflect.StructField
|
||||
}
|
||||
|
||||
func (e *UnmarshalFieldError) Error() string {
|
||||
return fmt.Sprintf("json: cannot unmarshal object key %s into unexported field %s of type %s",
|
||||
strconv.Quote(e.Key), e.Field.Name, e.Type.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// An UnmarshalTypeError describes a JSON value that was
|
||||
// not appropriate for a value of a specific Go type.
|
||||
type UnmarshalTypeError struct {
|
||||
Value string // description of JSON value - "bool", "array", "number -5"
|
||||
Type reflect.Type // type of Go value it could not be assigned to
|
||||
Offset int64 // error occurred after reading Offset bytes
|
||||
Struct string // name of the struct type containing the field
|
||||
Field string // the full path from root node to the field
|
||||
}
|
||||
|
||||
func (e *UnmarshalTypeError) Error() string {
|
||||
if e.Struct != "" || e.Field != "" {
|
||||
return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s.%s of type %s",
|
||||
e.Value, e.Struct, e.Field, e.Type,
|
||||
)
|
||||
}
|
||||
return fmt.Sprintf("json: cannot unmarshal %s into Go value of type %s", e.Value, e.Type)
|
||||
}
|
||||
|
||||
// An UnsupportedTypeError is returned by Marshal when attempting
|
||||
// to encode an unsupported value type.
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func (e *UnsupportedTypeError) Error() string {
|
||||
return fmt.Sprintf("json: unsupported type: %s", e.Type)
|
||||
}
|
||||
|
||||
type UnsupportedValueError struct {
|
||||
Value reflect.Value
|
||||
Str string
|
||||
}
|
||||
|
||||
func (e *UnsupportedValueError) Error() string {
|
||||
return fmt.Sprintf("json: unsupported value: %s", e.Str)
|
||||
}
|
||||
|
||||
func ErrSyntax(msg string, offset int64) *SyntaxError {
|
||||
return &SyntaxError{msg: msg, Offset: offset}
|
||||
}
|
||||
|
||||
func ErrMarshaler(typ reflect.Type, err error, msg string) *MarshalerError {
|
||||
return &MarshalerError{
|
||||
Type: typ,
|
||||
Err: err,
|
||||
sourceFunc: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrExceededMaxDepth(c byte, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf(`invalid character "%c" exceeded max depth`, c),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrNotAtBeginningOfValue(cursor int64) *SyntaxError {
|
||||
return &SyntaxError{msg: "not at beginning of value", Offset: cursor}
|
||||
}
|
||||
|
||||
func ErrUnexpectedEndOfJSON(msg string, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("json: %s unexpected end of JSON input", msg),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrExpected(msg string, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{msg: fmt.Sprintf("expected %s", msg), Offset: cursor}
|
||||
}
|
||||
|
||||
func ErrInvalidCharacter(c byte, context string, cursor int64) *SyntaxError {
|
||||
if c == 0 {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("json: invalid character as %s", context),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("json: invalid character %c as %s", c, context),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Type representing reflect.rtype for noescape trick
|
||||
type Type struct{}
|
||||
|
||||
//go:linkname rtype_Align reflect.(*rtype).Align
|
||||
//go:noescape
|
||||
func rtype_Align(*Type) int
|
||||
|
||||
func (t *Type) Align() int {
|
||||
return rtype_Align(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_FieldAlign reflect.(*rtype).FieldAlign
|
||||
//go:noescape
|
||||
func rtype_FieldAlign(*Type) int
|
||||
|
||||
func (t *Type) FieldAlign() int {
|
||||
return rtype_FieldAlign(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Method reflect.(*rtype).Method
|
||||
//go:noescape
|
||||
func rtype_Method(*Type, int) reflect.Method
|
||||
|
||||
func (t *Type) Method(a0 int) reflect.Method {
|
||||
return rtype_Method(t, a0)
|
||||
}
|
||||
|
||||
//go:linkname rtype_MethodByName reflect.(*rtype).MethodByName
|
||||
//go:noescape
|
||||
func rtype_MethodByName(*Type, string) (reflect.Method, bool)
|
||||
|
||||
func (t *Type) MethodByName(a0 string) (reflect.Method, bool) {
|
||||
return rtype_MethodByName(t, a0)
|
||||
}
|
||||
|
||||
//go:linkname rtype_NumMethod reflect.(*rtype).NumMethod
|
||||
//go:noescape
|
||||
func rtype_NumMethod(*Type) int
|
||||
|
||||
func (t *Type) NumMethod() int {
|
||||
return rtype_NumMethod(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Name reflect.(*rtype).Name
|
||||
//go:noescape
|
||||
func rtype_Name(*Type) string
|
||||
|
||||
func (t *Type) Name() string {
|
||||
return rtype_Name(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_PkgPath reflect.(*rtype).PkgPath
|
||||
//go:noescape
|
||||
func rtype_PkgPath(*Type) string
|
||||
|
||||
func (t *Type) PkgPath() string {
|
||||
return rtype_PkgPath(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Size reflect.(*rtype).Size
|
||||
//go:noescape
|
||||
func rtype_Size(*Type) uintptr
|
||||
|
||||
func (t *Type) Size() uintptr {
|
||||
return rtype_Size(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_String reflect.(*rtype).String
|
||||
//go:noescape
|
||||
func rtype_String(*Type) string
|
||||
|
||||
func (t *Type) String() string {
|
||||
return rtype_String(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Kind reflect.(*rtype).Kind
|
||||
//go:noescape
|
||||
func rtype_Kind(*Type) reflect.Kind
|
||||
|
||||
func (t *Type) Kind() reflect.Kind {
|
||||
return rtype_Kind(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Implements reflect.(*rtype).Implements
|
||||
//go:noescape
|
||||
func rtype_Implements(*Type, reflect.Type) bool
|
||||
|
||||
func (t *Type) Implements(u reflect.Type) bool {
|
||||
return rtype_Implements(t, u)
|
||||
}
|
||||
|
||||
//go:linkname rtype_AssignableTo reflect.(*rtype).AssignableTo
|
||||
//go:noescape
|
||||
func rtype_AssignableTo(*Type, reflect.Type) bool
|
||||
|
||||
func (t *Type) AssignableTo(u reflect.Type) bool {
|
||||
return rtype_AssignableTo(t, u)
|
||||
}
|
||||
|
||||
//go:linkname rtype_ConvertibleTo reflect.(*rtype).ConvertibleTo
|
||||
//go:noescape
|
||||
func rtype_ConvertibleTo(*Type, reflect.Type) bool
|
||||
|
||||
func (t *Type) ConvertibleTo(u reflect.Type) bool {
|
||||
return rtype_ConvertibleTo(t, u)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Comparable reflect.(*rtype).Comparable
|
||||
//go:noescape
|
||||
func rtype_Comparable(*Type) bool
|
||||
|
||||
func (t *Type) Comparable() bool {
|
||||
return rtype_Comparable(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Bits reflect.(*rtype).Bits
|
||||
//go:noescape
|
||||
func rtype_Bits(*Type) int
|
||||
|
||||
func (t *Type) Bits() int {
|
||||
return rtype_Bits(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_ChanDir reflect.(*rtype).ChanDir
|
||||
//go:noescape
|
||||
func rtype_ChanDir(*Type) reflect.ChanDir
|
||||
|
||||
func (t *Type) ChanDir() reflect.ChanDir {
|
||||
return rtype_ChanDir(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_IsVariadic reflect.(*rtype).IsVariadic
|
||||
//go:noescape
|
||||
func rtype_IsVariadic(*Type) bool
|
||||
|
||||
func (t *Type) IsVariadic() bool {
|
||||
return rtype_IsVariadic(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Elem reflect.(*rtype).Elem
|
||||
//go:noescape
|
||||
func rtype_Elem(*Type) reflect.Type
|
||||
|
||||
func (t *Type) Elem() *Type {
|
||||
return Type2RType(rtype_Elem(t))
|
||||
}
|
||||
|
||||
//go:linkname rtype_Field reflect.(*rtype).Field
|
||||
//go:noescape
|
||||
func rtype_Field(*Type, int) reflect.StructField
|
||||
|
||||
func (t *Type) Field(i int) reflect.StructField {
|
||||
return rtype_Field(t, i)
|
||||
}
|
||||
|
||||
//go:linkname rtype_FieldByIndex reflect.(*rtype).FieldByIndex
|
||||
//go:noescape
|
||||
func rtype_FieldByIndex(*Type, []int) reflect.StructField
|
||||
|
||||
func (t *Type) FieldByIndex(index []int) reflect.StructField {
|
||||
return rtype_FieldByIndex(t, index)
|
||||
}
|
||||
|
||||
//go:linkname rtype_FieldByName reflect.(*rtype).FieldByName
|
||||
//go:noescape
|
||||
func rtype_FieldByName(*Type, string) (reflect.StructField, bool)
|
||||
|
||||
func (t *Type) FieldByName(name string) (reflect.StructField, bool) {
|
||||
return rtype_FieldByName(t, name)
|
||||
}
|
||||
|
||||
//go:linkname rtype_FieldByNameFunc reflect.(*rtype).FieldByNameFunc
|
||||
//go:noescape
|
||||
func rtype_FieldByNameFunc(*Type, func(string) bool) (reflect.StructField, bool)
|
||||
|
||||
func (t *Type) FieldByNameFunc(match func(string) bool) (reflect.StructField, bool) {
|
||||
return rtype_FieldByNameFunc(t, match)
|
||||
}
|
||||
|
||||
//go:linkname rtype_In reflect.(*rtype).In
|
||||
//go:noescape
|
||||
func rtype_In(*Type, int) reflect.Type
|
||||
|
||||
func (t *Type) In(i int) reflect.Type {
|
||||
return rtype_In(t, i)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Key reflect.(*rtype).Key
|
||||
//go:noescape
|
||||
func rtype_Key(*Type) reflect.Type
|
||||
|
||||
func (t *Type) Key() *Type {
|
||||
return Type2RType(rtype_Key(t))
|
||||
}
|
||||
|
||||
//go:linkname rtype_Len reflect.(*rtype).Len
|
||||
//go:noescape
|
||||
func rtype_Len(*Type) int
|
||||
|
||||
func (t *Type) Len() int {
|
||||
return rtype_Len(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_NumField reflect.(*rtype).NumField
|
||||
//go:noescape
|
||||
func rtype_NumField(*Type) int
|
||||
|
||||
func (t *Type) NumField() int {
|
||||
return rtype_NumField(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_NumIn reflect.(*rtype).NumIn
|
||||
//go:noescape
|
||||
func rtype_NumIn(*Type) int
|
||||
|
||||
func (t *Type) NumIn() int {
|
||||
return rtype_NumIn(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_NumOut reflect.(*rtype).NumOut
|
||||
//go:noescape
|
||||
func rtype_NumOut(*Type) int
|
||||
|
||||
func (t *Type) NumOut() int {
|
||||
return rtype_NumOut(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Out reflect.(*rtype).Out
|
||||
//go:noescape
|
||||
func rtype_Out(*Type, int) reflect.Type
|
||||
|
||||
//go:linkname PtrTo reflect.(*rtype).ptrTo
|
||||
//go:noescape
|
||||
func PtrTo(*Type) *Type
|
||||
|
||||
func (t *Type) Out(i int) reflect.Type {
|
||||
return rtype_Out(t, i)
|
||||
}
|
||||
|
||||
//go:linkname IfaceIndir reflect.ifaceIndir
|
||||
//go:noescape
|
||||
func IfaceIndir(*Type) bool
|
||||
|
||||
//go:linkname RType2Type reflect.toType
|
||||
//go:noescape
|
||||
func RType2Type(t *Type) reflect.Type
|
||||
|
||||
//go:nolint structcheck
|
||||
type emptyInterface struct {
|
||||
_ *Type
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func Type2RType(t reflect.Type) *Type {
|
||||
return (*Type)(((*emptyInterface)(unsafe.Pointer(&t))).ptr)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package json
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
@ -10,7 +10,7 @@ func getTag(field reflect.StructField) string {
|
|||
return field.Tag.Get("json")
|
||||
}
|
||||
|
||||
func isIgnoredStructField(field reflect.StructField) bool {
|
||||
func IsIgnoredStructField(field reflect.StructField) bool {
|
||||
if field.PkgPath != "" {
|
||||
if field.Anonymous {
|
||||
if !(field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct) && field.Type.Kind() != reflect.Struct {
|
||||
|
@ -25,19 +25,19 @@ func isIgnoredStructField(field reflect.StructField) bool {
|
|||
return tag == "-"
|
||||
}
|
||||
|
||||
type structTag struct {
|
||||
key string
|
||||
isTaggedKey bool
|
||||
isOmitEmpty bool
|
||||
isString bool
|
||||
field reflect.StructField
|
||||
type StructTag struct {
|
||||
Key string
|
||||
IsTaggedKey bool
|
||||
IsOmitEmpty bool
|
||||
IsString bool
|
||||
Field reflect.StructField
|
||||
}
|
||||
|
||||
type structTags []*structTag
|
||||
type StructTags []*StructTag
|
||||
|
||||
func (t structTags) existsKey(key string) bool {
|
||||
func (t StructTags) ExistsKey(key string) bool {
|
||||
for _, tt := range t {
|
||||
if tt.key == key {
|
||||
if tt.Key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -61,21 +61,21 @@ func isValidTag(s string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func structTagFromField(field reflect.StructField) *structTag {
|
||||
func StructTagFromField(field reflect.StructField) *StructTag {
|
||||
keyName := field.Name
|
||||
tag := getTag(field)
|
||||
st := &structTag{field: field}
|
||||
st := &StructTag{Field: field}
|
||||
opts := strings.Split(tag, ",")
|
||||
if len(opts) > 0 {
|
||||
if opts[0] != "" && isValidTag(opts[0]) {
|
||||
keyName = opts[0]
|
||||
st.isTaggedKey = true
|
||||
st.IsTaggedKey = true
|
||||
}
|
||||
}
|
||||
st.key = keyName
|
||||
st.Key = keyName
|
||||
if len(opts) > 1 {
|
||||
st.isOmitEmpty = opts[1] == "omitempty"
|
||||
st.isString = opts[1] == "string"
|
||||
st.IsOmitEmpty = opts[1] == "omitempty"
|
||||
st.IsString = opts[1] == "string"
|
||||
}
|
||||
return st
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SliceHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
Cap int
|
||||
}
|
||||
|
||||
const (
|
||||
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
|
||||
)
|
||||
|
||||
type TypeAddr struct {
|
||||
BaseTypeAddr uintptr
|
||||
MaxTypeAddr uintptr
|
||||
AddrRange uintptr
|
||||
}
|
||||
|
||||
var (
|
||||
typeAddr *TypeAddr
|
||||
alreadyAnalyzed bool
|
||||
)
|
||||
|
||||
//go:linkname typelinks reflect.typelinks
|
||||
func typelinks() ([]unsafe.Pointer, [][]int32)
|
||||
|
||||
//go:linkname rtypeOff reflect.rtypeOff
|
||||
func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer
|
||||
|
||||
func AnalyzeTypeAddr() *TypeAddr {
|
||||
defer func() {
|
||||
alreadyAnalyzed = true
|
||||
}()
|
||||
if alreadyAnalyzed {
|
||||
return typeAddr
|
||||
}
|
||||
sections, offsets := typelinks()
|
||||
if len(sections) != 1 {
|
||||
return nil
|
||||
}
|
||||
if len(offsets) != 1 {
|
||||
return nil
|
||||
}
|
||||
section := sections[0]
|
||||
offset := offsets[0]
|
||||
var (
|
||||
min uintptr = uintptr(^uint(0))
|
||||
max uintptr = 0
|
||||
)
|
||||
for i := 0; i < len(offset); i++ {
|
||||
typ := (*Type)(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 nil
|
||||
}
|
||||
if addrRange > maxAcceptableTypeAddrRange {
|
||||
return nil
|
||||
}
|
||||
typeAddr = &TypeAddr{
|
||||
BaseTypeAddr: min,
|
||||
MaxTypeAddr: max,
|
||||
AddrRange: addrRange,
|
||||
}
|
||||
return typeAddr
|
||||
}
|
6
json.go
6
json.go
|
@ -4,6 +4,8 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
)
|
||||
|
||||
// Marshaler is the interface implemented by types that
|
||||
|
@ -308,7 +310,7 @@ func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
|||
// Compact appends to dst the JSON-encoded src with
|
||||
// insignificant space characters elided.
|
||||
func Compact(dst *bytes.Buffer, src []byte) error {
|
||||
return compact(dst, src, false)
|
||||
return encoder.Compact(dst, src, false)
|
||||
}
|
||||
|
||||
// Indent appends to dst an indented form of the JSON-encoded src.
|
||||
|
@ -323,7 +325,7 @@ func Compact(dst *bytes.Buffer, src []byte) error {
|
|||
// For example, if src has no trailing spaces, neither will dst;
|
||||
// if src ends in a trailing newline, so will dst.
|
||||
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
|
||||
return encodeWithIndent(dst, src, prefix, indent)
|
||||
return encoder.Indent(dst, src, prefix, indent)
|
||||
}
|
||||
|
||||
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
|
||||
|
|
263
rtype.go
263
rtype.go
|
@ -3,260 +3,25 @@ package json
|
|||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
// rtype representing reflect.rtype for noescape trick
|
||||
type rtype struct{}
|
||||
type rtype = runtime.Type
|
||||
|
||||
//go:linkname rtype_Align reflect.(*rtype).Align
|
||||
//go:noescape
|
||||
func rtype_Align(*rtype) int
|
||||
|
||||
func (t *rtype) Align() int {
|
||||
return rtype_Align(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_FieldAlign reflect.(*rtype).FieldAlign
|
||||
//go:noescape
|
||||
func rtype_FieldAlign(*rtype) int
|
||||
|
||||
func (t *rtype) FieldAlign() int {
|
||||
return rtype_FieldAlign(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Method reflect.(*rtype).Method
|
||||
//go:noescape
|
||||
func rtype_Method(*rtype, int) reflect.Method
|
||||
|
||||
func (t *rtype) Method(a0 int) reflect.Method {
|
||||
return rtype_Method(t, a0)
|
||||
}
|
||||
|
||||
//go:linkname rtype_MethodByName reflect.(*rtype).MethodByName
|
||||
//go:noescape
|
||||
func rtype_MethodByName(*rtype, string) (reflect.Method, bool)
|
||||
|
||||
func (t *rtype) MethodByName(a0 string) (reflect.Method, bool) {
|
||||
return rtype_MethodByName(t, a0)
|
||||
}
|
||||
|
||||
//go:linkname rtype_NumMethod reflect.(*rtype).NumMethod
|
||||
//go:noescape
|
||||
func rtype_NumMethod(*rtype) int
|
||||
|
||||
func (t *rtype) NumMethod() int {
|
||||
return rtype_NumMethod(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Name reflect.(*rtype).Name
|
||||
//go:noescape
|
||||
func rtype_Name(*rtype) string
|
||||
|
||||
func (t *rtype) Name() string {
|
||||
return rtype_Name(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_PkgPath reflect.(*rtype).PkgPath
|
||||
//go:noescape
|
||||
func rtype_PkgPath(*rtype) string
|
||||
|
||||
func (t *rtype) PkgPath() string {
|
||||
return rtype_PkgPath(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Size reflect.(*rtype).Size
|
||||
//go:noescape
|
||||
func rtype_Size(*rtype) uintptr
|
||||
|
||||
func (t *rtype) Size() uintptr {
|
||||
return rtype_Size(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_String reflect.(*rtype).String
|
||||
//go:noescape
|
||||
func rtype_String(*rtype) string
|
||||
|
||||
func (t *rtype) String() string {
|
||||
return rtype_String(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Kind reflect.(*rtype).Kind
|
||||
//go:noescape
|
||||
func rtype_Kind(*rtype) reflect.Kind
|
||||
|
||||
func (t *rtype) Kind() reflect.Kind {
|
||||
return rtype_Kind(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Implements reflect.(*rtype).Implements
|
||||
//go:noescape
|
||||
func rtype_Implements(*rtype, reflect.Type) bool
|
||||
|
||||
func (t *rtype) Implements(u reflect.Type) bool {
|
||||
return rtype_Implements(t, u)
|
||||
}
|
||||
|
||||
//go:linkname rtype_AssignableTo reflect.(*rtype).AssignableTo
|
||||
//go:noescape
|
||||
func rtype_AssignableTo(*rtype, reflect.Type) bool
|
||||
|
||||
func (t *rtype) AssignableTo(u reflect.Type) bool {
|
||||
return rtype_AssignableTo(t, u)
|
||||
}
|
||||
|
||||
//go:linkname rtype_ConvertibleTo reflect.(*rtype).ConvertibleTo
|
||||
//go:noescape
|
||||
func rtype_ConvertibleTo(*rtype, reflect.Type) bool
|
||||
|
||||
func (t *rtype) ConvertibleTo(u reflect.Type) bool {
|
||||
return rtype_ConvertibleTo(t, u)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Comparable reflect.(*rtype).Comparable
|
||||
//go:noescape
|
||||
func rtype_Comparable(*rtype) bool
|
||||
|
||||
func (t *rtype) Comparable() bool {
|
||||
return rtype_Comparable(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Bits reflect.(*rtype).Bits
|
||||
//go:noescape
|
||||
func rtype_Bits(*rtype) int
|
||||
|
||||
func (t *rtype) Bits() int {
|
||||
return rtype_Bits(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_ChanDir reflect.(*rtype).ChanDir
|
||||
//go:noescape
|
||||
func rtype_ChanDir(*rtype) reflect.ChanDir
|
||||
|
||||
func (t *rtype) ChanDir() reflect.ChanDir {
|
||||
return rtype_ChanDir(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_IsVariadic reflect.(*rtype).IsVariadic
|
||||
//go:noescape
|
||||
func rtype_IsVariadic(*rtype) bool
|
||||
|
||||
func (t *rtype) IsVariadic() bool {
|
||||
return rtype_IsVariadic(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Elem reflect.(*rtype).Elem
|
||||
//go:noescape
|
||||
func rtype_Elem(*rtype) reflect.Type
|
||||
|
||||
func (t *rtype) Elem() *rtype {
|
||||
return type2rtype(rtype_Elem(t))
|
||||
}
|
||||
|
||||
//go:linkname rtype_Field reflect.(*rtype).Field
|
||||
//go:noescape
|
||||
func rtype_Field(*rtype, int) reflect.StructField
|
||||
|
||||
func (t *rtype) Field(i int) reflect.StructField {
|
||||
return rtype_Field(t, i)
|
||||
}
|
||||
|
||||
//go:linkname rtype_FieldByIndex reflect.(*rtype).FieldByIndex
|
||||
//go:noescape
|
||||
func rtype_FieldByIndex(*rtype, []int) reflect.StructField
|
||||
|
||||
func (t *rtype) FieldByIndex(index []int) reflect.StructField {
|
||||
return rtype_FieldByIndex(t, index)
|
||||
}
|
||||
|
||||
//go:linkname rtype_FieldByName reflect.(*rtype).FieldByName
|
||||
//go:noescape
|
||||
func rtype_FieldByName(*rtype, string) (reflect.StructField, bool)
|
||||
|
||||
func (t *rtype) FieldByName(name string) (reflect.StructField, bool) {
|
||||
return rtype_FieldByName(t, name)
|
||||
}
|
||||
|
||||
//go:linkname rtype_FieldByNameFunc reflect.(*rtype).FieldByNameFunc
|
||||
//go:noescape
|
||||
func rtype_FieldByNameFunc(*rtype, func(string) bool) (reflect.StructField, bool)
|
||||
|
||||
func (t *rtype) FieldByNameFunc(match func(string) bool) (reflect.StructField, bool) {
|
||||
return rtype_FieldByNameFunc(t, match)
|
||||
}
|
||||
|
||||
//go:linkname rtype_In reflect.(*rtype).In
|
||||
//go:noescape
|
||||
func rtype_In(*rtype, int) reflect.Type
|
||||
|
||||
func (t *rtype) In(i int) reflect.Type {
|
||||
return rtype_In(t, i)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Key reflect.(*rtype).Key
|
||||
//go:noescape
|
||||
func rtype_Key(*rtype) reflect.Type
|
||||
|
||||
func (t *rtype) Key() *rtype {
|
||||
return type2rtype(rtype_Key(t))
|
||||
}
|
||||
|
||||
//go:linkname rtype_Len reflect.(*rtype).Len
|
||||
//go:noescape
|
||||
func rtype_Len(*rtype) int
|
||||
|
||||
func (t *rtype) Len() int {
|
||||
return rtype_Len(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_NumField reflect.(*rtype).NumField
|
||||
//go:noescape
|
||||
func rtype_NumField(*rtype) int
|
||||
|
||||
func (t *rtype) NumField() int {
|
||||
return rtype_NumField(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_NumIn reflect.(*rtype).NumIn
|
||||
//go:noescape
|
||||
func rtype_NumIn(*rtype) int
|
||||
|
||||
func (t *rtype) NumIn() int {
|
||||
return rtype_NumIn(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_NumOut reflect.(*rtype).NumOut
|
||||
//go:noescape
|
||||
func rtype_NumOut(*rtype) int
|
||||
|
||||
func (t *rtype) NumOut() int {
|
||||
return rtype_NumOut(t)
|
||||
}
|
||||
|
||||
//go:linkname rtype_Out reflect.(*rtype).Out
|
||||
//go:noescape
|
||||
func rtype_Out(*rtype, int) reflect.Type
|
||||
|
||||
//go:linkname rtype_ptrTo reflect.(*rtype).ptrTo
|
||||
//go:noescape
|
||||
func rtype_ptrTo(*rtype) *rtype
|
||||
|
||||
func (t *rtype) Out(i int) reflect.Type {
|
||||
return rtype_Out(t, i)
|
||||
}
|
||||
|
||||
//go:linkname ifaceIndir reflect.ifaceIndir
|
||||
//go:noescape
|
||||
func ifaceIndir(*rtype) bool
|
||||
|
||||
//go:linkname rtype2type reflect.toType
|
||||
//go:noescape
|
||||
func rtype2type(t *rtype) reflect.Type
|
||||
|
||||
type interfaceHeader struct {
|
||||
type emptyInterface struct {
|
||||
typ *rtype
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func type2rtype(t reflect.Type) *rtype {
|
||||
return (*rtype)(((*interfaceHeader)(unsafe.Pointer(&t))).ptr)
|
||||
func rtype_ptrTo(t *rtype) *rtype {
|
||||
return runtime.PtrTo(t)
|
||||
}
|
||||
|
||||
func rtype2type(t *rtype) reflect.Type {
|
||||
return runtime.RType2Type(t)
|
||||
}
|
||||
|
||||
func type2rtype(t reflect.Type) *rtype {
|
||||
return runtime.Type2RType(t)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue