Adding upstream version 0.10.5.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
104c0c203d
commit
e733edafba
141 changed files with 102352 additions and 0 deletions
319
internal/cmd/generator/main.go
Normal file
319
internal/cmd/generator/main.go
Normal file
|
@ -0,0 +1,319 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type opType struct {
|
||||
Op string
|
||||
Code string
|
||||
}
|
||||
|
||||
func createOpType(op, code string) opType {
|
||||
return opType{
|
||||
Op: op,
|
||||
Code: code,
|
||||
}
|
||||
}
|
||||
|
||||
func _main() error {
|
||||
tmpl, err := template.New("").Parse(`// Code generated by internal/cmd/generator. DO NOT EDIT!
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CodeType int
|
||||
|
||||
const (
|
||||
{{- range $index, $type := .CodeTypes }}
|
||||
Code{{ $type }} CodeType = {{ $index }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
var opTypeStrings = [{{ .OpLen }}]string{
|
||||
{{- range $type := .OpTypes }}
|
||||
"{{ $type.Op }}",
|
||||
{{- end }}
|
||||
}
|
||||
|
||||
type OpType uint16
|
||||
|
||||
const (
|
||||
{{- range $index, $type := .OpTypes }}
|
||||
Op{{ $type.Op }} OpType = {{ $index }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
func (t OpType) String() string {
|
||||
if int(t) >= {{ .OpLen }} {
|
||||
return ""
|
||||
}
|
||||
return opTypeStrings[int(t)]
|
||||
}
|
||||
|
||||
func (t OpType) CodeType() CodeType {
|
||||
if strings.Contains(t.String(), "Struct") {
|
||||
if strings.Contains(t.String(), "End") {
|
||||
return CodeStructEnd
|
||||
}
|
||||
return CodeStructField
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func (t OpType) HeadToPtrHead() OpType {
|
||||
if strings.Index(t.String(), "PtrHead") > 0 {
|
||||
return t
|
||||
}
|
||||
|
||||
idx := strings.Index(t.String(), "Head")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
suffix := "PtrHead"+t.String()[idx+len("Head"):]
|
||||
|
||||
const toPtrOffset = 2
|
||||
if strings.Contains(OpType(int(t) + toPtrOffset).String(), suffix) {
|
||||
return OpType(int(t) + toPtrOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t OpType) HeadToOmitEmptyHead() OpType {
|
||||
const toOmitEmptyOffset = 1
|
||||
if strings.Contains(OpType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return OpType(int(t) + toOmitEmptyOffset)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t OpType) PtrHeadToHead() OpType {
|
||||
idx := strings.Index(t.String(), "PtrHead")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
suffix := t.String()[idx+len("Ptr"):]
|
||||
|
||||
const toPtrOffset = 2
|
||||
if strings.Contains(OpType(int(t) - toPtrOffset).String(), suffix) {
|
||||
return OpType(int(t) - toPtrOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
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" {
|
||||
return t
|
||||
}
|
||||
const toEndOffset = 2
|
||||
if strings.Contains(OpType(int(t) + toEndOffset).String(), "End"+suffix) {
|
||||
return OpType(int(t) + toEndOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t OpType) FieldToOmitEmptyField() OpType {
|
||||
const toOmitEmptyOffset = 1
|
||||
if strings.Contains(OpType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return OpType(int(t) + toOmitEmptyOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
codeTypes := []string{
|
||||
"Op",
|
||||
"ArrayHead",
|
||||
"ArrayElem",
|
||||
"SliceHead",
|
||||
"SliceElem",
|
||||
"MapHead",
|
||||
"MapKey",
|
||||
"MapValue",
|
||||
"MapEnd",
|
||||
"Recursive",
|
||||
"StructField",
|
||||
"StructEnd",
|
||||
}
|
||||
primitiveTypes := []string{
|
||||
"int", "uint", "float32", "float64", "bool", "string", "bytes", "number",
|
||||
"array", "map", "slice", "struct", "MarshalJSON", "MarshalText",
|
||||
"intString", "uintString", "float32String", "float64String", "boolString", "stringString", "numberString",
|
||||
"intPtr", "uintPtr", "float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "bytesPtr", "numberPtr",
|
||||
"arrayPtr", "mapPtr", "slicePtr", "marshalJSONPtr", "marshalTextPtr", "interfacePtr",
|
||||
"intPtrString", "uintPtrString", "float32PtrString", "float64PtrString", "boolPtrString", "stringPtrString", "numberPtrString",
|
||||
}
|
||||
primitiveTypesUpper := []string{}
|
||||
for _, typ := range primitiveTypes {
|
||||
primitiveTypesUpper = append(primitiveTypesUpper, strings.ToUpper(string(typ[0]))+typ[1:])
|
||||
}
|
||||
opTypes := []opType{
|
||||
createOpType("End", "Op"),
|
||||
createOpType("Interface", "Op"),
|
||||
createOpType("Ptr", "Op"),
|
||||
createOpType("SliceElem", "SliceElem"),
|
||||
createOpType("SliceEnd", "Op"),
|
||||
createOpType("ArrayElem", "ArrayElem"),
|
||||
createOpType("ArrayEnd", "Op"),
|
||||
createOpType("MapKey", "MapKey"),
|
||||
createOpType("MapValue", "MapValue"),
|
||||
createOpType("MapEnd", "Op"),
|
||||
createOpType("Recursive", "Op"),
|
||||
createOpType("RecursivePtr", "Op"),
|
||||
createOpType("RecursiveEnd", "Op"),
|
||||
createOpType("InterfaceEnd", "Op"),
|
||||
}
|
||||
for _, typ := range primitiveTypesUpper {
|
||||
typ := typ
|
||||
opTypes = append(opTypes, createOpType(typ, "Op"))
|
||||
}
|
||||
for _, typ := range append(primitiveTypesUpper, "") {
|
||||
for _, ptrOrNot := range []string{"", "Ptr"} {
|
||||
for _, opt := range []string{"", "OmitEmpty"} {
|
||||
ptrOrNot := ptrOrNot
|
||||
opt := opt
|
||||
typ := typ
|
||||
|
||||
op := fmt.Sprintf(
|
||||
"Struct%sHead%s%s",
|
||||
ptrOrNot,
|
||||
opt,
|
||||
typ,
|
||||
)
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: op,
|
||||
Code: "StructField",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, typ := range append(primitiveTypesUpper, "") {
|
||||
for _, opt := range []string{"", "OmitEmpty"} {
|
||||
opt := opt
|
||||
typ := typ
|
||||
|
||||
op := fmt.Sprintf(
|
||||
"StructField%s%s",
|
||||
opt,
|
||||
typ,
|
||||
)
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: op,
|
||||
Code: "StructField",
|
||||
})
|
||||
}
|
||||
for _, opt := range []string{"", "OmitEmpty"} {
|
||||
opt := opt
|
||||
typ := typ
|
||||
|
||||
op := fmt.Sprintf(
|
||||
"StructEnd%s%s",
|
||||
opt,
|
||||
typ,
|
||||
)
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: op,
|
||||
Code: "StructEnd",
|
||||
})
|
||||
}
|
||||
}
|
||||
var b bytes.Buffer
|
||||
if err := tmpl.Execute(&b, struct {
|
||||
CodeTypes []string
|
||||
OpTypes []opType
|
||||
OpLen int
|
||||
}{
|
||||
CodeTypes: codeTypes,
|
||||
OpTypes: opTypes,
|
||||
OpLen: len(opTypes),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(repoRoot(), "internal", "encoder", "optype.go")
|
||||
buf, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, buf, 0644)
|
||||
}
|
||||
|
||||
func generateVM() error {
|
||||
file, err := os.ReadFile("vm.go.tmpl")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, "", string(file), parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pkg := range []string{"vm", "vm_indent", "vm_color", "vm_color_indent"} {
|
||||
f.Name.Name = pkg
|
||||
var buf bytes.Buffer
|
||||
printer.Fprint(&buf, fset, f)
|
||||
path := filepath.Join(repoRoot(), "internal", "encoder", pkg, "vm.go")
|
||||
source, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(path, source, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func repoRoot() string {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
relativePathFromRepoRoot := filepath.Join("internal", "cmd", "generator")
|
||||
return strings.TrimSuffix(filepath.Dir(file), relativePathFromRepoRoot)
|
||||
}
|
||||
|
||||
//go:generate go run main.go
|
||||
func main() {
|
||||
if err := generateVM(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := _main(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
4859
internal/cmd/generator/vm.go.tmpl
Normal file
4859
internal/cmd/generator/vm.go.tmpl
Normal file
File diff suppressed because it is too large
Load diff
41
internal/decoder/anonymous_field.go
Normal file
41
internal/decoder/anonymous_field.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type anonymousFieldDecoder struct {
|
||||
structType *runtime.Type
|
||||
offset uintptr
|
||||
dec Decoder
|
||||
}
|
||||
|
||||
func newAnonymousFieldDecoder(structType *runtime.Type, offset uintptr, dec Decoder) *anonymousFieldDecoder {
|
||||
return &anonymousFieldDecoder{
|
||||
structType: structType,
|
||||
offset: offset,
|
||||
dec: dec,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *anonymousFieldDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
if *(*unsafe.Pointer)(p) == nil {
|
||||
*(*unsafe.Pointer)(p) = unsafe_New(d.structType)
|
||||
}
|
||||
p = *(*unsafe.Pointer)(p)
|
||||
return d.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+d.offset))
|
||||
}
|
||||
|
||||
func (d *anonymousFieldDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
if *(*unsafe.Pointer)(p) == nil {
|
||||
*(*unsafe.Pointer)(p) = unsafe_New(d.structType)
|
||||
}
|
||||
p = *(*unsafe.Pointer)(p)
|
||||
return d.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset))
|
||||
}
|
||||
|
||||
func (d *anonymousFieldDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return d.dec.DecodePath(ctx, cursor, depth)
|
||||
}
|
176
internal/decoder/array.go
Normal file
176
internal/decoder/array.go
Normal file
|
@ -0,0 +1,176 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type arrayDecoder struct {
|
||||
elemType *runtime.Type
|
||||
size uintptr
|
||||
valueDecoder Decoder
|
||||
alen int
|
||||
structName string
|
||||
fieldName string
|
||||
zeroValue unsafe.Pointer
|
||||
}
|
||||
|
||||
func newArrayDecoder(dec Decoder, elemType *runtime.Type, alen int, structName, fieldName string) *arrayDecoder {
|
||||
// workaround to avoid checkptr errors. cannot use `*(*unsafe.Pointer)(unsafe_New(elemType))` directly.
|
||||
zeroValuePtr := unsafe_New(elemType)
|
||||
zeroValue := **(**unsafe.Pointer)(unsafe.Pointer(&zeroValuePtr))
|
||||
return &arrayDecoder{
|
||||
valueDecoder: dec,
|
||||
elemType: elemType,
|
||||
size: elemType.Size(),
|
||||
alen: alen,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
zeroValue: zeroValue,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *arrayDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
|
||||
}
|
||||
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case '[':
|
||||
idx := 0
|
||||
s.cursor++
|
||||
if s.skipWhiteSpace() == ']' {
|
||||
for idx < d.alen {
|
||||
*(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue
|
||||
idx++
|
||||
}
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
if idx < d.alen {
|
||||
if err := d.valueDecoder.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
idx++
|
||||
switch s.skipWhiteSpace() {
|
||||
case ']':
|
||||
for idx < d.alen {
|
||||
*(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue
|
||||
idx++
|
||||
}
|
||||
s.cursor++
|
||||
return nil
|
||||
case ',':
|
||||
s.cursor++
|
||||
continue
|
||||
case nul:
|
||||
if s.read() {
|
||||
s.cursor++
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
default:
|
||||
goto ERROR
|
||||
}
|
||||
}
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
default:
|
||||
goto ERROR
|
||||
}
|
||||
s.cursor++
|
||||
}
|
||||
ERROR:
|
||||
return errors.ErrUnexpectedEndOfJSON("array", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return cursor, nil
|
||||
case '[':
|
||||
idx := 0
|
||||
cursor++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == ']' {
|
||||
for idx < d.alen {
|
||||
*(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue
|
||||
idx++
|
||||
}
|
||||
cursor++
|
||||
return cursor, nil
|
||||
}
|
||||
for {
|
||||
if idx < d.alen {
|
||||
c, err := d.valueDecoder.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = c
|
||||
} else {
|
||||
c, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = c
|
||||
}
|
||||
idx++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
switch buf[cursor] {
|
||||
case ']':
|
||||
for idx < d.alen {
|
||||
*(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue
|
||||
idx++
|
||||
}
|
||||
cursor++
|
||||
return cursor, nil
|
||||
case ',':
|
||||
cursor++
|
||||
continue
|
||||
default:
|
||||
return 0, errors.ErrInvalidCharacter(buf[cursor], "array", cursor)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("array", cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *arrayDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: array decoder does not support decode path")
|
||||
}
|
438
internal/decoder/assign.go
Normal file
438
internal/decoder/assign.go
Normal file
|
@ -0,0 +1,438 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
nilValue = reflect.ValueOf(nil)
|
||||
)
|
||||
|
||||
func AssignValue(src, dst reflect.Value) error {
|
||||
if dst.Type().Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("invalid dst type. required pointer type: %T", dst.Type())
|
||||
}
|
||||
casted, err := castValue(dst.Elem().Type(), src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst.Elem().Set(casted)
|
||||
return nil
|
||||
}
|
||||
|
||||
func castValue(t reflect.Type, v reflect.Value) (reflect.Value, error) {
|
||||
switch t.Kind() {
|
||||
case reflect.Int:
|
||||
vv, err := castInt(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(int(vv.Int())), nil
|
||||
case reflect.Int8:
|
||||
vv, err := castInt(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(int8(vv.Int())), nil
|
||||
case reflect.Int16:
|
||||
vv, err := castInt(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(int16(vv.Int())), nil
|
||||
case reflect.Int32:
|
||||
vv, err := castInt(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(int32(vv.Int())), nil
|
||||
case reflect.Int64:
|
||||
return castInt(v)
|
||||
case reflect.Uint:
|
||||
vv, err := castUint(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(uint(vv.Uint())), nil
|
||||
case reflect.Uint8:
|
||||
vv, err := castUint(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(uint8(vv.Uint())), nil
|
||||
case reflect.Uint16:
|
||||
vv, err := castUint(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(uint16(vv.Uint())), nil
|
||||
case reflect.Uint32:
|
||||
vv, err := castUint(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(uint32(vv.Uint())), nil
|
||||
case reflect.Uint64:
|
||||
return castUint(v)
|
||||
case reflect.Uintptr:
|
||||
vv, err := castUint(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(uintptr(vv.Uint())), nil
|
||||
case reflect.String:
|
||||
return castString(v)
|
||||
case reflect.Bool:
|
||||
return castBool(v)
|
||||
case reflect.Float32:
|
||||
vv, err := castFloat(v)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(float32(vv.Float())), nil
|
||||
case reflect.Float64:
|
||||
return castFloat(v)
|
||||
case reflect.Array:
|
||||
return castArray(t, v)
|
||||
case reflect.Slice:
|
||||
return castSlice(t, v)
|
||||
case reflect.Map:
|
||||
return castMap(t, v)
|
||||
case reflect.Struct:
|
||||
return castStruct(t, v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func castInt(v reflect.Value) (reflect.Value, error) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v, nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return reflect.ValueOf(int64(v.Uint())), nil
|
||||
case reflect.String:
|
||||
i64, err := strconv.ParseInt(v.String(), 10, 64)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(i64), nil
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return reflect.ValueOf(int64(1)), nil
|
||||
}
|
||||
return reflect.ValueOf(int64(0)), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return reflect.ValueOf(int64(v.Float())), nil
|
||||
case reflect.Array:
|
||||
if v.Len() > 0 {
|
||||
return castInt(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to int64 from empty array")
|
||||
case reflect.Slice:
|
||||
if v.Len() > 0 {
|
||||
return castInt(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to int64 from empty slice")
|
||||
case reflect.Interface:
|
||||
return castInt(reflect.ValueOf(v.Interface()))
|
||||
case reflect.Map:
|
||||
return nilValue, fmt.Errorf("failed to cast to int64 from map")
|
||||
case reflect.Struct:
|
||||
return nilValue, fmt.Errorf("failed to cast to int64 from struct")
|
||||
case reflect.Ptr:
|
||||
return castInt(v.Elem())
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to int64 from %s", v.Type().Kind())
|
||||
}
|
||||
|
||||
func castUint(v reflect.Value) (reflect.Value, error) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return reflect.ValueOf(uint64(v.Int())), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v, nil
|
||||
case reflect.String:
|
||||
u64, err := strconv.ParseUint(v.String(), 10, 64)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(u64), nil
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return reflect.ValueOf(uint64(1)), nil
|
||||
}
|
||||
return reflect.ValueOf(uint64(0)), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return reflect.ValueOf(uint64(v.Float())), nil
|
||||
case reflect.Array:
|
||||
if v.Len() > 0 {
|
||||
return castUint(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to uint64 from empty array")
|
||||
case reflect.Slice:
|
||||
if v.Len() > 0 {
|
||||
return castUint(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to uint64 from empty slice")
|
||||
case reflect.Interface:
|
||||
return castUint(reflect.ValueOf(v.Interface()))
|
||||
case reflect.Map:
|
||||
return nilValue, fmt.Errorf("failed to cast to uint64 from map")
|
||||
case reflect.Struct:
|
||||
return nilValue, fmt.Errorf("failed to cast to uint64 from struct")
|
||||
case reflect.Ptr:
|
||||
return castUint(v.Elem())
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to uint64 from %s", v.Type().Kind())
|
||||
}
|
||||
|
||||
func castString(v reflect.Value) (reflect.Value, error) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return reflect.ValueOf(fmt.Sprint(v.Int())), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return reflect.ValueOf(fmt.Sprint(v.Uint())), nil
|
||||
case reflect.String:
|
||||
return v, nil
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return reflect.ValueOf("true"), nil
|
||||
}
|
||||
return reflect.ValueOf("false"), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return reflect.ValueOf(fmt.Sprint(v.Float())), nil
|
||||
case reflect.Array:
|
||||
if v.Len() > 0 {
|
||||
return castString(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to string from empty array")
|
||||
case reflect.Slice:
|
||||
if v.Len() > 0 {
|
||||
return castString(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to string from empty slice")
|
||||
case reflect.Interface:
|
||||
return castString(reflect.ValueOf(v.Interface()))
|
||||
case reflect.Map:
|
||||
return nilValue, fmt.Errorf("failed to cast to string from map")
|
||||
case reflect.Struct:
|
||||
return nilValue, fmt.Errorf("failed to cast to string from struct")
|
||||
case reflect.Ptr:
|
||||
return castString(v.Elem())
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to string from %s", v.Type().Kind())
|
||||
}
|
||||
|
||||
func castBool(v reflect.Value) (reflect.Value, error) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
switch v.Int() {
|
||||
case 0:
|
||||
return reflect.ValueOf(false), nil
|
||||
case 1:
|
||||
return reflect.ValueOf(true), nil
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to bool from %d", v.Int())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
switch v.Uint() {
|
||||
case 0:
|
||||
return reflect.ValueOf(false), nil
|
||||
case 1:
|
||||
return reflect.ValueOf(true), nil
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to bool from %d", v.Uint())
|
||||
case reflect.String:
|
||||
b, err := strconv.ParseBool(v.String())
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(b), nil
|
||||
case reflect.Bool:
|
||||
return v, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch v.Float() {
|
||||
case 0:
|
||||
return reflect.ValueOf(false), nil
|
||||
case 1:
|
||||
return reflect.ValueOf(true), nil
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to bool from %f", v.Float())
|
||||
case reflect.Array:
|
||||
if v.Len() > 0 {
|
||||
return castBool(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to string from empty array")
|
||||
case reflect.Slice:
|
||||
if v.Len() > 0 {
|
||||
return castBool(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to string from empty slice")
|
||||
case reflect.Interface:
|
||||
return castBool(reflect.ValueOf(v.Interface()))
|
||||
case reflect.Map:
|
||||
return nilValue, fmt.Errorf("failed to cast to string from map")
|
||||
case reflect.Struct:
|
||||
return nilValue, fmt.Errorf("failed to cast to string from struct")
|
||||
case reflect.Ptr:
|
||||
return castBool(v.Elem())
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to bool from %s", v.Type().Kind())
|
||||
}
|
||||
|
||||
func castFloat(v reflect.Value) (reflect.Value, error) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return reflect.ValueOf(float64(v.Int())), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return reflect.ValueOf(float64(v.Uint())), nil
|
||||
case reflect.String:
|
||||
f64, err := strconv.ParseFloat(v.String(), 64)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
return reflect.ValueOf(f64), nil
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return reflect.ValueOf(float64(1)), nil
|
||||
}
|
||||
return reflect.ValueOf(float64(0)), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v, nil
|
||||
case reflect.Array:
|
||||
if v.Len() > 0 {
|
||||
return castFloat(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to float64 from empty array")
|
||||
case reflect.Slice:
|
||||
if v.Len() > 0 {
|
||||
return castFloat(v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to float64 from empty slice")
|
||||
case reflect.Interface:
|
||||
return castFloat(reflect.ValueOf(v.Interface()))
|
||||
case reflect.Map:
|
||||
return nilValue, fmt.Errorf("failed to cast to float64 from map")
|
||||
case reflect.Struct:
|
||||
return nilValue, fmt.Errorf("failed to cast to float64 from struct")
|
||||
case reflect.Ptr:
|
||||
return castFloat(v.Elem())
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to float64 from %s", v.Type().Kind())
|
||||
}
|
||||
|
||||
func castArray(t reflect.Type, v reflect.Value) (reflect.Value, error) {
|
||||
kind := v.Type().Kind()
|
||||
if kind == reflect.Interface {
|
||||
return castArray(t, reflect.ValueOf(v.Interface()))
|
||||
}
|
||||
if kind != reflect.Slice && kind != reflect.Array {
|
||||
return nilValue, fmt.Errorf("failed to cast to array from %s", kind)
|
||||
}
|
||||
if t.Elem() == v.Type().Elem() {
|
||||
return v, nil
|
||||
}
|
||||
if t.Len() != v.Len() {
|
||||
return nilValue, fmt.Errorf("failed to cast [%d]array from slice of %d length", t.Len(), v.Len())
|
||||
}
|
||||
ret := reflect.New(t).Elem()
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
vv, err := castValue(t.Elem(), v.Index(i))
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
ret.Index(i).Set(vv)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func castSlice(t reflect.Type, v reflect.Value) (reflect.Value, error) {
|
||||
kind := v.Type().Kind()
|
||||
if kind == reflect.Interface {
|
||||
return castSlice(t, reflect.ValueOf(v.Interface()))
|
||||
}
|
||||
if kind != reflect.Slice && kind != reflect.Array {
|
||||
return nilValue, fmt.Errorf("failed to cast to slice from %s", kind)
|
||||
}
|
||||
if t.Elem() == v.Type().Elem() {
|
||||
return v, nil
|
||||
}
|
||||
ret := reflect.MakeSlice(t, v.Len(), v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
vv, err := castValue(t.Elem(), v.Index(i))
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
ret.Index(i).Set(vv)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func castMap(t reflect.Type, v reflect.Value) (reflect.Value, error) {
|
||||
ret := reflect.MakeMap(t)
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Map:
|
||||
iter := v.MapRange()
|
||||
for iter.Next() {
|
||||
key, err := castValue(t.Key(), iter.Key())
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
value, err := castValue(t.Elem(), iter.Value())
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
ret.SetMapIndex(key, value)
|
||||
}
|
||||
return ret, nil
|
||||
case reflect.Interface:
|
||||
return castMap(t, reflect.ValueOf(v.Interface()))
|
||||
case reflect.Slice:
|
||||
if v.Len() > 0 {
|
||||
return castMap(t, v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to map from empty slice")
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to map from %s", v.Type().Kind())
|
||||
}
|
||||
|
||||
func castStruct(t reflect.Type, v reflect.Value) (reflect.Value, error) {
|
||||
ret := reflect.New(t).Elem()
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Map:
|
||||
iter := v.MapRange()
|
||||
for iter.Next() {
|
||||
key := iter.Key()
|
||||
k, err := castString(key)
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
fieldName := k.String()
|
||||
field, ok := t.FieldByName(fieldName)
|
||||
if ok {
|
||||
value, err := castValue(field.Type, iter.Value())
|
||||
if err != nil {
|
||||
return nilValue, err
|
||||
}
|
||||
ret.FieldByName(fieldName).Set(value)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.Type().NumField(); i++ {
|
||||
name := v.Type().Field(i).Name
|
||||
ret.FieldByName(name).Set(v.FieldByName(name))
|
||||
}
|
||||
return ret, nil
|
||||
case reflect.Interface:
|
||||
return castStruct(t, reflect.ValueOf(v.Interface()))
|
||||
case reflect.Slice:
|
||||
if v.Len() > 0 {
|
||||
return castStruct(t, v.Index(0))
|
||||
}
|
||||
return nilValue, fmt.Errorf("failed to cast to struct from empty slice")
|
||||
default:
|
||||
return nilValue, fmt.Errorf("failed to cast to struct from %s", v.Type().Kind())
|
||||
}
|
||||
}
|
83
internal/decoder/bool.go
Normal file
83
internal/decoder/bool.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
type boolDecoder struct {
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newBoolDecoder(structName, fieldName string) *boolDecoder {
|
||||
return &boolDecoder{structName: structName, fieldName: fieldName}
|
||||
}
|
||||
|
||||
func (d *boolDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
c := s.skipWhiteSpace()
|
||||
for {
|
||||
switch c {
|
||||
case 't':
|
||||
if err := trueBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
**(**bool)(unsafe.Pointer(&p)) = true
|
||||
return nil
|
||||
case 'f':
|
||||
if err := falseBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
**(**bool)(unsafe.Pointer(&p)) = false
|
||||
return nil
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
c = s.char()
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
}
|
||||
break
|
||||
}
|
||||
ERROR:
|
||||
return errors.ErrUnexpectedEndOfJSON("bool", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *boolDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
switch buf[cursor] {
|
||||
case 't':
|
||||
if err := validateTrue(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
**(**bool)(unsafe.Pointer(&p)) = true
|
||||
return cursor, nil
|
||||
case 'f':
|
||||
if err := validateFalse(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 5
|
||||
**(**bool)(unsafe.Pointer(&p)) = false
|
||||
return cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return cursor, nil
|
||||
}
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("bool", cursor)
|
||||
}
|
||||
|
||||
func (d *boolDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: bool decoder does not support decode path")
|
||||
}
|
118
internal/decoder/bytes.go
Normal file
118
internal/decoder/bytes.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type bytesDecoder struct {
|
||||
typ *runtime.Type
|
||||
sliceDecoder Decoder
|
||||
stringDecoder *stringDecoder
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func byteUnmarshalerSliceDecoder(typ *runtime.Type, structName string, fieldName string) Decoder {
|
||||
var unmarshalDecoder Decoder
|
||||
switch {
|
||||
case runtime.PtrTo(typ).Implements(unmarshalJSONType):
|
||||
unmarshalDecoder = newUnmarshalJSONDecoder(runtime.PtrTo(typ), structName, fieldName)
|
||||
case runtime.PtrTo(typ).Implements(unmarshalTextType):
|
||||
unmarshalDecoder = newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName)
|
||||
default:
|
||||
unmarshalDecoder, _ = compileUint8(typ, structName, fieldName)
|
||||
}
|
||||
return newSliceDecoder(unmarshalDecoder, typ, 1, structName, fieldName)
|
||||
}
|
||||
|
||||
func newBytesDecoder(typ *runtime.Type, structName string, fieldName string) *bytesDecoder {
|
||||
return &bytesDecoder{
|
||||
typ: typ,
|
||||
sliceDecoder: byteUnmarshalerSliceDecoder(typ, structName, fieldName),
|
||||
stringDecoder: newStringDecoder(structName, fieldName),
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *bytesDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
bytes, err := d.decodeStreamBinary(s, depth, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes == nil {
|
||||
s.reset()
|
||||
return nil
|
||||
}
|
||||
decodedLen := base64.StdEncoding.DecodedLen(len(bytes))
|
||||
buf := make([]byte, decodedLen)
|
||||
n, err := base64.StdEncoding.Decode(buf, bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*(*[]byte)(p) = buf[:n]
|
||||
s.reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *bytesDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
bytes, c, err := d.decodeBinary(ctx, cursor, depth, p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
return c, nil
|
||||
}
|
||||
cursor = c
|
||||
decodedLen := base64.StdEncoding.DecodedLen(len(bytes))
|
||||
b := make([]byte, decodedLen)
|
||||
n, err := base64.StdEncoding.Decode(b, bytes)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
*(*[]byte)(p) = b[:n]
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (d *bytesDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: []byte decoder does not support decode path")
|
||||
}
|
||||
|
||||
func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Pointer) ([]byte, error) {
|
||||
c := s.skipWhiteSpace()
|
||||
if c == '[' {
|
||||
if d.sliceDecoder == nil {
|
||||
return nil, &errors.UnmarshalTypeError{
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
}
|
||||
err := d.sliceDecoder.DecodeStream(s, depth, p)
|
||||
return nil, err
|
||||
}
|
||||
return d.stringDecoder.decodeStreamByte(s)
|
||||
}
|
||||
|
||||
func (d *bytesDecoder) decodeBinary(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) ([]byte, int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == '[' {
|
||||
if d.sliceDecoder == nil {
|
||||
return nil, 0, &errors.UnmarshalTypeError{
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
c, err := d.sliceDecoder.Decode(ctx, cursor, depth, p)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return nil, c, nil
|
||||
}
|
||||
return d.stringDecoder.decodeByte(buf, cursor)
|
||||
}
|
493
internal/decoder/compile.go
Normal file
493
internal/decoder/compile.go
Normal file
|
@ -0,0 +1,493 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unicode"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
jsonNumberType = reflect.TypeOf(json.Number(""))
|
||||
typeAddr *runtime.TypeAddr
|
||||
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
|
||||
cachedDecoder []Decoder
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
func initDecoder() {
|
||||
initOnce.Do(func() {
|
||||
typeAddr = runtime.AnalyzeTypeAddr()
|
||||
if typeAddr == nil {
|
||||
typeAddr = &runtime.TypeAddr{}
|
||||
}
|
||||
cachedDecoder = make([]Decoder, typeAddr.AddrRange>>typeAddr.AddrShift+1)
|
||||
})
|
||||
}
|
||||
|
||||
func loadDecoderMap() map[uintptr]Decoder {
|
||||
initDecoder()
|
||||
p := atomic.LoadPointer(&cachedDecoderMap)
|
||||
return *(*map[uintptr]Decoder)(unsafe.Pointer(&p))
|
||||
}
|
||||
|
||||
func storeDecoder(typ uintptr, dec Decoder, m map[uintptr]Decoder) {
|
||||
initDecoder()
|
||||
newDecoderMap := make(map[uintptr]Decoder, len(m)+1)
|
||||
newDecoderMap[typ] = dec
|
||||
|
||||
for k, v := range m {
|
||||
newDecoderMap[k] = v
|
||||
}
|
||||
|
||||
atomic.StorePointer(&cachedDecoderMap, *(*unsafe.Pointer)(unsafe.Pointer(&newDecoderMap)))
|
||||
}
|
||||
|
||||
func compileToGetDecoderSlowPath(typeptr uintptr, typ *runtime.Type) (Decoder, error) {
|
||||
decoderMap := loadDecoderMap()
|
||||
if dec, exists := decoderMap[typeptr]; exists {
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
dec, err := compileHead(typ, map[uintptr]Decoder{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storeDecoder(typeptr, dec, decoderMap)
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
func compileHead(typ *runtime.Type, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
switch {
|
||||
case implementsUnmarshalJSONType(runtime.PtrTo(typ)):
|
||||
return newUnmarshalJSONDecoder(runtime.PtrTo(typ), "", ""), nil
|
||||
case runtime.PtrTo(typ).Implements(unmarshalTextType):
|
||||
return newUnmarshalTextDecoder(runtime.PtrTo(typ), "", ""), nil
|
||||
}
|
||||
return compile(typ.Elem(), "", "", structTypeToDecoder)
|
||||
}
|
||||
|
||||
func compile(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
switch {
|
||||
case implementsUnmarshalJSONType(runtime.PtrTo(typ)):
|
||||
return newUnmarshalJSONDecoder(runtime.PtrTo(typ), structName, fieldName), nil
|
||||
case runtime.PtrTo(typ).Implements(unmarshalTextType):
|
||||
return newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName), nil
|
||||
}
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
return compilePtr(typ, structName, fieldName, structTypeToDecoder)
|
||||
case reflect.Struct:
|
||||
return compileStruct(typ, structName, fieldName, structTypeToDecoder)
|
||||
case reflect.Slice:
|
||||
elem := typ.Elem()
|
||||
if elem.Kind() == reflect.Uint8 {
|
||||
return compileBytes(elem, structName, fieldName)
|
||||
}
|
||||
return compileSlice(typ, structName, fieldName, structTypeToDecoder)
|
||||
case reflect.Array:
|
||||
return compileArray(typ, structName, fieldName, structTypeToDecoder)
|
||||
case reflect.Map:
|
||||
return compileMap(typ, structName, fieldName, structTypeToDecoder)
|
||||
case reflect.Interface:
|
||||
return compileInterface(typ, structName, fieldName)
|
||||
case reflect.Uintptr:
|
||||
return compileUint(typ, structName, fieldName)
|
||||
case reflect.Int:
|
||||
return compileInt(typ, structName, fieldName)
|
||||
case reflect.Int8:
|
||||
return compileInt8(typ, structName, fieldName)
|
||||
case reflect.Int16:
|
||||
return compileInt16(typ, structName, fieldName)
|
||||
case reflect.Int32:
|
||||
return compileInt32(typ, structName, fieldName)
|
||||
case reflect.Int64:
|
||||
return compileInt64(typ, structName, fieldName)
|
||||
case reflect.Uint:
|
||||
return compileUint(typ, structName, fieldName)
|
||||
case reflect.Uint8:
|
||||
return compileUint8(typ, structName, fieldName)
|
||||
case reflect.Uint16:
|
||||
return compileUint16(typ, structName, fieldName)
|
||||
case reflect.Uint32:
|
||||
return compileUint32(typ, structName, fieldName)
|
||||
case reflect.Uint64:
|
||||
return compileUint64(typ, structName, fieldName)
|
||||
case reflect.String:
|
||||
return compileString(typ, structName, fieldName)
|
||||
case reflect.Bool:
|
||||
return compileBool(structName, fieldName)
|
||||
case reflect.Float32:
|
||||
return compileFloat32(structName, fieldName)
|
||||
case reflect.Float64:
|
||||
return compileFloat64(structName, fieldName)
|
||||
case reflect.Func:
|
||||
return compileFunc(typ, structName, fieldName)
|
||||
}
|
||||
return newInvalidDecoder(typ, structName, fieldName), nil
|
||||
}
|
||||
|
||||
func isStringTagSupportedType(typ *runtime.Type) bool {
|
||||
switch {
|
||||
case implementsUnmarshalJSONType(runtime.PtrTo(typ)):
|
||||
return false
|
||||
case runtime.PtrTo(typ).Implements(unmarshalTextType):
|
||||
return false
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Map:
|
||||
return false
|
||||
case reflect.Slice:
|
||||
return false
|
||||
case reflect.Array:
|
||||
return false
|
||||
case reflect.Struct:
|
||||
return false
|
||||
case reflect.Interface:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func compileMapKey(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
if runtime.PtrTo(typ).Implements(unmarshalTextType) {
|
||||
return newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName), nil
|
||||
}
|
||||
if typ.Kind() == reflect.String {
|
||||
return newStringDecoder(structName, fieldName), nil
|
||||
}
|
||||
dec, err := compile(typ, structName, fieldName, structTypeToDecoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
switch t := dec.(type) {
|
||||
case *stringDecoder, *interfaceDecoder:
|
||||
return dec, nil
|
||||
case *boolDecoder, *intDecoder, *uintDecoder, *numberDecoder:
|
||||
return newWrappedStringDecoder(typ, dec, structName, fieldName), nil
|
||||
case *ptrDecoder:
|
||||
dec = t.dec
|
||||
default:
|
||||
return newInvalidDecoder(typ, structName, fieldName), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compilePtr(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
dec, err := compile(typ.Elem(), structName, fieldName, structTypeToDecoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newPtrDecoder(dec, typ.Elem(), structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileInt(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) {
|
||||
*(*int)(p) = int(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileInt8(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) {
|
||||
*(*int8)(p) = int8(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileInt16(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) {
|
||||
*(*int16)(p) = int16(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileInt32(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) {
|
||||
*(*int32)(p) = int32(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileInt64(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) {
|
||||
*(*int64)(p) = v
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileUint(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) {
|
||||
*(*uint)(p) = uint(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileUint8(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) {
|
||||
*(*uint8)(p) = uint8(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileUint16(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) {
|
||||
*(*uint16)(p) = uint16(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileUint32(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) {
|
||||
*(*uint32)(p) = uint32(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileUint64(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) {
|
||||
*(*uint64)(p) = v
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileFloat32(structName, fieldName string) (Decoder, error) {
|
||||
return newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) {
|
||||
*(*float32)(p) = float32(v)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileFloat64(structName, fieldName string) (Decoder, error) {
|
||||
return newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) {
|
||||
*(*float64)(p) = v
|
||||
}), nil
|
||||
}
|
||||
|
||||
func compileString(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
if typ == runtime.Type2RType(jsonNumberType) {
|
||||
return newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v json.Number) {
|
||||
*(*json.Number)(p) = v
|
||||
}), nil
|
||||
}
|
||||
return newStringDecoder(structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileBool(structName, fieldName string) (Decoder, error) {
|
||||
return newBoolDecoder(structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileBytes(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newBytesDecoder(typ, structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileSlice(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
elem := typ.Elem()
|
||||
decoder, err := compile(elem, structName, fieldName, structTypeToDecoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newSliceDecoder(decoder, elem, elem.Size(), structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileArray(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
elem := typ.Elem()
|
||||
decoder, err := compile(elem, structName, fieldName, structTypeToDecoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newArrayDecoder(decoder, elem, typ.Len(), structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileMap(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
keyDec, err := compileMapKey(typ.Key(), structName, fieldName, structTypeToDecoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueDec, err := compile(typ.Elem(), structName, fieldName, structTypeToDecoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newMapDecoder(typ, typ.Key(), keyDec, typ.Elem(), valueDec, structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileInterface(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
|
||||
return newInterfaceDecoder(typ, structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileFunc(typ *runtime.Type, strutName, fieldName string) (Decoder, error) {
|
||||
return newFuncDecoder(typ, strutName, fieldName), nil
|
||||
}
|
||||
|
||||
func typeToStructTags(typ *runtime.Type) runtime.StructTags {
|
||||
tags := runtime.StructTags{}
|
||||
fieldNum := typ.NumField()
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
field := typ.Field(i)
|
||||
if runtime.IsIgnoredStructField(field) {
|
||||
continue
|
||||
}
|
||||
tags = append(tags, runtime.StructTagFromField(field))
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
fieldNum := typ.NumField()
|
||||
fieldMap := map[string]*structFieldSet{}
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
if dec, exists := structTypeToDecoder[typeptr]; exists {
|
||||
return dec, nil
|
||||
}
|
||||
structDec := newStructDecoder(structName, fieldName, fieldMap)
|
||||
structTypeToDecoder[typeptr] = structDec
|
||||
structName = typ.Name()
|
||||
tags := typeToStructTags(typ)
|
||||
allFields := []*structFieldSet{}
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
field := typ.Field(i)
|
||||
if runtime.IsIgnoredStructField(field) {
|
||||
continue
|
||||
}
|
||||
isUnexportedField := unicode.IsLower([]rune(field.Name)[0])
|
||||
tag := runtime.StructTagFromField(field)
|
||||
dec, err := compile(runtime.Type2RType(field.Type), structName, field.Name, structTypeToDecoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field.Anonymous && !tag.IsTaggedKey {
|
||||
if stDec, ok := dec.(*structDecoder); ok {
|
||||
if runtime.Type2RType(field.Type) == typ {
|
||||
// recursive definition
|
||||
continue
|
||||
}
|
||||
for k, v := range stDec.fieldMap {
|
||||
if tags.ExistsKey(k) {
|
||||
continue
|
||||
}
|
||||
fieldSet := &structFieldSet{
|
||||
dec: v.dec,
|
||||
offset: field.Offset + v.offset,
|
||||
isTaggedKey: v.isTaggedKey,
|
||||
key: k,
|
||||
keyLen: int64(len(k)),
|
||||
}
|
||||
allFields = append(allFields, fieldSet)
|
||||
}
|
||||
} else if pdec, ok := dec.(*ptrDecoder); ok {
|
||||
contentDec := pdec.contentDecoder()
|
||||
if pdec.typ == typ {
|
||||
// recursive definition
|
||||
continue
|
||||
}
|
||||
var fieldSetErr error
|
||||
if isUnexportedField {
|
||||
fieldSetErr = fmt.Errorf(
|
||||
"json: cannot set embedded pointer to unexported struct: %v",
|
||||
field.Type.Elem(),
|
||||
)
|
||||
}
|
||||
if dec, ok := contentDec.(*structDecoder); ok {
|
||||
for k, v := range dec.fieldMap {
|
||||
if tags.ExistsKey(k) {
|
||||
continue
|
||||
}
|
||||
fieldSet := &structFieldSet{
|
||||
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
|
||||
offset: field.Offset,
|
||||
isTaggedKey: v.isTaggedKey,
|
||||
key: k,
|
||||
keyLen: int64(len(k)),
|
||||
err: fieldSetErr,
|
||||
}
|
||||
allFields = append(allFields, fieldSet)
|
||||
}
|
||||
} else {
|
||||
fieldSet := &structFieldSet{
|
||||
dec: pdec,
|
||||
offset: field.Offset,
|
||||
isTaggedKey: tag.IsTaggedKey,
|
||||
key: field.Name,
|
||||
keyLen: int64(len(field.Name)),
|
||||
}
|
||||
allFields = append(allFields, fieldSet)
|
||||
}
|
||||
} else {
|
||||
fieldSet := &structFieldSet{
|
||||
dec: dec,
|
||||
offset: field.Offset,
|
||||
isTaggedKey: tag.IsTaggedKey,
|
||||
key: field.Name,
|
||||
keyLen: int64(len(field.Name)),
|
||||
}
|
||||
allFields = append(allFields, fieldSet)
|
||||
}
|
||||
} else {
|
||||
if tag.IsString && isStringTagSupportedType(runtime.Type2RType(field.Type)) {
|
||||
dec = newWrappedStringDecoder(runtime.Type2RType(field.Type), dec, structName, field.Name)
|
||||
}
|
||||
var key string
|
||||
if tag.Key != "" {
|
||||
key = tag.Key
|
||||
} else {
|
||||
key = field.Name
|
||||
}
|
||||
fieldSet := &structFieldSet{
|
||||
dec: dec,
|
||||
offset: field.Offset,
|
||||
isTaggedKey: tag.IsTaggedKey,
|
||||
key: key,
|
||||
keyLen: int64(len(key)),
|
||||
}
|
||||
allFields = append(allFields, fieldSet)
|
||||
}
|
||||
}
|
||||
for _, set := range filterDuplicatedFields(allFields) {
|
||||
fieldMap[set.key] = set
|
||||
lower := strings.ToLower(set.key)
|
||||
if _, exists := fieldMap[lower]; !exists {
|
||||
// first win
|
||||
fieldMap[lower] = set
|
||||
}
|
||||
}
|
||||
delete(structTypeToDecoder, typeptr)
|
||||
structDec.tryOptimize()
|
||||
return structDec, nil
|
||||
}
|
||||
|
||||
func filterDuplicatedFields(allFields []*structFieldSet) []*structFieldSet {
|
||||
fieldMap := map[string][]*structFieldSet{}
|
||||
for _, field := range allFields {
|
||||
fieldMap[field.key] = append(fieldMap[field.key], field)
|
||||
}
|
||||
duplicatedFieldMap := map[string]struct{}{}
|
||||
for k, sets := range fieldMap {
|
||||
sets = filterFieldSets(sets)
|
||||
if len(sets) != 1 {
|
||||
duplicatedFieldMap[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
filtered := make([]*structFieldSet, 0, len(allFields))
|
||||
for _, field := range allFields {
|
||||
if _, exists := duplicatedFieldMap[field.key]; exists {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, field)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func filterFieldSets(sets []*structFieldSet) []*structFieldSet {
|
||||
if len(sets) == 1 {
|
||||
return sets
|
||||
}
|
||||
filtered := make([]*structFieldSet, 0, len(sets))
|
||||
for _, set := range sets {
|
||||
if set.isTaggedKey {
|
||||
filtered = append(filtered, set)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func implementsUnmarshalJSONType(typ *runtime.Type) bool {
|
||||
return typ.Implements(unmarshalJSONType) || typ.Implements(unmarshalJSONContextType)
|
||||
}
|
30
internal/decoder/compile_norace.go
Normal file
30
internal/decoder/compile_norace.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) {
|
||||
initDecoder()
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
if typeptr > typeAddr.MaxTypeAddr {
|
||||
return compileToGetDecoderSlowPath(typeptr, typ)
|
||||
}
|
||||
|
||||
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
|
||||
if dec := cachedDecoder[index]; dec != nil {
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
dec, err := compileHead(typ, map[uintptr]Decoder{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cachedDecoder[index] = dec
|
||||
return dec, nil
|
||||
}
|
38
internal/decoder/compile_race.go
Normal file
38
internal/decoder/compile_race.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
//go:build race
|
||||
// +build race
|
||||
|
||||
package decoder
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
var decMu sync.RWMutex
|
||||
|
||||
func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) {
|
||||
initDecoder()
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
if typeptr > typeAddr.MaxTypeAddr {
|
||||
return compileToGetDecoderSlowPath(typeptr, typ)
|
||||
}
|
||||
|
||||
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
|
||||
decMu.RLock()
|
||||
if dec := cachedDecoder[index]; dec != nil {
|
||||
decMu.RUnlock()
|
||||
return dec, nil
|
||||
}
|
||||
decMu.RUnlock()
|
||||
|
||||
dec, err := compileHead(typ, map[uintptr]Decoder{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decMu.Lock()
|
||||
cachedDecoder[index] = dec
|
||||
decMu.Unlock()
|
||||
return dec, nil
|
||||
}
|
254
internal/decoder/context.go
Normal file
254
internal/decoder/context.go
Normal file
|
@ -0,0 +1,254 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
type RuntimeContext struct {
|
||||
Buf []byte
|
||||
Option *Option
|
||||
}
|
||||
|
||||
var (
|
||||
runtimeContextPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &RuntimeContext{
|
||||
Option: &Option{},
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TakeRuntimeContext() *RuntimeContext {
|
||||
return runtimeContextPool.Get().(*RuntimeContext)
|
||||
}
|
||||
|
||||
func ReleaseRuntimeContext(ctx *RuntimeContext) {
|
||||
runtimeContextPool.Put(ctx)
|
||||
}
|
||||
|
||||
var (
|
||||
isWhiteSpace = [256]bool{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
isWhiteSpace[' '] = true
|
||||
isWhiteSpace['\n'] = true
|
||||
isWhiteSpace['\t'] = true
|
||||
isWhiteSpace['\r'] = true
|
||||
}
|
||||
|
||||
func char(ptr unsafe.Pointer, offset int64) byte {
|
||||
return *(*byte)(unsafe.Pointer(uintptr(ptr) + uintptr(offset)))
|
||||
}
|
||||
|
||||
func skipWhiteSpace(buf []byte, cursor int64) int64 {
|
||||
for isWhiteSpace[buf[cursor]] {
|
||||
cursor++
|
||||
}
|
||||
return cursor
|
||||
}
|
||||
|
||||
func skipObject(buf []byte, cursor, depth int64) (int64, error) {
|
||||
braceCount := 1
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case '{':
|
||||
braceCount++
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
case '}':
|
||||
depth--
|
||||
braceCount--
|
||||
if braceCount == 0 {
|
||||
return cursor + 1, nil
|
||||
}
|
||||
case '[':
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
case ']':
|
||||
depth--
|
||||
case '"':
|
||||
for {
|
||||
cursor++
|
||||
switch buf[cursor] {
|
||||
case '\\':
|
||||
cursor++
|
||||
if buf[cursor] == nul {
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
case '"':
|
||||
goto SWITCH_OUT
|
||||
case nul:
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
}
|
||||
case nul:
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("object of object", cursor)
|
||||
}
|
||||
SWITCH_OUT:
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func skipArray(buf []byte, cursor, depth int64) (int64, error) {
|
||||
bracketCount := 1
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case '[':
|
||||
bracketCount++
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
case ']':
|
||||
bracketCount--
|
||||
depth--
|
||||
if bracketCount == 0 {
|
||||
return cursor + 1, nil
|
||||
}
|
||||
case '{':
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
case '}':
|
||||
depth--
|
||||
case '"':
|
||||
for {
|
||||
cursor++
|
||||
switch buf[cursor] {
|
||||
case '\\':
|
||||
cursor++
|
||||
if buf[cursor] == nul {
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
case '"':
|
||||
goto SWITCH_OUT
|
||||
case nul:
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
}
|
||||
case nul:
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("array of object", cursor)
|
||||
}
|
||||
SWITCH_OUT:
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func skipValue(buf []byte, cursor, depth int64) (int64, error) {
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case '{':
|
||||
return skipObject(buf, cursor+1, depth+1)
|
||||
case '[':
|
||||
return skipArray(buf, cursor+1, depth+1)
|
||||
case '"':
|
||||
for {
|
||||
cursor++
|
||||
switch buf[cursor] {
|
||||
case '\\':
|
||||
cursor++
|
||||
if buf[cursor] == nul {
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
case '"':
|
||||
return cursor + 1, nil
|
||||
case nul:
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
for {
|
||||
cursor++
|
||||
if floatTable[buf[cursor]] {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return cursor, nil
|
||||
case 't':
|
||||
if err := validateTrue(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return cursor, nil
|
||||
case 'f':
|
||||
if err := validateFalse(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 5
|
||||
return cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return cursor, nil
|
||||
default:
|
||||
return cursor, errors.ErrUnexpectedEndOfJSON("null", cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateTrue(buf []byte, cursor int64) error {
|
||||
if cursor+3 >= int64(len(buf)) {
|
||||
return errors.ErrUnexpectedEndOfJSON("true", cursor)
|
||||
}
|
||||
if buf[cursor+1] != 'r' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+1], "true", cursor)
|
||||
}
|
||||
if buf[cursor+2] != 'u' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+2], "true", cursor)
|
||||
}
|
||||
if buf[cursor+3] != 'e' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+3], "true", cursor)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateFalse(buf []byte, cursor int64) error {
|
||||
if cursor+4 >= int64(len(buf)) {
|
||||
return errors.ErrUnexpectedEndOfJSON("false", cursor)
|
||||
}
|
||||
if buf[cursor+1] != 'a' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+1], "false", cursor)
|
||||
}
|
||||
if buf[cursor+2] != 'l' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+2], "false", cursor)
|
||||
}
|
||||
if buf[cursor+3] != 's' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+3], "false", cursor)
|
||||
}
|
||||
if buf[cursor+4] != 'e' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+4], "false", cursor)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateNull(buf []byte, cursor int64) error {
|
||||
if cursor+3 >= int64(len(buf)) {
|
||||
return errors.ErrUnexpectedEndOfJSON("null", cursor)
|
||||
}
|
||||
if buf[cursor+1] != 'u' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+1], "null", cursor)
|
||||
}
|
||||
if buf[cursor+2] != 'l' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+2], "null", cursor)
|
||||
}
|
||||
if buf[cursor+3] != 'l' {
|
||||
return errors.ErrInvalidCharacter(buf[cursor+3], "null", cursor)
|
||||
}
|
||||
return nil
|
||||
}
|
170
internal/decoder/float.go
Normal file
170
internal/decoder/float.go
Normal file
|
@ -0,0 +1,170 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
type floatDecoder struct {
|
||||
op func(unsafe.Pointer, float64)
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newFloatDecoder(structName, fieldName string, op func(unsafe.Pointer, float64)) *floatDecoder {
|
||||
return &floatDecoder{op: op, structName: structName, fieldName: fieldName}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
validEndNumberChar = [256]bool{
|
||||
nul: true,
|
||||
' ': true,
|
||||
'\t': true,
|
||||
'\r': true,
|
||||
'\n': true,
|
||||
',': true,
|
||||
':': true,
|
||||
'}': true,
|
||||
']': true,
|
||||
}
|
||||
)
|
||||
|
||||
func floatBytes(s *Stream) []byte {
|
||||
start := s.cursor
|
||||
for {
|
||||
s.cursor++
|
||||
if floatTable[s.char()] {
|
||||
continue
|
||||
} else if s.char() == nul {
|
||||
if s.read() {
|
||||
s.cursor-- // for retry current character
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return s.buf[start:s.cursor]
|
||||
}
|
||||
|
||||
func (d *floatDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
continue
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return floatBytes(s), nil
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
default:
|
||||
goto ERROR
|
||||
}
|
||||
}
|
||||
ERROR:
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("float", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *floatDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
start := cursor
|
||||
cursor++
|
||||
for floatTable[buf[cursor]] {
|
||||
cursor++
|
||||
}
|
||||
num := buf[start:cursor]
|
||||
return num, cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return nil, cursor, nil
|
||||
default:
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("float", cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *floatDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
bytes, err := d.decodeStreamByte(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes == nil {
|
||||
return nil
|
||||
}
|
||||
str := *(*string)(unsafe.Pointer(&bytes))
|
||||
f64, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return errors.ErrSyntax(err.Error(), s.totalOffset())
|
||||
}
|
||||
d.op(p, f64)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *floatDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
bytes, c, err := d.decodeByte(buf, cursor)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
return c, nil
|
||||
}
|
||||
cursor = c
|
||||
if !validEndNumberChar[buf[cursor]] {
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("float", cursor)
|
||||
}
|
||||
s := *(*string)(unsafe.Pointer(&bytes))
|
||||
f64, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return 0, errors.ErrSyntax(err.Error(), cursor)
|
||||
}
|
||||
d.op(p, f64)
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (d *floatDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
buf := ctx.Buf
|
||||
bytes, c, err := d.decodeByte(buf, cursor)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
return [][]byte{nullbytes}, c, nil
|
||||
}
|
||||
return [][]byte{bytes}, c, nil
|
||||
}
|
146
internal/decoder/func.go
Normal file
146
internal/decoder/func.go
Normal file
|
@ -0,0 +1,146 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type funcDecoder struct {
|
||||
typ *runtime.Type
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newFuncDecoder(typ *runtime.Type, structName, fieldName string) *funcDecoder {
|
||||
fnDecoder := &funcDecoder{typ, structName, fieldName}
|
||||
return fnDecoder
|
||||
}
|
||||
|
||||
func (d *funcDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
s.skipWhiteSpace()
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
src := s.buf[start:s.cursor]
|
||||
if len(src) > 0 {
|
||||
switch src[0] {
|
||||
case '"':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "string",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case '[':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "array",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case '{':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "number",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return nil
|
||||
case 't':
|
||||
if err := trueBytes(s); err == nil {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "boolean",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
}
|
||||
case 'f':
|
||||
if err := falseBytes(s); err == nil {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "boolean",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.ErrInvalidBeginningOfValue(s.buf[s.cursor], s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *funcDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src := buf[start:end]
|
||||
if len(src) > 0 {
|
||||
switch src[0] {
|
||||
case '"':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "string",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case '[':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "array",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case '{':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "number",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case 'n':
|
||||
if bytes.Equal(src, nullbytes) {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return end, nil
|
||||
}
|
||||
case 't':
|
||||
if err := validateTrue(buf, start); err == nil {
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "boolean",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
}
|
||||
case 'f':
|
||||
if err := validateFalse(buf, start); err == nil {
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "boolean",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
func (d *funcDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: func decoder does not support decode path")
|
||||
}
|
246
internal/decoder/int.go
Normal file
246
internal/decoder/int.go
Normal file
|
@ -0,0 +1,246 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type intDecoder struct {
|
||||
typ *runtime.Type
|
||||
kind reflect.Kind
|
||||
op func(unsafe.Pointer, int64)
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newIntDecoder(typ *runtime.Type, structName, fieldName string, op func(unsafe.Pointer, int64)) *intDecoder {
|
||||
return &intDecoder{
|
||||
typ: typ,
|
||||
kind: typ.Kind(),
|
||||
op: op,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *intDecoder) typeError(buf []byte, offset int64) *errors.UnmarshalTypeError {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: fmt.Sprintf("number %s", string(buf)),
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Struct: d.structName,
|
||||
Field: d.fieldName,
|
||||
Offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
pow10i64 = [...]int64{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18,
|
||||
}
|
||||
pow10i64Len = len(pow10i64)
|
||||
)
|
||||
|
||||
func (d *intDecoder) parseInt(b []byte) (int64, error) {
|
||||
isNegative := false
|
||||
if b[0] == '-' {
|
||||
b = b[1:]
|
||||
isNegative = true
|
||||
}
|
||||
maxDigit := len(b)
|
||||
if maxDigit > pow10i64Len {
|
||||
return 0, fmt.Errorf("invalid length of number")
|
||||
}
|
||||
sum := int64(0)
|
||||
for i := 0; i < maxDigit; i++ {
|
||||
c := int64(b[i]) - 48
|
||||
digitValue := pow10i64[maxDigit-i-1]
|
||||
sum += c * digitValue
|
||||
}
|
||||
if isNegative {
|
||||
return -1 * sum, nil
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
var (
|
||||
numTable = [256]bool{
|
||||
'0': true,
|
||||
'1': true,
|
||||
'2': true,
|
||||
'3': true,
|
||||
'4': true,
|
||||
'5': true,
|
||||
'6': true,
|
||||
'7': true,
|
||||
'8': true,
|
||||
'9': true,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
numZeroBuf = []byte{'0'}
|
||||
)
|
||||
|
||||
func (d *intDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
continue
|
||||
case '-':
|
||||
start := s.cursor
|
||||
for {
|
||||
s.cursor++
|
||||
if numTable[s.char()] {
|
||||
continue
|
||||
} else if s.char() == nul {
|
||||
if s.read() {
|
||||
s.cursor-- // for retry current character
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
num := s.buf[start:s.cursor]
|
||||
if len(num) < 2 {
|
||||
goto ERROR
|
||||
}
|
||||
return num, nil
|
||||
case '0':
|
||||
s.cursor++
|
||||
return numZeroBuf, nil
|
||||
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
start := s.cursor
|
||||
for {
|
||||
s.cursor++
|
||||
if numTable[s.char()] {
|
||||
continue
|
||||
} else if s.char() == nul {
|
||||
if s.read() {
|
||||
s.cursor-- // for retry current character
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
num := s.buf[start:s.cursor]
|
||||
return num, nil
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
default:
|
||||
return nil, d.typeError([]byte{s.char()}, s.totalOffset())
|
||||
}
|
||||
}
|
||||
ERROR:
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("number(integer)", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *intDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
|
||||
for {
|
||||
switch char(b, cursor) {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case '0':
|
||||
cursor++
|
||||
return numZeroBuf, cursor, nil
|
||||
case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
start := cursor
|
||||
cursor++
|
||||
for numTable[char(b, cursor)] {
|
||||
cursor++
|
||||
}
|
||||
num := buf[start:cursor]
|
||||
return num, cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return nil, cursor, nil
|
||||
default:
|
||||
return nil, 0, d.typeError([]byte{char(b, cursor)}, cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *intDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
bytes, err := d.decodeStreamByte(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes == nil {
|
||||
return nil
|
||||
}
|
||||
i64, err := d.parseInt(bytes)
|
||||
if err != nil {
|
||||
return d.typeError(bytes, s.totalOffset())
|
||||
}
|
||||
switch d.kind {
|
||||
case reflect.Int8:
|
||||
if i64 < -1*(1<<7) || (1<<7) <= i64 {
|
||||
return d.typeError(bytes, s.totalOffset())
|
||||
}
|
||||
case reflect.Int16:
|
||||
if i64 < -1*(1<<15) || (1<<15) <= i64 {
|
||||
return d.typeError(bytes, s.totalOffset())
|
||||
}
|
||||
case reflect.Int32:
|
||||
if i64 < -1*(1<<31) || (1<<31) <= i64 {
|
||||
return d.typeError(bytes, s.totalOffset())
|
||||
}
|
||||
}
|
||||
d.op(p, i64)
|
||||
s.reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *intDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
return c, nil
|
||||
}
|
||||
cursor = c
|
||||
|
||||
i64, err := d.parseInt(bytes)
|
||||
if err != nil {
|
||||
return 0, d.typeError(bytes, cursor)
|
||||
}
|
||||
switch d.kind {
|
||||
case reflect.Int8:
|
||||
if i64 < -1*(1<<7) || (1<<7) <= i64 {
|
||||
return 0, d.typeError(bytes, cursor)
|
||||
}
|
||||
case reflect.Int16:
|
||||
if i64 < -1*(1<<15) || (1<<15) <= i64 {
|
||||
return 0, d.typeError(bytes, cursor)
|
||||
}
|
||||
case reflect.Int32:
|
||||
if i64 < -1*(1<<31) || (1<<31) <= i64 {
|
||||
return 0, d.typeError(bytes, cursor)
|
||||
}
|
||||
}
|
||||
d.op(p, i64)
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (d *intDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: int decoder does not support decode path")
|
||||
}
|
528
internal/decoder/interface.go
Normal file
528
internal/decoder/interface.go
Normal file
|
@ -0,0 +1,528 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type interfaceDecoder struct {
|
||||
typ *runtime.Type
|
||||
structName string
|
||||
fieldName string
|
||||
sliceDecoder *sliceDecoder
|
||||
mapDecoder *mapDecoder
|
||||
floatDecoder *floatDecoder
|
||||
numberDecoder *numberDecoder
|
||||
stringDecoder *stringDecoder
|
||||
}
|
||||
|
||||
func newEmptyInterfaceDecoder(structName, fieldName string) *interfaceDecoder {
|
||||
ifaceDecoder := &interfaceDecoder{
|
||||
typ: emptyInterfaceType,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
floatDecoder: newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) {
|
||||
*(*interface{})(p) = v
|
||||
}),
|
||||
numberDecoder: newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v json.Number) {
|
||||
*(*interface{})(p) = v
|
||||
}),
|
||||
stringDecoder: newStringDecoder(structName, fieldName),
|
||||
}
|
||||
ifaceDecoder.sliceDecoder = newSliceDecoder(
|
||||
ifaceDecoder,
|
||||
emptyInterfaceType,
|
||||
emptyInterfaceType.Size(),
|
||||
structName, fieldName,
|
||||
)
|
||||
ifaceDecoder.mapDecoder = newMapDecoder(
|
||||
interfaceMapType,
|
||||
stringType,
|
||||
ifaceDecoder.stringDecoder,
|
||||
interfaceMapType.Elem(),
|
||||
ifaceDecoder,
|
||||
structName,
|
||||
fieldName,
|
||||
)
|
||||
return ifaceDecoder
|
||||
}
|
||||
|
||||
func newInterfaceDecoder(typ *runtime.Type, structName, fieldName string) *interfaceDecoder {
|
||||
emptyIfaceDecoder := newEmptyInterfaceDecoder(structName, fieldName)
|
||||
stringDecoder := newStringDecoder(structName, fieldName)
|
||||
return &interfaceDecoder{
|
||||
typ: typ,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
sliceDecoder: newSliceDecoder(
|
||||
emptyIfaceDecoder,
|
||||
emptyInterfaceType,
|
||||
emptyInterfaceType.Size(),
|
||||
structName, fieldName,
|
||||
),
|
||||
mapDecoder: newMapDecoder(
|
||||
interfaceMapType,
|
||||
stringType,
|
||||
stringDecoder,
|
||||
interfaceMapType.Elem(),
|
||||
emptyIfaceDecoder,
|
||||
structName,
|
||||
fieldName,
|
||||
),
|
||||
floatDecoder: newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) {
|
||||
*(*interface{})(p) = v
|
||||
}),
|
||||
numberDecoder: newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v json.Number) {
|
||||
*(*interface{})(p) = v
|
||||
}),
|
||||
stringDecoder: stringDecoder,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *interfaceDecoder) numDecoder(s *Stream) Decoder {
|
||||
if s.UseNumber {
|
||||
return d.numberDecoder
|
||||
}
|
||||
return d.floatDecoder
|
||||
}
|
||||
|
||||
var (
|
||||
emptyInterfaceType = runtime.Type2RType(reflect.TypeOf((*interface{})(nil)).Elem())
|
||||
EmptyInterfaceType = emptyInterfaceType
|
||||
interfaceMapType = runtime.Type2RType(
|
||||
reflect.TypeOf((*map[string]interface{})(nil)).Elem(),
|
||||
)
|
||||
stringType = runtime.Type2RType(
|
||||
reflect.TypeOf(""),
|
||||
)
|
||||
)
|
||||
|
||||
func decodeStreamUnmarshaler(s *Stream, depth int64, unmarshaler json.Unmarshaler) error {
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
src := s.buf[start:s.cursor]
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
if err := unmarshaler.UnmarshalJSON(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeStreamUnmarshalerContext(s *Stream, depth int64, unmarshaler unmarshalerContext) error {
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
src := s.buf[start:s.cursor]
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
if err := unmarshaler.UnmarshalJSON(s.Option.Context, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeUnmarshaler(buf []byte, cursor, depth int64, unmarshaler json.Unmarshaler) (int64, error) {
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src := buf[start:end]
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
if err := unmarshaler.UnmarshalJSON(dst); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return end, nil
|
||||
}
|
||||
|
||||
func decodeUnmarshalerContext(ctx *RuntimeContext, buf []byte, cursor, depth int64, unmarshaler unmarshalerContext) (int64, error) {
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src := buf[start:end]
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
if err := unmarshaler.UnmarshalJSON(ctx.Option.Context, dst); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return end, nil
|
||||
}
|
||||
|
||||
func decodeStreamTextUnmarshaler(s *Stream, depth int64, unmarshaler encoding.TextUnmarshaler, p unsafe.Pointer) error {
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
src := s.buf[start:s.cursor]
|
||||
if bytes.Equal(src, nullbytes) {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
if err := unmarshaler.UnmarshalText(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeTextUnmarshaler(buf []byte, cursor, depth int64, unmarshaler encoding.TextUnmarshaler, p unsafe.Pointer) (int64, error) {
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src := buf[start:end]
|
||||
if bytes.Equal(src, nullbytes) {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return end, nil
|
||||
}
|
||||
if s, ok := unquoteBytes(src); ok {
|
||||
src = s
|
||||
}
|
||||
if err := unmarshaler.UnmarshalText(src); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return end, nil
|
||||
}
|
||||
|
||||
func (d *interfaceDecoder) decodeStreamEmptyInterface(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
c := s.skipWhiteSpace()
|
||||
for {
|
||||
switch c {
|
||||
case '{':
|
||||
var v map[string]interface{}
|
||||
ptr := unsafe.Pointer(&v)
|
||||
if err := d.mapDecoder.DecodeStream(s, depth, ptr); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*interface{})(p) = v
|
||||
return nil
|
||||
case '[':
|
||||
var v []interface{}
|
||||
ptr := unsafe.Pointer(&v)
|
||||
if err := d.sliceDecoder.DecodeStream(s, depth, ptr); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*interface{})(p) = v
|
||||
return nil
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return d.numDecoder(s).DecodeStream(s, depth, p)
|
||||
case '"':
|
||||
s.cursor++
|
||||
start := s.cursor
|
||||
for {
|
||||
switch s.char() {
|
||||
case '\\':
|
||||
if _, err := decodeEscapeString(s, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
case '"':
|
||||
literal := s.buf[start:s.cursor]
|
||||
s.cursor++
|
||||
*(*interface{})(p) = string(literal)
|
||||
return nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
}
|
||||
s.cursor++
|
||||
}
|
||||
case 't':
|
||||
if err := trueBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
**(**interface{})(unsafe.Pointer(&p)) = true
|
||||
return nil
|
||||
case 'f':
|
||||
if err := falseBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
**(**interface{})(unsafe.Pointer(&p)) = false
|
||||
return nil
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*interface{})(p) = nil
|
||||
return nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
c = s.char()
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return errors.ErrInvalidBeginningOfValue(c, s.totalOffset())
|
||||
}
|
||||
|
||||
type emptyInterface struct {
|
||||
typ *runtime.Type
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func (d *interfaceDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
runtimeInterfaceValue := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
rv := reflect.ValueOf(runtimeInterfaceValue)
|
||||
if rv.NumMethod() > 0 && rv.CanInterface() {
|
||||
if u, ok := rv.Interface().(unmarshalerContext); ok {
|
||||
return decodeStreamUnmarshalerContext(s, depth, u)
|
||||
}
|
||||
if u, ok := rv.Interface().(json.Unmarshaler); ok {
|
||||
return decodeStreamUnmarshaler(s, depth, u)
|
||||
}
|
||||
if u, ok := rv.Interface().(encoding.TextUnmarshaler); ok {
|
||||
return decodeStreamTextUnmarshaler(s, depth, u, p)
|
||||
}
|
||||
if s.skipWhiteSpace() == 'n' {
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*interface{})(p) = nil
|
||||
return nil
|
||||
}
|
||||
return d.errUnmarshalType(rv.Type(), s.totalOffset())
|
||||
}
|
||||
iface := rv.Interface()
|
||||
ifaceHeader := (*emptyInterface)(unsafe.Pointer(&iface))
|
||||
typ := ifaceHeader.typ
|
||||
if ifaceHeader.ptr == nil || d.typ == typ || typ == nil {
|
||||
// concrete type is empty interface
|
||||
return d.decodeStreamEmptyInterface(s, depth, p)
|
||||
}
|
||||
if typ.Kind() == reflect.Ptr && typ.Elem() == d.typ || typ.Kind() != reflect.Ptr {
|
||||
return d.decodeStreamEmptyInterface(s, depth, p)
|
||||
}
|
||||
if s.skipWhiteSpace() == 'n' {
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*interface{})(p) = nil
|
||||
return nil
|
||||
}
|
||||
decoder, err := CompileToGetDecoder(typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return decoder.DecodeStream(s, depth, ifaceHeader.ptr)
|
||||
}
|
||||
|
||||
func (d *interfaceDecoder) errUnmarshalType(typ reflect.Type, offset int64) *errors.UnmarshalTypeError {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: typ.String(),
|
||||
Type: typ,
|
||||
Offset: offset,
|
||||
Struct: d.structName,
|
||||
Field: d.fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *interfaceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
runtimeInterfaceValue := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
rv := reflect.ValueOf(runtimeInterfaceValue)
|
||||
if rv.NumMethod() > 0 && rv.CanInterface() {
|
||||
if u, ok := rv.Interface().(unmarshalerContext); ok {
|
||||
return decodeUnmarshalerContext(ctx, buf, cursor, depth, u)
|
||||
}
|
||||
if u, ok := rv.Interface().(json.Unmarshaler); ok {
|
||||
return decodeUnmarshaler(buf, cursor, depth, u)
|
||||
}
|
||||
if u, ok := rv.Interface().(encoding.TextUnmarshaler); ok {
|
||||
return decodeTextUnmarshaler(buf, cursor, depth, u, p)
|
||||
}
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == 'n' {
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
**(**interface{})(unsafe.Pointer(&p)) = nil
|
||||
return cursor, nil
|
||||
}
|
||||
return 0, d.errUnmarshalType(rv.Type(), cursor)
|
||||
}
|
||||
|
||||
iface := rv.Interface()
|
||||
ifaceHeader := (*emptyInterface)(unsafe.Pointer(&iface))
|
||||
typ := ifaceHeader.typ
|
||||
if ifaceHeader.ptr == nil || d.typ == typ || typ == nil {
|
||||
// concrete type is empty interface
|
||||
return d.decodeEmptyInterface(ctx, cursor, depth, p)
|
||||
}
|
||||
if typ.Kind() == reflect.Ptr && typ.Elem() == d.typ || typ.Kind() != reflect.Ptr {
|
||||
return d.decodeEmptyInterface(ctx, cursor, depth, p)
|
||||
}
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == 'n' {
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
**(**interface{})(unsafe.Pointer(&p)) = nil
|
||||
return cursor, nil
|
||||
}
|
||||
decoder, err := CompileToGetDecoder(typ)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return decoder.Decode(ctx, cursor, depth, ifaceHeader.ptr)
|
||||
}
|
||||
|
||||
func (d *interfaceDecoder) decodeEmptyInterface(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
switch buf[cursor] {
|
||||
case '{':
|
||||
var v map[string]interface{}
|
||||
ptr := unsafe.Pointer(&v)
|
||||
cursor, err := d.mapDecoder.Decode(ctx, cursor, depth, ptr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
**(**interface{})(unsafe.Pointer(&p)) = v
|
||||
return cursor, nil
|
||||
case '[':
|
||||
var v []interface{}
|
||||
ptr := unsafe.Pointer(&v)
|
||||
cursor, err := d.sliceDecoder.Decode(ctx, cursor, depth, ptr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
**(**interface{})(unsafe.Pointer(&p)) = v
|
||||
return cursor, nil
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return d.floatDecoder.Decode(ctx, cursor, depth, p)
|
||||
case '"':
|
||||
var v string
|
||||
ptr := unsafe.Pointer(&v)
|
||||
cursor, err := d.stringDecoder.Decode(ctx, cursor, depth, ptr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
**(**interface{})(unsafe.Pointer(&p)) = v
|
||||
return cursor, nil
|
||||
case 't':
|
||||
if err := validateTrue(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
**(**interface{})(unsafe.Pointer(&p)) = true
|
||||
return cursor, nil
|
||||
case 'f':
|
||||
if err := validateFalse(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 5
|
||||
**(**interface{})(unsafe.Pointer(&p)) = false
|
||||
return cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
**(**interface{})(unsafe.Pointer(&p)) = nil
|
||||
return cursor, nil
|
||||
}
|
||||
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
func NewPathDecoder() Decoder {
|
||||
ifaceDecoder := &interfaceDecoder{
|
||||
typ: emptyInterfaceType,
|
||||
structName: "",
|
||||
fieldName: "",
|
||||
floatDecoder: newFloatDecoder("", "", func(p unsafe.Pointer, v float64) {
|
||||
*(*interface{})(p) = v
|
||||
}),
|
||||
numberDecoder: newNumberDecoder("", "", func(p unsafe.Pointer, v json.Number) {
|
||||
*(*interface{})(p) = v
|
||||
}),
|
||||
stringDecoder: newStringDecoder("", ""),
|
||||
}
|
||||
ifaceDecoder.sliceDecoder = newSliceDecoder(
|
||||
ifaceDecoder,
|
||||
emptyInterfaceType,
|
||||
emptyInterfaceType.Size(),
|
||||
"", "",
|
||||
)
|
||||
ifaceDecoder.mapDecoder = newMapDecoder(
|
||||
interfaceMapType,
|
||||
stringType,
|
||||
ifaceDecoder.stringDecoder,
|
||||
interfaceMapType.Elem(),
|
||||
ifaceDecoder,
|
||||
"", "",
|
||||
)
|
||||
return ifaceDecoder
|
||||
}
|
||||
|
||||
var (
|
||||
truebytes = []byte("true")
|
||||
falsebytes = []byte("false")
|
||||
)
|
||||
|
||||
func (d *interfaceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
switch buf[cursor] {
|
||||
case '{':
|
||||
return d.mapDecoder.DecodePath(ctx, cursor, depth)
|
||||
case '[':
|
||||
return d.sliceDecoder.DecodePath(ctx, cursor, depth)
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return d.floatDecoder.DecodePath(ctx, cursor, depth)
|
||||
case '"':
|
||||
return d.stringDecoder.DecodePath(ctx, cursor, depth)
|
||||
case 't':
|
||||
if err := validateTrue(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return [][]byte{truebytes}, cursor, nil
|
||||
case 'f':
|
||||
if err := validateFalse(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 5
|
||||
return [][]byte{falsebytes}, cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return [][]byte{nullbytes}, cursor, nil
|
||||
}
|
||||
return nil, cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
|
||||
}
|
55
internal/decoder/invalid.go
Normal file
55
internal/decoder/invalid.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type invalidDecoder struct {
|
||||
typ *runtime.Type
|
||||
kind reflect.Kind
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newInvalidDecoder(typ *runtime.Type, structName, fieldName string) *invalidDecoder {
|
||||
return &invalidDecoder{
|
||||
typ: typ,
|
||||
kind: typ.Kind(),
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *invalidDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
Struct: d.structName,
|
||||
Field: d.fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *invalidDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: cursor,
|
||||
Struct: d.structName,
|
||||
Field: d.fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *invalidDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: cursor,
|
||||
Struct: d.structName,
|
||||
Field: d.fieldName,
|
||||
}
|
||||
}
|
280
internal/decoder/map.go
Normal file
280
internal/decoder/map.go
Normal file
|
@ -0,0 +1,280 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type mapDecoder struct {
|
||||
mapType *runtime.Type
|
||||
keyType *runtime.Type
|
||||
valueType *runtime.Type
|
||||
canUseAssignFaststrType bool
|
||||
keyDecoder Decoder
|
||||
valueDecoder Decoder
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder {
|
||||
return &mapDecoder{
|
||||
mapType: mapType,
|
||||
keyDecoder: keyDec,
|
||||
keyType: keyType,
|
||||
canUseAssignFaststrType: canUseAssignFaststrType(keyType, valueType),
|
||||
valueType: valueType,
|
||||
valueDecoder: valueDec,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
mapMaxElemSize = 128
|
||||
)
|
||||
|
||||
// See detail: https://github.com/goccy/go-json/pull/283
|
||||
func canUseAssignFaststrType(key *runtime.Type, value *runtime.Type) bool {
|
||||
indirectElem := value.Size() > mapMaxElemSize
|
||||
if indirectElem {
|
||||
return false
|
||||
}
|
||||
return key.Kind() == reflect.String
|
||||
}
|
||||
|
||||
//go:linkname makemap reflect.makemap
|
||||
func makemap(*runtime.Type, int) unsafe.Pointer
|
||||
|
||||
//nolint:golint
|
||||
//go:linkname mapassign_faststr runtime.mapassign_faststr
|
||||
//go:noescape
|
||||
func mapassign_faststr(t *runtime.Type, m unsafe.Pointer, s string) unsafe.Pointer
|
||||
|
||||
//go:linkname mapassign reflect.mapassign
|
||||
//go:noescape
|
||||
func mapassign(t *runtime.Type, m unsafe.Pointer, k, v unsafe.Pointer)
|
||||
|
||||
func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) {
|
||||
if d.canUseAssignFaststrType {
|
||||
mapV := mapassign_faststr(t, m, *(*string)(k))
|
||||
typedmemmove(d.valueType, mapV, v)
|
||||
} else {
|
||||
mapassign(t, m, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
|
||||
}
|
||||
|
||||
switch s.skipWhiteSpace() {
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil
|
||||
return nil
|
||||
case '{':
|
||||
default:
|
||||
return errors.ErrExpected("{ character for map value", s.totalOffset())
|
||||
}
|
||||
mapValue := *(*unsafe.Pointer)(p)
|
||||
if mapValue == nil {
|
||||
mapValue = makemap(d.mapType, 0)
|
||||
}
|
||||
s.cursor++
|
||||
if s.skipWhiteSpace() == '}' {
|
||||
*(*unsafe.Pointer)(p) = mapValue
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
k := unsafe_New(d.keyType)
|
||||
if err := d.keyDecoder.DecodeStream(s, depth, k); err != nil {
|
||||
return err
|
||||
}
|
||||
s.skipWhiteSpace()
|
||||
if !s.equalChar(':') {
|
||||
return errors.ErrExpected("colon after object key", s.totalOffset())
|
||||
}
|
||||
s.cursor++
|
||||
v := unsafe_New(d.valueType)
|
||||
if err := d.valueDecoder.DecodeStream(s, depth, v); err != nil {
|
||||
return err
|
||||
}
|
||||
d.mapassign(d.mapType, mapValue, k, v)
|
||||
s.skipWhiteSpace()
|
||||
if s.equalChar('}') {
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
if !s.equalChar(',') {
|
||||
return errors.ErrExpected("comma after object value", s.totalOffset())
|
||||
}
|
||||
s.cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
buflen := int64(len(buf))
|
||||
if buflen < 2 {
|
||||
return 0, errors.ErrExpected("{} for map", cursor)
|
||||
}
|
||||
switch buf[cursor] {
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil
|
||||
return cursor, nil
|
||||
case '{':
|
||||
default:
|
||||
return 0, errors.ErrExpected("{ character for map value", cursor)
|
||||
}
|
||||
cursor++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
mapValue := *(*unsafe.Pointer)(p)
|
||||
if mapValue == nil {
|
||||
mapValue = makemap(d.mapType, 0)
|
||||
}
|
||||
if buf[cursor] == '}' {
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
|
||||
cursor++
|
||||
return cursor, nil
|
||||
}
|
||||
for {
|
||||
k := unsafe_New(d.keyType)
|
||||
keyCursor, err := d.keyDecoder.Decode(ctx, cursor, depth, k)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(buf, keyCursor)
|
||||
if buf[cursor] != ':' {
|
||||
return 0, errors.ErrExpected("colon after object key", cursor)
|
||||
}
|
||||
cursor++
|
||||
v := unsafe_New(d.valueType)
|
||||
valueCursor, err := d.valueDecoder.Decode(ctx, cursor, depth, v)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.mapassign(d.mapType, mapValue, k, v)
|
||||
cursor = skipWhiteSpace(buf, valueCursor)
|
||||
if buf[cursor] == '}' {
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
|
||||
cursor++
|
||||
return cursor, nil
|
||||
}
|
||||
if buf[cursor] != ',' {
|
||||
return 0, errors.ErrExpected("comma after object value", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func (d *mapDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
buf := ctx.Buf
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
buflen := int64(len(buf))
|
||||
if buflen < 2 {
|
||||
return nil, 0, errors.ErrExpected("{} for map", cursor)
|
||||
}
|
||||
switch buf[cursor] {
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return [][]byte{nullbytes}, cursor, nil
|
||||
case '{':
|
||||
default:
|
||||
return nil, 0, errors.ErrExpected("{ character for map value", cursor)
|
||||
}
|
||||
cursor++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == '}' {
|
||||
cursor++
|
||||
return nil, cursor, nil
|
||||
}
|
||||
keyDecoder, ok := d.keyDecoder.(*stringDecoder)
|
||||
if !ok {
|
||||
return nil, 0, &errors.UnmarshalTypeError{
|
||||
Value: "string",
|
||||
Type: reflect.TypeOf(""),
|
||||
Offset: cursor,
|
||||
Struct: d.structName,
|
||||
Field: d.fieldName,
|
||||
}
|
||||
}
|
||||
ret := [][]byte{}
|
||||
for {
|
||||
key, keyCursor, err := keyDecoder.decodeByte(buf, cursor)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(buf, keyCursor)
|
||||
if buf[cursor] != ':' {
|
||||
return nil, 0, errors.ErrExpected("colon after object key", cursor)
|
||||
}
|
||||
cursor++
|
||||
child, found, err := ctx.Option.Path.Field(string(key))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if found {
|
||||
if child != nil {
|
||||
oldPath := ctx.Option.Path.node
|
||||
ctx.Option.Path.node = child
|
||||
paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ctx.Option.Path.node = oldPath
|
||||
ret = append(ret, paths...)
|
||||
cursor = c
|
||||
} else {
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ret = append(ret, buf[start:end])
|
||||
cursor = end
|
||||
}
|
||||
} else {
|
||||
c, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = c
|
||||
}
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == '}' {
|
||||
cursor++
|
||||
return ret, cursor, nil
|
||||
}
|
||||
if buf[cursor] != ',' {
|
||||
return nil, 0, errors.ErrExpected("comma after object value", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
}
|
123
internal/decoder/number.go
Normal file
123
internal/decoder/number.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
type numberDecoder struct {
|
||||
stringDecoder *stringDecoder
|
||||
op func(unsafe.Pointer, json.Number)
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newNumberDecoder(structName, fieldName string, op func(unsafe.Pointer, json.Number)) *numberDecoder {
|
||||
return &numberDecoder{
|
||||
stringDecoder: newStringDecoder(structName, fieldName),
|
||||
op: op,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *numberDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
bytes, err := d.decodeStreamByte(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil {
|
||||
return errors.ErrSyntax(err.Error(), s.totalOffset())
|
||||
}
|
||||
d.op(p, json.Number(string(bytes)))
|
||||
s.reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *numberDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil {
|
||||
return 0, errors.ErrSyntax(err.Error(), c)
|
||||
}
|
||||
cursor = c
|
||||
s := *(*string)(unsafe.Pointer(&bytes))
|
||||
d.op(p, json.Number(s))
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (d *numberDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
return [][]byte{nullbytes}, c, nil
|
||||
}
|
||||
return [][]byte{bytes}, c, nil
|
||||
}
|
||||
|
||||
func (d *numberDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
|
||||
start := s.cursor
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
continue
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return floatBytes(s), nil
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case '"':
|
||||
return d.stringDecoder.decodeStreamByte(s)
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
default:
|
||||
goto ERROR
|
||||
}
|
||||
}
|
||||
ERROR:
|
||||
if s.cursor == start {
|
||||
return nil, errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
|
||||
}
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("json.Number", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *numberDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
start := cursor
|
||||
cursor++
|
||||
for floatTable[buf[cursor]] {
|
||||
cursor++
|
||||
}
|
||||
num := buf[start:cursor]
|
||||
return num, cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return nil, cursor, nil
|
||||
case '"':
|
||||
return d.stringDecoder.decodeByte(buf, cursor)
|
||||
default:
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("json.Number", cursor)
|
||||
}
|
||||
}
|
||||
}
|
17
internal/decoder/option.go
Normal file
17
internal/decoder/option.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package decoder
|
||||
|
||||
import "context"
|
||||
|
||||
type OptionFlags uint8
|
||||
|
||||
const (
|
||||
FirstWinOption OptionFlags = 1 << iota
|
||||
ContextOption
|
||||
PathOption
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
Flags OptionFlags
|
||||
Context context.Context
|
||||
Path *Path
|
||||
}
|
670
internal/decoder/path.go
Normal file
670
internal/decoder/path.go
Normal file
|
@ -0,0 +1,670 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type PathString string
|
||||
|
||||
func (s PathString) Build() (*Path, error) {
|
||||
builder := new(PathBuilder)
|
||||
return builder.Build([]rune(s))
|
||||
}
|
||||
|
||||
type PathBuilder struct {
|
||||
root PathNode
|
||||
node PathNode
|
||||
singleQuotePathSelector bool
|
||||
doubleQuotePathSelector bool
|
||||
}
|
||||
|
||||
func (b *PathBuilder) Build(buf []rune) (*Path, error) {
|
||||
node, err := b.build(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Path{
|
||||
node: node,
|
||||
RootSelectorOnly: node == nil,
|
||||
SingleQuotePathSelector: b.singleQuotePathSelector,
|
||||
DoubleQuotePathSelector: b.doubleQuotePathSelector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *PathBuilder) build(buf []rune) (PathNode, error) {
|
||||
if len(buf) == 0 {
|
||||
return nil, errors.ErrEmptyPath()
|
||||
}
|
||||
if buf[0] != '$' {
|
||||
return nil, errors.ErrInvalidPath("JSON Path must start with a $ character")
|
||||
}
|
||||
if len(buf) == 1 {
|
||||
return nil, nil
|
||||
}
|
||||
buf = buf[1:]
|
||||
offset, err := b.buildNext(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(buf) > offset {
|
||||
return nil, errors.ErrInvalidPath("remain invalid path %q", buf[offset:])
|
||||
}
|
||||
return b.root, nil
|
||||
}
|
||||
|
||||
func (b *PathBuilder) buildNextCharIfExists(buf []rune, cursor int) (int, error) {
|
||||
if len(buf) > cursor {
|
||||
offset, err := b.buildNext(buf[cursor:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return cursor + 1 + offset, nil
|
||||
}
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (b *PathBuilder) buildNext(buf []rune) (int, error) {
|
||||
switch buf[0] {
|
||||
case '.':
|
||||
if len(buf) == 1 {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
|
||||
}
|
||||
offset, err := b.buildSelector(buf[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return offset + 1, nil
|
||||
case '[':
|
||||
if len(buf) == 1 {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
|
||||
}
|
||||
offset, err := b.buildIndex(buf[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return offset + 1, nil
|
||||
default:
|
||||
return 0, errors.ErrInvalidPath("expect dot or left bracket character. but found %c character", buf[0])
|
||||
}
|
||||
}
|
||||
|
||||
func (b *PathBuilder) buildSelector(buf []rune) (int, error) {
|
||||
switch buf[0] {
|
||||
case '.':
|
||||
if len(buf) == 1 {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with double dot character")
|
||||
}
|
||||
offset, err := b.buildPathRecursive(buf[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 1 + offset, nil
|
||||
case '[', ']', '$', '*':
|
||||
return 0, errors.ErrInvalidPath("found invalid path character %c after dot", buf[0])
|
||||
}
|
||||
for cursor := 0; cursor < len(buf); cursor++ {
|
||||
switch buf[cursor] {
|
||||
case '$', '*', ']':
|
||||
return 0, errors.ErrInvalidPath("found %c character in field selector context", buf[cursor])
|
||||
case '.':
|
||||
if cursor+1 >= len(buf) {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
|
||||
}
|
||||
selector := buf[:cursor]
|
||||
b.addSelectorNode(string(selector))
|
||||
offset, err := b.buildSelector(buf[cursor+1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return cursor + 1 + offset, nil
|
||||
case '[':
|
||||
if cursor+1 >= len(buf) {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
|
||||
}
|
||||
selector := buf[:cursor]
|
||||
b.addSelectorNode(string(selector))
|
||||
offset, err := b.buildIndex(buf[cursor+1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return cursor + 1 + offset, nil
|
||||
case '"':
|
||||
if cursor+1 >= len(buf) {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with double quote character")
|
||||
}
|
||||
offset, err := b.buildQuoteSelector(buf[cursor+1:], DoubleQuotePathSelector)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return cursor + 1 + offset, nil
|
||||
}
|
||||
}
|
||||
b.addSelectorNode(string(buf))
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (b *PathBuilder) buildQuoteSelector(buf []rune, sel QuotePathSelector) (int, error) {
|
||||
switch buf[0] {
|
||||
case '[', ']', '$', '.', '*', '\'', '"':
|
||||
return 0, errors.ErrInvalidPath("found invalid path character %c after quote", buf[0])
|
||||
}
|
||||
for cursor := 0; cursor < len(buf); cursor++ {
|
||||
switch buf[cursor] {
|
||||
case '\'':
|
||||
if sel != SingleQuotePathSelector {
|
||||
return 0, errors.ErrInvalidPath("found double quote character in field selector with single quote context")
|
||||
}
|
||||
if len(buf) <= cursor+1 {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with single quote character in field selector context")
|
||||
}
|
||||
if buf[cursor+1] != ']' {
|
||||
return 0, errors.ErrInvalidPath("expect right bracket for field selector with single quote but found %c", buf[cursor+1])
|
||||
}
|
||||
selector := buf[:cursor]
|
||||
b.addSelectorNode(string(selector))
|
||||
b.singleQuotePathSelector = true
|
||||
return b.buildNextCharIfExists(buf, cursor+2)
|
||||
case '"':
|
||||
if sel != DoubleQuotePathSelector {
|
||||
return 0, errors.ErrInvalidPath("found single quote character in field selector with double quote context")
|
||||
}
|
||||
selector := buf[:cursor]
|
||||
b.addSelectorNode(string(selector))
|
||||
b.doubleQuotePathSelector = true
|
||||
return b.buildNextCharIfExists(buf, cursor+1)
|
||||
}
|
||||
}
|
||||
return 0, errors.ErrInvalidPath("couldn't find quote character in selector quote path context")
|
||||
}
|
||||
|
||||
func (b *PathBuilder) buildPathRecursive(buf []rune) (int, error) {
|
||||
switch buf[0] {
|
||||
case '.', '[', ']', '$', '*':
|
||||
return 0, errors.ErrInvalidPath("found invalid path character %c after double dot", buf[0])
|
||||
}
|
||||
for cursor := 0; cursor < len(buf); cursor++ {
|
||||
switch buf[cursor] {
|
||||
case '$', '*', ']':
|
||||
return 0, errors.ErrInvalidPath("found %c character in field selector context", buf[cursor])
|
||||
case '.':
|
||||
if cursor+1 >= len(buf) {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
|
||||
}
|
||||
selector := buf[:cursor]
|
||||
b.addRecursiveNode(string(selector))
|
||||
offset, err := b.buildSelector(buf[cursor+1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return cursor + 1 + offset, nil
|
||||
case '[':
|
||||
if cursor+1 >= len(buf) {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
|
||||
}
|
||||
selector := buf[:cursor]
|
||||
b.addRecursiveNode(string(selector))
|
||||
offset, err := b.buildIndex(buf[cursor+1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return cursor + 1 + offset, nil
|
||||
}
|
||||
}
|
||||
b.addRecursiveNode(string(buf))
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (b *PathBuilder) buildIndex(buf []rune) (int, error) {
|
||||
switch buf[0] {
|
||||
case '.', '[', ']', '$':
|
||||
return 0, errors.ErrInvalidPath("found invalid path character %c after left bracket", buf[0])
|
||||
case '\'':
|
||||
if len(buf) == 1 {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with single quote character")
|
||||
}
|
||||
offset, err := b.buildQuoteSelector(buf[1:], SingleQuotePathSelector)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return 1 + offset, nil
|
||||
case '*':
|
||||
if len(buf) == 1 {
|
||||
return 0, errors.ErrInvalidPath("JSON Path ends with star character")
|
||||
}
|
||||
if buf[1] != ']' {
|
||||
return 0, errors.ErrInvalidPath("expect right bracket character for index all path but found %c character", buf[1])
|
||||
}
|
||||
b.addIndexAllNode()
|
||||
offset := len("*]")
|
||||
if len(buf) > 2 {
|
||||
buildOffset, err := b.buildNext(buf[2:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return offset + buildOffset, nil
|
||||
}
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
for cursor := 0; cursor < len(buf); cursor++ {
|
||||
switch buf[cursor] {
|
||||
case ']':
|
||||
index, err := strconv.ParseInt(string(buf[:cursor]), 10, 64)
|
||||
if err != nil {
|
||||
return 0, errors.ErrInvalidPath("%q is unexpected index path", buf[:cursor])
|
||||
}
|
||||
b.addIndexNode(int(index))
|
||||
return b.buildNextCharIfExists(buf, cursor+1)
|
||||
}
|
||||
}
|
||||
return 0, errors.ErrInvalidPath("couldn't find right bracket character in index path context")
|
||||
}
|
||||
|
||||
func (b *PathBuilder) addIndexAllNode() {
|
||||
node := newPathIndexAllNode()
|
||||
if b.root == nil {
|
||||
b.root = node
|
||||
b.node = node
|
||||
} else {
|
||||
b.node = b.node.chain(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *PathBuilder) addRecursiveNode(selector string) {
|
||||
node := newPathRecursiveNode(selector)
|
||||
if b.root == nil {
|
||||
b.root = node
|
||||
b.node = node
|
||||
} else {
|
||||
b.node = b.node.chain(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *PathBuilder) addSelectorNode(name string) {
|
||||
node := newPathSelectorNode(name)
|
||||
if b.root == nil {
|
||||
b.root = node
|
||||
b.node = node
|
||||
} else {
|
||||
b.node = b.node.chain(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *PathBuilder) addIndexNode(idx int) {
|
||||
node := newPathIndexNode(idx)
|
||||
if b.root == nil {
|
||||
b.root = node
|
||||
b.node = node
|
||||
} else {
|
||||
b.node = b.node.chain(node)
|
||||
}
|
||||
}
|
||||
|
||||
type QuotePathSelector int
|
||||
|
||||
const (
|
||||
SingleQuotePathSelector QuotePathSelector = 1
|
||||
DoubleQuotePathSelector QuotePathSelector = 2
|
||||
)
|
||||
|
||||
type Path struct {
|
||||
node PathNode
|
||||
RootSelectorOnly bool
|
||||
SingleQuotePathSelector bool
|
||||
DoubleQuotePathSelector bool
|
||||
}
|
||||
|
||||
func (p *Path) Field(sel string) (PathNode, bool, error) {
|
||||
if p.node == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
return p.node.Field(sel)
|
||||
}
|
||||
|
||||
func (p *Path) Get(src, dst reflect.Value) error {
|
||||
if p.node == nil {
|
||||
return nil
|
||||
}
|
||||
return p.node.Get(src, dst)
|
||||
}
|
||||
|
||||
func (p *Path) String() string {
|
||||
if p.node == nil {
|
||||
return "$"
|
||||
}
|
||||
return p.node.String()
|
||||
}
|
||||
|
||||
type PathNode interface {
|
||||
fmt.Stringer
|
||||
Index(idx int) (PathNode, bool, error)
|
||||
Field(fieldName string) (PathNode, bool, error)
|
||||
Get(src, dst reflect.Value) error
|
||||
chain(PathNode) PathNode
|
||||
target() bool
|
||||
single() bool
|
||||
}
|
||||
|
||||
type BasePathNode struct {
|
||||
child PathNode
|
||||
}
|
||||
|
||||
func (n *BasePathNode) chain(node PathNode) PathNode {
|
||||
n.child = node
|
||||
return node
|
||||
}
|
||||
|
||||
func (n *BasePathNode) target() bool {
|
||||
return n.child == nil
|
||||
}
|
||||
|
||||
func (n *BasePathNode) single() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type PathSelectorNode struct {
|
||||
*BasePathNode
|
||||
selector string
|
||||
}
|
||||
|
||||
func newPathSelectorNode(selector string) *PathSelectorNode {
|
||||
return &PathSelectorNode{
|
||||
BasePathNode: &BasePathNode{},
|
||||
selector: selector,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *PathSelectorNode) Index(idx int) (PathNode, bool, error) {
|
||||
return nil, false, &errors.PathError{}
|
||||
}
|
||||
|
||||
func (n *PathSelectorNode) Field(fieldName string) (PathNode, bool, error) {
|
||||
if n.selector == fieldName {
|
||||
return n.child, true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (n *PathSelectorNode) Get(src, dst reflect.Value) error {
|
||||
switch src.Type().Kind() {
|
||||
case reflect.Map:
|
||||
iter := src.MapRange()
|
||||
for iter.Next() {
|
||||
key, ok := iter.Key().Interface().(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid map key type %T", src.Type().Key())
|
||||
}
|
||||
child, found, err := n.Field(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
if child != nil {
|
||||
return child.Get(iter.Value(), dst)
|
||||
}
|
||||
return AssignValue(iter.Value(), dst)
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
typ := src.Type()
|
||||
for i := 0; i < typ.Len(); i++ {
|
||||
tag := runtime.StructTagFromField(typ.Field(i))
|
||||
child, found, err := n.Field(tag.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
if child != nil {
|
||||
return child.Get(src.Field(i), dst)
|
||||
}
|
||||
return AssignValue(src.Field(i), dst)
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
return n.Get(src.Elem(), dst)
|
||||
case reflect.Interface:
|
||||
return n.Get(reflect.ValueOf(src.Interface()), dst)
|
||||
case reflect.Float64, reflect.String, reflect.Bool:
|
||||
return AssignValue(src, dst)
|
||||
}
|
||||
return fmt.Errorf("failed to get %s value from %s", n.selector, src.Type())
|
||||
}
|
||||
|
||||
func (n *PathSelectorNode) String() string {
|
||||
s := fmt.Sprintf(".%s", n.selector)
|
||||
if n.child != nil {
|
||||
s += n.child.String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type PathIndexNode struct {
|
||||
*BasePathNode
|
||||
selector int
|
||||
}
|
||||
|
||||
func newPathIndexNode(selector int) *PathIndexNode {
|
||||
return &PathIndexNode{
|
||||
BasePathNode: &BasePathNode{},
|
||||
selector: selector,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *PathIndexNode) Index(idx int) (PathNode, bool, error) {
|
||||
if n.selector == idx {
|
||||
return n.child, true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (n *PathIndexNode) Field(fieldName string) (PathNode, bool, error) {
|
||||
return nil, false, &errors.PathError{}
|
||||
}
|
||||
|
||||
func (n *PathIndexNode) Get(src, dst reflect.Value) error {
|
||||
switch src.Type().Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if src.Len() > n.selector {
|
||||
if n.child != nil {
|
||||
return n.child.Get(src.Index(n.selector), dst)
|
||||
}
|
||||
return AssignValue(src.Index(n.selector), dst)
|
||||
}
|
||||
case reflect.Ptr:
|
||||
return n.Get(src.Elem(), dst)
|
||||
case reflect.Interface:
|
||||
return n.Get(reflect.ValueOf(src.Interface()), dst)
|
||||
}
|
||||
return fmt.Errorf("failed to get [%d] value from %s", n.selector, src.Type())
|
||||
}
|
||||
|
||||
func (n *PathIndexNode) String() string {
|
||||
s := fmt.Sprintf("[%d]", n.selector)
|
||||
if n.child != nil {
|
||||
s += n.child.String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type PathIndexAllNode struct {
|
||||
*BasePathNode
|
||||
}
|
||||
|
||||
func newPathIndexAllNode() *PathIndexAllNode {
|
||||
return &PathIndexAllNode{
|
||||
BasePathNode: &BasePathNode{},
|
||||
}
|
||||
}
|
||||
|
||||
func (n *PathIndexAllNode) Index(idx int) (PathNode, bool, error) {
|
||||
return n.child, true, nil
|
||||
}
|
||||
|
||||
func (n *PathIndexAllNode) Field(fieldName string) (PathNode, bool, error) {
|
||||
return nil, false, &errors.PathError{}
|
||||
}
|
||||
|
||||
func (n *PathIndexAllNode) Get(src, dst reflect.Value) error {
|
||||
switch src.Type().Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
var arr []interface{}
|
||||
for i := 0; i < src.Len(); i++ {
|
||||
var v interface{}
|
||||
rv := reflect.ValueOf(&v)
|
||||
if n.child != nil {
|
||||
if err := n.child.Get(src.Index(i), rv); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := AssignValue(src.Index(i), rv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
arr = append(arr, v)
|
||||
}
|
||||
if err := AssignValue(reflect.ValueOf(arr), dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case reflect.Ptr:
|
||||
return n.Get(src.Elem(), dst)
|
||||
case reflect.Interface:
|
||||
return n.Get(reflect.ValueOf(src.Interface()), dst)
|
||||
}
|
||||
return fmt.Errorf("failed to get all value from %s", src.Type())
|
||||
}
|
||||
|
||||
func (n *PathIndexAllNode) String() string {
|
||||
s := "[*]"
|
||||
if n.child != nil {
|
||||
s += n.child.String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type PathRecursiveNode struct {
|
||||
*BasePathNode
|
||||
selector string
|
||||
}
|
||||
|
||||
func newPathRecursiveNode(selector string) *PathRecursiveNode {
|
||||
node := newPathSelectorNode(selector)
|
||||
return &PathRecursiveNode{
|
||||
BasePathNode: &BasePathNode{
|
||||
child: node,
|
||||
},
|
||||
selector: selector,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *PathRecursiveNode) Field(fieldName string) (PathNode, bool, error) {
|
||||
if n.selector == fieldName {
|
||||
return n.child, true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (n *PathRecursiveNode) Index(_ int) (PathNode, bool, error) {
|
||||
return n, true, nil
|
||||
}
|
||||
|
||||
func valueToSliceValue(v interface{}) []interface{} {
|
||||
rv := reflect.ValueOf(v)
|
||||
ret := []interface{}{}
|
||||
if rv.Type().Kind() == reflect.Slice || rv.Type().Kind() == reflect.Array {
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
ret = append(ret, rv.Index(i).Interface())
|
||||
}
|
||||
return ret
|
||||
}
|
||||
return []interface{}{v}
|
||||
}
|
||||
|
||||
func (n *PathRecursiveNode) Get(src, dst reflect.Value) error {
|
||||
if n.child == nil {
|
||||
return fmt.Errorf("failed to get by recursive path ..%s", n.selector)
|
||||
}
|
||||
var arr []interface{}
|
||||
switch src.Type().Kind() {
|
||||
case reflect.Map:
|
||||
iter := src.MapRange()
|
||||
for iter.Next() {
|
||||
key, ok := iter.Key().Interface().(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid map key type %T", src.Type().Key())
|
||||
}
|
||||
child, found, err := n.Field(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
var v interface{}
|
||||
rv := reflect.ValueOf(&v)
|
||||
_ = child.Get(iter.Value(), rv)
|
||||
arr = append(arr, valueToSliceValue(v)...)
|
||||
} else {
|
||||
var v interface{}
|
||||
rv := reflect.ValueOf(&v)
|
||||
_ = n.Get(iter.Value(), rv)
|
||||
if v != nil {
|
||||
arr = append(arr, valueToSliceValue(v)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = AssignValue(reflect.ValueOf(arr), dst)
|
||||
return nil
|
||||
case reflect.Struct:
|
||||
typ := src.Type()
|
||||
for i := 0; i < typ.Len(); i++ {
|
||||
tag := runtime.StructTagFromField(typ.Field(i))
|
||||
child, found, err := n.Field(tag.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
var v interface{}
|
||||
rv := reflect.ValueOf(&v)
|
||||
_ = child.Get(src.Field(i), rv)
|
||||
arr = append(arr, valueToSliceValue(v)...)
|
||||
} else {
|
||||
var v interface{}
|
||||
rv := reflect.ValueOf(&v)
|
||||
_ = n.Get(src.Field(i), rv)
|
||||
if v != nil {
|
||||
arr = append(arr, valueToSliceValue(v)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = AssignValue(reflect.ValueOf(arr), dst)
|
||||
return nil
|
||||
case reflect.Array, reflect.Slice:
|
||||
for i := 0; i < src.Len(); i++ {
|
||||
var v interface{}
|
||||
rv := reflect.ValueOf(&v)
|
||||
_ = n.Get(src.Index(i), rv)
|
||||
if v != nil {
|
||||
arr = append(arr, valueToSliceValue(v)...)
|
||||
}
|
||||
}
|
||||
_ = AssignValue(reflect.ValueOf(arr), dst)
|
||||
return nil
|
||||
case reflect.Ptr:
|
||||
return n.Get(src.Elem(), dst)
|
||||
case reflect.Interface:
|
||||
return n.Get(reflect.ValueOf(src.Interface()), dst)
|
||||
}
|
||||
return fmt.Errorf("failed to get %s value from %s", n.selector, src.Type())
|
||||
}
|
||||
|
||||
func (n *PathRecursiveNode) String() string {
|
||||
s := fmt.Sprintf("..%s", n.selector)
|
||||
if n.child != nil {
|
||||
s += n.child.String()
|
||||
}
|
||||
return s
|
||||
}
|
97
internal/decoder/ptr.go
Normal file
97
internal/decoder/ptr.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type ptrDecoder struct {
|
||||
dec Decoder
|
||||
typ *runtime.Type
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newPtrDecoder(dec Decoder, typ *runtime.Type, structName, fieldName string) *ptrDecoder {
|
||||
return &ptrDecoder{
|
||||
dec: dec,
|
||||
typ: typ,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *ptrDecoder) contentDecoder() Decoder {
|
||||
dec, ok := d.dec.(*ptrDecoder)
|
||||
if !ok {
|
||||
return d.dec
|
||||
}
|
||||
return dec.contentDecoder()
|
||||
}
|
||||
|
||||
//nolint:golint
|
||||
//go:linkname unsafe_New reflect.unsafe_New
|
||||
func unsafe_New(*runtime.Type) unsafe.Pointer
|
||||
|
||||
func UnsafeNew(t *runtime.Type) unsafe.Pointer {
|
||||
return unsafe_New(t)
|
||||
}
|
||||
|
||||
func (d *ptrDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
if s.skipWhiteSpace() == nul {
|
||||
s.read()
|
||||
}
|
||||
if s.char() == 'n' {
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return nil
|
||||
}
|
||||
var newptr unsafe.Pointer
|
||||
if *(*unsafe.Pointer)(p) == nil {
|
||||
newptr = unsafe_New(d.typ)
|
||||
*(*unsafe.Pointer)(p) = newptr
|
||||
} else {
|
||||
newptr = *(*unsafe.Pointer)(p)
|
||||
}
|
||||
if err := d.dec.DecodeStream(s, depth, newptr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *ptrDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == 'n' {
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if p != nil {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
}
|
||||
cursor += 4
|
||||
return cursor, nil
|
||||
}
|
||||
var newptr unsafe.Pointer
|
||||
if *(*unsafe.Pointer)(p) == nil {
|
||||
newptr = unsafe_New(d.typ)
|
||||
*(*unsafe.Pointer)(p) = newptr
|
||||
} else {
|
||||
newptr = *(*unsafe.Pointer)(p)
|
||||
}
|
||||
c, err := d.dec.Decode(ctx, cursor, depth, newptr)
|
||||
if err != nil {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return 0, err
|
||||
}
|
||||
cursor = c
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (d *ptrDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: ptr decoder does not support decode path")
|
||||
}
|
380
internal/decoder/slice.go
Normal file
380
internal/decoder/slice.go
Normal file
|
@ -0,0 +1,380 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
sliceType = runtime.Type2RType(
|
||||
reflect.TypeOf((*sliceHeader)(nil)).Elem(),
|
||||
)
|
||||
nilSlice = unsafe.Pointer(&sliceHeader{})
|
||||
)
|
||||
|
||||
type sliceDecoder struct {
|
||||
elemType *runtime.Type
|
||||
isElemPointerType bool
|
||||
valueDecoder Decoder
|
||||
size uintptr
|
||||
arrayPool sync.Pool
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
// If use reflect.SliceHeader, data type is uintptr.
|
||||
// In this case, Go compiler cannot trace reference created by newArray().
|
||||
// So, define using unsafe.Pointer as data type
|
||||
type sliceHeader struct {
|
||||
data unsafe.Pointer
|
||||
len int
|
||||
cap int
|
||||
}
|
||||
|
||||
const (
|
||||
defaultSliceCapacity = 2
|
||||
)
|
||||
|
||||
func newSliceDecoder(dec Decoder, elemType *runtime.Type, size uintptr, structName, fieldName string) *sliceDecoder {
|
||||
return &sliceDecoder{
|
||||
valueDecoder: dec,
|
||||
elemType: elemType,
|
||||
isElemPointerType: elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map,
|
||||
size: size,
|
||||
arrayPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &sliceHeader{
|
||||
data: newArray(elemType, defaultSliceCapacity),
|
||||
len: 0,
|
||||
cap: defaultSliceCapacity,
|
||||
}
|
||||
},
|
||||
},
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *sliceDecoder) newSlice(src *sliceHeader) *sliceHeader {
|
||||
slice := d.arrayPool.Get().(*sliceHeader)
|
||||
if src.len > 0 {
|
||||
// copy original elem
|
||||
if slice.cap < src.cap {
|
||||
data := newArray(d.elemType, src.cap)
|
||||
slice = &sliceHeader{data: data, len: src.len, cap: src.cap}
|
||||
} else {
|
||||
slice.len = src.len
|
||||
}
|
||||
copySlice(d.elemType, *slice, *src)
|
||||
} else {
|
||||
slice.len = 0
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
func (d *sliceDecoder) releaseSlice(p *sliceHeader) {
|
||||
d.arrayPool.Put(p)
|
||||
}
|
||||
|
||||
//go:linkname copySlice reflect.typedslicecopy
|
||||
func copySlice(elemType *runtime.Type, dst, src sliceHeader) int
|
||||
|
||||
//go:linkname newArray reflect.unsafe_NewArray
|
||||
func newArray(*runtime.Type, int) unsafe.Pointer
|
||||
|
||||
//go:linkname typedmemmove reflect.typedmemmove
|
||||
func typedmemmove(t *runtime.Type, dst, src unsafe.Pointer)
|
||||
|
||||
func (d *sliceDecoder) errNumber(offset int64) *errors.UnmarshalTypeError {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "number",
|
||||
Type: reflect.SliceOf(runtime.RType2Type(d.elemType)),
|
||||
Struct: d.structName,
|
||||
Field: d.fieldName,
|
||||
Offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *sliceDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
|
||||
}
|
||||
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
continue
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
typedmemmove(sliceType, p, nilSlice)
|
||||
return nil
|
||||
case '[':
|
||||
s.cursor++
|
||||
if s.skipWhiteSpace() == ']' {
|
||||
dst := (*sliceHeader)(p)
|
||||
if dst.data == nil {
|
||||
dst.data = newArray(d.elemType, 0)
|
||||
} else {
|
||||
dst.len = 0
|
||||
}
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
idx := 0
|
||||
slice := d.newSlice((*sliceHeader)(p))
|
||||
srcLen := slice.len
|
||||
capacity := slice.cap
|
||||
data := slice.data
|
||||
for {
|
||||
if capacity <= idx {
|
||||
src := sliceHeader{data: data, len: idx, cap: capacity}
|
||||
capacity *= 2
|
||||
data = newArray(d.elemType, capacity)
|
||||
dst := sliceHeader{data: data, len: idx, cap: capacity}
|
||||
copySlice(d.elemType, dst, src)
|
||||
}
|
||||
ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size)
|
||||
|
||||
// if srcLen is greater than idx, keep the original reference
|
||||
if srcLen <= idx {
|
||||
if d.isElemPointerType {
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer
|
||||
} else {
|
||||
// assign new element to the slice
|
||||
typedmemmove(d.elemType, ep, unsafe_New(d.elemType))
|
||||
}
|
||||
}
|
||||
|
||||
if err := d.valueDecoder.DecodeStream(s, depth, ep); err != nil {
|
||||
return err
|
||||
}
|
||||
s.skipWhiteSpace()
|
||||
RETRY:
|
||||
switch s.char() {
|
||||
case ']':
|
||||
slice.cap = capacity
|
||||
slice.len = idx + 1
|
||||
slice.data = data
|
||||
dst := (*sliceHeader)(p)
|
||||
dst.len = idx + 1
|
||||
if dst.len > dst.cap {
|
||||
dst.data = newArray(d.elemType, dst.len)
|
||||
dst.cap = dst.len
|
||||
}
|
||||
copySlice(d.elemType, *dst, *slice)
|
||||
d.releaseSlice(slice)
|
||||
s.cursor++
|
||||
return nil
|
||||
case ',':
|
||||
idx++
|
||||
case nul:
|
||||
if s.read() {
|
||||
goto RETRY
|
||||
}
|
||||
slice.cap = capacity
|
||||
slice.data = data
|
||||
d.releaseSlice(slice)
|
||||
goto ERROR
|
||||
default:
|
||||
slice.cap = capacity
|
||||
slice.data = data
|
||||
d.releaseSlice(slice)
|
||||
goto ERROR
|
||||
}
|
||||
s.cursor++
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return d.errNumber(s.totalOffset())
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
default:
|
||||
goto ERROR
|
||||
}
|
||||
}
|
||||
ERROR:
|
||||
return errors.ErrUnexpectedEndOfJSON("slice", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *sliceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
typedmemmove(sliceType, p, nilSlice)
|
||||
return cursor, nil
|
||||
case '[':
|
||||
cursor++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == ']' {
|
||||
dst := (*sliceHeader)(p)
|
||||
if dst.data == nil {
|
||||
dst.data = newArray(d.elemType, 0)
|
||||
} else {
|
||||
dst.len = 0
|
||||
}
|
||||
cursor++
|
||||
return cursor, nil
|
||||
}
|
||||
idx := 0
|
||||
slice := d.newSlice((*sliceHeader)(p))
|
||||
srcLen := slice.len
|
||||
capacity := slice.cap
|
||||
data := slice.data
|
||||
for {
|
||||
if capacity <= idx {
|
||||
src := sliceHeader{data: data, len: idx, cap: capacity}
|
||||
capacity *= 2
|
||||
data = newArray(d.elemType, capacity)
|
||||
dst := sliceHeader{data: data, len: idx, cap: capacity}
|
||||
copySlice(d.elemType, dst, src)
|
||||
}
|
||||
ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size)
|
||||
// if srcLen is greater than idx, keep the original reference
|
||||
if srcLen <= idx {
|
||||
if d.isElemPointerType {
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer
|
||||
} else {
|
||||
// assign new element to the slice
|
||||
typedmemmove(d.elemType, ep, unsafe_New(d.elemType))
|
||||
}
|
||||
}
|
||||
c, err := d.valueDecoder.Decode(ctx, cursor, depth, ep)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = c
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
switch buf[cursor] {
|
||||
case ']':
|
||||
slice.cap = capacity
|
||||
slice.len = idx + 1
|
||||
slice.data = data
|
||||
dst := (*sliceHeader)(p)
|
||||
dst.len = idx + 1
|
||||
if dst.len > dst.cap {
|
||||
dst.data = newArray(d.elemType, dst.len)
|
||||
dst.cap = dst.len
|
||||
}
|
||||
copySlice(d.elemType, *dst, *slice)
|
||||
d.releaseSlice(slice)
|
||||
cursor++
|
||||
return cursor, nil
|
||||
case ',':
|
||||
idx++
|
||||
default:
|
||||
slice.cap = capacity
|
||||
slice.data = data
|
||||
d.releaseSlice(slice)
|
||||
return 0, errors.ErrInvalidCharacter(buf[cursor], "slice", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return 0, d.errNumber(cursor)
|
||||
default:
|
||||
return 0, errors.ErrUnexpectedEndOfJSON("slice", cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *sliceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
buf := ctx.Buf
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
ret := [][]byte{}
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return [][]byte{nullbytes}, cursor, nil
|
||||
case '[':
|
||||
cursor++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == ']' {
|
||||
cursor++
|
||||
return ret, cursor, nil
|
||||
}
|
||||
idx := 0
|
||||
for {
|
||||
child, found, err := ctx.Option.Path.node.Index(idx)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if found {
|
||||
if child != nil {
|
||||
oldPath := ctx.Option.Path.node
|
||||
ctx.Option.Path.node = child
|
||||
paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ctx.Option.Path.node = oldPath
|
||||
ret = append(ret, paths...)
|
||||
cursor = c
|
||||
} else {
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
ret = append(ret, buf[start:end])
|
||||
cursor = end
|
||||
}
|
||||
} else {
|
||||
c, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = c
|
||||
}
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
switch buf[cursor] {
|
||||
case ']':
|
||||
cursor++
|
||||
return ret, cursor, nil
|
||||
case ',':
|
||||
idx++
|
||||
default:
|
||||
return nil, 0, errors.ErrInvalidCharacter(buf[cursor], "slice", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return nil, 0, d.errNumber(cursor)
|
||||
default:
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("slice", cursor)
|
||||
}
|
||||
}
|
||||
}
|
556
internal/decoder/stream.go
Normal file
556
internal/decoder/stream.go
Normal file
|
@ -0,0 +1,556 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
initBufSize = 512
|
||||
)
|
||||
|
||||
type Stream struct {
|
||||
buf []byte
|
||||
bufSize int64
|
||||
length int64
|
||||
r io.Reader
|
||||
offset int64
|
||||
cursor int64
|
||||
filledBuffer bool
|
||||
allRead bool
|
||||
UseNumber bool
|
||||
DisallowUnknownFields bool
|
||||
Option *Option
|
||||
}
|
||||
|
||||
func NewStream(r io.Reader) *Stream {
|
||||
return &Stream{
|
||||
r: r,
|
||||
bufSize: initBufSize,
|
||||
buf: make([]byte, initBufSize),
|
||||
Option: &Option{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) TotalOffset() int64 {
|
||||
return s.totalOffset()
|
||||
}
|
||||
|
||||
func (s *Stream) Buffered() io.Reader {
|
||||
buflen := int64(len(s.buf))
|
||||
for i := s.cursor; i < buflen; i++ {
|
||||
if s.buf[i] == nul {
|
||||
return bytes.NewReader(s.buf[s.cursor:i])
|
||||
}
|
||||
}
|
||||
return bytes.NewReader(s.buf[s.cursor:])
|
||||
}
|
||||
|
||||
func (s *Stream) PrepareForDecode() error {
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\t', '\r', '\n':
|
||||
s.cursor++
|
||||
continue
|
||||
case ',', ':':
|
||||
s.cursor++
|
||||
return nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
return io.EOF
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stream) totalOffset() int64 {
|
||||
return s.offset + s.cursor
|
||||
}
|
||||
|
||||
func (s *Stream) char() byte {
|
||||
return s.buf[s.cursor]
|
||||
}
|
||||
|
||||
func (s *Stream) equalChar(c byte) bool {
|
||||
cur := s.buf[s.cursor]
|
||||
if cur == nul {
|
||||
s.read()
|
||||
cur = s.buf[s.cursor]
|
||||
}
|
||||
return cur == c
|
||||
}
|
||||
|
||||
func (s *Stream) stat() ([]byte, int64, unsafe.Pointer) {
|
||||
return s.buf, s.cursor, (*sliceHeader)(unsafe.Pointer(&s.buf)).data
|
||||
}
|
||||
|
||||
func (s *Stream) bufptr() unsafe.Pointer {
|
||||
return (*sliceHeader)(unsafe.Pointer(&s.buf)).data
|
||||
}
|
||||
|
||||
func (s *Stream) statForRetry() ([]byte, int64, unsafe.Pointer) {
|
||||
s.cursor-- // for retry ( because caller progress cursor position in each loop )
|
||||
return s.buf, s.cursor, (*sliceHeader)(unsafe.Pointer(&s.buf)).data
|
||||
}
|
||||
|
||||
func (s *Stream) Reset() {
|
||||
s.reset()
|
||||
s.bufSize = int64(len(s.buf))
|
||||
}
|
||||
|
||||
func (s *Stream) More() bool {
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\r', '\t':
|
||||
s.cursor++
|
||||
continue
|
||||
case '}', ']':
|
||||
return false
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
break
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Stream) Token() (interface{}, error) {
|
||||
for {
|
||||
c := s.char()
|
||||
switch c {
|
||||
case ' ', '\n', '\r', '\t':
|
||||
s.cursor++
|
||||
case '{', '[', ']', '}':
|
||||
s.cursor++
|
||||
return json.Delim(c), nil
|
||||
case ',', ':':
|
||||
s.cursor++
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
bytes := floatBytes(s)
|
||||
str := *(*string)(unsafe.Pointer(&bytes))
|
||||
if s.UseNumber {
|
||||
return json.Number(str), nil
|
||||
}
|
||||
f64, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f64, nil
|
||||
case '"':
|
||||
bytes, err := stringBytes(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
case 't':
|
||||
if err := trueBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, nil
|
||||
case 'f':
|
||||
if err := falseBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return false, nil
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
goto END
|
||||
default:
|
||||
return nil, errors.ErrInvalidCharacter(s.char(), "token", s.totalOffset())
|
||||
}
|
||||
}
|
||||
END:
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
||||
func (s *Stream) reset() {
|
||||
s.offset += s.cursor
|
||||
s.buf = s.buf[s.cursor:]
|
||||
s.length -= s.cursor
|
||||
s.cursor = 0
|
||||
}
|
||||
|
||||
func (s *Stream) readBuf() []byte {
|
||||
if s.filledBuffer {
|
||||
s.bufSize *= 2
|
||||
remainBuf := s.buf
|
||||
s.buf = make([]byte, s.bufSize)
|
||||
copy(s.buf, remainBuf)
|
||||
}
|
||||
remainLen := s.length - s.cursor
|
||||
remainNotNulCharNum := int64(0)
|
||||
for i := int64(0); i < remainLen; i++ {
|
||||
if s.buf[s.cursor+i] == nul {
|
||||
break
|
||||
}
|
||||
remainNotNulCharNum++
|
||||
}
|
||||
s.length = s.cursor + remainNotNulCharNum
|
||||
return s.buf[s.cursor+remainNotNulCharNum:]
|
||||
}
|
||||
|
||||
func (s *Stream) read() bool {
|
||||
if s.allRead {
|
||||
return false
|
||||
}
|
||||
buf := s.readBuf()
|
||||
last := len(buf) - 1
|
||||
buf[last] = nul
|
||||
n, err := s.r.Read(buf[:last])
|
||||
s.length += int64(n)
|
||||
if n == last {
|
||||
s.filledBuffer = true
|
||||
} else {
|
||||
s.filledBuffer = false
|
||||
}
|
||||
if err == io.EOF {
|
||||
s.allRead = true
|
||||
} else if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Stream) skipWhiteSpace() byte {
|
||||
p := s.bufptr()
|
||||
LOOP:
|
||||
c := char(p, s.cursor)
|
||||
switch c {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
goto LOOP
|
||||
case nul:
|
||||
if s.read() {
|
||||
p = s.bufptr()
|
||||
goto LOOP
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (s *Stream) skipObject(depth int64) error {
|
||||
braceCount := 1
|
||||
_, cursor, p := s.stat()
|
||||
for {
|
||||
switch char(p, cursor) {
|
||||
case '{':
|
||||
braceCount++
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
|
||||
}
|
||||
case '}':
|
||||
braceCount--
|
||||
depth--
|
||||
if braceCount == 0 {
|
||||
s.cursor = cursor + 1
|
||||
return nil
|
||||
}
|
||||
case '[':
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
|
||||
}
|
||||
case ']':
|
||||
depth--
|
||||
case '"':
|
||||
for {
|
||||
cursor++
|
||||
switch char(p, cursor) {
|
||||
case '\\':
|
||||
cursor++
|
||||
if char(p, cursor) == nul {
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
case '"':
|
||||
goto SWITCH_OUT
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.statForRetry()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
}
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("object of object", cursor)
|
||||
}
|
||||
SWITCH_OUT:
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) skipArray(depth int64) error {
|
||||
bracketCount := 1
|
||||
_, cursor, p := s.stat()
|
||||
for {
|
||||
switch char(p, cursor) {
|
||||
case '[':
|
||||
bracketCount++
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
|
||||
}
|
||||
case ']':
|
||||
bracketCount--
|
||||
depth--
|
||||
if bracketCount == 0 {
|
||||
s.cursor = cursor + 1
|
||||
return nil
|
||||
}
|
||||
case '{':
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
|
||||
}
|
||||
case '}':
|
||||
depth--
|
||||
case '"':
|
||||
for {
|
||||
cursor++
|
||||
switch char(p, cursor) {
|
||||
case '\\':
|
||||
cursor++
|
||||
if char(p, cursor) == nul {
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
case '"':
|
||||
goto SWITCH_OUT
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.statForRetry()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
||||
}
|
||||
}
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("array of object", cursor)
|
||||
}
|
||||
SWITCH_OUT:
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) skipValue(depth int64) error {
|
||||
_, cursor, p := s.stat()
|
||||
for {
|
||||
switch char(p, cursor) {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("value of object", s.totalOffset())
|
||||
case '{':
|
||||
s.cursor = cursor + 1
|
||||
return s.skipObject(depth + 1)
|
||||
case '[':
|
||||
s.cursor = cursor + 1
|
||||
return s.skipArray(depth + 1)
|
||||
case '"':
|
||||
for {
|
||||
cursor++
|
||||
switch char(p, cursor) {
|
||||
case '\\':
|
||||
cursor++
|
||||
if char(p, cursor) == nul {
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("value of string", s.totalOffset())
|
||||
}
|
||||
case '"':
|
||||
s.cursor = cursor + 1
|
||||
return nil
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.statForRetry()
|
||||
continue
|
||||
}
|
||||
return errors.ErrUnexpectedEndOfJSON("value of string", s.totalOffset())
|
||||
}
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
for {
|
||||
cursor++
|
||||
c := char(p, cursor)
|
||||
if floatTable[c] {
|
||||
continue
|
||||
} else if c == nul {
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
}
|
||||
s.cursor = cursor
|
||||
return nil
|
||||
}
|
||||
case 't':
|
||||
s.cursor = cursor
|
||||
if err := trueBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case 'f':
|
||||
s.cursor = cursor
|
||||
if err := falseBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case 'n':
|
||||
s.cursor = cursor
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func nullBytes(s *Stream) error {
|
||||
// current cursor's character is 'n'
|
||||
s.cursor++
|
||||
if s.char() != 'u' {
|
||||
if err := retryReadNull(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
if s.char() != 'l' {
|
||||
if err := retryReadNull(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
if s.char() != 'l' {
|
||||
if err := retryReadNull(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
|
||||
func retryReadNull(s *Stream) error {
|
||||
if s.char() == nul && s.read() {
|
||||
return nil
|
||||
}
|
||||
return errors.ErrInvalidCharacter(s.char(), "null", s.totalOffset())
|
||||
}
|
||||
|
||||
func trueBytes(s *Stream) error {
|
||||
// current cursor's character is 't'
|
||||
s.cursor++
|
||||
if s.char() != 'r' {
|
||||
if err := retryReadTrue(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
if s.char() != 'u' {
|
||||
if err := retryReadTrue(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
if s.char() != 'e' {
|
||||
if err := retryReadTrue(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
|
||||
func retryReadTrue(s *Stream) error {
|
||||
if s.char() == nul && s.read() {
|
||||
return nil
|
||||
}
|
||||
return errors.ErrInvalidCharacter(s.char(), "bool(true)", s.totalOffset())
|
||||
}
|
||||
|
||||
func falseBytes(s *Stream) error {
|
||||
// current cursor's character is 'f'
|
||||
s.cursor++
|
||||
if s.char() != 'a' {
|
||||
if err := retryReadFalse(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
if s.char() != 'l' {
|
||||
if err := retryReadFalse(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
if s.char() != 's' {
|
||||
if err := retryReadFalse(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
if s.char() != 'e' {
|
||||
if err := retryReadFalse(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
|
||||
func retryReadFalse(s *Stream) error {
|
||||
if s.char() == nul && s.read() {
|
||||
return nil
|
||||
}
|
||||
return errors.ErrInvalidCharacter(s.char(), "bool(false)", s.totalOffset())
|
||||
}
|
452
internal/decoder/string.go
Normal file
452
internal/decoder/string.go
Normal file
|
@ -0,0 +1,452 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
type stringDecoder struct {
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newStringDecoder(structName, fieldName string) *stringDecoder {
|
||||
return &stringDecoder{
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *stringDecoder) errUnmarshalType(typeName string, offset int64) *errors.UnmarshalTypeError {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: typeName,
|
||||
Type: reflect.TypeOf(""),
|
||||
Offset: offset,
|
||||
Struct: d.structName,
|
||||
Field: d.fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *stringDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
bytes, err := d.decodeStreamByte(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes == nil {
|
||||
return nil
|
||||
}
|
||||
**(**string)(unsafe.Pointer(&p)) = *(*string)(unsafe.Pointer(&bytes))
|
||||
s.reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *stringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
return c, nil
|
||||
}
|
||||
cursor = c
|
||||
**(**string)(unsafe.Pointer(&p)) = *(*string)(unsafe.Pointer(&bytes))
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (d *stringDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
return [][]byte{nullbytes}, c, nil
|
||||
}
|
||||
return [][]byte{bytes}, c, nil
|
||||
}
|
||||
|
||||
var (
|
||||
hexToInt = [256]int{
|
||||
'0': 0,
|
||||
'1': 1,
|
||||
'2': 2,
|
||||
'3': 3,
|
||||
'4': 4,
|
||||
'5': 5,
|
||||
'6': 6,
|
||||
'7': 7,
|
||||
'8': 8,
|
||||
'9': 9,
|
||||
'A': 10,
|
||||
'B': 11,
|
||||
'C': 12,
|
||||
'D': 13,
|
||||
'E': 14,
|
||||
'F': 15,
|
||||
'a': 10,
|
||||
'b': 11,
|
||||
'c': 12,
|
||||
'd': 13,
|
||||
'e': 14,
|
||||
'f': 15,
|
||||
}
|
||||
)
|
||||
|
||||
func unicodeToRune(code []byte) rune {
|
||||
var r rune
|
||||
for i := 0; i < len(code); i++ {
|
||||
r = r*16 + rune(hexToInt[code[i]])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func readAtLeast(s *Stream, n int64, p *unsafe.Pointer) bool {
|
||||
for s.cursor+n >= s.length {
|
||||
if !s.read() {
|
||||
return false
|
||||
}
|
||||
*p = s.bufptr()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func decodeUnicodeRune(s *Stream, p unsafe.Pointer) (rune, int64, unsafe.Pointer, error) {
|
||||
const defaultOffset = 5
|
||||
const surrogateOffset = 11
|
||||
|
||||
if !readAtLeast(s, defaultOffset, &p) {
|
||||
return rune(0), 0, nil, errors.ErrInvalidCharacter(s.char(), "escaped string", s.totalOffset())
|
||||
}
|
||||
|
||||
r := unicodeToRune(s.buf[s.cursor+1 : s.cursor+defaultOffset])
|
||||
if utf16.IsSurrogate(r) {
|
||||
if !readAtLeast(s, surrogateOffset, &p) {
|
||||
return unicode.ReplacementChar, defaultOffset, p, nil
|
||||
}
|
||||
if s.buf[s.cursor+defaultOffset] != '\\' || s.buf[s.cursor+defaultOffset+1] != 'u' {
|
||||
return unicode.ReplacementChar, defaultOffset, p, nil
|
||||
}
|
||||
r2 := unicodeToRune(s.buf[s.cursor+defaultOffset+2 : s.cursor+surrogateOffset])
|
||||
if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar {
|
||||
return r, surrogateOffset, p, nil
|
||||
}
|
||||
}
|
||||
return r, defaultOffset, p, nil
|
||||
}
|
||||
|
||||
func decodeUnicode(s *Stream, p unsafe.Pointer) (unsafe.Pointer, error) {
|
||||
const backSlashAndULen = 2 // length of \u
|
||||
|
||||
r, offset, pp, err := decodeUnicodeRune(s, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unicode := []byte(string(r))
|
||||
unicodeLen := int64(len(unicode))
|
||||
s.buf = append(append(s.buf[:s.cursor-1], unicode...), s.buf[s.cursor+offset:]...)
|
||||
unicodeOrgLen := offset - 1
|
||||
s.length = s.length - (backSlashAndULen + (unicodeOrgLen - unicodeLen))
|
||||
s.cursor = s.cursor - backSlashAndULen + unicodeLen
|
||||
return pp, nil
|
||||
}
|
||||
|
||||
func decodeEscapeString(s *Stream, p unsafe.Pointer) (unsafe.Pointer, error) {
|
||||
s.cursor++
|
||||
RETRY:
|
||||
switch s.buf[s.cursor] {
|
||||
case '"':
|
||||
s.buf[s.cursor] = '"'
|
||||
case '\\':
|
||||
s.buf[s.cursor] = '\\'
|
||||
case '/':
|
||||
s.buf[s.cursor] = '/'
|
||||
case 'b':
|
||||
s.buf[s.cursor] = '\b'
|
||||
case 'f':
|
||||
s.buf[s.cursor] = '\f'
|
||||
case 'n':
|
||||
s.buf[s.cursor] = '\n'
|
||||
case 'r':
|
||||
s.buf[s.cursor] = '\r'
|
||||
case 't':
|
||||
s.buf[s.cursor] = '\t'
|
||||
case 'u':
|
||||
return decodeUnicode(s, p)
|
||||
case nul:
|
||||
if !s.read() {
|
||||
return nil, errors.ErrInvalidCharacter(s.char(), "escaped string", s.totalOffset())
|
||||
}
|
||||
p = s.bufptr()
|
||||
goto RETRY
|
||||
default:
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
}
|
||||
s.buf = append(s.buf[:s.cursor-1], s.buf[s.cursor:]...)
|
||||
s.length--
|
||||
s.cursor--
|
||||
p = s.bufptr()
|
||||
return p, nil
|
||||
}
|
||||
|
||||
var (
|
||||
runeErrBytes = []byte(string(utf8.RuneError))
|
||||
runeErrBytesLen = int64(len(runeErrBytes))
|
||||
)
|
||||
|
||||
func stringBytes(s *Stream) ([]byte, error) {
|
||||
_, cursor, p := s.stat()
|
||||
cursor++ // skip double quote char
|
||||
start := cursor
|
||||
for {
|
||||
switch char(p, cursor) {
|
||||
case '\\':
|
||||
s.cursor = cursor
|
||||
pp, err := decodeEscapeString(s, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p = pp
|
||||
cursor = s.cursor
|
||||
case '"':
|
||||
literal := s.buf[start:cursor]
|
||||
cursor++
|
||||
s.cursor = cursor
|
||||
return literal, nil
|
||||
case
|
||||
// 0x00 is nul, 0x5c is '\\', 0x22 is '"' .
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 0x00-0x0F
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, // 0x10-0x1F
|
||||
0x20, 0x21 /*0x22,*/, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, // 0x20-0x2F
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, // 0x30-0x3F
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, // 0x40-0x4F
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B /*0x5C,*/, 0x5D, 0x5E, 0x5F, // 0x50-0x5F
|
||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 0x60-0x6F
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F: // 0x70-0x7F
|
||||
// character is ASCII. skip to next char
|
||||
case
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, // 0x80-0x8F
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, // 0x90-0x9F
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // 0xA0-0xAF
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, // 0xB0-0xBF
|
||||
0xC0, 0xC1, // 0xC0-0xC1
|
||||
0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF: // 0xF5-0xFE
|
||||
// character is invalid
|
||||
s.buf = append(append(append([]byte{}, s.buf[:cursor]...), runeErrBytes...), s.buf[cursor+1:]...)
|
||||
_, _, p = s.stat()
|
||||
cursor += runeErrBytesLen
|
||||
s.length += runeErrBytesLen
|
||||
continue
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
case 0xEF:
|
||||
// RuneError is {0xEF, 0xBF, 0xBD}
|
||||
if s.buf[cursor+1] == 0xBF && s.buf[cursor+2] == 0xBD {
|
||||
// found RuneError: skip
|
||||
cursor += 2
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
// multi bytes character
|
||||
if !utf8.FullRune(s.buf[cursor : len(s.buf)-1]) {
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
}
|
||||
r, size := utf8.DecodeRune(s.buf[cursor:])
|
||||
if r == utf8.RuneError {
|
||||
s.buf = append(append(append([]byte{}, s.buf[:cursor]...), runeErrBytes...), s.buf[cursor+1:]...)
|
||||
cursor += runeErrBytesLen
|
||||
s.length += runeErrBytesLen
|
||||
_, _, p = s.stat()
|
||||
} else {
|
||||
cursor += int64(size)
|
||||
}
|
||||
continue
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
ERROR:
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *stringDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
continue
|
||||
case '[':
|
||||
return nil, d.errUnmarshalType("array", s.totalOffset())
|
||||
case '{':
|
||||
return nil, d.errUnmarshalType("object", s.totalOffset())
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return nil, d.errUnmarshalType("number", s.totalOffset())
|
||||
case '"':
|
||||
return stringBytes(s)
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil, errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
case '[':
|
||||
return nil, 0, d.errUnmarshalType("array", cursor)
|
||||
case '{':
|
||||
return nil, 0, d.errUnmarshalType("object", cursor)
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return nil, 0, d.errUnmarshalType("number", cursor)
|
||||
case '"':
|
||||
cursor++
|
||||
start := cursor
|
||||
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
|
||||
escaped := 0
|
||||
for {
|
||||
switch char(b, cursor) {
|
||||
case '\\':
|
||||
escaped++
|
||||
cursor++
|
||||
switch char(b, cursor) {
|
||||
case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
|
||||
cursor++
|
||||
case 'u':
|
||||
buflen := int64(len(buf))
|
||||
if cursor+5 >= buflen {
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
|
||||
}
|
||||
for i := int64(1); i <= 4; i++ {
|
||||
c := char(b, cursor+i)
|
||||
if !(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')) {
|
||||
return nil, 0, errors.ErrSyntax(fmt.Sprintf("json: invalid character %c in \\u hexadecimal character escape", c), cursor+i)
|
||||
}
|
||||
}
|
||||
cursor += 5
|
||||
default:
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
|
||||
}
|
||||
continue
|
||||
case '"':
|
||||
literal := buf[start:cursor]
|
||||
if escaped > 0 {
|
||||
literal = literal[:unescapeString(literal)]
|
||||
}
|
||||
cursor++
|
||||
return literal, cursor, nil
|
||||
case nul:
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("string", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return nil, cursor, nil
|
||||
default:
|
||||
return nil, 0, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unescapeMap = [256]byte{
|
||||
'"': '"',
|
||||
'\\': '\\',
|
||||
'/': '/',
|
||||
'b': '\b',
|
||||
'f': '\f',
|
||||
'n': '\n',
|
||||
'r': '\r',
|
||||
't': '\t',
|
||||
}
|
||||
|
||||
func unsafeAdd(ptr unsafe.Pointer, offset int) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(ptr) + uintptr(offset))
|
||||
}
|
||||
|
||||
func unescapeString(buf []byte) int {
|
||||
p := (*sliceHeader)(unsafe.Pointer(&buf)).data
|
||||
end := unsafeAdd(p, len(buf))
|
||||
src := unsafeAdd(p, bytes.IndexByte(buf, '\\'))
|
||||
dst := src
|
||||
for src != end {
|
||||
c := char(src, 0)
|
||||
if c == '\\' {
|
||||
escapeChar := char(src, 1)
|
||||
if escapeChar != 'u' {
|
||||
*(*byte)(dst) = unescapeMap[escapeChar]
|
||||
src = unsafeAdd(src, 2)
|
||||
dst = unsafeAdd(dst, 1)
|
||||
} else {
|
||||
v1 := hexToInt[char(src, 2)]
|
||||
v2 := hexToInt[char(src, 3)]
|
||||
v3 := hexToInt[char(src, 4)]
|
||||
v4 := hexToInt[char(src, 5)]
|
||||
code := rune((v1 << 12) | (v2 << 8) | (v3 << 4) | v4)
|
||||
if code >= 0xd800 && code < 0xdc00 && uintptr(unsafeAdd(src, 11)) < uintptr(end) {
|
||||
if char(src, 6) == '\\' && char(src, 7) == 'u' {
|
||||
v1 := hexToInt[char(src, 8)]
|
||||
v2 := hexToInt[char(src, 9)]
|
||||
v3 := hexToInt[char(src, 10)]
|
||||
v4 := hexToInt[char(src, 11)]
|
||||
lo := rune((v1 << 12) | (v2 << 8) | (v3 << 4) | v4)
|
||||
if lo >= 0xdc00 && lo < 0xe000 {
|
||||
code = (code-0xd800)<<10 | (lo - 0xdc00) + 0x10000
|
||||
src = unsafeAdd(src, 6)
|
||||
}
|
||||
}
|
||||
}
|
||||
var b [utf8.UTFMax]byte
|
||||
n := utf8.EncodeRune(b[:], code)
|
||||
switch n {
|
||||
case 4:
|
||||
*(*byte)(unsafeAdd(dst, 3)) = b[3]
|
||||
fallthrough
|
||||
case 3:
|
||||
*(*byte)(unsafeAdd(dst, 2)) = b[2]
|
||||
fallthrough
|
||||
case 2:
|
||||
*(*byte)(unsafeAdd(dst, 1)) = b[1]
|
||||
fallthrough
|
||||
case 1:
|
||||
*(*byte)(unsafeAdd(dst, 0)) = b[0]
|
||||
}
|
||||
src = unsafeAdd(src, 6)
|
||||
dst = unsafeAdd(dst, n)
|
||||
}
|
||||
} else {
|
||||
*(*byte)(dst) = c
|
||||
src = unsafeAdd(src, 1)
|
||||
dst = unsafeAdd(dst, 1)
|
||||
}
|
||||
}
|
||||
return int(uintptr(dst) - uintptr(p))
|
||||
}
|
845
internal/decoder/struct.go
Normal file
845
internal/decoder/struct.go
Normal file
|
@ -0,0 +1,845 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/bits"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
type structFieldSet struct {
|
||||
dec Decoder
|
||||
offset uintptr
|
||||
isTaggedKey bool
|
||||
fieldIdx int
|
||||
key string
|
||||
keyLen int64
|
||||
err error
|
||||
}
|
||||
|
||||
type structDecoder struct {
|
||||
fieldMap map[string]*structFieldSet
|
||||
fieldUniqueNameNum int
|
||||
stringDecoder *stringDecoder
|
||||
structName string
|
||||
fieldName string
|
||||
isTriedOptimize bool
|
||||
keyBitmapUint8 [][256]uint8
|
||||
keyBitmapUint16 [][256]uint16
|
||||
sortedFieldSets []*structFieldSet
|
||||
keyDecoder func(*structDecoder, []byte, int64) (int64, *structFieldSet, error)
|
||||
keyStreamDecoder func(*structDecoder, *Stream) (*structFieldSet, string, error)
|
||||
}
|
||||
|
||||
var (
|
||||
largeToSmallTable [256]byte
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 256; i++ {
|
||||
c := i
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
c += 'a' - 'A'
|
||||
}
|
||||
largeToSmallTable[i] = byte(c)
|
||||
}
|
||||
}
|
||||
|
||||
func toASCIILower(s string) string {
|
||||
b := []byte(s)
|
||||
for i := range b {
|
||||
b[i] = largeToSmallTable[b[i]]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func newStructDecoder(structName, fieldName string, fieldMap map[string]*structFieldSet) *structDecoder {
|
||||
return &structDecoder{
|
||||
fieldMap: fieldMap,
|
||||
stringDecoder: newStringDecoder(structName, fieldName),
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
keyDecoder: decodeKey,
|
||||
keyStreamDecoder: decodeKeyStream,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
allowOptimizeMaxKeyLen = 64
|
||||
allowOptimizeMaxFieldLen = 16
|
||||
)
|
||||
|
||||
func (d *structDecoder) tryOptimize() {
|
||||
fieldUniqueNameMap := map[string]int{}
|
||||
fieldIdx := -1
|
||||
for k, v := range d.fieldMap {
|
||||
lower := strings.ToLower(k)
|
||||
idx, exists := fieldUniqueNameMap[lower]
|
||||
if exists {
|
||||
v.fieldIdx = idx
|
||||
} else {
|
||||
fieldIdx++
|
||||
v.fieldIdx = fieldIdx
|
||||
}
|
||||
fieldUniqueNameMap[lower] = fieldIdx
|
||||
}
|
||||
d.fieldUniqueNameNum = len(fieldUniqueNameMap)
|
||||
|
||||
if d.isTriedOptimize {
|
||||
return
|
||||
}
|
||||
fieldMap := map[string]*structFieldSet{}
|
||||
conflicted := map[string]struct{}{}
|
||||
for k, v := range d.fieldMap {
|
||||
key := strings.ToLower(k)
|
||||
if key != k {
|
||||
if key != toASCIILower(k) {
|
||||
d.isTriedOptimize = true
|
||||
return
|
||||
}
|
||||
// already exists same key (e.g. Hello and HELLO has same lower case key
|
||||
if _, exists := conflicted[key]; exists {
|
||||
d.isTriedOptimize = true
|
||||
return
|
||||
}
|
||||
conflicted[key] = struct{}{}
|
||||
}
|
||||
if field, exists := fieldMap[key]; exists {
|
||||
if field != v {
|
||||
d.isTriedOptimize = true
|
||||
return
|
||||
}
|
||||
}
|
||||
fieldMap[key] = v
|
||||
}
|
||||
|
||||
if len(fieldMap) > allowOptimizeMaxFieldLen {
|
||||
d.isTriedOptimize = true
|
||||
return
|
||||
}
|
||||
|
||||
var maxKeyLen int
|
||||
sortedKeys := []string{}
|
||||
for key := range fieldMap {
|
||||
keyLen := len(key)
|
||||
if keyLen > allowOptimizeMaxKeyLen {
|
||||
d.isTriedOptimize = true
|
||||
return
|
||||
}
|
||||
if maxKeyLen < keyLen {
|
||||
maxKeyLen = keyLen
|
||||
}
|
||||
sortedKeys = append(sortedKeys, key)
|
||||
}
|
||||
sort.Strings(sortedKeys)
|
||||
|
||||
// By allocating one extra capacity than `maxKeyLen`,
|
||||
// it is possible to avoid the process of comparing the index of the key with the length of the bitmap each time.
|
||||
bitmapLen := maxKeyLen + 1
|
||||
if len(sortedKeys) <= 8 {
|
||||
keyBitmap := make([][256]uint8, bitmapLen)
|
||||
for i, key := range sortedKeys {
|
||||
for j := 0; j < len(key); j++ {
|
||||
c := key[j]
|
||||
keyBitmap[j][c] |= (1 << uint(i))
|
||||
}
|
||||
d.sortedFieldSets = append(d.sortedFieldSets, fieldMap[key])
|
||||
}
|
||||
d.keyBitmapUint8 = keyBitmap
|
||||
d.keyDecoder = decodeKeyByBitmapUint8
|
||||
d.keyStreamDecoder = decodeKeyByBitmapUint8Stream
|
||||
} else {
|
||||
keyBitmap := make([][256]uint16, bitmapLen)
|
||||
for i, key := range sortedKeys {
|
||||
for j := 0; j < len(key); j++ {
|
||||
c := key[j]
|
||||
keyBitmap[j][c] |= (1 << uint(i))
|
||||
}
|
||||
d.sortedFieldSets = append(d.sortedFieldSets, fieldMap[key])
|
||||
}
|
||||
d.keyBitmapUint16 = keyBitmap
|
||||
d.keyDecoder = decodeKeyByBitmapUint16
|
||||
d.keyStreamDecoder = decodeKeyByBitmapUint16Stream
|
||||
}
|
||||
}
|
||||
|
||||
// decode from '\uXXXX'
|
||||
func decodeKeyCharByUnicodeRune(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
const defaultOffset = 4
|
||||
const surrogateOffset = 6
|
||||
|
||||
if cursor+defaultOffset >= int64(len(buf)) {
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
|
||||
}
|
||||
|
||||
r := unicodeToRune(buf[cursor : cursor+defaultOffset])
|
||||
if utf16.IsSurrogate(r) {
|
||||
cursor += defaultOffset
|
||||
if cursor+surrogateOffset >= int64(len(buf)) || buf[cursor] != '\\' || buf[cursor+1] != 'u' {
|
||||
return []byte(string(unicode.ReplacementChar)), cursor + defaultOffset - 1, nil
|
||||
}
|
||||
cursor += 2
|
||||
r2 := unicodeToRune(buf[cursor : cursor+defaultOffset])
|
||||
if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar {
|
||||
return []byte(string(r)), cursor + defaultOffset - 1, nil
|
||||
}
|
||||
}
|
||||
return []byte(string(r)), cursor + defaultOffset - 1, nil
|
||||
}
|
||||
|
||||
func decodeKeyCharByEscapedChar(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
c := buf[cursor]
|
||||
cursor++
|
||||
switch c {
|
||||
case '"':
|
||||
return []byte{'"'}, cursor, nil
|
||||
case '\\':
|
||||
return []byte{'\\'}, cursor, nil
|
||||
case '/':
|
||||
return []byte{'/'}, cursor, nil
|
||||
case 'b':
|
||||
return []byte{'\b'}, cursor, nil
|
||||
case 'f':
|
||||
return []byte{'\f'}, cursor, nil
|
||||
case 'n':
|
||||
return []byte{'\n'}, cursor, nil
|
||||
case 'r':
|
||||
return []byte{'\r'}, cursor, nil
|
||||
case 't':
|
||||
return []byte{'\t'}, cursor, nil
|
||||
case 'u':
|
||||
return decodeKeyCharByUnicodeRune(buf, cursor)
|
||||
}
|
||||
return nil, cursor, nil
|
||||
}
|
||||
|
||||
func decodeKeyByBitmapUint8(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
|
||||
var (
|
||||
curBit uint8 = math.MaxUint8
|
||||
)
|
||||
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
|
||||
for {
|
||||
switch char(b, cursor) {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
case '"':
|
||||
cursor++
|
||||
c := char(b, cursor)
|
||||
switch c {
|
||||
case '"':
|
||||
cursor++
|
||||
return cursor, nil, nil
|
||||
case nul:
|
||||
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
|
||||
}
|
||||
keyIdx := 0
|
||||
bitmap := d.keyBitmapUint8
|
||||
start := cursor
|
||||
for {
|
||||
c := char(b, cursor)
|
||||
switch c {
|
||||
case '"':
|
||||
fieldSetIndex := bits.TrailingZeros8(curBit)
|
||||
field := d.sortedFieldSets[fieldSetIndex]
|
||||
keyLen := cursor - start
|
||||
cursor++
|
||||
if keyLen < field.keyLen {
|
||||
// early match
|
||||
return cursor, nil, nil
|
||||
}
|
||||
return cursor, field, nil
|
||||
case nul:
|
||||
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
|
||||
case '\\':
|
||||
cursor++
|
||||
chars, nextCursor, err := decodeKeyCharByEscapedChar(buf, cursor)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, c := range chars {
|
||||
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
|
||||
if curBit == 0 {
|
||||
return decodeKeyNotFound(b, cursor)
|
||||
}
|
||||
keyIdx++
|
||||
}
|
||||
cursor = nextCursor
|
||||
default:
|
||||
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
|
||||
if curBit == 0 {
|
||||
return decodeKeyNotFound(b, cursor)
|
||||
}
|
||||
keyIdx++
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
default:
|
||||
return cursor, nil, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeKeyByBitmapUint16(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
|
||||
var (
|
||||
curBit uint16 = math.MaxUint16
|
||||
)
|
||||
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
|
||||
for {
|
||||
switch char(b, cursor) {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
case '"':
|
||||
cursor++
|
||||
c := char(b, cursor)
|
||||
switch c {
|
||||
case '"':
|
||||
cursor++
|
||||
return cursor, nil, nil
|
||||
case nul:
|
||||
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
|
||||
}
|
||||
keyIdx := 0
|
||||
bitmap := d.keyBitmapUint16
|
||||
start := cursor
|
||||
for {
|
||||
c := char(b, cursor)
|
||||
switch c {
|
||||
case '"':
|
||||
fieldSetIndex := bits.TrailingZeros16(curBit)
|
||||
field := d.sortedFieldSets[fieldSetIndex]
|
||||
keyLen := cursor - start
|
||||
cursor++
|
||||
if keyLen < field.keyLen {
|
||||
// early match
|
||||
return cursor, nil, nil
|
||||
}
|
||||
return cursor, field, nil
|
||||
case nul:
|
||||
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
|
||||
case '\\':
|
||||
cursor++
|
||||
chars, nextCursor, err := decodeKeyCharByEscapedChar(buf, cursor)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, c := range chars {
|
||||
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
|
||||
if curBit == 0 {
|
||||
return decodeKeyNotFound(b, cursor)
|
||||
}
|
||||
keyIdx++
|
||||
}
|
||||
cursor = nextCursor
|
||||
default:
|
||||
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
|
||||
if curBit == 0 {
|
||||
return decodeKeyNotFound(b, cursor)
|
||||
}
|
||||
keyIdx++
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
default:
|
||||
return cursor, nil, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeKeyNotFound(b unsafe.Pointer, cursor int64) (int64, *structFieldSet, error) {
|
||||
for {
|
||||
cursor++
|
||||
switch char(b, cursor) {
|
||||
case '"':
|
||||
cursor++
|
||||
return cursor, nil, nil
|
||||
case '\\':
|
||||
cursor++
|
||||
if char(b, cursor) == nul {
|
||||
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
|
||||
}
|
||||
case nul:
|
||||
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeKey(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
|
||||
key, c, err := d.stringDecoder.decodeByte(buf, cursor)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
cursor = c
|
||||
k := *(*string)(unsafe.Pointer(&key))
|
||||
field, exists := d.fieldMap[k]
|
||||
if !exists {
|
||||
return cursor, nil, nil
|
||||
}
|
||||
return cursor, field, nil
|
||||
}
|
||||
|
||||
func decodeKeyByBitmapUint8Stream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
|
||||
var (
|
||||
curBit uint8 = math.MaxUint8
|
||||
)
|
||||
_, cursor, p := s.stat()
|
||||
for {
|
||||
switch char(p, cursor) {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
|
||||
case '"':
|
||||
cursor++
|
||||
FIRST_CHAR:
|
||||
start := cursor
|
||||
switch char(p, cursor) {
|
||||
case '"':
|
||||
cursor++
|
||||
s.cursor = cursor
|
||||
return nil, "", nil
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
goto FIRST_CHAR
|
||||
}
|
||||
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
}
|
||||
keyIdx := 0
|
||||
bitmap := d.keyBitmapUint8
|
||||
for {
|
||||
c := char(p, cursor)
|
||||
switch c {
|
||||
case '"':
|
||||
fieldSetIndex := bits.TrailingZeros8(curBit)
|
||||
field := d.sortedFieldSets[fieldSetIndex]
|
||||
keyLen := cursor - start
|
||||
cursor++
|
||||
s.cursor = cursor
|
||||
if keyLen < field.keyLen {
|
||||
// early match
|
||||
return nil, field.key, nil
|
||||
}
|
||||
return field, field.key, nil
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
case '\\':
|
||||
s.cursor = cursor + 1 // skip '\' char
|
||||
chars, err := decodeKeyCharByEscapeCharStream(s)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
cursor = s.cursor
|
||||
for _, c := range chars {
|
||||
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
|
||||
if curBit == 0 {
|
||||
s.cursor = cursor
|
||||
return decodeKeyNotFoundStream(s, start)
|
||||
}
|
||||
keyIdx++
|
||||
}
|
||||
default:
|
||||
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
|
||||
if curBit == 0 {
|
||||
s.cursor = cursor
|
||||
return decodeKeyNotFoundStream(s, start)
|
||||
}
|
||||
keyIdx++
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
default:
|
||||
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeKeyByBitmapUint16Stream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
|
||||
var (
|
||||
curBit uint16 = math.MaxUint16
|
||||
)
|
||||
_, cursor, p := s.stat()
|
||||
for {
|
||||
switch char(p, cursor) {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
|
||||
case '"':
|
||||
cursor++
|
||||
FIRST_CHAR:
|
||||
start := cursor
|
||||
switch char(p, cursor) {
|
||||
case '"':
|
||||
cursor++
|
||||
s.cursor = cursor
|
||||
return nil, "", nil
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
goto FIRST_CHAR
|
||||
}
|
||||
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
}
|
||||
keyIdx := 0
|
||||
bitmap := d.keyBitmapUint16
|
||||
for {
|
||||
c := char(p, cursor)
|
||||
switch c {
|
||||
case '"':
|
||||
fieldSetIndex := bits.TrailingZeros16(curBit)
|
||||
field := d.sortedFieldSets[fieldSetIndex]
|
||||
keyLen := cursor - start
|
||||
cursor++
|
||||
s.cursor = cursor
|
||||
if keyLen < field.keyLen {
|
||||
// early match
|
||||
return nil, field.key, nil
|
||||
}
|
||||
return field, field.key, nil
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
case '\\':
|
||||
s.cursor = cursor + 1 // skip '\' char
|
||||
chars, err := decodeKeyCharByEscapeCharStream(s)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
cursor = s.cursor
|
||||
for _, c := range chars {
|
||||
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
|
||||
if curBit == 0 {
|
||||
s.cursor = cursor
|
||||
return decodeKeyNotFoundStream(s, start)
|
||||
}
|
||||
keyIdx++
|
||||
}
|
||||
default:
|
||||
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
|
||||
if curBit == 0 {
|
||||
s.cursor = cursor
|
||||
return decodeKeyNotFoundStream(s, start)
|
||||
}
|
||||
keyIdx++
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
default:
|
||||
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decode from '\uXXXX'
|
||||
func decodeKeyCharByUnicodeRuneStream(s *Stream) ([]byte, error) {
|
||||
const defaultOffset = 4
|
||||
const surrogateOffset = 6
|
||||
|
||||
if s.cursor+defaultOffset >= s.length {
|
||||
if !s.read() {
|
||||
return nil, errors.ErrInvalidCharacter(s.char(), "escaped unicode char", s.totalOffset())
|
||||
}
|
||||
}
|
||||
|
||||
r := unicodeToRune(s.buf[s.cursor : s.cursor+defaultOffset])
|
||||
if utf16.IsSurrogate(r) {
|
||||
s.cursor += defaultOffset
|
||||
if s.cursor+surrogateOffset >= s.length {
|
||||
s.read()
|
||||
}
|
||||
if s.cursor+surrogateOffset >= s.length || s.buf[s.cursor] != '\\' || s.buf[s.cursor+1] != 'u' {
|
||||
s.cursor += defaultOffset - 1
|
||||
return []byte(string(unicode.ReplacementChar)), nil
|
||||
}
|
||||
r2 := unicodeToRune(s.buf[s.cursor+defaultOffset+2 : s.cursor+surrogateOffset])
|
||||
if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar {
|
||||
s.cursor += defaultOffset - 1
|
||||
return []byte(string(r)), nil
|
||||
}
|
||||
}
|
||||
s.cursor += defaultOffset - 1
|
||||
return []byte(string(r)), nil
|
||||
}
|
||||
|
||||
func decodeKeyCharByEscapeCharStream(s *Stream) ([]byte, error) {
|
||||
c := s.buf[s.cursor]
|
||||
s.cursor++
|
||||
RETRY:
|
||||
switch c {
|
||||
case '"':
|
||||
return []byte{'"'}, nil
|
||||
case '\\':
|
||||
return []byte{'\\'}, nil
|
||||
case '/':
|
||||
return []byte{'/'}, nil
|
||||
case 'b':
|
||||
return []byte{'\b'}, nil
|
||||
case 'f':
|
||||
return []byte{'\f'}, nil
|
||||
case 'n':
|
||||
return []byte{'\n'}, nil
|
||||
case 'r':
|
||||
return []byte{'\r'}, nil
|
||||
case 't':
|
||||
return []byte{'\t'}, nil
|
||||
case 'u':
|
||||
return decodeKeyCharByUnicodeRuneStream(s)
|
||||
case nul:
|
||||
if !s.read() {
|
||||
return nil, errors.ErrInvalidCharacter(s.char(), "escaped char", s.totalOffset())
|
||||
}
|
||||
goto RETRY
|
||||
default:
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("struct field", s.totalOffset())
|
||||
}
|
||||
}
|
||||
|
||||
func decodeKeyNotFoundStream(s *Stream, start int64) (*structFieldSet, string, error) {
|
||||
buf, cursor, p := s.stat()
|
||||
for {
|
||||
cursor++
|
||||
switch char(p, cursor) {
|
||||
case '"':
|
||||
b := buf[start:cursor]
|
||||
key := *(*string)(unsafe.Pointer(&b))
|
||||
cursor++
|
||||
s.cursor = cursor
|
||||
return nil, key, nil
|
||||
case '\\':
|
||||
cursor++
|
||||
if char(p, cursor) == nul {
|
||||
s.cursor = cursor
|
||||
if !s.read() {
|
||||
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
}
|
||||
buf, cursor, p = s.statForRetry()
|
||||
}
|
||||
case nul:
|
||||
s.cursor = cursor
|
||||
if !s.read() {
|
||||
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
|
||||
}
|
||||
buf, cursor, p = s.statForRetry()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeKeyStream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
|
||||
key, err := d.stringDecoder.decodeStreamByte(s)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
k := *(*string)(unsafe.Pointer(&key))
|
||||
return d.fieldMap[k], k, nil
|
||||
}
|
||||
|
||||
func (d *structDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
|
||||
}
|
||||
|
||||
c := s.skipWhiteSpace()
|
||||
switch c {
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
if s.char() != '{' {
|
||||
return errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
if s.skipWhiteSpace() == '}' {
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
seenFields map[int]struct{}
|
||||
seenFieldNum int
|
||||
)
|
||||
firstWin := (s.Option.Flags & FirstWinOption) != 0
|
||||
if firstWin {
|
||||
seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
|
||||
}
|
||||
for {
|
||||
s.reset()
|
||||
field, key, err := d.keyStreamDecoder(d, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.skipWhiteSpace() != ':' {
|
||||
return errors.ErrExpected("colon after object key", s.totalOffset())
|
||||
}
|
||||
s.cursor++
|
||||
if field != nil {
|
||||
if field.err != nil {
|
||||
return field.err
|
||||
}
|
||||
if firstWin {
|
||||
if _, exists := seenFields[field.fieldIdx]; exists {
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := field.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
|
||||
return err
|
||||
}
|
||||
seenFieldNum++
|
||||
if d.fieldUniqueNameNum <= seenFieldNum {
|
||||
return s.skipObject(depth)
|
||||
}
|
||||
seenFields[field.fieldIdx] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
if err := field.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if s.DisallowUnknownFields {
|
||||
return fmt.Errorf("json: unknown field %q", key)
|
||||
} else {
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c := s.skipWhiteSpace()
|
||||
if c == '}' {
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
if c != ',' {
|
||||
return errors.ErrExpected("comma after object element", s.totalOffset())
|
||||
}
|
||||
s.cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
depth++
|
||||
if depth > maxDecodeNestingDepth {
|
||||
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
||||
}
|
||||
buflen := int64(len(buf))
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
|
||||
switch char(b, cursor) {
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return cursor, nil
|
||||
case '{':
|
||||
default:
|
||||
return 0, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
|
||||
}
|
||||
cursor++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == '}' {
|
||||
cursor++
|
||||
return cursor, nil
|
||||
}
|
||||
var (
|
||||
seenFields map[int]struct{}
|
||||
seenFieldNum int
|
||||
)
|
||||
firstWin := (ctx.Option.Flags & FirstWinOption) != 0
|
||||
if firstWin {
|
||||
seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
|
||||
}
|
||||
for {
|
||||
c, field, err := d.keyDecoder(d, buf, cursor)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(buf, c)
|
||||
if char(b, cursor) != ':' {
|
||||
return 0, errors.ErrExpected("colon after object key", cursor)
|
||||
}
|
||||
cursor++
|
||||
if cursor >= buflen {
|
||||
return 0, errors.ErrExpected("object value after colon", cursor)
|
||||
}
|
||||
if field != nil {
|
||||
if field.err != nil {
|
||||
return 0, field.err
|
||||
}
|
||||
if firstWin {
|
||||
if _, exists := seenFields[field.fieldIdx]; exists {
|
||||
c, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = c
|
||||
} else {
|
||||
c, err := field.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = c
|
||||
seenFieldNum++
|
||||
if d.fieldUniqueNameNum <= seenFieldNum {
|
||||
return skipObject(buf, cursor, depth)
|
||||
}
|
||||
seenFields[field.fieldIdx] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
c, err := field.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = c
|
||||
}
|
||||
} else {
|
||||
c, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
cursor = c
|
||||
}
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if char(b, cursor) == '}' {
|
||||
cursor++
|
||||
return cursor, nil
|
||||
}
|
||||
if char(b, cursor) != ',' {
|
||||
return 0, errors.ErrExpected("comma after object element", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func (d *structDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: struct decoder does not support decode path")
|
||||
}
|
30
internal/decoder/type.go
Normal file
30
internal/decoder/type.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Decoder interface {
|
||||
Decode(*RuntimeContext, int64, int64, unsafe.Pointer) (int64, error)
|
||||
DecodePath(*RuntimeContext, int64, int64) ([][]byte, int64, error)
|
||||
DecodeStream(*Stream, int64, unsafe.Pointer) error
|
||||
}
|
||||
|
||||
const (
|
||||
nul = '\000'
|
||||
maxDecodeNestingDepth = 10000
|
||||
)
|
||||
|
||||
type unmarshalerContext interface {
|
||||
UnmarshalJSON(context.Context, []byte) error
|
||||
}
|
||||
|
||||
var (
|
||||
unmarshalJSONType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
|
||||
unmarshalJSONContextType = reflect.TypeOf((*unmarshalerContext)(nil)).Elem()
|
||||
unmarshalTextType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
)
|
194
internal/decoder/uint.go
Normal file
194
internal/decoder/uint.go
Normal file
|
@ -0,0 +1,194 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type uintDecoder struct {
|
||||
typ *runtime.Type
|
||||
kind reflect.Kind
|
||||
op func(unsafe.Pointer, uint64)
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newUintDecoder(typ *runtime.Type, structName, fieldName string, op func(unsafe.Pointer, uint64)) *uintDecoder {
|
||||
return &uintDecoder{
|
||||
typ: typ,
|
||||
kind: typ.Kind(),
|
||||
op: op,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *uintDecoder) typeError(buf []byte, offset int64) *errors.UnmarshalTypeError {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: fmt.Sprintf("number %s", string(buf)),
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
pow10u64 = [...]uint64{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
}
|
||||
pow10u64Len = len(pow10u64)
|
||||
)
|
||||
|
||||
func (d *uintDecoder) parseUint(b []byte) (uint64, error) {
|
||||
maxDigit := len(b)
|
||||
if maxDigit > pow10u64Len {
|
||||
return 0, fmt.Errorf("invalid length of number")
|
||||
}
|
||||
sum := uint64(0)
|
||||
for i := 0; i < maxDigit; i++ {
|
||||
c := uint64(b[i]) - 48
|
||||
digitValue := pow10u64[maxDigit-i-1]
|
||||
sum += c * digitValue
|
||||
}
|
||||
return sum, nil
|
||||
}
|
||||
|
||||
func (d *uintDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
continue
|
||||
case '0':
|
||||
s.cursor++
|
||||
return numZeroBuf, nil
|
||||
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
start := s.cursor
|
||||
for {
|
||||
s.cursor++
|
||||
if numTable[s.char()] {
|
||||
continue
|
||||
} else if s.char() == nul {
|
||||
if s.read() {
|
||||
s.cursor-- // for retry current character
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
num := s.buf[start:s.cursor]
|
||||
return num, nil
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
return nil, d.typeError([]byte{s.char()}, s.totalOffset())
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("number(unsigned integer)", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *uintDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case '0':
|
||||
cursor++
|
||||
return numZeroBuf, cursor, nil
|
||||
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
start := cursor
|
||||
cursor++
|
||||
for numTable[buf[cursor]] {
|
||||
cursor++
|
||||
}
|
||||
num := buf[start:cursor]
|
||||
return num, cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return nil, cursor, nil
|
||||
default:
|
||||
return nil, 0, d.typeError([]byte{buf[cursor]}, cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *uintDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
bytes, err := d.decodeStreamByte(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes == nil {
|
||||
return nil
|
||||
}
|
||||
u64, err := d.parseUint(bytes)
|
||||
if err != nil {
|
||||
return d.typeError(bytes, s.totalOffset())
|
||||
}
|
||||
switch d.kind {
|
||||
case reflect.Uint8:
|
||||
if (1 << 8) <= u64 {
|
||||
return d.typeError(bytes, s.totalOffset())
|
||||
}
|
||||
case reflect.Uint16:
|
||||
if (1 << 16) <= u64 {
|
||||
return d.typeError(bytes, s.totalOffset())
|
||||
}
|
||||
case reflect.Uint32:
|
||||
if (1 << 32) <= u64 {
|
||||
return d.typeError(bytes, s.totalOffset())
|
||||
}
|
||||
}
|
||||
d.op(p, u64)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *uintDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
return c, nil
|
||||
}
|
||||
cursor = c
|
||||
u64, err := d.parseUint(bytes)
|
||||
if err != nil {
|
||||
return 0, d.typeError(bytes, cursor)
|
||||
}
|
||||
switch d.kind {
|
||||
case reflect.Uint8:
|
||||
if (1 << 8) <= u64 {
|
||||
return 0, d.typeError(bytes, cursor)
|
||||
}
|
||||
case reflect.Uint16:
|
||||
if (1 << 16) <= u64 {
|
||||
return 0, d.typeError(bytes, cursor)
|
||||
}
|
||||
case reflect.Uint32:
|
||||
if (1 << 32) <= u64 {
|
||||
return 0, d.typeError(bytes, cursor)
|
||||
}
|
||||
}
|
||||
d.op(p, u64)
|
||||
return cursor, nil
|
||||
}
|
||||
|
||||
func (d *uintDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: uint decoder does not support decode path")
|
||||
}
|
104
internal/decoder/unmarshal_json.go
Normal file
104
internal/decoder/unmarshal_json.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type unmarshalJSONDecoder struct {
|
||||
typ *runtime.Type
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newUnmarshalJSONDecoder(typ *runtime.Type, structName, fieldName string) *unmarshalJSONDecoder {
|
||||
return &unmarshalJSONDecoder{
|
||||
typ: typ,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *unmarshalJSONDecoder) annotateError(cursor int64, err error) {
|
||||
switch e := err.(type) {
|
||||
case *errors.UnmarshalTypeError:
|
||||
e.Struct = d.structName
|
||||
e.Field = d.fieldName
|
||||
case *errors.SyntaxError:
|
||||
e.Offset = cursor
|
||||
}
|
||||
}
|
||||
|
||||
func (d *unmarshalJSONDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
s.skipWhiteSpace()
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
src := s.buf[start:s.cursor]
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
switch v := v.(type) {
|
||||
case unmarshalerContext:
|
||||
var ctx context.Context
|
||||
if (s.Option.Flags & ContextOption) != 0 {
|
||||
ctx = s.Option.Context
|
||||
} else {
|
||||
ctx = context.Background()
|
||||
}
|
||||
if err := v.UnmarshalJSON(ctx, dst); err != nil {
|
||||
d.annotateError(s.cursor, err)
|
||||
return err
|
||||
}
|
||||
case json.Unmarshaler:
|
||||
if err := v.UnmarshalJSON(dst); err != nil {
|
||||
d.annotateError(s.cursor, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *unmarshalJSONDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src := buf[start:end]
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
if (ctx.Option.Flags & ContextOption) != 0 {
|
||||
if err := v.(unmarshalerContext).UnmarshalJSON(ctx.Option.Context, dst); err != nil {
|
||||
d.annotateError(cursor, err)
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
if err := v.(json.Unmarshaler).UnmarshalJSON(dst); err != nil {
|
||||
d.annotateError(cursor, err)
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return end, nil
|
||||
}
|
||||
|
||||
func (d *unmarshalJSONDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: unmarshal json decoder does not support decode path")
|
||||
}
|
285
internal/decoder/unmarshal_text.go
Normal file
285
internal/decoder/unmarshal_text.go
Normal file
|
@ -0,0 +1,285 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"fmt"
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type unmarshalTextDecoder struct {
|
||||
typ *runtime.Type
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newUnmarshalTextDecoder(typ *runtime.Type, structName, fieldName string) *unmarshalTextDecoder {
|
||||
return &unmarshalTextDecoder{
|
||||
typ: typ,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *unmarshalTextDecoder) annotateError(cursor int64, err error) {
|
||||
switch e := err.(type) {
|
||||
case *errors.UnmarshalTypeError:
|
||||
e.Struct = d.structName
|
||||
e.Field = d.fieldName
|
||||
case *errors.SyntaxError:
|
||||
e.Offset = cursor
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
nullbytes = []byte(`null`)
|
||||
)
|
||||
|
||||
func (d *unmarshalTextDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
s.skipWhiteSpace()
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
src := s.buf[start:s.cursor]
|
||||
if len(src) > 0 {
|
||||
switch src[0] {
|
||||
case '[':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "array",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case '{':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "number",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case 'n':
|
||||
if bytes.Equal(src, nullbytes) {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
if b, ok := unquoteBytes(dst); ok {
|
||||
dst = b
|
||||
}
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
if err := v.(encoding.TextUnmarshaler).UnmarshalText(dst); err != nil {
|
||||
d.annotateError(s.cursor, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *unmarshalTextDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src := buf[start:end]
|
||||
if len(src) > 0 {
|
||||
switch src[0] {
|
||||
case '[':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "array",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case '{':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "number",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case 'n':
|
||||
if bytes.Equal(src, nullbytes) {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return end, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := unquoteBytes(src); ok {
|
||||
src = s
|
||||
}
|
||||
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
|
||||
typ: d.typ,
|
||||
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
|
||||
}))
|
||||
if err := v.(encoding.TextUnmarshaler).UnmarshalText(src); err != nil {
|
||||
d.annotateError(cursor, err)
|
||||
return 0, err
|
||||
}
|
||||
return end, nil
|
||||
}
|
||||
|
||||
func (d *unmarshalTextDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: unmarshal text decoder does not support decode path")
|
||||
}
|
||||
|
||||
func unquoteBytes(s []byte) (t []byte, ok bool) { //nolint: nonamedreturns
|
||||
length := len(s)
|
||||
if length < 2 || s[0] != '"' || s[length-1] != '"' {
|
||||
return
|
||||
}
|
||||
s = s[1 : length-1]
|
||||
length -= 2
|
||||
|
||||
// Check for unusual characters. If there are none,
|
||||
// then no unquoting is needed, so return a slice of the
|
||||
// original bytes.
|
||||
r := 0
|
||||
for r < length {
|
||||
c := s[r]
|
||||
if c == '\\' || c == '"' || c < ' ' {
|
||||
break
|
||||
}
|
||||
if c < utf8.RuneSelf {
|
||||
r++
|
||||
continue
|
||||
}
|
||||
rr, size := utf8.DecodeRune(s[r:])
|
||||
if rr == utf8.RuneError && size == 1 {
|
||||
break
|
||||
}
|
||||
r += size
|
||||
}
|
||||
if r == length {
|
||||
return s, true
|
||||
}
|
||||
|
||||
b := make([]byte, length+2*utf8.UTFMax)
|
||||
w := copy(b, s[0:r])
|
||||
for r < length {
|
||||
// Out of room? Can only happen if s is full of
|
||||
// malformed UTF-8 and we're replacing each
|
||||
// byte with RuneError.
|
||||
if w >= len(b)-2*utf8.UTFMax {
|
||||
nb := make([]byte, (len(b)+utf8.UTFMax)*2)
|
||||
copy(nb, b[0:w])
|
||||
b = nb
|
||||
}
|
||||
switch c := s[r]; {
|
||||
case c == '\\':
|
||||
r++
|
||||
if r >= length {
|
||||
return
|
||||
}
|
||||
switch s[r] {
|
||||
default:
|
||||
return
|
||||
case '"', '\\', '/', '\'':
|
||||
b[w] = s[r]
|
||||
r++
|
||||
w++
|
||||
case 'b':
|
||||
b[w] = '\b'
|
||||
r++
|
||||
w++
|
||||
case 'f':
|
||||
b[w] = '\f'
|
||||
r++
|
||||
w++
|
||||
case 'n':
|
||||
b[w] = '\n'
|
||||
r++
|
||||
w++
|
||||
case 'r':
|
||||
b[w] = '\r'
|
||||
r++
|
||||
w++
|
||||
case 't':
|
||||
b[w] = '\t'
|
||||
r++
|
||||
w++
|
||||
case 'u':
|
||||
r--
|
||||
rr := getu4(s[r:])
|
||||
if rr < 0 {
|
||||
return
|
||||
}
|
||||
r += 6
|
||||
if utf16.IsSurrogate(rr) {
|
||||
rr1 := getu4(s[r:])
|
||||
if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
|
||||
// A valid pair; consume.
|
||||
r += 6
|
||||
w += utf8.EncodeRune(b[w:], dec)
|
||||
break
|
||||
}
|
||||
// Invalid surrogate; fall back to replacement rune.
|
||||
rr = unicode.ReplacementChar
|
||||
}
|
||||
w += utf8.EncodeRune(b[w:], rr)
|
||||
}
|
||||
|
||||
// Quote, control characters are invalid.
|
||||
case c == '"', c < ' ':
|
||||
return
|
||||
|
||||
// ASCII
|
||||
case c < utf8.RuneSelf:
|
||||
b[w] = c
|
||||
r++
|
||||
w++
|
||||
|
||||
// Coerce to well-formed UTF-8.
|
||||
default:
|
||||
rr, size := utf8.DecodeRune(s[r:])
|
||||
r += size
|
||||
w += utf8.EncodeRune(b[w:], rr)
|
||||
}
|
||||
}
|
||||
return b[0:w], true
|
||||
}
|
||||
|
||||
func getu4(s []byte) rune {
|
||||
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
|
||||
return -1
|
||||
}
|
||||
var r rune
|
||||
for _, c := range s[2:6] {
|
||||
switch {
|
||||
case '0' <= c && c <= '9':
|
||||
c = c - '0'
|
||||
case 'a' <= c && c <= 'f':
|
||||
c = c - 'a' + 10
|
||||
case 'A' <= c && c <= 'F':
|
||||
c = c - 'A' + 10
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
r = r*16 + rune(c)
|
||||
}
|
||||
return r
|
||||
}
|
73
internal/decoder/wrapped_string.go
Normal file
73
internal/decoder/wrapped_string.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type wrappedStringDecoder struct {
|
||||
typ *runtime.Type
|
||||
dec Decoder
|
||||
stringDecoder *stringDecoder
|
||||
structName string
|
||||
fieldName string
|
||||
isPtrType bool
|
||||
}
|
||||
|
||||
func newWrappedStringDecoder(typ *runtime.Type, dec Decoder, structName, fieldName string) *wrappedStringDecoder {
|
||||
return &wrappedStringDecoder{
|
||||
typ: typ,
|
||||
dec: dec,
|
||||
stringDecoder: newStringDecoder(structName, fieldName),
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
isPtrType: typ.Kind() == reflect.Ptr,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *wrappedStringDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
bytes, err := d.stringDecoder.decodeStreamByte(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes == nil {
|
||||
if d.isPtrType {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
b := make([]byte, len(bytes)+1)
|
||||
copy(b, bytes)
|
||||
if _, err := d.dec.Decode(&RuntimeContext{Buf: b}, 0, depth, p); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *wrappedStringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
bytes, c, err := d.stringDecoder.decodeByte(ctx.Buf, cursor)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if bytes == nil {
|
||||
if d.isPtrType {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
bytes = append(bytes, nul)
|
||||
oldBuf := ctx.Buf
|
||||
ctx.Buf = bytes
|
||||
if _, err := d.dec.Decode(ctx, 0, depth, p); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ctx.Buf = oldBuf
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (d *wrappedStringDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: wrapped string decoder does not support decode path")
|
||||
}
|
1023
internal/encoder/code.go
Normal file
1023
internal/encoder/code.go
Normal file
File diff suppressed because it is too large
Load diff
286
internal/encoder/compact.go
Normal file
286
internal/encoder/compact.go
Normal file
|
@ -0,0 +1,286 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
isWhiteSpace = [256]bool{
|
||||
' ': true,
|
||||
'\n': true,
|
||||
'\t': true,
|
||||
'\r': true,
|
||||
}
|
||||
isHTMLEscapeChar = [256]bool{
|
||||
'<': true,
|
||||
'>': true,
|
||||
'&': true,
|
||||
}
|
||||
nul = byte('\000')
|
||||
)
|
||||
|
||||
func Compact(buf *bytes.Buffer, src []byte, escape bool) error {
|
||||
if len(src) == 0 {
|
||||
return errors.ErrUnexpectedEndOfJSON("", 0)
|
||||
}
|
||||
buf.Grow(len(src))
|
||||
dst := buf.Bytes()
|
||||
|
||||
ctx := TakeRuntimeContext()
|
||||
ctxBuf := ctx.Buf[:0]
|
||||
ctxBuf = append(append(ctxBuf, src...), nul)
|
||||
ctx.Buf = ctxBuf
|
||||
|
||||
if err := compactAndWrite(buf, dst, ctxBuf, escape); err != nil {
|
||||
ReleaseRuntimeContext(ctx)
|
||||
return err
|
||||
}
|
||||
ReleaseRuntimeContext(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func compactAndWrite(buf *bytes.Buffer, dst []byte, src []byte, escape bool) error {
|
||||
dst, err := compact(dst, src, escape)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := buf.Write(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func compact(dst, src []byte, escape bool) ([]byte, error) {
|
||||
buf, cursor, err := compactValue(dst, src, 0, escape)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateEndBuf(src, cursor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func validateEndBuf(src []byte, cursor int64) error {
|
||||
for {
|
||||
switch src[cursor] {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case nul:
|
||||
return nil
|
||||
}
|
||||
return errors.ErrSyntax(
|
||||
fmt.Sprintf("invalid character '%c' after top-level value", src[cursor]),
|
||||
cursor+1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func skipWhiteSpace(buf []byte, cursor int64) int64 {
|
||||
LOOP:
|
||||
if isWhiteSpace[buf[cursor]] {
|
||||
cursor++
|
||||
goto LOOP
|
||||
}
|
||||
return cursor
|
||||
}
|
||||
|
||||
func compactValue(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) {
|
||||
for {
|
||||
switch src[cursor] {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case '{':
|
||||
return compactObject(dst, src, cursor, escape)
|
||||
case '}':
|
||||
return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor)
|
||||
case '[':
|
||||
return compactArray(dst, src, cursor, escape)
|
||||
case ']':
|
||||
return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor)
|
||||
case '"':
|
||||
return compactString(dst, src, cursor, escape)
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return compactNumber(dst, src, cursor)
|
||||
case 't':
|
||||
return compactTrue(dst, src, cursor)
|
||||
case 'f':
|
||||
return compactFalse(dst, src, cursor)
|
||||
case 'n':
|
||||
return compactNull(dst, src, cursor)
|
||||
default:
|
||||
return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compactObject(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) {
|
||||
if src[cursor] == '{' {
|
||||
dst = append(dst, '{')
|
||||
} else {
|
||||
return nil, 0, errors.ErrExpected("expected { character for object value", cursor)
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor+1)
|
||||
if src[cursor] == '}' {
|
||||
dst = append(dst, '}')
|
||||
return dst, cursor + 1, nil
|
||||
}
|
||||
var err error
|
||||
for {
|
||||
cursor = skipWhiteSpace(src, cursor)
|
||||
dst, cursor, err = compactString(dst, src, cursor, escape)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor)
|
||||
if src[cursor] != ':' {
|
||||
return nil, 0, errors.ErrExpected("colon after object key", cursor)
|
||||
}
|
||||
dst = append(dst, ':')
|
||||
dst, cursor, err = compactValue(dst, src, cursor+1, escape)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor)
|
||||
switch src[cursor] {
|
||||
case '}':
|
||||
dst = append(dst, '}')
|
||||
cursor++
|
||||
return dst, cursor, nil
|
||||
case ',':
|
||||
dst = append(dst, ',')
|
||||
default:
|
||||
return nil, 0, errors.ErrExpected("comma after object value", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func compactArray(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) {
|
||||
if src[cursor] == '[' {
|
||||
dst = append(dst, '[')
|
||||
} else {
|
||||
return nil, 0, errors.ErrExpected("expected [ character for array value", cursor)
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor+1)
|
||||
if src[cursor] == ']' {
|
||||
dst = append(dst, ']')
|
||||
return dst, cursor + 1, nil
|
||||
}
|
||||
var err error
|
||||
for {
|
||||
dst, cursor, err = compactValue(dst, src, cursor, escape)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor)
|
||||
switch src[cursor] {
|
||||
case ']':
|
||||
dst = append(dst, ']')
|
||||
cursor++
|
||||
return dst, cursor, nil
|
||||
case ',':
|
||||
dst = append(dst, ',')
|
||||
default:
|
||||
return nil, 0, errors.ErrExpected("comma after array value", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func compactString(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) {
|
||||
if src[cursor] != '"' {
|
||||
return nil, 0, errors.ErrInvalidCharacter(src[cursor], "string", cursor)
|
||||
}
|
||||
start := cursor
|
||||
for {
|
||||
cursor++
|
||||
c := src[cursor]
|
||||
if escape {
|
||||
if isHTMLEscapeChar[c] {
|
||||
dst = append(dst, src[start:cursor]...)
|
||||
dst = append(dst, `\u00`...)
|
||||
dst = append(dst, hex[c>>4], hex[c&0xF])
|
||||
start = cursor + 1
|
||||
} else if c == 0xE2 && cursor+2 < int64(len(src)) && src[cursor+1] == 0x80 && src[cursor+2]&^1 == 0xA8 {
|
||||
dst = append(dst, src[start:cursor]...)
|
||||
dst = append(dst, `\u202`...)
|
||||
dst = append(dst, hex[src[cursor+2]&0xF])
|
||||
start = cursor + 3
|
||||
cursor += 2
|
||||
}
|
||||
}
|
||||
switch c {
|
||||
case '\\':
|
||||
cursor++
|
||||
if src[cursor] == nul {
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("string", int64(len(src)))
|
||||
}
|
||||
case '"':
|
||||
cursor++
|
||||
return append(dst, src[start:cursor]...), cursor, nil
|
||||
case nul:
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("string", int64(len(src)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compactNumber(dst, src []byte, cursor int64) ([]byte, int64, error) {
|
||||
start := cursor
|
||||
for {
|
||||
cursor++
|
||||
if floatTable[src[cursor]] {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
num := src[start:cursor]
|
||||
if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&num)), 64); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
dst = append(dst, num...)
|
||||
return dst, cursor, nil
|
||||
}
|
||||
|
||||
func compactTrue(dst, src []byte, cursor int64) ([]byte, int64, error) {
|
||||
if cursor+3 >= int64(len(src)) {
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("true", cursor)
|
||||
}
|
||||
if !bytes.Equal(src[cursor:cursor+4], []byte(`true`)) {
|
||||
return nil, 0, errors.ErrInvalidCharacter(src[cursor], "true", cursor)
|
||||
}
|
||||
dst = append(dst, "true"...)
|
||||
cursor += 4
|
||||
return dst, cursor, nil
|
||||
}
|
||||
|
||||
func compactFalse(dst, src []byte, cursor int64) ([]byte, int64, error) {
|
||||
if cursor+4 >= int64(len(src)) {
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("false", cursor)
|
||||
}
|
||||
if !bytes.Equal(src[cursor:cursor+5], []byte(`false`)) {
|
||||
return nil, 0, errors.ErrInvalidCharacter(src[cursor], "false", cursor)
|
||||
}
|
||||
dst = append(dst, "false"...)
|
||||
cursor += 5
|
||||
return dst, cursor, nil
|
||||
}
|
||||
|
||||
func compactNull(dst, src []byte, cursor int64) ([]byte, int64, error) {
|
||||
if cursor+3 >= int64(len(src)) {
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("null", cursor)
|
||||
}
|
||||
if !bytes.Equal(src[cursor:cursor+4], []byte(`null`)) {
|
||||
return nil, 0, errors.ErrInvalidCharacter(src[cursor], "null", cursor)
|
||||
}
|
||||
dst = append(dst, "null"...)
|
||||
cursor += 4
|
||||
return dst, cursor, nil
|
||||
}
|
939
internal/encoder/compiler.go
Normal file
939
internal/encoder/compiler.go
Normal file
|
@ -0,0 +1,939 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type marshalerContext interface {
|
||||
MarshalJSON(context.Context) ([]byte, error)
|
||||
}
|
||||
|
||||
var (
|
||||
marshalJSONType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
|
||||
marshalJSONContextType = reflect.TypeOf((*marshalerContext)(nil)).Elem()
|
||||
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||
jsonNumberType = reflect.TypeOf(json.Number(""))
|
||||
cachedOpcodeSets []*OpcodeSet
|
||||
cachedOpcodeMap unsafe.Pointer // map[uintptr]*OpcodeSet
|
||||
typeAddr *runtime.TypeAddr
|
||||
initEncoderOnce sync.Once
|
||||
)
|
||||
|
||||
func initEncoder() {
|
||||
initEncoderOnce.Do(func() {
|
||||
typeAddr = runtime.AnalyzeTypeAddr()
|
||||
if typeAddr == nil {
|
||||
typeAddr = &runtime.TypeAddr{}
|
||||
}
|
||||
cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange>>typeAddr.AddrShift+1)
|
||||
})
|
||||
}
|
||||
|
||||
func loadOpcodeMap() map[uintptr]*OpcodeSet {
|
||||
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 compileToGetCodeSetSlowPath(typeptr uintptr) (*OpcodeSet, error) {
|
||||
opcodeMap := loadOpcodeMap()
|
||||
if codeSet, exists := opcodeMap[typeptr]; exists {
|
||||
return codeSet, nil
|
||||
}
|
||||
codeSet, err := newCompiler().compile(typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storeOpcodeSet(typeptr, codeSet, opcodeMap)
|
||||
return codeSet, nil
|
||||
}
|
||||
|
||||
func getFilteredCodeSetIfNeeded(ctx *RuntimeContext, codeSet *OpcodeSet) (*OpcodeSet, error) {
|
||||
if (ctx.Option.Flag & ContextOption) == 0 {
|
||||
return codeSet, nil
|
||||
}
|
||||
query := FieldQueryFromContext(ctx.Option.Context)
|
||||
if query == nil {
|
||||
return codeSet, nil
|
||||
}
|
||||
ctx.Option.Flag |= FieldQueryOption
|
||||
cacheCodeSet := codeSet.getQueryCache(query.Hash())
|
||||
if cacheCodeSet != nil {
|
||||
return cacheCodeSet, nil
|
||||
}
|
||||
queryCodeSet, err := newCompiler().codeToOpcodeSet(codeSet.Type, codeSet.Code.Filter(query))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
codeSet.setQueryCache(query.Hash(), queryCodeSet)
|
||||
return queryCodeSet, nil
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
structTypeToCode map[uintptr]*StructCode
|
||||
}
|
||||
|
||||
func newCompiler() *Compiler {
|
||||
return &Compiler{
|
||||
structTypeToCode: map[uintptr]*StructCode{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) compile(typeptr uintptr) (*OpcodeSet, error) {
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
typ := *(**runtime.Type)(unsafe.Pointer(&typeptr))
|
||||
code, err := c.typeToCode(typ)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.codeToOpcodeSet(typ, code)
|
||||
}
|
||||
|
||||
func (c *Compiler) codeToOpcodeSet(typ *runtime.Type, code Code) (*OpcodeSet, error) {
|
||||
noescapeKeyCode := c.codeToOpcode(&compileContext{
|
||||
structTypeToCodes: map[uintptr]Opcodes{},
|
||||
recursiveCodes: &Opcodes{},
|
||||
}, typ, code)
|
||||
if err := noescapeKeyCode.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
escapeKeyCode := c.codeToOpcode(&compileContext{
|
||||
structTypeToCodes: map[uintptr]Opcodes{},
|
||||
recursiveCodes: &Opcodes{},
|
||||
escapeKey: true,
|
||||
}, typ, code)
|
||||
noescapeKeyCode = copyOpcode(noescapeKeyCode)
|
||||
escapeKeyCode = copyOpcode(escapeKeyCode)
|
||||
setTotalLengthToInterfaceOp(noescapeKeyCode)
|
||||
setTotalLengthToInterfaceOp(escapeKeyCode)
|
||||
interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
|
||||
interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
|
||||
codeLength := noescapeKeyCode.TotalLength()
|
||||
return &OpcodeSet{
|
||||
Type: typ,
|
||||
NoescapeKeyCode: noescapeKeyCode,
|
||||
EscapeKeyCode: escapeKeyCode,
|
||||
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
|
||||
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
|
||||
CodeLength: codeLength,
|
||||
EndCode: ToEndCode(interfaceNoescapeKeyCode),
|
||||
Code: code,
|
||||
QueryCache: map[string]*OpcodeSet{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) typeToCode(typ *runtime.Type) (Code, error) {
|
||||
switch {
|
||||
case c.implementsMarshalJSON(typ):
|
||||
return c.marshalJSONCode(typ)
|
||||
case c.implementsMarshalText(typ):
|
||||
return c.marshalTextCode(typ)
|
||||
}
|
||||
|
||||
isPtr := false
|
||||
orgType := typ
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
isPtr = true
|
||||
}
|
||||
switch {
|
||||
case c.implementsMarshalJSON(typ):
|
||||
return c.marshalJSONCode(orgType)
|
||||
case c.implementsMarshalText(typ):
|
||||
return c.marshalTextCode(orgType)
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Slice:
|
||||
elem := typ.Elem()
|
||||
if elem.Kind() == reflect.Uint8 {
|
||||
p := runtime.PtrTo(elem)
|
||||
if !c.implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
|
||||
return c.bytesCode(typ, isPtr)
|
||||
}
|
||||
}
|
||||
return c.sliceCode(typ)
|
||||
case reflect.Map:
|
||||
if isPtr {
|
||||
return c.ptrCode(runtime.PtrTo(typ))
|
||||
}
|
||||
return c.mapCode(typ)
|
||||
case reflect.Struct:
|
||||
return c.structCode(typ, isPtr)
|
||||
case reflect.Int:
|
||||
return c.intCode(typ, isPtr)
|
||||
case reflect.Int8:
|
||||
return c.int8Code(typ, isPtr)
|
||||
case reflect.Int16:
|
||||
return c.int16Code(typ, isPtr)
|
||||
case reflect.Int32:
|
||||
return c.int32Code(typ, isPtr)
|
||||
case reflect.Int64:
|
||||
return c.int64Code(typ, isPtr)
|
||||
case reflect.Uint, reflect.Uintptr:
|
||||
return c.uintCode(typ, isPtr)
|
||||
case reflect.Uint8:
|
||||
return c.uint8Code(typ, isPtr)
|
||||
case reflect.Uint16:
|
||||
return c.uint16Code(typ, isPtr)
|
||||
case reflect.Uint32:
|
||||
return c.uint32Code(typ, isPtr)
|
||||
case reflect.Uint64:
|
||||
return c.uint64Code(typ, isPtr)
|
||||
case reflect.Float32:
|
||||
return c.float32Code(typ, isPtr)
|
||||
case reflect.Float64:
|
||||
return c.float64Code(typ, isPtr)
|
||||
case reflect.String:
|
||||
return c.stringCode(typ, isPtr)
|
||||
case reflect.Bool:
|
||||
return c.boolCode(typ, isPtr)
|
||||
case reflect.Interface:
|
||||
return c.interfaceCode(typ, isPtr)
|
||||
default:
|
||||
if isPtr && typ.Implements(marshalTextType) {
|
||||
typ = orgType
|
||||
}
|
||||
return c.typeToCodeWithPtr(typ, isPtr)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) typeToCodeWithPtr(typ *runtime.Type, isPtr bool) (Code, error) {
|
||||
switch {
|
||||
case c.implementsMarshalJSON(typ):
|
||||
return c.marshalJSONCode(typ)
|
||||
case c.implementsMarshalText(typ):
|
||||
return c.marshalTextCode(typ)
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
return c.ptrCode(typ)
|
||||
case reflect.Slice:
|
||||
elem := typ.Elem()
|
||||
if elem.Kind() == reflect.Uint8 {
|
||||
p := runtime.PtrTo(elem)
|
||||
if !c.implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
|
||||
return c.bytesCode(typ, false)
|
||||
}
|
||||
}
|
||||
return c.sliceCode(typ)
|
||||
case reflect.Array:
|
||||
return c.arrayCode(typ)
|
||||
case reflect.Map:
|
||||
return c.mapCode(typ)
|
||||
case reflect.Struct:
|
||||
return c.structCode(typ, isPtr)
|
||||
case reflect.Interface:
|
||||
return c.interfaceCode(typ, false)
|
||||
case reflect.Int:
|
||||
return c.intCode(typ, false)
|
||||
case reflect.Int8:
|
||||
return c.int8Code(typ, false)
|
||||
case reflect.Int16:
|
||||
return c.int16Code(typ, false)
|
||||
case reflect.Int32:
|
||||
return c.int32Code(typ, false)
|
||||
case reflect.Int64:
|
||||
return c.int64Code(typ, false)
|
||||
case reflect.Uint:
|
||||
return c.uintCode(typ, false)
|
||||
case reflect.Uint8:
|
||||
return c.uint8Code(typ, false)
|
||||
case reflect.Uint16:
|
||||
return c.uint16Code(typ, false)
|
||||
case reflect.Uint32:
|
||||
return c.uint32Code(typ, false)
|
||||
case reflect.Uint64:
|
||||
return c.uint64Code(typ, false)
|
||||
case reflect.Uintptr:
|
||||
return c.uintCode(typ, false)
|
||||
case reflect.Float32:
|
||||
return c.float32Code(typ, false)
|
||||
case reflect.Float64:
|
||||
return c.float64Code(typ, false)
|
||||
case reflect.String:
|
||||
return c.stringCode(typ, false)
|
||||
case reflect.Bool:
|
||||
return c.boolCode(typ, false)
|
||||
}
|
||||
return nil, &errors.UnsupportedTypeError{Type: runtime.RType2Type(typ)}
|
||||
}
|
||||
|
||||
const intSize = 32 << (^uint(0) >> 63)
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) intCode(typ *runtime.Type, isPtr bool) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: intSize, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) int8Code(typ *runtime.Type, isPtr bool) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: 8, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) int16Code(typ *runtime.Type, isPtr bool) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: 16, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) int32Code(typ *runtime.Type, isPtr bool) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: 32, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) int64Code(typ *runtime.Type, isPtr bool) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: 64, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uintCode(typ *runtime.Type, isPtr bool) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: intSize, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uint8Code(typ *runtime.Type, isPtr bool) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: 8, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uint16Code(typ *runtime.Type, isPtr bool) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: 16, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uint32Code(typ *runtime.Type, isPtr bool) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: 32, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uint64Code(typ *runtime.Type, isPtr bool) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: 64, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) float32Code(typ *runtime.Type, isPtr bool) (*FloatCode, error) {
|
||||
return &FloatCode{typ: typ, bitSize: 32, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) float64Code(typ *runtime.Type, isPtr bool) (*FloatCode, error) {
|
||||
return &FloatCode{typ: typ, bitSize: 64, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) stringCode(typ *runtime.Type, isPtr bool) (*StringCode, error) {
|
||||
return &StringCode{typ: typ, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) boolCode(typ *runtime.Type, isPtr bool) (*BoolCode, error) {
|
||||
return &BoolCode{typ: typ, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) intStringCode(typ *runtime.Type) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: intSize, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) int8StringCode(typ *runtime.Type) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: 8, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) int16StringCode(typ *runtime.Type) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: 16, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) int32StringCode(typ *runtime.Type) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: 32, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) int64StringCode(typ *runtime.Type) (*IntCode, error) {
|
||||
return &IntCode{typ: typ, bitSize: 64, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uintStringCode(typ *runtime.Type) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: intSize, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uint8StringCode(typ *runtime.Type) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: 8, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uint16StringCode(typ *runtime.Type) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: 16, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uint32StringCode(typ *runtime.Type) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: 32, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) uint64StringCode(typ *runtime.Type) (*UintCode, error) {
|
||||
return &UintCode{typ: typ, bitSize: 64, isString: true}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) bytesCode(typ *runtime.Type, isPtr bool) (*BytesCode, error) {
|
||||
return &BytesCode{typ: typ, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) interfaceCode(typ *runtime.Type, isPtr bool) (*InterfaceCode, error) {
|
||||
return &InterfaceCode{typ: typ, isPtr: isPtr}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) marshalJSONCode(typ *runtime.Type) (*MarshalJSONCode, error) {
|
||||
return &MarshalJSONCode{
|
||||
typ: typ,
|
||||
isAddrForMarshaler: c.isPtrMarshalJSONType(typ),
|
||||
isNilableType: c.isNilableType(typ),
|
||||
isMarshalerContext: typ.Implements(marshalJSONContextType) || runtime.PtrTo(typ).Implements(marshalJSONContextType),
|
||||
}, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (c *Compiler) marshalTextCode(typ *runtime.Type) (*MarshalTextCode, error) {
|
||||
return &MarshalTextCode{
|
||||
typ: typ,
|
||||
isAddrForMarshaler: c.isPtrMarshalTextType(typ),
|
||||
isNilableType: c.isNilableType(typ),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) ptrCode(typ *runtime.Type) (*PtrCode, error) {
|
||||
code, err := c.typeToCodeWithPtr(typ.Elem(), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ptr, ok := code.(*PtrCode)
|
||||
if ok {
|
||||
return &PtrCode{typ: typ, value: ptr.value, ptrNum: ptr.ptrNum + 1}, nil
|
||||
}
|
||||
return &PtrCode{typ: typ, value: code, ptrNum: 1}, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) sliceCode(typ *runtime.Type) (*SliceCode, error) {
|
||||
elem := typ.Elem()
|
||||
code, err := c.listElemCode(elem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if code.Kind() == CodeKindStruct {
|
||||
structCode := code.(*StructCode)
|
||||
structCode.enableIndirect()
|
||||
}
|
||||
return &SliceCode{typ: typ, value: code}, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) arrayCode(typ *runtime.Type) (*ArrayCode, error) {
|
||||
elem := typ.Elem()
|
||||
code, err := c.listElemCode(elem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if code.Kind() == CodeKindStruct {
|
||||
structCode := code.(*StructCode)
|
||||
structCode.enableIndirect()
|
||||
}
|
||||
return &ArrayCode{typ: typ, value: code}, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) mapCode(typ *runtime.Type) (*MapCode, error) {
|
||||
keyCode, err := c.mapKeyCode(typ.Key())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueCode, err := c.mapValueCode(typ.Elem())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if valueCode.Kind() == CodeKindStruct {
|
||||
structCode := valueCode.(*StructCode)
|
||||
structCode.enableIndirect()
|
||||
}
|
||||
return &MapCode{typ: typ, key: keyCode, value: valueCode}, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) listElemCode(typ *runtime.Type) (Code, error) {
|
||||
switch {
|
||||
case c.implementsMarshalJSONType(typ) || c.implementsMarshalJSONType(runtime.PtrTo(typ)):
|
||||
return c.marshalJSONCode(typ)
|
||||
case !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType):
|
||||
return c.marshalTextCode(typ)
|
||||
case typ.Kind() == reflect.Map:
|
||||
return c.ptrCode(runtime.PtrTo(typ))
|
||||
default:
|
||||
// isPtr was originally used to indicate whether the type of top level is pointer.
|
||||
// However, since the slice/array element is a specification that can get the pointer address, explicitly set isPtr to true.
|
||||
// See here for related issues: https://github.com/goccy/go-json/issues/370
|
||||
code, err := c.typeToCodeWithPtr(typ, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ptr, ok := code.(*PtrCode)
|
||||
if ok {
|
||||
if ptr.value.Kind() == CodeKindMap {
|
||||
ptr.ptrNum++
|
||||
}
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) mapKeyCode(typ *runtime.Type) (Code, error) {
|
||||
switch {
|
||||
case c.implementsMarshalText(typ):
|
||||
return c.marshalTextCode(typ)
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
return c.ptrCode(typ)
|
||||
case reflect.String:
|
||||
return c.stringCode(typ, false)
|
||||
case reflect.Int:
|
||||
return c.intStringCode(typ)
|
||||
case reflect.Int8:
|
||||
return c.int8StringCode(typ)
|
||||
case reflect.Int16:
|
||||
return c.int16StringCode(typ)
|
||||
case reflect.Int32:
|
||||
return c.int32StringCode(typ)
|
||||
case reflect.Int64:
|
||||
return c.int64StringCode(typ)
|
||||
case reflect.Uint:
|
||||
return c.uintStringCode(typ)
|
||||
case reflect.Uint8:
|
||||
return c.uint8StringCode(typ)
|
||||
case reflect.Uint16:
|
||||
return c.uint16StringCode(typ)
|
||||
case reflect.Uint32:
|
||||
return c.uint32StringCode(typ)
|
||||
case reflect.Uint64:
|
||||
return c.uint64StringCode(typ)
|
||||
case reflect.Uintptr:
|
||||
return c.uintStringCode(typ)
|
||||
}
|
||||
return nil, &errors.UnsupportedTypeError{Type: runtime.RType2Type(typ)}
|
||||
}
|
||||
|
||||
func (c *Compiler) mapValueCode(typ *runtime.Type) (Code, error) {
|
||||
switch typ.Kind() {
|
||||
case reflect.Map:
|
||||
return c.ptrCode(runtime.PtrTo(typ))
|
||||
default:
|
||||
code, err := c.typeToCodeWithPtr(typ, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ptr, ok := code.(*PtrCode)
|
||||
if ok {
|
||||
if ptr.value.Kind() == CodeKindMap {
|
||||
ptr.ptrNum++
|
||||
}
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) structCode(typ *runtime.Type, isPtr bool) (*StructCode, error) {
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
if code, exists := c.structTypeToCode[typeptr]; exists {
|
||||
derefCode := *code
|
||||
derefCode.isRecursive = true
|
||||
return &derefCode, nil
|
||||
}
|
||||
indirect := runtime.IfaceIndir(typ)
|
||||
code := &StructCode{typ: typ, isPtr: isPtr, isIndirect: indirect}
|
||||
c.structTypeToCode[typeptr] = code
|
||||
|
||||
fieldNum := typ.NumField()
|
||||
tags := c.typeToStructTags(typ)
|
||||
fields := []*StructFieldCode{}
|
||||
for i, tag := range tags {
|
||||
isOnlyOneFirstField := i == 0 && fieldNum == 1
|
||||
field, err := c.structFieldCode(code, tag, isPtr, isOnlyOneFirstField)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field.isAnonymous {
|
||||
structCode := field.getAnonymousStruct()
|
||||
if structCode != nil {
|
||||
structCode.removeFieldsByTags(tags)
|
||||
if c.isAssignableIndirect(field, isPtr) {
|
||||
if indirect {
|
||||
structCode.isIndirect = true
|
||||
} else {
|
||||
structCode.isIndirect = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
structCode := field.getStruct()
|
||||
if structCode != nil {
|
||||
if indirect {
|
||||
// if parent is indirect type, set child indirect property to true
|
||||
structCode.isIndirect = true
|
||||
} else {
|
||||
// if parent is not indirect type, set child indirect property to false.
|
||||
// but if parent's indirect is false and isPtr is true, then indirect must be true.
|
||||
// Do this only if indirectConversion is enabled at the end of compileStruct.
|
||||
structCode.isIndirect = false
|
||||
}
|
||||
}
|
||||
}
|
||||
fields = append(fields, field)
|
||||
}
|
||||
fieldMap := c.getFieldMap(fields)
|
||||
duplicatedFieldMap := c.getDuplicatedFieldMap(fieldMap)
|
||||
code.fields = c.filteredDuplicatedFields(fields, duplicatedFieldMap)
|
||||
if !code.disableIndirectConversion && !indirect && isPtr {
|
||||
code.enableIndirect()
|
||||
}
|
||||
delete(c.structTypeToCode, typeptr)
|
||||
return code, nil
|
||||
}
|
||||
|
||||
func toElemType(t *runtime.Type) *runtime.Type {
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (c *Compiler) structFieldCode(structCode *StructCode, tag *runtime.StructTag, isPtr, isOnlyOneFirstField bool) (*StructFieldCode, error) {
|
||||
field := tag.Field
|
||||
fieldType := runtime.Type2RType(field.Type)
|
||||
isIndirectSpecialCase := isPtr && isOnlyOneFirstField
|
||||
fieldCode := &StructFieldCode{
|
||||
typ: fieldType,
|
||||
key: tag.Key,
|
||||
tag: tag,
|
||||
offset: field.Offset,
|
||||
isAnonymous: field.Anonymous && !tag.IsTaggedKey && toElemType(fieldType).Kind() == reflect.Struct,
|
||||
isTaggedKey: tag.IsTaggedKey,
|
||||
isNilableType: c.isNilableType(fieldType),
|
||||
isNilCheck: true,
|
||||
}
|
||||
switch {
|
||||
case c.isMovePointerPositionFromHeadToFirstMarshalJSONFieldCase(fieldType, isIndirectSpecialCase):
|
||||
code, err := c.marshalJSONCode(fieldType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldCode.value = code
|
||||
fieldCode.isAddrForMarshaler = true
|
||||
fieldCode.isNilCheck = false
|
||||
structCode.isIndirect = false
|
||||
structCode.disableIndirectConversion = true
|
||||
case c.isMovePointerPositionFromHeadToFirstMarshalTextFieldCase(fieldType, isIndirectSpecialCase):
|
||||
code, err := c.marshalTextCode(fieldType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldCode.value = code
|
||||
fieldCode.isAddrForMarshaler = true
|
||||
fieldCode.isNilCheck = false
|
||||
structCode.isIndirect = false
|
||||
structCode.disableIndirectConversion = true
|
||||
case isPtr && c.isPtrMarshalJSONType(fieldType):
|
||||
// *struct{ field T }
|
||||
// func (*T) MarshalJSON() ([]byte, error)
|
||||
code, err := c.marshalJSONCode(fieldType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldCode.value = code
|
||||
fieldCode.isAddrForMarshaler = true
|
||||
fieldCode.isNilCheck = false
|
||||
case isPtr && c.isPtrMarshalTextType(fieldType):
|
||||
// *struct{ field T }
|
||||
// func (*T) MarshalText() ([]byte, error)
|
||||
code, err := c.marshalTextCode(fieldType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldCode.value = code
|
||||
fieldCode.isAddrForMarshaler = true
|
||||
fieldCode.isNilCheck = false
|
||||
default:
|
||||
code, err := c.typeToCodeWithPtr(fieldType, isPtr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch code.Kind() {
|
||||
case CodeKindPtr, CodeKindInterface:
|
||||
fieldCode.isNextOpPtrType = true
|
||||
}
|
||||
fieldCode.value = code
|
||||
}
|
||||
return fieldCode, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) isAssignableIndirect(fieldCode *StructFieldCode, isPtr bool) bool {
|
||||
if isPtr {
|
||||
return false
|
||||
}
|
||||
codeType := fieldCode.value.Kind()
|
||||
if codeType == CodeKindMarshalJSON {
|
||||
return false
|
||||
}
|
||||
if codeType == CodeKindMarshalText {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Compiler) getFieldMap(fields []*StructFieldCode) map[string][]*StructFieldCode {
|
||||
fieldMap := map[string][]*StructFieldCode{}
|
||||
for _, field := range fields {
|
||||
if field.isAnonymous {
|
||||
for k, v := range c.getAnonymousFieldMap(field) {
|
||||
fieldMap[k] = append(fieldMap[k], v...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
fieldMap[field.key] = append(fieldMap[field.key], field)
|
||||
}
|
||||
return fieldMap
|
||||
}
|
||||
|
||||
func (c *Compiler) getAnonymousFieldMap(field *StructFieldCode) map[string][]*StructFieldCode {
|
||||
fieldMap := map[string][]*StructFieldCode{}
|
||||
structCode := field.getAnonymousStruct()
|
||||
if structCode == nil || structCode.isRecursive {
|
||||
fieldMap[field.key] = append(fieldMap[field.key], field)
|
||||
return fieldMap
|
||||
}
|
||||
for k, v := range c.getFieldMapFromAnonymousParent(structCode.fields) {
|
||||
fieldMap[k] = append(fieldMap[k], v...)
|
||||
}
|
||||
return fieldMap
|
||||
}
|
||||
|
||||
func (c *Compiler) getFieldMapFromAnonymousParent(fields []*StructFieldCode) map[string][]*StructFieldCode {
|
||||
fieldMap := map[string][]*StructFieldCode{}
|
||||
for _, field := range fields {
|
||||
if field.isAnonymous {
|
||||
for k, v := range c.getAnonymousFieldMap(field) {
|
||||
// Do not handle tagged key when embedding more than once
|
||||
for _, vv := range v {
|
||||
vv.isTaggedKey = false
|
||||
}
|
||||
fieldMap[k] = append(fieldMap[k], v...)
|
||||
}
|
||||
continue
|
||||
}
|
||||
fieldMap[field.key] = append(fieldMap[field.key], field)
|
||||
}
|
||||
return fieldMap
|
||||
}
|
||||
|
||||
func (c *Compiler) getDuplicatedFieldMap(fieldMap map[string][]*StructFieldCode) map[*StructFieldCode]struct{} {
|
||||
duplicatedFieldMap := map[*StructFieldCode]struct{}{}
|
||||
for _, fields := range fieldMap {
|
||||
if len(fields) == 1 {
|
||||
continue
|
||||
}
|
||||
if c.isTaggedKeyOnly(fields) {
|
||||
for _, field := range fields {
|
||||
if field.isTaggedKey {
|
||||
continue
|
||||
}
|
||||
duplicatedFieldMap[field] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
for _, field := range fields {
|
||||
duplicatedFieldMap[field] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
return duplicatedFieldMap
|
||||
}
|
||||
|
||||
func (c *Compiler) filteredDuplicatedFields(fields []*StructFieldCode, duplicatedFieldMap map[*StructFieldCode]struct{}) []*StructFieldCode {
|
||||
filteredFields := make([]*StructFieldCode, 0, len(fields))
|
||||
for _, field := range fields {
|
||||
if field.isAnonymous {
|
||||
structCode := field.getAnonymousStruct()
|
||||
if structCode != nil && !structCode.isRecursive {
|
||||
structCode.fields = c.filteredDuplicatedFields(structCode.fields, duplicatedFieldMap)
|
||||
if len(structCode.fields) > 0 {
|
||||
filteredFields = append(filteredFields, field)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
if _, exists := duplicatedFieldMap[field]; exists {
|
||||
continue
|
||||
}
|
||||
filteredFields = append(filteredFields, field)
|
||||
}
|
||||
return filteredFields
|
||||
}
|
||||
|
||||
func (c *Compiler) isTaggedKeyOnly(fields []*StructFieldCode) bool {
|
||||
var taggedKeyFieldCount int
|
||||
for _, field := range fields {
|
||||
if field.isTaggedKey {
|
||||
taggedKeyFieldCount++
|
||||
}
|
||||
}
|
||||
return taggedKeyFieldCount == 1
|
||||
}
|
||||
|
||||
func (c *Compiler) typeToStructTags(typ *runtime.Type) runtime.StructTags {
|
||||
tags := runtime.StructTags{}
|
||||
fieldNum := typ.NumField()
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
field := typ.Field(i)
|
||||
if runtime.IsIgnoredStructField(field) {
|
||||
continue
|
||||
}
|
||||
tags = append(tags, runtime.StructTagFromField(field))
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
// *struct{ field T } => struct { field *T }
|
||||
// func (*T) MarshalJSON() ([]byte, error)
|
||||
func (c *Compiler) isMovePointerPositionFromHeadToFirstMarshalJSONFieldCase(typ *runtime.Type, isIndirectSpecialCase bool) bool {
|
||||
return isIndirectSpecialCase && !c.isNilableType(typ) && c.isPtrMarshalJSONType(typ)
|
||||
}
|
||||
|
||||
// *struct{ field T } => struct { field *T }
|
||||
// func (*T) MarshalText() ([]byte, error)
|
||||
func (c *Compiler) isMovePointerPositionFromHeadToFirstMarshalTextFieldCase(typ *runtime.Type, isIndirectSpecialCase bool) bool {
|
||||
return isIndirectSpecialCase && !c.isNilableType(typ) && c.isPtrMarshalTextType(typ)
|
||||
}
|
||||
|
||||
func (c *Compiler) implementsMarshalJSON(typ *runtime.Type) bool {
|
||||
if !c.implementsMarshalJSONType(typ) {
|
||||
return false
|
||||
}
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
return true
|
||||
}
|
||||
// type kind is reflect.Ptr
|
||||
if !c.implementsMarshalJSONType(typ.Elem()) {
|
||||
return true
|
||||
}
|
||||
// needs to dereference
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Compiler) implementsMarshalText(typ *runtime.Type) bool {
|
||||
if !typ.Implements(marshalTextType) {
|
||||
return false
|
||||
}
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
return true
|
||||
}
|
||||
// type kind is reflect.Ptr
|
||||
if !typ.Elem().Implements(marshalTextType) {
|
||||
return true
|
||||
}
|
||||
// needs to dereference
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Compiler) isNilableType(typ *runtime.Type) bool {
|
||||
if !runtime.IfaceIndir(typ) {
|
||||
return true
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
return true
|
||||
case reflect.Map:
|
||||
return true
|
||||
case reflect.Func:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) implementsMarshalJSONType(typ *runtime.Type) bool {
|
||||
return typ.Implements(marshalJSONType) || typ.Implements(marshalJSONContextType)
|
||||
}
|
||||
|
||||
func (c *Compiler) isPtrMarshalJSONType(typ *runtime.Type) bool {
|
||||
return !c.implementsMarshalJSONType(typ) && c.implementsMarshalJSONType(runtime.PtrTo(typ))
|
||||
}
|
||||
|
||||
func (c *Compiler) isPtrMarshalTextType(typ *runtime.Type) bool {
|
||||
return !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType)
|
||||
}
|
||||
|
||||
func (c *Compiler) codeToOpcode(ctx *compileContext, typ *runtime.Type, code Code) *Opcode {
|
||||
codes := code.ToOpcode(ctx)
|
||||
codes.Last().Next = newEndOp(ctx, typ)
|
||||
c.linkRecursiveCode(ctx)
|
||||
return codes.First()
|
||||
}
|
||||
|
||||
func (c *Compiler) linkRecursiveCode(ctx *compileContext) {
|
||||
recursiveCodes := map[uintptr]*CompiledCode{}
|
||||
for _, recursive := range *ctx.recursiveCodes {
|
||||
typeptr := uintptr(unsafe.Pointer(recursive.Type))
|
||||
codes := ctx.structTypeToCodes[typeptr]
|
||||
if recursiveCode, ok := recursiveCodes[typeptr]; ok {
|
||||
*recursive.Jmp = *recursiveCode
|
||||
continue
|
||||
}
|
||||
|
||||
code := copyOpcode(codes.First())
|
||||
code.Op = code.Op.PtrHeadToHead()
|
||||
lastCode := newEndOp(&compileContext{}, recursive.Type)
|
||||
lastCode.Op = OpRecursiveEnd
|
||||
|
||||
// OpRecursiveEnd must set before call TotalLength
|
||||
code.End.Next = lastCode
|
||||
|
||||
totalLength := code.TotalLength()
|
||||
|
||||
// Idx, ElemIdx, Length must set after call TotalLength
|
||||
lastCode.Idx = uint32((totalLength + 1) * uintptrSize)
|
||||
lastCode.ElemIdx = lastCode.Idx + uintptrSize
|
||||
lastCode.Length = lastCode.Idx + 2*uintptrSize
|
||||
|
||||
// extend length to alloc slot for elemIdx + length
|
||||
curTotalLength := uintptr(recursive.TotalLength()) + 3
|
||||
nextTotalLength := uintptr(totalLength) + 3
|
||||
|
||||
compiled := recursive.Jmp
|
||||
compiled.Code = code
|
||||
compiled.CurLen = curTotalLength
|
||||
compiled.NextLen = nextTotalLength
|
||||
compiled.Linked = true
|
||||
|
||||
recursiveCodes[typeptr] = compiled
|
||||
}
|
||||
}
|
33
internal/encoder/compiler_norace.go
Normal file
33
internal/encoder/compiler_norace.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
package encoder
|
||||
|
||||
func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) {
|
||||
initEncoder()
|
||||
if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr {
|
||||
codeSet, err := compileToGetCodeSetSlowPath(typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
}
|
||||
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
|
||||
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
|
||||
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return filtered, nil
|
||||
}
|
||||
codeSet, err := newCompiler().compile(typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cachedOpcodeSets[index] = codeSet
|
||||
return filtered, nil
|
||||
}
|
46
internal/encoder/compiler_race.go
Normal file
46
internal/encoder/compiler_race.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
//go:build race
|
||||
// +build race
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var setsMu sync.RWMutex
|
||||
|
||||
func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) {
|
||||
initEncoder()
|
||||
if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr {
|
||||
codeSet, err := compileToGetCodeSetSlowPath(typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
}
|
||||
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
|
||||
setsMu.RLock()
|
||||
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
|
||||
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
if err != nil {
|
||||
setsMu.RUnlock()
|
||||
return nil, err
|
||||
}
|
||||
setsMu.RUnlock()
|
||||
return filtered, nil
|
||||
}
|
||||
setsMu.RUnlock()
|
||||
|
||||
codeSet, err := newCompiler().compile(typeptr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setsMu.Lock()
|
||||
cachedOpcodeSets[index] = codeSet
|
||||
setsMu.Unlock()
|
||||
return filtered, nil
|
||||
}
|
105
internal/encoder/context.go
Normal file
105
internal/encoder/context.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type compileContext struct {
|
||||
opcodeIndex uint32
|
||||
ptrIndex int
|
||||
indent uint32
|
||||
escapeKey bool
|
||||
structTypeToCodes map[uintptr]Opcodes
|
||||
recursiveCodes *Opcodes
|
||||
}
|
||||
|
||||
func (c *compileContext) incIndent() {
|
||||
c.indent++
|
||||
}
|
||||
|
||||
func (c *compileContext) decIndent() {
|
||||
c.indent--
|
||||
}
|
||||
|
||||
func (c *compileContext) incIndex() {
|
||||
c.incOpcodeIndex()
|
||||
c.incPtrIndex()
|
||||
}
|
||||
|
||||
func (c *compileContext) decIndex() {
|
||||
c.decOpcodeIndex()
|
||||
c.decPtrIndex()
|
||||
}
|
||||
|
||||
func (c *compileContext) incOpcodeIndex() {
|
||||
c.opcodeIndex++
|
||||
}
|
||||
|
||||
func (c *compileContext) decOpcodeIndex() {
|
||||
c.opcodeIndex--
|
||||
}
|
||||
|
||||
func (c *compileContext) incPtrIndex() {
|
||||
c.ptrIndex++
|
||||
}
|
||||
|
||||
func (c *compileContext) decPtrIndex() {
|
||||
c.ptrIndex--
|
||||
}
|
||||
|
||||
const (
|
||||
bufSize = 1024
|
||||
)
|
||||
|
||||
var (
|
||||
runtimeContextPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &RuntimeContext{
|
||||
Buf: make([]byte, 0, bufSize),
|
||||
Ptrs: make([]uintptr, 128),
|
||||
KeepRefs: make([]unsafe.Pointer, 0, 8),
|
||||
Option: &Option{},
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type RuntimeContext struct {
|
||||
Context context.Context
|
||||
Buf []byte
|
||||
MarshalBuf []byte
|
||||
Ptrs []uintptr
|
||||
KeepRefs []unsafe.Pointer
|
||||
SeenPtr []uintptr
|
||||
BaseIndent uint32
|
||||
Prefix []byte
|
||||
IndentStr []byte
|
||||
Option *Option
|
||||
}
|
||||
|
||||
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 TakeRuntimeContext() *RuntimeContext {
|
||||
return runtimeContextPool.Get().(*RuntimeContext)
|
||||
}
|
||||
|
||||
func ReleaseRuntimeContext(ctx *RuntimeContext) {
|
||||
runtimeContextPool.Put(ctx)
|
||||
}
|
126
internal/encoder/decode_rune.go
Normal file
126
internal/encoder/decode_rune.go
Normal file
|
@ -0,0 +1,126 @@
|
|||
package encoder
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
const (
|
||||
// The default lowest and highest continuation byte.
|
||||
locb = 128 //0b10000000
|
||||
hicb = 191 //0b10111111
|
||||
|
||||
// These names of these constants are chosen to give nice alignment in the
|
||||
// table below. The first nibble is an index into acceptRanges or F for
|
||||
// special one-byte cases. The second nibble is the Rune length or the
|
||||
// Status for the special one-byte case.
|
||||
xx = 0xF1 // invalid: size 1
|
||||
as = 0xF0 // ASCII: size 1
|
||||
s1 = 0x02 // accept 0, size 2
|
||||
s2 = 0x13 // accept 1, size 3
|
||||
s3 = 0x03 // accept 0, size 3
|
||||
s4 = 0x23 // accept 2, size 3
|
||||
s5 = 0x34 // accept 3, size 4
|
||||
s6 = 0x04 // accept 0, size 4
|
||||
s7 = 0x44 // accept 4, size 4
|
||||
)
|
||||
|
||||
// first is information about the first byte in a UTF-8 sequence.
|
||||
var first = [256]uint8{
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF
|
||||
xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF
|
||||
s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF
|
||||
s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF
|
||||
s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF
|
||||
}
|
||||
|
||||
const (
|
||||
lineSep = byte(168) //'\u2028'
|
||||
paragraphSep = byte(169) //'\u2029'
|
||||
)
|
||||
|
||||
type decodeRuneState int
|
||||
|
||||
const (
|
||||
validUTF8State decodeRuneState = iota
|
||||
runeErrorState
|
||||
lineSepState
|
||||
paragraphSepState
|
||||
)
|
||||
|
||||
func decodeRuneInString(s string) (decodeRuneState, int) {
|
||||
n := len(s)
|
||||
s0 := s[0]
|
||||
x := first[s0]
|
||||
if x >= as {
|
||||
// The following code simulates an additional check for x == xx and
|
||||
// handling the ASCII and invalid cases accordingly. This mask-and-or
|
||||
// approach prevents an additional branch.
|
||||
mask := rune(x) << 31 >> 31 // Create 0x0000 or 0xFFFF.
|
||||
if rune(s[0])&^mask|utf8.RuneError&mask == utf8.RuneError {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
return validUTF8State, 1
|
||||
}
|
||||
sz := int(x & 7)
|
||||
if n < sz {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
s1 := s[1]
|
||||
switch x >> 4 {
|
||||
case 0:
|
||||
if s1 < locb || hicb < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
case 1:
|
||||
if s1 < 0xA0 || hicb < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
case 2:
|
||||
if s1 < locb || 0x9F < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
case 3:
|
||||
if s1 < 0x90 || hicb < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
case 4:
|
||||
if s1 < locb || 0x8F < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
}
|
||||
if sz <= 2 {
|
||||
return validUTF8State, 2
|
||||
}
|
||||
s2 := s[2]
|
||||
if s2 < locb || hicb < s2 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
if sz <= 3 {
|
||||
// separator character prefixes: [2]byte{226, 128}
|
||||
if s0 == 226 && s1 == 128 {
|
||||
switch s2 {
|
||||
case lineSep:
|
||||
return lineSepState, 3
|
||||
case paragraphSep:
|
||||
return paragraphSepState, 3
|
||||
}
|
||||
}
|
||||
return validUTF8State, 3
|
||||
}
|
||||
s3 := s[3]
|
||||
if s3 < locb || hicb < s3 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
return validUTF8State, 4
|
||||
}
|
20
internal/encoder/encode_opcode_test.go
Normal file
20
internal/encoder/encode_opcode_test.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestDumpOpcode(t *testing.T) {
|
||||
ctx := TakeRuntimeContext()
|
||||
defer ReleaseRuntimeContext(ctx)
|
||||
var v interface{} = 1
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
typ := header.typ
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
codeSet, err := CompileToGetCodeSet(ctx, typeptr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codeSet.EscapeKeyCode.Dump()
|
||||
}
|
601
internal/encoder/encoder.go
Normal file
601
internal/encoder/encoder.go
Normal file
|
@ -0,0 +1,601 @@
|
|||
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"
|
||||
)
|
||||
|
||||
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 OpStructHeadOmitEmptyArray:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyMap:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyStruct:
|
||||
return true
|
||||
case OpStructHeadSlicePtr:
|
||||
return true
|
||||
case OpStructHeadOmitEmptySlicePtr:
|
||||
return true
|
||||
case OpStructHeadArrayPtr:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyArrayPtr:
|
||||
return true
|
||||
case OpStructHeadMapPtr:
|
||||
return true
|
||||
case OpStructHeadOmitEmptyMapPtr:
|
||||
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 OpStructFieldOmitEmptyArray:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyMap:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyStruct:
|
||||
return true
|
||||
case OpStructFieldSlicePtr:
|
||||
return true
|
||||
case OpStructFieldOmitEmptySlicePtr:
|
||||
return true
|
||||
case OpStructFieldArrayPtr:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyArrayPtr:
|
||||
return true
|
||||
case OpStructFieldMapPtr:
|
||||
return true
|
||||
case OpStructFieldOmitEmptyMapPtr:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type OpcodeSet struct {
|
||||
Type *runtime.Type
|
||||
NoescapeKeyCode *Opcode
|
||||
EscapeKeyCode *Opcode
|
||||
InterfaceNoescapeKeyCode *Opcode
|
||||
InterfaceEscapeKeyCode *Opcode
|
||||
CodeLength int
|
||||
EndCode *Opcode
|
||||
Code Code
|
||||
QueryCache map[string]*OpcodeSet
|
||||
cacheMu sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *OpcodeSet) getQueryCache(hash string) *OpcodeSet {
|
||||
s.cacheMu.RLock()
|
||||
codeSet := s.QueryCache[hash]
|
||||
s.cacheMu.RUnlock()
|
||||
return codeSet
|
||||
}
|
||||
|
||||
func (s *OpcodeSet) setQueryCache(hash string, codeSet *OpcodeSet) {
|
||||
s.cacheMu.Lock()
|
||||
s.QueryCache[hash] = codeSet
|
||||
s.cacheMu.Unlock()
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
//nolint:structcheck,unused
|
||||
type mapIter struct {
|
||||
key unsafe.Pointer
|
||||
elem unsafe.Pointer
|
||||
t unsafe.Pointer
|
||||
h unsafe.Pointer
|
||||
buckets unsafe.Pointer
|
||||
bptr unsafe.Pointer
|
||||
overflow unsafe.Pointer
|
||||
oldoverflow unsafe.Pointer
|
||||
startBucket uintptr
|
||||
offset uint8
|
||||
wrapped bool
|
||||
B uint8
|
||||
i uint8
|
||||
bucket uintptr
|
||||
checkBucket uintptr
|
||||
}
|
||||
|
||||
type MapContext struct {
|
||||
Start int
|
||||
First int
|
||||
Idx int
|
||||
Slice *Mapslice
|
||||
Buf []byte
|
||||
Len int
|
||||
Iter mapIter
|
||||
}
|
||||
|
||||
var mapContextPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &MapContext{
|
||||
Slice: &Mapslice{},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func NewMapContext(mapLen int, unorderedMap bool) *MapContext {
|
||||
ctx := mapContextPool.Get().(*MapContext)
|
||||
if !unorderedMap {
|
||||
if len(ctx.Slice.Items) < mapLen {
|
||||
ctx.Slice.Items = make([]MapItem, mapLen)
|
||||
} else {
|
||||
ctx.Slice.Items = ctx.Slice.Items[:mapLen]
|
||||
}
|
||||
}
|
||||
ctx.Buf = ctx.Buf[:0]
|
||||
ctx.Iter = mapIter{}
|
||||
ctx.Idx = 0
|
||||
ctx.Len = mapLen
|
||||
return ctx
|
||||
}
|
||||
|
||||
func ReleaseMapContext(c *MapContext) {
|
||||
mapContextPool.Put(c)
|
||||
}
|
||||
|
||||
//go:linkname MapIterInit runtime.mapiterinit
|
||||
//go:noescape
|
||||
func MapIterInit(mapType *runtime.Type, m unsafe.Pointer, it *mapIter)
|
||||
|
||||
//go:linkname MapIterKey reflect.mapiterkey
|
||||
//go:noescape
|
||||
func MapIterKey(it *mapIter) unsafe.Pointer
|
||||
|
||||
//go:linkname MapIterNext reflect.mapiternext
|
||||
//go:noescape
|
||||
func MapIterNext(it *mapIter)
|
||||
|
||||
//go:linkname MapLen reflect.maplen
|
||||
//go:noescape
|
||||
func MapLen(m unsafe.Pointer) int
|
||||
|
||||
func AppendByteSlice(_ *RuntimeContext, 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(_ *RuntimeContext, 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(_ *RuntimeContext, 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(_ *RuntimeContext, 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(_ *RuntimeContext, 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(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if (code.Flags & AddrForMarshalerFlags) != 0 {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
|
||||
if rv.Kind() == reflect.Ptr && rv.IsNil() {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
|
||||
v = rv.Interface()
|
||||
var bb []byte
|
||||
if (code.Flags & MarshalerContextFlags) != 0 {
|
||||
marshaler, ok := v.(marshalerContext)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
stdctx := ctx.Option.Context
|
||||
if ctx.Option.Flag&FieldQueryOption != 0 {
|
||||
stdctx = SetFieldQueryToContext(stdctx, code.FieldQuery)
|
||||
}
|
||||
b, err := marshaler.MarshalJSON(stdctx)
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
bb = b
|
||||
} else {
|
||||
marshaler, ok := v.(json.Marshaler)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
b, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
bb = b
|
||||
}
|
||||
marshalBuf := ctx.MarshalBuf[:0]
|
||||
marshalBuf = append(append(marshalBuf, bb...), nul)
|
||||
compactedBuf, err := compact(b, marshalBuf, (ctx.Option.Flag&HTMLEscapeOption) != 0)
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
ctx.MarshalBuf = marshalBuf
|
||||
return compactedBuf, nil
|
||||
}
|
||||
|
||||
func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if (code.Flags & AddrForMarshalerFlags) != 0 {
|
||||
if rv.CanAddr() {
|
||||
rv = rv.Addr()
|
||||
} else {
|
||||
newV := reflect.New(rv.Type())
|
||||
newV.Elem().Set(rv)
|
||||
rv = newV
|
||||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
var bb []byte
|
||||
if (code.Flags & MarshalerContextFlags) != 0 {
|
||||
marshaler, ok := v.(marshalerContext)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
b, err := marshaler.MarshalJSON(ctx.Option.Context)
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
bb = b
|
||||
} else {
|
||||
marshaler, ok := v.(json.Marshaler)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
b, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
bb = b
|
||||
}
|
||||
marshalBuf := ctx.MarshalBuf[:0]
|
||||
marshalBuf = append(append(marshalBuf, bb...), nul)
|
||||
indentedBuf, err := doIndent(
|
||||
b,
|
||||
marshalBuf,
|
||||
string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), int(ctx.BaseIndent+code.Indent)),
|
||||
string(ctx.IndentStr),
|
||||
(ctx.Option.Flag&HTMLEscapeOption) != 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
ctx.MarshalBuf = marshalBuf
|
||||
return indentedBuf, nil
|
||||
}
|
||||
|
||||
func AppendMarshalText(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if (code.Flags & AddrForMarshalerFlags) != 0 {
|
||||
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(ctx, b), nil
|
||||
}
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
return AppendString(ctx, b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
|
||||
func AppendMarshalTextIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
rv := reflect.ValueOf(v) // convert by dynamic interface type
|
||||
if (code.Flags & AddrForMarshalerFlags) != 0 {
|
||||
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(ctx, b), nil
|
||||
}
|
||||
bytes, err := marshaler.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
return AppendString(ctx, b, *(*string)(unsafe.Pointer(&bytes))), nil
|
||||
}
|
||||
|
||||
func AppendNull(_ *RuntimeContext, b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func AppendComma(_ *RuntimeContext, b []byte) []byte {
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func AppendCommaIndent(_ *RuntimeContext, b []byte) []byte {
|
||||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func AppendStructEnd(_ *RuntimeContext, b []byte) []byte {
|
||||
return append(b, '}', ',')
|
||||
}
|
||||
|
||||
func AppendStructEndIndent(ctx *RuntimeContext, code *Opcode, b []byte) []byte {
|
||||
b = append(b, '\n')
|
||||
b = append(b, ctx.Prefix...)
|
||||
indentNum := ctx.BaseIndent + code.Indent - 1
|
||||
for i := uint32(0); i < indentNum; i++ {
|
||||
b = append(b, ctx.IndentStr...)
|
||||
}
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func AppendIndent(ctx *RuntimeContext, b []byte, indent uint32) []byte {
|
||||
b = append(b, ctx.Prefix...)
|
||||
indentNum := ctx.BaseIndent + indent
|
||||
for i := uint32(0); i < indentNum; i++ {
|
||||
b = append(b, ctx.IndentStr...)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func IsNilForMarshaler(v interface{}) bool {
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Bool:
|
||||
return !rv.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return rv.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return math.Float64bits(rv.Float()) == 0
|
||||
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Func:
|
||||
return rv.IsNil()
|
||||
case reflect.Slice:
|
||||
return rv.IsNil() || rv.Len() == 0
|
||||
case reflect.String:
|
||||
return rv.Len() == 0
|
||||
}
|
||||
return false
|
||||
}
|
211
internal/encoder/indent.go
Normal file
211
internal/encoder/indent.go
Normal file
|
@ -0,0 +1,211 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
)
|
||||
|
||||
func takeIndentSrcRuntimeContext(src []byte) (*RuntimeContext, []byte) {
|
||||
ctx := TakeRuntimeContext()
|
||||
buf := ctx.Buf[:0]
|
||||
buf = append(append(buf, src...), nul)
|
||||
ctx.Buf = buf
|
||||
return ctx, buf
|
||||
}
|
||||
|
||||
func Indent(buf *bytes.Buffer, src []byte, prefix, indentStr string) error {
|
||||
if len(src) == 0 {
|
||||
return errors.ErrUnexpectedEndOfJSON("", 0)
|
||||
}
|
||||
|
||||
srcCtx, srcBuf := takeIndentSrcRuntimeContext(src)
|
||||
dstCtx := TakeRuntimeContext()
|
||||
dst := dstCtx.Buf[:0]
|
||||
|
||||
dst, err := indentAndWrite(buf, dst, srcBuf, prefix, indentStr)
|
||||
if err != nil {
|
||||
ReleaseRuntimeContext(srcCtx)
|
||||
ReleaseRuntimeContext(dstCtx)
|
||||
return err
|
||||
}
|
||||
dstCtx.Buf = dst
|
||||
ReleaseRuntimeContext(srcCtx)
|
||||
ReleaseRuntimeContext(dstCtx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func indentAndWrite(buf *bytes.Buffer, dst []byte, src []byte, prefix, indentStr string) ([]byte, error) {
|
||||
dst, err := doIndent(dst, src, prefix, indentStr, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := buf.Write(dst); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func doIndent(dst, src []byte, prefix, indentStr string, escape bool) ([]byte, error) {
|
||||
buf, cursor, err := indentValue(dst, src, 0, 0, []byte(prefix), []byte(indentStr), escape)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateEndBuf(src, cursor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func indentValue(
|
||||
dst []byte,
|
||||
src []byte,
|
||||
indentNum int,
|
||||
cursor int64,
|
||||
prefix []byte,
|
||||
indentBytes []byte,
|
||||
escape bool) ([]byte, int64, error) {
|
||||
for {
|
||||
switch src[cursor] {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
cursor++
|
||||
continue
|
||||
case '{':
|
||||
return indentObject(dst, src, indentNum, cursor, prefix, indentBytes, escape)
|
||||
case '}':
|
||||
return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor)
|
||||
case '[':
|
||||
return indentArray(dst, src, indentNum, cursor, prefix, indentBytes, escape)
|
||||
case ']':
|
||||
return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor)
|
||||
case '"':
|
||||
return compactString(dst, src, cursor, escape)
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return compactNumber(dst, src, cursor)
|
||||
case 't':
|
||||
return compactTrue(dst, src, cursor)
|
||||
case 'f':
|
||||
return compactFalse(dst, src, cursor)
|
||||
case 'n':
|
||||
return compactNull(dst, src, cursor)
|
||||
default:
|
||||
return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func indentObject(
|
||||
dst []byte,
|
||||
src []byte,
|
||||
indentNum int,
|
||||
cursor int64,
|
||||
prefix []byte,
|
||||
indentBytes []byte,
|
||||
escape bool) ([]byte, int64, error) {
|
||||
if src[cursor] == '{' {
|
||||
dst = append(dst, '{')
|
||||
} else {
|
||||
return nil, 0, errors.ErrExpected("expected { character for object value", cursor)
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor+1)
|
||||
if src[cursor] == '}' {
|
||||
dst = append(dst, '}')
|
||||
return dst, cursor + 1, nil
|
||||
}
|
||||
indentNum++
|
||||
var err error
|
||||
for {
|
||||
dst = append(append(dst, '\n'), prefix...)
|
||||
for i := 0; i < indentNum; i++ {
|
||||
dst = append(dst, indentBytes...)
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor)
|
||||
dst, cursor, err = compactString(dst, src, cursor, escape)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor)
|
||||
if src[cursor] != ':' {
|
||||
return nil, 0, errors.ErrSyntax(
|
||||
fmt.Sprintf("invalid character '%c' after object key", src[cursor]),
|
||||
cursor+1,
|
||||
)
|
||||
}
|
||||
dst = append(dst, ':', ' ')
|
||||
dst, cursor, err = indentValue(dst, src, indentNum, cursor+1, prefix, indentBytes, escape)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor)
|
||||
switch src[cursor] {
|
||||
case '}':
|
||||
dst = append(append(dst, '\n'), prefix...)
|
||||
for i := 0; i < indentNum-1; i++ {
|
||||
dst = append(dst, indentBytes...)
|
||||
}
|
||||
dst = append(dst, '}')
|
||||
cursor++
|
||||
return dst, cursor, nil
|
||||
case ',':
|
||||
dst = append(dst, ',')
|
||||
default:
|
||||
return nil, 0, errors.ErrSyntax(
|
||||
fmt.Sprintf("invalid character '%c' after object key:value pair", src[cursor]),
|
||||
cursor+1,
|
||||
)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
}
|
||||
|
||||
func indentArray(
|
||||
dst []byte,
|
||||
src []byte,
|
||||
indentNum int,
|
||||
cursor int64,
|
||||
prefix []byte,
|
||||
indentBytes []byte,
|
||||
escape bool) ([]byte, int64, error) {
|
||||
if src[cursor] == '[' {
|
||||
dst = append(dst, '[')
|
||||
} else {
|
||||
return nil, 0, errors.ErrExpected("expected [ character for array value", cursor)
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor+1)
|
||||
if src[cursor] == ']' {
|
||||
dst = append(dst, ']')
|
||||
return dst, cursor + 1, nil
|
||||
}
|
||||
indentNum++
|
||||
var err error
|
||||
for {
|
||||
dst = append(append(dst, '\n'), prefix...)
|
||||
for i := 0; i < indentNum; i++ {
|
||||
dst = append(dst, indentBytes...)
|
||||
}
|
||||
dst, cursor, err = indentValue(dst, src, indentNum, cursor, prefix, indentBytes, escape)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor = skipWhiteSpace(src, cursor)
|
||||
switch src[cursor] {
|
||||
case ']':
|
||||
dst = append(append(dst, '\n'), prefix...)
|
||||
for i := 0; i < indentNum-1; i++ {
|
||||
dst = append(dst, indentBytes...)
|
||||
}
|
||||
dst = append(dst, ']')
|
||||
cursor++
|
||||
return dst, cursor, nil
|
||||
case ',':
|
||||
dst = append(dst, ',')
|
||||
default:
|
||||
return nil, 0, errors.ErrSyntax(
|
||||
fmt.Sprintf("invalid character '%c' after array value", src[cursor]),
|
||||
cursor+1,
|
||||
)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
}
|
176
internal/encoder/int.go
Normal file
176
internal/encoder/int.go
Normal file
|
@ -0,0 +1,176 @@
|
|||
// This files's processing codes are inspired by https://github.com/segmentio/encoding.
|
||||
// The license notation is as follows.
|
||||
//
|
||||
// # MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Segment.io, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var endianness int
|
||||
|
||||
func init() {
|
||||
var b [2]byte
|
||||
*(*uint16)(unsafe.Pointer(&b)) = uint16(0xABCD)
|
||||
|
||||
switch b[0] {
|
||||
case 0xCD:
|
||||
endianness = 0 // LE
|
||||
case 0xAB:
|
||||
endianness = 1 // BE
|
||||
default:
|
||||
panic("could not determine endianness")
|
||||
}
|
||||
}
|
||||
|
||||
// "00010203...96979899" cast to []uint16
|
||||
var intLELookup = [100]uint16{
|
||||
0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930,
|
||||
0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931,
|
||||
0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932,
|
||||
0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933,
|
||||
0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934,
|
||||
0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935,
|
||||
0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936,
|
||||
0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937,
|
||||
0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938,
|
||||
0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939,
|
||||
}
|
||||
|
||||
var intBELookup = [100]uint16{
|
||||
0x3030, 0x3031, 0x3032, 0x3033, 0x3034, 0x3035, 0x3036, 0x3037, 0x3038, 0x3039,
|
||||
0x3130, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3138, 0x3139,
|
||||
0x3230, 0x3231, 0x3232, 0x3233, 0x3234, 0x3235, 0x3236, 0x3237, 0x3238, 0x3239,
|
||||
0x3330, 0x3331, 0x3332, 0x3333, 0x3334, 0x3335, 0x3336, 0x3337, 0x3338, 0x3339,
|
||||
0x3430, 0x3431, 0x3432, 0x3433, 0x3434, 0x3435, 0x3436, 0x3437, 0x3438, 0x3439,
|
||||
0x3530, 0x3531, 0x3532, 0x3533, 0x3534, 0x3535, 0x3536, 0x3537, 0x3538, 0x3539,
|
||||
0x3630, 0x3631, 0x3632, 0x3633, 0x3634, 0x3635, 0x3636, 0x3637, 0x3638, 0x3639,
|
||||
0x3730, 0x3731, 0x3732, 0x3733, 0x3734, 0x3735, 0x3736, 0x3737, 0x3738, 0x3739,
|
||||
0x3830, 0x3831, 0x3832, 0x3833, 0x3834, 0x3835, 0x3836, 0x3837, 0x3838, 0x3839,
|
||||
0x3930, 0x3931, 0x3932, 0x3933, 0x3934, 0x3935, 0x3936, 0x3937, 0x3938, 0x3939,
|
||||
}
|
||||
|
||||
var intLookup = [2]*[100]uint16{&intLELookup, &intBELookup}
|
||||
|
||||
func numMask(numBitSize uint8) uint64 {
|
||||
return 1<<numBitSize - 1
|
||||
}
|
||||
|
||||
func AppendInt(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte {
|
||||
var u64 uint64
|
||||
switch code.NumBitSize {
|
||||
case 8:
|
||||
u64 = (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
u64 = (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
u64 = (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
u64 = **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
mask := numMask(code.NumBitSize)
|
||||
n := u64 & mask
|
||||
negative := (u64>>(code.NumBitSize-1))&1 == 1
|
||||
if !negative {
|
||||
if n < 10 {
|
||||
return append(out, byte(n+'0'))
|
||||
} else if n < 100 {
|
||||
u := intLELookup[n]
|
||||
return append(out, byte(u), byte(u>>8))
|
||||
}
|
||||
} else {
|
||||
n = -n & mask
|
||||
}
|
||||
|
||||
lookup := intLookup[endianness]
|
||||
|
||||
var b [22]byte
|
||||
u := (*[11]uint16)(unsafe.Pointer(&b))
|
||||
i := 11
|
||||
|
||||
for n >= 100 {
|
||||
j := n % 100
|
||||
n /= 100
|
||||
i--
|
||||
u[i] = lookup[j]
|
||||
}
|
||||
|
||||
i--
|
||||
u[i] = lookup[n]
|
||||
|
||||
i *= 2 // convert to byte index
|
||||
if n < 10 {
|
||||
i++ // remove leading zero
|
||||
}
|
||||
if negative {
|
||||
i--
|
||||
b[i] = '-'
|
||||
}
|
||||
|
||||
return append(out, b[i:]...)
|
||||
}
|
||||
|
||||
func AppendUint(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte {
|
||||
var u64 uint64
|
||||
switch code.NumBitSize {
|
||||
case 8:
|
||||
u64 = (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
u64 = (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
u64 = (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
u64 = **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
mask := numMask(code.NumBitSize)
|
||||
n := u64 & mask
|
||||
if n < 10 {
|
||||
return append(out, byte(n+'0'))
|
||||
} else if n < 100 {
|
||||
u := intLELookup[n]
|
||||
return append(out, byte(u), byte(u>>8))
|
||||
}
|
||||
|
||||
lookup := intLookup[endianness]
|
||||
|
||||
var b [22]byte
|
||||
u := (*[11]uint16)(unsafe.Pointer(&b))
|
||||
i := 11
|
||||
|
||||
for n >= 100 {
|
||||
j := n % 100
|
||||
n /= 100
|
||||
i--
|
||||
u[i] = lookup[j]
|
||||
}
|
||||
|
||||
i--
|
||||
u[i] = lookup[n]
|
||||
|
||||
i *= 2 // convert to byte index
|
||||
if n < 10 {
|
||||
i++ // remove leading zero
|
||||
}
|
||||
return append(out, b[i:]...)
|
||||
}
|
9
internal/encoder/map112.go
Normal file
9
internal/encoder/map112.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
//go:build !go1.13
|
||||
// +build !go1.13
|
||||
|
||||
package encoder
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:linkname MapIterValue reflect.mapitervalue
|
||||
func MapIterValue(it *mapIter) unsafe.Pointer
|
9
internal/encoder/map113.go
Normal file
9
internal/encoder/map113.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
package encoder
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:linkname MapIterValue reflect.mapiterelem
|
||||
func MapIterValue(it *mapIter) unsafe.Pointer
|
752
internal/encoder/opcode.go
Normal file
752
internal/encoder/opcode.go
Normal file
|
@ -0,0 +1,752 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||
|
||||
type OpFlags uint16
|
||||
|
||||
const (
|
||||
AnonymousHeadFlags OpFlags = 1 << 0
|
||||
AnonymousKeyFlags OpFlags = 1 << 1
|
||||
IndirectFlags OpFlags = 1 << 2
|
||||
IsTaggedKeyFlags OpFlags = 1 << 3
|
||||
NilCheckFlags OpFlags = 1 << 4
|
||||
AddrForMarshalerFlags OpFlags = 1 << 5
|
||||
IsNextOpPtrTypeFlags OpFlags = 1 << 6
|
||||
IsNilableTypeFlags OpFlags = 1 << 7
|
||||
MarshalerContextFlags OpFlags = 1 << 8
|
||||
NonEmptyInterfaceFlags OpFlags = 1 << 9
|
||||
)
|
||||
|
||||
type Opcode struct {
|
||||
Op OpType // operation type
|
||||
Idx uint32 // offset to access ptr
|
||||
Next *Opcode // next opcode
|
||||
End *Opcode // array/slice/struct/map end
|
||||
NextField *Opcode // next struct field
|
||||
Key string // struct field key
|
||||
Offset uint32 // offset size from struct header
|
||||
PtrNum uint8 // pointer number: e.g. double pointer is 2.
|
||||
NumBitSize uint8
|
||||
Flags OpFlags
|
||||
|
||||
Type *runtime.Type // go type
|
||||
Jmp *CompiledCode // for recursive call
|
||||
FieldQuery *FieldQuery // field query for Interface / MarshalJSON / MarshalText
|
||||
ElemIdx uint32 // offset to access array/slice elem
|
||||
Length uint32 // offset to access slice length or array length
|
||||
Indent uint32 // indent number
|
||||
Size uint32 // array/slice elem size
|
||||
DisplayIdx uint32 // opcode index
|
||||
DisplayKey string // key text to display
|
||||
}
|
||||
|
||||
func (c *Opcode) Validate() error {
|
||||
var prevIdx uint32
|
||||
for code := c; !code.IsEnd(); {
|
||||
if prevIdx != 0 {
|
||||
if code.DisplayIdx != prevIdx+1 {
|
||||
return fmt.Errorf(
|
||||
"invalid index. previous display index is %d but next is %d. dump = %s",
|
||||
prevIdx, code.DisplayIdx, c.Dump(),
|
||||
)
|
||||
}
|
||||
}
|
||||
prevIdx = code.DisplayIdx
|
||||
code = code.IterNext()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Opcode) IterNext() *Opcode {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
switch c.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
return c.End
|
||||
default:
|
||||
return c.Next
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Opcode) IsEnd() bool {
|
||||
if c == nil {
|
||||
return true
|
||||
}
|
||||
return c.Op == OpEnd || c.Op == OpInterfaceEnd || c.Op == OpRecursiveEnd
|
||||
}
|
||||
|
||||
func (c *Opcode) MaxIdx() uint32 {
|
||||
max := uint32(0)
|
||||
for _, value := range []uint32{
|
||||
c.Idx,
|
||||
c.ElemIdx,
|
||||
c.Length,
|
||||
c.Size,
|
||||
} {
|
||||
if max < value {
|
||||
max = value
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
func (c *Opcode) ToHeaderType(isString bool) OpType {
|
||||
switch c.Op {
|
||||
case OpInt:
|
||||
if isString {
|
||||
return OpStructHeadIntString
|
||||
}
|
||||
return OpStructHeadInt
|
||||
case OpIntPtr:
|
||||
if isString {
|
||||
return OpStructHeadIntPtrString
|
||||
}
|
||||
return OpStructHeadIntPtr
|
||||
case OpUint:
|
||||
if isString {
|
||||
return OpStructHeadUintString
|
||||
}
|
||||
return OpStructHeadUint
|
||||
case OpUintPtr:
|
||||
if isString {
|
||||
return OpStructHeadUintPtrString
|
||||
}
|
||||
return OpStructHeadUintPtr
|
||||
case OpFloat32:
|
||||
if isString {
|
||||
return OpStructHeadFloat32String
|
||||
}
|
||||
return OpStructHeadFloat32
|
||||
case OpFloat32Ptr:
|
||||
if isString {
|
||||
return OpStructHeadFloat32PtrString
|
||||
}
|
||||
return OpStructHeadFloat32Ptr
|
||||
case OpFloat64:
|
||||
if isString {
|
||||
return OpStructHeadFloat64String
|
||||
}
|
||||
return OpStructHeadFloat64
|
||||
case OpFloat64Ptr:
|
||||
if isString {
|
||||
return OpStructHeadFloat64PtrString
|
||||
}
|
||||
return OpStructHeadFloat64Ptr
|
||||
case OpString:
|
||||
if isString {
|
||||
return OpStructHeadStringString
|
||||
}
|
||||
return OpStructHeadString
|
||||
case OpStringPtr:
|
||||
if isString {
|
||||
return OpStructHeadStringPtrString
|
||||
}
|
||||
return OpStructHeadStringPtr
|
||||
case OpNumber:
|
||||
if isString {
|
||||
return OpStructHeadNumberString
|
||||
}
|
||||
return OpStructHeadNumber
|
||||
case OpNumberPtr:
|
||||
if isString {
|
||||
return OpStructHeadNumberPtrString
|
||||
}
|
||||
return OpStructHeadNumberPtr
|
||||
case OpBool:
|
||||
if isString {
|
||||
return OpStructHeadBoolString
|
||||
}
|
||||
return OpStructHeadBool
|
||||
case OpBoolPtr:
|
||||
if isString {
|
||||
return OpStructHeadBoolPtrString
|
||||
}
|
||||
return OpStructHeadBoolPtr
|
||||
case OpBytes:
|
||||
return OpStructHeadBytes
|
||||
case OpBytesPtr:
|
||||
return OpStructHeadBytesPtr
|
||||
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(isString bool) OpType {
|
||||
switch c.Op {
|
||||
case OpInt:
|
||||
if isString {
|
||||
return OpStructFieldIntString
|
||||
}
|
||||
return OpStructFieldInt
|
||||
case OpIntPtr:
|
||||
if isString {
|
||||
return OpStructFieldIntPtrString
|
||||
}
|
||||
return OpStructFieldIntPtr
|
||||
case OpUint:
|
||||
if isString {
|
||||
return OpStructFieldUintString
|
||||
}
|
||||
return OpStructFieldUint
|
||||
case OpUintPtr:
|
||||
if isString {
|
||||
return OpStructFieldUintPtrString
|
||||
}
|
||||
return OpStructFieldUintPtr
|
||||
case OpFloat32:
|
||||
if isString {
|
||||
return OpStructFieldFloat32String
|
||||
}
|
||||
return OpStructFieldFloat32
|
||||
case OpFloat32Ptr:
|
||||
if isString {
|
||||
return OpStructFieldFloat32PtrString
|
||||
}
|
||||
return OpStructFieldFloat32Ptr
|
||||
case OpFloat64:
|
||||
if isString {
|
||||
return OpStructFieldFloat64String
|
||||
}
|
||||
return OpStructFieldFloat64
|
||||
case OpFloat64Ptr:
|
||||
if isString {
|
||||
return OpStructFieldFloat64PtrString
|
||||
}
|
||||
return OpStructFieldFloat64Ptr
|
||||
case OpString:
|
||||
if isString {
|
||||
return OpStructFieldStringString
|
||||
}
|
||||
return OpStructFieldString
|
||||
case OpStringPtr:
|
||||
if isString {
|
||||
return OpStructFieldStringPtrString
|
||||
}
|
||||
return OpStructFieldStringPtr
|
||||
case OpNumber:
|
||||
if isString {
|
||||
return OpStructFieldNumberString
|
||||
}
|
||||
return OpStructFieldNumber
|
||||
case OpNumberPtr:
|
||||
if isString {
|
||||
return OpStructFieldNumberPtrString
|
||||
}
|
||||
return OpStructFieldNumberPtr
|
||||
case OpBool:
|
||||
if isString {
|
||||
return OpStructFieldBoolString
|
||||
}
|
||||
return OpStructFieldBool
|
||||
case OpBoolPtr:
|
||||
if isString {
|
||||
return OpStructFieldBoolPtrString
|
||||
}
|
||||
return OpStructFieldBoolPtr
|
||||
case OpBytes:
|
||||
return OpStructFieldBytes
|
||||
case OpBytesPtr:
|
||||
return OpStructFieldBytesPtr
|
||||
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, typ *runtime.Type, op OpType) *Opcode {
|
||||
return newOpCodeWithNext(ctx, typ, op, newEndOp(ctx, typ))
|
||||
}
|
||||
|
||||
func opcodeOffset(idx int) uint32 {
|
||||
return uint32(idx) * uintptrSize
|
||||
}
|
||||
|
||||
func getCodeAddrByIdx(head *Opcode, idx uint32) *Opcode {
|
||||
addr := uintptr(unsafe.Pointer(head)) + uintptr(idx)*unsafe.Sizeof(Opcode{})
|
||||
return *(**Opcode)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func copyOpcode(code *Opcode) *Opcode {
|
||||
codeNum := ToEndCode(code).DisplayIdx + 1
|
||||
codeSlice := make([]Opcode, codeNum)
|
||||
head := (*Opcode)((*runtime.SliceHeader)(unsafe.Pointer(&codeSlice)).Data)
|
||||
ptr := head
|
||||
c := code
|
||||
for {
|
||||
*ptr = Opcode{
|
||||
Op: c.Op,
|
||||
Key: c.Key,
|
||||
PtrNum: c.PtrNum,
|
||||
NumBitSize: c.NumBitSize,
|
||||
Flags: c.Flags,
|
||||
Idx: c.Idx,
|
||||
Offset: c.Offset,
|
||||
Type: c.Type,
|
||||
FieldQuery: c.FieldQuery,
|
||||
DisplayIdx: c.DisplayIdx,
|
||||
DisplayKey: c.DisplayKey,
|
||||
ElemIdx: c.ElemIdx,
|
||||
Length: c.Length,
|
||||
Size: c.Size,
|
||||
Indent: c.Indent,
|
||||
Jmp: c.Jmp,
|
||||
}
|
||||
if c.End != nil {
|
||||
ptr.End = getCodeAddrByIdx(head, c.End.DisplayIdx)
|
||||
}
|
||||
if c.NextField != nil {
|
||||
ptr.NextField = getCodeAddrByIdx(head, c.NextField.DisplayIdx)
|
||||
}
|
||||
if c.Next != nil {
|
||||
ptr.Next = getCodeAddrByIdx(head, c.Next.DisplayIdx)
|
||||
}
|
||||
if c.IsEnd() {
|
||||
break
|
||||
}
|
||||
ptr = getCodeAddrByIdx(head, c.DisplayIdx+1)
|
||||
c = c.IterNext()
|
||||
}
|
||||
return head
|
||||
}
|
||||
|
||||
func setTotalLengthToInterfaceOp(code *Opcode) {
|
||||
for c := code; !c.IsEnd(); {
|
||||
if c.Op == OpInterface || c.Op == OpInterfacePtr {
|
||||
c.Length = uint32(code.TotalLength())
|
||||
}
|
||||
c = c.IterNext()
|
||||
}
|
||||
}
|
||||
|
||||
func ToEndCode(code *Opcode) *Opcode {
|
||||
c := code
|
||||
for !c.IsEnd() {
|
||||
c = c.IterNext()
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func copyToInterfaceOpcode(code *Opcode) *Opcode {
|
||||
copied := copyOpcode(code)
|
||||
c := copied
|
||||
c = ToEndCode(c)
|
||||
c.Idx += uintptrSize
|
||||
c.ElemIdx = c.Idx + uintptrSize
|
||||
c.Length = c.Idx + 2*uintptrSize
|
||||
c.Op = OpInterfaceEnd
|
||||
return copied
|
||||
}
|
||||
|
||||
func newOpCodeWithNext(ctx *compileContext, typ *runtime.Type, op OpType, next *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: op,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
Next: next,
|
||||
Type: typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newEndOp(ctx *compileContext, typ *runtime.Type) *Opcode {
|
||||
return newOpCodeWithNext(ctx, typ, OpEnd, nil)
|
||||
}
|
||||
|
||||
func (c *Opcode) TotalLength() int {
|
||||
var idx int
|
||||
code := c
|
||||
for !code.IsEnd() {
|
||||
maxIdx := int(code.MaxIdx() / uintptrSize)
|
||||
if idx < maxIdx {
|
||||
idx = maxIdx
|
||||
}
|
||||
if code.Op == OpRecursiveEnd {
|
||||
break
|
||||
}
|
||||
code = code.IterNext()
|
||||
}
|
||||
maxIdx := int(code.MaxIdx() / uintptrSize)
|
||||
if idx < maxIdx {
|
||||
idx = maxIdx
|
||||
}
|
||||
return idx + 1
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpHead(code *Opcode) string {
|
||||
var length uint32
|
||||
if code.Op.CodeType() == CodeArrayHead {
|
||||
length = code.Length
|
||||
} else {
|
||||
length = code.Length / uintptrSize
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.ElemIdx/uintptrSize,
|
||||
length,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpMapHead(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpMapEnd(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpElem(code *Opcode) string {
|
||||
var length uint32
|
||||
if code.Op.CodeType() == CodeArrayElem {
|
||||
length = code.Length
|
||||
} else {
|
||||
length = code.Length / uintptrSize
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.ElemIdx/uintptrSize,
|
||||
length,
|
||||
code.Size,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpField(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d][key:%s][offset:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.DisplayKey,
|
||||
code.Offset,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpKey(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpValue(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) Dump() string {
|
||||
codes := []string{}
|
||||
for code := c; !code.IsEnd(); {
|
||||
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(
|
||||
"[%03d]%s%s ([idx:%d])",
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
))
|
||||
code = code.Next
|
||||
}
|
||||
}
|
||||
return strings.Join(codes, "\n")
|
||||
}
|
||||
|
||||
func (c *Opcode) DumpDOT() string {
|
||||
type edge struct {
|
||||
from, to *Opcode
|
||||
label string
|
||||
weight int
|
||||
}
|
||||
var edges []edge
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
fmt.Fprintf(b, "digraph \"%p\" {\n", c.Type)
|
||||
fmt.Fprintln(b, "mclimit=1.5;\nrankdir=TD;\nordering=out;\nnode[shape=box];")
|
||||
for code := c; !code.IsEnd(); {
|
||||
label := code.Op.String()
|
||||
fmt.Fprintf(b, "\"%p\" [label=%q];\n", code, label)
|
||||
if p := code.Next; p != nil {
|
||||
edges = append(edges, edge{
|
||||
from: code,
|
||||
to: p,
|
||||
label: "Next",
|
||||
weight: 10,
|
||||
})
|
||||
}
|
||||
if p := code.NextField; p != nil {
|
||||
edges = append(edges, edge{
|
||||
from: code,
|
||||
to: p,
|
||||
label: "NextField",
|
||||
weight: 2,
|
||||
})
|
||||
}
|
||||
if p := code.End; p != nil {
|
||||
edges = append(edges, edge{
|
||||
from: code,
|
||||
to: p,
|
||||
label: "End",
|
||||
weight: 1,
|
||||
})
|
||||
}
|
||||
if p := code.Jmp; p != nil {
|
||||
edges = append(edges, edge{
|
||||
from: code,
|
||||
to: p.Code,
|
||||
label: "Jmp",
|
||||
weight: 1,
|
||||
})
|
||||
}
|
||||
|
||||
switch code.Op.CodeType() {
|
||||
case CodeSliceHead:
|
||||
code = code.Next
|
||||
case CodeMapHead:
|
||||
code = code.Next
|
||||
case CodeArrayElem, CodeSliceElem:
|
||||
code = code.End
|
||||
case CodeMapKey:
|
||||
code = code.End
|
||||
case CodeMapValue:
|
||||
code = code.Next
|
||||
case CodeMapEnd:
|
||||
code = code.Next
|
||||
case CodeStructField:
|
||||
code = code.Next
|
||||
case CodeStructEnd:
|
||||
code = code.Next
|
||||
default:
|
||||
code = code.Next
|
||||
}
|
||||
if code.IsEnd() {
|
||||
fmt.Fprintf(b, "\"%p\" [label=%q];\n", code, code.Op.String())
|
||||
}
|
||||
}
|
||||
sort.Slice(edges, func(i, j int) bool {
|
||||
return edges[i].to.DisplayIdx < edges[j].to.DisplayIdx
|
||||
})
|
||||
for _, e := range edges {
|
||||
fmt.Fprintf(b, "\"%p\" -> \"%p\" [label=%q][weight=%d];\n", e.from, e.to, e.label, e.weight)
|
||||
}
|
||||
fmt.Fprint(b, "}")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
length := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpSlice,
|
||||
Type: typ,
|
||||
Idx: idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: elemIdx,
|
||||
Length: length,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newSliceElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, size uintptr) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpSliceElem,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: head.ElemIdx,
|
||||
Length: head.Length,
|
||||
Indent: ctx.indent,
|
||||
Size: uint32(size),
|
||||
}
|
||||
}
|
||||
|
||||
func newArrayHeaderCode(ctx *compileContext, typ *runtime.Type, alen int) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpArray,
|
||||
Type: typ,
|
||||
Idx: idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: elemIdx,
|
||||
Indent: ctx.indent,
|
||||
Length: uint32(alen),
|
||||
}
|
||||
}
|
||||
|
||||
func newArrayElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, length int, size uintptr) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpArrayElem,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: head.ElemIdx,
|
||||
Length: uint32(length),
|
||||
Indent: ctx.indent,
|
||||
Size: uint32(size),
|
||||
}
|
||||
}
|
||||
|
||||
func newMapHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
return &Opcode{
|
||||
Op: OpMap,
|
||||
Type: typ,
|
||||
Idx: idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapKeyCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpMapKey,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapValueCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpMapValue,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapEndCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpMapEnd,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
Next: newEndOp(ctx, typ),
|
||||
}
|
||||
}
|
||||
|
||||
func newRecursiveCode(ctx *compileContext, typ *runtime.Type, jmp *CompiledCode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpRecursive,
|
||||
Type: typ,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
Next: newEndOp(ctx, typ),
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
Jmp: jmp,
|
||||
}
|
||||
}
|
48
internal/encoder/option.go
Normal file
48
internal/encoder/option.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
type OptionFlag uint8
|
||||
|
||||
const (
|
||||
HTMLEscapeOption OptionFlag = 1 << iota
|
||||
IndentOption
|
||||
UnorderedMapOption
|
||||
DebugOption
|
||||
ColorizeOption
|
||||
ContextOption
|
||||
NormalizeUTF8Option
|
||||
FieldQueryOption
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
Flag OptionFlag
|
||||
ColorScheme *ColorScheme
|
||||
Context context.Context
|
||||
DebugOut io.Writer
|
||||
DebugDOTOut io.WriteCloser
|
||||
}
|
||||
|
||||
type EncodeFormat struct {
|
||||
Header string
|
||||
Footer string
|
||||
}
|
||||
|
||||
type EncodeFormatScheme struct {
|
||||
Int EncodeFormat
|
||||
Uint EncodeFormat
|
||||
Float EncodeFormat
|
||||
Bool EncodeFormat
|
||||
String EncodeFormat
|
||||
Binary EncodeFormat
|
||||
ObjectKey EncodeFormat
|
||||
Null EncodeFormat
|
||||
}
|
||||
|
||||
type (
|
||||
ColorScheme = EncodeFormatScheme
|
||||
ColorFormat = EncodeFormat
|
||||
)
|
932
internal/encoder/optype.go
Normal file
932
internal/encoder/optype.go
Normal file
|
@ -0,0 +1,932 @@
|
|||
// Code generated by internal/cmd/generator. DO NOT EDIT!
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CodeType int
|
||||
|
||||
const (
|
||||
CodeOp CodeType = 0
|
||||
CodeArrayHead CodeType = 1
|
||||
CodeArrayElem CodeType = 2
|
||||
CodeSliceHead CodeType = 3
|
||||
CodeSliceElem CodeType = 4
|
||||
CodeMapHead CodeType = 5
|
||||
CodeMapKey CodeType = 6
|
||||
CodeMapValue CodeType = 7
|
||||
CodeMapEnd CodeType = 8
|
||||
CodeRecursive CodeType = 9
|
||||
CodeStructField CodeType = 10
|
||||
CodeStructEnd CodeType = 11
|
||||
)
|
||||
|
||||
var opTypeStrings = [400]string{
|
||||
"End",
|
||||
"Interface",
|
||||
"Ptr",
|
||||
"SliceElem",
|
||||
"SliceEnd",
|
||||
"ArrayElem",
|
||||
"ArrayEnd",
|
||||
"MapKey",
|
||||
"MapValue",
|
||||
"MapEnd",
|
||||
"Recursive",
|
||||
"RecursivePtr",
|
||||
"RecursiveEnd",
|
||||
"InterfaceEnd",
|
||||
"Int",
|
||||
"Uint",
|
||||
"Float32",
|
||||
"Float64",
|
||||
"Bool",
|
||||
"String",
|
||||
"Bytes",
|
||||
"Number",
|
||||
"Array",
|
||||
"Map",
|
||||
"Slice",
|
||||
"Struct",
|
||||
"MarshalJSON",
|
||||
"MarshalText",
|
||||
"IntString",
|
||||
"UintString",
|
||||
"Float32String",
|
||||
"Float64String",
|
||||
"BoolString",
|
||||
"StringString",
|
||||
"NumberString",
|
||||
"IntPtr",
|
||||
"UintPtr",
|
||||
"Float32Ptr",
|
||||
"Float64Ptr",
|
||||
"BoolPtr",
|
||||
"StringPtr",
|
||||
"BytesPtr",
|
||||
"NumberPtr",
|
||||
"ArrayPtr",
|
||||
"MapPtr",
|
||||
"SlicePtr",
|
||||
"MarshalJSONPtr",
|
||||
"MarshalTextPtr",
|
||||
"InterfacePtr",
|
||||
"IntPtrString",
|
||||
"UintPtrString",
|
||||
"Float32PtrString",
|
||||
"Float64PtrString",
|
||||
"BoolPtrString",
|
||||
"StringPtrString",
|
||||
"NumberPtrString",
|
||||
"StructHeadInt",
|
||||
"StructHeadOmitEmptyInt",
|
||||
"StructPtrHeadInt",
|
||||
"StructPtrHeadOmitEmptyInt",
|
||||
"StructHeadUint",
|
||||
"StructHeadOmitEmptyUint",
|
||||
"StructPtrHeadUint",
|
||||
"StructPtrHeadOmitEmptyUint",
|
||||
"StructHeadFloat32",
|
||||
"StructHeadOmitEmptyFloat32",
|
||||
"StructPtrHeadFloat32",
|
||||
"StructPtrHeadOmitEmptyFloat32",
|
||||
"StructHeadFloat64",
|
||||
"StructHeadOmitEmptyFloat64",
|
||||
"StructPtrHeadFloat64",
|
||||
"StructPtrHeadOmitEmptyFloat64",
|
||||
"StructHeadBool",
|
||||
"StructHeadOmitEmptyBool",
|
||||
"StructPtrHeadBool",
|
||||
"StructPtrHeadOmitEmptyBool",
|
||||
"StructHeadString",
|
||||
"StructHeadOmitEmptyString",
|
||||
"StructPtrHeadString",
|
||||
"StructPtrHeadOmitEmptyString",
|
||||
"StructHeadBytes",
|
||||
"StructHeadOmitEmptyBytes",
|
||||
"StructPtrHeadBytes",
|
||||
"StructPtrHeadOmitEmptyBytes",
|
||||
"StructHeadNumber",
|
||||
"StructHeadOmitEmptyNumber",
|
||||
"StructPtrHeadNumber",
|
||||
"StructPtrHeadOmitEmptyNumber",
|
||||
"StructHeadArray",
|
||||
"StructHeadOmitEmptyArray",
|
||||
"StructPtrHeadArray",
|
||||
"StructPtrHeadOmitEmptyArray",
|
||||
"StructHeadMap",
|
||||
"StructHeadOmitEmptyMap",
|
||||
"StructPtrHeadMap",
|
||||
"StructPtrHeadOmitEmptyMap",
|
||||
"StructHeadSlice",
|
||||
"StructHeadOmitEmptySlice",
|
||||
"StructPtrHeadSlice",
|
||||
"StructPtrHeadOmitEmptySlice",
|
||||
"StructHeadStruct",
|
||||
"StructHeadOmitEmptyStruct",
|
||||
"StructPtrHeadStruct",
|
||||
"StructPtrHeadOmitEmptyStruct",
|
||||
"StructHeadMarshalJSON",
|
||||
"StructHeadOmitEmptyMarshalJSON",
|
||||
"StructPtrHeadMarshalJSON",
|
||||
"StructPtrHeadOmitEmptyMarshalJSON",
|
||||
"StructHeadMarshalText",
|
||||
"StructHeadOmitEmptyMarshalText",
|
||||
"StructPtrHeadMarshalText",
|
||||
"StructPtrHeadOmitEmptyMarshalText",
|
||||
"StructHeadIntString",
|
||||
"StructHeadOmitEmptyIntString",
|
||||
"StructPtrHeadIntString",
|
||||
"StructPtrHeadOmitEmptyIntString",
|
||||
"StructHeadUintString",
|
||||
"StructHeadOmitEmptyUintString",
|
||||
"StructPtrHeadUintString",
|
||||
"StructPtrHeadOmitEmptyUintString",
|
||||
"StructHeadFloat32String",
|
||||
"StructHeadOmitEmptyFloat32String",
|
||||
"StructPtrHeadFloat32String",
|
||||
"StructPtrHeadOmitEmptyFloat32String",
|
||||
"StructHeadFloat64String",
|
||||
"StructHeadOmitEmptyFloat64String",
|
||||
"StructPtrHeadFloat64String",
|
||||
"StructPtrHeadOmitEmptyFloat64String",
|
||||
"StructHeadBoolString",
|
||||
"StructHeadOmitEmptyBoolString",
|
||||
"StructPtrHeadBoolString",
|
||||
"StructPtrHeadOmitEmptyBoolString",
|
||||
"StructHeadStringString",
|
||||
"StructHeadOmitEmptyStringString",
|
||||
"StructPtrHeadStringString",
|
||||
"StructPtrHeadOmitEmptyStringString",
|
||||
"StructHeadNumberString",
|
||||
"StructHeadOmitEmptyNumberString",
|
||||
"StructPtrHeadNumberString",
|
||||
"StructPtrHeadOmitEmptyNumberString",
|
||||
"StructHeadIntPtr",
|
||||
"StructHeadOmitEmptyIntPtr",
|
||||
"StructPtrHeadIntPtr",
|
||||
"StructPtrHeadOmitEmptyIntPtr",
|
||||
"StructHeadUintPtr",
|
||||
"StructHeadOmitEmptyUintPtr",
|
||||
"StructPtrHeadUintPtr",
|
||||
"StructPtrHeadOmitEmptyUintPtr",
|
||||
"StructHeadFloat32Ptr",
|
||||
"StructHeadOmitEmptyFloat32Ptr",
|
||||
"StructPtrHeadFloat32Ptr",
|
||||
"StructPtrHeadOmitEmptyFloat32Ptr",
|
||||
"StructHeadFloat64Ptr",
|
||||
"StructHeadOmitEmptyFloat64Ptr",
|
||||
"StructPtrHeadFloat64Ptr",
|
||||
"StructPtrHeadOmitEmptyFloat64Ptr",
|
||||
"StructHeadBoolPtr",
|
||||
"StructHeadOmitEmptyBoolPtr",
|
||||
"StructPtrHeadBoolPtr",
|
||||
"StructPtrHeadOmitEmptyBoolPtr",
|
||||
"StructHeadStringPtr",
|
||||
"StructHeadOmitEmptyStringPtr",
|
||||
"StructPtrHeadStringPtr",
|
||||
"StructPtrHeadOmitEmptyStringPtr",
|
||||
"StructHeadBytesPtr",
|
||||
"StructHeadOmitEmptyBytesPtr",
|
||||
"StructPtrHeadBytesPtr",
|
||||
"StructPtrHeadOmitEmptyBytesPtr",
|
||||
"StructHeadNumberPtr",
|
||||
"StructHeadOmitEmptyNumberPtr",
|
||||
"StructPtrHeadNumberPtr",
|
||||
"StructPtrHeadOmitEmptyNumberPtr",
|
||||
"StructHeadArrayPtr",
|
||||
"StructHeadOmitEmptyArrayPtr",
|
||||
"StructPtrHeadArrayPtr",
|
||||
"StructPtrHeadOmitEmptyArrayPtr",
|
||||
"StructHeadMapPtr",
|
||||
"StructHeadOmitEmptyMapPtr",
|
||||
"StructPtrHeadMapPtr",
|
||||
"StructPtrHeadOmitEmptyMapPtr",
|
||||
"StructHeadSlicePtr",
|
||||
"StructHeadOmitEmptySlicePtr",
|
||||
"StructPtrHeadSlicePtr",
|
||||
"StructPtrHeadOmitEmptySlicePtr",
|
||||
"StructHeadMarshalJSONPtr",
|
||||
"StructHeadOmitEmptyMarshalJSONPtr",
|
||||
"StructPtrHeadMarshalJSONPtr",
|
||||
"StructPtrHeadOmitEmptyMarshalJSONPtr",
|
||||
"StructHeadMarshalTextPtr",
|
||||
"StructHeadOmitEmptyMarshalTextPtr",
|
||||
"StructPtrHeadMarshalTextPtr",
|
||||
"StructPtrHeadOmitEmptyMarshalTextPtr",
|
||||
"StructHeadInterfacePtr",
|
||||
"StructHeadOmitEmptyInterfacePtr",
|
||||
"StructPtrHeadInterfacePtr",
|
||||
"StructPtrHeadOmitEmptyInterfacePtr",
|
||||
"StructHeadIntPtrString",
|
||||
"StructHeadOmitEmptyIntPtrString",
|
||||
"StructPtrHeadIntPtrString",
|
||||
"StructPtrHeadOmitEmptyIntPtrString",
|
||||
"StructHeadUintPtrString",
|
||||
"StructHeadOmitEmptyUintPtrString",
|
||||
"StructPtrHeadUintPtrString",
|
||||
"StructPtrHeadOmitEmptyUintPtrString",
|
||||
"StructHeadFloat32PtrString",
|
||||
"StructHeadOmitEmptyFloat32PtrString",
|
||||
"StructPtrHeadFloat32PtrString",
|
||||
"StructPtrHeadOmitEmptyFloat32PtrString",
|
||||
"StructHeadFloat64PtrString",
|
||||
"StructHeadOmitEmptyFloat64PtrString",
|
||||
"StructPtrHeadFloat64PtrString",
|
||||
"StructPtrHeadOmitEmptyFloat64PtrString",
|
||||
"StructHeadBoolPtrString",
|
||||
"StructHeadOmitEmptyBoolPtrString",
|
||||
"StructPtrHeadBoolPtrString",
|
||||
"StructPtrHeadOmitEmptyBoolPtrString",
|
||||
"StructHeadStringPtrString",
|
||||
"StructHeadOmitEmptyStringPtrString",
|
||||
"StructPtrHeadStringPtrString",
|
||||
"StructPtrHeadOmitEmptyStringPtrString",
|
||||
"StructHeadNumberPtrString",
|
||||
"StructHeadOmitEmptyNumberPtrString",
|
||||
"StructPtrHeadNumberPtrString",
|
||||
"StructPtrHeadOmitEmptyNumberPtrString",
|
||||
"StructHead",
|
||||
"StructHeadOmitEmpty",
|
||||
"StructPtrHead",
|
||||
"StructPtrHeadOmitEmpty",
|
||||
"StructFieldInt",
|
||||
"StructFieldOmitEmptyInt",
|
||||
"StructEndInt",
|
||||
"StructEndOmitEmptyInt",
|
||||
"StructFieldUint",
|
||||
"StructFieldOmitEmptyUint",
|
||||
"StructEndUint",
|
||||
"StructEndOmitEmptyUint",
|
||||
"StructFieldFloat32",
|
||||
"StructFieldOmitEmptyFloat32",
|
||||
"StructEndFloat32",
|
||||
"StructEndOmitEmptyFloat32",
|
||||
"StructFieldFloat64",
|
||||
"StructFieldOmitEmptyFloat64",
|
||||
"StructEndFloat64",
|
||||
"StructEndOmitEmptyFloat64",
|
||||
"StructFieldBool",
|
||||
"StructFieldOmitEmptyBool",
|
||||
"StructEndBool",
|
||||
"StructEndOmitEmptyBool",
|
||||
"StructFieldString",
|
||||
"StructFieldOmitEmptyString",
|
||||
"StructEndString",
|
||||
"StructEndOmitEmptyString",
|
||||
"StructFieldBytes",
|
||||
"StructFieldOmitEmptyBytes",
|
||||
"StructEndBytes",
|
||||
"StructEndOmitEmptyBytes",
|
||||
"StructFieldNumber",
|
||||
"StructFieldOmitEmptyNumber",
|
||||
"StructEndNumber",
|
||||
"StructEndOmitEmptyNumber",
|
||||
"StructFieldArray",
|
||||
"StructFieldOmitEmptyArray",
|
||||
"StructEndArray",
|
||||
"StructEndOmitEmptyArray",
|
||||
"StructFieldMap",
|
||||
"StructFieldOmitEmptyMap",
|
||||
"StructEndMap",
|
||||
"StructEndOmitEmptyMap",
|
||||
"StructFieldSlice",
|
||||
"StructFieldOmitEmptySlice",
|
||||
"StructEndSlice",
|
||||
"StructEndOmitEmptySlice",
|
||||
"StructFieldStruct",
|
||||
"StructFieldOmitEmptyStruct",
|
||||
"StructEndStruct",
|
||||
"StructEndOmitEmptyStruct",
|
||||
"StructFieldMarshalJSON",
|
||||
"StructFieldOmitEmptyMarshalJSON",
|
||||
"StructEndMarshalJSON",
|
||||
"StructEndOmitEmptyMarshalJSON",
|
||||
"StructFieldMarshalText",
|
||||
"StructFieldOmitEmptyMarshalText",
|
||||
"StructEndMarshalText",
|
||||
"StructEndOmitEmptyMarshalText",
|
||||
"StructFieldIntString",
|
||||
"StructFieldOmitEmptyIntString",
|
||||
"StructEndIntString",
|
||||
"StructEndOmitEmptyIntString",
|
||||
"StructFieldUintString",
|
||||
"StructFieldOmitEmptyUintString",
|
||||
"StructEndUintString",
|
||||
"StructEndOmitEmptyUintString",
|
||||
"StructFieldFloat32String",
|
||||
"StructFieldOmitEmptyFloat32String",
|
||||
"StructEndFloat32String",
|
||||
"StructEndOmitEmptyFloat32String",
|
||||
"StructFieldFloat64String",
|
||||
"StructFieldOmitEmptyFloat64String",
|
||||
"StructEndFloat64String",
|
||||
"StructEndOmitEmptyFloat64String",
|
||||
"StructFieldBoolString",
|
||||
"StructFieldOmitEmptyBoolString",
|
||||
"StructEndBoolString",
|
||||
"StructEndOmitEmptyBoolString",
|
||||
"StructFieldStringString",
|
||||
"StructFieldOmitEmptyStringString",
|
||||
"StructEndStringString",
|
||||
"StructEndOmitEmptyStringString",
|
||||
"StructFieldNumberString",
|
||||
"StructFieldOmitEmptyNumberString",
|
||||
"StructEndNumberString",
|
||||
"StructEndOmitEmptyNumberString",
|
||||
"StructFieldIntPtr",
|
||||
"StructFieldOmitEmptyIntPtr",
|
||||
"StructEndIntPtr",
|
||||
"StructEndOmitEmptyIntPtr",
|
||||
"StructFieldUintPtr",
|
||||
"StructFieldOmitEmptyUintPtr",
|
||||
"StructEndUintPtr",
|
||||
"StructEndOmitEmptyUintPtr",
|
||||
"StructFieldFloat32Ptr",
|
||||
"StructFieldOmitEmptyFloat32Ptr",
|
||||
"StructEndFloat32Ptr",
|
||||
"StructEndOmitEmptyFloat32Ptr",
|
||||
"StructFieldFloat64Ptr",
|
||||
"StructFieldOmitEmptyFloat64Ptr",
|
||||
"StructEndFloat64Ptr",
|
||||
"StructEndOmitEmptyFloat64Ptr",
|
||||
"StructFieldBoolPtr",
|
||||
"StructFieldOmitEmptyBoolPtr",
|
||||
"StructEndBoolPtr",
|
||||
"StructEndOmitEmptyBoolPtr",
|
||||
"StructFieldStringPtr",
|
||||
"StructFieldOmitEmptyStringPtr",
|
||||
"StructEndStringPtr",
|
||||
"StructEndOmitEmptyStringPtr",
|
||||
"StructFieldBytesPtr",
|
||||
"StructFieldOmitEmptyBytesPtr",
|
||||
"StructEndBytesPtr",
|
||||
"StructEndOmitEmptyBytesPtr",
|
||||
"StructFieldNumberPtr",
|
||||
"StructFieldOmitEmptyNumberPtr",
|
||||
"StructEndNumberPtr",
|
||||
"StructEndOmitEmptyNumberPtr",
|
||||
"StructFieldArrayPtr",
|
||||
"StructFieldOmitEmptyArrayPtr",
|
||||
"StructEndArrayPtr",
|
||||
"StructEndOmitEmptyArrayPtr",
|
||||
"StructFieldMapPtr",
|
||||
"StructFieldOmitEmptyMapPtr",
|
||||
"StructEndMapPtr",
|
||||
"StructEndOmitEmptyMapPtr",
|
||||
"StructFieldSlicePtr",
|
||||
"StructFieldOmitEmptySlicePtr",
|
||||
"StructEndSlicePtr",
|
||||
"StructEndOmitEmptySlicePtr",
|
||||
"StructFieldMarshalJSONPtr",
|
||||
"StructFieldOmitEmptyMarshalJSONPtr",
|
||||
"StructEndMarshalJSONPtr",
|
||||
"StructEndOmitEmptyMarshalJSONPtr",
|
||||
"StructFieldMarshalTextPtr",
|
||||
"StructFieldOmitEmptyMarshalTextPtr",
|
||||
"StructEndMarshalTextPtr",
|
||||
"StructEndOmitEmptyMarshalTextPtr",
|
||||
"StructFieldInterfacePtr",
|
||||
"StructFieldOmitEmptyInterfacePtr",
|
||||
"StructEndInterfacePtr",
|
||||
"StructEndOmitEmptyInterfacePtr",
|
||||
"StructFieldIntPtrString",
|
||||
"StructFieldOmitEmptyIntPtrString",
|
||||
"StructEndIntPtrString",
|
||||
"StructEndOmitEmptyIntPtrString",
|
||||
"StructFieldUintPtrString",
|
||||
"StructFieldOmitEmptyUintPtrString",
|
||||
"StructEndUintPtrString",
|
||||
"StructEndOmitEmptyUintPtrString",
|
||||
"StructFieldFloat32PtrString",
|
||||
"StructFieldOmitEmptyFloat32PtrString",
|
||||
"StructEndFloat32PtrString",
|
||||
"StructEndOmitEmptyFloat32PtrString",
|
||||
"StructFieldFloat64PtrString",
|
||||
"StructFieldOmitEmptyFloat64PtrString",
|
||||
"StructEndFloat64PtrString",
|
||||
"StructEndOmitEmptyFloat64PtrString",
|
||||
"StructFieldBoolPtrString",
|
||||
"StructFieldOmitEmptyBoolPtrString",
|
||||
"StructEndBoolPtrString",
|
||||
"StructEndOmitEmptyBoolPtrString",
|
||||
"StructFieldStringPtrString",
|
||||
"StructFieldOmitEmptyStringPtrString",
|
||||
"StructEndStringPtrString",
|
||||
"StructEndOmitEmptyStringPtrString",
|
||||
"StructFieldNumberPtrString",
|
||||
"StructFieldOmitEmptyNumberPtrString",
|
||||
"StructEndNumberPtrString",
|
||||
"StructEndOmitEmptyNumberPtrString",
|
||||
"StructField",
|
||||
"StructFieldOmitEmpty",
|
||||
"StructEnd",
|
||||
"StructEndOmitEmpty",
|
||||
}
|
||||
|
||||
type OpType uint16
|
||||
|
||||
const (
|
||||
OpEnd OpType = 0
|
||||
OpInterface OpType = 1
|
||||
OpPtr OpType = 2
|
||||
OpSliceElem OpType = 3
|
||||
OpSliceEnd OpType = 4
|
||||
OpArrayElem OpType = 5
|
||||
OpArrayEnd OpType = 6
|
||||
OpMapKey OpType = 7
|
||||
OpMapValue OpType = 8
|
||||
OpMapEnd OpType = 9
|
||||
OpRecursive OpType = 10
|
||||
OpRecursivePtr OpType = 11
|
||||
OpRecursiveEnd OpType = 12
|
||||
OpInterfaceEnd OpType = 13
|
||||
OpInt OpType = 14
|
||||
OpUint OpType = 15
|
||||
OpFloat32 OpType = 16
|
||||
OpFloat64 OpType = 17
|
||||
OpBool OpType = 18
|
||||
OpString OpType = 19
|
||||
OpBytes OpType = 20
|
||||
OpNumber OpType = 21
|
||||
OpArray OpType = 22
|
||||
OpMap OpType = 23
|
||||
OpSlice OpType = 24
|
||||
OpStruct OpType = 25
|
||||
OpMarshalJSON OpType = 26
|
||||
OpMarshalText OpType = 27
|
||||
OpIntString OpType = 28
|
||||
OpUintString OpType = 29
|
||||
OpFloat32String OpType = 30
|
||||
OpFloat64String OpType = 31
|
||||
OpBoolString OpType = 32
|
||||
OpStringString OpType = 33
|
||||
OpNumberString OpType = 34
|
||||
OpIntPtr OpType = 35
|
||||
OpUintPtr OpType = 36
|
||||
OpFloat32Ptr OpType = 37
|
||||
OpFloat64Ptr OpType = 38
|
||||
OpBoolPtr OpType = 39
|
||||
OpStringPtr OpType = 40
|
||||
OpBytesPtr OpType = 41
|
||||
OpNumberPtr OpType = 42
|
||||
OpArrayPtr OpType = 43
|
||||
OpMapPtr OpType = 44
|
||||
OpSlicePtr OpType = 45
|
||||
OpMarshalJSONPtr OpType = 46
|
||||
OpMarshalTextPtr OpType = 47
|
||||
OpInterfacePtr OpType = 48
|
||||
OpIntPtrString OpType = 49
|
||||
OpUintPtrString OpType = 50
|
||||
OpFloat32PtrString OpType = 51
|
||||
OpFloat64PtrString OpType = 52
|
||||
OpBoolPtrString OpType = 53
|
||||
OpStringPtrString OpType = 54
|
||||
OpNumberPtrString OpType = 55
|
||||
OpStructHeadInt OpType = 56
|
||||
OpStructHeadOmitEmptyInt OpType = 57
|
||||
OpStructPtrHeadInt OpType = 58
|
||||
OpStructPtrHeadOmitEmptyInt OpType = 59
|
||||
OpStructHeadUint OpType = 60
|
||||
OpStructHeadOmitEmptyUint OpType = 61
|
||||
OpStructPtrHeadUint OpType = 62
|
||||
OpStructPtrHeadOmitEmptyUint OpType = 63
|
||||
OpStructHeadFloat32 OpType = 64
|
||||
OpStructHeadOmitEmptyFloat32 OpType = 65
|
||||
OpStructPtrHeadFloat32 OpType = 66
|
||||
OpStructPtrHeadOmitEmptyFloat32 OpType = 67
|
||||
OpStructHeadFloat64 OpType = 68
|
||||
OpStructHeadOmitEmptyFloat64 OpType = 69
|
||||
OpStructPtrHeadFloat64 OpType = 70
|
||||
OpStructPtrHeadOmitEmptyFloat64 OpType = 71
|
||||
OpStructHeadBool OpType = 72
|
||||
OpStructHeadOmitEmptyBool OpType = 73
|
||||
OpStructPtrHeadBool OpType = 74
|
||||
OpStructPtrHeadOmitEmptyBool OpType = 75
|
||||
OpStructHeadString OpType = 76
|
||||
OpStructHeadOmitEmptyString OpType = 77
|
||||
OpStructPtrHeadString OpType = 78
|
||||
OpStructPtrHeadOmitEmptyString OpType = 79
|
||||
OpStructHeadBytes OpType = 80
|
||||
OpStructHeadOmitEmptyBytes OpType = 81
|
||||
OpStructPtrHeadBytes OpType = 82
|
||||
OpStructPtrHeadOmitEmptyBytes OpType = 83
|
||||
OpStructHeadNumber OpType = 84
|
||||
OpStructHeadOmitEmptyNumber OpType = 85
|
||||
OpStructPtrHeadNumber OpType = 86
|
||||
OpStructPtrHeadOmitEmptyNumber OpType = 87
|
||||
OpStructHeadArray OpType = 88
|
||||
OpStructHeadOmitEmptyArray OpType = 89
|
||||
OpStructPtrHeadArray OpType = 90
|
||||
OpStructPtrHeadOmitEmptyArray OpType = 91
|
||||
OpStructHeadMap OpType = 92
|
||||
OpStructHeadOmitEmptyMap OpType = 93
|
||||
OpStructPtrHeadMap OpType = 94
|
||||
OpStructPtrHeadOmitEmptyMap OpType = 95
|
||||
OpStructHeadSlice OpType = 96
|
||||
OpStructHeadOmitEmptySlice OpType = 97
|
||||
OpStructPtrHeadSlice OpType = 98
|
||||
OpStructPtrHeadOmitEmptySlice OpType = 99
|
||||
OpStructHeadStruct OpType = 100
|
||||
OpStructHeadOmitEmptyStruct OpType = 101
|
||||
OpStructPtrHeadStruct OpType = 102
|
||||
OpStructPtrHeadOmitEmptyStruct OpType = 103
|
||||
OpStructHeadMarshalJSON OpType = 104
|
||||
OpStructHeadOmitEmptyMarshalJSON OpType = 105
|
||||
OpStructPtrHeadMarshalJSON OpType = 106
|
||||
OpStructPtrHeadOmitEmptyMarshalJSON OpType = 107
|
||||
OpStructHeadMarshalText OpType = 108
|
||||
OpStructHeadOmitEmptyMarshalText OpType = 109
|
||||
OpStructPtrHeadMarshalText OpType = 110
|
||||
OpStructPtrHeadOmitEmptyMarshalText OpType = 111
|
||||
OpStructHeadIntString OpType = 112
|
||||
OpStructHeadOmitEmptyIntString OpType = 113
|
||||
OpStructPtrHeadIntString OpType = 114
|
||||
OpStructPtrHeadOmitEmptyIntString OpType = 115
|
||||
OpStructHeadUintString OpType = 116
|
||||
OpStructHeadOmitEmptyUintString OpType = 117
|
||||
OpStructPtrHeadUintString OpType = 118
|
||||
OpStructPtrHeadOmitEmptyUintString OpType = 119
|
||||
OpStructHeadFloat32String OpType = 120
|
||||
OpStructHeadOmitEmptyFloat32String OpType = 121
|
||||
OpStructPtrHeadFloat32String OpType = 122
|
||||
OpStructPtrHeadOmitEmptyFloat32String OpType = 123
|
||||
OpStructHeadFloat64String OpType = 124
|
||||
OpStructHeadOmitEmptyFloat64String OpType = 125
|
||||
OpStructPtrHeadFloat64String OpType = 126
|
||||
OpStructPtrHeadOmitEmptyFloat64String OpType = 127
|
||||
OpStructHeadBoolString OpType = 128
|
||||
OpStructHeadOmitEmptyBoolString OpType = 129
|
||||
OpStructPtrHeadBoolString OpType = 130
|
||||
OpStructPtrHeadOmitEmptyBoolString OpType = 131
|
||||
OpStructHeadStringString OpType = 132
|
||||
OpStructHeadOmitEmptyStringString OpType = 133
|
||||
OpStructPtrHeadStringString OpType = 134
|
||||
OpStructPtrHeadOmitEmptyStringString OpType = 135
|
||||
OpStructHeadNumberString OpType = 136
|
||||
OpStructHeadOmitEmptyNumberString OpType = 137
|
||||
OpStructPtrHeadNumberString OpType = 138
|
||||
OpStructPtrHeadOmitEmptyNumberString OpType = 139
|
||||
OpStructHeadIntPtr OpType = 140
|
||||
OpStructHeadOmitEmptyIntPtr OpType = 141
|
||||
OpStructPtrHeadIntPtr OpType = 142
|
||||
OpStructPtrHeadOmitEmptyIntPtr OpType = 143
|
||||
OpStructHeadUintPtr OpType = 144
|
||||
OpStructHeadOmitEmptyUintPtr OpType = 145
|
||||
OpStructPtrHeadUintPtr OpType = 146
|
||||
OpStructPtrHeadOmitEmptyUintPtr OpType = 147
|
||||
OpStructHeadFloat32Ptr OpType = 148
|
||||
OpStructHeadOmitEmptyFloat32Ptr OpType = 149
|
||||
OpStructPtrHeadFloat32Ptr OpType = 150
|
||||
OpStructPtrHeadOmitEmptyFloat32Ptr OpType = 151
|
||||
OpStructHeadFloat64Ptr OpType = 152
|
||||
OpStructHeadOmitEmptyFloat64Ptr OpType = 153
|
||||
OpStructPtrHeadFloat64Ptr OpType = 154
|
||||
OpStructPtrHeadOmitEmptyFloat64Ptr OpType = 155
|
||||
OpStructHeadBoolPtr OpType = 156
|
||||
OpStructHeadOmitEmptyBoolPtr OpType = 157
|
||||
OpStructPtrHeadBoolPtr OpType = 158
|
||||
OpStructPtrHeadOmitEmptyBoolPtr OpType = 159
|
||||
OpStructHeadStringPtr OpType = 160
|
||||
OpStructHeadOmitEmptyStringPtr OpType = 161
|
||||
OpStructPtrHeadStringPtr OpType = 162
|
||||
OpStructPtrHeadOmitEmptyStringPtr OpType = 163
|
||||
OpStructHeadBytesPtr OpType = 164
|
||||
OpStructHeadOmitEmptyBytesPtr OpType = 165
|
||||
OpStructPtrHeadBytesPtr OpType = 166
|
||||
OpStructPtrHeadOmitEmptyBytesPtr OpType = 167
|
||||
OpStructHeadNumberPtr OpType = 168
|
||||
OpStructHeadOmitEmptyNumberPtr OpType = 169
|
||||
OpStructPtrHeadNumberPtr OpType = 170
|
||||
OpStructPtrHeadOmitEmptyNumberPtr OpType = 171
|
||||
OpStructHeadArrayPtr OpType = 172
|
||||
OpStructHeadOmitEmptyArrayPtr OpType = 173
|
||||
OpStructPtrHeadArrayPtr OpType = 174
|
||||
OpStructPtrHeadOmitEmptyArrayPtr OpType = 175
|
||||
OpStructHeadMapPtr OpType = 176
|
||||
OpStructHeadOmitEmptyMapPtr OpType = 177
|
||||
OpStructPtrHeadMapPtr OpType = 178
|
||||
OpStructPtrHeadOmitEmptyMapPtr OpType = 179
|
||||
OpStructHeadSlicePtr OpType = 180
|
||||
OpStructHeadOmitEmptySlicePtr OpType = 181
|
||||
OpStructPtrHeadSlicePtr OpType = 182
|
||||
OpStructPtrHeadOmitEmptySlicePtr OpType = 183
|
||||
OpStructHeadMarshalJSONPtr OpType = 184
|
||||
OpStructHeadOmitEmptyMarshalJSONPtr OpType = 185
|
||||
OpStructPtrHeadMarshalJSONPtr OpType = 186
|
||||
OpStructPtrHeadOmitEmptyMarshalJSONPtr OpType = 187
|
||||
OpStructHeadMarshalTextPtr OpType = 188
|
||||
OpStructHeadOmitEmptyMarshalTextPtr OpType = 189
|
||||
OpStructPtrHeadMarshalTextPtr OpType = 190
|
||||
OpStructPtrHeadOmitEmptyMarshalTextPtr OpType = 191
|
||||
OpStructHeadInterfacePtr OpType = 192
|
||||
OpStructHeadOmitEmptyInterfacePtr OpType = 193
|
||||
OpStructPtrHeadInterfacePtr OpType = 194
|
||||
OpStructPtrHeadOmitEmptyInterfacePtr OpType = 195
|
||||
OpStructHeadIntPtrString OpType = 196
|
||||
OpStructHeadOmitEmptyIntPtrString OpType = 197
|
||||
OpStructPtrHeadIntPtrString OpType = 198
|
||||
OpStructPtrHeadOmitEmptyIntPtrString OpType = 199
|
||||
OpStructHeadUintPtrString OpType = 200
|
||||
OpStructHeadOmitEmptyUintPtrString OpType = 201
|
||||
OpStructPtrHeadUintPtrString OpType = 202
|
||||
OpStructPtrHeadOmitEmptyUintPtrString OpType = 203
|
||||
OpStructHeadFloat32PtrString OpType = 204
|
||||
OpStructHeadOmitEmptyFloat32PtrString OpType = 205
|
||||
OpStructPtrHeadFloat32PtrString OpType = 206
|
||||
OpStructPtrHeadOmitEmptyFloat32PtrString OpType = 207
|
||||
OpStructHeadFloat64PtrString OpType = 208
|
||||
OpStructHeadOmitEmptyFloat64PtrString OpType = 209
|
||||
OpStructPtrHeadFloat64PtrString OpType = 210
|
||||
OpStructPtrHeadOmitEmptyFloat64PtrString OpType = 211
|
||||
OpStructHeadBoolPtrString OpType = 212
|
||||
OpStructHeadOmitEmptyBoolPtrString OpType = 213
|
||||
OpStructPtrHeadBoolPtrString OpType = 214
|
||||
OpStructPtrHeadOmitEmptyBoolPtrString OpType = 215
|
||||
OpStructHeadStringPtrString OpType = 216
|
||||
OpStructHeadOmitEmptyStringPtrString OpType = 217
|
||||
OpStructPtrHeadStringPtrString OpType = 218
|
||||
OpStructPtrHeadOmitEmptyStringPtrString OpType = 219
|
||||
OpStructHeadNumberPtrString OpType = 220
|
||||
OpStructHeadOmitEmptyNumberPtrString OpType = 221
|
||||
OpStructPtrHeadNumberPtrString OpType = 222
|
||||
OpStructPtrHeadOmitEmptyNumberPtrString OpType = 223
|
||||
OpStructHead OpType = 224
|
||||
OpStructHeadOmitEmpty OpType = 225
|
||||
OpStructPtrHead OpType = 226
|
||||
OpStructPtrHeadOmitEmpty OpType = 227
|
||||
OpStructFieldInt OpType = 228
|
||||
OpStructFieldOmitEmptyInt OpType = 229
|
||||
OpStructEndInt OpType = 230
|
||||
OpStructEndOmitEmptyInt OpType = 231
|
||||
OpStructFieldUint OpType = 232
|
||||
OpStructFieldOmitEmptyUint OpType = 233
|
||||
OpStructEndUint OpType = 234
|
||||
OpStructEndOmitEmptyUint OpType = 235
|
||||
OpStructFieldFloat32 OpType = 236
|
||||
OpStructFieldOmitEmptyFloat32 OpType = 237
|
||||
OpStructEndFloat32 OpType = 238
|
||||
OpStructEndOmitEmptyFloat32 OpType = 239
|
||||
OpStructFieldFloat64 OpType = 240
|
||||
OpStructFieldOmitEmptyFloat64 OpType = 241
|
||||
OpStructEndFloat64 OpType = 242
|
||||
OpStructEndOmitEmptyFloat64 OpType = 243
|
||||
OpStructFieldBool OpType = 244
|
||||
OpStructFieldOmitEmptyBool OpType = 245
|
||||
OpStructEndBool OpType = 246
|
||||
OpStructEndOmitEmptyBool OpType = 247
|
||||
OpStructFieldString OpType = 248
|
||||
OpStructFieldOmitEmptyString OpType = 249
|
||||
OpStructEndString OpType = 250
|
||||
OpStructEndOmitEmptyString OpType = 251
|
||||
OpStructFieldBytes OpType = 252
|
||||
OpStructFieldOmitEmptyBytes OpType = 253
|
||||
OpStructEndBytes OpType = 254
|
||||
OpStructEndOmitEmptyBytes OpType = 255
|
||||
OpStructFieldNumber OpType = 256
|
||||
OpStructFieldOmitEmptyNumber OpType = 257
|
||||
OpStructEndNumber OpType = 258
|
||||
OpStructEndOmitEmptyNumber OpType = 259
|
||||
OpStructFieldArray OpType = 260
|
||||
OpStructFieldOmitEmptyArray OpType = 261
|
||||
OpStructEndArray OpType = 262
|
||||
OpStructEndOmitEmptyArray OpType = 263
|
||||
OpStructFieldMap OpType = 264
|
||||
OpStructFieldOmitEmptyMap OpType = 265
|
||||
OpStructEndMap OpType = 266
|
||||
OpStructEndOmitEmptyMap OpType = 267
|
||||
OpStructFieldSlice OpType = 268
|
||||
OpStructFieldOmitEmptySlice OpType = 269
|
||||
OpStructEndSlice OpType = 270
|
||||
OpStructEndOmitEmptySlice OpType = 271
|
||||
OpStructFieldStruct OpType = 272
|
||||
OpStructFieldOmitEmptyStruct OpType = 273
|
||||
OpStructEndStruct OpType = 274
|
||||
OpStructEndOmitEmptyStruct OpType = 275
|
||||
OpStructFieldMarshalJSON OpType = 276
|
||||
OpStructFieldOmitEmptyMarshalJSON OpType = 277
|
||||
OpStructEndMarshalJSON OpType = 278
|
||||
OpStructEndOmitEmptyMarshalJSON OpType = 279
|
||||
OpStructFieldMarshalText OpType = 280
|
||||
OpStructFieldOmitEmptyMarshalText OpType = 281
|
||||
OpStructEndMarshalText OpType = 282
|
||||
OpStructEndOmitEmptyMarshalText OpType = 283
|
||||
OpStructFieldIntString OpType = 284
|
||||
OpStructFieldOmitEmptyIntString OpType = 285
|
||||
OpStructEndIntString OpType = 286
|
||||
OpStructEndOmitEmptyIntString OpType = 287
|
||||
OpStructFieldUintString OpType = 288
|
||||
OpStructFieldOmitEmptyUintString OpType = 289
|
||||
OpStructEndUintString OpType = 290
|
||||
OpStructEndOmitEmptyUintString OpType = 291
|
||||
OpStructFieldFloat32String OpType = 292
|
||||
OpStructFieldOmitEmptyFloat32String OpType = 293
|
||||
OpStructEndFloat32String OpType = 294
|
||||
OpStructEndOmitEmptyFloat32String OpType = 295
|
||||
OpStructFieldFloat64String OpType = 296
|
||||
OpStructFieldOmitEmptyFloat64String OpType = 297
|
||||
OpStructEndFloat64String OpType = 298
|
||||
OpStructEndOmitEmptyFloat64String OpType = 299
|
||||
OpStructFieldBoolString OpType = 300
|
||||
OpStructFieldOmitEmptyBoolString OpType = 301
|
||||
OpStructEndBoolString OpType = 302
|
||||
OpStructEndOmitEmptyBoolString OpType = 303
|
||||
OpStructFieldStringString OpType = 304
|
||||
OpStructFieldOmitEmptyStringString OpType = 305
|
||||
OpStructEndStringString OpType = 306
|
||||
OpStructEndOmitEmptyStringString OpType = 307
|
||||
OpStructFieldNumberString OpType = 308
|
||||
OpStructFieldOmitEmptyNumberString OpType = 309
|
||||
OpStructEndNumberString OpType = 310
|
||||
OpStructEndOmitEmptyNumberString OpType = 311
|
||||
OpStructFieldIntPtr OpType = 312
|
||||
OpStructFieldOmitEmptyIntPtr OpType = 313
|
||||
OpStructEndIntPtr OpType = 314
|
||||
OpStructEndOmitEmptyIntPtr OpType = 315
|
||||
OpStructFieldUintPtr OpType = 316
|
||||
OpStructFieldOmitEmptyUintPtr OpType = 317
|
||||
OpStructEndUintPtr OpType = 318
|
||||
OpStructEndOmitEmptyUintPtr OpType = 319
|
||||
OpStructFieldFloat32Ptr OpType = 320
|
||||
OpStructFieldOmitEmptyFloat32Ptr OpType = 321
|
||||
OpStructEndFloat32Ptr OpType = 322
|
||||
OpStructEndOmitEmptyFloat32Ptr OpType = 323
|
||||
OpStructFieldFloat64Ptr OpType = 324
|
||||
OpStructFieldOmitEmptyFloat64Ptr OpType = 325
|
||||
OpStructEndFloat64Ptr OpType = 326
|
||||
OpStructEndOmitEmptyFloat64Ptr OpType = 327
|
||||
OpStructFieldBoolPtr OpType = 328
|
||||
OpStructFieldOmitEmptyBoolPtr OpType = 329
|
||||
OpStructEndBoolPtr OpType = 330
|
||||
OpStructEndOmitEmptyBoolPtr OpType = 331
|
||||
OpStructFieldStringPtr OpType = 332
|
||||
OpStructFieldOmitEmptyStringPtr OpType = 333
|
||||
OpStructEndStringPtr OpType = 334
|
||||
OpStructEndOmitEmptyStringPtr OpType = 335
|
||||
OpStructFieldBytesPtr OpType = 336
|
||||
OpStructFieldOmitEmptyBytesPtr OpType = 337
|
||||
OpStructEndBytesPtr OpType = 338
|
||||
OpStructEndOmitEmptyBytesPtr OpType = 339
|
||||
OpStructFieldNumberPtr OpType = 340
|
||||
OpStructFieldOmitEmptyNumberPtr OpType = 341
|
||||
OpStructEndNumberPtr OpType = 342
|
||||
OpStructEndOmitEmptyNumberPtr OpType = 343
|
||||
OpStructFieldArrayPtr OpType = 344
|
||||
OpStructFieldOmitEmptyArrayPtr OpType = 345
|
||||
OpStructEndArrayPtr OpType = 346
|
||||
OpStructEndOmitEmptyArrayPtr OpType = 347
|
||||
OpStructFieldMapPtr OpType = 348
|
||||
OpStructFieldOmitEmptyMapPtr OpType = 349
|
||||
OpStructEndMapPtr OpType = 350
|
||||
OpStructEndOmitEmptyMapPtr OpType = 351
|
||||
OpStructFieldSlicePtr OpType = 352
|
||||
OpStructFieldOmitEmptySlicePtr OpType = 353
|
||||
OpStructEndSlicePtr OpType = 354
|
||||
OpStructEndOmitEmptySlicePtr OpType = 355
|
||||
OpStructFieldMarshalJSONPtr OpType = 356
|
||||
OpStructFieldOmitEmptyMarshalJSONPtr OpType = 357
|
||||
OpStructEndMarshalJSONPtr OpType = 358
|
||||
OpStructEndOmitEmptyMarshalJSONPtr OpType = 359
|
||||
OpStructFieldMarshalTextPtr OpType = 360
|
||||
OpStructFieldOmitEmptyMarshalTextPtr OpType = 361
|
||||
OpStructEndMarshalTextPtr OpType = 362
|
||||
OpStructEndOmitEmptyMarshalTextPtr OpType = 363
|
||||
OpStructFieldInterfacePtr OpType = 364
|
||||
OpStructFieldOmitEmptyInterfacePtr OpType = 365
|
||||
OpStructEndInterfacePtr OpType = 366
|
||||
OpStructEndOmitEmptyInterfacePtr OpType = 367
|
||||
OpStructFieldIntPtrString OpType = 368
|
||||
OpStructFieldOmitEmptyIntPtrString OpType = 369
|
||||
OpStructEndIntPtrString OpType = 370
|
||||
OpStructEndOmitEmptyIntPtrString OpType = 371
|
||||
OpStructFieldUintPtrString OpType = 372
|
||||
OpStructFieldOmitEmptyUintPtrString OpType = 373
|
||||
OpStructEndUintPtrString OpType = 374
|
||||
OpStructEndOmitEmptyUintPtrString OpType = 375
|
||||
OpStructFieldFloat32PtrString OpType = 376
|
||||
OpStructFieldOmitEmptyFloat32PtrString OpType = 377
|
||||
OpStructEndFloat32PtrString OpType = 378
|
||||
OpStructEndOmitEmptyFloat32PtrString OpType = 379
|
||||
OpStructFieldFloat64PtrString OpType = 380
|
||||
OpStructFieldOmitEmptyFloat64PtrString OpType = 381
|
||||
OpStructEndFloat64PtrString OpType = 382
|
||||
OpStructEndOmitEmptyFloat64PtrString OpType = 383
|
||||
OpStructFieldBoolPtrString OpType = 384
|
||||
OpStructFieldOmitEmptyBoolPtrString OpType = 385
|
||||
OpStructEndBoolPtrString OpType = 386
|
||||
OpStructEndOmitEmptyBoolPtrString OpType = 387
|
||||
OpStructFieldStringPtrString OpType = 388
|
||||
OpStructFieldOmitEmptyStringPtrString OpType = 389
|
||||
OpStructEndStringPtrString OpType = 390
|
||||
OpStructEndOmitEmptyStringPtrString OpType = 391
|
||||
OpStructFieldNumberPtrString OpType = 392
|
||||
OpStructFieldOmitEmptyNumberPtrString OpType = 393
|
||||
OpStructEndNumberPtrString OpType = 394
|
||||
OpStructEndOmitEmptyNumberPtrString OpType = 395
|
||||
OpStructField OpType = 396
|
||||
OpStructFieldOmitEmpty OpType = 397
|
||||
OpStructEnd OpType = 398
|
||||
OpStructEndOmitEmpty OpType = 399
|
||||
)
|
||||
|
||||
func (t OpType) String() string {
|
||||
if int(t) >= 400 {
|
||||
return ""
|
||||
}
|
||||
return opTypeStrings[int(t)]
|
||||
}
|
||||
|
||||
func (t OpType) CodeType() CodeType {
|
||||
if strings.Contains(t.String(), "Struct") {
|
||||
if strings.Contains(t.String(), "End") {
|
||||
return CodeStructEnd
|
||||
}
|
||||
return CodeStructField
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func (t OpType) HeadToPtrHead() OpType {
|
||||
if strings.Index(t.String(), "PtrHead") > 0 {
|
||||
return t
|
||||
}
|
||||
|
||||
idx := strings.Index(t.String(), "Head")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
suffix := "PtrHead" + t.String()[idx+len("Head"):]
|
||||
|
||||
const toPtrOffset = 2
|
||||
if strings.Contains(OpType(int(t)+toPtrOffset).String(), suffix) {
|
||||
return OpType(int(t) + toPtrOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t OpType) HeadToOmitEmptyHead() OpType {
|
||||
const toOmitEmptyOffset = 1
|
||||
if strings.Contains(OpType(int(t)+toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return OpType(int(t) + toOmitEmptyOffset)
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func (t OpType) PtrHeadToHead() OpType {
|
||||
idx := strings.Index(t.String(), "PtrHead")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
suffix := t.String()[idx+len("Ptr"):]
|
||||
|
||||
const toPtrOffset = 2
|
||||
if strings.Contains(OpType(int(t)-toPtrOffset).String(), suffix) {
|
||||
return OpType(int(t) - toPtrOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
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" {
|
||||
return t
|
||||
}
|
||||
const toEndOffset = 2
|
||||
if strings.Contains(OpType(int(t)+toEndOffset).String(), "End"+suffix) {
|
||||
return OpType(int(t) + toEndOffset)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t OpType) FieldToOmitEmptyField() OpType {
|
||||
const toOmitEmptyOffset = 1
|
||||
if strings.Contains(OpType(int(t)+toOmitEmptyOffset).String(), "OmitEmpty") {
|
||||
return OpType(int(t) + toOmitEmptyOffset)
|
||||
}
|
||||
return t
|
||||
}
|
135
internal/encoder/query.go
Normal file
135
internal/encoder/query.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
Marshal func(interface{}) ([]byte, error)
|
||||
Unmarshal func([]byte, interface{}) error
|
||||
)
|
||||
|
||||
type FieldQuery struct {
|
||||
Name string
|
||||
Fields []*FieldQuery
|
||||
hash string
|
||||
}
|
||||
|
||||
func (q *FieldQuery) Hash() string {
|
||||
if q.hash != "" {
|
||||
return q.hash
|
||||
}
|
||||
b, _ := Marshal(q)
|
||||
q.hash = string(b)
|
||||
return q.hash
|
||||
}
|
||||
|
||||
func (q *FieldQuery) MarshalJSON() ([]byte, error) {
|
||||
if q.Name != "" {
|
||||
if len(q.Fields) > 0 {
|
||||
return Marshal(map[string][]*FieldQuery{q.Name: q.Fields})
|
||||
}
|
||||
return Marshal(q.Name)
|
||||
}
|
||||
return Marshal(q.Fields)
|
||||
}
|
||||
|
||||
func (q *FieldQuery) QueryString() (FieldQueryString, error) {
|
||||
b, err := Marshal(q)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return FieldQueryString(b), nil
|
||||
}
|
||||
|
||||
type FieldQueryString string
|
||||
|
||||
func (s FieldQueryString) Build() (*FieldQuery, error) {
|
||||
var query interface{}
|
||||
if err := Unmarshal([]byte(s), &query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.build(reflect.ValueOf(query))
|
||||
}
|
||||
|
||||
func (s FieldQueryString) build(v reflect.Value) (*FieldQuery, error) {
|
||||
switch v.Type().Kind() {
|
||||
case reflect.String:
|
||||
return s.buildString(v)
|
||||
case reflect.Map:
|
||||
return s.buildMap(v)
|
||||
case reflect.Slice:
|
||||
return s.buildSlice(v)
|
||||
case reflect.Interface:
|
||||
return s.build(reflect.ValueOf(v.Interface()))
|
||||
}
|
||||
return nil, fmt.Errorf("failed to build field query")
|
||||
}
|
||||
|
||||
func (s FieldQueryString) buildString(v reflect.Value) (*FieldQuery, error) {
|
||||
b := []byte(v.String())
|
||||
switch b[0] {
|
||||
case '[', '{':
|
||||
var query interface{}
|
||||
if err := Unmarshal(b, &query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if str, ok := query.(string); ok {
|
||||
return &FieldQuery{Name: str}, nil
|
||||
}
|
||||
return s.build(reflect.ValueOf(query))
|
||||
}
|
||||
return &FieldQuery{Name: string(b)}, nil
|
||||
}
|
||||
|
||||
func (s FieldQueryString) buildSlice(v reflect.Value) (*FieldQuery, error) {
|
||||
fields := make([]*FieldQuery, 0, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
def, err := s.build(v.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields = append(fields, def)
|
||||
}
|
||||
return &FieldQuery{Fields: fields}, nil
|
||||
}
|
||||
|
||||
func (s FieldQueryString) buildMap(v reflect.Value) (*FieldQuery, error) {
|
||||
keys := v.MapKeys()
|
||||
if len(keys) != 1 {
|
||||
return nil, fmt.Errorf("failed to build field query object")
|
||||
}
|
||||
key := keys[0]
|
||||
if key.Type().Kind() != reflect.String {
|
||||
return nil, fmt.Errorf("failed to build field query. invalid object key type")
|
||||
}
|
||||
name := key.String()
|
||||
def, err := s.build(v.MapIndex(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FieldQuery{
|
||||
Name: name,
|
||||
Fields: def.Fields,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type queryKey struct{}
|
||||
|
||||
func FieldQueryFromContext(ctx context.Context) *FieldQuery {
|
||||
query := ctx.Value(queryKey{})
|
||||
if query == nil {
|
||||
return nil
|
||||
}
|
||||
q, ok := query.(*FieldQuery)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func SetFieldQueryToContext(ctx context.Context, query *FieldQuery) context.Context {
|
||||
return context.WithValue(ctx, queryKey{}, query)
|
||||
}
|
483
internal/encoder/string.go
Normal file
483
internal/encoder/string.go
Normal file
|
@ -0,0 +1,483 @@
|
|||
// This files's string processing codes are inspired by https://github.com/segmentio/encoding.
|
||||
// The license notation is as follows.
|
||||
//
|
||||
// # MIT License
|
||||
//
|
||||
// Copyright (c) 2019 Segment.io, Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
lsb = 0x0101010101010101
|
||||
msb = 0x8080808080808080
|
||||
)
|
||||
|
||||
var hex = "0123456789abcdef"
|
||||
|
||||
//nolint:govet
|
||||
func stringToUint64Slice(s string) []uint64 {
|
||||
return *(*[]uint64)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: ((*reflect.StringHeader)(unsafe.Pointer(&s))).Data,
|
||||
Len: len(s) / 8,
|
||||
Cap: len(s) / 8,
|
||||
}))
|
||||
}
|
||||
|
||||
func AppendString(ctx *RuntimeContext, buf []byte, s string) []byte {
|
||||
if ctx.Option.Flag&HTMLEscapeOption != 0 {
|
||||
if ctx.Option.Flag&NormalizeUTF8Option != 0 {
|
||||
return appendNormalizedHTMLString(buf, s)
|
||||
}
|
||||
return appendHTMLString(buf, s)
|
||||
}
|
||||
if ctx.Option.Flag&NormalizeUTF8Option != 0 {
|
||||
return appendNormalizedString(buf, s)
|
||||
}
|
||||
return appendString(buf, s)
|
||||
}
|
||||
|
||||
func appendNormalizedHTMLString(buf []byte, s string) []byte {
|
||||
valLen := len(s)
|
||||
if valLen == 0 {
|
||||
return append(buf, `""`...)
|
||||
}
|
||||
buf = append(buf, '"')
|
||||
var (
|
||||
i, j int
|
||||
)
|
||||
if valLen >= 8 {
|
||||
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 {
|
||||
j = bits.TrailingZeros64(mask&msb) / 8
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
for i := len(chunks) * 8; i < valLen; i++ {
|
||||
if needEscapeHTMLNormalizeUTF8[s[i]] {
|
||||
j = i
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
// no found any escape characters.
|
||||
return append(append(buf, s...), '"')
|
||||
}
|
||||
ESCAPE_END:
|
||||
for j < valLen {
|
||||
c := s[j]
|
||||
|
||||
if !needEscapeHTMLNormalizeUTF8[c] {
|
||||
// fast path: most of the time, printable ascii characters are used
|
||||
j++
|
||||
continue
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\\', '"':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', c)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\n':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'n')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\r':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'r')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\t':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 't')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '<', '>', '&':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
state, size := decodeRuneInString(s[j:])
|
||||
switch state {
|
||||
case runeErrorState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\ufffd`...)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
// U+2028 is LINE SEPARATOR.
|
||||
// U+2029 is PARAGRAPH SEPARATOR.
|
||||
// They are both technically valid characters in JSON strings,
|
||||
// but don't work in JSONP, which has to be evaluated as JavaScript,
|
||||
// and can lead to security holes there. It is valid JSON to
|
||||
// escape them, so we do so unconditionally.
|
||||
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
|
||||
case lineSepState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u2028`...)
|
||||
i = j + 3
|
||||
j = j + 3
|
||||
continue
|
||||
case paragraphSepState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u2029`...)
|
||||
i = j + 3
|
||||
j = j + 3
|
||||
continue
|
||||
}
|
||||
j += size
|
||||
}
|
||||
|
||||
return append(append(buf, s[i:]...), '"')
|
||||
}
|
||||
|
||||
func appendHTMLString(buf []byte, s string) []byte {
|
||||
valLen := len(s)
|
||||
if valLen == 0 {
|
||||
return append(buf, `""`...)
|
||||
}
|
||||
buf = append(buf, '"')
|
||||
var (
|
||||
i, j int
|
||||
)
|
||||
if valLen >= 8 {
|
||||
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 {
|
||||
j = bits.TrailingZeros64(mask&msb) / 8
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
for i := len(chunks) * 8; i < valLen; i++ {
|
||||
if needEscapeHTML[s[i]] {
|
||||
j = i
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
// no found any escape characters.
|
||||
return append(append(buf, s...), '"')
|
||||
}
|
||||
ESCAPE_END:
|
||||
for j < valLen {
|
||||
c := s[j]
|
||||
|
||||
if !needEscapeHTML[c] {
|
||||
// fast path: most of the time, printable ascii characters are used
|
||||
j++
|
||||
continue
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\\', '"':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', c)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\n':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'n')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\r':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'r')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\t':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 't')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '<', '>', '&':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
return append(append(buf, s[i:]...), '"')
|
||||
}
|
||||
|
||||
func appendNormalizedString(buf []byte, s string) []byte {
|
||||
valLen := len(s)
|
||||
if valLen == 0 {
|
||||
return append(buf, `""`...)
|
||||
}
|
||||
buf = append(buf, '"')
|
||||
var (
|
||||
i, j int
|
||||
)
|
||||
if valLen >= 8 {
|
||||
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)
|
||||
if (mask & msb) != 0 {
|
||||
j = bits.TrailingZeros64(mask&msb) / 8
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
valLen := len(s)
|
||||
for i := len(chunks) * 8; i < valLen; i++ {
|
||||
if needEscapeNormalizeUTF8[s[i]] {
|
||||
j = i
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
return append(append(buf, s...), '"')
|
||||
}
|
||||
ESCAPE_END:
|
||||
for j < valLen {
|
||||
c := s[j]
|
||||
|
||||
if !needEscapeNormalizeUTF8[c] {
|
||||
// fast path: most of the time, printable ascii characters are used
|
||||
j++
|
||||
continue
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\\', '"':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', c)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\n':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'n')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\r':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'r')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\t':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 't')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
|
||||
state, size := decodeRuneInString(s[j:])
|
||||
switch state {
|
||||
case runeErrorState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\ufffd`...)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
// U+2028 is LINE SEPARATOR.
|
||||
// U+2029 is PARAGRAPH SEPARATOR.
|
||||
// They are both technically valid characters in JSON strings,
|
||||
// but don't work in JSONP, which has to be evaluated as JavaScript,
|
||||
// and can lead to security holes there. It is valid JSON to
|
||||
// escape them, so we do so unconditionally.
|
||||
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
|
||||
case lineSepState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u2028`...)
|
||||
i = j + 3
|
||||
j = j + 3
|
||||
continue
|
||||
case paragraphSepState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u2029`...)
|
||||
i = j + 3
|
||||
j = j + 3
|
||||
continue
|
||||
}
|
||||
j += size
|
||||
}
|
||||
|
||||
return append(append(buf, s[i:]...), '"')
|
||||
}
|
||||
|
||||
func appendString(buf []byte, s string) []byte {
|
||||
valLen := len(s)
|
||||
if valLen == 0 {
|
||||
return append(buf, `""`...)
|
||||
}
|
||||
buf = append(buf, '"')
|
||||
var (
|
||||
i, j int
|
||||
)
|
||||
if valLen >= 8 {
|
||||
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)
|
||||
if (mask & msb) != 0 {
|
||||
j = bits.TrailingZeros64(mask&msb) / 8
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
valLen := len(s)
|
||||
for i := len(chunks) * 8; i < valLen; i++ {
|
||||
if needEscape[s[i]] {
|
||||
j = i
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
return append(append(buf, s...), '"')
|
||||
}
|
||||
ESCAPE_END:
|
||||
for j < valLen {
|
||||
c := s[j]
|
||||
|
||||
if !needEscape[c] {
|
||||
// fast path: most of the time, printable ascii characters are used
|
||||
j++
|
||||
continue
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\\', '"':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', c)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\n':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'n')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\r':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'r')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\t':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 't')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
return append(append(buf, s[i:]...), '"')
|
||||
}
|
415
internal/encoder/string_table.go
Normal file
415
internal/encoder/string_table.go
Normal file
|
@ -0,0 +1,415 @@
|
|||
package encoder
|
||||
|
||||
var needEscapeHTMLNormalizeUTF8 = [256]bool{
|
||||
'"': true,
|
||||
'&': true,
|
||||
'<': true,
|
||||
'>': true,
|
||||
'\\': true,
|
||||
0x00: true,
|
||||
0x01: true,
|
||||
0x02: true,
|
||||
0x03: true,
|
||||
0x04: true,
|
||||
0x05: true,
|
||||
0x06: true,
|
||||
0x07: true,
|
||||
0x08: true,
|
||||
0x09: true,
|
||||
0x0a: true,
|
||||
0x0b: true,
|
||||
0x0c: true,
|
||||
0x0d: true,
|
||||
0x0e: true,
|
||||
0x0f: true,
|
||||
0x10: true,
|
||||
0x11: true,
|
||||
0x12: true,
|
||||
0x13: true,
|
||||
0x14: true,
|
||||
0x15: true,
|
||||
0x16: true,
|
||||
0x17: true,
|
||||
0x18: true,
|
||||
0x19: true,
|
||||
0x1a: true,
|
||||
0x1b: true,
|
||||
0x1c: true,
|
||||
0x1d: true,
|
||||
0x1e: true,
|
||||
0x1f: true,
|
||||
/* 0x20 - 0x7f */
|
||||
0x80: true,
|
||||
0x81: true,
|
||||
0x82: true,
|
||||
0x83: true,
|
||||
0x84: true,
|
||||
0x85: true,
|
||||
0x86: true,
|
||||
0x87: true,
|
||||
0x88: true,
|
||||
0x89: true,
|
||||
0x8a: true,
|
||||
0x8b: true,
|
||||
0x8c: true,
|
||||
0x8d: true,
|
||||
0x8e: true,
|
||||
0x8f: true,
|
||||
0x90: true,
|
||||
0x91: true,
|
||||
0x92: true,
|
||||
0x93: true,
|
||||
0x94: true,
|
||||
0x95: true,
|
||||
0x96: true,
|
||||
0x97: true,
|
||||
0x98: true,
|
||||
0x99: true,
|
||||
0x9a: true,
|
||||
0x9b: true,
|
||||
0x9c: true,
|
||||
0x9d: true,
|
||||
0x9e: true,
|
||||
0x9f: true,
|
||||
0xa0: true,
|
||||
0xa1: true,
|
||||
0xa2: true,
|
||||
0xa3: true,
|
||||
0xa4: true,
|
||||
0xa5: true,
|
||||
0xa6: true,
|
||||
0xa7: true,
|
||||
0xa8: true,
|
||||
0xa9: true,
|
||||
0xaa: true,
|
||||
0xab: true,
|
||||
0xac: true,
|
||||
0xad: true,
|
||||
0xae: true,
|
||||
0xaf: true,
|
||||
0xb0: true,
|
||||
0xb1: true,
|
||||
0xb2: true,
|
||||
0xb3: true,
|
||||
0xb4: true,
|
||||
0xb5: true,
|
||||
0xb6: true,
|
||||
0xb7: true,
|
||||
0xb8: true,
|
||||
0xb9: true,
|
||||
0xba: true,
|
||||
0xbb: true,
|
||||
0xbc: true,
|
||||
0xbd: true,
|
||||
0xbe: true,
|
||||
0xbf: true,
|
||||
0xc0: true,
|
||||
0xc1: true,
|
||||
0xc2: true,
|
||||
0xc3: true,
|
||||
0xc4: true,
|
||||
0xc5: true,
|
||||
0xc6: true,
|
||||
0xc7: true,
|
||||
0xc8: true,
|
||||
0xc9: true,
|
||||
0xca: true,
|
||||
0xcb: true,
|
||||
0xcc: true,
|
||||
0xcd: true,
|
||||
0xce: true,
|
||||
0xcf: true,
|
||||
0xd0: true,
|
||||
0xd1: true,
|
||||
0xd2: true,
|
||||
0xd3: true,
|
||||
0xd4: true,
|
||||
0xd5: true,
|
||||
0xd6: true,
|
||||
0xd7: true,
|
||||
0xd8: true,
|
||||
0xd9: true,
|
||||
0xda: true,
|
||||
0xdb: true,
|
||||
0xdc: true,
|
||||
0xdd: true,
|
||||
0xde: true,
|
||||
0xdf: true,
|
||||
0xe0: true,
|
||||
0xe1: true,
|
||||
0xe2: true,
|
||||
0xe3: true,
|
||||
0xe4: true,
|
||||
0xe5: true,
|
||||
0xe6: true,
|
||||
0xe7: true,
|
||||
0xe8: true,
|
||||
0xe9: true,
|
||||
0xea: true,
|
||||
0xeb: true,
|
||||
0xec: true,
|
||||
0xed: true,
|
||||
0xee: true,
|
||||
0xef: true,
|
||||
0xf0: true,
|
||||
0xf1: true,
|
||||
0xf2: true,
|
||||
0xf3: true,
|
||||
0xf4: true,
|
||||
0xf5: true,
|
||||
0xf6: true,
|
||||
0xf7: true,
|
||||
0xf8: true,
|
||||
0xf9: true,
|
||||
0xfa: true,
|
||||
0xfb: true,
|
||||
0xfc: true,
|
||||
0xfd: true,
|
||||
0xfe: true,
|
||||
0xff: true,
|
||||
}
|
||||
|
||||
var needEscapeNormalizeUTF8 = [256]bool{
|
||||
'"': true,
|
||||
'\\': true,
|
||||
0x00: true,
|
||||
0x01: true,
|
||||
0x02: true,
|
||||
0x03: true,
|
||||
0x04: true,
|
||||
0x05: true,
|
||||
0x06: true,
|
||||
0x07: true,
|
||||
0x08: true,
|
||||
0x09: true,
|
||||
0x0a: true,
|
||||
0x0b: true,
|
||||
0x0c: true,
|
||||
0x0d: true,
|
||||
0x0e: true,
|
||||
0x0f: true,
|
||||
0x10: true,
|
||||
0x11: true,
|
||||
0x12: true,
|
||||
0x13: true,
|
||||
0x14: true,
|
||||
0x15: true,
|
||||
0x16: true,
|
||||
0x17: true,
|
||||
0x18: true,
|
||||
0x19: true,
|
||||
0x1a: true,
|
||||
0x1b: true,
|
||||
0x1c: true,
|
||||
0x1d: true,
|
||||
0x1e: true,
|
||||
0x1f: true,
|
||||
/* 0x20 - 0x7f */
|
||||
0x80: true,
|
||||
0x81: true,
|
||||
0x82: true,
|
||||
0x83: true,
|
||||
0x84: true,
|
||||
0x85: true,
|
||||
0x86: true,
|
||||
0x87: true,
|
||||
0x88: true,
|
||||
0x89: true,
|
||||
0x8a: true,
|
||||
0x8b: true,
|
||||
0x8c: true,
|
||||
0x8d: true,
|
||||
0x8e: true,
|
||||
0x8f: true,
|
||||
0x90: true,
|
||||
0x91: true,
|
||||
0x92: true,
|
||||
0x93: true,
|
||||
0x94: true,
|
||||
0x95: true,
|
||||
0x96: true,
|
||||
0x97: true,
|
||||
0x98: true,
|
||||
0x99: true,
|
||||
0x9a: true,
|
||||
0x9b: true,
|
||||
0x9c: true,
|
||||
0x9d: true,
|
||||
0x9e: true,
|
||||
0x9f: true,
|
||||
0xa0: true,
|
||||
0xa1: true,
|
||||
0xa2: true,
|
||||
0xa3: true,
|
||||
0xa4: true,
|
||||
0xa5: true,
|
||||
0xa6: true,
|
||||
0xa7: true,
|
||||
0xa8: true,
|
||||
0xa9: true,
|
||||
0xaa: true,
|
||||
0xab: true,
|
||||
0xac: true,
|
||||
0xad: true,
|
||||
0xae: true,
|
||||
0xaf: true,
|
||||
0xb0: true,
|
||||
0xb1: true,
|
||||
0xb2: true,
|
||||
0xb3: true,
|
||||
0xb4: true,
|
||||
0xb5: true,
|
||||
0xb6: true,
|
||||
0xb7: true,
|
||||
0xb8: true,
|
||||
0xb9: true,
|
||||
0xba: true,
|
||||
0xbb: true,
|
||||
0xbc: true,
|
||||
0xbd: true,
|
||||
0xbe: true,
|
||||
0xbf: true,
|
||||
0xc0: true,
|
||||
0xc1: true,
|
||||
0xc2: true,
|
||||
0xc3: true,
|
||||
0xc4: true,
|
||||
0xc5: true,
|
||||
0xc6: true,
|
||||
0xc7: true,
|
||||
0xc8: true,
|
||||
0xc9: true,
|
||||
0xca: true,
|
||||
0xcb: true,
|
||||
0xcc: true,
|
||||
0xcd: true,
|
||||
0xce: true,
|
||||
0xcf: true,
|
||||
0xd0: true,
|
||||
0xd1: true,
|
||||
0xd2: true,
|
||||
0xd3: true,
|
||||
0xd4: true,
|
||||
0xd5: true,
|
||||
0xd6: true,
|
||||
0xd7: true,
|
||||
0xd8: true,
|
||||
0xd9: true,
|
||||
0xda: true,
|
||||
0xdb: true,
|
||||
0xdc: true,
|
||||
0xdd: true,
|
||||
0xde: true,
|
||||
0xdf: true,
|
||||
0xe0: true,
|
||||
0xe1: true,
|
||||
0xe2: true,
|
||||
0xe3: true,
|
||||
0xe4: true,
|
||||
0xe5: true,
|
||||
0xe6: true,
|
||||
0xe7: true,
|
||||
0xe8: true,
|
||||
0xe9: true,
|
||||
0xea: true,
|
||||
0xeb: true,
|
||||
0xec: true,
|
||||
0xed: true,
|
||||
0xee: true,
|
||||
0xef: true,
|
||||
0xf0: true,
|
||||
0xf1: true,
|
||||
0xf2: true,
|
||||
0xf3: true,
|
||||
0xf4: true,
|
||||
0xf5: true,
|
||||
0xf6: true,
|
||||
0xf7: true,
|
||||
0xf8: true,
|
||||
0xf9: true,
|
||||
0xfa: true,
|
||||
0xfb: true,
|
||||
0xfc: true,
|
||||
0xfd: true,
|
||||
0xfe: true,
|
||||
0xff: true,
|
||||
}
|
||||
|
||||
var needEscapeHTML = [256]bool{
|
||||
'"': true,
|
||||
'&': true,
|
||||
'<': true,
|
||||
'>': true,
|
||||
'\\': true,
|
||||
0x00: true,
|
||||
0x01: true,
|
||||
0x02: true,
|
||||
0x03: true,
|
||||
0x04: true,
|
||||
0x05: true,
|
||||
0x06: true,
|
||||
0x07: true,
|
||||
0x08: true,
|
||||
0x09: true,
|
||||
0x0a: true,
|
||||
0x0b: true,
|
||||
0x0c: true,
|
||||
0x0d: true,
|
||||
0x0e: true,
|
||||
0x0f: true,
|
||||
0x10: true,
|
||||
0x11: true,
|
||||
0x12: true,
|
||||
0x13: true,
|
||||
0x14: true,
|
||||
0x15: true,
|
||||
0x16: true,
|
||||
0x17: true,
|
||||
0x18: true,
|
||||
0x19: true,
|
||||
0x1a: true,
|
||||
0x1b: true,
|
||||
0x1c: true,
|
||||
0x1d: true,
|
||||
0x1e: true,
|
||||
0x1f: true,
|
||||
/* 0x20 - 0xff */
|
||||
}
|
||||
|
||||
var needEscape = [256]bool{
|
||||
'"': true,
|
||||
'\\': true,
|
||||
0x00: true,
|
||||
0x01: true,
|
||||
0x02: true,
|
||||
0x03: true,
|
||||
0x04: true,
|
||||
0x05: true,
|
||||
0x06: true,
|
||||
0x07: true,
|
||||
0x08: true,
|
||||
0x09: true,
|
||||
0x0a: true,
|
||||
0x0b: true,
|
||||
0x0c: true,
|
||||
0x0d: true,
|
||||
0x0e: true,
|
||||
0x0f: true,
|
||||
0x10: true,
|
||||
0x11: true,
|
||||
0x12: true,
|
||||
0x13: true,
|
||||
0x14: true,
|
||||
0x15: true,
|
||||
0x16: true,
|
||||
0x17: true,
|
||||
0x18: true,
|
||||
0x19: true,
|
||||
0x1a: true,
|
||||
0x1b: true,
|
||||
0x1c: true,
|
||||
0x1d: true,
|
||||
0x1e: true,
|
||||
0x1f: true,
|
||||
/* 0x20 - 0xff */
|
||||
}
|
41
internal/encoder/vm/debug_vm.go
Normal file
41
internal/encoder/vm/debug_vm.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
)
|
||||
|
||||
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
|
||||
defer func() {
|
||||
var code *encoder.Opcode
|
||||
if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 {
|
||||
code = codeSet.EscapeKeyCode
|
||||
} else {
|
||||
code = codeSet.NoescapeKeyCode
|
||||
}
|
||||
if wc := ctx.Option.DebugDOTOut; wc != nil {
|
||||
_, _ = io.WriteString(wc, code.DumpDOT())
|
||||
wc.Close()
|
||||
ctx.Option.DebugDOTOut = nil
|
||||
}
|
||||
|
||||
if err := recover(); err != nil {
|
||||
w := ctx.Option.DebugOut
|
||||
fmt.Fprintln(w, "=============[DEBUG]===============")
|
||||
fmt.Fprintln(w, "* [TYPE]")
|
||||
fmt.Fprintln(w, codeSet.Type)
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintln(w, "* [ALL OPCODE]")
|
||||
fmt.Fprintln(w, code.Dump())
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintln(w, "* [CONTEXT]")
|
||||
fmt.Fprintf(w, "%+v\n", ctx)
|
||||
fmt.Fprintln(w, "===================================")
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return Run(ctx, b, codeSet)
|
||||
}
|
9
internal/encoder/vm/hack.go
Normal file
9
internal/encoder/vm/hack.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
// HACK: compile order
|
||||
// `vm`, `vm_indent`, `vm_color`, `vm_color_indent` packages uses a lot of memory to compile,
|
||||
// so forcibly make dependencies and avoid compiling in concurrent.
|
||||
// dependency order: vm => vm_indent => vm_color => vm_color_indent
|
||||
_ "github.com/goccy/go-json/internal/encoder/vm_indent"
|
||||
)
|
207
internal/encoder/vm/util.go
Normal file
207
internal/encoder/vm/util.go
Normal file
|
@ -0,0 +1,207 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||
|
||||
var (
|
||||
appendInt = encoder.AppendInt
|
||||
appendUint = encoder.AppendUint
|
||||
appendFloat32 = encoder.AppendFloat32
|
||||
appendFloat64 = encoder.AppendFloat64
|
||||
appendString = encoder.AppendString
|
||||
appendByteSlice = encoder.AppendByteSlice
|
||||
appendNumber = encoder.AppendNumber
|
||||
errUnsupportedValue = encoder.ErrUnsupportedValue
|
||||
errUnsupportedFloat = encoder.ErrUnsupportedFloat
|
||||
mapiterinit = encoder.MapIterInit
|
||||
mapiterkey = encoder.MapIterKey
|
||||
mapitervalue = encoder.MapIterValue
|
||||
mapiternext = encoder.MapIterNext
|
||||
maplen = encoder.MapLen
|
||||
)
|
||||
|
||||
type emptyInterface struct {
|
||||
typ *runtime.Type
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type nonEmptyInterface struct {
|
||||
itab *struct {
|
||||
ityp *runtime.Type // static interface type
|
||||
typ *runtime.Type // dynamic concrete type
|
||||
// unused fields...
|
||||
}
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func errUnimplementedOp(op encoder.OpType) error {
|
||||
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
|
||||
}
|
||||
|
||||
func load(base uintptr, idx uint32) uintptr {
|
||||
addr := base + uintptr(idx)
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func store(base uintptr, idx uint32, p uintptr) {
|
||||
addr := base + uintptr(idx)
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
|
||||
addr := base + uintptr(idx)
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
for i := uint8(0); i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = ptrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr, bitSize uint8) uint64 {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
return (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
return (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
return (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
return **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
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 uint8) uintptr {
|
||||
for i := uint8(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(_ *encoder.RuntimeContext, b []byte, v bool) []byte {
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
}
|
||||
|
||||
func appendNull(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendNullComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, "null,"...)
|
||||
}
|
||||
|
||||
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = ':'
|
||||
return b
|
||||
}
|
||||
|
||||
func appendMapKeyValue(_ *encoder.RuntimeContext, _ *encoder.Opcode, b, key, value []byte) []byte {
|
||||
b = append(b, key...)
|
||||
b[len(b)-1] = ':'
|
||||
return append(b, value...)
|
||||
}
|
||||
|
||||
func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
b[len(b)-1] = '}'
|
||||
b = append(b, ',')
|
||||
return b
|
||||
}
|
||||
|
||||
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
return encoder.AppendMarshalJSON(ctx, code, b, v)
|
||||
}
|
||||
|
||||
func appendMarshalText(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
return encoder.AppendMarshalText(ctx, code, b, v)
|
||||
}
|
||||
|
||||
func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
return append(b, '[')
|
||||
}
|
||||
|
||||
func appendArrayEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = ']'
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '[', ']', ',')
|
||||
}
|
||||
|
||||
func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '{', '}', ',')
|
||||
}
|
||||
|
||||
func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = '}'
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '{')
|
||||
}
|
||||
|
||||
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
return append(b, code.Key...)
|
||||
}
|
||||
|
||||
func appendStructEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
return append(b, '}', ',')
|
||||
}
|
||||
|
||||
func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
if b[last] == ',' {
|
||||
b[last] = '}'
|
||||
return appendComma(ctx, b)
|
||||
}
|
||||
return appendStructEnd(ctx, code, b)
|
||||
}
|
||||
|
||||
func restoreIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, _ uintptr) {}
|
||||
func storeIndent(_ uintptr, _ *encoder.Opcode, _ uintptr) {}
|
||||
func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
|
||||
func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
|
4859
internal/encoder/vm/vm.go
Normal file
4859
internal/encoder/vm/vm.go
Normal file
File diff suppressed because it is too large
Load diff
35
internal/encoder/vm_color/debug_vm.go
Normal file
35
internal/encoder/vm_color/debug_vm.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package vm_color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
)
|
||||
|
||||
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
|
||||
var code *encoder.Opcode
|
||||
if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 {
|
||||
code = codeSet.EscapeKeyCode
|
||||
} else {
|
||||
code = codeSet.NoescapeKeyCode
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
w := ctx.Option.DebugOut
|
||||
fmt.Fprintln(w, "=============[DEBUG]===============")
|
||||
fmt.Fprintln(w, "* [TYPE]")
|
||||
fmt.Fprintln(w, codeSet.Type)
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintln(w, "* [ALL OPCODE]")
|
||||
fmt.Fprintln(w, code.Dump())
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintln(w, "* [CONTEXT]")
|
||||
fmt.Fprintf(w, "%+v\n", ctx)
|
||||
fmt.Fprintln(w, "===================================")
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return Run(ctx, b, codeSet)
|
||||
}
|
9
internal/encoder/vm_color/hack.go
Normal file
9
internal/encoder/vm_color/hack.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package vm_color
|
||||
|
||||
import (
|
||||
// HACK: compile order
|
||||
// `vm`, `vm_indent`, `vm_color`, `vm_color_indent` packages uses a lot of memory to compile,
|
||||
// so forcibly make dependencies and avoid compiling in concurrent.
|
||||
// dependency order: vm => vm_indent => vm_color => vm_color_indent
|
||||
_ "github.com/goccy/go-json/internal/encoder/vm_color_indent"
|
||||
)
|
274
internal/encoder/vm_color/util.go
Normal file
274
internal/encoder/vm_color/util.go
Normal file
|
@ -0,0 +1,274 @@
|
|||
package vm_color
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||
|
||||
var (
|
||||
errUnsupportedValue = encoder.ErrUnsupportedValue
|
||||
errUnsupportedFloat = encoder.ErrUnsupportedFloat
|
||||
mapiterinit = encoder.MapIterInit
|
||||
mapiterkey = encoder.MapIterKey
|
||||
mapitervalue = encoder.MapIterValue
|
||||
mapiternext = encoder.MapIterNext
|
||||
maplen = encoder.MapLen
|
||||
)
|
||||
|
||||
type emptyInterface struct {
|
||||
typ *runtime.Type
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type nonEmptyInterface struct {
|
||||
itab *struct {
|
||||
ityp *runtime.Type // static interface type
|
||||
typ *runtime.Type // dynamic concrete type
|
||||
// unused fields...
|
||||
}
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func errUnimplementedOp(op encoder.OpType) error {
|
||||
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
|
||||
}
|
||||
|
||||
func load(base uintptr, idx uint32) uintptr {
|
||||
addr := base + uintptr(idx)
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func store(base uintptr, idx uint32, p uintptr) {
|
||||
addr := base + uintptr(idx)
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
|
||||
addr := base + uintptr(idx)
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
for i := uint8(0); i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = ptrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr, bitSize uint8) uint64 {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
return (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
return (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
return (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
return **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
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 uint8) uintptr {
|
||||
for i := uint8(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 appendInt(ctx *encoder.RuntimeContext, b []byte, p uintptr, code *encoder.Opcode) []byte {
|
||||
format := ctx.Option.ColorScheme.Int
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendInt(ctx, b, p, code)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendUint(ctx *encoder.RuntimeContext, b []byte, p uintptr, code *encoder.Opcode) []byte {
|
||||
format := ctx.Option.ColorScheme.Uint
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendUint(ctx, b, p, code)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendFloat32(ctx *encoder.RuntimeContext, b []byte, v float32) []byte {
|
||||
format := ctx.Option.ColorScheme.Float
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendFloat32(ctx, b, v)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendFloat64(ctx *encoder.RuntimeContext, b []byte, v float64) []byte {
|
||||
format := ctx.Option.ColorScheme.Float
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendFloat64(ctx, b, v)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendString(ctx *encoder.RuntimeContext, b []byte, v string) []byte {
|
||||
format := ctx.Option.ColorScheme.String
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendString(ctx, b, v)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendByteSlice(ctx *encoder.RuntimeContext, b []byte, src []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.Binary
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendByteSlice(ctx, b, src)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendNumber(ctx *encoder.RuntimeContext, b []byte, n json.Number) ([]byte, error) {
|
||||
format := ctx.Option.ColorScheme.Int
|
||||
b = append(b, format.Header...)
|
||||
bb, err := encoder.AppendNumber(ctx, b, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(bb, format.Footer...), nil
|
||||
}
|
||||
|
||||
func appendBool(ctx *encoder.RuntimeContext, b []byte, v bool) []byte {
|
||||
format := ctx.Option.ColorScheme.Bool
|
||||
b = append(b, format.Header...)
|
||||
if v {
|
||||
b = append(b, "true"...)
|
||||
} else {
|
||||
b = append(b, "false"...)
|
||||
}
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendNull(ctx *encoder.RuntimeContext, b []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.Null
|
||||
b = append(b, format.Header...)
|
||||
b = append(b, "null"...)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendNullComma(ctx *encoder.RuntimeContext, b []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.Null
|
||||
b = append(b, format.Header...)
|
||||
b = append(b, "null"...)
|
||||
return append(append(b, format.Footer...), ',')
|
||||
}
|
||||
|
||||
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = ':'
|
||||
return b
|
||||
}
|
||||
|
||||
func appendMapKeyValue(_ *encoder.RuntimeContext, _ *encoder.Opcode, b, key, value []byte) []byte {
|
||||
b = append(b, key[:len(key)-1]...)
|
||||
b = append(b, ':')
|
||||
return append(b, value...)
|
||||
}
|
||||
|
||||
func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = '}'
|
||||
b = append(b, ',')
|
||||
return b
|
||||
}
|
||||
|
||||
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
return encoder.AppendMarshalJSON(ctx, code, b, v)
|
||||
}
|
||||
|
||||
func appendMarshalText(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
format := ctx.Option.ColorScheme.String
|
||||
b = append(b, format.Header...)
|
||||
bb, err := encoder.AppendMarshalText(ctx, code, b, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(bb, format.Footer...), nil
|
||||
}
|
||||
|
||||
func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
return append(b, '[')
|
||||
}
|
||||
|
||||
func appendArrayEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = ']'
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '[', ']', ',')
|
||||
}
|
||||
|
||||
func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '{', '}', ',')
|
||||
}
|
||||
|
||||
func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = '}'
|
||||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '{')
|
||||
}
|
||||
|
||||
func appendStructKey(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.ObjectKey
|
||||
b = append(b, format.Header...)
|
||||
b = append(b, code.Key[:len(code.Key)-1]...)
|
||||
b = append(b, format.Footer...)
|
||||
|
||||
return append(b, ':')
|
||||
}
|
||||
|
||||
func appendStructEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
|
||||
return append(b, '}', ',')
|
||||
}
|
||||
|
||||
func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
if b[last] == ',' {
|
||||
b[last] = '}'
|
||||
return appendComma(ctx, b)
|
||||
}
|
||||
return appendStructEnd(ctx, code, b)
|
||||
}
|
||||
|
||||
func restoreIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, _ uintptr) {}
|
||||
func storeIndent(_ uintptr, _ *encoder.Opcode, _ uintptr) {}
|
||||
func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
|
||||
func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b }
|
4859
internal/encoder/vm_color/vm.go
Normal file
4859
internal/encoder/vm_color/vm.go
Normal file
File diff suppressed because it is too large
Load diff
35
internal/encoder/vm_color_indent/debug_vm.go
Normal file
35
internal/encoder/vm_color_indent/debug_vm.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package vm_color_indent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
)
|
||||
|
||||
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
|
||||
var code *encoder.Opcode
|
||||
if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 {
|
||||
code = codeSet.EscapeKeyCode
|
||||
} else {
|
||||
code = codeSet.NoescapeKeyCode
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
w := ctx.Option.DebugOut
|
||||
fmt.Fprintln(w, "=============[DEBUG]===============")
|
||||
fmt.Fprintln(w, "* [TYPE]")
|
||||
fmt.Fprintln(w, codeSet.Type)
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintln(w, "* [ALL OPCODE]")
|
||||
fmt.Fprintln(w, code.Dump())
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintln(w, "* [CONTEXT]")
|
||||
fmt.Fprintf(w, "%+v\n", ctx)
|
||||
fmt.Fprintln(w, "===================================")
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return Run(ctx, b, codeSet)
|
||||
}
|
297
internal/encoder/vm_color_indent/util.go
Normal file
297
internal/encoder/vm_color_indent/util.go
Normal file
|
@ -0,0 +1,297 @@
|
|||
package vm_color_indent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||
|
||||
var (
|
||||
appendIndent = encoder.AppendIndent
|
||||
appendStructEnd = encoder.AppendStructEndIndent
|
||||
errUnsupportedValue = encoder.ErrUnsupportedValue
|
||||
errUnsupportedFloat = encoder.ErrUnsupportedFloat
|
||||
mapiterinit = encoder.MapIterInit
|
||||
mapiterkey = encoder.MapIterKey
|
||||
mapitervalue = encoder.MapIterValue
|
||||
mapiternext = encoder.MapIterNext
|
||||
maplen = encoder.MapLen
|
||||
)
|
||||
|
||||
type emptyInterface struct {
|
||||
typ *runtime.Type
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type nonEmptyInterface struct {
|
||||
itab *struct {
|
||||
ityp *runtime.Type // static interface type
|
||||
typ *runtime.Type // dynamic concrete type
|
||||
// unused fields...
|
||||
}
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func errUnimplementedOp(op encoder.OpType) error {
|
||||
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
|
||||
}
|
||||
|
||||
func load(base uintptr, idx uint32) uintptr {
|
||||
addr := base + uintptr(idx)
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func store(base uintptr, idx uint32, p uintptr) {
|
||||
addr := base + uintptr(idx)
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
|
||||
addr := base + uintptr(idx)
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
for i := uint8(0); i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = ptrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr, bitSize uint8) uint64 {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
return (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
return (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
return (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
return **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
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 uint8) uintptr {
|
||||
for i := uint8(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 appendInt(ctx *encoder.RuntimeContext, b []byte, p uintptr, code *encoder.Opcode) []byte {
|
||||
format := ctx.Option.ColorScheme.Int
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendInt(ctx, b, p, code)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendUint(ctx *encoder.RuntimeContext, b []byte, p uintptr, code *encoder.Opcode) []byte {
|
||||
format := ctx.Option.ColorScheme.Uint
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendUint(ctx, b, p, code)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendFloat32(ctx *encoder.RuntimeContext, b []byte, v float32) []byte {
|
||||
format := ctx.Option.ColorScheme.Float
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendFloat32(ctx, b, v)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendFloat64(ctx *encoder.RuntimeContext, b []byte, v float64) []byte {
|
||||
format := ctx.Option.ColorScheme.Float
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendFloat64(ctx, b, v)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendString(ctx *encoder.RuntimeContext, b []byte, v string) []byte {
|
||||
format := ctx.Option.ColorScheme.String
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendString(ctx, b, v)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendByteSlice(ctx *encoder.RuntimeContext, b []byte, src []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.Binary
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendByteSlice(ctx, b, src)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendNumber(ctx *encoder.RuntimeContext, b []byte, n json.Number) ([]byte, error) {
|
||||
format := ctx.Option.ColorScheme.Int
|
||||
b = append(b, format.Header...)
|
||||
bb, err := encoder.AppendNumber(ctx, b, n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(bb, format.Footer...), nil
|
||||
}
|
||||
|
||||
func appendBool(ctx *encoder.RuntimeContext, b []byte, v bool) []byte {
|
||||
format := ctx.Option.ColorScheme.Bool
|
||||
b = append(b, format.Header...)
|
||||
if v {
|
||||
b = append(b, "true"...)
|
||||
} else {
|
||||
b = append(b, "false"...)
|
||||
}
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendNull(ctx *encoder.RuntimeContext, b []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.Null
|
||||
b = append(b, format.Header...)
|
||||
b = append(b, "null"...)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func appendNullComma(ctx *encoder.RuntimeContext, b []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.Null
|
||||
b = append(b, format.Header...)
|
||||
b = append(b, "null"...)
|
||||
return append(append(b, format.Footer...), ',', '\n')
|
||||
}
|
||||
|
||||
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b[:len(b)-2], ':', ' ')
|
||||
}
|
||||
|
||||
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
|
||||
b = appendIndent(ctx, b, code.Indent+1)
|
||||
b = append(b, key...)
|
||||
b[len(b)-2] = ':'
|
||||
b[len(b)-1] = ' '
|
||||
return append(b, value...)
|
||||
}
|
||||
|
||||
func appendMapEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
b = b[:len(b)-2]
|
||||
b = append(b, '\n')
|
||||
b = appendIndent(ctx, b, code.Indent)
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func appendArrayHead(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
b = append(b, '[', '\n')
|
||||
return appendIndent(ctx, b, code.Indent+1)
|
||||
}
|
||||
|
||||
func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
b = b[:len(b)-2]
|
||||
b = append(b, '\n')
|
||||
b = appendIndent(ctx, b, code.Indent)
|
||||
return append(b, ']', ',', '\n')
|
||||
}
|
||||
|
||||
func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '[', ']', ',', '\n')
|
||||
}
|
||||
|
||||
func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '{', '}', ',', '\n')
|
||||
}
|
||||
|
||||
func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
// replace comma to newline
|
||||
b[last-1] = '\n'
|
||||
b = appendIndent(ctx, b[:last], code.Indent)
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
return encoder.AppendMarshalJSONIndent(ctx, code, b, v)
|
||||
}
|
||||
|
||||
func appendMarshalText(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
format := ctx.Option.ColorScheme.String
|
||||
b = append(b, format.Header...)
|
||||
bb, err := encoder.AppendMarshalTextIndent(ctx, code, b, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(bb, format.Footer...), nil
|
||||
}
|
||||
|
||||
func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '{', '\n')
|
||||
}
|
||||
|
||||
func appendStructKey(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
b = appendIndent(ctx, b, code.Indent)
|
||||
|
||||
format := ctx.Option.ColorScheme.ObjectKey
|
||||
b = append(b, format.Header...)
|
||||
b = append(b, code.Key[:len(code.Key)-1]...)
|
||||
b = append(b, format.Footer...)
|
||||
|
||||
return append(b, ':', ' ')
|
||||
}
|
||||
|
||||
func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
if b[last-1] == '{' {
|
||||
b[last] = '}'
|
||||
} else {
|
||||
if b[last] == '\n' {
|
||||
// to remove ',' and '\n' characters
|
||||
b = b[:len(b)-2]
|
||||
}
|
||||
b = append(b, '\n')
|
||||
b = appendIndent(ctx, b, code.Indent-1)
|
||||
b = append(b, '}')
|
||||
}
|
||||
return appendComma(ctx, b)
|
||||
}
|
||||
|
||||
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {
|
||||
ctx.BaseIndent = uint32(load(ctxptr, code.Length))
|
||||
}
|
||||
|
||||
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {
|
||||
store(ctxptr, code.Length, indent)
|
||||
}
|
||||
|
||||
func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
return appendIndent(ctx, b, code.Indent+1)
|
||||
}
|
||||
|
||||
func appendMapKeyIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
return appendIndent(ctx, b, code.Indent)
|
||||
}
|
4859
internal/encoder/vm_color_indent/vm.go
Normal file
4859
internal/encoder/vm_color_indent/vm.go
Normal file
File diff suppressed because it is too large
Load diff
35
internal/encoder/vm_indent/debug_vm.go
Normal file
35
internal/encoder/vm_indent/debug_vm.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package vm_indent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
)
|
||||
|
||||
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
|
||||
var code *encoder.Opcode
|
||||
if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 {
|
||||
code = codeSet.EscapeKeyCode
|
||||
} else {
|
||||
code = codeSet.NoescapeKeyCode
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
w := ctx.Option.DebugOut
|
||||
fmt.Fprintln(w, "=============[DEBUG]===============")
|
||||
fmt.Fprintln(w, "* [TYPE]")
|
||||
fmt.Fprintln(w, codeSet.Type)
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintln(w, "* [ALL OPCODE]")
|
||||
fmt.Fprintln(w, code.Dump())
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintln(w, "* [CONTEXT]")
|
||||
fmt.Fprintf(w, "%+v\n", ctx)
|
||||
fmt.Fprintln(w, "===================================")
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
return Run(ctx, b, codeSet)
|
||||
}
|
9
internal/encoder/vm_indent/hack.go
Normal file
9
internal/encoder/vm_indent/hack.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package vm_indent
|
||||
|
||||
import (
|
||||
// HACK: compile order
|
||||
// `vm`, `vm_indent`, `vm_color`, `vm_color_indent` packages uses a lot of memory to compile,
|
||||
// so forcibly make dependencies and avoid compiling in concurrent.
|
||||
// dependency order: vm => vm_indent => vm_color => vm_color_indent
|
||||
_ "github.com/goccy/go-json/internal/encoder/vm_color"
|
||||
)
|
230
internal/encoder/vm_indent/util.go
Normal file
230
internal/encoder/vm_indent/util.go
Normal file
|
@ -0,0 +1,230 @@
|
|||
package vm_indent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||
|
||||
var (
|
||||
appendInt = encoder.AppendInt
|
||||
appendUint = encoder.AppendUint
|
||||
appendFloat32 = encoder.AppendFloat32
|
||||
appendFloat64 = encoder.AppendFloat64
|
||||
appendString = encoder.AppendString
|
||||
appendByteSlice = encoder.AppendByteSlice
|
||||
appendNumber = encoder.AppendNumber
|
||||
appendStructEnd = encoder.AppendStructEndIndent
|
||||
appendIndent = encoder.AppendIndent
|
||||
errUnsupportedValue = encoder.ErrUnsupportedValue
|
||||
errUnsupportedFloat = encoder.ErrUnsupportedFloat
|
||||
mapiterinit = encoder.MapIterInit
|
||||
mapiterkey = encoder.MapIterKey
|
||||
mapitervalue = encoder.MapIterValue
|
||||
mapiternext = encoder.MapIterNext
|
||||
maplen = encoder.MapLen
|
||||
)
|
||||
|
||||
type emptyInterface struct {
|
||||
typ *runtime.Type
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type nonEmptyInterface struct {
|
||||
itab *struct {
|
||||
ityp *runtime.Type // static interface type
|
||||
typ *runtime.Type // dynamic concrete type
|
||||
// unused fields...
|
||||
}
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func errUnimplementedOp(op encoder.OpType) error {
|
||||
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
|
||||
}
|
||||
|
||||
func load(base uintptr, idx uint32) uintptr {
|
||||
addr := base + uintptr(idx)
|
||||
return **(**uintptr)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func store(base uintptr, idx uint32, p uintptr) {
|
||||
addr := base + uintptr(idx)
|
||||
**(**uintptr)(unsafe.Pointer(&addr)) = p
|
||||
}
|
||||
|
||||
func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
|
||||
addr := base + uintptr(idx)
|
||||
p := **(**uintptr)(unsafe.Pointer(&addr))
|
||||
for i := uint8(0); i < ptrNum; i++ {
|
||||
if p == 0 {
|
||||
return 0
|
||||
}
|
||||
p = ptrToPtr(p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr, bitSize uint8) uint64 {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
return (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
return (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
return (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
return **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
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 uint8) uintptr {
|
||||
for i := uint8(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(_ *encoder.RuntimeContext, b []byte, v bool) []byte {
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
}
|
||||
|
||||
func appendNull(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, "null"...)
|
||||
}
|
||||
|
||||
func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func appendNullComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, "null,\n"...)
|
||||
}
|
||||
|
||||
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b[:len(b)-2], ':', ' ')
|
||||
}
|
||||
|
||||
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
|
||||
b = appendIndent(ctx, b, code.Indent+1)
|
||||
b = append(b, key...)
|
||||
b[len(b)-2] = ':'
|
||||
b[len(b)-1] = ' '
|
||||
return append(b, value...)
|
||||
}
|
||||
|
||||
func appendMapEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
b = b[:len(b)-2]
|
||||
b = append(b, '\n')
|
||||
b = appendIndent(ctx, b, code.Indent)
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func appendArrayHead(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
b = append(b, '[', '\n')
|
||||
return appendIndent(ctx, b, code.Indent+1)
|
||||
}
|
||||
|
||||
func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
b = b[:len(b)-2]
|
||||
b = append(b, '\n')
|
||||
b = appendIndent(ctx, b, code.Indent)
|
||||
return append(b, ']', ',', '\n')
|
||||
}
|
||||
|
||||
func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '[', ']', ',', '\n')
|
||||
}
|
||||
|
||||
func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '{', '}', ',', '\n')
|
||||
}
|
||||
|
||||
func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
// replace comma to newline
|
||||
b[last-1] = '\n'
|
||||
b = appendIndent(ctx, b[:last], code.Indent)
|
||||
return append(b, '}', ',', '\n')
|
||||
}
|
||||
|
||||
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
return encoder.AppendMarshalJSONIndent(ctx, code, b, v)
|
||||
}
|
||||
|
||||
func appendMarshalText(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
return encoder.AppendMarshalTextIndent(ctx, code, b, v)
|
||||
}
|
||||
|
||||
func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, '{', '\n')
|
||||
}
|
||||
|
||||
func appendStructKey(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
b = appendIndent(ctx, b, code.Indent)
|
||||
b = append(b, code.Key...)
|
||||
return append(b, ' ')
|
||||
}
|
||||
|
||||
func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
if b[last-1] == '{' {
|
||||
b[last] = '}'
|
||||
} else {
|
||||
if b[last] == '\n' {
|
||||
// to remove ',' and '\n' characters
|
||||
b = b[:len(b)-2]
|
||||
}
|
||||
b = append(b, '\n')
|
||||
b = appendIndent(ctx, b, code.Indent-1)
|
||||
b = append(b, '}')
|
||||
}
|
||||
return appendComma(ctx, b)
|
||||
}
|
||||
|
||||
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {
|
||||
ctx.BaseIndent = uint32(load(ctxptr, code.Length))
|
||||
}
|
||||
|
||||
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {
|
||||
store(ctxptr, code.Length, indent)
|
||||
}
|
||||
|
||||
func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
return appendIndent(ctx, b, code.Indent+1)
|
||||
}
|
||||
|
||||
func appendMapKeyIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
return appendIndent(ctx, b, code.Indent)
|
||||
}
|
4859
internal/encoder/vm_indent/vm.go
Normal file
4859
internal/encoder/vm_indent/vm.go
Normal file
File diff suppressed because it is too large
Load diff
183
internal/errors/error.go
Normal file
183
internal/errors/error.go
Normal file
|
@ -0,0 +1,183 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrInvalidBeginningOfValue(c byte, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("invalid character '%c' looking for beginning of value", c),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
|
||||
type PathError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *PathError) Error() string {
|
||||
return fmt.Sprintf("json: invalid path format: %s", e.msg)
|
||||
}
|
||||
|
||||
func ErrInvalidPath(msg string, args ...interface{}) *PathError {
|
||||
if len(args) != 0 {
|
||||
return &PathError{msg: fmt.Sprintf(msg, args...)}
|
||||
}
|
||||
return &PathError{msg: msg}
|
||||
}
|
||||
|
||||
func ErrEmptyPath() *PathError {
|
||||
return &PathError{msg: "path is empty"}
|
||||
}
|
262
internal/runtime/rtype.go
Normal file
262
internal/runtime/rtype.go
Normal file
|
@ -0,0 +1,262 @@
|
|||
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
|
||||
|
||||
type emptyInterface struct {
|
||||
_ *Type
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func Type2RType(t reflect.Type) *Type {
|
||||
return (*Type)(((*emptyInterface)(unsafe.Pointer(&t))).ptr)
|
||||
}
|
91
internal/runtime/struct_field.go
Normal file
91
internal/runtime/struct_field.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func getTag(field reflect.StructField) string {
|
||||
return field.Tag.Get("json")
|
||||
}
|
||||
|
||||
func IsIgnoredStructField(field reflect.StructField) bool {
|
||||
if field.PkgPath != "" {
|
||||
if field.Anonymous {
|
||||
t := field.Type
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
if t.Kind() != reflect.Struct {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// private field
|
||||
return true
|
||||
}
|
||||
}
|
||||
tag := getTag(field)
|
||||
return tag == "-"
|
||||
}
|
||||
|
||||
type StructTag struct {
|
||||
Key string
|
||||
IsTaggedKey bool
|
||||
IsOmitEmpty bool
|
||||
IsString bool
|
||||
Field reflect.StructField
|
||||
}
|
||||
|
||||
type StructTags []*StructTag
|
||||
|
||||
func (t StructTags) ExistsKey(key string) bool {
|
||||
for _, tt := range t {
|
||||
if tt.Key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isValidTag(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
switch {
|
||||
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
|
||||
// Backslash and quote chars are reserved, but
|
||||
// otherwise any punctuation chars are allowed
|
||||
// in a tag name.
|
||||
case !unicode.IsLetter(c) && !unicode.IsDigit(c):
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func StructTagFromField(field reflect.StructField) *StructTag {
|
||||
keyName := field.Name
|
||||
tag := getTag(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.Key = keyName
|
||||
if len(opts) > 1 {
|
||||
for _, opt := range opts[1:] {
|
||||
switch opt {
|
||||
case "omitempty":
|
||||
st.IsOmitEmpty = true
|
||||
case "string":
|
||||
st.IsString = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return st
|
||||
}
|
98
internal/runtime/type.go
Normal file
98
internal/runtime/type.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"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
|
||||
AddrShift uintptr
|
||||
}
|
||||
|
||||
var (
|
||||
typeAddr *TypeAddr
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
//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 {
|
||||
once.Do(func() {
|
||||
sections, offsets := typelinks()
|
||||
if len(sections) != 1 {
|
||||
return
|
||||
}
|
||||
if len(offsets) != 1 {
|
||||
return
|
||||
}
|
||||
section := sections[0]
|
||||
offset := offsets[0]
|
||||
var (
|
||||
min uintptr = uintptr(^uint(0))
|
||||
max uintptr = 0
|
||||
isAligned64 = true
|
||||
isAligned32 = true
|
||||
)
|
||||
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
|
||||
}
|
||||
}
|
||||
isAligned64 = isAligned64 && (addr-min)&63 == 0
|
||||
isAligned32 = isAligned32 && (addr-min)&31 == 0
|
||||
}
|
||||
addrRange := max - min
|
||||
if addrRange == 0 {
|
||||
return
|
||||
}
|
||||
var addrShift uintptr
|
||||
if isAligned64 {
|
||||
addrShift = 6
|
||||
} else if isAligned32 {
|
||||
addrShift = 5
|
||||
}
|
||||
cacheSize := addrRange >> addrShift
|
||||
if cacheSize > maxAcceptableTypeAddrRange {
|
||||
return
|
||||
}
|
||||
typeAddr = &TypeAddr{
|
||||
BaseTypeAddr: min,
|
||||
MaxTypeAddr: max,
|
||||
AddrRange: addrRange,
|
||||
AddrShift: addrShift,
|
||||
}
|
||||
})
|
||||
|
||||
return typeAddr
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue