Merge branch 'master' into golangci-lint

This commit is contained in:
Daisuke Maki 2021-02-02 07:54:20 +09:00
commit 5f9f30fc8e
25 changed files with 184 additions and 478 deletions

View File

@ -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

View File

@ -22,7 +22,6 @@ type decoder interface {
type Decoder struct { type Decoder struct {
s *stream s *stream
disallowUnknownFields bool
structTypeToDecoder map[uintptr]decoder structTypeToDecoder map[uintptr]decoder
} }
@ -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
} }

View File

@ -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{

View File

@ -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

View File

@ -142,5 +142,4 @@ func skipValue(buf []byte, cursor int64) (int64, error) {
} }
cursor++ cursor++
} }
return cursor, errUnexpectedEndOfJSON("value of object", cursor)
} }

View File

@ -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 {

View File

@ -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)

View File

@ -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] == '}' {

View File

@ -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

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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) {

View File

@ -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
} }

View File

@ -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"...)
} }

View File

@ -27,6 +27,7 @@ var (
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...)
} }
} }

34
encode_compile_norace.go Normal file
View File

@ -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
}

44
encode_compile_race.go Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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 {

18
encode_opcode_test.go Normal file
View File

@ -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()
}

View File

@ -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

View File

@ -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

View File

@ -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,11 +7825,14 @@ 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)
} }
if p == 0 {
code = code.next
break
}
v := ptrToInterface(code, p) v := ptrToInterface(code, p)
if v != nil && p != 0 {
bb, err := v.(Marshaler).MarshalJSON() bb, err := v.(Marshaler).MarshalJSON()
if err != nil { if err != nil {
return nil, errMarshaler(code, err) return nil, errMarshaler(code, err)
@ -7832,7 +7844,6 @@ func encodeRunEscaped(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, o
} }
b = buf.Bytes() b = buf.Bytes()
b = encodeComma(b) b = encodeComma(b)
}
code = code.next code = code.next
case opStructFieldStringTagMarshalJSON: case opStructFieldStringTagMarshalJSON:
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)
@ -9256,8 +9267,21 @@ 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
if code.typ.Kind() == reflect.Ptr {
p = ptrToPtr(p)
}
if p == 0 {
last := len(b) - 1
if b[last] == ',' {
b[last] = '}'
b = encodeComma(b)
} else {
b = appendStructEnd(b)
}
code = code.next
break
}
v := ptrToInterface(code, p) v := ptrToInterface(code, p)
if v != nil && (code.typ.Kind() != reflect.Ptr || ptrToPtr(p) != 0) {
bb, err := v.(Marshaler).MarshalJSON() bb, err := v.(Marshaler).MarshalJSON()
if err != nil { if err != nil {
return nil, errMarshaler(code, err) return nil, errMarshaler(code, err)
@ -9269,15 +9293,6 @@ func encodeRunEscaped(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, o
} }
b = buf.Bytes() b = buf.Bytes()
b = appendStructEnd(b) b = appendStructEnd(b)
} else {
last := len(b) - 1
if b[last] == ',' {
b[last] = '}'
b = encodeComma(b)
} else {
b = appendStructEnd(b)
}
}
code = code.next code = code.next
case opStructEndStringTagMarshalJSON: case opStructEndStringTagMarshalJSON:
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)

View File

@ -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 '{':

View File

@ -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 {