1
0
Fork 0
forked from forgejo/forgejo

Upgrade xorm to v1.2.2 (#16663)

* Upgrade xorm to v1.2.2

* Change the Engine interface to match xorm v1.2.2
This commit is contained in:
Lunny Xiao 2021-08-13 07:11:42 +08:00 committed by GitHub
parent 5fbccad906
commit 7224cfc578
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
134 changed files with 42889 additions and 5428 deletions

View file

@ -0,0 +1,37 @@
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))
}

View file

@ -0,0 +1,169 @@
package decoder
import (
"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 {
zeroValue := *(*unsafe.Pointer)(unsafe_New(elemType))
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)
}
}
}

View file

@ -0,0 +1,78 @@
package decoder
import (
"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)
}

View file

@ -0,0 +1,177 @@
package decoder
import (
"encoding/base64"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type bytesDecoder struct {
typ *runtime.Type
sliceDecoder Decoder
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)
}
if unmarshalDecoder == nil {
return nil
}
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),
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 binaryBytes(s *Stream) ([]byte, error) {
s.cursor++
start := s.cursor
for {
switch s.char() {
case '"':
literal := s.buf[start:s.cursor]
s.cursor++
return literal, nil
case nul:
if s.read() {
continue
}
goto ERROR
}
s.cursor++
}
ERROR:
return nil, errors.ErrUnexpectedEndOfJSON("[]byte", s.totalOffset())
}
func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Pointer) ([]byte, error) {
for {
switch s.char() {
case ' ', '\n', '\t', '\r':
s.cursor++
continue
case '"':
return binaryBytes(s)
case 'n':
if err := nullBytes(s); err != nil {
return nil, err
}
return nil, nil
case '[':
if d.sliceDecoder == nil {
return nil, &errors.UnmarshalTypeError{
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
}
if err := d.sliceDecoder.DecodeStream(s, depth, p); err != nil {
return nil, err
}
return nil, nil
case nul:
if s.read() {
continue
}
}
break
}
return nil, errors.ErrNotAtBeginningOfValue(s.totalOffset())
}
func (d *bytesDecoder) decodeBinary(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) ([]byte, int64, error) {
buf := ctx.Buf
for {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
cursor++
case '"':
cursor++
start := cursor
for {
switch buf[cursor] {
case '"':
literal := buf[start:cursor]
cursor++
return literal, cursor, nil
case nul:
return nil, 0, errors.ErrUnexpectedEndOfJSON("[]byte", cursor)
}
cursor++
}
case '[':
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
case 'n':
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return nil, cursor, nil
default:
return nil, 0, errors.ErrNotAtBeginningOfValue(cursor)
}
}
}

View file

@ -0,0 +1,510 @@
package decoder
import (
"encoding/json"
"fmt"
"reflect"
"strings"
"sync/atomic"
"unicode"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
var (
jsonNumberType = reflect.TypeOf(json.Number(""))
typeAddr *runtime.TypeAddr
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
cachedDecoder []Decoder
)
func init() {
typeAddr = runtime.AnalyzeTypeAddr()
if typeAddr == nil {
typeAddr = &runtime.TypeAddr{}
}
cachedDecoder = make([]Decoder, typeAddr.AddrRange>>typeAddr.AddrShift)
}
func loadDecoderMap() map[uintptr]Decoder {
p := atomic.LoadPointer(&cachedDecoderMap)
return *(*map[uintptr]Decoder)(unsafe.Pointer(&p))
}
func storeDecoder(typ uintptr, dec Decoder, m map[uintptr]Decoder) {
newDecoderMap := make(map[uintptr]Decoder, len(m)+1)
newDecoderMap[typ] = dec
for k, v := range m {
newDecoderMap[k] = v
}
atomic.StorePointer(&cachedDecoderMap, *(*unsafe.Pointer)(unsafe.Pointer(&newDecoderMap)))
}
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 nil, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(typ),
Offset: 0,
Struct: structName,
Field: fieldName,
}
}
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
}
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:
goto ERROR
}
}
ERROR:
return nil, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(typ),
Offset: 0,
Struct: structName,
Field: fieldName,
}
}
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 removeConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, field reflect.StructField) {
for k, v := range dec.fieldMap {
if _, exists := conflictedMap[k]; exists {
// already conflicted key
continue
}
set, exists := fieldMap[k]
if !exists {
fieldSet := &structFieldSet{
dec: v.dec,
offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
continue
}
if set.isTaggedKey {
if v.isTaggedKey {
// conflict tag key
delete(fieldMap, k)
delete(fieldMap, strings.ToLower(k))
conflictedMap[k] = struct{}{}
conflictedMap[strings.ToLower(k)] = struct{}{}
}
} else {
if v.isTaggedKey {
fieldSet := &structFieldSet{
dec: v.dec,
offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
} else {
// conflict tag key
delete(fieldMap, k)
delete(fieldMap, strings.ToLower(k))
conflictedMap[k] = struct{}{}
conflictedMap[strings.ToLower(k)] = struct{}{}
}
}
}
}
func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
fieldNum := typ.NumField()
conflictedMap := map[string]struct{}{}
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()
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
}
removeConflictFields(fieldMap, conflictedMap, stDec, field)
} 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 _, exists := conflictedMap[k]; exists {
// already conflicted key
continue
}
set, exists := fieldMap[k]
if !exists {
fieldSet := &structFieldSet{
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
offset: field.Offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
err: fieldSetErr,
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
continue
}
if set.isTaggedKey {
if v.isTaggedKey {
// conflict tag key
delete(fieldMap, k)
delete(fieldMap, strings.ToLower(k))
conflictedMap[k] = struct{}{}
conflictedMap[strings.ToLower(k)] = struct{}{}
}
} else {
if v.isTaggedKey {
fieldSet := &structFieldSet{
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
offset: field.Offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
err: fieldSetErr,
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
} else {
// conflict tag key
delete(fieldMap, k)
delete(fieldMap, strings.ToLower(k))
conflictedMap[k] = struct{}{}
conflictedMap[strings.ToLower(k)] = struct{}{}
}
}
}
}
}
} 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)),
}
fieldMap[key] = fieldSet
lower := strings.ToLower(key)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
}
}
delete(structTypeToDecoder, typeptr)
structDec.tryOptimize()
return structDec, nil
}
func implementsUnmarshalJSONType(typ *runtime.Type) bool {
return typ.Implements(unmarshalJSONType) || typ.Implements(unmarshalJSONContextType)
}

View file

@ -0,0 +1,28 @@
// +build !race
package decoder
import (
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) {
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
}

View file

@ -0,0 +1,36 @@
// +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) {
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
}

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

View file

@ -0,0 +1,158 @@
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
}

View file

@ -0,0 +1,141 @@
package decoder
import (
"bytes"
"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.ErrNotAtBeginningOfValue(start)
}
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 0, errors.ErrNotAtBeginningOfValue(start)
}

242
vendor/github.com/goccy/go-json/internal/decoder/int.go generated vendored Normal file
View file

@ -0,0 +1,242 @@
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
}

View file

@ -0,0 +1,458 @@
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())
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.ErrNotAtBeginningOfValue(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.ErrNotAtBeginningOfValue(cursor)
}

173
vendor/github.com/goccy/go-json/internal/decoder/map.go generated vendored Normal file
View file

@ -0,0 +1,173 @@
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
stringKeyType 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,
stringKeyType: keyType.Kind() == reflect.String,
valueType: valueType,
valueDecoder: valueDec,
structName: structName,
fieldName: fieldName,
}
}
//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.stringKeyType {
mapV := mapassign_faststr(d.mapType, 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)
}
if s.buf[s.cursor+1] == '}' {
*(*unsafe.Pointer)(p) = mapValue
s.cursor += 2
return nil
}
for {
s.cursor++
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())
}
}
}
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++
}
}

View file

@ -0,0 +1,108 @@
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) 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 '"':
return d.stringDecoder.decodeStreamByte(s)
case nul:
if s.read() {
continue
}
goto ERROR
default:
goto ERROR
}
}
ERROR:
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)
}
}
}

View file

@ -0,0 +1,15 @@
package decoder
import "context"
type OptionFlags uint8
const (
FirstWinOption OptionFlags = 1 << iota
ContextOption
)
type Option struct {
Flags OptionFlags
Context context.Context
}

View file

@ -0,0 +1,87 @@
package decoder
import (
"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 (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 {
return 0, err
}
cursor = c
return cursor, nil
}

View file

@ -0,0 +1,294 @@
package decoder
import (
"reflect"
"sync"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
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
}
*(*unsafe.Pointer)(p) = nil
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
*(*unsafe.Pointer)(p) = nil
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)
}
}
}

View file

@ -0,0 +1,554 @@
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 = initBufSize
}
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)
s := *(*string)(unsafe.Pointer(&bytes))
f64, err := strconv.ParseFloat(s, 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.statForRetry()
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.statForRetry()
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.statForRetry()
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() {
s.cursor-- // for retry current character
_, 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())
}

View file

@ -0,0 +1,361 @@
package decoder
import (
"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
}
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 decodeUnicodeRune(s *Stream, p unsafe.Pointer) (rune, int64, unsafe.Pointer, error) {
const defaultOffset = 5
const surrogateOffset = 11
if s.cursor+defaultOffset >= s.length {
if !s.read() {
return rune(0), 0, nil, errors.ErrInvalidCharacter(s.char(), "escaped string", s.totalOffset())
}
p = s.bufptr()
}
r := unicodeToRune(s.buf[s.cursor+1 : s.cursor+defaultOffset])
if utf16.IsSurrogate(r) {
if s.cursor+surrogateOffset >= s.length {
s.read()
p = s.bufptr()
}
if s.cursor+surrogateOffset >= s.length || 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())
}
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--
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
r, _ := utf8.DecodeRune(s.buf[cursor:])
b := []byte(string(r))
if r == utf8.RuneError {
s.buf = append(append(append([]byte{}, s.buf[:cursor]...), b...), s.buf[cursor+1:]...)
_, _, p = s.stat()
}
cursor += int64(len(b))
s.length += int64(len(b))
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.ErrNotAtBeginningOfValue(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
for {
switch char(b, cursor) {
case '\\':
cursor++
switch char(b, cursor) {
case '"':
buf[cursor] = '"'
buf = append(buf[:cursor-1], buf[cursor:]...)
case '\\':
buf[cursor] = '\\'
buf = append(buf[:cursor-1], buf[cursor:]...)
case '/':
buf[cursor] = '/'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'b':
buf[cursor] = '\b'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'f':
buf[cursor] = '\f'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'n':
buf[cursor] = '\n'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'r':
buf[cursor] = '\r'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 't':
buf[cursor] = '\t'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'u':
buflen := int64(len(buf))
if cursor+5 >= buflen {
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
}
code := unicodeToRune(buf[cursor+1 : cursor+5])
unicode := []byte(string(code))
buf = append(append(buf[:cursor-1], unicode...), buf[cursor+5:]...)
default:
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
}
continue
case '"':
literal := buf[start:cursor]
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.ErrNotAtBeginningOfValue(cursor)
}
}
}

View file

@ -0,0 +1,819 @@
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 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 {
// 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) {
const defaultOffset = 4
const surrogateOffset = 6
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
}
cursor += 2
r2 := unicodeToRune(buf[cursor : cursor+defaultOffset])
if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar {
return []byte(string(r)), cursor + defaultOffset - 1
}
}
return []byte(string(r)), cursor + defaultOffset - 1
}
func decodeKeyCharByEscapedChar(buf []byte, cursor int64) ([]byte, int64) {
c := buf[cursor]
cursor++
switch c {
case '"':
return []byte{'"'}, cursor
case '\\':
return []byte{'\\'}, cursor
case '/':
return []byte{'/'}, cursor
case 'b':
return []byte{'\b'}, cursor
case 'f':
return []byte{'\f'}, cursor
case 'n':
return []byte{'\n'}, cursor
case 'r':
return []byte{'\r'}, cursor
case 't':
return []byte{'\t'}, cursor
case 'u':
return decodeKeyCharByUnicodeRune(buf, cursor)
}
return nil, cursor
}
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 := decodeKeyCharByEscapedChar(buf, cursor)
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.ErrNotAtBeginningOfValue(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 := decodeKeyCharByEscapedChar(buf, cursor)
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.ErrNotAtBeginningOfValue(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.ErrNotAtBeginningOfValue(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.ErrNotAtBeginningOfValue(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.ErrNotAtBeginningOfValue(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.ErrNotAtBeginningOfValue(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.ErrNotAtBeginningOfValue(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.ErrNotAtBeginningOfValue(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++
}
}

View file

@ -0,0 +1,29 @@
package decoder
import (
"context"
"encoding"
"encoding/json"
"reflect"
"unsafe"
)
type Decoder interface {
Decode(*RuntimeContext, int64, int64, unsafe.Pointer) (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()
)

View file

@ -0,0 +1,190 @@
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
}

View file

@ -0,0 +1,91 @@
package decoder
import (
"encoding/json"
"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,
}))
if (s.Option.Flags & ContextOption) != 0 {
if err := v.(unmarshalerContext).UnmarshalJSON(s.Option.Context, dst); err != nil {
d.annotateError(s.cursor, err)
return err
}
} else {
if err := v.(json.Unmarshaler).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
}

View file

@ -0,0 +1,280 @@
package decoder
import (
"bytes"
"encoding"
"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 unquoteBytes(s []byte) (t []byte, ok bool) {
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
}

View file

@ -0,0 +1,68 @@
package decoder
import (
"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
}

View 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])
cursor += 2
start = cursor + 3
}
}
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
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,56 @@
// +build !race
package encoder
import (
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
if typeptr > typeAddr.MaxTypeAddr {
return compileToGetCodeSetSlowPath(typeptr)
}
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
return codeSet, nil
}
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
noescapeKeyCode, err := compileHead(&compileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
})
if err != nil {
return nil, err
}
escapeKeyCode, err := compileHead(&compileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
escapeKey: true,
})
if err != nil {
return nil, err
}
noescapeKeyCode = copyOpcode(noescapeKeyCode)
escapeKeyCode = copyOpcode(escapeKeyCode)
setTotalLengthToInterfaceOp(noescapeKeyCode)
setTotalLengthToInterfaceOp(escapeKeyCode)
interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
codeLength := noescapeKeyCode.TotalLength()
codeSet := &OpcodeSet{
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
}
cachedOpcodeSets[index] = codeSet
return codeSet, nil
}

View file

@ -0,0 +1,65 @@
// +build race
package encoder
import (
"sync"
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
var setsMu sync.RWMutex
func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
if typeptr > typeAddr.MaxTypeAddr {
return compileToGetCodeSetSlowPath(typeptr)
}
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
setsMu.RLock()
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
setsMu.RUnlock()
return codeSet, nil
}
setsMu.RUnlock()
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
noescapeKeyCode, err := compileHead(&compileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
})
if err != nil {
return nil, err
}
escapeKeyCode, err := compileHead(&compileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
escapeKey: true,
})
if err != nil {
return nil, err
}
noescapeKeyCode = copyOpcode(noescapeKeyCode)
escapeKeyCode = copyOpcode(escapeKeyCode)
setTotalLengthToInterfaceOp(noescapeKeyCode)
setTotalLengthToInterfaceOp(escapeKeyCode)
interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
codeLength := noescapeKeyCode.TotalLength()
codeSet := &OpcodeSet{
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
}
setsMu.Lock()
cachedOpcodeSets[index] = codeSet
setsMu.Unlock()
return codeSet, nil
}

View file

@ -0,0 +1,141 @@
package encoder
import (
"context"
"sync"
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
type compileContext struct {
typ *runtime.Type
opcodeIndex uint32
ptrIndex int
indent uint32
escapeKey bool
structTypeToCompiledCode map[uintptr]*CompiledCode
parent *compileContext
}
func (c *compileContext) context() *compileContext {
return &compileContext{
typ: c.typ,
opcodeIndex: c.opcodeIndex,
ptrIndex: c.ptrIndex,
indent: c.indent,
escapeKey: c.escapeKey,
structTypeToCompiledCode: c.structTypeToCompiledCode,
parent: c,
}
}
func (c *compileContext) withType(typ *runtime.Type) *compileContext {
ctx := c.context()
ctx.typ = typ
return ctx
}
func (c *compileContext) incIndent() *compileContext {
ctx := c.context()
ctx.indent++
return ctx
}
func (c *compileContext) decIndent() *compileContext {
ctx := c.context()
ctx.indent--
return ctx
}
func (c *compileContext) incIndex() {
c.incOpcodeIndex()
c.incPtrIndex()
}
func (c *compileContext) decIndex() {
c.decOpcodeIndex()
c.decPtrIndex()
}
func (c *compileContext) incOpcodeIndex() {
c.opcodeIndex++
if c.parent != nil {
c.parent.incOpcodeIndex()
}
}
func (c *compileContext) decOpcodeIndex() {
c.opcodeIndex--
if c.parent != nil {
c.parent.decOpcodeIndex()
}
}
func (c *compileContext) incPtrIndex() {
c.ptrIndex++
if c.parent != nil {
c.parent.incPtrIndex()
}
}
func (c *compileContext) decPtrIndex() {
c.ptrIndex--
if c.parent != nil {
c.parent.decPtrIndex()
}
}
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)
}

View file

@ -0,0 +1,551 @@
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
}
type CompiledCode struct {
Code *Opcode
Linked bool // whether recursive code already have linked
CurLen uintptr
NextLen uintptr
}
const StartDetectingCyclesAfter = 1000
func Load(base uintptr, idx uintptr) uintptr {
addr := base + idx
return **(**uintptr)(unsafe.Pointer(&addr))
}
func Store(base uintptr, idx uintptr, p uintptr) {
addr := base + idx
**(**uintptr)(unsafe.Pointer(&addr)) = p
}
func LoadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
addr := base + idx
p := **(**uintptr)(unsafe.Pointer(&addr))
if p == 0 {
return 0
}
return PtrToPtr(p)
/*
for i := 0; i < ptrNum; i++ {
if p == 0 {
return p
}
p = PtrToPtr(p)
}
return p
*/
}
func PtrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
func PtrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
func PtrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
func PtrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
func PtrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
func PtrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) }
func PtrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
func PtrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) }
func PtrToPtr(p uintptr) uintptr {
return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
}
func PtrToNPtr(p uintptr, ptrNum int) uintptr {
for i := 0; i < ptrNum; i++ {
if p == 0 {
return 0
}
p = PtrToPtr(p)
}
return p
}
func PtrToUnsafePtr(p uintptr) unsafe.Pointer {
return *(*unsafe.Pointer)(unsafe.Pointer(&p))
}
func PtrToInterface(code *Opcode, p uintptr) interface{} {
return *(*interface{})(unsafe.Pointer(&emptyInterface{
typ: code.Type,
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
}))
}
func ErrUnsupportedValue(code *Opcode, ptr uintptr) *errors.UnsupportedValueError {
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
typ: code.Type,
ptr: *(*unsafe.Pointer)(unsafe.Pointer(&ptr)),
}))
return &errors.UnsupportedValueError{
Value: reflect.ValueOf(v),
Str: fmt.Sprintf("encountered a cycle via %s", code.Type),
}
}
func ErrUnsupportedFloat(v float64) *errors.UnsupportedValueError {
return &errors.UnsupportedValueError{
Value: reflect.ValueOf(v),
Str: strconv.FormatFloat(v, 'g', -1, 64),
}
}
func ErrMarshalerWithCode(code *Opcode, err error) *errors.MarshalerError {
return &errors.MarshalerError{
Type: runtime.RType2Type(code.Type),
Err: err,
}
}
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
type MapItem struct {
Key []byte
Value []byte
}
type Mapslice struct {
Items []MapItem
}
func (m *Mapslice) Len() int {
return len(m.Items)
}
func (m *Mapslice) Less(i, j int) bool {
return bytes.Compare(m.Items[i].Key, m.Items[j].Key) < 0
}
func (m *Mapslice) Swap(i, j int) {
m.Items[i], m.Items[j] = m.Items[j], m.Items[i]
}
type MapContext struct {
Pos []int
Slice *Mapslice
Buf []byte
}
var mapContextPool = sync.Pool{
New: func() interface{} {
return &MapContext{}
},
}
func NewMapContext(mapLen int) *MapContext {
ctx := mapContextPool.Get().(*MapContext)
if ctx.Slice == nil {
ctx.Slice = &Mapslice{
Items: make([]MapItem, 0, mapLen),
}
}
if cap(ctx.Pos) < (mapLen*2 + 1) {
ctx.Pos = make([]int, 0, mapLen*2+1)
ctx.Slice.Items = make([]MapItem, 0, mapLen)
} else {
ctx.Pos = ctx.Pos[:0]
ctx.Slice.Items = ctx.Slice.Items[:0]
}
ctx.Buf = ctx.Buf[:0]
return ctx
}
func ReleaseMapContext(c *MapContext) {
mapContextPool.Put(c)
}
//go:linkname MapIterInit reflect.mapiterinit
//go:noescape
func MapIterInit(mapType *runtime.Type, m unsafe.Pointer) unsafe.Pointer
//go:linkname MapIterKey reflect.mapiterkey
//go:noescape
func MapIterKey(it unsafe.Pointer) unsafe.Pointer
//go:linkname MapIterNext reflect.mapiternext
//go:noescape
func MapIterNext(it unsafe.Pointer)
//go:linkname MapLen reflect.maplen
//go:noescape
func MapLen(m unsafe.Pointer) int
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
}
}
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)
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
}
return false
}

View 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++
}
}

130
vendor/github.com/goccy/go-json/internal/encoder/int.go generated vendored Normal file
View file

@ -0,0 +1,130 @@
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, u64 uint64, code *Opcode) []byte {
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, u64 uint64, code *Opcode) []byte {
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:]...)
}

View file

@ -0,0 +1,8 @@
// +build !go1.13
package encoder
import "unsafe"
//go:linkname MapIterValue reflect.mapitervalue
func MapIterValue(it unsafe.Pointer) unsafe.Pointer

View file

@ -0,0 +1,8 @@
// +build go1.13
package encoder
import "unsafe"
//go:linkname MapIterValue reflect.mapiterelem
func MapIterValue(it unsafe.Pointer) unsafe.Pointer

View file

@ -0,0 +1,766 @@
package encoder
import (
"fmt"
"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
)
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
PrevField *Opcode // prev struct field
Jmp *CompiledCode // for recursive call
ElemIdx uint32 // offset to access array/slice/map elem
Length uint32 // offset to access slice/map length or array length
MapIter uint32 // offset to access map iterator
MapPos uint32 // offset to access position list for sorted map
Indent uint32 // indent number
Size uint32 // array/slice elem size
DisplayIdx uint32 // opcode index
DisplayKey string // key text to display
}
func (c *Opcode) MaxIdx() uint32 {
max := uint32(0)
for _, value := range []uint32{
c.Idx,
c.ElemIdx,
c.Length,
c.MapIter,
c.MapPos,
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, op OpType) *Opcode {
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
}
func opcodeOffset(idx int) uint32 {
return uint32(idx) * uintptrSize
}
func copyOpcode(code *Opcode) *Opcode {
codeMap := map[uintptr]*Opcode{}
return code.copy(codeMap)
}
func setTotalLengthToInterfaceOp(code *Opcode) {
c := code
for c.Op != OpEnd && c.Op != OpInterfaceEnd {
if c.Op == OpInterface {
c.Length = uint32(code.TotalLength())
}
switch c.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
c = c.End
default:
c = c.Next
}
}
}
func ToEndCode(code *Opcode) *Opcode {
c := code
for c.Op != OpEnd && c.Op != OpInterfaceEnd {
switch c.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
c = c.End
default:
c = c.Next
}
}
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, op OpType, next *Opcode) *Opcode {
return &Opcode{
Op: op,
Idx: opcodeOffset(ctx.ptrIndex),
Next: next,
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
}
}
func newEndOp(ctx *compileContext) *Opcode {
return newOpCodeWithNext(ctx, OpEnd, nil)
}
func (c *Opcode) copy(codeMap map[uintptr]*Opcode) *Opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
copied := &Opcode{
Op: c.Op,
Key: c.Key,
PtrNum: c.PtrNum,
NumBitSize: c.NumBitSize,
Flags: c.Flags,
Idx: c.Idx,
Offset: c.Offset,
Type: c.Type,
DisplayIdx: c.DisplayIdx,
DisplayKey: c.DisplayKey,
ElemIdx: c.ElemIdx,
Length: c.Length,
MapIter: c.MapIter,
MapPos: c.MapPos,
Size: c.Size,
Indent: c.Indent,
}
codeMap[addr] = copied
copied.End = c.End.copy(codeMap)
copied.PrevField = c.PrevField.copy(codeMap)
copied.NextField = c.NextField.copy(codeMap)
copied.Next = c.Next.copy(codeMap)
copied.Jmp = c.Jmp
return copied
}
func (c *Opcode) BeforeLastCode() *Opcode {
code := c
for {
var nextCode *Opcode
switch code.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
nextCode = code.End
default:
nextCode = code.Next
}
if nextCode.Op == OpEnd {
return code
}
code = nextCode
}
}
func (c *Opcode) TotalLength() int {
var idx int
code := c
for code.Op != OpEnd && code.Op != OpInterfaceEnd {
maxIdx := int(code.MaxIdx() / uintptrSize)
if idx < maxIdx {
idx = maxIdx
}
if code.Op == OpRecursiveEnd {
break
}
switch code.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
code = code.End
default:
code = code.Next
}
}
maxIdx := int(code.MaxIdx() / uintptrSize)
if idx < maxIdx {
idx = maxIdx
}
return idx + 1
}
func (c *Opcode) decOpcodeIndex() {
for code := c; code.Op != OpEnd; {
code.DisplayIdx--
if code.Idx > 0 {
code.Idx -= uintptrSize
}
if code.ElemIdx > 0 {
code.ElemIdx -= uintptrSize
}
if code.MapIter > 0 {
code.MapIter -= uintptrSize
}
if code.Length > 0 && code.Op.CodeType() != CodeArrayHead && code.Op.CodeType() != CodeArrayElem {
code.Length -= uintptrSize
}
switch code.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
code = code.End
default:
code = code.Next
}
}
}
func (c *Opcode) decIndent() {
for code := c; code.Op != OpEnd; {
code.Indent--
switch code.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
code = code.End
default:
code = code.Next
}
}
}
func (c *Opcode) dumpHead(code *Opcode) string {
var length uint32
if code.Op.CodeType() == CodeArrayHead {
length = code.Length
} else {
length = code.Length / uintptrSize
}
return fmt.Sprintf(
`[%d]%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(
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.ElemIdx/uintptrSize,
code.Length/uintptrSize,
code.MapIter/uintptrSize,
)
}
func (c *Opcode) dumpMapEnd(code *Opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.MapPos/uintptrSize,
code.Length/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(
`[%d]%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(
`[%d]%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(
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.ElemIdx/uintptrSize,
code.Length/uintptrSize,
code.MapIter/uintptrSize,
)
}
func (c *Opcode) dumpValue(code *Opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.MapIter/uintptrSize,
)
}
func (c *Opcode) Dump() string {
codes := []string{}
for code := c; code.Op != OpEnd && code.Op != OpInterfaceEnd; {
switch code.Op.CodeType() {
case CodeSliceHead:
codes = append(codes, c.dumpHead(code))
code = code.Next
case CodeMapHead:
codes = append(codes, c.dumpMapHead(code))
code = code.Next
case CodeArrayElem, CodeSliceElem:
codes = append(codes, c.dumpElem(code))
code = code.End
case CodeMapKey:
codes = append(codes, c.dumpKey(code))
code = code.End
case CodeMapValue:
codes = append(codes, c.dumpValue(code))
code = code.Next
case CodeMapEnd:
codes = append(codes, c.dumpMapEnd(code))
code = code.Next
case CodeStructField:
codes = append(codes, c.dumpField(code))
code = code.Next
case CodeStructEnd:
codes = append(codes, c.dumpField(code))
code = code.Next
default:
codes = append(codes, fmt.Sprintf(
"[%d]%s%s ([idx:%d])",
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
))
code = code.Next
}
}
return strings.Join(codes, "\n")
}
func prevField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
if _, exists := removedFields[code]; exists {
return prevField(code.PrevField, removedFields)
}
return code
}
func nextField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
if _, exists := removedFields[code]; exists {
return nextField(code.NextField, removedFields)
}
return code
}
func linkPrevToNextField(cur *Opcode, removedFields map[*Opcode]struct{}) {
prev := prevField(cur.PrevField, removedFields)
prev.NextField = nextField(cur.NextField, removedFields)
code := prev
fcode := cur
for {
var nextCode *Opcode
switch code.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
nextCode = code.End
default:
nextCode = code.Next
}
if nextCode == fcode {
code.Next = fcode.Next
break
} else if nextCode.Op == OpEnd {
break
}
code = nextCode
}
}
func newSliceHeaderCode(ctx *compileContext) *Opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpSlice,
Idx: idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: elemIdx,
Length: length,
Indent: ctx.indent,
}
}
func newSliceElemCode(ctx *compileContext, head *Opcode, size uintptr) *Opcode {
return &Opcode{
Op: OpSliceElem,
Idx: head.Idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: head.Length,
Indent: ctx.indent,
Size: uint32(size),
}
}
func newArrayHeaderCode(ctx *compileContext, alen int) *Opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpArray,
Idx: idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: elemIdx,
Indent: ctx.indent,
Length: uint32(alen),
}
}
func newArrayElemCode(ctx *compileContext, head *Opcode, length int, size uintptr) *Opcode {
return &Opcode{
Op: OpArrayElem,
Idx: head.Idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: uint32(length),
Indent: ctx.indent,
Size: uint32(size),
}
}
func newMapHeaderCode(ctx *compileContext) *Opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
mapIter := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpMap,
Idx: idx,
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: elemIdx,
Length: length,
MapIter: mapIter,
Indent: ctx.indent,
}
}
func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
return &Opcode{
Op: OpMapKey,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: head.Length,
MapIter: head.MapIter,
Indent: ctx.indent,
}
}
func newMapValueCode(ctx *compileContext, head *Opcode) *Opcode {
return &Opcode{
Op: OpMapValue,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: head.Length,
MapIter: head.MapIter,
Indent: ctx.indent,
}
}
func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
mapPos := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
idx := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpMapEnd,
Idx: idx,
Next: newEndOp(ctx),
DisplayIdx: ctx.opcodeIndex,
Length: head.Length,
MapPos: mapPos,
Indent: ctx.indent,
}
}
func newInterfaceCode(ctx *compileContext) *Opcode {
return &Opcode{
Op: OpInterface,
Idx: opcodeOffset(ctx.ptrIndex),
Next: newEndOp(ctx),
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
}
}
func newRecursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
return &Opcode{
Op: OpRecursive,
Idx: opcodeOffset(ctx.ptrIndex),
Next: newEndOp(ctx),
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
Jmp: jmp,
}
}

View file

@ -0,0 +1,41 @@
package encoder
import "context"
type OptionFlag uint8
const (
HTMLEscapeOption OptionFlag = 1 << iota
IndentOption
UnorderedMapOption
DebugOption
ColorizeOption
ContextOption
)
type Option struct {
Flag OptionFlag
ColorScheme *ColorScheme
Context context.Context
}
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
)

View file

@ -0,0 +1,934 @@
// 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 = [401]string{
"End",
"Interface",
"Ptr",
"SliceElem",
"SliceEnd",
"ArrayElem",
"ArrayEnd",
"MapKey",
"MapValue",
"MapEnd",
"Recursive",
"RecursivePtr",
"RecursiveEnd",
"InterfaceEnd",
"StructAnonymousEnd",
"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
OpStructAnonymousEnd OpType = 14
OpInt OpType = 15
OpUint OpType = 16
OpFloat32 OpType = 17
OpFloat64 OpType = 18
OpBool OpType = 19
OpString OpType = 20
OpBytes OpType = 21
OpNumber OpType = 22
OpArray OpType = 23
OpMap OpType = 24
OpSlice OpType = 25
OpStruct OpType = 26
OpMarshalJSON OpType = 27
OpMarshalText OpType = 28
OpIntString OpType = 29
OpUintString OpType = 30
OpFloat32String OpType = 31
OpFloat64String OpType = 32
OpBoolString OpType = 33
OpStringString OpType = 34
OpNumberString OpType = 35
OpIntPtr OpType = 36
OpUintPtr OpType = 37
OpFloat32Ptr OpType = 38
OpFloat64Ptr OpType = 39
OpBoolPtr OpType = 40
OpStringPtr OpType = 41
OpBytesPtr OpType = 42
OpNumberPtr OpType = 43
OpArrayPtr OpType = 44
OpMapPtr OpType = 45
OpSlicePtr OpType = 46
OpMarshalJSONPtr OpType = 47
OpMarshalTextPtr OpType = 48
OpInterfacePtr OpType = 49
OpIntPtrString OpType = 50
OpUintPtrString OpType = 51
OpFloat32PtrString OpType = 52
OpFloat64PtrString OpType = 53
OpBoolPtrString OpType = 54
OpStringPtrString OpType = 55
OpNumberPtrString OpType = 56
OpStructHeadInt OpType = 57
OpStructHeadOmitEmptyInt OpType = 58
OpStructPtrHeadInt OpType = 59
OpStructPtrHeadOmitEmptyInt OpType = 60
OpStructHeadUint OpType = 61
OpStructHeadOmitEmptyUint OpType = 62
OpStructPtrHeadUint OpType = 63
OpStructPtrHeadOmitEmptyUint OpType = 64
OpStructHeadFloat32 OpType = 65
OpStructHeadOmitEmptyFloat32 OpType = 66
OpStructPtrHeadFloat32 OpType = 67
OpStructPtrHeadOmitEmptyFloat32 OpType = 68
OpStructHeadFloat64 OpType = 69
OpStructHeadOmitEmptyFloat64 OpType = 70
OpStructPtrHeadFloat64 OpType = 71
OpStructPtrHeadOmitEmptyFloat64 OpType = 72
OpStructHeadBool OpType = 73
OpStructHeadOmitEmptyBool OpType = 74
OpStructPtrHeadBool OpType = 75
OpStructPtrHeadOmitEmptyBool OpType = 76
OpStructHeadString OpType = 77
OpStructHeadOmitEmptyString OpType = 78
OpStructPtrHeadString OpType = 79
OpStructPtrHeadOmitEmptyString OpType = 80
OpStructHeadBytes OpType = 81
OpStructHeadOmitEmptyBytes OpType = 82
OpStructPtrHeadBytes OpType = 83
OpStructPtrHeadOmitEmptyBytes OpType = 84
OpStructHeadNumber OpType = 85
OpStructHeadOmitEmptyNumber OpType = 86
OpStructPtrHeadNumber OpType = 87
OpStructPtrHeadOmitEmptyNumber OpType = 88
OpStructHeadArray OpType = 89
OpStructHeadOmitEmptyArray OpType = 90
OpStructPtrHeadArray OpType = 91
OpStructPtrHeadOmitEmptyArray OpType = 92
OpStructHeadMap OpType = 93
OpStructHeadOmitEmptyMap OpType = 94
OpStructPtrHeadMap OpType = 95
OpStructPtrHeadOmitEmptyMap OpType = 96
OpStructHeadSlice OpType = 97
OpStructHeadOmitEmptySlice OpType = 98
OpStructPtrHeadSlice OpType = 99
OpStructPtrHeadOmitEmptySlice OpType = 100
OpStructHeadStruct OpType = 101
OpStructHeadOmitEmptyStruct OpType = 102
OpStructPtrHeadStruct OpType = 103
OpStructPtrHeadOmitEmptyStruct OpType = 104
OpStructHeadMarshalJSON OpType = 105
OpStructHeadOmitEmptyMarshalJSON OpType = 106
OpStructPtrHeadMarshalJSON OpType = 107
OpStructPtrHeadOmitEmptyMarshalJSON OpType = 108
OpStructHeadMarshalText OpType = 109
OpStructHeadOmitEmptyMarshalText OpType = 110
OpStructPtrHeadMarshalText OpType = 111
OpStructPtrHeadOmitEmptyMarshalText OpType = 112
OpStructHeadIntString OpType = 113
OpStructHeadOmitEmptyIntString OpType = 114
OpStructPtrHeadIntString OpType = 115
OpStructPtrHeadOmitEmptyIntString OpType = 116
OpStructHeadUintString OpType = 117
OpStructHeadOmitEmptyUintString OpType = 118
OpStructPtrHeadUintString OpType = 119
OpStructPtrHeadOmitEmptyUintString OpType = 120
OpStructHeadFloat32String OpType = 121
OpStructHeadOmitEmptyFloat32String OpType = 122
OpStructPtrHeadFloat32String OpType = 123
OpStructPtrHeadOmitEmptyFloat32String OpType = 124
OpStructHeadFloat64String OpType = 125
OpStructHeadOmitEmptyFloat64String OpType = 126
OpStructPtrHeadFloat64String OpType = 127
OpStructPtrHeadOmitEmptyFloat64String OpType = 128
OpStructHeadBoolString OpType = 129
OpStructHeadOmitEmptyBoolString OpType = 130
OpStructPtrHeadBoolString OpType = 131
OpStructPtrHeadOmitEmptyBoolString OpType = 132
OpStructHeadStringString OpType = 133
OpStructHeadOmitEmptyStringString OpType = 134
OpStructPtrHeadStringString OpType = 135
OpStructPtrHeadOmitEmptyStringString OpType = 136
OpStructHeadNumberString OpType = 137
OpStructHeadOmitEmptyNumberString OpType = 138
OpStructPtrHeadNumberString OpType = 139
OpStructPtrHeadOmitEmptyNumberString OpType = 140
OpStructHeadIntPtr OpType = 141
OpStructHeadOmitEmptyIntPtr OpType = 142
OpStructPtrHeadIntPtr OpType = 143
OpStructPtrHeadOmitEmptyIntPtr OpType = 144
OpStructHeadUintPtr OpType = 145
OpStructHeadOmitEmptyUintPtr OpType = 146
OpStructPtrHeadUintPtr OpType = 147
OpStructPtrHeadOmitEmptyUintPtr OpType = 148
OpStructHeadFloat32Ptr OpType = 149
OpStructHeadOmitEmptyFloat32Ptr OpType = 150
OpStructPtrHeadFloat32Ptr OpType = 151
OpStructPtrHeadOmitEmptyFloat32Ptr OpType = 152
OpStructHeadFloat64Ptr OpType = 153
OpStructHeadOmitEmptyFloat64Ptr OpType = 154
OpStructPtrHeadFloat64Ptr OpType = 155
OpStructPtrHeadOmitEmptyFloat64Ptr OpType = 156
OpStructHeadBoolPtr OpType = 157
OpStructHeadOmitEmptyBoolPtr OpType = 158
OpStructPtrHeadBoolPtr OpType = 159
OpStructPtrHeadOmitEmptyBoolPtr OpType = 160
OpStructHeadStringPtr OpType = 161
OpStructHeadOmitEmptyStringPtr OpType = 162
OpStructPtrHeadStringPtr OpType = 163
OpStructPtrHeadOmitEmptyStringPtr OpType = 164
OpStructHeadBytesPtr OpType = 165
OpStructHeadOmitEmptyBytesPtr OpType = 166
OpStructPtrHeadBytesPtr OpType = 167
OpStructPtrHeadOmitEmptyBytesPtr OpType = 168
OpStructHeadNumberPtr OpType = 169
OpStructHeadOmitEmptyNumberPtr OpType = 170
OpStructPtrHeadNumberPtr OpType = 171
OpStructPtrHeadOmitEmptyNumberPtr OpType = 172
OpStructHeadArrayPtr OpType = 173
OpStructHeadOmitEmptyArrayPtr OpType = 174
OpStructPtrHeadArrayPtr OpType = 175
OpStructPtrHeadOmitEmptyArrayPtr OpType = 176
OpStructHeadMapPtr OpType = 177
OpStructHeadOmitEmptyMapPtr OpType = 178
OpStructPtrHeadMapPtr OpType = 179
OpStructPtrHeadOmitEmptyMapPtr OpType = 180
OpStructHeadSlicePtr OpType = 181
OpStructHeadOmitEmptySlicePtr OpType = 182
OpStructPtrHeadSlicePtr OpType = 183
OpStructPtrHeadOmitEmptySlicePtr OpType = 184
OpStructHeadMarshalJSONPtr OpType = 185
OpStructHeadOmitEmptyMarshalJSONPtr OpType = 186
OpStructPtrHeadMarshalJSONPtr OpType = 187
OpStructPtrHeadOmitEmptyMarshalJSONPtr OpType = 188
OpStructHeadMarshalTextPtr OpType = 189
OpStructHeadOmitEmptyMarshalTextPtr OpType = 190
OpStructPtrHeadMarshalTextPtr OpType = 191
OpStructPtrHeadOmitEmptyMarshalTextPtr OpType = 192
OpStructHeadInterfacePtr OpType = 193
OpStructHeadOmitEmptyInterfacePtr OpType = 194
OpStructPtrHeadInterfacePtr OpType = 195
OpStructPtrHeadOmitEmptyInterfacePtr OpType = 196
OpStructHeadIntPtrString OpType = 197
OpStructHeadOmitEmptyIntPtrString OpType = 198
OpStructPtrHeadIntPtrString OpType = 199
OpStructPtrHeadOmitEmptyIntPtrString OpType = 200
OpStructHeadUintPtrString OpType = 201
OpStructHeadOmitEmptyUintPtrString OpType = 202
OpStructPtrHeadUintPtrString OpType = 203
OpStructPtrHeadOmitEmptyUintPtrString OpType = 204
OpStructHeadFloat32PtrString OpType = 205
OpStructHeadOmitEmptyFloat32PtrString OpType = 206
OpStructPtrHeadFloat32PtrString OpType = 207
OpStructPtrHeadOmitEmptyFloat32PtrString OpType = 208
OpStructHeadFloat64PtrString OpType = 209
OpStructHeadOmitEmptyFloat64PtrString OpType = 210
OpStructPtrHeadFloat64PtrString OpType = 211
OpStructPtrHeadOmitEmptyFloat64PtrString OpType = 212
OpStructHeadBoolPtrString OpType = 213
OpStructHeadOmitEmptyBoolPtrString OpType = 214
OpStructPtrHeadBoolPtrString OpType = 215
OpStructPtrHeadOmitEmptyBoolPtrString OpType = 216
OpStructHeadStringPtrString OpType = 217
OpStructHeadOmitEmptyStringPtrString OpType = 218
OpStructPtrHeadStringPtrString OpType = 219
OpStructPtrHeadOmitEmptyStringPtrString OpType = 220
OpStructHeadNumberPtrString OpType = 221
OpStructHeadOmitEmptyNumberPtrString OpType = 222
OpStructPtrHeadNumberPtrString OpType = 223
OpStructPtrHeadOmitEmptyNumberPtrString OpType = 224
OpStructHead OpType = 225
OpStructHeadOmitEmpty OpType = 226
OpStructPtrHead OpType = 227
OpStructPtrHeadOmitEmpty OpType = 228
OpStructFieldInt OpType = 229
OpStructFieldOmitEmptyInt OpType = 230
OpStructEndInt OpType = 231
OpStructEndOmitEmptyInt OpType = 232
OpStructFieldUint OpType = 233
OpStructFieldOmitEmptyUint OpType = 234
OpStructEndUint OpType = 235
OpStructEndOmitEmptyUint OpType = 236
OpStructFieldFloat32 OpType = 237
OpStructFieldOmitEmptyFloat32 OpType = 238
OpStructEndFloat32 OpType = 239
OpStructEndOmitEmptyFloat32 OpType = 240
OpStructFieldFloat64 OpType = 241
OpStructFieldOmitEmptyFloat64 OpType = 242
OpStructEndFloat64 OpType = 243
OpStructEndOmitEmptyFloat64 OpType = 244
OpStructFieldBool OpType = 245
OpStructFieldOmitEmptyBool OpType = 246
OpStructEndBool OpType = 247
OpStructEndOmitEmptyBool OpType = 248
OpStructFieldString OpType = 249
OpStructFieldOmitEmptyString OpType = 250
OpStructEndString OpType = 251
OpStructEndOmitEmptyString OpType = 252
OpStructFieldBytes OpType = 253
OpStructFieldOmitEmptyBytes OpType = 254
OpStructEndBytes OpType = 255
OpStructEndOmitEmptyBytes OpType = 256
OpStructFieldNumber OpType = 257
OpStructFieldOmitEmptyNumber OpType = 258
OpStructEndNumber OpType = 259
OpStructEndOmitEmptyNumber OpType = 260
OpStructFieldArray OpType = 261
OpStructFieldOmitEmptyArray OpType = 262
OpStructEndArray OpType = 263
OpStructEndOmitEmptyArray OpType = 264
OpStructFieldMap OpType = 265
OpStructFieldOmitEmptyMap OpType = 266
OpStructEndMap OpType = 267
OpStructEndOmitEmptyMap OpType = 268
OpStructFieldSlice OpType = 269
OpStructFieldOmitEmptySlice OpType = 270
OpStructEndSlice OpType = 271
OpStructEndOmitEmptySlice OpType = 272
OpStructFieldStruct OpType = 273
OpStructFieldOmitEmptyStruct OpType = 274
OpStructEndStruct OpType = 275
OpStructEndOmitEmptyStruct OpType = 276
OpStructFieldMarshalJSON OpType = 277
OpStructFieldOmitEmptyMarshalJSON OpType = 278
OpStructEndMarshalJSON OpType = 279
OpStructEndOmitEmptyMarshalJSON OpType = 280
OpStructFieldMarshalText OpType = 281
OpStructFieldOmitEmptyMarshalText OpType = 282
OpStructEndMarshalText OpType = 283
OpStructEndOmitEmptyMarshalText OpType = 284
OpStructFieldIntString OpType = 285
OpStructFieldOmitEmptyIntString OpType = 286
OpStructEndIntString OpType = 287
OpStructEndOmitEmptyIntString OpType = 288
OpStructFieldUintString OpType = 289
OpStructFieldOmitEmptyUintString OpType = 290
OpStructEndUintString OpType = 291
OpStructEndOmitEmptyUintString OpType = 292
OpStructFieldFloat32String OpType = 293
OpStructFieldOmitEmptyFloat32String OpType = 294
OpStructEndFloat32String OpType = 295
OpStructEndOmitEmptyFloat32String OpType = 296
OpStructFieldFloat64String OpType = 297
OpStructFieldOmitEmptyFloat64String OpType = 298
OpStructEndFloat64String OpType = 299
OpStructEndOmitEmptyFloat64String OpType = 300
OpStructFieldBoolString OpType = 301
OpStructFieldOmitEmptyBoolString OpType = 302
OpStructEndBoolString OpType = 303
OpStructEndOmitEmptyBoolString OpType = 304
OpStructFieldStringString OpType = 305
OpStructFieldOmitEmptyStringString OpType = 306
OpStructEndStringString OpType = 307
OpStructEndOmitEmptyStringString OpType = 308
OpStructFieldNumberString OpType = 309
OpStructFieldOmitEmptyNumberString OpType = 310
OpStructEndNumberString OpType = 311
OpStructEndOmitEmptyNumberString OpType = 312
OpStructFieldIntPtr OpType = 313
OpStructFieldOmitEmptyIntPtr OpType = 314
OpStructEndIntPtr OpType = 315
OpStructEndOmitEmptyIntPtr OpType = 316
OpStructFieldUintPtr OpType = 317
OpStructFieldOmitEmptyUintPtr OpType = 318
OpStructEndUintPtr OpType = 319
OpStructEndOmitEmptyUintPtr OpType = 320
OpStructFieldFloat32Ptr OpType = 321
OpStructFieldOmitEmptyFloat32Ptr OpType = 322
OpStructEndFloat32Ptr OpType = 323
OpStructEndOmitEmptyFloat32Ptr OpType = 324
OpStructFieldFloat64Ptr OpType = 325
OpStructFieldOmitEmptyFloat64Ptr OpType = 326
OpStructEndFloat64Ptr OpType = 327
OpStructEndOmitEmptyFloat64Ptr OpType = 328
OpStructFieldBoolPtr OpType = 329
OpStructFieldOmitEmptyBoolPtr OpType = 330
OpStructEndBoolPtr OpType = 331
OpStructEndOmitEmptyBoolPtr OpType = 332
OpStructFieldStringPtr OpType = 333
OpStructFieldOmitEmptyStringPtr OpType = 334
OpStructEndStringPtr OpType = 335
OpStructEndOmitEmptyStringPtr OpType = 336
OpStructFieldBytesPtr OpType = 337
OpStructFieldOmitEmptyBytesPtr OpType = 338
OpStructEndBytesPtr OpType = 339
OpStructEndOmitEmptyBytesPtr OpType = 340
OpStructFieldNumberPtr OpType = 341
OpStructFieldOmitEmptyNumberPtr OpType = 342
OpStructEndNumberPtr OpType = 343
OpStructEndOmitEmptyNumberPtr OpType = 344
OpStructFieldArrayPtr OpType = 345
OpStructFieldOmitEmptyArrayPtr OpType = 346
OpStructEndArrayPtr OpType = 347
OpStructEndOmitEmptyArrayPtr OpType = 348
OpStructFieldMapPtr OpType = 349
OpStructFieldOmitEmptyMapPtr OpType = 350
OpStructEndMapPtr OpType = 351
OpStructEndOmitEmptyMapPtr OpType = 352
OpStructFieldSlicePtr OpType = 353
OpStructFieldOmitEmptySlicePtr OpType = 354
OpStructEndSlicePtr OpType = 355
OpStructEndOmitEmptySlicePtr OpType = 356
OpStructFieldMarshalJSONPtr OpType = 357
OpStructFieldOmitEmptyMarshalJSONPtr OpType = 358
OpStructEndMarshalJSONPtr OpType = 359
OpStructEndOmitEmptyMarshalJSONPtr OpType = 360
OpStructFieldMarshalTextPtr OpType = 361
OpStructFieldOmitEmptyMarshalTextPtr OpType = 362
OpStructEndMarshalTextPtr OpType = 363
OpStructEndOmitEmptyMarshalTextPtr OpType = 364
OpStructFieldInterfacePtr OpType = 365
OpStructFieldOmitEmptyInterfacePtr OpType = 366
OpStructEndInterfacePtr OpType = 367
OpStructEndOmitEmptyInterfacePtr OpType = 368
OpStructFieldIntPtrString OpType = 369
OpStructFieldOmitEmptyIntPtrString OpType = 370
OpStructEndIntPtrString OpType = 371
OpStructEndOmitEmptyIntPtrString OpType = 372
OpStructFieldUintPtrString OpType = 373
OpStructFieldOmitEmptyUintPtrString OpType = 374
OpStructEndUintPtrString OpType = 375
OpStructEndOmitEmptyUintPtrString OpType = 376
OpStructFieldFloat32PtrString OpType = 377
OpStructFieldOmitEmptyFloat32PtrString OpType = 378
OpStructEndFloat32PtrString OpType = 379
OpStructEndOmitEmptyFloat32PtrString OpType = 380
OpStructFieldFloat64PtrString OpType = 381
OpStructFieldOmitEmptyFloat64PtrString OpType = 382
OpStructEndFloat64PtrString OpType = 383
OpStructEndOmitEmptyFloat64PtrString OpType = 384
OpStructFieldBoolPtrString OpType = 385
OpStructFieldOmitEmptyBoolPtrString OpType = 386
OpStructEndBoolPtrString OpType = 387
OpStructEndOmitEmptyBoolPtrString OpType = 388
OpStructFieldStringPtrString OpType = 389
OpStructFieldOmitEmptyStringPtrString OpType = 390
OpStructEndStringPtrString OpType = 391
OpStructEndOmitEmptyStringPtrString OpType = 392
OpStructFieldNumberPtrString OpType = 393
OpStructFieldOmitEmptyNumberPtrString OpType = 394
OpStructEndNumberPtrString OpType = 395
OpStructEndOmitEmptyNumberPtrString OpType = 396
OpStructField OpType = 397
OpStructFieldOmitEmpty OpType = 398
OpStructEnd OpType = 399
OpStructEndOmitEmpty OpType = 400
)
func (t OpType) String() string {
if int(t) >= 401 {
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(), "Ptr")
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
}

View file

@ -0,0 +1,640 @@
package encoder
import (
"math/bits"
"reflect"
"unicode/utf8"
"unsafe"
)
const (
lsb = 0x0101010101010101
msb = 0x8080808080808080
)
var needEscapeWithHTML = [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 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 - 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 hex = "0123456789abcdef"
// escapeIndex finds the index of the first char in `s` that requires escaping.
// A char requires escaping if it's outside of the range of [0x20, 0x7F] or if
// it includes a double quote or backslash.
// If no chars in `s` require escaping, the return value is -1.
func escapeIndex(s string) int {
chunks := stringToUint64Slice(s)
for _, n := range chunks {
// combine masks before checking for the MSB of each byte. We include
// `n` in the mask to check whether any of the *input* byte MSBs were
// set (i.e. the byte was outside the ASCII range).
mask := n | below(n, 0x20) | contains(n, '"') | contains(n, '\\')
if (mask & msb) != 0 {
return bits.TrailingZeros64(mask&msb) / 8
}
}
valLen := len(s)
for i := len(chunks) * 8; i < valLen; i++ {
if needEscape[s[i]] {
return i
}
}
return -1
}
// below return a mask that can be used to determine if any of the bytes
// in `n` are below `b`. If a byte's MSB is set in the mask then that byte was
// below `b`. The result is only valid if `b`, and each byte in `n`, is below
// 0x80.
func below(n uint64, b byte) uint64 {
return n - expand(b)
}
// contains returns a mask that can be used to determine if any of the
// bytes in `n` are equal to `b`. If a byte's MSB is set in the mask then
// that byte is equal to `b`. The result is only valid if `b`, and each
// byte in `n`, is below 0x80.
func contains(n uint64, b byte) uint64 {
return (n ^ expand(b)) - lsb
}
// expand puts the specified byte into each of the 8 bytes of a uint64.
func expand(b byte) uint64 {
return lsb * uint64(b)
}
//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 {
return appendString(buf, s)
}
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 needEscapeWithHTML[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 !needEscapeWithHTML[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
}
// This encodes bytes < 0x20 except for \t, \n and \r.
if c < 0x20 {
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
}
r, size := utf8.DecodeRuneInString(s[j:])
if r == utf8.RuneError && size == 1 {
buf = append(buf, s[i:j]...)
buf = append(buf, `\ufffd`...)
i = j + size
j = j + size
continue
}
switch r {
case '\u2028', '\u2029':
// 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.
buf = append(buf, s[i:j]...)
buf = append(buf, `\u202`...)
buf = append(buf, hex[r&0xF])
i = j + size
j = j + size
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 escapeIdx int
if valLen >= 8 {
if escapeIdx = escapeIndex(s); escapeIdx < 0 {
return append(append(buf, s...), '"')
}
}
i := 0
j := escapeIdx
for j < valLen {
c := s[j]
if c >= 0x20 && c <= 0x7f && c != '\\' && 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
}
// This encodes bytes < 0x20 except for \t, \n and \r.
if c < 0x20 {
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
}
r, size := utf8.DecodeRuneInString(s[j:])
if r == utf8.RuneError && size == 1 {
buf = append(buf, s[i:j]...)
buf = append(buf, `\ufffd`...)
i = j + size
j = j + size
continue
}
switch r {
case '\u2028', '\u2029':
// 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.
buf = append(buf, s[i:j]...)
buf = append(buf, `\u202`...)
buf = append(buf, hex[r&0xF])
i = j + size
j = j + size
continue
}
j += size
}
return append(append(buf, s[i:]...), '"')
}

View file

@ -0,0 +1,34 @@
package vm
import (
"fmt"
"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 err := recover(); err != nil {
fmt.Println("=============[DEBUG]===============")
fmt.Println("* [TYPE]")
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
fmt.Println("===================================")
panic(err)
}
}()
return Run(ctx, b, codeSet)
}

View 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"
)

View file

@ -0,0 +1,182 @@
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
}
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) 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 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 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 }

5041
vendor/github.com/goccy/go-json/internal/encoder/vm/vm.go generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
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 {
fmt.Println("=============[DEBUG]===============")
fmt.Println("* [TYPE]")
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
fmt.Println("===================================")
panic(err)
}
}()
return Run(ctx, b, codeSet)
}

View 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"
)

View file

@ -0,0 +1,246 @@
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
}
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) 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 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, v uint64, code *encoder.Opcode) []byte {
format := ctx.Option.ColorScheme.Int
b = append(b, format.Header...)
b = encoder.AppendInt(ctx, b, v, code)
return append(b, format.Footer...)
}
func appendUint(ctx *encoder.RuntimeContext, b []byte, v uint64, code *encoder.Opcode) []byte {
format := ctx.Option.ColorScheme.Uint
b = append(b, format.Header...)
b = encoder.AppendUint(ctx, b, v, 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 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 }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
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 {
fmt.Println("=============[DEBUG]===============")
fmt.Println("* [TYPE]")
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
fmt.Println("===================================")
panic(err)
}
}()
return Run(ctx, b, codeSet)
}

View file

@ -0,0 +1,267 @@
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
}
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) 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 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, v uint64, code *encoder.Opcode) []byte {
format := ctx.Option.ColorScheme.Int
b = append(b, format.Header...)
b = encoder.AppendInt(ctx, b, v, code)
return append(b, format.Footer...)
}
func appendUint(ctx *encoder.RuntimeContext, b []byte, v uint64, code *encoder.Opcode) []byte {
format := ctx.Option.ColorScheme.Uint
b = append(b, format.Header...)
b = encoder.AppendUint(ctx, b, v, 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 appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, ':', ' ')
}
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
b[last] = '\n'
b = appendIndent(ctx, b, code.Indent-1)
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)
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
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 {
fmt.Println("=============[DEBUG]===============")
fmt.Println("* [TYPE]")
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
fmt.Println("===================================")
panic(err)
}
}()
return Run(ctx, b, codeSet)
}

View 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"
)

View file

@ -0,0 +1,204 @@
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
}
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) 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 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 appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, ':', ' ')
}
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
b[last] = '\n'
b = appendIndent(ctx, b, code.Indent-1)
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)
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,157 @@
package errors
import (
"fmt"
"reflect"
"strconv"
)
type InvalidUTF8Error struct {
S string // the whole string value that caused the error
}
func (e *InvalidUTF8Error) Error() string {
return fmt.Sprintf("json: invalid UTF-8 in string: %s", strconv.Quote(e.S))
}
type InvalidUnmarshalError struct {
Type reflect.Type
}
func (e *InvalidUnmarshalError) Error() string {
if e.Type == nil {
return "json: Unmarshal(nil)"
}
if e.Type.Kind() != reflect.Ptr {
return fmt.Sprintf("json: Unmarshal(non-pointer %s)", e.Type)
}
return fmt.Sprintf("json: Unmarshal(nil %s)", e.Type)
}
// A MarshalerError represents an error from calling a MarshalJSON or MarshalText method.
type MarshalerError struct {
Type reflect.Type
Err error
sourceFunc string
}
func (e *MarshalerError) Error() string {
srcFunc := e.sourceFunc
if srcFunc == "" {
srcFunc = "MarshalJSON"
}
return fmt.Sprintf("json: error calling %s for type %s: %s", srcFunc, e.Type, e.Err.Error())
}
// Unwrap returns the underlying error.
func (e *MarshalerError) Unwrap() error { return e.Err }
// A SyntaxError is a description of a JSON syntax error.
type SyntaxError struct {
msg string // description of error
Offset int64 // error occurred after reading Offset bytes
}
func (e *SyntaxError) Error() string { return e.msg }
// An UnmarshalFieldError describes a JSON object key that
// led to an unexported (and therefore unwritable) struct field.
//
// Deprecated: No longer used; kept for compatibility.
type UnmarshalFieldError struct {
Key string
Type reflect.Type
Field reflect.StructField
}
func (e *UnmarshalFieldError) Error() string {
return fmt.Sprintf("json: cannot unmarshal object key %s into unexported field %s of type %s",
strconv.Quote(e.Key), e.Field.Name, e.Type.String(),
)
}
// An UnmarshalTypeError describes a JSON value that was
// not appropriate for a value of a specific Go type.
type UnmarshalTypeError struct {
Value string // description of JSON value - "bool", "array", "number -5"
Type reflect.Type // type of Go value it could not be assigned to
Offset int64 // error occurred after reading Offset bytes
Struct string // name of the struct type containing the field
Field string // the full path from root node to the field
}
func (e *UnmarshalTypeError) Error() string {
if e.Struct != "" || e.Field != "" {
return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s.%s of type %s",
e.Value, e.Struct, e.Field, e.Type,
)
}
return fmt.Sprintf("json: cannot unmarshal %s into Go value of type %s", e.Value, e.Type)
}
// An UnsupportedTypeError is returned by Marshal when attempting
// to encode an unsupported value type.
type UnsupportedTypeError struct {
Type reflect.Type
}
func (e *UnsupportedTypeError) Error() string {
return fmt.Sprintf("json: unsupported type: %s", e.Type)
}
type UnsupportedValueError struct {
Value reflect.Value
Str string
}
func (e *UnsupportedValueError) Error() string {
return fmt.Sprintf("json: unsupported value: %s", e.Str)
}
func ErrSyntax(msg string, offset int64) *SyntaxError {
return &SyntaxError{msg: msg, Offset: offset}
}
func ErrMarshaler(typ reflect.Type, err error, msg string) *MarshalerError {
return &MarshalerError{
Type: typ,
Err: err,
sourceFunc: msg,
}
}
func ErrExceededMaxDepth(c byte, cursor int64) *SyntaxError {
return &SyntaxError{
msg: fmt.Sprintf(`invalid character "%c" exceeded max depth`, c),
Offset: cursor,
}
}
func ErrNotAtBeginningOfValue(cursor int64) *SyntaxError {
return &SyntaxError{msg: "not at beginning of value", Offset: cursor}
}
func ErrUnexpectedEndOfJSON(msg string, cursor int64) *SyntaxError {
return &SyntaxError{
msg: fmt.Sprintf("json: %s unexpected end of JSON input", msg),
Offset: cursor,
}
}
func ErrExpected(msg string, cursor int64) *SyntaxError {
return &SyntaxError{msg: fmt.Sprintf("expected %s", msg), Offset: cursor}
}
func ErrInvalidCharacter(c byte, context string, cursor int64) *SyntaxError {
if c == 0 {
return &SyntaxError{
msg: fmt.Sprintf("json: invalid character as %s", context),
Offset: cursor,
}
}
return &SyntaxError{
msg: fmt.Sprintf("json: invalid character %c as %s", c, context),
Offset: cursor,
}
}

View file

@ -0,0 +1,263 @@
package runtime
import (
"reflect"
"unsafe"
)
// Type representing reflect.rtype for noescape trick
type Type struct{}
//go:linkname rtype_Align reflect.(*rtype).Align
//go:noescape
func rtype_Align(*Type) int
func (t *Type) Align() int {
return rtype_Align(t)
}
//go:linkname rtype_FieldAlign reflect.(*rtype).FieldAlign
//go:noescape
func rtype_FieldAlign(*Type) int
func (t *Type) FieldAlign() int {
return rtype_FieldAlign(t)
}
//go:linkname rtype_Method reflect.(*rtype).Method
//go:noescape
func rtype_Method(*Type, int) reflect.Method
func (t *Type) Method(a0 int) reflect.Method {
return rtype_Method(t, a0)
}
//go:linkname rtype_MethodByName reflect.(*rtype).MethodByName
//go:noescape
func rtype_MethodByName(*Type, string) (reflect.Method, bool)
func (t *Type) MethodByName(a0 string) (reflect.Method, bool) {
return rtype_MethodByName(t, a0)
}
//go:linkname rtype_NumMethod reflect.(*rtype).NumMethod
//go:noescape
func rtype_NumMethod(*Type) int
func (t *Type) NumMethod() int {
return rtype_NumMethod(t)
}
//go:linkname rtype_Name reflect.(*rtype).Name
//go:noescape
func rtype_Name(*Type) string
func (t *Type) Name() string {
return rtype_Name(t)
}
//go:linkname rtype_PkgPath reflect.(*rtype).PkgPath
//go:noescape
func rtype_PkgPath(*Type) string
func (t *Type) PkgPath() string {
return rtype_PkgPath(t)
}
//go:linkname rtype_Size reflect.(*rtype).Size
//go:noescape
func rtype_Size(*Type) uintptr
func (t *Type) Size() uintptr {
return rtype_Size(t)
}
//go:linkname rtype_String reflect.(*rtype).String
//go:noescape
func rtype_String(*Type) string
func (t *Type) String() string {
return rtype_String(t)
}
//go:linkname rtype_Kind reflect.(*rtype).Kind
//go:noescape
func rtype_Kind(*Type) reflect.Kind
func (t *Type) Kind() reflect.Kind {
return rtype_Kind(t)
}
//go:linkname rtype_Implements reflect.(*rtype).Implements
//go:noescape
func rtype_Implements(*Type, reflect.Type) bool
func (t *Type) Implements(u reflect.Type) bool {
return rtype_Implements(t, u)
}
//go:linkname rtype_AssignableTo reflect.(*rtype).AssignableTo
//go:noescape
func rtype_AssignableTo(*Type, reflect.Type) bool
func (t *Type) AssignableTo(u reflect.Type) bool {
return rtype_AssignableTo(t, u)
}
//go:linkname rtype_ConvertibleTo reflect.(*rtype).ConvertibleTo
//go:noescape
func rtype_ConvertibleTo(*Type, reflect.Type) bool
func (t *Type) ConvertibleTo(u reflect.Type) bool {
return rtype_ConvertibleTo(t, u)
}
//go:linkname rtype_Comparable reflect.(*rtype).Comparable
//go:noescape
func rtype_Comparable(*Type) bool
func (t *Type) Comparable() bool {
return rtype_Comparable(t)
}
//go:linkname rtype_Bits reflect.(*rtype).Bits
//go:noescape
func rtype_Bits(*Type) int
func (t *Type) Bits() int {
return rtype_Bits(t)
}
//go:linkname rtype_ChanDir reflect.(*rtype).ChanDir
//go:noescape
func rtype_ChanDir(*Type) reflect.ChanDir
func (t *Type) ChanDir() reflect.ChanDir {
return rtype_ChanDir(t)
}
//go:linkname rtype_IsVariadic reflect.(*rtype).IsVariadic
//go:noescape
func rtype_IsVariadic(*Type) bool
func (t *Type) IsVariadic() bool {
return rtype_IsVariadic(t)
}
//go:linkname rtype_Elem reflect.(*rtype).Elem
//go:noescape
func rtype_Elem(*Type) reflect.Type
func (t *Type) Elem() *Type {
return Type2RType(rtype_Elem(t))
}
//go:linkname rtype_Field reflect.(*rtype).Field
//go:noescape
func rtype_Field(*Type, int) reflect.StructField
func (t *Type) Field(i int) reflect.StructField {
return rtype_Field(t, i)
}
//go:linkname rtype_FieldByIndex reflect.(*rtype).FieldByIndex
//go:noescape
func rtype_FieldByIndex(*Type, []int) reflect.StructField
func (t *Type) FieldByIndex(index []int) reflect.StructField {
return rtype_FieldByIndex(t, index)
}
//go:linkname rtype_FieldByName reflect.(*rtype).FieldByName
//go:noescape
func rtype_FieldByName(*Type, string) (reflect.StructField, bool)
func (t *Type) FieldByName(name string) (reflect.StructField, bool) {
return rtype_FieldByName(t, name)
}
//go:linkname rtype_FieldByNameFunc reflect.(*rtype).FieldByNameFunc
//go:noescape
func rtype_FieldByNameFunc(*Type, func(string) bool) (reflect.StructField, bool)
func (t *Type) FieldByNameFunc(match func(string) bool) (reflect.StructField, bool) {
return rtype_FieldByNameFunc(t, match)
}
//go:linkname rtype_In reflect.(*rtype).In
//go:noescape
func rtype_In(*Type, int) reflect.Type
func (t *Type) In(i int) reflect.Type {
return rtype_In(t, i)
}
//go:linkname rtype_Key reflect.(*rtype).Key
//go:noescape
func rtype_Key(*Type) reflect.Type
func (t *Type) Key() *Type {
return Type2RType(rtype_Key(t))
}
//go:linkname rtype_Len reflect.(*rtype).Len
//go:noescape
func rtype_Len(*Type) int
func (t *Type) Len() int {
return rtype_Len(t)
}
//go:linkname rtype_NumField reflect.(*rtype).NumField
//go:noescape
func rtype_NumField(*Type) int
func (t *Type) NumField() int {
return rtype_NumField(t)
}
//go:linkname rtype_NumIn reflect.(*rtype).NumIn
//go:noescape
func rtype_NumIn(*Type) int
func (t *Type) NumIn() int {
return rtype_NumIn(t)
}
//go:linkname rtype_NumOut reflect.(*rtype).NumOut
//go:noescape
func rtype_NumOut(*Type) int
func (t *Type) NumOut() int {
return rtype_NumOut(t)
}
//go:linkname rtype_Out reflect.(*rtype).Out
//go:noescape
func rtype_Out(*Type, int) reflect.Type
//go:linkname PtrTo reflect.(*rtype).ptrTo
//go:noescape
func PtrTo(*Type) *Type
func (t *Type) Out(i int) reflect.Type {
return rtype_Out(t, i)
}
//go:linkname IfaceIndir reflect.ifaceIndir
//go:noescape
func IfaceIndir(*Type) bool
//go:linkname RType2Type reflect.toType
//go:noescape
func RType2Type(t *Type) reflect.Type
//go:nolint structcheck
type emptyInterface struct {
_ *Type
ptr unsafe.Pointer
}
func Type2RType(t reflect.Type) *Type {
return (*Type)(((*emptyInterface)(unsafe.Pointer(&t))).ptr)
}

View file

@ -0,0 +1,87 @@
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 {
if !(field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct) && field.Type.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
}

View file

@ -0,0 +1,100 @@
package runtime
import (
"reflect"
"unsafe"
)
type SliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
const (
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
)
type TypeAddr struct {
BaseTypeAddr uintptr
MaxTypeAddr uintptr
AddrRange uintptr
AddrShift uintptr
}
var (
typeAddr *TypeAddr
alreadyAnalyzed bool
)
//go:linkname typelinks reflect.typelinks
func typelinks() ([]unsafe.Pointer, [][]int32)
//go:linkname rtypeOff reflect.rtypeOff
func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer
func AnalyzeTypeAddr() *TypeAddr {
defer func() {
alreadyAnalyzed = true
}()
if alreadyAnalyzed {
return typeAddr
}
sections, offsets := typelinks()
if len(sections) != 1 {
return nil
}
if len(offsets) != 1 {
return nil
}
section := sections[0]
offset := offsets[0]
var (
min uintptr = uintptr(^uint(0))
max uintptr = 0
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 nil
}
var addrShift uintptr
if isAligned64 {
addrShift = 6
} else if isAligned32 {
addrShift = 5
}
cacheSize := addrRange >> addrShift
if cacheSize > maxAcceptableTypeAddrRange {
return nil
}
typeAddr = &TypeAddr{
BaseTypeAddr: min,
MaxTypeAddr: max,
AddrRange: addrRange,
AddrShift: addrShift,
}
return typeAddr
}