migrate go version to 1.17 (#203)

* migrate go version to 1.17

* update contributing
This commit is contained in:
tobi 2021-09-10 14:42:14 +02:00 committed by GitHub
commit f2e5bedea6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
282 changed files with 11863 additions and 12600 deletions

View file

@ -28,4 +28,5 @@ comment:
require_changes: no
ignore:
- internal/encoder/vm_debug
- internal/encoder/vm_color
- internal/encoder/vm_color_indent

View file

@ -1,3 +1,118 @@
# v0.7.8 - 2021/09/01
* Fix mapassign_faststr for indirect struct type ( #283 )
* Fix encoding of not empty interface type ( #284 )
* Fix encoding of empty struct interface type ( #286 )
# v0.7.7 - 2021/08/25
* Fix invalid utf8 on stream decoder ( #279 )
* Fix buffer length bug on string stream decoder ( #280 )
Thank you @orisano !!
# v0.7.6 - 2021/08/13
* Fix nil slice assignment ( #276 )
* Improve error message ( #277 )
# v0.7.5 - 2021/08/12
* Fix encoding of embedded struct with tags ( #265 )
* Fix encoding of embedded struct that isn't first field ( #272 )
* Fix decoding of binary type with escaped char ( #273 )
# v0.7.4 - 2021/07/06
* Fix encoding of indirect layout structure ( #264 )
# v0.7.3 - 2021/06/29
* Fix encoding of pointer type in empty interface ( #262 )
# v0.7.2 - 2021/06/26
### Fix decoder
* Add decoder for func type to fix decoding of nil function value ( #257 )
* Fix stream decoding of []byte type ( #258 )
### Performance
* Improve decoding performance of map[string]interface{} type ( use `mapassign_faststr` ) ( #256 )
* Improve encoding performance of empty interface type ( remove recursive calling of `vm.Run` ) ( #259 )
### Benchmark
* Add bytedance/sonic as benchmark target ( #254 )
# v0.7.1 - 2021/06/18
### Fix decoder
* Fix error when unmarshal empty array ( #253 )
# v0.7.0 - 2021/06/12
### Support context for MarshalJSON and UnmarshalJSON ( #248 )
* json.MarshalContext(context.Context, interface{}, ...json.EncodeOption) ([]byte, error)
* json.NewEncoder(io.Writer).EncodeContext(context.Context, interface{}, ...json.EncodeOption) error
* json.UnmarshalContext(context.Context, []byte, interface{}, ...json.DecodeOption) error
* json.NewDecoder(io.Reader).DecodeContext(context.Context, interface{}) error
```go
type MarshalerContext interface {
MarshalJSON(context.Context) ([]byte, error)
}
type UnmarshalerContext interface {
UnmarshalJSON(context.Context, []byte) error
}
```
### Add DecodeFieldPriorityFirstWin option ( #242 )
In the default behavior, go-json, like encoding/json, will reflect the result of the last evaluation when a field with the same name exists. I've added new options to allow you to change this behavior. `json.DecodeFieldPriorityFirstWin` option reflects the result of the first evaluation if a field with the same name exists. This behavior has a performance advantage as it allows the subsequent strings to be skipped if all fields have been evaluated.
### Fix encoder
* Fix indent number contains recursive type ( #249 )
* Fix encoding of using empty interface as map key ( #244 )
### Fix decoder
* Fix decoding fields containing escaped characters ( #237 )
### Refactor
* Move some tests to subdirectory ( #243 )
* Refactor package layout for decoder ( #238 )
# v0.6.1 - 2021/06/02
### Fix encoder
* Fix value of totalLength for encoding ( #236 )
# v0.6.0 - 2021/06/01
### Support Colorize option for encoding (#233)
```go
b, err := json.MarshalWithOption(v, json.Colorize(json.DefaultColorScheme))
if err != nil {
...
}
fmt.Println(string(b)) // print colored json
```
### Refactor
* Fix opcode layout - Adjust memory layout of the opcode to 128 bytes in a 64-bit environment ( #230 )
* Refactor encode option ( #231 )
* Refactor escape string ( #232 )
# v0.5.1 - 2021/5/20
### Optimization

View file

@ -1,7 +1,7 @@
PKG := github.com/goccy/go-json
BIN_DIR := $(CURDIR)/bin
PKGS := $(shell go list ./... | grep -v internal/cmd)
PKGS := $(shell go list ./... | grep -v internal/cmd|grep -v test)
COVER_PKGS := $(foreach pkg,$(PKGS),$(subst $(PKG),.,$(pkg)))
COMMA := ,
@ -14,7 +14,7 @@ $(BIN_DIR):
.PHONY: cover
cover:
go test -coverpkg=$(COVERPKG_OPT) -coverprofile=cover.out .
go test -coverpkg=$(COVERPKG_OPT) -coverprofile=cover.out ./...
.PHONY: cover-html
cover-html: cover

View file

@ -13,23 +13,26 @@ Fast JSON encoder/decoder compatible with encoding/json for Go
```
* version ( expected release date )
* v0.5.0
|
| refactor all sources for maintainability and improve performance
|
v
* v0.6.0 ( 2021/05 Mid )
* v0.7.0
|
| while maintaining compatibility with encoding/json, we will add convenient APIs
|
v
* v1.0.0 ( 2021/06 Mid )
* v1.0.0
```
We are accepting requests for features that will be implemented between v0.6.0 and v.1.0.0.
We are accepting requests for features that will be implemented between v0.7.0 and v.1.0.0.
If you have the API you need, please submit your issue [here](https://github.com/goccy/go-json/issues).
For example, I'm thinking of supporting `context.Context` of `json.Marshaler` and decoding using JSON Path.
# Features
- Drop-in replacement of `encoding/json`
- Fast ( See [Benchmark section](https://github.com/goccy/go-json#benchmarks) )
- Flexible customization with options
- Coloring the encoded string
- Can propagate context.Context to `MarshalJSON` or `UnmarshalJSON`
# Installation
```
@ -53,13 +56,26 @@ Replace import statement from `encoding/json` to `github.com/goccy/go-json`
| [json-iterator/go](https://github.com/json-iterator/go) | yes | yes | partial |
| [easyjson](https://github.com/mailru/easyjson) | yes | yes | no |
| [gojay](https://github.com/francoispqt/gojay) | yes | yes | no |
| [segmentio/encoding/json](https://github.com/segmentio/encoding/tree/master/json) | yes | yes | yes |
| [segmentio/encoding/json](https://github.com/segmentio/encoding/tree/master/json) | yes | yes | partial |
| [jettison](https://github.com/wI2L/jettison) | yes | no | no |
| [simdjson-go](https://github.com/minio/simdjson-go) | no | yes | no |
| goccy/go-json | yes | yes | yes |
- `json-iterator/go` isn't compatible with `encoding/json` in many ways, but it hasn't been supported for a long time.
- `json-iterator/go` isn't compatible with `encoding/json` in many ways (e.g. https://github.com/json-iterator/go/issues/229 ), but it hasn't been supported for a long time.
- `segmentio/encoding/json` is well supported for encoders, but some are not supported for decoder APIs such as `Token` ( streaming decode )
## Other libraries
- [jingo](https://github.com/bet365/jingo)
I tried the benchmark but it didn't work.
Also, it seems to panic when it receives an unexpected value because there is no error handling...
- [ffjson](https://github.com/pquerna/ffjson)
Benchmarking gave very slow results.
It seems that it is assumed that the user will use the buffer pool properly.
Also, development seems to have already stopped
# Benchmarks
@ -176,7 +192,7 @@ For this reason, to date `reflect.Type` is the same as `*reflect.rtype`.
Therefore, by directly handling `*reflect.rtype`, which is an implementation of `reflect.Type`, it is possible to avoid escaping because it changes from `interface` to using `struct`.
The technique for working with `*reflect.rtype` directly from `go-json` is implemented at https://github.com/goccy/go-json/blob/master/rtype.go.
The technique for working with `*reflect.rtype` directly from `go-json` is implemented at [rtype.go](https://github.com/goccy/go-json/blob/master/internal/runtime/rtype.go)
Also, the same technique is cut out as a library ( https://github.com/goccy/go-reflect )
@ -337,7 +353,7 @@ However, if there is too much type information, it will use a lot of memory, so
If this approach is not available, it will fall back to the `atomic` based process described above.
If you want to know more, please refer to the implementation [here](https://github.com/goccy/go-json/blob/master/codec.go#L24-L76 )
If you want to know more, please refer to the implementation [here](https://github.com/goccy/go-json/blob/master/internal/runtime/type.go#L36-L100)
## Decoder

View file

@ -1,104 +0,0 @@
package json
import (
"fmt"
"reflect"
"sync/atomic"
"unsafe"
)
const (
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
)
var (
cachedDecoder []decoder
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
baseTypeAddr uintptr
maxTypeAddr uintptr
typeAddrShift uintptr
)
//go:linkname typelinks reflect.typelinks
func typelinks() ([]unsafe.Pointer, [][]int32)
//go:linkname rtypeOff reflect.rtypeOff
func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer
func setupCodec() error {
sections, offsets := typelinks()
if len(sections) != 1 {
return fmt.Errorf("failed to get sections")
}
if len(offsets) != 1 {
return fmt.Errorf("failed to get offsets")
}
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 := (*rtype)(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
}
}
// check every address is aligned from the base address
isAligned64 = isAligned64 && (addr-min)&63 == 0
isAligned32 = isAligned32 && (addr-min)&31 == 0
}
addrRange := max - min
if addrRange == 0 {
return fmt.Errorf("failed to get address range of types")
}
if isAligned64 {
typeAddrShift = 6
} else if isAligned32 {
typeAddrShift = 5
}
cacheSize := addrRange >> typeAddrShift
if cacheSize > maxAcceptableTypeAddrRange {
return fmt.Errorf("too big address range %d", addrRange)
}
cachedDecoder = make([]decoder, cacheSize)
baseTypeAddr = min
maxTypeAddr = max
return nil
}
func init() {
_ = setupCodec()
}
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)))
}

68
vendor/github.com/goccy/go-json/color.go generated vendored Normal file
View file

@ -0,0 +1,68 @@
package json
import (
"fmt"
"github.com/goccy/go-json/internal/encoder"
)
type (
ColorFormat = encoder.ColorFormat
ColorScheme = encoder.ColorScheme
)
const escape = "\x1b"
type colorAttr int
//nolint:deadcode,varcheck
const (
fgBlackColor colorAttr = iota + 30
fgRedColor
fgGreenColor
fgYellowColor
fgBlueColor
fgMagentaColor
fgCyanColor
fgWhiteColor
)
//nolint:deadcode,varcheck
const (
fgHiBlackColor colorAttr = iota + 90
fgHiRedColor
fgHiGreenColor
fgHiYellowColor
fgHiBlueColor
fgHiMagentaColor
fgHiCyanColor
fgHiWhiteColor
)
func createColorFormat(attr colorAttr) ColorFormat {
return ColorFormat{
Header: wrapColor(attr),
Footer: resetColor(),
}
}
func wrapColor(attr colorAttr) string {
return fmt.Sprintf("%s[%dm", escape, attr)
}
func resetColor() string {
return wrapColor(colorAttr(0))
}
var (
DefaultColorScheme = &ColorScheme{
Int: createColorFormat(fgHiMagentaColor),
Uint: createColorFormat(fgHiMagentaColor),
Float: createColorFormat(fgHiMagentaColor),
Bool: createColorFormat(fgHiYellowColor),
String: createColorFormat(fgHiGreenColor),
Binary: createColorFormat(fgHiRedColor),
ObjectKey: createColorFormat(fgHiCyanColor),
Null: createColorFormat(fgBlueColor),
}
)

View file

@ -1,34 +1,31 @@
package json
import (
"encoding"
"context"
"fmt"
"io"
"reflect"
"strconv"
"unsafe"
)
type decoder interface {
decode([]byte, int64, int64, unsafe.Pointer) (int64, error)
decodeStream(*stream, int64, unsafe.Pointer) error
}
"github.com/goccy/go-json/internal/decoder"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type Decoder struct {
s *stream
s *decoder.Stream
}
var (
unmarshalJSONType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
unmarshalTextType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
)
const (
nul = '\000'
maxDecodeNestingDepth = 10000
nul = '\000'
)
func unmarshal(data []byte, v interface{}) error {
type emptyInterface struct {
typ *runtime.Type
ptr unsafe.Pointer
}
func unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data)
@ -37,18 +34,26 @@ func unmarshal(data []byte, v interface{}) error {
if err := validateType(header.typ, uintptr(header.ptr)); err != nil {
return err
}
dec, err := decodeCompileToGetDecoder(header.typ)
dec, err := decoder.CompileToGetDecoder(header.typ)
if err != nil {
return err
}
cursor, err := dec.decode(src, 0, 0, header.ptr)
ctx := decoder.TakeRuntimeContext()
ctx.Buf = src
ctx.Option.Flags = 0
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
cursor, err := dec.Decode(ctx, 0, 0, header.ptr)
if err != nil {
decoder.ReleaseRuntimeContext(ctx)
return err
}
decoder.ReleaseRuntimeContext(ctx)
return validateEndBuf(src, cursor)
}
func unmarshalNoEscape(data []byte, v interface{}) error {
func unmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data)
@ -57,14 +62,53 @@ func unmarshalNoEscape(data []byte, v interface{}) error {
if err := validateType(header.typ, uintptr(header.ptr)); err != nil {
return err
}
dec, err := decodeCompileToGetDecoder(header.typ)
dec, err := decoder.CompileToGetDecoder(header.typ)
if err != nil {
return err
}
cursor, err := dec.decode(src, 0, 0, noescape(header.ptr))
rctx := decoder.TakeRuntimeContext()
rctx.Buf = src
rctx.Option.Flags = 0
rctx.Option.Flags |= decoder.ContextOption
rctx.Option.Context = ctx
for _, optFunc := range optFuncs {
optFunc(rctx.Option)
}
cursor, err := dec.Decode(rctx, 0, 0, header.ptr)
if err != nil {
decoder.ReleaseRuntimeContext(rctx)
return err
}
decoder.ReleaseRuntimeContext(rctx)
return validateEndBuf(src, cursor)
}
func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data)
header := (*emptyInterface)(unsafe.Pointer(&v))
if err := validateType(header.typ, uintptr(header.ptr)); err != nil {
return err
}
dec, err := decoder.CompileToGetDecoder(header.typ)
if err != nil {
return err
}
ctx := decoder.TakeRuntimeContext()
ctx.Buf = src
ctx.Option.Flags = 0
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
cursor, err := dec.Decode(ctx, 0, 0, noescape(header.ptr))
if err != nil {
decoder.ReleaseRuntimeContext(ctx)
return err
}
decoder.ReleaseRuntimeContext(ctx)
return validateEndBuf(src, cursor)
}
@ -77,7 +121,7 @@ func validateEndBuf(src []byte, cursor int64) error {
case nul:
return nil
}
return errSyntax(
return errors.ErrSyntax(
fmt.Sprintf("invalid character '%c' after top-level value", src[cursor]),
cursor+1,
)
@ -91,9 +135,9 @@ func noescape(p unsafe.Pointer) unsafe.Pointer {
return unsafe.Pointer(x ^ 0)
}
func validateType(typ *rtype, p uintptr) error {
func validateType(typ *runtime.Type, p uintptr) error {
if typ == nil || typ.Kind() != reflect.Ptr || p == 0 {
return &InvalidUnmarshalError{Type: rtype2type(typ)}
return &InvalidUnmarshalError{Type: runtime.RType2Type(typ)}
}
return nil
}
@ -103,7 +147,7 @@ func validateType(typ *rtype, p uintptr) error {
// The decoder introduces its own buffering and may
// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder {
s := newStream(r)
s := decoder.NewStream(r)
return &Decoder{
s: s,
}
@ -112,28 +156,7 @@ func NewDecoder(r io.Reader) *Decoder {
// Buffered returns a reader of the data remaining in the Decoder's
// buffer. The reader is valid until the next call to Decode.
func (d *Decoder) Buffered() io.Reader {
return d.s.buffered()
}
func (d *Decoder) prepareForDecode() error {
s := d.s
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
return d.s.Buffered()
}
// Decode reads the next JSON-encoded value from its
@ -142,120 +165,68 @@ func (d *Decoder) prepareForDecode() error {
// See the documentation for Unmarshal for details about
// the conversion of JSON into a Go value.
func (d *Decoder) Decode(v interface{}) error {
return d.DecodeWithOption(v)
}
// DecodeContext reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v with context.Context.
func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error {
d.s.Option.Flags |= decoder.ContextOption
d.s.Option.Context = ctx
return d.DecodeWithOption(v)
}
func (d *Decoder) DecodeWithOption(v interface{}, optFuncs ...DecodeOptionFunc) error {
header := (*emptyInterface)(unsafe.Pointer(&v))
typ := header.typ
ptr := uintptr(header.ptr)
typeptr := uintptr(unsafe.Pointer(typ))
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
if err := validateType(copiedType, ptr); err != nil {
return err
}
dec, err := decodeCompileToGetDecoder(typ)
dec, err := decoder.CompileToGetDecoder(typ)
if err != nil {
return err
}
if err := d.prepareForDecode(); err != nil {
if err := d.s.PrepareForDecode(); err != nil {
return err
}
s := d.s
if err := dec.decodeStream(s, 0, header.ptr); err != nil {
for _, optFunc := range optFuncs {
optFunc(s.Option)
}
if err := dec.DecodeStream(s, 0, header.ptr); err != nil {
return err
}
s.reset()
s.bufSize = initBufSize
s.Reset()
return nil
}
func (d *Decoder) More() bool {
s := d.s
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
return d.s.More()
}
func (d *Decoder) Token() (Token, error) {
s := d.s
for {
c := s.char()
switch c {
case ' ', '\n', '\r', '\t':
s.cursor++
case '{', '[', ']', '}':
s.cursor++
return 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, errInvalidCharacter(s.char(), "token", s.totalOffset())
}
}
END:
return nil, io.EOF
return d.s.Token()
}
// DisallowUnknownFields causes the Decoder to return an error when the destination
// is a struct and the input contains object keys which do not match any
// non-ignored, exported fields in the destination.
func (d *Decoder) DisallowUnknownFields() {
d.s.disallowUnknownFields = true
d.s.DisallowUnknownFields = true
}
func (d *Decoder) InputOffset() int64 {
return d.s.totalOffset()
return d.s.TotalOffset()
}
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64.
func (d *Decoder) UseNumber() {
d.s.useNumber = true
d.s.UseNumber = true
}

View file

@ -1,35 +0,0 @@
package json
import (
"unsafe"
)
type anonymousFieldDecoder struct {
structType *rtype
offset uintptr
dec decoder
}
func newAnonymousFieldDecoder(structType *rtype, 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(buf []byte, 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(buf, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset))
}

View file

@ -1,172 +0,0 @@
package json
import (
"encoding/base64"
"unsafe"
)
type bytesDecoder struct {
typ *rtype
sliceDecoder decoder
structName string
fieldName string
}
func byteUnmarshalerSliceDecoder(typ *rtype, structName string, fieldName string) decoder {
var unmarshalDecoder decoder
switch {
case rtype_ptrTo(typ).Implements(unmarshalJSONType):
unmarshalDecoder = newUnmarshalJSONDecoder(rtype_ptrTo(typ), structName, fieldName)
case rtype_ptrTo(typ).Implements(unmarshalTextType):
unmarshalDecoder = newUnmarshalTextDecoder(rtype_ptrTo(typ), structName, fieldName)
}
if unmarshalDecoder == nil {
return nil
}
return newSliceDecoder(unmarshalDecoder, typ, 1, structName, fieldName)
}
func newBytesDecoder(typ *rtype, 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)
if _, err := base64.StdEncoding.Decode(buf, bytes); err != nil {
return err
}
*(*[]byte)(p) = buf
s.reset()
return nil
}
func (d *bytesDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
bytes, c, err := d.decodeBinary(buf, 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, 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, &UnmarshalTypeError{
Type: 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, errNotAtBeginningOfValue(s.totalOffset())
}
func (d *bytesDecoder) decodeBinary(buf []byte, cursor, depth int64, p unsafe.Pointer) ([]byte, int64, error) {
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, errUnexpectedEndOfJSON("[]byte", cursor)
}
cursor++
}
case '[':
if d.sliceDecoder == nil {
return nil, 0, &UnmarshalTypeError{
Type: rtype2type(d.typ),
Offset: cursor,
}
}
c, err := d.sliceDecoder.decode(buf, 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, errNotAtBeginningOfValue(cursor)
}
}
}

View file

@ -1,24 +0,0 @@
// +build !race
package json
import "unsafe"
func decodeCompileToGetDecoder(typ *rtype) (decoder, error) {
typeptr := uintptr(unsafe.Pointer(typ))
if typeptr > maxTypeAddr {
return decodeCompileToGetDecoderSlowPath(typeptr, typ)
}
index := (typeptr - baseTypeAddr) >> typeAddrShift
if dec := cachedDecoder[index]; dec != nil {
return dec, nil
}
dec, err := decodeCompileHead(typ, map[uintptr]decoder{})
if err != nil {
return nil, err
}
cachedDecoder[index] = dec
return dec, nil
}

View file

@ -1,154 +0,0 @@
package json
import (
"unsafe"
)
type mapDecoder struct {
mapType *rtype
keyType *rtype
valueType *rtype
keyDecoder decoder
valueDecoder decoder
structName string
fieldName string
}
func newMapDecoder(mapType *rtype, keyType *rtype, keyDec decoder, valueType *rtype, valueDec decoder, structName, fieldName string) *mapDecoder {
return &mapDecoder{
mapType: mapType,
keyDecoder: keyDec,
keyType: keyType,
valueType: valueType,
valueDecoder: valueDec,
structName: structName,
fieldName: fieldName,
}
}
//go:linkname makemap reflect.makemap
func makemap(*rtype, int) unsafe.Pointer
//go:linkname mapassign reflect.mapassign
//go:noescape
func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer)
func (d *mapDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
depth++
if depth > maxDecodeNestingDepth {
return errExceededMaxDepth(s.char(), s.cursor)
}
s.skipWhiteSpace()
switch s.char() {
case 'n':
if err := nullBytes(s); err != nil {
return err
}
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil
return nil
case '{':
default:
return errExpected("{ character for map value", s.totalOffset())
}
s.skipWhiteSpace()
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 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
}
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 errExpected("comma after object value", s.totalOffset())
}
}
}
func (d *mapDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
depth++
if depth > maxDecodeNestingDepth {
return 0, errExceededMaxDepth(buf[cursor], cursor)
}
cursor = skipWhiteSpace(buf, cursor)
buflen := int64(len(buf))
if buflen < 2 {
return 0, 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, 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(buf, cursor, depth, k)
if err != nil {
return 0, err
}
cursor = skipWhiteSpace(buf, keyCursor)
if buf[cursor] != ':' {
return 0, errExpected("colon after object key", cursor)
}
cursor++
v := unsafe_New(d.valueType)
valueCursor, err := d.valueDecoder.decode(buf, cursor, depth, v)
if err != nil {
return 0, err
}
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, errExpected("comma after object value", cursor)
}
cursor++
}
}

View file

@ -1,623 +0,0 @@
package json
import (
"fmt"
"math"
"math/bits"
"sort"
"strings"
"unsafe"
)
type structFieldSet struct {
dec decoder
offset uintptr
isTaggedKey bool
key string
keyLen int64
err error
}
type structDecoder struct {
fieldMap map[string]*structFieldSet
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() {
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
}
}
func decodeKeyByBitmapUint8(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
var (
field *structFieldSet
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, field, nil
case nul:
return 0, nil, 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, errUnexpectedEndOfJSON("string", cursor)
default:
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 {
for {
cursor++
switch char(b, cursor) {
case '"':
cursor++
return cursor, field, nil
case '\\':
cursor++
if char(b, cursor) == nul {
return 0, nil, errUnexpectedEndOfJSON("string", cursor)
}
case nul:
return 0, nil, errUnexpectedEndOfJSON("string", cursor)
}
}
}
keyIdx++
}
cursor++
}
default:
return cursor, nil, errNotAtBeginningOfValue(cursor)
}
}
}
func decodeKeyByBitmapUint16(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
var (
field *structFieldSet
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, field, nil
case nul:
return 0, nil, 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, errUnexpectedEndOfJSON("string", cursor)
default:
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 {
for {
cursor++
switch char(b, cursor) {
case '"':
cursor++
return cursor, field, nil
case '\\':
cursor++
if char(b, cursor) == nul {
return 0, nil, errUnexpectedEndOfJSON("string", cursor)
}
case nul:
return 0, nil, errUnexpectedEndOfJSON("string", cursor)
}
}
}
keyIdx++
}
cursor++
}
default:
return cursor, nil, errNotAtBeginningOfValue(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 (
field *structFieldSet
curBit uint8 = math.MaxUint8
)
buf, cursor, p := s.stat()
for {
switch char(p, cursor) {
case ' ', '\n', '\t', '\r':
cursor++
case nul:
s.cursor = cursor
if s.read() {
buf, cursor, p = s.stat()
continue
}
return nil, "", errNotAtBeginningOfValue(s.totalOffset())
case '"':
cursor++
FIRST_CHAR:
start := cursor
switch char(p, cursor) {
case '"':
cursor++
s.cursor = cursor
return field, "", nil
case nul:
s.cursor = cursor
if s.read() {
buf, cursor, p = s.stat()
goto FIRST_CHAR
}
return nil, "", 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() {
buf, cursor, p = s.stat()
continue
}
return nil, "", errUnexpectedEndOfJSON("string", s.totalOffset())
default:
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 {
for {
cursor++
switch char(p, cursor) {
case '"':
b := buf[start:cursor]
key := *(*string)(unsafe.Pointer(&b))
cursor++
s.cursor = cursor
return field, key, nil
case '\\':
cursor++
if char(p, cursor) == nul {
s.cursor = cursor
if !s.read() {
return nil, "", errUnexpectedEndOfJSON("string", s.totalOffset())
}
buf, cursor, p = s.statForRetry()
}
case nul:
s.cursor = cursor
if !s.read() {
return nil, "", errUnexpectedEndOfJSON("string", s.totalOffset())
}
buf, cursor, p = s.statForRetry()
}
}
}
keyIdx++
}
cursor++
}
default:
return nil, "", errNotAtBeginningOfValue(s.totalOffset())
}
}
}
func decodeKeyByBitmapUint16Stream(d *structDecoder, s *stream) (*structFieldSet, string, error) {
var (
field *structFieldSet
curBit uint16 = math.MaxUint16
)
buf, cursor, p := s.stat()
for {
switch char(p, cursor) {
case ' ', '\n', '\t', '\r':
cursor++
case nul:
s.cursor = cursor
if s.read() {
buf, cursor, p = s.stat()
continue
}
return nil, "", errNotAtBeginningOfValue(s.totalOffset())
case '"':
cursor++
FIRST_CHAR:
start := cursor
switch char(p, cursor) {
case '"':
cursor++
s.cursor = cursor
return field, "", nil
case nul:
s.cursor = cursor
if s.read() {
buf, cursor, p = s.stat()
goto FIRST_CHAR
}
return nil, "", 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() {
buf, cursor, p = s.stat()
continue
}
return nil, "", errUnexpectedEndOfJSON("string", s.totalOffset())
default:
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 {
for {
cursor++
switch char(p, cursor) {
case '"':
b := buf[start:cursor]
key := *(*string)(unsafe.Pointer(&b))
cursor++
s.cursor = cursor
return field, key, nil
case '\\':
cursor++
if char(p, cursor) == nul {
s.cursor = cursor
if !s.read() {
return nil, "", errUnexpectedEndOfJSON("string", s.totalOffset())
}
buf, cursor, p = s.statForRetry()
}
case nul:
s.cursor = cursor
if !s.read() {
return nil, "", errUnexpectedEndOfJSON("string", s.totalOffset())
}
buf, cursor, p = s.statForRetry()
}
}
}
keyIdx++
}
cursor++
}
default:
return nil, "", errNotAtBeginningOfValue(s.totalOffset())
}
}
}
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 errExceededMaxDepth(s.char(), s.cursor)
}
s.skipWhiteSpace()
switch s.char() {
case 'n':
if err := nullBytes(s); err != nil {
return err
}
return nil
case nul:
s.read()
default:
if s.char() != '{' {
return errNotAtBeginningOfValue(s.totalOffset())
}
}
s.cursor++
s.skipWhiteSpace()
if s.char() == '}' {
s.cursor++
return nil
}
for {
s.reset()
field, key, err := d.keyStreamDecoder(d, s)
if err != nil {
return err
}
s.skipWhiteSpace()
if s.char() != ':' {
return errExpected("colon after object key", s.totalOffset())
}
s.cursor++
if s.char() == nul {
if !s.read() {
return errExpected("object value after colon", s.totalOffset())
}
}
if field != nil {
if field.err != nil {
return field.err
}
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
}
}
s.skipWhiteSpace()
c := s.char()
if c == '}' {
s.cursor++
return nil
}
if c != ',' {
return errExpected("comma after object element", s.totalOffset())
}
s.cursor++
}
}
func (d *structDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
depth++
if depth > maxDecodeNestingDepth {
return 0, 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, errNotAtBeginningOfValue(cursor)
}
cursor++
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == '}' {
cursor++
return cursor, nil
}
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, errExpected("colon after object key", cursor)
}
cursor++
if cursor >= buflen {
return 0, errExpected("object value after colon", cursor)
}
if field != nil {
if field.err != nil {
return 0, field.err
}
c, err := field.dec.decode(buf, 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, errExpected("comma after object element", cursor)
}
cursor++
}
}

View file

@ -1,72 +0,0 @@
package json
import (
"unsafe"
)
type unmarshalJSONDecoder struct {
typ *rtype
structName string
fieldName string
}
func newUnmarshalJSONDecoder(typ *rtype, 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 *UnmarshalTypeError:
e.Struct = d.structName
e.Field = d.fieldName
case *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 err := v.(Unmarshaler).UnmarshalJSON(dst); err != nil {
d.annotateError(s.cursor, err)
return err
}
return nil
}
func (d *unmarshalJSONDecoder) decode(buf []byte, cursor, depth int64, 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]
dst := make([]byte, len(src))
copy(dst, src)
v := *(*interface{})(unsafe.Pointer(&emptyInterface{
typ: d.typ,
ptr: p,
}))
if err := v.(Unmarshaler).UnmarshalJSON(dst); err != nil {
d.annotateError(cursor, err)
return 0, err
}
return end, nil
}

View file

@ -1,13 +1,14 @@
package json
import (
"context"
"io"
"unsafe"
"github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/encoder/vm"
"github.com/goccy/go-json/internal/encoder/vm_escaped"
"github.com/goccy/go-json/internal/encoder/vm_escaped_indent"
"github.com/goccy/go-json/internal/encoder/vm_color"
"github.com/goccy/go-json/internal/encoder/vm_color_indent"
"github.com/goccy/go-json/internal/encoder/vm_indent"
)
@ -20,15 +21,6 @@ type Encoder struct {
indentStr string
}
type EncodeOption int
const (
EncodeOptionHTMLEscape EncodeOption = 1 << iota
EncodeOptionIndent
EncodeOptionUnorderedMap
EncodeOptionDebug
)
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w, enabledHTMLEscape: true}
@ -44,6 +36,7 @@ func (e *Encoder) Encode(v interface{}) error {
// EncodeWithOption call Encode with EncodeOption.
func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc) error {
ctx := encoder.TakeRuntimeContext()
ctx.Option.Flag = 0
err := e.encodeWithOption(ctx, v, optFuncs...)
@ -51,22 +44,34 @@ func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc)
return err
}
// EncodeContext call Encode with context.Context and EncodeOption.
func (e *Encoder) EncodeContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) error {
rctx := encoder.TakeRuntimeContext()
rctx.Option.Flag = 0
rctx.Option.Flag |= encoder.ContextOption
rctx.Option.Context = ctx
err := e.encodeWithOption(rctx, v, optFuncs...)
encoder.ReleaseRuntimeContext(rctx)
return err
}
func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
var opt EncodeOption
if e.enabledHTMLEscape {
opt |= EncodeOptionHTMLEscape
ctx.Option.Flag |= encoder.HTMLEscapeOption
}
for _, optFunc := range optFuncs {
opt = optFunc(opt)
optFunc(ctx.Option)
}
var (
buf []byte
err error
)
if e.enabledIndent {
buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr, opt)
buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr)
} else {
buf, err = encode(ctx, v, opt)
buf, err = encode(ctx, v)
}
if err != nil {
return err
@ -103,10 +108,43 @@ func (e *Encoder) SetIndent(prefix, indent string) {
e.enabledIndent = true
}
func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
func marshalContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
rctx := encoder.TakeRuntimeContext()
rctx.Option.Flag = 0
rctx.Option.Flag = encoder.HTMLEscapeOption | encoder.ContextOption
rctx.Option.Context = ctx
for _, optFunc := range optFuncs {
optFunc(rctx.Option)
}
buf, err := encode(rctx, v)
if err != nil {
encoder.ReleaseRuntimeContext(rctx)
return nil, err
}
// this line exists to escape call of `runtime.makeslicecopy` .
// if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`,
// dst buffer size and src buffer size are differrent.
// in this case, compiler uses `runtime.makeslicecopy`, but it is slow.
buf = buf[:len(buf)-1]
copied := make([]byte, len(buf))
copy(copied, buf)
encoder.ReleaseRuntimeContext(rctx)
return copied, nil
}
func marshal(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
ctx := encoder.TakeRuntimeContext()
buf, err := encode(ctx, v, opt|EncodeOptionHTMLEscape)
ctx.Option.Flag = 0
ctx.Option.Flag |= encoder.HTMLEscapeOption
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
buf, err := encode(ctx, v)
if err != nil {
encoder.ReleaseRuntimeContext(ctx)
return nil, err
@ -124,10 +162,13 @@ func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
return copied, nil
}
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
func marshalNoEscape(v interface{}) ([]byte, error) {
ctx := encoder.TakeRuntimeContext()
buf, err := encodeNoEscape(ctx, v, opt|EncodeOptionHTMLEscape)
ctx.Option.Flag = 0
ctx.Option.Flag |= encoder.HTMLEscapeOption
buf, err := encodeNoEscape(ctx, v)
if err != nil {
encoder.ReleaseRuntimeContext(ctx)
return nil, err
@ -145,10 +186,16 @@ func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
return copied, nil
}
func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
func marshalIndent(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
ctx := encoder.TakeRuntimeContext()
buf, err := encodeIndent(ctx, v, prefix, indent, opt|EncodeOptionHTMLEscape)
ctx.Option.Flag = 0
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.IndentOption)
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
buf, err := encodeIndent(ctx, v, prefix, indent)
if err != nil {
encoder.ReleaseRuntimeContext(ctx)
return nil, err
@ -162,11 +209,11 @@ func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]by
return copied, nil
}
func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
func encode(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
b := ctx.Buf[:0]
if v == nil {
b = encoder.AppendNull(b)
b = encoder.AppendComma(b)
b = encoder.AppendNull(ctx, b)
b = encoder.AppendComma(ctx, b)
return b, nil
}
header := (*emptyInterface)(unsafe.Pointer(&v))
@ -182,7 +229,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
ctx.Init(p, codeSet.CodeLength)
ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
buf, err := encodeRunCode(ctx, b, codeSet, opt)
buf, err := encodeRunCode(ctx, b, codeSet)
if err != nil {
return nil, err
}
@ -190,11 +237,11 @@ func encode(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byt
return buf, nil
}
func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
b := ctx.Buf[:0]
if v == nil {
b = encoder.AppendNull(b)
b = encoder.AppendComma(b)
b = encoder.AppendNull(ctx, b)
b = encoder.AppendComma(ctx, b)
return b, nil
}
header := (*emptyInterface)(unsafe.Pointer(&v))
@ -208,7 +255,7 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption
p := uintptr(header.ptr)
ctx.Init(p, codeSet.CodeLength)
buf, err := encodeRunCode(ctx, b, codeSet, opt)
buf, err := encodeRunCode(ctx, b, codeSet)
if err != nil {
return nil, err
}
@ -217,11 +264,11 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}, opt EncodeOption
return buf, nil
}
func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent string) ([]byte, error) {
b := ctx.Buf[:0]
if v == nil {
b = encoder.AppendNull(b)
b = encoder.AppendCommaIndent(b)
b = encoder.AppendNull(ctx, b)
b = encoder.AppendCommaIndent(ctx, b)
return b, nil
}
header := (*emptyInterface)(unsafe.Pointer(&v))
@ -235,7 +282,7 @@ func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent str
p := uintptr(header.ptr)
ctx.Init(p, codeSet.CodeLength)
buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent, opt)
buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent)
ctx.KeepRefs = append(ctx.KeepRefs, header.ptr)
@ -247,38 +294,30 @@ func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent str
return buf, nil
}
func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) {
if (opt & EncodeOptionDebug) != 0 {
return encodeDebugRunCode(ctx, b, codeSet, opt)
func encodeRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
if (ctx.Option.Flag & encoder.DebugOption) != 0 {
if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
return vm_color.DebugRun(ctx, b, codeSet)
}
return vm.DebugRun(ctx, b, codeSet)
}
if (opt & EncodeOptionHTMLEscape) != 0 {
return vm_escaped.Run(ctx, b, codeSet, encoder.Option(opt))
if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
return vm_color.Run(ctx, b, codeSet)
}
return vm.Run(ctx, b, codeSet, encoder.Option(opt))
return vm.Run(ctx, b, codeSet)
}
func encodeDebugRunCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) {
if (opt & EncodeOptionHTMLEscape) != 0 {
return vm_escaped.DebugRun(ctx, b, codeSet, encoder.Option(opt))
}
return vm.DebugRun(ctx, b, codeSet, encoder.Option(opt))
}
func encodeRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) {
func encodeRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, prefix, indent string) ([]byte, error) {
ctx.Prefix = []byte(prefix)
ctx.IndentStr = []byte(indent)
if (opt & EncodeOptionDebug) != 0 {
return encodeDebugRunIndentCode(ctx, b, codeSet, opt)
if (ctx.Option.Flag & encoder.DebugOption) != 0 {
if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
return vm_color_indent.DebugRun(ctx, b, codeSet)
}
return vm_indent.DebugRun(ctx, b, codeSet)
}
if (opt & EncodeOptionHTMLEscape) != 0 {
return vm_escaped_indent.Run(ctx, b, codeSet, encoder.Option(opt))
if (ctx.Option.Flag & encoder.ColorizeOption) != 0 {
return vm_color_indent.Run(ctx, b, codeSet)
}
return vm_indent.Run(ctx, b, codeSet, encoder.Option(opt))
}
func encodeDebugRunIndentCode(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt EncodeOption) ([]byte, error) {
if (opt & EncodeOptionHTMLEscape) != 0 {
return vm_escaped_indent.DebugRun(ctx, b, codeSet, encoder.Option(opt))
}
return vm_indent.DebugRun(ctx, b, codeSet, encoder.Option(opt))
return vm_indent.Run(ctx, b, codeSet)
}

View file

@ -37,13 +37,3 @@ type UnmarshalTypeError = errors.UnmarshalTypeError
type UnsupportedTypeError = errors.UnsupportedTypeError
type UnsupportedValueError = errors.UnsupportedValueError
var (
errExceededMaxDepth = errors.ErrExceededMaxDepth
errNotAtBeginningOfValue = errors.ErrNotAtBeginningOfValue
errUnexpectedEndOfJSON = errors.ErrUnexpectedEndOfJSON
errExpected = errors.ErrExpected
errInvalidCharacter = errors.ErrInvalidCharacter
errSyntax = errors.ErrSyntax
errMarshaler = errors.ErrMarshaler
)

View file

@ -1,3 +0,0 @@
module github.com/goccy/go-json
go 1.12

View file

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

@ -1,20 +1,23 @@
package json
package decoder
import (
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type arrayDecoder struct {
elemType *rtype
elemType *runtime.Type
size uintptr
valueDecoder decoder
valueDecoder Decoder
alen int
structName string
fieldName string
zeroValue unsafe.Pointer
}
func newArrayDecoder(dec decoder, elemType *rtype, alen int, structName, fieldName string) *arrayDecoder {
func newArrayDecoder(dec Decoder, elemType *runtime.Type, alen int, structName, fieldName string) *arrayDecoder {
zeroValue := *(*unsafe.Pointer)(unsafe_New(elemType))
return &arrayDecoder{
valueDecoder: dec,
@ -27,10 +30,10 @@ func newArrayDecoder(dec decoder, elemType *rtype, alen int, structName, fieldNa
}
}
func (d *arrayDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
func (d *arrayDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
depth++
if depth > maxDecodeNestingDepth {
return errExceededMaxDepth(s.char(), s.cursor)
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
}
for {
@ -43,10 +46,18 @@ func (d *arrayDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
return nil
case '[':
idx := 0
for {
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 {
if err := d.valueDecoder.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size)); err != nil {
return err
}
} else {
@ -55,8 +66,7 @@ func (d *arrayDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
}
}
idx++
s.skipWhiteSpace()
switch s.char() {
switch s.skipWhiteSpace() {
case ']':
for idx < d.alen {
*(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue
@ -65,9 +75,11 @@ func (d *arrayDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
s.cursor++
return nil
case ',':
s.cursor++
continue
case nul:
if s.read() {
s.cursor++
continue
}
goto ERROR
@ -86,13 +98,14 @@ func (d *arrayDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
s.cursor++
}
ERROR:
return errUnexpectedEndOfJSON("array", s.totalOffset())
return errors.ErrUnexpectedEndOfJSON("array", s.totalOffset())
}
func (d *arrayDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
buf := ctx.Buf
depth++
if depth > maxDecodeNestingDepth {
return 0, errExceededMaxDepth(buf[cursor], cursor)
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
for {
@ -108,10 +121,19 @@ func (d *arrayDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
return cursor, nil
case '[':
idx := 0
for {
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(buf, cursor, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size))
c, err := d.valueDecoder.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size))
if err != nil {
return 0, err
}
@ -134,13 +156,14 @@ func (d *arrayDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
cursor++
return cursor, nil
case ',':
cursor++
continue
default:
return 0, errInvalidCharacter(buf[cursor], "array", cursor)
return 0, errors.ErrInvalidCharacter(buf[cursor], "array", cursor)
}
}
default:
return 0, errUnexpectedEndOfJSON("array", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("array", cursor)
}
}
}

View file

@ -1,7 +1,9 @@
package json
package decoder
import (
"unsafe"
"github.com/goccy/go-json/internal/errors"
)
type boolDecoder struct {
@ -13,10 +15,10 @@ func newBoolDecoder(structName, fieldName string) *boolDecoder {
return &boolDecoder{structName: structName, fieldName: fieldName}
}
func (d *boolDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
s.skipWhiteSpace()
func (d *boolDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
c := s.skipWhiteSpace()
for {
switch s.char() {
switch c {
case 't':
if err := trueBytes(s); err != nil {
return err
@ -36,6 +38,7 @@ func (d *boolDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) err
return nil
case nul:
if s.read() {
c = s.char()
continue
}
goto ERROR
@ -43,10 +46,11 @@ func (d *boolDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) err
break
}
ERROR:
return errUnexpectedEndOfJSON("bool", s.totalOffset())
return errors.ErrUnexpectedEndOfJSON("bool", s.totalOffset())
}
func (d *boolDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
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':
@ -70,5 +74,5 @@ func (d *boolDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
cursor += 4
return cursor, nil
}
return 0, errUnexpectedEndOfJSON("bool", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("bool", cursor)
}

View file

@ -0,0 +1,114 @@
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
stringDecoder *stringDecoder
structName string
fieldName string
}
func byteUnmarshalerSliceDecoder(typ *runtime.Type, structName string, fieldName string) Decoder {
var unmarshalDecoder Decoder
switch {
case runtime.PtrTo(typ).Implements(unmarshalJSONType):
unmarshalDecoder = newUnmarshalJSONDecoder(runtime.PtrTo(typ), structName, fieldName)
case runtime.PtrTo(typ).Implements(unmarshalTextType):
unmarshalDecoder = newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName)
}
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),
stringDecoder: newStringDecoder(structName, fieldName),
structName: structName,
fieldName: fieldName,
}
}
func (d *bytesDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
bytes, err := d.decodeStreamBinary(s, depth, p)
if err != nil {
return err
}
if bytes == nil {
s.reset()
return nil
}
decodedLen := base64.StdEncoding.DecodedLen(len(bytes))
buf := make([]byte, decodedLen)
n, err := base64.StdEncoding.Decode(buf, bytes)
if err != nil {
return err
}
*(*[]byte)(p) = buf[:n]
s.reset()
return nil
}
func (d *bytesDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
bytes, c, err := d.decodeBinary(ctx, cursor, depth, p)
if err != nil {
return 0, err
}
if bytes == nil {
return c, nil
}
cursor = c
decodedLen := base64.StdEncoding.DecodedLen(len(bytes))
b := make([]byte, decodedLen)
n, err := base64.StdEncoding.Decode(b, bytes)
if err != nil {
return 0, err
}
*(*[]byte)(p) = b[:n]
return cursor, nil
}
func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Pointer) ([]byte, error) {
c := s.skipWhiteSpace()
if c == '[' {
if d.sliceDecoder == nil {
return nil, &errors.UnmarshalTypeError{
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
}
err := d.sliceDecoder.DecodeStream(s, depth, p)
return nil, err
}
return d.stringDecoder.decodeStreamByte(s)
}
func (d *bytesDecoder) decodeBinary(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) ([]byte, int64, error) {
buf := ctx.Buf
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == '[' {
if d.sliceDecoder == nil {
return nil, 0, &errors.UnmarshalTypeError{
Type: runtime.RType2Type(d.typ),
Offset: cursor,
}
}
c, err := d.sliceDecoder.Decode(ctx, cursor, depth, p)
if err != nil {
return nil, 0, err
}
return nil, c, nil
}
return d.stringDecoder.decodeByte(buf, cursor)
}

View file

@ -1,27 +1,56 @@
package json
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(""))
jsonNumberType = reflect.TypeOf(json.Number(""))
typeAddr *runtime.TypeAddr
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
cachedDecoder []Decoder
)
func decodeCompileToGetDecoderSlowPath(typeptr uintptr, typ *rtype) (decoder, error) {
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 := decodeCompileHead(typ, map[uintptr]decoder{})
dec, err := compileHead(typ, map[uintptr]Decoder{})
if err != nil {
return nil, err
}
@ -29,84 +58,88 @@ func decodeCompileToGetDecoderSlowPath(typeptr uintptr, typ *rtype) (decoder, er
return dec, nil
}
func decodeCompileHead(typ *rtype, structTypeToDecoder map[uintptr]decoder) (decoder, error) {
func compileHead(typ *runtime.Type, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
switch {
case rtype_ptrTo(typ).Implements(unmarshalJSONType):
return newUnmarshalJSONDecoder(rtype_ptrTo(typ), "", ""), nil
case rtype_ptrTo(typ).Implements(unmarshalTextType):
return newUnmarshalTextDecoder(rtype_ptrTo(typ), "", ""), nil
case implementsUnmarshalJSONType(runtime.PtrTo(typ)):
return newUnmarshalJSONDecoder(runtime.PtrTo(typ), "", ""), nil
case runtime.PtrTo(typ).Implements(unmarshalTextType):
return newUnmarshalTextDecoder(runtime.PtrTo(typ), "", ""), nil
}
return decodeCompile(typ.Elem(), "", "", structTypeToDecoder)
return compile(typ.Elem(), "", "", structTypeToDecoder)
}
func decodeCompile(typ *rtype, structName, fieldName string, structTypeToDecoder map[uintptr]decoder) (decoder, error) {
func compile(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
switch {
case rtype_ptrTo(typ).Implements(unmarshalJSONType):
return newUnmarshalJSONDecoder(rtype_ptrTo(typ), structName, fieldName), nil
case rtype_ptrTo(typ).Implements(unmarshalTextType):
return newUnmarshalTextDecoder(rtype_ptrTo(typ), structName, fieldName), nil
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 decodeCompilePtr(typ, structName, fieldName, structTypeToDecoder)
return compilePtr(typ, structName, fieldName, structTypeToDecoder)
case reflect.Struct:
return decodeCompileStruct(typ, structName, fieldName, structTypeToDecoder)
return compileStruct(typ, structName, fieldName, structTypeToDecoder)
case reflect.Slice:
elem := typ.Elem()
if elem.Kind() == reflect.Uint8 {
return decodeCompileBytes(elem, structName, fieldName)
return compileBytes(elem, structName, fieldName)
}
return decodeCompileSlice(typ, structName, fieldName, structTypeToDecoder)
return compileSlice(typ, structName, fieldName, structTypeToDecoder)
case reflect.Array:
return decodeCompileArray(typ, structName, fieldName, structTypeToDecoder)
return compileArray(typ, structName, fieldName, structTypeToDecoder)
case reflect.Map:
return decodeCompileMap(typ, structName, fieldName, structTypeToDecoder)
return compileMap(typ, structName, fieldName, structTypeToDecoder)
case reflect.Interface:
return decodeCompileInterface(typ, structName, fieldName)
return compileInterface(typ, structName, fieldName)
case reflect.Uintptr:
return decodeCompileUint(typ, structName, fieldName)
return compileUint(typ, structName, fieldName)
case reflect.Int:
return decodeCompileInt(typ, structName, fieldName)
return compileInt(typ, structName, fieldName)
case reflect.Int8:
return decodeCompileInt8(typ, structName, fieldName)
return compileInt8(typ, structName, fieldName)
case reflect.Int16:
return decodeCompileInt16(typ, structName, fieldName)
return compileInt16(typ, structName, fieldName)
case reflect.Int32:
return decodeCompileInt32(typ, structName, fieldName)
return compileInt32(typ, structName, fieldName)
case reflect.Int64:
return decodeCompileInt64(typ, structName, fieldName)
return compileInt64(typ, structName, fieldName)
case reflect.Uint:
return decodeCompileUint(typ, structName, fieldName)
return compileUint(typ, structName, fieldName)
case reflect.Uint8:
return decodeCompileUint8(typ, structName, fieldName)
return compileUint8(typ, structName, fieldName)
case reflect.Uint16:
return decodeCompileUint16(typ, structName, fieldName)
return compileUint16(typ, structName, fieldName)
case reflect.Uint32:
return decodeCompileUint32(typ, structName, fieldName)
return compileUint32(typ, structName, fieldName)
case reflect.Uint64:
return decodeCompileUint64(typ, structName, fieldName)
return compileUint64(typ, structName, fieldName)
case reflect.String:
return decodeCompileString(typ, structName, fieldName)
return compileString(typ, structName, fieldName)
case reflect.Bool:
return decodeCompileBool(structName, fieldName)
return compileBool(structName, fieldName)
case reflect.Float32:
return decodeCompileFloat32(structName, fieldName)
return compileFloat32(structName, fieldName)
case reflect.Float64:
return decodeCompileFloat64(structName, fieldName)
return compileFloat64(structName, fieldName)
case reflect.Func:
return compileFunc(typ, structName, fieldName)
}
return nil, &UnmarshalTypeError{
return nil, &errors.UnmarshalTypeError{
Value: "object",
Type: rtype2type(typ),
Type: runtime.RType2Type(typ),
Offset: 0,
Struct: structName,
Field: fieldName,
}
}
func isStringTagSupportedType(typ *rtype) bool {
func isStringTagSupportedType(typ *runtime.Type) bool {
switch {
case rtype_ptrTo(typ).Implements(unmarshalJSONType):
case implementsUnmarshalJSONType(runtime.PtrTo(typ)):
return false
case rtype_ptrTo(typ).Implements(unmarshalTextType):
case runtime.PtrTo(typ).Implements(unmarshalTextType):
return false
}
switch typ.Kind() {
@ -124,11 +157,11 @@ func isStringTagSupportedType(typ *rtype) bool {
return true
}
func decodeCompileMapKey(typ *rtype, structName, fieldName string, structTypeToDecoder map[uintptr]decoder) (decoder, error) {
if rtype_ptrTo(typ).Implements(unmarshalTextType) {
return newUnmarshalTextDecoder(rtype_ptrTo(typ), structName, fieldName), nil
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 := decodeCompile(typ, structName, fieldName, structTypeToDecoder)
dec, err := compile(typ, structName, fieldName, structTypeToDecoder)
if err != nil {
return nil, err
}
@ -145,145 +178,151 @@ func decodeCompileMapKey(typ *rtype, structName, fieldName string, structTypeToD
}
}
ERROR:
return nil, &UnmarshalTypeError{
return nil, &errors.UnmarshalTypeError{
Value: "object",
Type: rtype2type(typ),
Type: runtime.RType2Type(typ),
Offset: 0,
Struct: structName,
Field: fieldName,
}
}
func decodeCompilePtr(typ *rtype, structName, fieldName string, structTypeToDecoder map[uintptr]decoder) (decoder, error) {
dec, err := decodeCompile(typ.Elem(), structName, fieldName, structTypeToDecoder)
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 decodeCompileInt(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileInt8(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileInt16(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileInt32(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileInt64(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileUint(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileUint8(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileUint16(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileUint32(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileUint64(typ *rtype, structName, fieldName string) (decoder, error) {
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 decodeCompileFloat32(structName, fieldName string) (decoder, error) {
func compileFloat32(structName, fieldName string) (Decoder, error) {
return newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) {
*(*float32)(p) = float32(v)
}), nil
}
func decodeCompileFloat64(structName, fieldName string) (decoder, error) {
func compileFloat64(structName, fieldName string) (Decoder, error) {
return newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) {
*(*float64)(p) = v
}), nil
}
func decodeCompileString(typ *rtype, structName, fieldName string) (decoder, error) {
if typ == type2rtype(jsonNumberType) {
return newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v Number) {
*(*Number)(p) = v
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 decodeCompileBool(structName, fieldName string) (decoder, error) {
func compileBool(structName, fieldName string) (Decoder, error) {
return newBoolDecoder(structName, fieldName), nil
}
func decodeCompileBytes(typ *rtype, structName, fieldName string) (decoder, error) {
func compileBytes(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
return newBytesDecoder(typ, structName, fieldName), nil
}
func decodeCompileSlice(typ *rtype, structName, fieldName string, structTypeToDecoder map[uintptr]decoder) (decoder, error) {
func compileSlice(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
elem := typ.Elem()
decoder, err := decodeCompile(elem, structName, fieldName, structTypeToDecoder)
decoder, err := compile(elem, structName, fieldName, structTypeToDecoder)
if err != nil {
return nil, err
}
return newSliceDecoder(decoder, elem, elem.Size(), structName, fieldName), nil
}
func decodeCompileArray(typ *rtype, structName, fieldName string, structTypeToDecoder map[uintptr]decoder) (decoder, error) {
func compileArray(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
elem := typ.Elem()
decoder, err := decodeCompile(elem, structName, fieldName, structTypeToDecoder)
decoder, err := compile(elem, structName, fieldName, structTypeToDecoder)
if err != nil {
return nil, err
}
return newArrayDecoder(decoder, elem, typ.Len(), structName, fieldName), nil
}
func decodeCompileMap(typ *rtype, structName, fieldName string, structTypeToDecoder map[uintptr]decoder) (decoder, error) {
keyDec, err := decodeCompileMapKey(typ.Key(), structName, fieldName, structTypeToDecoder)
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 := decodeCompile(typ.Elem(), structName, fieldName, structTypeToDecoder)
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 decodeCompileInterface(typ *rtype, structName, fieldName string) (decoder, error) {
func compileInterface(typ *runtime.Type, structName, fieldName string) (Decoder, error) {
return newInterfaceDecoder(typ, structName, fieldName), nil
}
func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, field reflect.StructField) {
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
@ -338,7 +377,7 @@ func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedM
}
}
func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToDecoder map[uintptr]decoder) (decoder, error) {
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{}
@ -356,17 +395,17 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
}
isUnexportedField := unicode.IsLower([]rune(field.Name)[0])
tag := runtime.StructTagFromField(field)
dec, err := decodeCompile(type2rtype(field.Type), structName, field.Name, structTypeToDecoder)
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 type2rtype(field.Type) == typ {
if runtime.Type2RType(field.Type) == typ {
// recursive definition
continue
}
decodeRemoveConflictFields(fieldMap, conflictedMap, stDec, field)
removeConflictFields(fieldMap, conflictedMap, stDec, field)
} else if pdec, ok := dec.(*ptrDecoder); ok {
contentDec := pdec.contentDecoder()
if pdec.typ == typ {
@ -438,8 +477,8 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
}
}
} else {
if tag.IsString && isStringTagSupportedType(type2rtype(field.Type)) {
dec = newWrappedStringDecoder(type2rtype(field.Type), dec, structName, field.Name)
if tag.IsString && isStringTagSupportedType(runtime.Type2RType(field.Type)) {
dec = newWrappedStringDecoder(runtime.Type2RType(field.Type), dec, structName, field.Name)
}
var key string
if tag.Key != "" {
@ -465,3 +504,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
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

@ -1,21 +1,23 @@
// +build race
package json
package decoder
import (
"sync"
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
var decMu sync.RWMutex
func decodeCompileToGetDecoder(typ *rtype) (decoder, error) {
func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) {
typeptr := uintptr(unsafe.Pointer(typ))
if typeptr > maxTypeAddr {
return decodeCompileToGetDecoderSlowPath(typeptr, typ)
if typeptr > typeAddr.MaxTypeAddr {
return compileToGetDecoderSlowPath(typeptr, typ)
}
index := (typeptr - baseTypeAddr) >> typeAddrShift
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
decMu.RLock()
if dec := cachedDecoder[index]; dec != nil {
decMu.RUnlock()
@ -23,7 +25,7 @@ func decodeCompileToGetDecoder(typ *rtype) (decoder, error) {
}
decMu.RUnlock()
dec, err := decodeCompileHead(typ, map[uintptr]decoder{})
dec, err := compileHead(typ, map[uintptr]Decoder{})
if err != nil {
return nil, err
}

View file

@ -1,9 +1,35 @@
package json
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{}
)
@ -34,7 +60,7 @@ func skipObject(buf []byte, cursor, depth int64) (int64, error) {
braceCount++
depth++
if depth > maxDecodeNestingDepth {
return 0, errExceededMaxDepth(buf[cursor], cursor)
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
case '}':
depth--
@ -45,7 +71,7 @@ func skipObject(buf []byte, cursor, depth int64) (int64, error) {
case '[':
depth++
if depth > maxDecodeNestingDepth {
return 0, errExceededMaxDepth(buf[cursor], cursor)
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
case ']':
depth--
@ -56,16 +82,16 @@ func skipObject(buf []byte, cursor, depth int64) (int64, error) {
case '\\':
cursor++
if buf[cursor] == nul {
return 0, errUnexpectedEndOfJSON("string of object", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
case '"':
goto SWITCH_OUT
case nul:
return 0, errUnexpectedEndOfJSON("string of object", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
}
case nul:
return 0, errUnexpectedEndOfJSON("object of object", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("object of object", cursor)
}
SWITCH_OUT:
cursor++
@ -80,7 +106,7 @@ func skipArray(buf []byte, cursor, depth int64) (int64, error) {
bracketCount++
depth++
if depth > maxDecodeNestingDepth {
return 0, errExceededMaxDepth(buf[cursor], cursor)
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
case ']':
bracketCount--
@ -91,7 +117,7 @@ func skipArray(buf []byte, cursor, depth int64) (int64, error) {
case '{':
depth++
if depth > maxDecodeNestingDepth {
return 0, errExceededMaxDepth(buf[cursor], cursor)
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
case '}':
depth--
@ -102,16 +128,16 @@ func skipArray(buf []byte, cursor, depth int64) (int64, error) {
case '\\':
cursor++
if buf[cursor] == nul {
return 0, errUnexpectedEndOfJSON("string of object", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
case '"':
goto SWITCH_OUT
case nul:
return 0, errUnexpectedEndOfJSON("string of object", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
}
case nul:
return 0, errUnexpectedEndOfJSON("array of object", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("array of object", cursor)
}
SWITCH_OUT:
cursor++
@ -135,12 +161,12 @@ func skipValue(buf []byte, cursor, depth int64) (int64, error) {
case '\\':
cursor++
if buf[cursor] == nul {
return 0, errUnexpectedEndOfJSON("string of object", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
case '"':
return cursor + 1, nil
case nul:
return 0, errUnexpectedEndOfJSON("string of object", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
@ -171,58 +197,58 @@ func skipValue(buf []byte, cursor, depth int64) (int64, error) {
cursor += 4
return cursor, nil
default:
return cursor, errUnexpectedEndOfJSON("null", cursor)
return cursor, errors.ErrUnexpectedEndOfJSON("null", cursor)
}
}
}
func validateTrue(buf []byte, cursor int64) error {
if cursor+3 >= int64(len(buf)) {
return errUnexpectedEndOfJSON("true", cursor)
return errors.ErrUnexpectedEndOfJSON("true", cursor)
}
if buf[cursor+1] != 'r' {
return errInvalidCharacter(buf[cursor+1], "true", cursor)
return errors.ErrInvalidCharacter(buf[cursor+1], "true", cursor)
}
if buf[cursor+2] != 'u' {
return errInvalidCharacter(buf[cursor+2], "true", cursor)
return errors.ErrInvalidCharacter(buf[cursor+2], "true", cursor)
}
if buf[cursor+3] != 'e' {
return errInvalidCharacter(buf[cursor+3], "true", cursor)
return errors.ErrInvalidCharacter(buf[cursor+3], "true", cursor)
}
return nil
}
func validateFalse(buf []byte, cursor int64) error {
if cursor+4 >= int64(len(buf)) {
return errUnexpectedEndOfJSON("false", cursor)
return errors.ErrUnexpectedEndOfJSON("false", cursor)
}
if buf[cursor+1] != 'a' {
return errInvalidCharacter(buf[cursor+1], "false", cursor)
return errors.ErrInvalidCharacter(buf[cursor+1], "false", cursor)
}
if buf[cursor+2] != 'l' {
return errInvalidCharacter(buf[cursor+2], "false", cursor)
return errors.ErrInvalidCharacter(buf[cursor+2], "false", cursor)
}
if buf[cursor+3] != 's' {
return errInvalidCharacter(buf[cursor+3], "false", cursor)
return errors.ErrInvalidCharacter(buf[cursor+3], "false", cursor)
}
if buf[cursor+4] != 'e' {
return errInvalidCharacter(buf[cursor+4], "false", cursor)
return errors.ErrInvalidCharacter(buf[cursor+4], "false", cursor)
}
return nil
}
func validateNull(buf []byte, cursor int64) error {
if cursor+3 >= int64(len(buf)) {
return errUnexpectedEndOfJSON("null", cursor)
return errors.ErrUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return errInvalidCharacter(buf[cursor+1], "null", cursor)
return errors.ErrInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return errInvalidCharacter(buf[cursor+2], "null", cursor)
return errors.ErrInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return errInvalidCharacter(buf[cursor+3], "null", cursor)
return errors.ErrInvalidCharacter(buf[cursor+3], "null", cursor)
}
return nil
}

View file

@ -1,8 +1,10 @@
package json
package decoder
import (
"strconv"
"unsafe"
"github.com/goccy/go-json/internal/errors"
)
type floatDecoder struct {
@ -47,7 +49,7 @@ var (
}
)
func floatBytes(s *stream) []byte {
func floatBytes(s *Stream) []byte {
start := s.cursor
for {
s.cursor++
@ -64,7 +66,7 @@ func floatBytes(s *stream) []byte {
return s.buf[start:s.cursor]
}
func (d *floatDecoder) decodeStreamByte(s *stream) ([]byte, error) {
func (d *floatDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
for {
switch s.char() {
case ' ', '\n', '\t', '\r':
@ -87,7 +89,7 @@ func (d *floatDecoder) decodeStreamByte(s *stream) ([]byte, error) {
}
}
ERROR:
return nil, errUnexpectedEndOfJSON("float", s.totalOffset())
return nil, errors.ErrUnexpectedEndOfJSON("float", s.totalOffset())
}
func (d *floatDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
@ -111,12 +113,12 @@ func (d *floatDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, erro
cursor += 4
return nil, cursor, nil
default:
return nil, 0, errUnexpectedEndOfJSON("float", cursor)
return nil, 0, errors.ErrUnexpectedEndOfJSON("float", cursor)
}
}
}
func (d *floatDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
func (d *floatDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
bytes, err := d.decodeStreamByte(s)
if err != nil {
return err
@ -127,13 +129,14 @@ func (d *floatDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
str := *(*string)(unsafe.Pointer(&bytes))
f64, err := strconv.ParseFloat(str, 64)
if err != nil {
return errSyntax(err.Error(), s.totalOffset())
return errors.ErrSyntax(err.Error(), s.totalOffset())
}
d.op(p, f64)
return nil
}
func (d *floatDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
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
@ -143,12 +146,12 @@ func (d *floatDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
}
cursor = c
if !validEndNumberChar[buf[cursor]] {
return 0, errUnexpectedEndOfJSON("float", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("float", cursor)
}
s := *(*string)(unsafe.Pointer(&bytes))
f64, err := strconv.ParseFloat(s, 64)
if err != nil {
return 0, errSyntax(err.Error(), cursor)
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.ErrInvalidBeginningOfValue(s.buf[s.cursor], s.totalOffset())
}
func (d *funcDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
buf := ctx.Buf
cursor = skipWhiteSpace(buf, cursor)
start := cursor
end, err := skipValue(buf, cursor, depth)
if err != nil {
return 0, err
}
src := buf[start:end]
if len(src) > 0 {
switch src[0] {
case '"':
return 0, &errors.UnmarshalTypeError{
Value: "string",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case '[':
return 0, &errors.UnmarshalTypeError{
Value: "array",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case '{':
return 0, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return 0, &errors.UnmarshalTypeError{
Value: "number",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case 'n':
if bytes.Equal(src, nullbytes) {
*(*unsafe.Pointer)(p) = nil
return end, nil
}
case 't':
if err := validateTrue(buf, start); err == nil {
return 0, &errors.UnmarshalTypeError{
Value: "boolean",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
}
case 'f':
if err := validateFalse(buf, start); err == nil {
return 0, &errors.UnmarshalTypeError{
Value: "boolean",
Type: runtime.RType2Type(d.typ),
Offset: start,
}
}
}
}
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
}

View file

@ -1,20 +1,23 @@
package json
package decoder
import (
"fmt"
"reflect"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type intDecoder struct {
typ *rtype
typ *runtime.Type
kind reflect.Kind
op func(unsafe.Pointer, int64)
structName string
fieldName string
}
func newIntDecoder(typ *rtype, structName, fieldName string, op func(unsafe.Pointer, int64)) *intDecoder {
func newIntDecoder(typ *runtime.Type, structName, fieldName string, op func(unsafe.Pointer, int64)) *intDecoder {
return &intDecoder{
typ: typ,
kind: typ.Kind(),
@ -24,10 +27,10 @@ func newIntDecoder(typ *rtype, structName, fieldName string, op func(unsafe.Poin
}
}
func (d *intDecoder) typeError(buf []byte, offset int64) *UnmarshalTypeError {
return &UnmarshalTypeError{
func (d *intDecoder) typeError(buf []byte, offset int64) *errors.UnmarshalTypeError {
return &errors.UnmarshalTypeError{
Value: fmt.Sprintf("number %s", string(buf)),
Type: rtype2type(d.typ),
Type: runtime.RType2Type(d.typ),
Struct: d.structName,
Field: d.fieldName,
Offset: offset,
@ -79,7 +82,11 @@ var (
}
)
func (d *intDecoder) decodeStreamByte(s *stream) ([]byte, error) {
var (
numZeroBuf = []byte{'0'}
)
func (d *intDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
for {
switch s.char() {
case ' ', '\n', '\t', '\r':
@ -106,7 +113,7 @@ func (d *intDecoder) decodeStreamByte(s *stream) ([]byte, error) {
return num, nil
case '0':
s.cursor++
return []byte{'0'}, nil
return numZeroBuf, nil
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
start := s.cursor
for {
@ -138,7 +145,7 @@ func (d *intDecoder) decodeStreamByte(s *stream) ([]byte, error) {
}
}
ERROR:
return nil, errUnexpectedEndOfJSON("number(integer)", s.totalOffset())
return nil, errors.ErrUnexpectedEndOfJSON("number(integer)", s.totalOffset())
}
func (d *intDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
@ -150,7 +157,7 @@ func (d *intDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error)
continue
case '0':
cursor++
return []byte{'0'}, cursor, nil
return numZeroBuf, cursor, nil
case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9':
start := cursor
cursor++
@ -171,7 +178,7 @@ func (d *intDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error)
}
}
func (d *intDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
func (d *intDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
bytes, err := d.decodeStreamByte(s)
if err != nil {
return err
@ -202,8 +209,8 @@ func (d *intDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) erro
return nil
}
func (d *intDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
bytes, c, err := d.decodeByte(buf, cursor)
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
}

View file

@ -1,14 +1,18 @@
package json
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 *rtype
typ *runtime.Type
structName string
fieldName string
sliceDecoder *sliceDecoder
@ -26,7 +30,7 @@ func newEmptyInterfaceDecoder(structName, fieldName string) *interfaceDecoder {
floatDecoder: newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) {
*(*interface{})(p) = v
}),
numberDecoder: newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v Number) {
numberDecoder: newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v json.Number) {
*(*interface{})(p) = v
}),
stringDecoder: newStringDecoder(structName, fieldName),
@ -49,7 +53,7 @@ func newEmptyInterfaceDecoder(structName, fieldName string) *interfaceDecoder {
return ifaceDecoder
}
func newInterfaceDecoder(typ *rtype, structName, fieldName string) *interfaceDecoder {
func newInterfaceDecoder(typ *runtime.Type, structName, fieldName string) *interfaceDecoder {
emptyIfaceDecoder := newEmptyInterfaceDecoder(structName, fieldName)
stringDecoder := newStringDecoder(structName, fieldName)
return &interfaceDecoder{
@ -74,31 +78,31 @@ func newInterfaceDecoder(typ *rtype, structName, fieldName string) *interfaceDec
floatDecoder: newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) {
*(*interface{})(p) = v
}),
numberDecoder: newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v Number) {
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 {
func (d *interfaceDecoder) numDecoder(s *Stream) Decoder {
if s.UseNumber {
return d.numberDecoder
}
return d.floatDecoder
}
var (
emptyInterfaceType = type2rtype(reflect.TypeOf((*interface{})(nil)).Elem())
interfaceMapType = type2rtype(
emptyInterfaceType = runtime.Type2RType(reflect.TypeOf((*interface{})(nil)).Elem())
interfaceMapType = runtime.Type2RType(
reflect.TypeOf((*map[string]interface{})(nil)).Elem(),
)
stringType = type2rtype(
stringType = runtime.Type2RType(
reflect.TypeOf(""),
)
)
func decodeStreamUnmarshaler(s *stream, depth int64, unmarshaler Unmarshaler) error {
func decodeStreamUnmarshaler(s *Stream, depth int64, unmarshaler json.Unmarshaler) error {
start := s.cursor
if err := s.skipValue(depth); err != nil {
return err
@ -113,7 +117,22 @@ func decodeStreamUnmarshaler(s *stream, depth int64, unmarshaler Unmarshaler) er
return nil
}
func decodeUnmarshaler(buf []byte, cursor, depth int64, unmarshaler Unmarshaler) (int64, error) {
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)
@ -130,7 +149,24 @@ func decodeUnmarshaler(buf []byte, cursor, depth int64, unmarshaler Unmarshaler)
return end, nil
}
func decodeStreamTextUnmarshaler(s *stream, depth int64, unmarshaler encoding.TextUnmarshaler, p unsafe.Pointer) error {
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
@ -171,14 +207,14 @@ func decodeTextUnmarshaler(buf []byte, cursor, depth int64, unmarshaler encoding
return end, nil
}
func (d *interfaceDecoder) decodeStreamEmptyInterface(s *stream, depth int64, p unsafe.Pointer) error {
s.skipWhiteSpace()
func (d *interfaceDecoder) decodeStreamEmptyInterface(s *Stream, depth int64, p unsafe.Pointer) error {
c := s.skipWhiteSpace()
for {
switch s.char() {
switch c {
case '{':
var v map[string]interface{}
ptr := unsafe.Pointer(&v)
if err := d.mapDecoder.decodeStream(s, depth, ptr); err != nil {
if err := d.mapDecoder.DecodeStream(s, depth, ptr); err != nil {
return err
}
*(*interface{})(p) = v
@ -186,20 +222,20 @@ func (d *interfaceDecoder) decodeStreamEmptyInterface(s *stream, depth int64, p
case '[':
var v []interface{}
ptr := unsafe.Pointer(&v)
if err := d.sliceDecoder.decodeStream(s, depth, ptr); err != nil {
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)
return d.numDecoder(s).DecodeStream(s, depth, p)
case '"':
s.cursor++
start := s.cursor
for {
switch s.char() {
case '\\':
if err := decodeEscapeString(s); err != nil {
if _, err := decodeEscapeString(s, nil); err != nil {
return err
}
case '"':
@ -211,7 +247,7 @@ func (d *interfaceDecoder) decodeStreamEmptyInterface(s *stream, depth int64, p
if s.read() {
continue
}
return errUnexpectedEndOfJSON("string", s.totalOffset())
return errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
}
s.cursor++
}
@ -235,29 +271,37 @@ func (d *interfaceDecoder) decodeStreamEmptyInterface(s *stream, depth int64, p
return nil
case nul:
if s.read() {
c = s.char()
continue
}
}
break
}
return errNotAtBeginningOfValue(s.totalOffset())
return errors.ErrInvalidBeginningOfValue(c, s.totalOffset())
}
func (d *interfaceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
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().(Unmarshaler); ok {
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)
}
s.skipWhiteSpace()
if s.char() == 'n' {
if s.skipWhiteSpace() == 'n' {
if err := nullBytes(s); err != nil {
return err
}
@ -276,23 +320,22 @@ func (d *interfaceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer
if typ.Kind() == reflect.Ptr && typ.Elem() == d.typ || typ.Kind() != reflect.Ptr {
return d.decodeStreamEmptyInterface(s, depth, p)
}
s.skipWhiteSpace()
if s.char() == 'n' {
if s.skipWhiteSpace() == 'n' {
if err := nullBytes(s); err != nil {
return err
}
*(*interface{})(p) = nil
return nil
}
decoder, err := decodeCompileToGetDecoder(typ)
decoder, err := CompileToGetDecoder(typ)
if err != nil {
return err
}
return decoder.decodeStream(s, depth, ifaceHeader.ptr)
return decoder.DecodeStream(s, depth, ifaceHeader.ptr)
}
func (d *interfaceDecoder) errUnmarshalType(typ reflect.Type, offset int64) *UnmarshalTypeError {
return &UnmarshalTypeError{
func (d *interfaceDecoder) errUnmarshalType(typ reflect.Type, offset int64) *errors.UnmarshalTypeError {
return &errors.UnmarshalTypeError{
Value: typ.String(),
Type: typ,
Offset: offset,
@ -301,14 +344,18 @@ func (d *interfaceDecoder) errUnmarshalType(typ reflect.Type, offset int64) *Unm
}
}
func (d *interfaceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
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().(Unmarshaler); ok {
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 {
@ -331,10 +378,10 @@ func (d *interfaceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Poin
typ := ifaceHeader.typ
if ifaceHeader.ptr == nil || d.typ == typ || typ == nil {
// concrete type is empty interface
return d.decodeEmptyInterface(buf, cursor, depth, p)
return d.decodeEmptyInterface(ctx, cursor, depth, p)
}
if typ.Kind() == reflect.Ptr && typ.Elem() == d.typ || typ.Kind() != reflect.Ptr {
return d.decodeEmptyInterface(buf, cursor, depth, p)
return d.decodeEmptyInterface(ctx, cursor, depth, p)
}
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == 'n' {
@ -345,20 +392,21 @@ func (d *interfaceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Poin
**(**interface{})(unsafe.Pointer(&p)) = nil
return cursor, nil
}
decoder, err := decodeCompileToGetDecoder(typ)
decoder, err := CompileToGetDecoder(typ)
if err != nil {
return 0, err
}
return decoder.decode(buf, cursor, depth, ifaceHeader.ptr)
return decoder.Decode(ctx, cursor, depth, ifaceHeader.ptr)
}
func (d *interfaceDecoder) decodeEmptyInterface(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
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(buf, cursor, depth, ptr)
cursor, err := d.mapDecoder.Decode(ctx, cursor, depth, ptr)
if err != nil {
return 0, err
}
@ -367,18 +415,18 @@ func (d *interfaceDecoder) decodeEmptyInterface(buf []byte, cursor, depth int64,
case '[':
var v []interface{}
ptr := unsafe.Pointer(&v)
cursor, err := d.sliceDecoder.decode(buf, cursor, depth, ptr)
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(buf, cursor, depth, p)
return d.floatDecoder.Decode(ctx, cursor, depth, p)
case '"':
var v string
ptr := unsafe.Pointer(&v)
cursor, err := d.stringDecoder.decode(buf, cursor, depth, ptr)
cursor, err := d.stringDecoder.Decode(ctx, cursor, depth, ptr)
if err != nil {
return 0, err
}
@ -406,5 +454,5 @@ func (d *interfaceDecoder) decodeEmptyInterface(buf []byte, cursor, depth int64,
**(**interface{})(unsafe.Pointer(&p)) = nil
return cursor, nil
}
return cursor, errNotAtBeginningOfValue(cursor)
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
}

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

@ -0,0 +1,186 @@
package decoder
import (
"reflect"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type mapDecoder struct {
mapType *runtime.Type
keyType *runtime.Type
valueType *runtime.Type
canUseAssignFaststrType bool
keyDecoder Decoder
valueDecoder Decoder
structName string
fieldName string
}
func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder {
return &mapDecoder{
mapType: mapType,
keyDecoder: keyDec,
keyType: keyType,
canUseAssignFaststrType: canUseAssignFaststrType(keyType, valueType),
valueType: valueType,
valueDecoder: valueDec,
structName: structName,
fieldName: fieldName,
}
}
const (
mapMaxElemSize = 128
)
// See detail: https://github.com/goccy/go-json/pull/283
func canUseAssignFaststrType(key *runtime.Type, value *runtime.Type) bool {
indirectElem := value.Size() > mapMaxElemSize
if indirectElem {
return false
}
return key.Kind() == reflect.String
}
//go:linkname makemap reflect.makemap
func makemap(*runtime.Type, int) unsafe.Pointer
//nolint:golint
//go:linkname mapassign_faststr runtime.mapassign_faststr
//go:noescape
func mapassign_faststr(t *runtime.Type, m unsafe.Pointer, s string) unsafe.Pointer
//go:linkname mapassign reflect.mapassign
//go:noescape
func mapassign(t *runtime.Type, m unsafe.Pointer, k, v unsafe.Pointer)
func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) {
if d.canUseAssignFaststrType {
mapV := mapassign_faststr(t, m, *(*string)(k))
typedmemmove(d.valueType, mapV, v)
} else {
mapassign(t, m, k, v)
}
}
func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
depth++
if depth > maxDecodeNestingDepth {
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
}
switch s.skipWhiteSpace() {
case 'n':
if err := nullBytes(s); err != nil {
return err
}
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil
return nil
case '{':
default:
return errors.ErrExpected("{ character for map value", s.totalOffset())
}
mapValue := *(*unsafe.Pointer)(p)
if mapValue == nil {
mapValue = makemap(d.mapType, 0)
}
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

@ -1,18 +1,21 @@
package json
package decoder
import (
"encoding/json"
"strconv"
"unsafe"
"github.com/goccy/go-json/internal/errors"
)
type numberDecoder struct {
stringDecoder *stringDecoder
op func(unsafe.Pointer, Number)
op func(unsafe.Pointer, json.Number)
structName string
fieldName string
}
func newNumberDecoder(structName, fieldName string, op func(unsafe.Pointer, Number)) *numberDecoder {
func newNumberDecoder(structName, fieldName string, op func(unsafe.Pointer, json.Number)) *numberDecoder {
return &numberDecoder{
stringDecoder: newStringDecoder(structName, fieldName),
op: op,
@ -21,34 +24,35 @@ func newNumberDecoder(structName, fieldName string, op func(unsafe.Pointer, Numb
}
}
func (d *numberDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
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 errSyntax(err.Error(), s.totalOffset())
return errors.ErrSyntax(err.Error(), s.totalOffset())
}
d.op(p, Number(string(bytes)))
d.op(p, json.Number(string(bytes)))
s.reset()
return nil
}
func (d *numberDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
bytes, c, err := d.decodeByte(buf, cursor)
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, errSyntax(err.Error(), c)
return 0, errors.ErrSyntax(err.Error(), c)
}
cursor = c
s := *(*string)(unsafe.Pointer(&bytes))
d.op(p, Number(s))
d.op(p, json.Number(s))
return cursor, nil
}
func (d *numberDecoder) decodeStreamByte(s *stream) ([]byte, error) {
func (d *numberDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
start := s.cursor
for {
switch s.char() {
case ' ', '\n', '\t', '\r':
@ -73,7 +77,10 @@ func (d *numberDecoder) decodeStreamByte(s *stream) ([]byte, error) {
}
}
ERROR:
return nil, errUnexpectedEndOfJSON("json.Number", s.totalOffset())
if s.cursor == start {
return nil, errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
}
return nil, errors.ErrUnexpectedEndOfJSON("json.Number", s.totalOffset())
}
func (d *numberDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
@ -99,7 +106,7 @@ func (d *numberDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
case '"':
return d.stringDecoder.decodeByte(buf, cursor)
default:
return nil, 0, errUnexpectedEndOfJSON("json.Number", cursor)
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

@ -1,17 +1,19 @@
package json
package decoder
import (
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
type ptrDecoder struct {
dec decoder
typ *rtype
dec Decoder
typ *runtime.Type
structName string
fieldName string
}
func newPtrDecoder(dec decoder, typ *rtype, structName, fieldName string) *ptrDecoder {
func newPtrDecoder(dec Decoder, typ *runtime.Type, structName, fieldName string) *ptrDecoder {
return &ptrDecoder{
dec: dec,
typ: typ,
@ -20,7 +22,7 @@ func newPtrDecoder(dec decoder, typ *rtype, structName, fieldName string) *ptrDe
}
}
func (d *ptrDecoder) contentDecoder() decoder {
func (d *ptrDecoder) contentDecoder() Decoder {
dec, ok := d.dec.(*ptrDecoder)
if !ok {
return d.dec
@ -30,11 +32,10 @@ func (d *ptrDecoder) contentDecoder() decoder {
//nolint:golint
//go:linkname unsafe_New reflect.unsafe_New
func unsafe_New(*rtype) unsafe.Pointer
func unsafe_New(*runtime.Type) unsafe.Pointer
func (d *ptrDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
s.skipWhiteSpace()
if s.char() == nul {
func (d *ptrDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
if s.skipWhiteSpace() == nul {
s.read()
}
if s.char() == 'n' {
@ -51,13 +52,14 @@ func (d *ptrDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) erro
} else {
newptr = *(*unsafe.Pointer)(p)
}
if err := d.dec.decodeStream(s, depth, newptr); err != nil {
if err := d.dec.DecodeStream(s, depth, newptr); err != nil {
return err
}
return nil
}
func (d *ptrDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
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 {
@ -76,7 +78,7 @@ func (d *ptrDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (
} else {
newptr = *(*unsafe.Pointer)(p)
}
c, err := d.dec.decode(buf, cursor, depth, newptr)
c, err := d.dec.Decode(ctx, cursor, depth, newptr)
if err != nil {
return 0, err
}

View file

@ -1,15 +1,25 @@
package json
package decoder
import (
"reflect"
"sync"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
var (
sliceType = runtime.Type2RType(
reflect.TypeOf((*sliceHeader)(nil)).Elem(),
)
nilSlice = unsafe.Pointer(&sliceHeader{})
)
type sliceDecoder struct {
elemType *rtype
elemType *runtime.Type
isElemPointerType bool
valueDecoder decoder
valueDecoder Decoder
size uintptr
arrayPool sync.Pool
structName string
@ -29,7 +39,7 @@ const (
defaultSliceCapacity = 2
)
func newSliceDecoder(dec decoder, elemType *rtype, size uintptr, structName, fieldName string) *sliceDecoder {
func newSliceDecoder(dec Decoder, elemType *runtime.Type, size uintptr, structName, fieldName string) *sliceDecoder {
return &sliceDecoder{
valueDecoder: dec,
elemType: elemType,
@ -71,28 +81,28 @@ func (d *sliceDecoder) releaseSlice(p *sliceHeader) {
}
//go:linkname copySlice reflect.typedslicecopy
func copySlice(elemType *rtype, dst, src sliceHeader) int
func copySlice(elemType *runtime.Type, dst, src sliceHeader) int
//go:linkname newArray reflect.unsafe_NewArray
func newArray(*rtype, int) unsafe.Pointer
func newArray(*runtime.Type, int) unsafe.Pointer
//go:linkname typedmemmove reflect.typedmemmove
func typedmemmove(t *rtype, dst, src unsafe.Pointer)
func typedmemmove(t *runtime.Type, dst, src unsafe.Pointer)
func (d *sliceDecoder) errNumber(offset int64) *UnmarshalTypeError {
return &UnmarshalTypeError{
func (d *sliceDecoder) errNumber(offset int64) *errors.UnmarshalTypeError {
return &errors.UnmarshalTypeError{
Value: "number",
Type: reflect.SliceOf(rtype2type(d.elemType)),
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 {
func (d *sliceDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
depth++
if depth > maxDecodeNestingDepth {
return errExceededMaxDepth(s.char(), s.cursor)
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
}
for {
@ -104,12 +114,11 @@ func (d *sliceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
if err := nullBytes(s); err != nil {
return err
}
*(*unsafe.Pointer)(p) = nil
typedmemmove(sliceType, p, nilSlice)
return nil
case '[':
s.cursor++
s.skipWhiteSpace()
if s.char() == ']' {
if s.skipWhiteSpace() == ']' {
dst := (*sliceHeader)(p)
if dst.data == nil {
dst.data = newArray(d.elemType, 0)
@ -144,7 +153,7 @@ func (d *sliceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
}
}
if err := d.valueDecoder.decodeStream(s, depth, ep); err != nil {
if err := d.valueDecoder.DecodeStream(s, depth, ep); err != nil {
return err
}
s.skipWhiteSpace()
@ -194,13 +203,14 @@ func (d *sliceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
}
}
ERROR:
return errUnexpectedEndOfJSON("slice", s.totalOffset())
return errors.ErrUnexpectedEndOfJSON("slice", s.totalOffset())
}
func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
func (d *sliceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
buf := ctx.Buf
depth++
if depth > maxDecodeNestingDepth {
return 0, errExceededMaxDepth(buf[cursor], cursor)
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
for {
@ -213,7 +223,7 @@ func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
return 0, err
}
cursor += 4
*(*unsafe.Pointer)(p) = nil
typedmemmove(sliceType, p, nilSlice)
return cursor, nil
case '[':
cursor++
@ -251,7 +261,7 @@ func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
typedmemmove(d.elemType, ep, unsafe_New(d.elemType))
}
}
c, err := d.valueDecoder.decode(buf, cursor, depth, ep)
c, err := d.valueDecoder.Decode(ctx, cursor, depth, ep)
if err != nil {
return 0, err
}
@ -278,14 +288,14 @@ func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
slice.cap = capacity
slice.data = data
d.releaseSlice(slice)
return 0, errInvalidCharacter(buf[cursor], "slice", cursor)
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, errUnexpectedEndOfJSON("slice", cursor)
return 0, errors.ErrUnexpectedEndOfJSON("slice", cursor)
}
}
}

View file

@ -1,16 +1,20 @@
package json
package decoder
import (
"bytes"
"encoding/json"
"io"
"strconv"
"unsafe"
"github.com/goccy/go-json/internal/errors"
)
const (
initBufSize = 512
)
type stream struct {
type Stream struct {
buf []byte
bufSize int64
length int64
@ -19,19 +23,25 @@ type stream struct {
cursor int64
filledBuffer bool
allRead bool
useNumber bool
disallowUnknownFields bool
UseNumber bool
DisallowUnknownFields bool
Option *Option
}
func newStream(r io.Reader) *stream {
return &stream{
func NewStream(r io.Reader) *Stream {
return &Stream{
r: r,
bufSize: initBufSize,
buf: []byte{nul},
buf: make([]byte, initBufSize),
Option: &Option{},
}
}
func (s *stream) buffered() io.Reader {
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 {
@ -41,15 +51,35 @@ func (s *stream) buffered() io.Reader {
return bytes.NewReader(s.buf[s.cursor:])
}
func (s *stream) totalOffset() int64 {
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 {
func (s *Stream) char() byte {
return s.buf[s.cursor]
}
func (s *stream) equalChar(c byte) bool {
func (s *Stream) equalChar(c byte) bool {
cur := s.buf[s.cursor]
if cur == nul {
s.read()
@ -58,23 +88,104 @@ func (s *stream) equalChar(c byte) bool {
return cur == c
}
func (s *stream) stat() ([]byte, int64, unsafe.Pointer) {
func (s *Stream) stat() ([]byte, int64, unsafe.Pointer) {
return s.buf, s.cursor, (*sliceHeader)(unsafe.Pointer(&s.buf)).data
}
func (s *stream) statForRetry() ([]byte, int64, unsafe.Pointer) {
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() {
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 {
func (s *Stream) readBuf() []byte {
if s.filledBuffer {
s.bufSize *= 2
remainBuf := s.buf
@ -89,10 +200,11 @@ func (s *stream) readBuf() []byte {
}
remainNotNulCharNum++
}
s.length = s.cursor + remainNotNulCharNum
return s.buf[s.cursor+remainNotNulCharNum:]
}
func (s *stream) read() bool {
func (s *Stream) read() bool {
if s.allRead {
return false
}
@ -100,7 +212,7 @@ func (s *stream) read() bool {
last := len(buf) - 1
buf[last] = nul
n, err := s.r.Read(buf[:last])
s.length = s.cursor + int64(n)
s.length += int64(n)
if n == last {
s.filledBuffer = true
} else {
@ -114,20 +226,24 @@ func (s *stream) read() bool {
return true
}
func (s *stream) skipWhiteSpace() {
func (s *Stream) skipWhiteSpace() byte {
p := s.bufptr()
LOOP:
switch s.char() {
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 {
func (s *Stream) skipObject(depth int64) error {
braceCount := 1
_, cursor, p := s.stat()
for {
@ -136,7 +252,7 @@ func (s *stream) skipObject(depth int64) error {
braceCount++
depth++
if depth > maxDecodeNestingDepth {
return errExceededMaxDepth(s.char(), s.cursor)
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
}
case '}':
braceCount--
@ -148,7 +264,7 @@ func (s *stream) skipObject(depth int64) error {
case '[':
depth++
if depth > maxDecodeNestingDepth {
return errExceededMaxDepth(s.char(), s.cursor)
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
}
case ']':
depth--
@ -164,7 +280,7 @@ func (s *stream) skipObject(depth int64) error {
_, cursor, p = s.statForRetry()
continue
}
return errUnexpectedEndOfJSON("string of object", cursor)
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
case '"':
goto SWITCH_OUT
@ -174,7 +290,7 @@ func (s *stream) skipObject(depth int64) error {
_, cursor, p = s.statForRetry()
continue
}
return errUnexpectedEndOfJSON("string of object", cursor)
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
}
case nul:
@ -183,14 +299,14 @@ func (s *stream) skipObject(depth int64) error {
_, cursor, p = s.stat()
continue
}
return errUnexpectedEndOfJSON("object of object", cursor)
return errors.ErrUnexpectedEndOfJSON("object of object", cursor)
}
SWITCH_OUT:
cursor++
}
}
func (s *stream) skipArray(depth int64) error {
func (s *Stream) skipArray(depth int64) error {
bracketCount := 1
_, cursor, p := s.stat()
for {
@ -199,7 +315,7 @@ func (s *stream) skipArray(depth int64) error {
bracketCount++
depth++
if depth > maxDecodeNestingDepth {
return errExceededMaxDepth(s.char(), s.cursor)
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
}
case ']':
bracketCount--
@ -211,7 +327,7 @@ func (s *stream) skipArray(depth int64) error {
case '{':
depth++
if depth > maxDecodeNestingDepth {
return errExceededMaxDepth(s.char(), s.cursor)
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
}
case '}':
depth--
@ -227,7 +343,7 @@ func (s *stream) skipArray(depth int64) error {
_, cursor, p = s.statForRetry()
continue
}
return errUnexpectedEndOfJSON("string of object", cursor)
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
case '"':
goto SWITCH_OUT
@ -237,7 +353,7 @@ func (s *stream) skipArray(depth int64) error {
_, cursor, p = s.statForRetry()
continue
}
return errUnexpectedEndOfJSON("string of object", cursor)
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
}
}
case nul:
@ -246,14 +362,14 @@ func (s *stream) skipArray(depth int64) error {
_, cursor, p = s.stat()
continue
}
return errUnexpectedEndOfJSON("array of object", cursor)
return errors.ErrUnexpectedEndOfJSON("array of object", cursor)
}
SWITCH_OUT:
cursor++
}
}
func (s *stream) skipValue(depth int64) error {
func (s *Stream) skipValue(depth int64) error {
_, cursor, p := s.stat()
for {
switch char(p, cursor) {
@ -266,7 +382,7 @@ func (s *stream) skipValue(depth int64) error {
_, cursor, p = s.stat()
continue
}
return errUnexpectedEndOfJSON("value of object", s.totalOffset())
return errors.ErrUnexpectedEndOfJSON("value of object", s.totalOffset())
case '{':
s.cursor = cursor + 1
return s.skipObject(depth + 1)
@ -285,7 +401,7 @@ func (s *stream) skipValue(depth int64) error {
_, cursor, p = s.statForRetry()
continue
}
return errUnexpectedEndOfJSON("value of string", s.totalOffset())
return errors.ErrUnexpectedEndOfJSON("value of string", s.totalOffset())
}
case '"':
s.cursor = cursor + 1
@ -296,7 +412,7 @@ func (s *stream) skipValue(depth int64) error {
_, cursor, p = s.statForRetry()
continue
}
return errUnexpectedEndOfJSON("value of string", s.totalOffset())
return errors.ErrUnexpectedEndOfJSON("value of string", s.totalOffset())
}
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
@ -338,7 +454,7 @@ func (s *stream) skipValue(depth int64) error {
}
}
func nullBytes(s *stream) error {
func nullBytes(s *Stream) error {
// current cursor's character is 'n'
s.cursor++
if s.char() != 'u' {
@ -362,14 +478,14 @@ func nullBytes(s *stream) error {
return nil
}
func retryReadNull(s *stream) error {
func retryReadNull(s *Stream) error {
if s.char() == nul && s.read() {
return nil
}
return errInvalidCharacter(s.char(), "null", s.totalOffset())
return errors.ErrInvalidCharacter(s.char(), "null", s.totalOffset())
}
func trueBytes(s *stream) error {
func trueBytes(s *Stream) error {
// current cursor's character is 't'
s.cursor++
if s.char() != 'r' {
@ -393,14 +509,14 @@ func trueBytes(s *stream) error {
return nil
}
func retryReadTrue(s *stream) error {
func retryReadTrue(s *Stream) error {
if s.char() == nul && s.read() {
return nil
}
return errInvalidCharacter(s.char(), "bool(true)", s.totalOffset())
return errors.ErrInvalidCharacter(s.char(), "bool(true)", s.totalOffset())
}
func falseBytes(s *stream) error {
func falseBytes(s *Stream) error {
// current cursor's character is 'f'
s.cursor++
if s.char() != 'a' {
@ -430,9 +546,9 @@ func falseBytes(s *stream) error {
return nil
}
func retryReadFalse(s *stream) error {
func retryReadFalse(s *Stream) error {
if s.char() == nul && s.read() {
return nil
}
return errInvalidCharacter(s.char(), "bool(false)", s.totalOffset())
return errors.ErrInvalidCharacter(s.char(), "bool(false)", s.totalOffset())
}

View file

@ -1,4 +1,4 @@
package json
package decoder
import (
"reflect"
@ -6,6 +6,8 @@ import (
"unicode/utf16"
"unicode/utf8"
"unsafe"
"github.com/goccy/go-json/internal/errors"
)
type stringDecoder struct {
@ -20,8 +22,8 @@ func newStringDecoder(structName, fieldName string) *stringDecoder {
}
}
func (d *stringDecoder) errUnmarshalType(typeName string, offset int64) *UnmarshalTypeError {
return &UnmarshalTypeError{
func (d *stringDecoder) errUnmarshalType(typeName string, offset int64) *errors.UnmarshalTypeError {
return &errors.UnmarshalTypeError{
Value: typeName,
Type: reflect.TypeOf(""),
Offset: offset,
@ -30,7 +32,7 @@ func (d *stringDecoder) errUnmarshalType(typeName string, offset int64) *Unmarsh
}
}
func (d *stringDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
func (d *stringDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
bytes, err := d.decodeStreamByte(s)
if err != nil {
return err
@ -43,8 +45,8 @@ func (d *stringDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) e
return nil
}
func (d *stringDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
bytes, c, err := d.decodeByte(buf, cursor)
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
}
@ -91,38 +93,40 @@ func unicodeToRune(code []byte) rune {
return r
}
func decodeUnicodeRune(s *stream) (rune, int64, error) {
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, errInvalidCharacter(s.char(), "escaped string", s.totalOffset())
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, nil
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, nil
return r, surrogateOffset, p, nil
}
}
return r, defaultOffset, nil
return r, defaultOffset, p, nil
}
func decodeUnicode(s *stream) error {
func decodeUnicode(s *Stream, p unsafe.Pointer) (unsafe.Pointer, error) {
const backSlashAndULen = 2 // length of \u
r, offset, err := decodeUnicodeRune(s)
r, offset, pp, err := decodeUnicodeRune(s, p)
if err != nil {
return err
return nil, err
}
unicode := []byte(string(r))
unicodeLen := int64(len(unicode))
@ -130,10 +134,10 @@ func decodeUnicode(s *stream) error {
unicodeOrgLen := offset - 1
s.length = s.length - (backSlashAndULen + (unicodeOrgLen - unicodeLen))
s.cursor = s.cursor - backSlashAndULen + unicodeLen
return nil
return pp, nil
}
func decodeEscapeString(s *stream) error {
func decodeEscapeString(s *Stream, p unsafe.Pointer) (unsafe.Pointer, error) {
s.cursor++
RETRY:
switch s.buf[s.cursor] {
@ -154,19 +158,20 @@ RETRY:
case 't':
s.buf[s.cursor] = '\t'
case 'u':
return decodeUnicode(s)
return decodeUnicode(s, p)
case nul:
if !s.read() {
return errInvalidCharacter(s.char(), "escaped string", s.totalOffset())
return nil, errors.ErrInvalidCharacter(s.char(), "escaped string", s.totalOffset())
}
goto RETRY
default:
return errUnexpectedEndOfJSON("string", s.totalOffset())
return nil, errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
}
s.buf = append(s.buf[:s.cursor-1], s.buf[s.cursor:]...)
s.length--
s.cursor--
return nil
p = s.bufptr()
return p, nil
}
var (
@ -174,7 +179,7 @@ var (
runeErrBytesLen = int64(len(runeErrBytes))
)
func stringBytes(s *stream) ([]byte, error) {
func stringBytes(s *Stream) ([]byte, error) {
_, cursor, p := s.stat()
cursor++ // skip double quote char
start := cursor
@ -182,9 +187,11 @@ func stringBytes(s *stream) ([]byte, error) {
switch char(p, cursor) {
case '\\':
s.cursor = cursor
if err := decodeEscapeString(s); err != nil {
pp, err := decodeEscapeString(s, p)
if err != nil {
return nil, err
}
p = pp
cursor = s.cursor
case '"':
literal := s.buf[start:cursor]
@ -232,23 +239,32 @@ func stringBytes(s *stream) ([]byte, error) {
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()
if !utf8.FullRune(s.buf[cursor : len(s.buf)-1]) {
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
continue
}
goto ERROR
}
r, size := utf8.DecodeRune(s.buf[cursor:])
if r == utf8.RuneError {
s.buf = append(append(append([]byte{}, s.buf[:cursor]...), runeErrBytes...), s.buf[cursor+1:]...)
cursor += runeErrBytesLen
s.length += runeErrBytesLen
_, _, p = s.stat()
} else {
cursor += int64(size)
}
cursor += int64(len(b))
s.length += int64(len(b))
continue
}
cursor++
}
ERROR:
return nil, errUnexpectedEndOfJSON("string", s.totalOffset())
return nil, errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
}
func (d *stringDecoder) decodeStreamByte(s *stream) ([]byte, error) {
func (d *stringDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
for {
switch s.char() {
case ' ', '\n', '\t', '\r':
@ -274,7 +290,7 @@ func (d *stringDecoder) decodeStreamByte(s *stream) ([]byte, error) {
}
break
}
return nil, errNotAtBeginningOfValue(s.totalOffset())
return nil, errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
}
func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
@ -324,13 +340,13 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
case 'u':
buflen := int64(len(buf))
if cursor+5 >= buflen {
return nil, 0, errUnexpectedEndOfJSON("escaped string", cursor)
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, errUnexpectedEndOfJSON("escaped string", cursor)
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
}
continue
case '"':
@ -338,7 +354,7 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
cursor++
return literal, cursor, nil
case nul:
return nil, 0, errUnexpectedEndOfJSON("string", cursor)
return nil, 0, errors.ErrUnexpectedEndOfJSON("string", cursor)
}
cursor++
}
@ -349,7 +365,7 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
cursor += 4
return nil, cursor, nil
default:
return nil, 0, errNotAtBeginningOfValue(cursor)
return nil, 0, errors.ErrInvalidBeginningOfValue(buf[cursor], 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.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
}
}
}
func decodeKeyByBitmapUint16(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
var (
curBit uint16 = math.MaxUint16
)
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
for {
switch char(b, cursor) {
case ' ', '\n', '\t', '\r':
cursor++
case '"':
cursor++
c := char(b, cursor)
switch c {
case '"':
cursor++
return cursor, nil, nil
case nul:
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
}
keyIdx := 0
bitmap := d.keyBitmapUint16
start := cursor
for {
c := char(b, cursor)
switch c {
case '"':
fieldSetIndex := bits.TrailingZeros16(curBit)
field := d.sortedFieldSets[fieldSetIndex]
keyLen := cursor - start
cursor++
if keyLen < field.keyLen {
// early match
return cursor, nil, nil
}
return cursor, field, nil
case nul:
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
case '\\':
cursor++
chars, nextCursor := 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.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
}
}
}
func decodeKeyNotFound(b unsafe.Pointer, cursor int64) (int64, *structFieldSet, error) {
for {
cursor++
switch char(b, cursor) {
case '"':
cursor++
return cursor, nil, nil
case '\\':
cursor++
if char(b, cursor) == nul {
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
}
case nul:
return 0, nil, errors.ErrUnexpectedEndOfJSON("string", cursor)
}
}
}
func decodeKey(d *structDecoder, buf []byte, cursor int64) (int64, *structFieldSet, error) {
key, c, err := d.stringDecoder.decodeByte(buf, cursor)
if err != nil {
return 0, nil, err
}
cursor = c
k := *(*string)(unsafe.Pointer(&key))
field, exists := d.fieldMap[k]
if !exists {
return cursor, nil, nil
}
return cursor, field, nil
}
func decodeKeyByBitmapUint8Stream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
var (
curBit uint8 = math.MaxUint8
)
_, cursor, p := s.stat()
for {
switch char(p, cursor) {
case ' ', '\n', '\t', '\r':
cursor++
case nul:
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
continue
}
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
case '"':
cursor++
FIRST_CHAR:
start := cursor
switch char(p, cursor) {
case '"':
cursor++
s.cursor = cursor
return nil, "", nil
case nul:
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
goto FIRST_CHAR
}
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
}
keyIdx := 0
bitmap := d.keyBitmapUint8
for {
c := char(p, cursor)
switch c {
case '"':
fieldSetIndex := bits.TrailingZeros8(curBit)
field := d.sortedFieldSets[fieldSetIndex]
keyLen := cursor - start
cursor++
s.cursor = cursor
if keyLen < field.keyLen {
// early match
return nil, field.key, nil
}
return field, field.key, nil
case nul:
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
continue
}
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
case '\\':
s.cursor = cursor + 1 // skip '\' char
chars, err := decodeKeyCharByEscapeCharStream(s)
if err != nil {
return nil, "", err
}
cursor = s.cursor
for _, c := range chars {
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 {
s.cursor = cursor
return decodeKeyNotFoundStream(s, start)
}
keyIdx++
}
default:
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 {
s.cursor = cursor
return decodeKeyNotFoundStream(s, start)
}
keyIdx++
}
cursor++
}
default:
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
}
}
}
func decodeKeyByBitmapUint16Stream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
var (
curBit uint16 = math.MaxUint16
)
_, cursor, p := s.stat()
for {
switch char(p, cursor) {
case ' ', '\n', '\t', '\r':
cursor++
case nul:
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
continue
}
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
case '"':
cursor++
FIRST_CHAR:
start := cursor
switch char(p, cursor) {
case '"':
cursor++
s.cursor = cursor
return nil, "", nil
case nul:
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
goto FIRST_CHAR
}
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
}
keyIdx := 0
bitmap := d.keyBitmapUint16
for {
c := char(p, cursor)
switch c {
case '"':
fieldSetIndex := bits.TrailingZeros16(curBit)
field := d.sortedFieldSets[fieldSetIndex]
keyLen := cursor - start
cursor++
s.cursor = cursor
if keyLen < field.keyLen {
// early match
return nil, field.key, nil
}
return field, field.key, nil
case nul:
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
continue
}
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
case '\\':
s.cursor = cursor + 1 // skip '\' char
chars, err := decodeKeyCharByEscapeCharStream(s)
if err != nil {
return nil, "", err
}
cursor = s.cursor
for _, c := range chars {
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 {
s.cursor = cursor
return decodeKeyNotFoundStream(s, start)
}
keyIdx++
}
default:
curBit &= bitmap[keyIdx][largeToSmallTable[c]]
if curBit == 0 {
s.cursor = cursor
return decodeKeyNotFoundStream(s, start)
}
keyIdx++
}
cursor++
}
default:
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
}
}
}
// decode from '\uXXXX'
func decodeKeyCharByUnicodeRuneStream(s *Stream) ([]byte, error) {
const defaultOffset = 4
const surrogateOffset = 6
if s.cursor+defaultOffset >= s.length {
if !s.read() {
return nil, errors.ErrInvalidCharacter(s.char(), "escaped unicode char", s.totalOffset())
}
}
r := unicodeToRune(s.buf[s.cursor : s.cursor+defaultOffset])
if utf16.IsSurrogate(r) {
s.cursor += defaultOffset
if s.cursor+surrogateOffset >= s.length {
s.read()
}
if s.cursor+surrogateOffset >= s.length || s.buf[s.cursor] != '\\' || s.buf[s.cursor+1] != 'u' {
s.cursor += defaultOffset - 1
return []byte(string(unicode.ReplacementChar)), nil
}
r2 := unicodeToRune(s.buf[s.cursor+defaultOffset+2 : s.cursor+surrogateOffset])
if r := utf16.DecodeRune(r, r2); r != unicode.ReplacementChar {
s.cursor += defaultOffset - 1
return []byte(string(r)), nil
}
}
s.cursor += defaultOffset - 1
return []byte(string(r)), nil
}
func decodeKeyCharByEscapeCharStream(s *Stream) ([]byte, error) {
c := s.buf[s.cursor]
s.cursor++
RETRY:
switch c {
case '"':
return []byte{'"'}, nil
case '\\':
return []byte{'\\'}, nil
case '/':
return []byte{'/'}, nil
case 'b':
return []byte{'\b'}, nil
case 'f':
return []byte{'\f'}, nil
case 'n':
return []byte{'\n'}, nil
case 'r':
return []byte{'\r'}, nil
case 't':
return []byte{'\t'}, nil
case 'u':
return decodeKeyCharByUnicodeRuneStream(s)
case nul:
if !s.read() {
return nil, errors.ErrInvalidCharacter(s.char(), "escaped char", s.totalOffset())
}
goto RETRY
default:
return nil, errors.ErrUnexpectedEndOfJSON("struct field", s.totalOffset())
}
}
func decodeKeyNotFoundStream(s *Stream, start int64) (*structFieldSet, string, error) {
buf, cursor, p := s.stat()
for {
cursor++
switch char(p, cursor) {
case '"':
b := buf[start:cursor]
key := *(*string)(unsafe.Pointer(&b))
cursor++
s.cursor = cursor
return nil, key, nil
case '\\':
cursor++
if char(p, cursor) == nul {
s.cursor = cursor
if !s.read() {
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
}
buf, cursor, p = s.statForRetry()
}
case nul:
s.cursor = cursor
if !s.read() {
return nil, "", errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
}
buf, cursor, p = s.statForRetry()
}
}
}
func decodeKeyStream(d *structDecoder, s *Stream) (*structFieldSet, string, error) {
key, err := d.stringDecoder.decodeStreamByte(s)
if err != nil {
return nil, "", err
}
k := *(*string)(unsafe.Pointer(&key))
return d.fieldMap[k], k, nil
}
func (d *structDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
depth++
if depth > maxDecodeNestingDepth {
return errors.ErrExceededMaxDepth(s.char(), s.cursor)
}
c := s.skipWhiteSpace()
switch c {
case 'n':
if err := nullBytes(s); err != nil {
return err
}
return nil
default:
if s.char() != '{' {
return errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
}
}
s.cursor++
if s.skipWhiteSpace() == '}' {
s.cursor++
return nil
}
var (
seenFields map[int]struct{}
seenFieldNum int
)
firstWin := (s.Option.Flags & FirstWinOption) != 0
if firstWin {
seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
}
for {
s.reset()
field, key, err := d.keyStreamDecoder(d, s)
if err != nil {
return err
}
if s.skipWhiteSpace() != ':' {
return errors.ErrExpected("colon after object key", s.totalOffset())
}
s.cursor++
if field != nil {
if field.err != nil {
return field.err
}
if firstWin {
if _, exists := seenFields[field.fieldIdx]; exists {
if err := s.skipValue(depth); err != nil {
return err
}
} else {
if err := field.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
return err
}
seenFieldNum++
if d.fieldUniqueNameNum <= seenFieldNum {
return s.skipObject(depth)
}
seenFields[field.fieldIdx] = struct{}{}
}
} else {
if err := field.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
return err
}
}
} else if s.DisallowUnknownFields {
return fmt.Errorf("json: unknown field %q", key)
} else {
if err := s.skipValue(depth); err != nil {
return err
}
}
c := s.skipWhiteSpace()
if c == '}' {
s.cursor++
return nil
}
if c != ',' {
return errors.ErrExpected("comma after object element", s.totalOffset())
}
s.cursor++
}
}
func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
buf := ctx.Buf
depth++
if depth > maxDecodeNestingDepth {
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
buflen := int64(len(buf))
cursor = skipWhiteSpace(buf, cursor)
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
switch char(b, cursor) {
case 'n':
if err := validateNull(buf, cursor); err != nil {
return 0, err
}
cursor += 4
return cursor, nil
case '{':
default:
return 0, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
}
cursor++
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == '}' {
cursor++
return cursor, nil
}
var (
seenFields map[int]struct{}
seenFieldNum int
)
firstWin := (ctx.Option.Flags & FirstWinOption) != 0
if firstWin {
seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
}
for {
c, field, err := d.keyDecoder(d, buf, cursor)
if err != nil {
return 0, err
}
cursor = skipWhiteSpace(buf, c)
if char(b, cursor) != ':' {
return 0, errors.ErrExpected("colon after object key", cursor)
}
cursor++
if cursor >= buflen {
return 0, errors.ErrExpected("object value after colon", cursor)
}
if field != nil {
if field.err != nil {
return 0, field.err
}
if firstWin {
if _, exists := seenFields[field.fieldIdx]; exists {
c, err := skipValue(buf, cursor, depth)
if err != nil {
return 0, err
}
cursor = c
} else {
c, err := field.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset))
if err != nil {
return 0, err
}
cursor = c
seenFieldNum++
if d.fieldUniqueNameNum <= seenFieldNum {
return skipObject(buf, cursor, depth)
}
seenFields[field.fieldIdx] = struct{}{}
}
} else {
c, err := field.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+field.offset))
if err != nil {
return 0, err
}
cursor = c
}
} else {
c, err := skipValue(buf, cursor, depth)
if err != nil {
return 0, err
}
cursor = c
}
cursor = skipWhiteSpace(buf, cursor)
if char(b, cursor) == '}' {
cursor++
return cursor, nil
}
if char(b, cursor) != ',' {
return 0, errors.ErrExpected("comma after object element", cursor)
}
cursor++
}
}

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

@ -1,20 +1,23 @@
package json
package decoder
import (
"fmt"
"reflect"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type uintDecoder struct {
typ *rtype
typ *runtime.Type
kind reflect.Kind
op func(unsafe.Pointer, uint64)
structName string
fieldName string
}
func newUintDecoder(typ *rtype, structName, fieldName string, op func(unsafe.Pointer, uint64)) *uintDecoder {
func newUintDecoder(typ *runtime.Type, structName, fieldName string, op func(unsafe.Pointer, uint64)) *uintDecoder {
return &uintDecoder{
typ: typ,
kind: typ.Kind(),
@ -24,10 +27,10 @@ func newUintDecoder(typ *rtype, structName, fieldName string, op func(unsafe.Poi
}
}
func (d *uintDecoder) typeError(buf []byte, offset int64) *UnmarshalTypeError {
return &UnmarshalTypeError{
func (d *uintDecoder) typeError(buf []byte, offset int64) *errors.UnmarshalTypeError {
return &errors.UnmarshalTypeError{
Value: fmt.Sprintf("number %s", string(buf)),
Type: rtype2type(d.typ),
Type: runtime.RType2Type(d.typ),
Offset: offset,
}
}
@ -54,7 +57,7 @@ func (d *uintDecoder) parseUint(b []byte) (uint64, error) {
return sum, nil
}
func (d *uintDecoder) decodeStreamByte(s *stream) ([]byte, error) {
func (d *uintDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
for {
switch s.char() {
case ' ', '\n', '\t', '\r':
@ -62,7 +65,7 @@ func (d *uintDecoder) decodeStreamByte(s *stream) ([]byte, error) {
continue
case '0':
s.cursor++
return []byte{'0'}, nil
return numZeroBuf, nil
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
start := s.cursor
for {
@ -93,7 +96,7 @@ func (d *uintDecoder) decodeStreamByte(s *stream) ([]byte, error) {
}
break
}
return nil, errUnexpectedEndOfJSON("number(unsigned integer)", s.totalOffset())
return nil, errors.ErrUnexpectedEndOfJSON("number(unsigned integer)", s.totalOffset())
}
func (d *uintDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
@ -104,7 +107,7 @@ func (d *uintDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error
continue
case '0':
cursor++
return []byte{'0'}, cursor, nil
return numZeroBuf, cursor, nil
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
start := cursor
cursor++
@ -125,7 +128,7 @@ func (d *uintDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error
}
}
func (d *uintDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
func (d *uintDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
bytes, err := d.decodeStreamByte(s)
if err != nil {
return err
@ -155,8 +158,8 @@ func (d *uintDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) err
return nil
}
func (d *uintDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
bytes, c, err := d.decodeByte(buf, cursor)
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
}

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

@ -1,4 +1,4 @@
package json
package decoder
import (
"bytes"
@ -7,15 +7,18 @@ import (
"unicode/utf16"
"unicode/utf8"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type unmarshalTextDecoder struct {
typ *rtype
typ *runtime.Type
structName string
fieldName string
}
func newUnmarshalTextDecoder(typ *rtype, structName, fieldName string) *unmarshalTextDecoder {
func newUnmarshalTextDecoder(typ *runtime.Type, structName, fieldName string) *unmarshalTextDecoder {
return &unmarshalTextDecoder{
typ: typ,
structName: structName,
@ -25,10 +28,10 @@ func newUnmarshalTextDecoder(typ *rtype, structName, fieldName string) *unmarsha
func (d *unmarshalTextDecoder) annotateError(cursor int64, err error) {
switch e := err.(type) {
case *UnmarshalTypeError:
case *errors.UnmarshalTypeError:
e.Struct = d.structName
e.Field = d.fieldName
case *SyntaxError:
case *errors.SyntaxError:
e.Offset = cursor
}
}
@ -37,7 +40,7 @@ var (
nullbytes = []byte(`null`)
)
func (d *unmarshalTextDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
func (d *unmarshalTextDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
s.skipWhiteSpace()
start := s.cursor
if err := s.skipValue(depth); err != nil {
@ -47,21 +50,21 @@ func (d *unmarshalTextDecoder) decodeStream(s *stream, depth int64, p unsafe.Poi
if len(src) > 0 {
switch src[0] {
case '[':
return &UnmarshalTypeError{
return &errors.UnmarshalTypeError{
Value: "array",
Type: rtype2type(d.typ),
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
case '{':
return &UnmarshalTypeError{
return &errors.UnmarshalTypeError{
Value: "object",
Type: rtype2type(d.typ),
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return &UnmarshalTypeError{
return &errors.UnmarshalTypeError{
Value: "number",
Type: rtype2type(d.typ),
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
}
case 'n':
@ -88,7 +91,8 @@ func (d *unmarshalTextDecoder) decodeStream(s *stream, depth int64, p unsafe.Poi
return nil
}
func (d *unmarshalTextDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
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)
@ -99,21 +103,21 @@ func (d *unmarshalTextDecoder) decode(buf []byte, cursor, depth int64, p unsafe.
if len(src) > 0 {
switch src[0] {
case '[':
return 0, &UnmarshalTypeError{
return 0, &errors.UnmarshalTypeError{
Value: "array",
Type: rtype2type(d.typ),
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case '{':
return 0, &UnmarshalTypeError{
return 0, &errors.UnmarshalTypeError{
Value: "object",
Type: rtype2type(d.typ),
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return 0, &UnmarshalTypeError{
return 0, &errors.UnmarshalTypeError{
Value: "number",
Type: rtype2type(d.typ),
Type: runtime.RType2Type(d.typ),
Offset: start,
}
case 'n':

View file

@ -1,20 +1,22 @@
package json
package decoder
import (
"reflect"
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
type wrappedStringDecoder struct {
typ *rtype
dec decoder
typ *runtime.Type
dec Decoder
stringDecoder *stringDecoder
structName string
fieldName string
isPtrType bool
}
func newWrappedStringDecoder(typ *rtype, dec decoder, structName, fieldName string) *wrappedStringDecoder {
func newWrappedStringDecoder(typ *runtime.Type, dec Decoder, structName, fieldName string) *wrappedStringDecoder {
return &wrappedStringDecoder{
typ: typ,
dec: dec,
@ -25,7 +27,7 @@ func newWrappedStringDecoder(typ *rtype, dec decoder, structName, fieldName stri
}
}
func (d *wrappedStringDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {
func (d *wrappedStringDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
bytes, err := d.stringDecoder.decodeStreamByte(s)
if err != nil {
return err
@ -38,14 +40,14 @@ func (d *wrappedStringDecoder) decodeStream(s *stream, depth int64, p unsafe.Poi
}
b := make([]byte, len(bytes)+1)
copy(b, bytes)
if _, err := d.dec.decode(b, 0, depth, p); err != nil {
if _, err := d.dec.Decode(&RuntimeContext{Buf: b}, 0, depth, p); err != nil {
return err
}
return nil
}
func (d *wrappedStringDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
bytes, c, err := d.stringDecoder.decodeByte(buf, cursor)
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
}
@ -56,8 +58,11 @@ func (d *wrappedStringDecoder) decode(buf []byte, cursor, depth int64, p unsafe.
return c, nil
}
bytes = append(bytes, nul)
if _, err := d.dec.decode(bytes, 0, depth, p); err != nil {
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

@ -1,6 +1,7 @@
package encoder
import (
"context"
"encoding"
"encoding/json"
"fmt"
@ -13,13 +14,18 @@ import (
"github.com/goccy/go-json/internal/runtime"
)
type marshalerContext interface {
MarshalJSON(context.Context) ([]byte, error)
}
var (
marshalJSONType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
jsonNumberType = reflect.TypeOf(json.Number(""))
cachedOpcodeSets []*OpcodeSet
cachedOpcodeMap unsafe.Pointer // map[uintptr]*OpcodeSet
typeAddr *runtime.TypeAddr
marshalJSONType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
marshalJSONContextType = reflect.TypeOf((*marshalerContext)(nil)).Elem()
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
jsonNumberType = reflect.TypeOf(json.Number(""))
cachedOpcodeSets []*OpcodeSet
cachedOpcodeMap unsafe.Pointer // map[uintptr]*OpcodeSet
typeAddr *runtime.TypeAddr
)
func init() {
@ -55,19 +61,36 @@ func compileToGetCodeSetSlowPath(typeptr uintptr) (*OpcodeSet, error) {
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
code, err := compileHead(&compileContext{
noescapeKeyCode, err := compileHead(&compileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
})
if err != nil {
return nil, err
}
code = copyOpcode(code)
codeLength := code.TotalLength()
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,
Code: code,
CodeLength: codeLength,
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
}
storeOpcodeSet(typeptr, codeSet, opcodeMap)
return codeSet, nil
@ -100,7 +123,7 @@ func compileHead(ctx *compileContext) (*Opcode, error) {
elem := typ.Elem()
if elem.Kind() == reflect.Uint8 {
p := runtime.PtrTo(elem)
if !p.Implements(marshalJSONType) && !p.Implements(marshalTextType) {
if !implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
if isPtr {
return compileBytesPtr(ctx)
}
@ -246,6 +269,7 @@ func linkRecursiveCode(c *Opcode) {
continue
}
code.Jmp.Code = copyOpcode(code.Jmp.Code)
c := code.Jmp.Code
c.End.Next = newEndOp(&compileContext{})
c.Op = c.Op.PtrHeadToHead()
@ -258,8 +282,8 @@ func linkRecursiveCode(c *Opcode) {
lastCode.Length = lastCode.Idx + 2*uintptrSize
// extend length to alloc slot for elemIdx + length
totalLength := uintptr(code.TotalLength() + 2)
nextTotalLength := uintptr(c.TotalLength() + 2)
totalLength := uintptr(code.TotalLength() + 3)
nextTotalLength := uintptr(c.TotalLength() + 3)
c.End.Next.Op = OpRecursiveEnd
@ -268,6 +292,7 @@ func linkRecursiveCode(c *Opcode) {
code.Jmp.Linked = true
linkRecursiveCode(code.Jmp.Code)
code = code.Next
continue
}
@ -328,14 +353,14 @@ func optimizeStructEnd(c *Opcode) {
}
func implementsMarshalJSON(typ *runtime.Type) bool {
if !typ.Implements(marshalJSONType) {
if !implementsMarshalJSONType(typ) {
return false
}
if typ.Kind() != reflect.Ptr {
return true
}
// type kind is reflect.Ptr
if !typ.Elem().Implements(marshalJSONType) {
if !implementsMarshalJSONType(typ.Elem()) {
return true
}
// needs to dereference
@ -372,7 +397,7 @@ func compile(ctx *compileContext, isPtr bool) (*Opcode, error) {
elem := typ.Elem()
if elem.Kind() == reflect.Uint8 {
p := runtime.PtrTo(elem)
if !p.Implements(marshalJSONType) && !p.Implements(marshalTextType) {
if !implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
return compileBytes(ctx)
}
}
@ -474,8 +499,6 @@ func compileKey(ctx *compileContext) (*Opcode, error) {
switch typ.Kind() {
case reflect.Ptr:
return compilePtr(ctx)
case reflect.Interface:
return compileInterface(ctx)
case reflect.String:
return compileString(ctx)
case reflect.Int:
@ -517,10 +540,17 @@ func compilePtr(ctx *compileContext) (*Opcode, error) {
func compileMarshalJSON(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpMarshalJSON)
typ := ctx.typ
if !typ.Implements(marshalJSONType) && runtime.PtrTo(typ).Implements(marshalJSONType) {
code.AddrForMarshaler = true
if isPtrMarshalJSONType(typ) {
code.Flags |= AddrForMarshalerFlags
}
if typ.Implements(marshalJSONContextType) || runtime.PtrTo(typ).Implements(marshalJSONContextType) {
code.Flags |= MarshalerContextFlags
}
if isNilableType(typ) {
code.Flags |= IsNilableTypeFlags
} else {
code.Flags &= ^IsNilableTypeFlags
}
code.IsNilableType = isNilableType(typ)
ctx.incIndex()
return code, nil
}
@ -529,9 +559,13 @@ func compileMarshalText(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpMarshalText)
typ := ctx.typ
if !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType) {
code.AddrForMarshaler = true
code.Flags |= AddrForMarshalerFlags
}
if isNilableType(typ) {
code.Flags |= IsNilableTypeFlags
} else {
code.Flags &= ^IsNilableTypeFlags
}
code.IsNilableType = isNilableType(typ)
ctx.incIndex()
return code, nil
}
@ -540,7 +574,7 @@ const intSize = 32 << (^uint(0) >> 63)
func compileInt(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpInt)
code.setMaskAndRshiftNum(intSize)
code.NumBitSize = intSize
ctx.incIndex()
return code, nil
}
@ -556,7 +590,7 @@ func compileIntPtr(ctx *compileContext) (*Opcode, error) {
func compileInt8(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpInt)
code.setMaskAndRshiftNum(8)
code.NumBitSize = 8
ctx.incIndex()
return code, nil
}
@ -572,7 +606,7 @@ func compileInt8Ptr(ctx *compileContext) (*Opcode, error) {
func compileInt16(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpInt)
code.setMaskAndRshiftNum(16)
code.NumBitSize = 16
ctx.incIndex()
return code, nil
}
@ -588,7 +622,7 @@ func compileInt16Ptr(ctx *compileContext) (*Opcode, error) {
func compileInt32(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpInt)
code.setMaskAndRshiftNum(32)
code.NumBitSize = 32
ctx.incIndex()
return code, nil
}
@ -604,7 +638,7 @@ func compileInt32Ptr(ctx *compileContext) (*Opcode, error) {
func compileInt64(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpInt)
code.setMaskAndRshiftNum(64)
code.NumBitSize = 64
ctx.incIndex()
return code, nil
}
@ -620,7 +654,7 @@ func compileInt64Ptr(ctx *compileContext) (*Opcode, error) {
func compileUint(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUint)
code.setMaskAndRshiftNum(intSize)
code.NumBitSize = intSize
ctx.incIndex()
return code, nil
}
@ -636,7 +670,7 @@ func compileUintPtr(ctx *compileContext) (*Opcode, error) {
func compileUint8(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUint)
code.setMaskAndRshiftNum(8)
code.NumBitSize = 8
ctx.incIndex()
return code, nil
}
@ -652,7 +686,7 @@ func compileUint8Ptr(ctx *compileContext) (*Opcode, error) {
func compileUint16(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUint)
code.setMaskAndRshiftNum(16)
code.NumBitSize = 16
ctx.incIndex()
return code, nil
}
@ -668,7 +702,7 @@ func compileUint16Ptr(ctx *compileContext) (*Opcode, error) {
func compileUint32(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUint)
code.setMaskAndRshiftNum(32)
code.NumBitSize = 32
ctx.incIndex()
return code, nil
}
@ -684,7 +718,7 @@ func compileUint32Ptr(ctx *compileContext) (*Opcode, error) {
func compileUint64(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUint)
code.setMaskAndRshiftNum(64)
code.NumBitSize = 64
ctx.incIndex()
return code, nil
}
@ -700,70 +734,70 @@ func compileUint64Ptr(ctx *compileContext) (*Opcode, error) {
func compileIntString(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpIntString)
code.setMaskAndRshiftNum(intSize)
code.NumBitSize = intSize
ctx.incIndex()
return code, nil
}
func compileInt8String(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpIntString)
code.setMaskAndRshiftNum(8)
code.NumBitSize = 8
ctx.incIndex()
return code, nil
}
func compileInt16String(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpIntString)
code.setMaskAndRshiftNum(16)
code.NumBitSize = 16
ctx.incIndex()
return code, nil
}
func compileInt32String(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpIntString)
code.setMaskAndRshiftNum(32)
code.NumBitSize = 32
ctx.incIndex()
return code, nil
}
func compileInt64String(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpIntString)
code.setMaskAndRshiftNum(64)
code.NumBitSize = 64
ctx.incIndex()
return code, nil
}
func compileUintString(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUintString)
code.setMaskAndRshiftNum(intSize)
code.NumBitSize = intSize
ctx.incIndex()
return code, nil
}
func compileUint8String(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUintString)
code.setMaskAndRshiftNum(8)
code.NumBitSize = 8
ctx.incIndex()
return code, nil
}
func compileUint16String(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUintString)
code.setMaskAndRshiftNum(16)
code.NumBitSize = 16
ctx.incIndex()
return code, nil
}
func compileUint32String(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUintString)
code.setMaskAndRshiftNum(32)
code.NumBitSize = 32
ctx.incIndex()
return code, nil
}
func compileUint64String(ctx *compileContext) (*Opcode, error) {
code := newOpCode(ctx, OpUintString)
code.setMaskAndRshiftNum(64)
code.NumBitSize = 64
ctx.incIndex()
return code, nil
}
@ -879,7 +913,7 @@ func compileSlice(ctx *compileContext) (*Opcode, error) {
if err != nil {
return nil, err
}
code.Indirect = true
code.Flags |= IndirectFlags
// header => opcode => elem => end
// ^ |
@ -891,7 +925,6 @@ func compileSlice(ctx *compileContext) (*Opcode, error) {
end := newOpCode(ctx, OpSliceEnd)
ctx.incIndex()
header.Elem = elemCode
header.End = end
header.Next = code
code.BeforeLastCode().Next = (*Opcode)(unsafe.Pointer(elemCode))
@ -903,7 +936,7 @@ func compileSlice(ctx *compileContext) (*Opcode, error) {
func compileListElem(ctx *compileContext) (*Opcode, error) {
typ := ctx.typ
switch {
case !typ.Implements(marshalJSONType) && runtime.PtrTo(typ).Implements(marshalJSONType):
case isPtrMarshalJSONType(typ):
return compileMarshalJSON(ctx)
case !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType):
return compileMarshalText(ctx)
@ -934,7 +967,7 @@ func compileArray(ctx *compileContext) (*Opcode, error) {
if err != nil {
return nil, err
}
code.Indirect = true
code.Flags |= IndirectFlags
// header => opcode => elem => end
// ^ |
// |________|
@ -945,7 +978,6 @@ func compileArray(ctx *compileContext) (*Opcode, error) {
end := newOpCode(ctx, OpArrayEnd)
ctx.incIndex()
header.Elem = elemCode
header.End = end
header.Next = code
code.BeforeLastCode().Next = (*Opcode)(unsafe.Pointer(elemCode))
@ -976,16 +1008,13 @@ func compileMap(ctx *compileContext) (*Opcode, error) {
if err != nil {
return nil, err
}
valueCode.Indirect = true
valueCode.Flags |= IndirectFlags
key := newMapKeyCode(ctx, header)
ctx.incIndex()
ctx = ctx.decIndent()
header.MapKey = key
header.MapValue = value
end := newMapEndCode(ctx, header)
ctx.incIndex()
@ -1052,8 +1081,7 @@ func compiledCode(ctx *compileContext) *Opcode {
func structHeader(ctx *compileContext, fieldCode *Opcode, valueCode *Opcode, tag *runtime.StructTag) *Opcode {
op := optimizeStructHeader(valueCode, tag)
fieldCode.Op = op
fieldCode.Mask = valueCode.Mask
fieldCode.RshiftNum = valueCode.RshiftNum
fieldCode.NumBitSize = valueCode.NumBitSize
fieldCode.PtrNum = valueCode.PtrNum
if op.IsMultipleOpHead() {
return valueCode.BeforeLastCode()
@ -1065,9 +1093,8 @@ func structHeader(ctx *compileContext, fieldCode *Opcode, valueCode *Opcode, tag
func structField(ctx *compileContext, fieldCode *Opcode, valueCode *Opcode, tag *runtime.StructTag) *Opcode {
op := optimizeStructField(valueCode, tag)
fieldCode.Op = op
fieldCode.NumBitSize = valueCode.NumBitSize
fieldCode.PtrNum = valueCode.PtrNum
fieldCode.Mask = valueCode.Mask
fieldCode.RshiftNum = valueCode.RshiftNum
if op.IsMultipleOpField() {
return valueCode.BeforeLastCode()
}
@ -1082,7 +1109,7 @@ func isNotExistsField(head *Opcode) bool {
if head.Op != OpStructHead {
return false
}
if !head.AnonymousHead {
if (head.Flags & AnonymousHeadFlags) == 0 {
return false
}
if head.Next == nil {
@ -1117,7 +1144,7 @@ func optimizeAnonymousFields(head *Opcode) {
if isNotExistsField(code.Next) {
code.Next = code.NextField
diff := code.Next.DisplayIdx - code.DisplayIdx
for i := 0; i < diff; i++ {
for i := uint32(0); i < diff; i++ {
code.Next.decOpcodeIndex()
}
linkPrevToNextField(code, removedFields)
@ -1147,20 +1174,20 @@ func anonymousStructFieldPairMap(tags runtime.StructTags, named string, valueCod
isHeadOp := strings.Contains(f.Op.String(), "Head")
if existsKey && f.Next != nil && strings.Contains(f.Next.Op.String(), "Recursive") {
// through
} else if isHeadOp && !f.AnonymousHead {
} else if isHeadOp && (f.Flags&AnonymousHeadFlags) == 0 {
if existsKey {
// TODO: need to remove this head
f.Op = OpStructHead
f.AnonymousKey = true
f.AnonymousHead = true
f.Flags |= AnonymousKeyFlags
f.Flags |= AnonymousHeadFlags
} else if named == "" {
f.AnonymousHead = true
f.Flags |= AnonymousHeadFlags
}
} else if named == "" && f.Op == OpStructEnd {
f.Op = OpStructAnonymousEnd
} else if existsKey {
diff := f.NextField.DisplayIdx - f.DisplayIdx
for i := 0; i < diff; i++ {
for i := uint32(0); i < diff; i++ {
f.NextField.decOpcodeIndex()
}
linkPrevToNextField(f, removedFields)
@ -1179,7 +1206,7 @@ func anonymousStructFieldPairMap(tags runtime.StructTags, named string, valueCod
anonymousFields[key] = append(anonymousFields[key], structFieldPair{
prevField: prevAnonymousField,
curField: f,
isTaggedKey: f.IsTaggedKey,
isTaggedKey: (f.Flags & IsTaggedKeyFlags) != 0,
})
if f.Next != nil && f.NextField != f.Next && f.Next.Op.CodeType() == CodeStructField {
for k, v := range anonymousFieldPairRecursively(named, f.Next) {
@ -1200,12 +1227,12 @@ func anonymousFieldPairRecursively(named string, valueCode *Opcode) map[string][
f := valueCode
var prevAnonymousField *Opcode
for {
if f.DisplayKey != "" && f.AnonymousHead {
if f.DisplayKey != "" && (f.Flags&AnonymousHeadFlags) != 0 {
key := fmt.Sprintf("%s.%s", named, f.DisplayKey)
anonymousFields[key] = append(anonymousFields[key], structFieldPair{
prevField: prevAnonymousField,
curField: f,
isTaggedKey: f.IsTaggedKey,
isTaggedKey: (f.Flags & IsTaggedKeyFlags) != 0,
})
if f.Next != nil && f.NextField != f.Next && f.Next.Op.CodeType() == CodeStructField {
for k, v := range anonymousFieldPairRecursively(named, f.Next) {
@ -1238,11 +1265,11 @@ func optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPai
if fieldPair.prevField == nil {
// head operation
fieldPair.curField.Op = OpStructHead
fieldPair.curField.AnonymousHead = true
fieldPair.curField.AnonymousKey = true
fieldPair.curField.Flags |= AnonymousHeadFlags
fieldPair.curField.Flags |= AnonymousKeyFlags
} else {
diff := fieldPair.curField.NextField.DisplayIdx - fieldPair.curField.DisplayIdx
for i := 0; i < diff; i++ {
for i := uint32(0); i < diff; i++ {
fieldPair.curField.NextField.decOpcodeIndex()
}
removedFields[fieldPair.curField] = struct{}{}
@ -1258,12 +1285,12 @@ func optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPai
if fieldPair.prevField == nil {
// head operation
fieldPair.curField.Op = OpStructHead
fieldPair.curField.AnonymousHead = true
fieldPair.curField.AnonymousKey = true
fieldPair.curField.Flags |= AnonymousHeadFlags
fieldPair.curField.Flags |= AnonymousKeyFlags
} else {
diff := fieldPair.curField.NextField.DisplayIdx - fieldPair.curField.DisplayIdx
removedFields[fieldPair.curField] = struct{}{}
for i := 0; i < diff; i++ {
for i := uint32(0); i < diff; i++ {
fieldPair.curField.NextField.decOpcodeIndex()
}
linkPrevToNextField(fieldPair.curField, removedFields)
@ -1273,7 +1300,7 @@ func optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPai
}
} else {
for _, fieldPair := range taggedPairs {
fieldPair.curField.IsTaggedKey = false
fieldPair.curField.Flags &= ^IsTaggedKeyFlags
}
}
}
@ -1390,7 +1417,7 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
valueCode = code
}
if field.Anonymous {
if field.Anonymous && !tag.IsTaggedKey {
tagKey := ""
if tag.IsTaggedKey {
tagKey = tag.Key
@ -1398,50 +1425,76 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
for k, v := range anonymousStructFieldPairMap(tags, tagKey, valueCode) {
anonymousFields[k] = append(anonymousFields[k], v...)
}
valueCode.decIndent()
// fix issue144
if !(isPtr && strings.Contains(valueCode.Op.String(), "Marshal")) {
valueCode.Indirect = indirect
if indirect {
valueCode.Flags |= IndirectFlags
} else {
valueCode.Flags &= ^IndirectFlags
}
}
} else {
if indirect {
// if parent is indirect type, set child indirect property to true
valueCode.Indirect = indirect
valueCode.Flags |= IndirectFlags
} else {
// if parent is not indirect type and child have only one field, set child indirect property to false
if i == 0 && valueCode.NextField != nil && valueCode.NextField.Op == OpStructEnd {
valueCode.Indirect = indirect
// if parent is not indirect type, set child indirect property to false.
// but if parent's indirect is false and isPtr is true, then indirect must be true.
// Do this only if indirectConversion is enabled at the end of compileStruct.
if i == 0 {
valueCode.Flags &= ^IndirectFlags
}
}
}
key := fmt.Sprintf(`"%s":`, tag.Key)
escapedKey := fmt.Sprintf(`%s:`, string(AppendEscapedString([]byte{}, tag.Key)))
var flags OpFlags
if indirect {
flags |= IndirectFlags
}
if field.Anonymous {
flags |= AnonymousKeyFlags
}
if tag.IsTaggedKey {
flags |= IsTaggedKeyFlags
}
if nilcheck {
flags |= NilCheckFlags
}
if addrForMarshaler {
flags |= AddrForMarshalerFlags
}
if strings.Contains(valueCode.Op.String(), "Ptr") || valueCode.Op == OpInterface {
flags |= IsNextOpPtrTypeFlags
}
if isNilableType {
flags |= IsNilableTypeFlags
}
var key string
if ctx.escapeKey {
rctx := &RuntimeContext{Option: &Option{Flag: HTMLEscapeOption}}
key = fmt.Sprintf(`%s:`, string(AppendString(rctx, []byte{}, tag.Key)))
} else {
key = fmt.Sprintf(`"%s":`, tag.Key)
}
fieldCode := &Opcode{
Type: valueCode.Type,
DisplayIdx: fieldOpcodeIndex,
Idx: opcodeOffset(fieldPtrIndex),
Next: valueCode,
Indent: ctx.indent,
AnonymousKey: field.Anonymous,
Key: []byte(key),
EscapedKey: []byte(escapedKey),
IsTaggedKey: tag.IsTaggedKey,
DisplayKey: tag.Key,
Offset: field.Offset,
Indirect: indirect,
Nilcheck: nilcheck,
AddrForMarshaler: addrForMarshaler,
IsNextOpPtrType: strings.Contains(valueCode.Op.String(), "Ptr") || valueCode.Op == OpInterface,
IsNilableType: isNilableType,
Idx: opcodeOffset(fieldPtrIndex),
Next: valueCode,
Flags: flags,
Key: key,
Offset: uint32(field.Offset),
Type: valueCode.Type,
DisplayIdx: fieldOpcodeIndex,
Indent: ctx.indent,
DisplayKey: tag.Key,
}
if fieldIdx == 0 {
fieldCode.HeadIdx = fieldCode.Idx
code = structHeader(ctx, fieldCode, valueCode, tag)
head = fieldCode
prevField = fieldCode
} else {
fieldCode.HeadIdx = head.HeadIdx
fieldCode.Idx = head.Idx
code.Next = fieldCode
code = structField(ctx, fieldCode, valueCode, tag)
prevField.NextField = fieldCode
@ -1455,7 +1508,6 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
Op: OpStructEnd,
Type: nil,
Indent: ctx.indent,
Next: newEndOp(ctx),
}
ctx = ctx.decIndent()
@ -1464,12 +1516,11 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
if head == nil {
head = &Opcode{
Op: OpStructHead,
Idx: opcodeOffset(ctx.ptrIndex),
NextField: structEndCode,
Type: typ,
DisplayIdx: ctx.opcodeIndex,
Idx: opcodeOffset(ctx.ptrIndex),
HeadIdx: opcodeOffset(ctx.ptrIndex),
Indent: ctx.indent,
NextField: structEndCode,
}
structEndCode.PrevField = head
ctx.incIndex()
@ -1479,6 +1530,7 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
structEndCode.DisplayIdx = ctx.opcodeIndex
structEndCode.Idx = opcodeOffset(ctx.ptrIndex)
ctx.incIndex()
structEndCode.Next = newEndOp(ctx)
if prevField != nil && prevField.NextField == nil {
prevField.NextField = structEndCode
@ -1494,15 +1546,23 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
delete(ctx.structTypeToCompiledCode, typeptr)
if !disableIndirectConversion && !head.Indirect && isPtr {
head.Indirect = true
if !disableIndirectConversion && (head.Flags&IndirectFlags == 0) && isPtr {
headCode := head
for strings.Contains(headCode.Op.String(), "Head") {
headCode.Flags |= IndirectFlags
headCode = headCode.Next
}
}
return ret, nil
}
func implementsMarshalJSONType(typ *runtime.Type) bool {
return typ.Implements(marshalJSONType) || typ.Implements(marshalJSONContextType)
}
func isPtrMarshalJSONType(typ *runtime.Type) bool {
return !typ.Implements(marshalJSONType) && runtime.PtrTo(typ).Implements(marshalJSONType)
return !implementsMarshalJSONType(typ) && implementsMarshalJSONType(runtime.PtrTo(typ))
}
func isPtrMarshalTextType(typ *runtime.Type) bool {

View file

@ -20,19 +20,36 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
code, err := compileHead(&compileContext{
noescapeKeyCode, err := compileHead(&compileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
})
if err != nil {
return nil, err
}
code = copyOpcode(code)
codeLength := code.TotalLength()
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,
Code: code,
CodeLength: codeLength,
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
}
cachedOpcodeSets[index] = codeSet
return codeSet, nil

View file

@ -26,19 +26,37 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
code, err := compileHead(&compileContext{
noescapeKeyCode, err := compileHead(&compileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
})
if err != nil {
return nil, err
}
code = copyOpcode(code)
codeLength := code.TotalLength()
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,
Code: code,
CodeLength: codeLength,
Type: copiedType,
NoescapeKeyCode: noescapeKeyCode,
EscapeKeyCode: escapeKeyCode,
InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
}
setsMu.Lock()
cachedOpcodeSets[index] = codeSet

View file

@ -1,6 +1,7 @@
package encoder
import (
"context"
"sync"
"unsafe"
@ -9,9 +10,10 @@ import (
type compileContext struct {
typ *runtime.Type
opcodeIndex int
opcodeIndex uint32
ptrIndex int
indent int
indent uint32
escapeKey bool
structTypeToCompiledCode map[uintptr]*CompiledCode
parent *compileContext
@ -23,6 +25,7 @@ func (c *compileContext) context() *compileContext {
opcodeIndex: c.opcodeIndex,
ptrIndex: c.ptrIndex,
indent: c.indent,
escapeKey: c.escapeKey,
structTypeToCompiledCode: c.structTypeToCompiledCode,
parent: c,
}
@ -95,20 +98,23 @@ var (
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 int
BaseIndent uint32
Prefix []byte
IndentStr []byte
Option *Option
}
func (c *RuntimeContext) Init(p uintptr, codelen int) {

View file

@ -17,14 +17,6 @@ import (
"github.com/goccy/go-json/internal/runtime"
)
type Option int
const (
HTMLEscapeOption Option = 1 << iota
IndentOption
UnorderedMapOption
)
func (t OpType) IsMultipleOpHead() bool {
switch t {
case OpStructHead:
@ -102,9 +94,13 @@ func (t OpType) IsMultipleOpField() bool {
}
type OpcodeSet struct {
Type *runtime.Type
Code *Opcode
CodeLength int
Type *runtime.Type
NoescapeKeyCode *Opcode
EscapeKeyCode *Opcode
InterfaceNoescapeKeyCode *Opcode
InterfaceEscapeKeyCode *Opcode
CodeLength int
EndCode *Opcode
}
type CompiledCode struct {
@ -276,7 +272,7 @@ func MapIterNext(it unsafe.Pointer)
//go:noescape
func MapLen(m unsafe.Pointer) int
func AppendByteSlice(b []byte, src []byte) []byte {
func AppendByteSlice(_ *RuntimeContext, b []byte, src []byte) []byte {
if src == nil {
return append(b, `null`...)
}
@ -294,7 +290,7 @@ func AppendByteSlice(b []byte, src []byte) []byte {
return append(append(b, buf...), '"')
}
func AppendFloat32(b []byte, v float32) []byte {
func AppendFloat32(_ *RuntimeContext, b []byte, v float32) []byte {
f64 := float64(v)
abs := math.Abs(f64)
fmt := byte('f')
@ -308,7 +304,7 @@ func AppendFloat32(b []byte, v float32) []byte {
return strconv.AppendFloat(b, f64, fmt, -1, 32)
}
func AppendFloat64(b []byte, v float64) []byte {
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.
@ -320,7 +316,7 @@ func AppendFloat64(b []byte, v float64) []byte {
return strconv.AppendFloat(b, v, fmt, -1, 64)
}
func AppendBool(b []byte, v bool) []byte {
func AppendBool(_ *RuntimeContext, b []byte, v bool) []byte {
if v {
return append(b, "true"...)
}
@ -347,7 +343,7 @@ var (
}
)
func AppendNumber(b []byte, n json.Number) ([]byte, error) {
func AppendNumber(_ *RuntimeContext, b []byte, n json.Number) ([]byte, error) {
if len(n) == 0 {
return append(b, '0'), nil
}
@ -360,9 +356,9 @@ func AppendNumber(b []byte, n json.Number) ([]byte, error) {
return b, nil
}
func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.AddrForMarshaler {
if (code.Flags & AddrForMarshalerFlags) != 0 {
if rv.CanAddr() {
rv = rv.Addr()
} else {
@ -372,17 +368,31 @@ func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{
}
}
v = rv.Interface()
marshaler, ok := v.(json.Marshaler)
if !ok {
return AppendNull(b), nil
}
bb, err := marshaler.MarshalJSON()
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
var 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, escape)
compactedBuf, err := compact(b, marshalBuf, (ctx.Option.Flag&HTMLEscapeOption) != 0)
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
@ -390,9 +400,9 @@ func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{
return compactedBuf, nil
}
func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.AddrForMarshaler {
if (code.Flags & AddrForMarshalerFlags) != 0 {
if rv.CanAddr() {
rv = rv.Addr()
} else {
@ -402,22 +412,36 @@ func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v inte
}
}
v = rv.Interface()
marshaler, ok := v.(json.Marshaler)
if !ok {
return AppendNull(b), nil
}
bb, err := marshaler.MarshalJSON()
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
var 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), ctx.BaseIndent+code.Indent),
string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), int(ctx.BaseIndent+code.Indent)),
string(ctx.IndentStr),
escape,
(ctx.Option.Flag&HTMLEscapeOption) != 0,
)
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
@ -426,9 +450,9 @@ func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v inte
return indentedBuf, nil
}
func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
func AppendMarshalText(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.AddrForMarshaler {
if (code.Flags & AddrForMarshalerFlags) != 0 {
if rv.CanAddr() {
rv = rv.Addr()
} else {
@ -440,21 +464,18 @@ func AppendMarshalText(code *Opcode, b []byte, v interface{}, escape bool) ([]by
v = rv.Interface()
marshaler, ok := v.(encoding.TextMarshaler)
if !ok {
return AppendNull(b), nil
return AppendNull(ctx, b), nil
}
bytes, err := marshaler.MarshalText()
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
if escape {
return AppendEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
}
return AppendString(b, *(*string)(unsafe.Pointer(&bytes))), nil
return AppendString(ctx, b, *(*string)(unsafe.Pointer(&bytes))), nil
}
func AppendMarshalTextIndent(code *Opcode, b []byte, v interface{}, escape bool) ([]byte, error) {
func AppendMarshalTextIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
rv := reflect.ValueOf(v) // convert by dynamic interface type
if code.AddrForMarshaler {
if (code.Flags & AddrForMarshalerFlags) != 0 {
if rv.CanAddr() {
rv = rv.Addr()
} else {
@ -466,31 +487,28 @@ func AppendMarshalTextIndent(code *Opcode, b []byte, v interface{}, escape bool)
v = rv.Interface()
marshaler, ok := v.(encoding.TextMarshaler)
if !ok {
return AppendNull(b), nil
return AppendNull(ctx, b), nil
}
bytes, err := marshaler.MarshalText()
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
if escape {
return AppendEscapedString(b, *(*string)(unsafe.Pointer(&bytes))), nil
}
return AppendString(b, *(*string)(unsafe.Pointer(&bytes))), nil
return AppendString(ctx, b, *(*string)(unsafe.Pointer(&bytes))), nil
}
func AppendNull(b []byte) []byte {
func AppendNull(_ *RuntimeContext, b []byte) []byte {
return append(b, "null"...)
}
func AppendComma(b []byte) []byte {
func AppendComma(_ *RuntimeContext, b []byte) []byte {
return append(b, ',')
}
func AppendCommaIndent(b []byte) []byte {
func AppendCommaIndent(_ *RuntimeContext, b []byte) []byte {
return append(b, ',', '\n')
}
func AppendStructEnd(b []byte) []byte {
func AppendStructEnd(_ *RuntimeContext, b []byte) []byte {
return append(b, '}', ',')
}
@ -498,16 +516,16 @@ 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 := 0; i < indentNum; i++ {
for i := uint32(0); i < indentNum; i++ {
b = append(b, ctx.IndentStr...)
}
return append(b, '}', ',', '\n')
}
func AppendIndent(ctx *RuntimeContext, b []byte, indent int) []byte {
func AppendIndent(ctx *RuntimeContext, b []byte, indent uint32) []byte {
b = append(b, ctx.Prefix...)
indentNum := ctx.BaseIndent + indent
for i := 0; i < indentNum; i++ {
for i := uint32(0); i < indentNum; i++ {
b = append(b, ctx.IndentStr...)
}
return b

View file

@ -49,9 +49,14 @@ var intBELookup = [100]uint16{
var intLookup = [2]*[100]uint16{&intLELookup, &intBELookup}
func AppendInt(out []byte, u64 uint64, code *Opcode) []byte {
n := u64 & code.Mask
negative := (u64>>code.RshiftNum)&1 == 1
func 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'))
@ -60,7 +65,7 @@ func AppendInt(out []byte, u64 uint64, code *Opcode) []byte {
return append(out, byte(u), byte(u>>8))
}
} else {
n = -n & code.Mask
n = -n & mask
}
lookup := intLookup[endianness]
@ -91,8 +96,9 @@ func AppendInt(out []byte, u64 uint64, code *Opcode) []byte {
return append(out, b[i:]...)
}
func AppendUint(out []byte, u64 uint64, code *Opcode) []byte {
n := u64 & code.Mask
func AppendUint(_ *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 {

View file

@ -2,7 +2,6 @@ package encoder
import (
"fmt"
"math"
"strings"
"unsafe"
@ -11,61 +10,61 @@ import (
const uintptrSize = 4 << (^uintptr(0) >> 63)
type OpFlags uint16
const (
AnonymousHeadFlags OpFlags = 1 << 0
AnonymousKeyFlags OpFlags = 1 << 1
IndirectFlags OpFlags = 1 << 2
IsTaggedKeyFlags OpFlags = 1 << 3
NilCheckFlags OpFlags = 1 << 4
AddrForMarshalerFlags OpFlags = 1 << 5
IsNextOpPtrTypeFlags OpFlags = 1 << 6
IsNilableTypeFlags OpFlags = 1 << 7
MarshalerContextFlags OpFlags = 1 << 8
NonEmptyInterfaceFlags OpFlags = 1 << 9
)
type Opcode struct {
Op OpType // operation type
Type *runtime.Type // go type
DisplayIdx int // opcode index
Key []byte // struct field key
EscapedKey []byte // struct field key ( HTML escaped )
PtrNum int // pointer number: e.g. double pointer is 2.
DisplayKey string // key text to display
IsTaggedKey bool // whether tagged key
AnonymousKey bool // whether anonymous key
AnonymousHead bool // whether anonymous head or not
Indirect bool // whether indirect or not
Nilcheck bool // whether needs to nilcheck or not
AddrForMarshaler bool // whether needs to addr for marshaler or not
IsNextOpPtrType bool // whether next operation is ptr type or not
IsNilableType bool // whether type is nilable or not
RshiftNum uint8 // use to take bit for judging whether negative integer or not
Mask uint64 // mask for number
Indent int // indent number
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
Idx uintptr // offset to access ptr
HeadIdx uintptr // offset to access slice/struct head
ElemIdx uintptr // offset to access array/slice/map elem
Length uintptr // offset to access slice/map length or array length
MapIter uintptr // offset to access map iterator
MapPos uintptr // offset to access position list for sorted map
Offset uintptr // offset size from struct header
Size uintptr // array/slice elem size
MapKey *Opcode // map key
MapValue *Opcode // map value
Elem *Opcode // array/slice elem
End *Opcode // array/slice/struct/map end
PrevField *Opcode // prev struct field
NextField *Opcode // next struct field
Next *Opcode // next opcode
Jmp *CompiledCode // for recursive call
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 rshitNum(bitSize uint8) uint8 {
return bitSize - 1
}
func (c *Opcode) setMaskAndRshiftNum(bitSize uint8) {
switch bitSize {
case 8:
c.Mask = math.MaxUint8
case 16:
c.Mask = math.MaxUint16
case 32:
c.Mask = math.MaxUint32
case 64:
c.Mask = math.MaxUint64
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
}
}
c.RshiftNum = rshitNum(bitSize)
return max
}
func (c *Opcode) ToHeaderType(isString bool) OpType {
@ -278,8 +277,8 @@ func newOpCode(ctx *compileContext, op OpType) *Opcode {
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
}
func opcodeOffset(idx int) uintptr {
return uintptr(idx) * uintptrSize
func opcodeOffset(idx int) uint32 {
return uint32(idx) * uintptrSize
}
func copyOpcode(code *Opcode) *Opcode {
@ -287,14 +286,53 @@ func copyOpcode(code *Opcode) *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,
Idx: opcodeOffset(ctx.ptrIndex),
Next: next,
}
}
@ -311,37 +349,24 @@ func (c *Opcode) copy(codeMap map[uintptr]*Opcode) *Opcode {
return code
}
copied := &Opcode{
Op: c.Op,
Type: c.Type,
DisplayIdx: c.DisplayIdx,
Key: c.Key,
EscapedKey: c.EscapedKey,
DisplayKey: c.DisplayKey,
PtrNum: c.PtrNum,
Mask: c.Mask,
RshiftNum: c.RshiftNum,
IsTaggedKey: c.IsTaggedKey,
AnonymousKey: c.AnonymousKey,
AnonymousHead: c.AnonymousHead,
Indirect: c.Indirect,
Nilcheck: c.Nilcheck,
AddrForMarshaler: c.AddrForMarshaler,
IsNextOpPtrType: c.IsNextOpPtrType,
IsNilableType: c.IsNilableType,
Indent: c.Indent,
Idx: c.Idx,
HeadIdx: c.HeadIdx,
ElemIdx: c.ElemIdx,
Length: c.Length,
MapIter: c.MapIter,
MapPos: c.MapPos,
Offset: c.Offset,
Size: c.Size,
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.MapKey = c.MapKey.copy(codeMap)
copied.MapValue = c.MapValue.copy(codeMap)
copied.Elem = c.Elem.copy(codeMap)
copied.End = c.End.copy(codeMap)
copied.PrevField = c.PrevField.copy(codeMap)
copied.NextField = c.NextField.copy(codeMap)
@ -369,8 +394,12 @@ func (c *Opcode) BeforeLastCode() *Opcode {
func (c *Opcode) TotalLength() int {
var idx int
for code := c; code.Op != OpEnd; {
idx = int(code.Idx / uintptrSize)
code := c
for code.Op != OpEnd && code.Op != OpInterfaceEnd {
maxIdx := int(code.MaxIdx() / uintptrSize)
if idx < maxIdx {
idx = maxIdx
}
if code.Op == OpRecursiveEnd {
break
}
@ -381,15 +410,18 @@ func (c *Opcode) TotalLength() int {
code = code.Next
}
}
return idx + 2 // opEnd + 1
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--
code.Idx -= uintptrSize
if code.HeadIdx > 0 {
code.HeadIdx -= uintptrSize
if code.Idx > 0 {
code.Idx -= uintptrSize
}
if code.ElemIdx > 0 {
code.ElemIdx -= uintptrSize
@ -422,19 +454,18 @@ func (c *Opcode) decIndent() {
}
func (c *Opcode) dumpHead(code *Opcode) string {
var length uintptr
var length uint32
if code.Op.CodeType() == CodeArrayHead {
length = code.Length
} else {
length = code.Length / uintptrSize
}
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d])`,
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
code.DisplayIdx,
strings.Repeat("-", code.Indent),
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.HeadIdx/uintptrSize,
code.ElemIdx/uintptrSize,
length,
)
@ -442,12 +473,11 @@ func (c *Opcode) dumpHead(code *Opcode) string {
func (c *Opcode) dumpMapHead(code *Opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", code.Indent),
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.HeadIdx/uintptrSize,
code.ElemIdx/uintptrSize,
code.Length/uintptrSize,
code.MapIter/uintptrSize,
@ -458,7 +488,7 @@ func (c *Opcode) dumpMapEnd(code *Opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
code.DisplayIdx,
strings.Repeat("-", code.Indent),
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.MapPos/uintptrSize,
@ -467,19 +497,18 @@ func (c *Opcode) dumpMapEnd(code *Opcode) string {
}
func (c *Opcode) dumpElem(code *Opcode) string {
var length uintptr
var length uint32
if code.Op.CodeType() == CodeArrayElem {
length = code.Length
} else {
length = code.Length / uintptrSize
}
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][headIdx:%d][elemIdx:%d][length:%d][size:%d])`,
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
code.DisplayIdx,
strings.Repeat("-", code.Indent),
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.HeadIdx/uintptrSize,
code.ElemIdx/uintptrSize,
length,
code.Size,
@ -488,14 +517,13 @@ func (c *Opcode) dumpElem(code *Opcode) string {
func (c *Opcode) dumpField(code *Opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][key:%s][offset:%d][headIdx:%d])`,
`[%d]%s%s ([idx:%d][key:%s][offset:%d])`,
code.DisplayIdx,
strings.Repeat("-", code.Indent),
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.DisplayKey,
code.Offset,
code.HeadIdx/uintptrSize,
)
}
@ -503,7 +531,7 @@ func (c *Opcode) dumpKey(code *Opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", code.Indent),
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.ElemIdx/uintptrSize,
@ -516,7 +544,7 @@ func (c *Opcode) dumpValue(code *Opcode) string {
return fmt.Sprintf(
`[%d]%s%s ([idx:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", code.Indent),
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.MapIter/uintptrSize,
@ -525,7 +553,7 @@ func (c *Opcode) dumpValue(code *Opcode) string {
func (c *Opcode) Dump() string {
codes := []string{}
for code := c; code.Op != OpEnd; {
for code := c; code.Op != OpEnd && code.Op != OpInterfaceEnd; {
switch code.Op.CodeType() {
case CodeSliceHead:
codes = append(codes, c.dumpHead(code))
@ -555,7 +583,7 @@ func (c *Opcode) Dump() string {
codes = append(codes, fmt.Sprintf(
"[%d]%s%s ([idx:%d])",
code.DisplayIdx,
strings.Repeat("-", code.Indent),
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
))
@ -610,9 +638,8 @@ func newSliceHeaderCode(ctx *compileContext) *Opcode {
length := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpSlice,
DisplayIdx: ctx.opcodeIndex,
Idx: idx,
HeadIdx: idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: elemIdx,
Length: length,
Indent: ctx.indent,
@ -622,13 +649,12 @@ func newSliceHeaderCode(ctx *compileContext) *Opcode {
func newSliceElemCode(ctx *compileContext, head *Opcode, size uintptr) *Opcode {
return &Opcode{
Op: OpSliceElem,
Idx: head.Idx,
DisplayIdx: ctx.opcodeIndex,
Idx: opcodeOffset(ctx.ptrIndex),
HeadIdx: head.Idx,
ElemIdx: head.ElemIdx,
Length: head.Length,
Indent: ctx.indent,
Size: size,
Size: uint32(size),
}
}
@ -638,25 +664,23 @@ func newArrayHeaderCode(ctx *compileContext, alen int) *Opcode {
elemIdx := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpArray,
DisplayIdx: ctx.opcodeIndex,
Idx: idx,
HeadIdx: idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: elemIdx,
Indent: ctx.indent,
Length: uintptr(alen),
Length: uint32(alen),
}
}
func newArrayElemCode(ctx *compileContext, head *Opcode, length int, size uintptr) *Opcode {
return &Opcode{
Op: OpArrayElem,
Idx: head.Idx,
DisplayIdx: ctx.opcodeIndex,
Idx: opcodeOffset(ctx.ptrIndex),
ElemIdx: head.ElemIdx,
HeadIdx: head.HeadIdx,
Length: uintptr(length),
Length: uint32(length),
Indent: ctx.indent,
Size: size,
Size: uint32(size),
}
}
@ -670,9 +694,9 @@ func newMapHeaderCode(ctx *compileContext) *Opcode {
mapIter := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpMap,
Idx: idx,
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Idx: idx,
ElemIdx: elemIdx,
Length: length,
MapIter: mapIter,
@ -683,8 +707,8 @@ func newMapHeaderCode(ctx *compileContext) *Opcode {
func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
return &Opcode{
Op: OpMapKey,
DisplayIdx: ctx.opcodeIndex,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: head.Length,
MapIter: head.MapIter,
@ -695,8 +719,8 @@ func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
func newMapValueCode(ctx *compileContext, head *Opcode) *Opcode {
return &Opcode{
Op: OpMapValue,
DisplayIdx: ctx.opcodeIndex,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: head.Length,
MapIter: head.MapIter,
@ -710,34 +734,39 @@ func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
idx := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpMapEnd,
DisplayIdx: ctx.opcodeIndex,
Idx: idx,
Next: newEndOp(ctx),
DisplayIdx: ctx.opcodeIndex,
Length: head.Length,
MapPos: mapPos,
Indent: ctx.indent,
Next: newEndOp(ctx),
}
}
func newInterfaceCode(ctx *compileContext) *Opcode {
var flag OpFlags
if ctx.typ.NumMethod() > 0 {
flag |= NonEmptyInterfaceFlags
}
return &Opcode{
Op: OpInterface,
Idx: opcodeOffset(ctx.ptrIndex),
Next: newEndOp(ctx),
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Idx: opcodeOffset(ctx.ptrIndex),
Indent: ctx.indent,
Next: newEndOp(ctx),
Flags: flag,
}
}
func newRecursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
return &Opcode{
Op: OpRecursive,
Idx: opcodeOffset(ctx.ptrIndex),
Next: newEndOp(ctx),
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Idx: opcodeOffset(ctx.ptrIndex),
Indent: ctx.indent,
Next: newEndOp(ctx),
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

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

View file

@ -405,7 +405,10 @@ func stringToUint64Slice(s string) []uint64 {
}))
}
func AppendEscapedString(buf []byte, s string) []byte {
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, `""`...)
@ -531,7 +534,7 @@ ESCAPE_END:
return append(append(buf, s[i:]...), '"')
}
func AppendString(buf []byte, s string) []byte {
func appendString(buf []byte, s string) []byte {
valLen := len(s)
if valLen == 0 {
return append(buf, `""`...)

View file

@ -6,15 +6,22 @@ import (
"github.com/goccy/go-json/internal/encoder"
)
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
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(codeSet.Code.Dump())
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
@ -23,5 +30,5 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
}
}()
return Run(ctx, b, codeSet, opt)
return Run(ctx, b, codeSet)
}

View file

@ -2,8 +2,8 @@ package vm
import (
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent` packages uses a lot of memory to compile,
// `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_escaped => vm_indent => vm_escaped_indent
_ "github.com/goccy/go-json/internal/encoder/vm_escaped"
// dependency order: vm => vm_indent => vm_color => vm_color_indent
_ "github.com/goccy/go-json/internal/encoder/vm_indent"
)

View file

@ -33,24 +33,33 @@ type emptyInterface struct {
ptr unsafe.Pointer
}
type nonEmptyInterface struct {
itab *struct {
ityp *runtime.Type // static interface type
typ *runtime.Type // dynamic concrete type
// unused fields...
}
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr {
addr := base + idx
func load(base uintptr, idx uint32) uintptr {
addr := base + uintptr(idx)
return **(**uintptr)(unsafe.Pointer(&addr))
}
func store(base uintptr, idx uintptr, p uintptr) {
addr := base + idx
func store(base uintptr, idx uint32, p uintptr) {
addr := base + uintptr(idx)
**(**uintptr)(unsafe.Pointer(&addr)) = p
}
func loadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
addr := base + idx
func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
addr := base + uintptr(idx)
p := **(**uintptr)(unsafe.Pointer(&addr))
for i := 0; i < ptrNum; i++ {
for i := uint8(0); i < ptrNum; i++ {
if p == 0 {
return 0
}
@ -70,8 +79,8 @@ func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader
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++ {
func ptrToNPtr(p uintptr, ptrNum uint8) uintptr {
for i := uint8(0); i < ptrNum; i++ {
if p == 0 {
return 0
}
@ -90,22 +99,22 @@ func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
}))
}
func appendBool(b []byte, v bool) []byte {
func appendBool(_ *encoder.RuntimeContext, b []byte, v bool) []byte {
if v {
return append(b, "true"...)
}
return append(b, "false"...)
}
func appendNull(b []byte) []byte {
func appendNull(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, "null"...)
}
func appendComma(b []byte) []byte {
func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, ',')
}
func appendColon(b []byte) []byte {
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
last := len(b) - 1
b[last] = ':'
return b
@ -123,45 +132,12 @@ func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte
return b
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, _ *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalJSON(ctx, code, b, v, false)
return encoder.AppendMarshalJSON(ctx, code, b, v)
}
func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalText(code, b, v, false)
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 {
@ -174,11 +150,11 @@ func appendArrayEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []by
return append(b, ',')
}
func appendEmptyArray(b []byte) []byte {
func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '[', ']', ',')
}
func appendEmptyObject(b []byte) []byte {
func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '{', '}', ',')
}
@ -188,7 +164,7 @@ func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []b
return append(b, ',')
}
func appendStructHead(b []byte) []byte {
func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '{')
}
@ -204,7 +180,7 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
last := len(b) - 1
if b[last] == ',' {
b[last] = '}'
return appendComma(b)
return appendComma(ctx, b)
}
return appendStructEnd(ctx, code, b)
}

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
package vm_escaped_indent
package vm_color
import (
"fmt"
@ -6,7 +6,14 @@ import (
"github.com/goccy/go-json/internal/encoder"
)
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
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]===============")
@ -14,7 +21,7 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(codeSet.Code.Dump())
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
@ -23,5 +30,5 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
}
}()
return Run(ctx, b, codeSet, opt)
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

@ -1,4 +1,4 @@
package vm_escaped
package vm_color
import (
"encoding/json"
@ -12,13 +12,6 @@ import (
const uintptrSize = 4 << (^uintptr(0) >> 63)
var (
appendInt = encoder.AppendInt
appendUint = encoder.AppendUint
appendFloat32 = encoder.AppendFloat32
appendFloat64 = encoder.AppendFloat64
appendString = encoder.AppendEscapedString
appendByteSlice = encoder.AppendByteSlice
appendNumber = encoder.AppendNumber
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
@ -33,24 +26,33 @@ type emptyInterface struct {
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (escaped): opcode %s has not been implemented", op)
type nonEmptyInterface struct {
itab *struct {
ityp *runtime.Type // static interface type
typ *runtime.Type // dynamic concrete type
// unused fields...
}
ptr unsafe.Pointer
}
func load(base uintptr, idx uintptr) uintptr {
addr := base + idx
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 uintptr, p uintptr) {
addr := base + idx
func store(base uintptr, idx uint32, p uintptr) {
addr := base + uintptr(idx)
**(**uintptr)(unsafe.Pointer(&addr)) = p
}
func loadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
addr := base + idx
func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
addr := base + uintptr(idx)
p := **(**uintptr)(unsafe.Pointer(&addr))
for i := 0; i < ptrNum; i++ {
for i := uint8(0); i < ptrNum; i++ {
if p == 0 {
return 0
}
@ -70,8 +72,8 @@ func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader
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++ {
func ptrToNPtr(p uintptr, ptrNum uint8) uintptr {
for i := uint8(0); i < ptrNum; i++ {
if p == 0 {
return 0
}
@ -90,78 +92,111 @@ func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
}))
}
func appendBool(b []byte, v bool) []byte {
if v {
return append(b, "true"...)
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(b, "false"...)
return append(bb, format.Footer...), nil
}
func appendNull(b []byte) []byte {
return append(b, "null"...)
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 appendComma(b []byte) []byte {
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(b []byte) []byte {
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] = ':'
b = append(b, key[:len(key)-1]...)
b = append(b, ':')
return append(b, value...)
}
func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte {
b[len(b)-1] = '}'
last := len(b) - 1
b[last] = '}'
b = append(b, ',')
return b
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, _ *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalJSON(ctx, code, b, v, true)
return encoder.AppendMarshalJSON(ctx, code, b, v)
}
func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalText(code, b, v, true)
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 {
@ -174,11 +209,11 @@ func appendArrayEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []by
return append(b, ',')
}
func appendEmptyArray(b []byte) []byte {
func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '[', ']', ',')
}
func appendEmptyObject(b []byte) []byte {
func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '{', '}', ',')
}
@ -188,12 +223,17 @@ func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []b
return append(b, ',')
}
func appendStructHead(b []byte) []byte {
func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '{')
}
func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
return append(b, code.EscapedKey...)
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 {
@ -204,7 +244,7 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
last := len(b) - 1
if b[last] == ',' {
b[last] = '}'
return appendComma(b)
return appendComma(ctx, b)
}
return appendStructEnd(ctx, code, b)
}

View file

@ -1,4 +1,4 @@
package vm_escaped
package vm_color_indent
import (
"fmt"
@ -6,7 +6,14 @@ import (
"github.com/goccy/go-json/internal/encoder"
)
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
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]===============")
@ -14,7 +21,7 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(codeSet.Code.Dump())
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
@ -23,5 +30,5 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
}
}()
return Run(ctx, b, codeSet, opt)
return Run(ctx, b, codeSet)
}

View file

@ -1,4 +1,4 @@
package vm_escaped_indent
package vm_color_indent
import (
"encoding/json"
@ -12,15 +12,8 @@ import (
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
appendStructEnd = encoder.AppendStructEndIndent
errUnsupportedValue = encoder.ErrUnsupportedValue
errUnsupportedFloat = encoder.ErrUnsupportedFloat
mapiterinit = encoder.MapIterInit
@ -35,24 +28,33 @@ type emptyInterface struct {
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (indent+escaped): opcode %s has not been implemented", op)
type nonEmptyInterface struct {
itab *struct {
ityp *runtime.Type // static interface type
typ *runtime.Type // dynamic concrete type
// unused fields...
}
ptr unsafe.Pointer
}
func load(base uintptr, idx uintptr) uintptr {
addr := base + idx
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 uintptr, p uintptr) {
addr := base + idx
func store(base uintptr, idx uint32, p uintptr) {
addr := base + uintptr(idx)
**(**uintptr)(unsafe.Pointer(&addr)) = p
}
func loadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
addr := base + idx
func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
addr := base + uintptr(idx)
p := **(**uintptr)(unsafe.Pointer(&addr))
for i := 0; i < ptrNum; i++ {
for i := uint8(0); i < ptrNum; i++ {
if p == 0 {
return 0
}
@ -72,8 +74,8 @@ func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader
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++ {
func ptrToNPtr(p uintptr, ptrNum uint8) uintptr {
for i := uint8(0); i < ptrNum; i++ {
if p == 0 {
return 0
}
@ -92,62 +94,84 @@ func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
}))
}
func appendBool(b []byte, v bool) []byte {
if v {
return append(b, "true"...)
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(b, "false"...)
return append(bb, format.Footer...), nil
}
func appendNull(b []byte) []byte {
return append(b, "null"...)
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 appendComma(b []byte) []byte {
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(b []byte) []byte {
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, ':', ' ')
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.BaseIndent = oldBaseIndent
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
b = appendIndent(ctx, b, code.Indent+1)
b = append(b, key...)
@ -175,11 +199,11 @@ func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte)
return append(b, ']', ',', '\n')
}
func appendEmptyArray(b []byte) []byte {
func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '[', ']', ',', '\n')
}
func appendEmptyObject(b []byte) []byte {
func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '{', '}', ',', '\n')
}
@ -191,21 +215,32 @@ func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte
}
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalJSONIndent(ctx, code, b, v, true)
return encoder.AppendMarshalJSONIndent(ctx, code, b, v)
}
func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalTextIndent(code, b, v, true)
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(b []byte) []byte {
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.EscapedKey...)
return append(b, ' ')
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 {
@ -221,15 +256,15 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
b = appendIndent(ctx, b, code.Indent-1)
b = append(b, '}')
}
return appendComma(b)
return appendComma(ctx, b)
}
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {
ctx.BaseIndent = int(load(ctxptr, code.Length))
ctx.BaseIndent = uint32(load(ctxptr, code.Length))
}
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {
store(ctxptr, code.End.Next.Length, indent)
store(ctxptr, code.Length, indent)
}
func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {

View file

@ -1,9 +0,0 @@
package vm_escaped
import (
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent
_ "github.com/goccy/go-json/internal/encoder/vm_indent"
)

View file

@ -1,6 +0,0 @@
package vm_escaped_indent
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent` packages uses a lot of memory to compile,
// so forcibly make dependencies and avoid compiling in concurrent.
// dependency order: vm => vm_escaped => vm_indent => vm_escaped_indent

View file

@ -6,7 +6,14 @@ import (
"github.com/goccy/go-json/internal/encoder"
)
func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet, opt encoder.Option) ([]byte, error) {
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]===============")
@ -14,7 +21,7 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(codeSet.Code.Dump())
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
@ -23,5 +30,5 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet,
}
}()
return Run(ctx, b, codeSet, opt)
return Run(ctx, b, codeSet)
}

View file

@ -2,8 +2,8 @@ package vm_indent
import (
// HACK: compile order
// `vm`, `vm_escaped`, `vm_indent`, `vm_escaped_indent` packages uses a lot of memory to compile,
// `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_escaped => vm_indent => vm_escaped_indent
_ "github.com/goccy/go-json/internal/encoder/vm_escaped_indent"
// dependency order: vm => vm_indent => vm_color => vm_color_indent
_ "github.com/goccy/go-json/internal/encoder/vm_color"
)

View file

@ -35,24 +35,33 @@ type emptyInterface struct {
ptr unsafe.Pointer
}
type nonEmptyInterface struct {
itab *struct {
ityp *runtime.Type // static interface type
typ *runtime.Type // dynamic concrete type
// unused fields...
}
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
}
func load(base uintptr, idx uintptr) uintptr {
addr := base + idx
func load(base uintptr, idx uint32) uintptr {
addr := base + uintptr(idx)
return **(**uintptr)(unsafe.Pointer(&addr))
}
func store(base uintptr, idx uintptr, p uintptr) {
addr := base + idx
func store(base uintptr, idx uint32, p uintptr) {
addr := base + uintptr(idx)
**(**uintptr)(unsafe.Pointer(&addr)) = p
}
func loadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
addr := base + idx
func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
addr := base + uintptr(idx)
p := **(**uintptr)(unsafe.Pointer(&addr))
for i := 0; i < ptrNum; i++ {
for i := uint8(0); i < ptrNum; i++ {
if p == 0 {
return 0
}
@ -72,8 +81,8 @@ func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader
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++ {
func ptrToNPtr(p uintptr, ptrNum uint8) uintptr {
for i := uint8(0); i < ptrNum; i++ {
if p == 0 {
return 0
}
@ -92,62 +101,25 @@ func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
}))
}
func appendBool(b []byte, v bool) []byte {
func appendBool(_ *encoder.RuntimeContext, b []byte, v bool) []byte {
if v {
return append(b, "true"...)
}
return append(b, "false"...)
}
func appendNull(b []byte) []byte {
func appendNull(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, "null"...)
}
func appendComma(b []byte) []byte {
func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, ',', '\n')
}
func appendColon(b []byte) []byte {
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, ':', ' ')
}
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, opt encoder.Option, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil {
return nil, err
}
totalLength := uintptr(codeSet.CodeLength)
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
curlen := uintptr(len(ctx.Ptrs))
offsetNum := ptrOffset / uintptrSize
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
}
oldPtrs := ctx.Ptrs
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
newPtrs[0] = uintptr(iface.ptr)
ctx.Ptrs = newPtrs
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent = code.Indent
bb, err := Run(ctx, b, ifaceCodeSet, opt)
if err != nil {
return nil, err
}
ctx.BaseIndent = oldBaseIndent
ctx.Ptrs = oldPtrs
return bb, nil
}
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
b = appendIndent(ctx, b, code.Indent+1)
b = append(b, key...)
@ -175,11 +147,11 @@ func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte)
return append(b, ']', ',', '\n')
}
func appendEmptyArray(b []byte) []byte {
func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '[', ']', ',', '\n')
}
func appendEmptyObject(b []byte) []byte {
func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '{', '}', ',', '\n')
}
@ -191,14 +163,14 @@ func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte
}
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalJSONIndent(ctx, code, b, v, false)
return encoder.AppendMarshalJSONIndent(ctx, code, b, v)
}
func appendMarshalText(code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalTextIndent(code, b, v, false)
func appendMarshalText(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
return encoder.AppendMarshalTextIndent(ctx, code, b, v)
}
func appendStructHead(b []byte) []byte {
func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte {
return append(b, '{', '\n')
}
@ -221,15 +193,15 @@ func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode,
b = appendIndent(ctx, b, code.Indent-1)
b = append(b, '}')
}
return appendComma(b)
return appendComma(ctx, b)
}
func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) {
ctx.BaseIndent = int(load(ctxptr, code.Length))
ctx.BaseIndent = uint32(load(ctxptr, code.Length))
}
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {
store(ctxptr, code.End.Next.Length, indent)
store(ctxptr, code.Length, indent)
}
func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {

File diff suppressed because it is too large Load diff

View file

@ -155,3 +155,10 @@ func ErrInvalidCharacter(c byte, context string, cursor int64) *SyntaxError {
Offset: cursor,
}
}
func ErrInvalidBeginningOfValue(c byte, cursor int64) *SyntaxError {
return &SyntaxError{
msg: fmt.Sprintf("invalid character '%c' looking for beginning of value", c),
Offset: cursor,
}
}

View file

@ -2,6 +2,7 @@ package json
import (
"bytes"
"context"
"encoding/json"
"github.com/goccy/go-json/internal/encoder"
@ -13,6 +14,12 @@ type Marshaler interface {
MarshalJSON() ([]byte, error)
}
// MarshalerContext is the interface implemented by types that
// can marshal themselves into valid JSON with context.Context.
type MarshalerContext interface {
MarshalJSON(context.Context) ([]byte, error)
}
// Unmarshaler is the interface implemented by types
// that can unmarshal a JSON description of themselves.
// The input can be assumed to be a valid encoding of
@ -25,6 +32,12 @@ type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
// UnmarshalerContext is the interface implemented by types
// that can unmarshal with context.Context a JSON description of themselves.
type UnmarshalerContext interface {
UnmarshalJSON(context.Context, []byte) error
}
// Marshal returns the JSON encoding of v.
//
// Marshal traverses the value v recursively.
@ -158,18 +171,19 @@ func Marshal(v interface{}) ([]byte, error) {
return MarshalWithOption(v)
}
// MarshalNoEscape
// MarshalNoEscape returns the JSON encoding of v and doesn't escape v.
func MarshalNoEscape(v interface{}) ([]byte, error) {
return marshalNoEscape(v, EncodeOptionHTMLEscape)
return marshalNoEscape(v)
}
// MarshalContext returns the JSON encoding of v with context.Context and EncodeOption.
func MarshalContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
return marshalContext(ctx, v, optFuncs...)
}
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
func MarshalWithOption(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
opt := EncodeOptionHTMLEscape
for _, optFunc := range optFuncs {
opt = optFunc(opt)
}
return marshal(v, opt)
return marshal(v, optFuncs...)
}
// MarshalIndent is like Marshal but applies Indent to format the output.
@ -181,11 +195,7 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
// MarshalIndentWithOption is like Marshal but applies Indent to format the output with EncodeOption.
func MarshalIndentWithOption(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
opt := EncodeOptionHTMLEscape | EncodeOptionIndent
for _, optFunc := range optFuncs {
opt = optFunc(opt)
}
return marshalIndent(v, prefix, indent, opt)
return marshalIndent(v, prefix, indent, optFuncs...)
}
// Unmarshal parses the JSON-encoded data and stores the result
@ -266,8 +276,19 @@ func Unmarshal(data []byte, v interface{}) error {
return unmarshal(data, v)
}
func UnmarshalNoEscape(data []byte, v interface{}) error {
return unmarshalNoEscape(data, v)
// UnmarshalContext parses the JSON-encoded data and stores the result
// in the value pointed to by v. If you implement the UnmarshalerContext interface,
// call it with ctx as an argument.
func UnmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
return unmarshalContext(ctx, data, v)
}
func UnmarshalWithOption(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
return unmarshal(data, v, optFuncs...)
}
func UnmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
return unmarshalNoEscape(data, v, optFuncs...)
}
// A Token holds a value of one of these types:
@ -326,7 +347,7 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) {
if err := dec.Decode(&v); err != nil {
return
}
buf, _ := marshal(v, EncodeOptionHTMLEscape)
buf, _ := marshal(v)
dst.Write(buf)
}

View file

@ -1,15 +1,46 @@
package json
type EncodeOptionFunc func(EncodeOption) EncodeOption
import (
"github.com/goccy/go-json/internal/decoder"
"github.com/goccy/go-json/internal/encoder"
)
func UnorderedMap() func(EncodeOption) EncodeOption {
return func(opt EncodeOption) EncodeOption {
return opt | EncodeOptionUnorderedMap
type EncodeOption = encoder.Option
type EncodeOptionFunc func(*EncodeOption)
// UnorderedMap doesn't sort when encoding map type.
func UnorderedMap() EncodeOptionFunc {
return func(opt *EncodeOption) {
opt.Flag |= encoder.UnorderedMapOption
}
}
func Debug() func(EncodeOption) EncodeOption {
return func(opt EncodeOption) EncodeOption {
return opt | EncodeOptionDebug
// Debug outputs debug information when panic occurs during encoding.
func Debug() EncodeOptionFunc {
return func(opt *EncodeOption) {
opt.Flag |= encoder.DebugOption
}
}
// Colorize add an identifier for coloring to the string of the encoded result.
func Colorize(scheme *ColorScheme) EncodeOptionFunc {
return func(opt *EncodeOption) {
opt.Flag |= encoder.ColorizeOption
opt.ColorScheme = scheme
}
}
type DecodeOption = decoder.Option
type DecodeOptionFunc func(*DecodeOption)
// DecodeFieldPriorityFirstWin
// in the default behavior, go-json, like encoding/json,
// will reflect the result of the last evaluation when a field with the same name exists.
// This option allow you to change this behavior.
// this option reflects the result of the first evaluation if a field with the same name exists.
// This behavior has a performance advantage as it allows the subsequent strings to be skipped if all fields have been evaluated.
func DecodeFieldPriorityFirstWin() DecodeOptionFunc {
return func(opt *DecodeOption) {
opt.Flags |= decoder.FirstWinOption
}
}

View file

@ -1,27 +0,0 @@
package json
import (
"reflect"
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
type rtype = runtime.Type
type emptyInterface struct {
typ *rtype
ptr unsafe.Pointer
}
func rtype_ptrTo(t *rtype) *rtype {
return runtime.PtrTo(t)
}
func rtype2type(t *rtype) reflect.Type {
return runtime.RType2Type(t)
}
func type2rtype(t reflect.Type) *rtype {
return runtime.Type2RType(t)
}