forked from mirror/go-json
Merge branch 'master' into golangci-lint
This commit is contained in:
commit
5f9f30fc8e
|
@ -1,4 +1,7 @@
|
||||||
run:
|
run:
|
||||||
|
skip-files:
|
||||||
|
- encode_optype.go
|
||||||
|
- ".*_test\\.go$"
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
govet:
|
govet:
|
||||||
|
@ -9,6 +12,7 @@ linters-settings:
|
||||||
linters:
|
linters:
|
||||||
enable-all: true
|
enable-all: true
|
||||||
disable:
|
disable:
|
||||||
|
- dogsled
|
||||||
- dupl
|
- dupl
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exhaustivestruct
|
- exhaustivestruct
|
||||||
|
@ -35,6 +39,12 @@ linters:
|
||||||
- testpackage
|
- testpackage
|
||||||
- thelper
|
- thelper
|
||||||
- wrapcheck
|
- wrapcheck
|
||||||
|
- interfacer
|
||||||
|
- lll
|
||||||
|
- nakedret
|
||||||
|
- nestif
|
||||||
|
- nlreturn
|
||||||
|
- testpackage
|
||||||
- wsl
|
- wsl
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
|
@ -48,10 +58,12 @@ issues:
|
||||||
text: "don't use an underscore in package name"
|
text: "don't use an underscore in package name"
|
||||||
linters:
|
linters:
|
||||||
- golint
|
- golint
|
||||||
|
- path: rtype.go
|
||||||
|
linters:
|
||||||
|
- golint
|
||||||
|
|
||||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||||
max-issues-per-linter: 0
|
max-issues-per-linter: 0
|
||||||
|
|
||||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||||
max-same-issues: 0
|
max-same-issues: 0
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,8 @@ type decoder interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
s *stream
|
s *stream
|
||||||
disallowUnknownFields bool
|
structTypeToDecoder map[uintptr]decoder
|
||||||
structTypeToDecoder map[uintptr]decoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type decoderMap struct {
|
type decoderMap struct {
|
||||||
|
@ -240,11 +239,12 @@ func (d *Decoder) Token() (Token, error) {
|
||||||
if s.read() {
|
if s.read() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, io.EOF
|
goto END
|
||||||
default:
|
default:
|
||||||
return nil, errInvalidCharacter(s.char(), "token", s.totalOffset())
|
return nil, errInvalidCharacter(s.char(), "token", s.totalOffset())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
END:
|
||||||
return nil, io.EOF
|
return nil, io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,6 @@ func (d *bytesDecoder) decodeBinary(buf []byte, cursor int64, p unsafe.Pointer)
|
||||||
}
|
}
|
||||||
cursor++
|
cursor++
|
||||||
}
|
}
|
||||||
return nil, 0, errUnexpectedEndOfJSON("[]byte", cursor)
|
|
||||||
case '[':
|
case '[':
|
||||||
if d.sliceDecoder == nil {
|
if d.sliceDecoder == nil {
|
||||||
return nil, 0, &UnmarshalTypeError{
|
return nil, 0, &UnmarshalTypeError{
|
||||||
|
|
|
@ -309,7 +309,7 @@ func (d *Decoder) compileStruct(typ *rtype, structName, fieldName string) (decod
|
||||||
// recursive definition
|
// recursive definition
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
d.removeConflictFields(fieldMap, conflictedMap, stDec, uintptr(field.Offset))
|
d.removeConflictFields(fieldMap, conflictedMap, stDec, field.Offset)
|
||||||
} else if pdec, ok := dec.(*ptrDecoder); ok {
|
} else if pdec, ok := dec.(*ptrDecoder); ok {
|
||||||
contentDec := pdec.contentDecoder()
|
contentDec := pdec.contentDecoder()
|
||||||
if pdec.typ == typ {
|
if pdec.typ == typ {
|
||||||
|
@ -326,7 +326,7 @@ func (d *Decoder) compileStruct(typ *rtype, structName, fieldName string) (decod
|
||||||
if !exists {
|
if !exists {
|
||||||
fieldSet := &structFieldSet{
|
fieldSet := &structFieldSet{
|
||||||
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
|
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
|
||||||
offset: uintptr(field.Offset),
|
offset: field.Offset,
|
||||||
isTaggedKey: v.isTaggedKey,
|
isTaggedKey: v.isTaggedKey,
|
||||||
}
|
}
|
||||||
fieldMap[k] = fieldSet
|
fieldMap[k] = fieldSet
|
||||||
|
@ -347,7 +347,7 @@ func (d *Decoder) compileStruct(typ *rtype, structName, fieldName string) (decod
|
||||||
if v.isTaggedKey {
|
if v.isTaggedKey {
|
||||||
fieldSet := &structFieldSet{
|
fieldSet := &structFieldSet{
|
||||||
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
|
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
|
||||||
offset: uintptr(field.Offset),
|
offset: field.Offset,
|
||||||
isTaggedKey: v.isTaggedKey,
|
isTaggedKey: v.isTaggedKey,
|
||||||
}
|
}
|
||||||
fieldMap[k] = fieldSet
|
fieldMap[k] = fieldSet
|
||||||
|
|
|
@ -142,5 +142,4 @@ func skipValue(buf []byte, cursor int64) (int64, error) {
|
||||||
}
|
}
|
||||||
cursor++
|
cursor++
|
||||||
}
|
}
|
||||||
return cursor, errUnexpectedEndOfJSON("value of object", cursor)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,6 @@ func (d *intDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error)
|
||||||
return nil, 0, d.typeError([]byte{buf[cursor]}, cursor)
|
return nil, 0, d.typeError([]byte{buf[cursor]}, cursor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, 0, errUnexpectedEndOfJSON("number(integer)", cursor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *intDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
func (d *intDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
||||||
|
|
|
@ -143,7 +143,6 @@ func (d *interfaceDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
||||||
}
|
}
|
||||||
s.cursor++
|
s.cursor++
|
||||||
}
|
}
|
||||||
return errUnexpectedEndOfJSON("string", s.totalOffset())
|
|
||||||
case 't':
|
case 't':
|
||||||
if err := trueBytes(s); err != nil {
|
if err := trueBytes(s); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -229,7 +228,6 @@ func (d *interfaceDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (i
|
||||||
}
|
}
|
||||||
cursor++
|
cursor++
|
||||||
}
|
}
|
||||||
return 0, errUnexpectedEndOfJSON("string", cursor)
|
|
||||||
case 't':
|
case 't':
|
||||||
if cursor+3 >= int64(len(buf)) {
|
if cursor+3 >= int64(len(buf)) {
|
||||||
return 0, errUnexpectedEndOfJSON("bool(true)", cursor)
|
return 0, errUnexpectedEndOfJSON("bool(true)", cursor)
|
||||||
|
|
|
@ -94,7 +94,6 @@ func (d *mapDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
||||||
return errExpected("comma after object value", s.totalOffset())
|
return errExpected("comma after object value", s.totalOffset())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *mapDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (int64, error) {
|
func (d *mapDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (int64, error) {
|
||||||
|
@ -151,7 +150,6 @@ func (d *mapDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (int64,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
cursor = valueCursor
|
|
||||||
mapassign(d.mapType, mapValue, unsafe.Pointer(&key), unsafe.Pointer(&value))
|
mapassign(d.mapType, mapValue, unsafe.Pointer(&key), unsafe.Pointer(&value))
|
||||||
cursor = skipWhiteSpace(buf, valueCursor)
|
cursor = skipWhiteSpace(buf, valueCursor)
|
||||||
if buf[cursor] == '}' {
|
if buf[cursor] == '}' {
|
||||||
|
|
|
@ -28,6 +28,7 @@ func (d *ptrDecoder) contentDecoder() decoder {
|
||||||
return dec.contentDecoder()
|
return dec.contentDecoder()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:golint
|
||||||
//go:linkname unsafe_New reflect.unsafe_New
|
//go:linkname unsafe_New reflect.unsafe_New
|
||||||
func unsafe_New(*rtype) unsafe.Pointer
|
func unsafe_New(*rtype) unsafe.Pointer
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ type stream struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
offset int64
|
offset int64
|
||||||
cursor int64
|
cursor int64
|
||||||
readPos int64
|
|
||||||
allRead bool
|
allRead bool
|
||||||
useNumber bool
|
useNumber bool
|
||||||
disallowUnknownFields bool
|
disallowUnknownFields bool
|
||||||
|
@ -200,5 +199,4 @@ func (s *stream) skipValue() error {
|
||||||
}
|
}
|
||||||
s.cursor++
|
s.cursor++
|
||||||
}
|
}
|
||||||
return errUnexpectedEndOfJSON("value of object", s.offset)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,6 +150,7 @@ RETRY:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:deadcode,unused
|
||||||
func appendCoerceInvalidUTF8(b []byte, s []byte) []byte {
|
func appendCoerceInvalidUTF8(b []byte, s []byte) []byte {
|
||||||
c := [4]byte{}
|
c := [4]byte{}
|
||||||
|
|
||||||
|
@ -298,7 +299,6 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
|
||||||
}
|
}
|
||||||
cursor++
|
cursor++
|
||||||
}
|
}
|
||||||
return nil, 0, errUnexpectedEndOfJSON("string", cursor)
|
|
||||||
case 'n':
|
case 'n':
|
||||||
buflen := int64(len(buf))
|
buflen := int64(len(buf))
|
||||||
if cursor+3 >= buflen {
|
if cursor+3 >= buflen {
|
||||||
|
|
|
@ -93,7 +93,6 @@ func (d *structDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
||||||
}
|
}
|
||||||
s.cursor++
|
s.cursor++
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *structDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (int64, error) {
|
func (d *structDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (int64, error) {
|
||||||
|
|
|
@ -70,12 +70,12 @@ func (d *uintDecoder) decodeStreamByte(s *stream) ([]byte, error) {
|
||||||
}
|
}
|
||||||
num := s.buf[start:s.cursor]
|
num := s.buf[start:s.cursor]
|
||||||
return num, nil
|
return num, nil
|
||||||
default:
|
|
||||||
return nil, d.typeError([]byte{s.char()}, s.totalOffset())
|
|
||||||
case nul:
|
case nul:
|
||||||
if s.read() {
|
if s.read() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return nil, d.typeError([]byte{s.char()}, s.totalOffset())
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
10
encode.go
10
encode.go
|
@ -128,7 +128,7 @@ func (e *Encoder) SetIndent(prefix, indent string) {
|
||||||
func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
|
func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
ctx := takeEncodeRuntimeContext()
|
ctx := takeEncodeRuntimeContext()
|
||||||
|
|
||||||
buf, err := encode(ctx, v, EncodeOptionHTMLEscape)
|
buf, err := encode(ctx, v, opt|EncodeOptionHTMLEscape)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
releaseEncodeRuntimeContext(ctx)
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -149,7 +149,7 @@ func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
|
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
ctx := takeEncodeRuntimeContext()
|
ctx := takeEncodeRuntimeContext()
|
||||||
|
|
||||||
buf, err := encodeNoEscape(ctx, v, EncodeOptionHTMLEscape)
|
buf, err := encodeNoEscape(ctx, v, opt|EncodeOptionHTMLEscape)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
releaseEncodeRuntimeContext(ctx)
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -170,7 +170,7 @@ func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||||
ctx := takeEncodeRuntimeContext()
|
ctx := takeEncodeRuntimeContext()
|
||||||
|
|
||||||
buf, err := encodeIndent(ctx, v, prefix, indent, EncodeOptionHTMLEscape)
|
buf, err := encodeIndent(ctx, v, prefix, indent, opt|EncodeOptionHTMLEscape)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
releaseEncodeRuntimeContext(ctx)
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -320,10 +320,6 @@ func encodeBool(b []byte, v bool) []byte {
|
||||||
return append(b, "false"...)
|
return append(b, "false"...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeBytes(dst []byte, src []byte) []byte {
|
|
||||||
return append(dst, src...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeNull(b []byte) []byte {
|
func encodeNull(b []byte) []byte {
|
||||||
return append(b, "null"...)
|
return append(b, "null"...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,12 @@ type opcodeSet struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||||
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||||
cachedOpcode unsafe.Pointer // map[uintptr]*opcodeSet
|
cachedOpcode unsafe.Pointer // map[uintptr]*opcodeSet
|
||||||
baseTypeAddr uintptr
|
baseTypeAddr uintptr
|
||||||
cachedOpcodeSets []*opcodeSet
|
cachedOpcodeSets []*opcodeSet
|
||||||
|
existsCachedOpcodeSets bool
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -72,7 +73,7 @@ func setupOpcodeSets() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addrRange := uintptr(max) - uintptr(min)
|
addrRange := max - min
|
||||||
if addrRange == 0 {
|
if addrRange == 0 {
|
||||||
return fmt.Errorf("failed to get address range of types")
|
return fmt.Errorf("failed to get address range of types")
|
||||||
}
|
}
|
||||||
|
@ -80,43 +81,13 @@ func setupOpcodeSets() error {
|
||||||
return fmt.Errorf("too big address range %d", addrRange)
|
return fmt.Errorf("too big address range %d", addrRange)
|
||||||
}
|
}
|
||||||
cachedOpcodeSets = make([]*opcodeSet, addrRange)
|
cachedOpcodeSets = make([]*opcodeSet, addrRange)
|
||||||
|
existsCachedOpcodeSets = true
|
||||||
baseTypeAddr = min
|
baseTypeAddr = min
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if err := setupOpcodeSets(); err != nil {
|
_ = setupOpcodeSets()
|
||||||
// fallback to slow path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
|
||||||
if cachedOpcodeSets == nil {
|
|
||||||
return encodeCompileToGetCodeSetSlowPath(typeptr)
|
|
||||||
}
|
|
||||||
if codeSet := cachedOpcodeSets[typeptr-baseTypeAddr]; codeSet != nil {
|
|
||||||
return codeSet, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// noescape trick for header.typ ( reflect.*rtype )
|
|
||||||
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
|
|
||||||
|
|
||||||
code, err := encodeCompileHead(&encodeCompileContext{
|
|
||||||
typ: copiedType,
|
|
||||||
root: true,
|
|
||||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
code = copyOpcode(code)
|
|
||||||
codeLength := code.totalLength()
|
|
||||||
codeSet := &opcodeSet{
|
|
||||||
code: code,
|
|
||||||
codeLength: codeLength,
|
|
||||||
}
|
|
||||||
cachedOpcodeSets[int(typeptr-baseTypeAddr)] = codeSet
|
|
||||||
return codeSet, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeCompileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
|
func encodeCompileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
|
||||||
|
@ -799,6 +770,7 @@ func encodeTypeToHeaderType(ctx *encodeCompileContext, code *opcode) opType {
|
||||||
ptrNum++
|
ptrNum++
|
||||||
code = code.next
|
code = code.next
|
||||||
ctx.decIndex()
|
ctx.decIndex()
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -923,6 +895,7 @@ func encodeTypeToFieldType(ctx *encodeCompileContext, code *opcode) opType {
|
||||||
ptrNum++
|
ptrNum++
|
||||||
code = code.next
|
code = code.next
|
||||||
ctx.decIndex()
|
ctx.decIndex()
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1182,7 +1155,7 @@ type structFieldPair struct {
|
||||||
linked bool
|
linked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeAnonymousStructFieldPairMap(typ *rtype, tags structTags, named string, valueCode *opcode) map[string][]structFieldPair {
|
func encodeAnonymousStructFieldPairMap(tags structTags, named string, valueCode *opcode) map[string][]structFieldPair {
|
||||||
anonymousFields := map[string][]structFieldPair{}
|
anonymousFields := map[string][]structFieldPair{}
|
||||||
f := valueCode
|
f := valueCode
|
||||||
var prevAnonymousField *opcode
|
var prevAnonymousField *opcode
|
||||||
|
@ -1224,7 +1197,7 @@ func encodeAnonymousStructFieldPairMap(typ *rtype, tags structTags, named string
|
||||||
isTaggedKey: f.isTaggedKey,
|
isTaggedKey: f.isTaggedKey,
|
||||||
})
|
})
|
||||||
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
|
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
|
||||||
for k, v := range encodeAnonymousStructFieldPairMap(typ, tags, named, f.next) {
|
for k, v := range encodeAnonymousStructFieldPairMap(tags, named, f.next) {
|
||||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1350,7 +1323,7 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
|
||||||
if tag.isTaggedKey {
|
if tag.isTaggedKey {
|
||||||
tagKey = tag.key
|
tagKey = tag.key
|
||||||
}
|
}
|
||||||
for k, v := range encodeAnonymousStructFieldPairMap(typ, tags, tagKey, valueCode) {
|
for k, v := range encodeAnonymousStructFieldPairMap(tags, tagKey, valueCode) {
|
||||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// +build !race
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
||||||
|
if !existsCachedOpcodeSets {
|
||||||
|
return encodeCompileToGetCodeSetSlowPath(typeptr)
|
||||||
|
}
|
||||||
|
if codeSet := cachedOpcodeSets[typeptr-baseTypeAddr]; codeSet != nil {
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// noescape trick for header.typ ( reflect.*rtype )
|
||||||
|
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
|
||||||
|
|
||||||
|
code, err := encodeCompileHead(&encodeCompileContext{
|
||||||
|
typ: copiedType,
|
||||||
|
root: true,
|
||||||
|
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
code = copyOpcode(code)
|
||||||
|
codeLength := code.totalLength()
|
||||||
|
codeSet := &opcodeSet{
|
||||||
|
code: code,
|
||||||
|
codeLength: codeLength,
|
||||||
|
}
|
||||||
|
cachedOpcodeSets[int(typeptr-baseTypeAddr)] = codeSet
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// +build race
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var setsMu sync.RWMutex
|
||||||
|
|
||||||
|
func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
||||||
|
if !existsCachedOpcodeSets {
|
||||||
|
return encodeCompileToGetCodeSetSlowPath(typeptr)
|
||||||
|
}
|
||||||
|
setsMu.RLock()
|
||||||
|
if codeSet := cachedOpcodeSets[typeptr-baseTypeAddr]; codeSet != nil {
|
||||||
|
setsMu.RUnlock()
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
||||||
|
setsMu.RUnlock()
|
||||||
|
|
||||||
|
// noescape trick for header.typ ( reflect.*rtype )
|
||||||
|
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
|
||||||
|
|
||||||
|
code, err := encodeCompileHead(&encodeCompileContext{
|
||||||
|
typ: copiedType,
|
||||||
|
root: true,
|
||||||
|
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
code = copyOpcode(code)
|
||||||
|
codeLength := code.totalLength()
|
||||||
|
codeSet := &opcodeSet{
|
||||||
|
code: code,
|
||||||
|
codeLength: codeLength,
|
||||||
|
}
|
||||||
|
setsMu.Lock()
|
||||||
|
cachedOpcodeSets[int(typeptr-baseTypeAddr)] = codeSet
|
||||||
|
setsMu.Unlock()
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
|
@ -28,7 +28,6 @@ func (m *mapslice) Swap(i, j int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type encodeMapContext struct {
|
type encodeMapContext struct {
|
||||||
iter unsafe.Pointer
|
|
||||||
pos []int
|
pos []int
|
||||||
slice *mapslice
|
slice *mapslice
|
||||||
buf []byte
|
buf []byte
|
||||||
|
|
|
@ -124,7 +124,6 @@ func (c *opcode) beforeLastCode() *opcode {
|
||||||
}
|
}
|
||||||
code = nextCode
|
code = nextCode
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *opcode) totalLength() int {
|
func (c *opcode) totalLength() int {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDumpOpcode(t *testing.T) {
|
||||||
|
var v interface{} = 1
|
||||||
|
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||||
|
typ := header.typ
|
||||||
|
typeptr := uintptr(unsafe.Pointer(typ))
|
||||||
|
codeSet, err := encodeCompileToGetCodeSet(typeptr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
codeSet.code.dump()
|
||||||
|
}
|
371
encode_string.go
371
encode_string.go
|
@ -347,345 +347,6 @@ var needEscape = [256]bool{
|
||||||
0xff: true,
|
0xff: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// htmlSafeSet holds the value true if the ASCII character with the given
|
|
||||||
// array position can be safely represented inside a JSON string, embedded
|
|
||||||
// inside of HTML <script> tags, without any additional escaping.
|
|
||||||
//
|
|
||||||
// All values are true except for the ASCII control characters (0-31), the
|
|
||||||
// double quote ("), the backslash character ("\"), HTML opening and closing
|
|
||||||
// tags ("<" and ">"), and the ampersand ("&").
|
|
||||||
var htmlSafeSet = [256]bool{
|
|
||||||
' ': true,
|
|
||||||
'!': true,
|
|
||||||
'"': false,
|
|
||||||
'#': true,
|
|
||||||
'$': true,
|
|
||||||
'%': true,
|
|
||||||
'&': false,
|
|
||||||
'\'': true,
|
|
||||||
'(': true,
|
|
||||||
')': true,
|
|
||||||
'*': true,
|
|
||||||
'+': true,
|
|
||||||
',': true,
|
|
||||||
'-': true,
|
|
||||||
'.': true,
|
|
||||||
'/': true,
|
|
||||||
'0': true,
|
|
||||||
'1': true,
|
|
||||||
'2': true,
|
|
||||||
'3': true,
|
|
||||||
'4': true,
|
|
||||||
'5': true,
|
|
||||||
'6': true,
|
|
||||||
'7': true,
|
|
||||||
'8': true,
|
|
||||||
'9': true,
|
|
||||||
':': true,
|
|
||||||
';': true,
|
|
||||||
'<': false,
|
|
||||||
'=': true,
|
|
||||||
'>': false,
|
|
||||||
'?': true,
|
|
||||||
'@': true,
|
|
||||||
'A': true,
|
|
||||||
'B': true,
|
|
||||||
'C': true,
|
|
||||||
'D': true,
|
|
||||||
'E': true,
|
|
||||||
'F': true,
|
|
||||||
'G': true,
|
|
||||||
'H': true,
|
|
||||||
'I': true,
|
|
||||||
'J': true,
|
|
||||||
'K': true,
|
|
||||||
'L': true,
|
|
||||||
'M': true,
|
|
||||||
'N': true,
|
|
||||||
'O': true,
|
|
||||||
'P': true,
|
|
||||||
'Q': true,
|
|
||||||
'R': true,
|
|
||||||
'S': true,
|
|
||||||
'T': true,
|
|
||||||
'U': true,
|
|
||||||
'V': true,
|
|
||||||
'W': true,
|
|
||||||
'X': true,
|
|
||||||
'Y': true,
|
|
||||||
'Z': true,
|
|
||||||
'[': true,
|
|
||||||
'\\': false,
|
|
||||||
']': true,
|
|
||||||
'^': true,
|
|
||||||
'_': true,
|
|
||||||
'`': true,
|
|
||||||
'a': true,
|
|
||||||
'b': true,
|
|
||||||
'c': true,
|
|
||||||
'd': true,
|
|
||||||
'e': true,
|
|
||||||
'f': true,
|
|
||||||
'g': true,
|
|
||||||
'h': true,
|
|
||||||
'i': true,
|
|
||||||
'j': true,
|
|
||||||
'k': true,
|
|
||||||
'l': true,
|
|
||||||
'm': true,
|
|
||||||
'n': true,
|
|
||||||
'o': true,
|
|
||||||
'p': true,
|
|
||||||
'q': true,
|
|
||||||
'r': true,
|
|
||||||
's': true,
|
|
||||||
't': true,
|
|
||||||
'u': true,
|
|
||||||
'v': true,
|
|
||||||
'w': true,
|
|
||||||
'x': true,
|
|
||||||
'y': true,
|
|
||||||
'z': true,
|
|
||||||
'{': true,
|
|
||||||
'|': true,
|
|
||||||
'}': true,
|
|
||||||
'~': true,
|
|
||||||
'\u007f': true,
|
|
||||||
0x80: false,
|
|
||||||
0x81: false,
|
|
||||||
0x82: false,
|
|
||||||
0x83: false,
|
|
||||||
0x84: false,
|
|
||||||
0x85: false,
|
|
||||||
0x86: false,
|
|
||||||
0x87: false,
|
|
||||||
0x88: false,
|
|
||||||
0x89: false,
|
|
||||||
0x8a: false,
|
|
||||||
0x8b: false,
|
|
||||||
0x8c: false,
|
|
||||||
0x8d: false,
|
|
||||||
0x8e: false,
|
|
||||||
0x8f: false,
|
|
||||||
0x90: false,
|
|
||||||
0x91: false,
|
|
||||||
0x92: false,
|
|
||||||
0x93: false,
|
|
||||||
0x94: false,
|
|
||||||
0x95: false,
|
|
||||||
0x96: false,
|
|
||||||
0x97: false,
|
|
||||||
0x98: false,
|
|
||||||
0x99: false,
|
|
||||||
0x9a: false,
|
|
||||||
0x9b: false,
|
|
||||||
0x9c: false,
|
|
||||||
0x9d: false,
|
|
||||||
0x9e: false,
|
|
||||||
0x9f: false,
|
|
||||||
0xa0: false,
|
|
||||||
0xa1: false,
|
|
||||||
0xa2: false,
|
|
||||||
0xa3: false,
|
|
||||||
0xa4: false,
|
|
||||||
0xa5: false,
|
|
||||||
0xa6: false,
|
|
||||||
0xa7: false,
|
|
||||||
0xa8: false,
|
|
||||||
0xa9: false,
|
|
||||||
0xaa: false,
|
|
||||||
0xab: false,
|
|
||||||
0xac: false,
|
|
||||||
0xad: false,
|
|
||||||
0xae: false,
|
|
||||||
0xaf: false,
|
|
||||||
0xb0: false,
|
|
||||||
0xb1: false,
|
|
||||||
0xb2: false,
|
|
||||||
0xb3: false,
|
|
||||||
0xb4: false,
|
|
||||||
0xb5: false,
|
|
||||||
0xb6: false,
|
|
||||||
0xb7: false,
|
|
||||||
0xb8: false,
|
|
||||||
0xb9: false,
|
|
||||||
0xba: false,
|
|
||||||
0xbb: false,
|
|
||||||
0xbc: false,
|
|
||||||
0xbd: false,
|
|
||||||
0xbe: false,
|
|
||||||
0xbf: false,
|
|
||||||
0xc0: false,
|
|
||||||
0xc1: false,
|
|
||||||
0xc2: false,
|
|
||||||
0xc3: false,
|
|
||||||
0xc4: false,
|
|
||||||
0xc5: false,
|
|
||||||
0xc6: false,
|
|
||||||
0xc7: false,
|
|
||||||
0xc8: false,
|
|
||||||
0xc9: false,
|
|
||||||
0xca: false,
|
|
||||||
0xcb: false,
|
|
||||||
0xcc: false,
|
|
||||||
0xcd: false,
|
|
||||||
0xce: false,
|
|
||||||
0xcf: false,
|
|
||||||
0xd0: false,
|
|
||||||
0xd1: false,
|
|
||||||
0xd2: false,
|
|
||||||
0xd3: false,
|
|
||||||
0xd4: false,
|
|
||||||
0xd5: false,
|
|
||||||
0xd6: false,
|
|
||||||
0xd7: false,
|
|
||||||
0xd8: false,
|
|
||||||
0xd9: false,
|
|
||||||
0xda: false,
|
|
||||||
0xdb: false,
|
|
||||||
0xdc: false,
|
|
||||||
0xdd: false,
|
|
||||||
0xde: false,
|
|
||||||
0xdf: false,
|
|
||||||
0xe0: false,
|
|
||||||
0xe1: false,
|
|
||||||
0xe2: false,
|
|
||||||
0xe3: false,
|
|
||||||
0xe4: false,
|
|
||||||
0xe5: false,
|
|
||||||
0xe6: false,
|
|
||||||
0xe7: false,
|
|
||||||
0xe8: false,
|
|
||||||
0xe9: false,
|
|
||||||
0xea: false,
|
|
||||||
0xeb: false,
|
|
||||||
0xec: false,
|
|
||||||
0xed: false,
|
|
||||||
0xee: false,
|
|
||||||
0xef: false,
|
|
||||||
0xf0: false,
|
|
||||||
0xf1: false,
|
|
||||||
0xf2: false,
|
|
||||||
0xf3: false,
|
|
||||||
0xf4: false,
|
|
||||||
0xf5: false,
|
|
||||||
0xf6: false,
|
|
||||||
0xf7: false,
|
|
||||||
0xf8: false,
|
|
||||||
0xf9: false,
|
|
||||||
0xfa: false,
|
|
||||||
0xfb: false,
|
|
||||||
0xfc: false,
|
|
||||||
0xfd: false,
|
|
||||||
0xfe: false,
|
|
||||||
0xff: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// safeSet holds the value true if the ASCII character with the given array
|
|
||||||
// position can be represented inside a JSON string without any further
|
|
||||||
// escaping.
|
|
||||||
//
|
|
||||||
// All values are true except for the ASCII control characters (0-31), the
|
|
||||||
// double quote ("), and the backslash character ("\").
|
|
||||||
var safeSet = [utf8.RuneSelf]bool{
|
|
||||||
' ': true,
|
|
||||||
'!': true,
|
|
||||||
'"': false,
|
|
||||||
'#': true,
|
|
||||||
'$': true,
|
|
||||||
'%': true,
|
|
||||||
'&': true,
|
|
||||||
'\'': true,
|
|
||||||
'(': true,
|
|
||||||
')': true,
|
|
||||||
'*': true,
|
|
||||||
'+': true,
|
|
||||||
',': true,
|
|
||||||
'-': true,
|
|
||||||
'.': true,
|
|
||||||
'/': true,
|
|
||||||
'0': true,
|
|
||||||
'1': true,
|
|
||||||
'2': true,
|
|
||||||
'3': true,
|
|
||||||
'4': true,
|
|
||||||
'5': true,
|
|
||||||
'6': true,
|
|
||||||
'7': true,
|
|
||||||
'8': true,
|
|
||||||
'9': true,
|
|
||||||
':': true,
|
|
||||||
';': true,
|
|
||||||
'<': true,
|
|
||||||
'=': true,
|
|
||||||
'>': true,
|
|
||||||
'?': true,
|
|
||||||
'@': true,
|
|
||||||
'A': true,
|
|
||||||
'B': true,
|
|
||||||
'C': true,
|
|
||||||
'D': true,
|
|
||||||
'E': true,
|
|
||||||
'F': true,
|
|
||||||
'G': true,
|
|
||||||
'H': true,
|
|
||||||
'I': true,
|
|
||||||
'J': true,
|
|
||||||
'K': true,
|
|
||||||
'L': true,
|
|
||||||
'M': true,
|
|
||||||
'N': true,
|
|
||||||
'O': true,
|
|
||||||
'P': true,
|
|
||||||
'Q': true,
|
|
||||||
'R': true,
|
|
||||||
'S': true,
|
|
||||||
'T': true,
|
|
||||||
'U': true,
|
|
||||||
'V': true,
|
|
||||||
'W': true,
|
|
||||||
'X': true,
|
|
||||||
'Y': true,
|
|
||||||
'Z': true,
|
|
||||||
'[': true,
|
|
||||||
'\\': false,
|
|
||||||
']': true,
|
|
||||||
'^': true,
|
|
||||||
'_': true,
|
|
||||||
'`': true,
|
|
||||||
'a': true,
|
|
||||||
'b': true,
|
|
||||||
'c': true,
|
|
||||||
'd': true,
|
|
||||||
'e': true,
|
|
||||||
'f': true,
|
|
||||||
'g': true,
|
|
||||||
'h': true,
|
|
||||||
'i': true,
|
|
||||||
'j': true,
|
|
||||||
'k': true,
|
|
||||||
'l': true,
|
|
||||||
'm': true,
|
|
||||||
'n': true,
|
|
||||||
'o': true,
|
|
||||||
'p': true,
|
|
||||||
'q': true,
|
|
||||||
'r': true,
|
|
||||||
's': true,
|
|
||||||
't': true,
|
|
||||||
'u': true,
|
|
||||||
'v': true,
|
|
||||||
'w': true,
|
|
||||||
'x': true,
|
|
||||||
'y': true,
|
|
||||||
'z': true,
|
|
||||||
'{': true,
|
|
||||||
'|': true,
|
|
||||||
'}': true,
|
|
||||||
'~': true,
|
|
||||||
'\u007f': true,
|
|
||||||
}
|
|
||||||
|
|
||||||
var hex = "0123456789abcdef"
|
var hex = "0123456789abcdef"
|
||||||
|
|
||||||
// escapeIndex finds the index of the first char in `s` that requires escaping.
|
// escapeIndex finds the index of the first char in `s` that requires escaping.
|
||||||
|
@ -714,38 +375,6 @@ func escapeIndex(s string) int {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// escapeIndex finds the index of the first char in `s` that requires escaping.
|
|
||||||
// A char requires escaping if it's outside of the range of [0x20, 0x7F] or if
|
|
||||||
// it includes a double quote or backslash.
|
|
||||||
// Also, the chars <, > and & require escaping.
|
|
||||||
// If no chars in `s` require escaping, the return value is -1.
|
|
||||||
func escapeIndexWithHTMLEscape(s string) int {
|
|
||||||
chunks := stringToUint64Slice(s)
|
|
||||||
for _, n := range chunks {
|
|
||||||
// combine masks before checking for the MSB of each byte. We include
|
|
||||||
// `n` in the mask to check whether any of the *input* byte MSBs were
|
|
||||||
// set (i.e. the byte was outside the ASCII range).
|
|
||||||
mask := n | (n - (lsb * 0x20)) |
|
|
||||||
((n ^ (lsb * '"')) - lsb) |
|
|
||||||
((n ^ (lsb * '\\')) - lsb) |
|
|
||||||
((n ^ (lsb * '<')) - lsb) |
|
|
||||||
((n ^ (lsb * '>')) - lsb) |
|
|
||||||
((n ^ (lsb * '&')) - lsb)
|
|
||||||
if (mask & msb) != 0 {
|
|
||||||
return bits.TrailingZeros64(mask&msb) / 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
valLen := len(s)
|
|
||||||
for i := len(chunks) * 8; i < valLen; i++ {
|
|
||||||
if needEscapeWithHTML[s[i]] {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// below return a mask that can be used to determine if any of the bytes
|
// below return a mask that can be used to determine if any of the bytes
|
||||||
// in `n` are below `b`. If a byte's MSB is set in the mask then that byte was
|
// in `n` are below `b`. If a byte's MSB is set in the mask then that byte was
|
||||||
// below `b`. The result is only valid if `b`, and each byte in `n`, is below
|
// below `b`. The result is only valid if `b`, and each byte in `n`, is below
|
||||||
|
|
|
@ -37,7 +37,6 @@ func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)
|
||||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||||
func ptrToByte(p uintptr) byte { return **(**byte)(unsafe.Pointer(&p)) }
|
|
||||||
func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
|
func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
|
||||||
func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
|
func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
|
||||||
func ptrToSlice(p uintptr) *sliceHeader { return *(**sliceHeader)(unsafe.Pointer(&p)) }
|
func ptrToSlice(p uintptr) *sliceHeader { return *(**sliceHeader)(unsafe.Pointer(&p)) }
|
||||||
|
@ -7637,7 +7636,7 @@ func encodeRun(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt Enco
|
||||||
ptr := load(ctxptr, code.headIdx)
|
ptr := load(ctxptr, code.headIdx)
|
||||||
b = append(b, code.key...)
|
b = append(b, code.key...)
|
||||||
b = append(b, '"')
|
b = append(b, '"')
|
||||||
b = appendUint(b, uint64(ptrToUint64(ptr+code.offset)))
|
b = appendUint(b, ptrToUint64(ptr+code.offset))
|
||||||
b = append(b, '"')
|
b = append(b, '"')
|
||||||
b = encodeComma(b)
|
b = encodeComma(b)
|
||||||
code = code.next
|
code = code.next
|
||||||
|
|
|
@ -7588,7 +7588,7 @@ func encodeRunEscaped(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, o
|
||||||
ptr := load(ctxptr, code.headIdx)
|
ptr := load(ctxptr, code.headIdx)
|
||||||
b = append(b, code.escapedKey...)
|
b = append(b, code.escapedKey...)
|
||||||
b = append(b, '"')
|
b = append(b, '"')
|
||||||
b = appendUint(b, uint64(ptrToUint64(ptr+code.offset)))
|
b = appendUint(b, ptrToUint64(ptr+code.offset))
|
||||||
b = append(b, '"')
|
b = append(b, '"')
|
||||||
b = encodeComma(b)
|
b = encodeComma(b)
|
||||||
code = code.next
|
code = code.next
|
||||||
|
@ -7801,6 +7801,15 @@ func encodeRunEscaped(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, o
|
||||||
ptr := load(ctxptr, code.headIdx)
|
ptr := load(ctxptr, code.headIdx)
|
||||||
b = append(b, code.escapedKey...)
|
b = append(b, code.escapedKey...)
|
||||||
p := ptr + code.offset
|
p := ptr + code.offset
|
||||||
|
if code.typ.Kind() == reflect.Ptr {
|
||||||
|
p = ptrToPtr(p)
|
||||||
|
}
|
||||||
|
if p == 0 {
|
||||||
|
b = encodeNull(b)
|
||||||
|
b = encodeComma(b)
|
||||||
|
code = code.next
|
||||||
|
break
|
||||||
|
}
|
||||||
v := ptrToInterface(code, p)
|
v := ptrToInterface(code, p)
|
||||||
bb, err := v.(Marshaler).MarshalJSON()
|
bb, err := v.(Marshaler).MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7816,23 +7825,25 @@ func encodeRunEscaped(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, o
|
||||||
case opStructFieldOmitEmptyMarshalJSON:
|
case opStructFieldOmitEmptyMarshalJSON:
|
||||||
ptr := load(ctxptr, code.headIdx)
|
ptr := load(ctxptr, code.headIdx)
|
||||||
p := ptr + code.offset
|
p := ptr + code.offset
|
||||||
if code.typ.Kind() == reflect.Ptr && code.typ.Elem().Implements(marshalJSONType) {
|
if code.typ.Kind() == reflect.Ptr {
|
||||||
p = ptrToPtr(p)
|
p = ptrToPtr(p)
|
||||||
}
|
}
|
||||||
v := ptrToInterface(code, p)
|
if p == 0 {
|
||||||
if v != nil && p != 0 {
|
code = code.next
|
||||||
bb, err := v.(Marshaler).MarshalJSON()
|
break
|
||||||
if err != nil {
|
|
||||||
return nil, errMarshaler(code, err)
|
|
||||||
}
|
|
||||||
b = append(b, code.escapedKey...)
|
|
||||||
buf := bytes.NewBuffer(b)
|
|
||||||
if err := compact(buf, bb, true); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b = buf.Bytes()
|
|
||||||
b = encodeComma(b)
|
|
||||||
}
|
}
|
||||||
|
v := ptrToInterface(code, p)
|
||||||
|
bb, err := v.(Marshaler).MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errMarshaler(code, err)
|
||||||
|
}
|
||||||
|
b = append(b, code.escapedKey...)
|
||||||
|
buf := bytes.NewBuffer(b)
|
||||||
|
if err := compact(buf, bb, true); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b = buf.Bytes()
|
||||||
|
b = encodeComma(b)
|
||||||
code = code.next
|
code = code.next
|
||||||
case opStructFieldStringTagMarshalJSON:
|
case opStructFieldStringTagMarshalJSON:
|
||||||
ptr := load(ctxptr, code.headIdx)
|
ptr := load(ctxptr, code.headIdx)
|
||||||
|
@ -9256,20 +9267,10 @@ func encodeRunEscaped(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, o
|
||||||
case opStructEndOmitEmptyMarshalJSON:
|
case opStructEndOmitEmptyMarshalJSON:
|
||||||
ptr := load(ctxptr, code.headIdx)
|
ptr := load(ctxptr, code.headIdx)
|
||||||
p := ptr + code.offset
|
p := ptr + code.offset
|
||||||
v := ptrToInterface(code, p)
|
if code.typ.Kind() == reflect.Ptr {
|
||||||
if v != nil && (code.typ.Kind() != reflect.Ptr || ptrToPtr(p) != 0) {
|
p = ptrToPtr(p)
|
||||||
bb, err := v.(Marshaler).MarshalJSON()
|
}
|
||||||
if err != nil {
|
if p == 0 {
|
||||||
return nil, errMarshaler(code, err)
|
|
||||||
}
|
|
||||||
b = append(b, code.escapedKey...)
|
|
||||||
buf := bytes.NewBuffer(b)
|
|
||||||
if err := compact(buf, bb, true); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b = buf.Bytes()
|
|
||||||
b = appendStructEnd(b)
|
|
||||||
} else {
|
|
||||||
last := len(b) - 1
|
last := len(b) - 1
|
||||||
if b[last] == ',' {
|
if b[last] == ',' {
|
||||||
b[last] = '}'
|
b[last] = '}'
|
||||||
|
@ -9277,7 +9278,21 @@ func encodeRunEscaped(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, o
|
||||||
} else {
|
} else {
|
||||||
b = appendStructEnd(b)
|
b = appendStructEnd(b)
|
||||||
}
|
}
|
||||||
|
code = code.next
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
v := ptrToInterface(code, p)
|
||||||
|
bb, err := v.(Marshaler).MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errMarshaler(code, err)
|
||||||
|
}
|
||||||
|
b = append(b, code.escapedKey...)
|
||||||
|
buf := bytes.NewBuffer(b)
|
||||||
|
if err := compact(buf, bb, true); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b = buf.Bytes()
|
||||||
|
b = appendStructEnd(b)
|
||||||
code = code.next
|
code = code.next
|
||||||
case opStructEndStringTagMarshalJSON:
|
case opStructEndStringTagMarshalJSON:
|
||||||
ptr := load(ctxptr, code.headIdx)
|
ptr := load(ctxptr, code.headIdx)
|
||||||
|
|
|
@ -29,7 +29,7 @@ func encodeWithIndent(dst *bytes.Buffer, src []byte, prefix, indentStr string) e
|
||||||
case '"':
|
case '"':
|
||||||
goto LOOP_END
|
goto LOOP_END
|
||||||
case nul:
|
case nul:
|
||||||
return errUnexpectedEndOfJSON("string", int64(length))
|
return errUnexpectedEndOfJSON("string", length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case '{':
|
case '{':
|
||||||
|
|
|
@ -22,10 +22,7 @@ func isIgnoredStructField(field reflect.StructField) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tag := getTag(field)
|
tag := getTag(field)
|
||||||
if tag == "-" {
|
return tag == "-"
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type structTag struct {
|
type structTag struct {
|
||||||
|
|
Loading…
Reference in New Issue