Compare commits

..

No commits in common. "master" and "feature/fix-escape-string" have entirely different histories.

75 changed files with 762 additions and 4537 deletions

View File

@ -18,7 +18,7 @@ jobs:
strategy:
matrix:
os: [ "ubuntu-latest", "macos-latest", "windows-latest" ]
go-version: [ "1.16", "1.17", "1.18" ]
go-version: [ "1.15", "1.16", "1.17" ]
runs-on: ${{ matrix.os }}
steps:
- name: setup Go ${{ matrix.go-version }}
@ -42,7 +42,7 @@ jobs:
- name: setup Go
uses: actions/setup-go@v2
with:
go-version: 1.18
go-version: 1.17
- name: checkout ( feature )
uses: actions/checkout@v2
- name: run benchmark ( feature )
@ -64,7 +64,7 @@ jobs:
- name: setup Go
uses: actions/setup-go@v2
with:
go-version: 1.18
go-version: 1.17
- name: checkout
uses: actions/checkout@v2
- name: measure coverage

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: golangci/golangci-lint-action@v3
- uses: golangci/golangci-lint-action@v2
with:
version: v1.45.2
version: v1.36.0
args: --timeout=5m

View File

@ -48,14 +48,6 @@ linters:
- nlreturn
- testpackage
- wsl
- varnamelen
- nilnil
- ireturn
- govet
- forcetypeassert
- cyclop
- containedctx
- revive
issues:
exclude-rules:

View File

@ -1,129 +1,3 @@
# v0.10.0 - 2022/11/29
### New features
* Support JSON Path ( #250 )
### Fix bugs
* Fix marshaler for map's key ( #409 )
# v0.9.11 - 2022/08/18
### Fix bugs
* Fix unexpected behavior when buffer ends with backslash ( #383 )
* Fix stream decoding of escaped character ( #387 )
# v0.9.10 - 2022/07/15
### Fix bugs
* Fix boundary exception of type caching ( #382 )
# v0.9.9 - 2022/07/15
### Fix bugs
* Fix encoding of directed interface with typed nil ( #377 )
* Fix embedded primitive type encoding using alias ( #378 )
* Fix slice/array type encoding with types implementing MarshalJSON ( #379 )
* Fix unicode decoding when the expected buffer state is not met after reading ( #380 )
# v0.9.8 - 2022/06/30
### Fix bugs
* Fix decoding of surrogate-pair ( #365 )
* Fix handling of embedded primitive type ( #366 )
* Add validation of escape sequence for decoder ( #367 )
* Fix stream tokenizing respecting UseNumber ( #369 )
* Fix encoding when struct pointer type that implements Marshal JSON is embedded ( #375 )
### Improve performance
* Improve performance of linkRecursiveCode ( #368 )
# v0.9.7 - 2022/04/22
### Fix bugs
#### Encoder
* Add filtering process for encoding on slow path ( #355 )
* Fix encoding of interface{} with pointer type ( #363 )
#### Decoder
* Fix map key decoder that implements UnmarshalJSON ( #353 )
* Fix decoding of []uint8 type ( #361 )
### New features
* Add DebugWith option for encoder ( #356 )
# v0.9.6 - 2022/03/22
### Fix bugs
* Correct the handling of the minimum value of int type for decoder ( #344 )
* Fix bugs of stream decoder's bufferSize ( #349 )
* Add a guard to use typeptr more safely ( #351 )
### Improve decoder performance
* Improve escapeString's performance ( #345 )
### Others
* Update go version for CI ( #347 )
# v0.9.5 - 2022/03/04
### Fix bugs
* Fix panic when decoding time.Time with context ( #328 )
* Fix reading the next character in buffer to nul consideration ( #338 )
* Fix incorrect handling on skipValue ( #341 )
### Improve decoder performance
* Improve performance when a payload contains escape sequence ( #334 )
# v0.9.4 - 2022/01/21
* Fix IsNilForMarshaler for string type with omitempty ( #323 )
* Fix the case where the embedded field is at the end ( #326 )
# v0.9.3 - 2022/01/14
* Fix logic of removing struct field for decoder ( #322 )
# v0.9.2 - 2022/01/14
* Add invalid decoder to delay type error judgment at decode ( #321 )
# v0.9.1 - 2022/01/11
* Fix encoding of MarshalText/MarshalJSON operation with head offset ( #319 )
# v0.9.0 - 2022/01/05
### New feature
* Supports dynamic filtering of struct fields ( #314 )
### Improve encoding performance
* Improve map encoding performance ( #310 )
* Optimize encoding path for escaped string ( #311 )
* Add encoding option for performance ( #312 )
### Fix bugs
* Fix panic at encoding map value on 1.18 ( #310 )
* Fix MarshalIndent for interface type ( #317 )
# v0.8.1 - 2021/12/05
* Fix operation conversion from PtrHead to Head in Recursive type ( #305 )

View File

@ -22,7 +22,7 @@ cover-html: cover
.PHONY: lint
lint: golangci-lint
$(BIN_DIR)/golangci-lint run
golangci-lint run
golangci-lint: | $(BIN_DIR)
@{ \
@ -30,7 +30,7 @@ golangci-lint: | $(BIN_DIR)
GOLANGCI_LINT_TMP_DIR=$$(mktemp -d); \
cd $$GOLANGCI_LINT_TMP_DIR; \
go mod init tmp; \
GOBIN=$(BIN_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0; \
GOBIN=$(BIN_DIR) go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.36.0; \
rm -rf $$GOLANGCI_LINT_TMP_DIR; \
}

View File

@ -13,7 +13,7 @@ Fast JSON encoder/decoder compatible with encoding/json for Go
```
* version ( expected release date )
* v0.9.0
* v0.8.0
|
| while maintaining compatibility with encoding/json, we will add convenient APIs
|
@ -21,8 +21,9 @@ Fast JSON encoder/decoder compatible with encoding/json for Go
* v1.0.0
```
We are accepting requests for features that will be implemented between v0.9.0 and v.1.0.0.
We are accepting requests for features that will be implemented between v0.8.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
@ -31,7 +32,6 @@ If you have the API you need, please submit your issue [here](https://github.com
- Flexible customization with options
- Coloring the encoded string
- Can propagate context.Context to `MarshalJSON` or `UnmarshalJSON`
- Can dynamically filter the fields of the structure type-safely
# Installation
@ -184,7 +184,7 @@ func Marshal(v interface{}) ([]byte, error) {
`json.Marshal` and `json.Unmarshal` receive `interface{}` value and they perform type determination dynamically to process.
In normal case, you need to use the `reflect` library to determine the type dynamically, but since `reflect.Type` is defined as `interface`, when you call the method of `reflect.Type`, The reflect's argument is escaped.
Therefore, the arguments for `Marshal` and `Unmarshal` are always escaped to the heap.
Therefore, the arguments for `Marshal` and `Unmarshal` are always escape to the heap.
However, `go-json` can use the feature of `reflect.Type` while avoiding escaping.
`reflect.Type` is defined as `interface`, but in reality `reflect.Type` is implemented only by the structure `rtype` defined in the `reflect` package.

View File

@ -477,13 +477,3 @@ func Benchmark_Decode_LargeStruct_Stream_GoJsonFirstWinMode(b *testing.B) {
}
}
}
func Benchmark_Decode_LargeSlice_EscapedString_GoJson(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var v []string
if err := gojson.Unmarshal(LargeSliceEscapedString, &v); err != nil {
b.Fatal(err)
}
}
}

View File

@ -2,7 +2,6 @@ package benchmark
import (
"bytes"
"context"
"encoding/json"
"testing"
@ -836,80 +835,3 @@ func Benchmark_Encode_MarshalJSON_GoJson(b *testing.B) {
}
}
}
type queryTestX struct {
XA int
XB string
XC *queryTestY
XD bool
XE float32
}
type queryTestY struct {
YA int
YB string
YC bool
YD float32
}
func Benchmark_Encode_FilterByMap(b *testing.B) {
v := &queryTestX{
XA: 1,
XB: "xb",
XC: &queryTestY{
YA: 2,
YB: "yb",
YC: true,
YD: 4,
},
XD: true,
XE: 5,
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
filteredMap := map[string]interface{}{
"XA": v.XA,
"XB": v.XB,
"XC": map[string]interface{}{
"YA": v.XC.YA,
"YB": v.XC.YB,
},
}
if _, err := gojson.Marshal(filteredMap); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_Encode_FilterByFieldQuery(b *testing.B) {
query, err := gojson.BuildFieldQuery(
"XA",
"XB",
gojson.BuildSubFieldQuery("XC").Fields(
"YA",
"YB",
),
)
if err != nil {
b.Fatal(err)
}
v := &queryTestX{
XA: 1,
XB: "xb",
XC: &queryTestY{
YA: 2,
YB: "yb",
YC: true,
YD: 4,
},
XD: true,
XE: 5,
}
ctx := gojson.SetFieldQueryToContext(context.Background(), query)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
if _, err := gojson.MarshalContext(ctx, v); err != nil {
b.Fatal(err)
}
}
}

View File

@ -2,7 +2,6 @@ package benchmark
import (
"strconv"
"strings"
"github.com/francoispqt/gojay"
)
@ -209,5 +208,3 @@ func NewLargePayloadEasyJson() *LargePayloadEasyJson {
},
}
}
var LargeSliceEscapedString = []byte("[" + strings.Repeat(",\"simple plain text\\r\\n\"", 10000)[1:] + "]")

View File

@ -1,24 +0,0 @@
package benchmark
import (
"testing"
gojson "github.com/goccy/go-json"
)
func Benchmark_Decode_SmallStruct_UnmarshalPath_GoJson(b *testing.B) {
path, err := gojson.CreatePath("$.st")
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var v int
if err := path.Unmarshal(SmallFixture, &v); err != nil {
b.Fatal(err)
}
if v != 1 {
b.Fatal("failed to unmarshal path")
}
}
}

View File

@ -83,37 +83,6 @@ func unmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs
return validateEndBuf(src, cursor)
}
var (
pathDecoder = decoder.NewPathDecoder()
)
func extractFromPath(path *Path, data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) {
if path.path.RootSelectorOnly {
return [][]byte{data}, nil
}
src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data)
ctx := decoder.TakeRuntimeContext()
ctx.Buf = src
ctx.Option.Flags = 0
ctx.Option.Flags |= decoder.PathOption
ctx.Option.Path = path.path
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
paths, cursor, err := pathDecoder.DecodePath(ctx, 0, 0)
if err != nil {
decoder.ReleaseRuntimeContext(ctx)
return nil, err
}
decoder.ReleaseRuntimeContext(ctx)
if err := validateEndBuf(src, cursor); err != nil {
return nil, err
}
return paths, nil
}
func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
src := make([]byte, len(data)+1) // append nul byte to the end
copy(src, data)

View File

@ -1317,12 +1317,6 @@ var unmarshalTests = []unmarshalTest{
ptr: new(string),
out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
},
{in: "-128", ptr: new(int8), out: int8(-128)},
{in: "127", ptr: new(int8), out: int8(127)},
{in: "-32768", ptr: new(int16), out: int16(-32768)},
{in: "32767", ptr: new(int16), out: int16(32767)},
{in: "-2147483648", ptr: new(int32), out: int32(-2147483648)},
{in: "2147483647", ptr: new(int32), out: int32(2147483647)},
}
type All struct {
@ -3782,236 +3776,3 @@ func TestIssue282(t *testing.T) {
t.Fatalf("failed to assign map value")
}
}
func TestDecodeStructFieldMap(t *testing.T) {
type Foo struct {
Bar map[float64]float64 `json:"bar,omitempty"`
}
var v Foo
if err := json.Unmarshal([]byte(`{"name":"test"}`), &v); err != nil {
t.Fatal(err)
}
if v.Bar != nil {
t.Fatalf("failed to decode v.Bar = %+v", v.Bar)
}
}
type issue303 struct {
Count int
Type string
Value interface{}
}
func (t *issue303) UnmarshalJSON(b []byte) error {
type tmpType issue303
wrapped := struct {
Value json.RawMessage
tmpType
}{}
if err := json.Unmarshal(b, &wrapped); err != nil {
return err
}
*t = issue303(wrapped.tmpType)
switch wrapped.Type {
case "string":
var str string
if err := json.Unmarshal(wrapped.Value, &str); err != nil {
return err
}
t.Value = str
}
return nil
}
func TestIssue303(t *testing.T) {
var v issue303
if err := json.Unmarshal([]byte(`{"Count":7,"Type":"string","Value":"hello"}`), &v); err != nil {
t.Fatal(err)
}
if v.Count != 7 || v.Type != "string" || v.Value != "hello" {
t.Fatalf("failed to decode. count = %d type = %s value = %v", v.Count, v.Type, v.Value)
}
}
func TestIssue327(t *testing.T) {
var v struct {
Date time.Time `json:"date"`
}
dec := json.NewDecoder(strings.NewReader(`{"date": "2021-11-23T13:47:30+01:00"})`))
if err := dec.DecodeContext(context.Background(), &v); err != nil {
t.Fatal(err)
}
expected := "2021-11-23T13:47:30+01:00"
if got := v.Date.Format(time.RFC3339); got != expected {
t.Fatalf("failed to decode. expected %q but got %q", expected, got)
}
}
func TestIssue337(t *testing.T) {
in := strings.Repeat(" ", 510) + "{}"
var m map[string]string
if err := json.NewDecoder(strings.NewReader(in)).Decode(&m); err != nil {
t.Fatal("unexpected error:", err)
}
if len(m) != 0 {
t.Fatal("unexpected result", m)
}
}
func Benchmark306(b *testing.B) {
type T0 struct {
Str string
}
in := []byte(`{"Str":"` + strings.Repeat(`abcd\"`, 10000) + `"}`)
b.Run("stdjson", func(b *testing.B) {
var x T0
for i := 0; i < b.N; i++ {
stdjson.Unmarshal(in, &x)
}
})
b.Run("go-json", func(b *testing.B) {
var x T0
for i := 0; i < b.N; i++ {
json.Unmarshal(in, &x)
}
})
}
func TestIssue348(t *testing.T) {
in := strings.Repeat("["+strings.Repeat(",1000", 500)[1:]+"]", 2)
dec := json.NewDecoder(strings.NewReader(in))
for dec.More() {
var foo interface{}
if err := dec.Decode(&foo); err != nil {
t.Error(err)
}
}
}
type issue342 string
func (t *issue342) UnmarshalJSON(b []byte) error {
panic("unreachable")
}
func TestIssue342(t *testing.T) {
var v map[issue342]int
in := []byte(`{"a":1}`)
if err := json.Unmarshal(in, &v); err != nil {
t.Errorf("unexpected error: %v", err)
}
expected := 1
if got := v["a"]; got != expected {
t.Errorf("unexpected result: got(%v) != expected(%v)", got, expected)
}
}
func TestIssue360(t *testing.T) {
var uints []uint8
err := json.Unmarshal([]byte(`[0, 1, 2]`), &uints)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if len(uints) != 3 || !(uints[0] == 0 && uints[1] == 1 && uints[2] == 2) {
t.Errorf("unexpected result: %v", uints)
}
}
func TestIssue359(t *testing.T) {
var a interface{} = 1
var b interface{} = &a
var c interface{} = &b
v, err := json.Marshal(c)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if string(v) != "1" {
t.Errorf("unexpected result: %v", string(v))
}
}
func TestIssue364(t *testing.T) {
var v struct {
Description string `json:"description"`
}
err := json.Unmarshal([]byte(`{"description":"\uD83D\uDE87 Toledo is a metro station"}`), &v)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if v.Description != "🚇 Toledo is a metro station" {
t.Errorf("unexpected result: %v", v.Description)
}
}
func TestIssue362(t *testing.T) {
type AliasedPrimitive int
type Combiner struct {
SomeField int
AliasedPrimitive
}
originalCombiner := Combiner{AliasedPrimitive: 7}
b, err := json.Marshal(originalCombiner)
assertErr(t, err)
newCombiner := Combiner{}
err = json.Unmarshal(b, &newCombiner)
assertErr(t, err)
assertEq(t, "TestEmbeddedPrimitiveAlias", originalCombiner, newCombiner)
}
func TestIssue335(t *testing.T) {
var v []string
in := []byte(`["\u","A"]`)
err := json.Unmarshal(in, &v)
if err == nil {
t.Errorf("unexpected success")
}
}
func TestIssue372(t *testing.T) {
type A int
type T struct {
_ int
*A
}
var v T
err := json.Unmarshal([]byte(`{"A":7}`), &v)
assertErr(t, err)
got := *v.A
expected := A(7)
if got != expected {
t.Errorf("unexpected result: %v != %v", got, expected)
}
}
type issue384 struct{}
func (t *issue384) UnmarshalJSON(b []byte) error {
return nil
}
func TestIssue384(t *testing.T) {
testcases := []string{
`{"data": "` + strings.Repeat("-", 500) + `\""}`,
`["` + strings.Repeat("-", 508) + `\""]`,
}
for _, tc := range testcases {
dec := json.NewDecoder(strings.NewReader(tc))
var v issue384
if err := dec.Decode(&v); err != nil {
t.Errorf("unexpected error: %v", err)
}
}
}
func TestIssue408(t *testing.T) {
type T struct {
Arr [2]int32 `json:"arr"`
}
var v T
if err := json.Unmarshal([]byte(`{"arr": [1,2]}`), &v); err != nil {
t.Fatal(err)
}
}

View File

@ -1,7 +1,7 @@
version: '2'
services:
go-json:
image: golang:1.18
image: golang:1.17
volumes:
- '.:/go/src/go-json'
deploy:

View File

@ -3,7 +3,6 @@ package json
import (
"context"
"io"
"os"
"unsafe"
"github.com/goccy/go-json/internal/encoder"
@ -62,8 +61,6 @@ func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, o
if e.enabledHTMLEscape {
ctx.Option.Flag |= encoder.HTMLEscapeOption
}
ctx.Option.Flag |= encoder.NormalizeUTF8Option
ctx.Option.DebugOut = os.Stdout
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
@ -114,7 +111,7 @@ func (e *Encoder) SetIndent(prefix, indent string) {
func marshalContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
rctx := encoder.TakeRuntimeContext()
rctx.Option.Flag = 0
rctx.Option.Flag = encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option | encoder.ContextOption
rctx.Option.Flag = encoder.HTMLEscapeOption | encoder.ContextOption
rctx.Option.Context = ctx
for _, optFunc := range optFuncs {
optFunc(rctx.Option)
@ -142,7 +139,7 @@ func marshal(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
ctx := encoder.TakeRuntimeContext()
ctx.Option.Flag = 0
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option)
ctx.Option.Flag |= encoder.HTMLEscapeOption
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
@ -169,7 +166,7 @@ func marshalNoEscape(v interface{}) ([]byte, error) {
ctx := encoder.TakeRuntimeContext()
ctx.Option.Flag = 0
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option)
ctx.Option.Flag |= encoder.HTMLEscapeOption
buf, err := encodeNoEscape(ctx, v)
if err != nil {
@ -193,7 +190,7 @@ func marshalIndent(v interface{}, prefix, indent string, optFuncs ...EncodeOptio
ctx := encoder.TakeRuntimeContext()
ctx.Option.Flag = 0
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.NormalizeUTF8Option | encoder.IndentOption)
ctx.Option.Flag |= (encoder.HTMLEscapeOption | encoder.IndentOption)
for _, optFunc := range optFuncs {
optFunc(ctx.Option)
}
@ -223,7 +220,7 @@ func encode(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error) {
typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
if err != nil {
return nil, err
}
@ -251,7 +248,7 @@ func encodeNoEscape(ctx *encoder.RuntimeContext, v interface{}) ([]byte, error)
typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
if err != nil {
return nil, err
}
@ -278,7 +275,7 @@ func encodeIndent(ctx *encoder.RuntimeContext, v interface{}, prefix, indent str
typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := encoder.CompileToGetCodeSet(ctx, typeptr)
codeSet, err := encoder.CompileToGetCodeSet(typeptr)
if err != nil {
return nil, err
}

View File

@ -7,14 +7,11 @@ import (
stdjson "encoding/json"
"errors"
"fmt"
"io"
"log"
"math"
"math/big"
"reflect"
"regexp"
"strconv"
"strings"
"testing"
"time"
@ -480,8 +477,7 @@ func TestDebugMode(t *testing.T) {
t.Fatal("expected error")
}
}()
var buf bytes.Buffer
json.MarshalWithOption(mustErrTypeForDebug{}, json.Debug(), json.DebugWith(&buf))
json.MarshalWithOption(mustErrTypeForDebug{}, json.Debug())
}
func TestIssue116(t *testing.T) {
@ -554,28 +550,20 @@ func Test_MarshalIndent(t *testing.T) {
prefix := "-"
indent := "\t"
t.Run("struct", func(t *testing.T) {
v := struct {
A int `json:"a"`
B uint `json:"b"`
C string `json:"c"`
D interface{} `json:"d"`
X int `json:"-"` // ignore field
a int `json:"aa"` // private field
bytes, err := json.MarshalIndent(struct {
A int `json:"a"`
B uint `json:"b"`
C string `json:"c"`
D int `json:"-"` // ignore field
a int `json:"aa"` // private field
}{
A: -1,
B: 1,
C: "hello world",
D: struct {
E bool `json:"e"`
}{
E: true,
},
}
expected, err := stdjson.MarshalIndent(v, prefix, indent)
}, prefix, indent)
assertErr(t, err)
got, err := json.MarshalIndent(v, prefix, indent)
assertErr(t, err)
assertEq(t, "struct", string(expected), string(got))
result := "{\n-\t\"a\": -1,\n-\t\"b\": 1,\n-\t\"c\": \"hello world\"\n-}"
assertEq(t, "struct", result, string(bytes))
})
t.Run("slice", func(t *testing.T) {
t.Run("[]int", func(t *testing.T) {
@ -1022,13 +1010,12 @@ func (u *unmarshalerText) UnmarshalText(b []byte) error {
}
func TestTextMarshalerMapKeysAreSorted(t *testing.T) {
data := map[unmarshalerText]int{
b, err := json.Marshal(map[unmarshalerText]int{
{"x", "y"}: 1,
{"y", "x"}: 2,
{"a", "z"}: 3,
{"z", "a"}: 4,
}
b, err := json.Marshal(data)
})
if err != nil {
t.Fatalf("Failed to Marshal text.Marshaler: %v", err)
}
@ -1036,14 +1023,6 @@ func TestTextMarshalerMapKeysAreSorted(t *testing.T) {
if string(b) != want {
t.Errorf("Marshal map with text.Marshaler keys: got %#q, want %#q", b, want)
}
b, err = stdjson.Marshal(data)
if err != nil {
t.Fatalf("Failed to std Marshal text.Marshaler: %v", err)
}
if string(b) != want {
t.Errorf("std Marshal map with text.Marshaler keys: got %#q, want %#q", b, want)
}
}
// https://golang.org/issue/33675
@ -2299,333 +2278,3 @@ func TestRecursivePtrHead(t *testing.T) {
t.Fatalf("failed to encode. expected %q but got %q", expected, got)
}
}
func TestMarshalIndent(t *testing.T) {
v := map[string]map[string]interface{}{
"a": {
"b": "1",
"c": map[string]interface{}{
"d": "1",
},
},
}
expected, err := stdjson.MarshalIndent(v, "", " ")
if err != nil {
t.Fatal(err)
}
got, err := json.MarshalIndent(v, "", " ")
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(expected, got) {
t.Fatalf("expected: %q but got %q", expected, got)
}
}
type issue318Embedded struct {
_ [64]byte
}
type issue318 struct {
issue318Embedded `json:"-"`
ID issue318MarshalText `json:"id,omitempty"`
}
type issue318MarshalText struct {
ID string
}
func (i issue318MarshalText) MarshalText() ([]byte, error) {
return []byte(i.ID), nil
}
func TestIssue318(t *testing.T) {
v := issue318{
ID: issue318MarshalText{ID: "1"},
}
b, err := json.Marshal(v)
if err != nil {
t.Fatal(err)
}
expected := `{"id":"1"}`
if string(b) != expected {
t.Fatalf("failed to encode. expected %s but got %s", expected, string(b))
}
}
type emptyStringMarshaler struct {
Value stringMarshaler `json:"value,omitempty"`
}
type stringMarshaler string
func (s stringMarshaler) MarshalJSON() ([]byte, error) {
return []byte(`"` + s + `"`), nil
}
func TestEmptyStringMarshaler(t *testing.T) {
value := emptyStringMarshaler{}
expected, err := stdjson.Marshal(value)
assertErr(t, err)
got, err := json.Marshal(value)
assertErr(t, err)
assertEq(t, "struct", string(expected), string(got))
}
func TestIssue324(t *testing.T) {
type T struct {
FieldA *string `json:"fieldA,omitempty"`
FieldB *string `json:"fieldB,omitempty"`
FieldC *bool `json:"fieldC"`
FieldD []string `json:"fieldD,omitempty"`
}
v := &struct {
Code string `json:"code"`
*T
}{
T: &T{},
}
var sv = "Test Field"
v.Code = "Test"
v.T.FieldB = &sv
expected, err := stdjson.Marshal(v)
if err != nil {
t.Fatal(err)
}
got, err := json.Marshal(v)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(expected, got) {
t.Fatalf("failed to encode. expected %q but got %q", expected, got)
}
}
func TestIssue339(t *testing.T) {
type T1 struct {
*big.Int
}
type T2 struct {
T1 T1 `json:"T1"`
}
v := T2{T1{Int: big.NewInt(10000)}}
b, err := json.Marshal(&v)
assertErr(t, err)
got := string(b)
expected := `{"T1":10000}`
if got != expected {
t.Errorf("unexpected result: %v != %v", got, expected)
}
}
func TestIssue376(t *testing.T) {
type Container struct {
V interface{} `json:"value"`
}
type MapOnly struct {
Map map[string]int64 `json:"map"`
}
b, err := json.Marshal(Container{MapOnly{}})
if err != nil {
t.Fatal(err)
}
got := string(b)
expected := `{"value":{"map":null}}`
if got != expected {
t.Errorf("unexpected result: %v != %v", got, expected)
}
}
type Issue370 struct {
String string
Valid bool
}
func (i *Issue370) MarshalJSON() ([]byte, error) {
if !i.Valid {
return json.Marshal(nil)
}
return json.Marshal(i.String)
}
func TestIssue370(t *testing.T) {
v := []struct {
V Issue370
}{
{V: Issue370{String: "test", Valid: true}},
}
b, err := json.Marshal(v)
if err != nil {
t.Fatal(err)
}
got := string(b)
expected := `[{"V":"test"}]`
if got != expected {
t.Errorf("unexpected result: %v != %v", got, expected)
}
}
func TestIssue374(t *testing.T) {
r := io.MultiReader(strings.NewReader(strings.Repeat(" ", 505)+`"\u`), strings.NewReader(`0000"`))
var v interface{}
if err := json.NewDecoder(r).Decode(&v); err != nil {
t.Fatal(err)
}
got := v.(string)
expected := "\u0000"
if got != expected {
t.Errorf("unexpected result: %q != %q", got, expected)
}
}
func TestIssue381(t *testing.T) {
var v struct {
Field0 bool
Field1 bool
Field2 bool
Field3 bool
Field4 bool
Field5 bool
Field6 bool
Field7 bool
Field8 bool
Field9 bool
Field10 bool
Field11 bool
Field12 bool
Field13 bool
Field14 bool
Field15 bool
Field16 bool
Field17 bool
Field18 bool
Field19 bool
Field20 bool
Field21 bool
Field22 bool
Field23 bool
Field24 bool
Field25 bool
Field26 bool
Field27 bool
Field28 bool
Field29 bool
Field30 bool
Field31 bool
Field32 bool
Field33 bool
Field34 bool
Field35 bool
Field36 bool
Field37 bool
Field38 bool
Field39 bool
Field40 bool
Field41 bool
Field42 bool
Field43 bool
Field44 bool
Field45 bool
Field46 bool
Field47 bool
Field48 bool
Field49 bool
Field50 bool
Field51 bool
Field52 bool
Field53 bool
Field54 bool
Field55 bool
Field56 bool
Field57 bool
Field58 bool
Field59 bool
Field60 bool
Field61 bool
Field62 bool
Field63 bool
Field64 bool
Field65 bool
Field66 bool
Field67 bool
Field68 bool
Field69 bool
Field70 bool
Field71 bool
Field72 bool
Field73 bool
Field74 bool
Field75 bool
Field76 bool
Field77 bool
Field78 bool
Field79 bool
Field80 bool
Field81 bool
Field82 bool
Field83 bool
Field84 bool
Field85 bool
Field86 bool
Field87 bool
Field88 bool
Field89 bool
Field90 bool
Field91 bool
Field92 bool
Field93 bool
Field94 bool
Field95 bool
Field96 bool
Field97 bool
Field98 bool
Field99 bool
}
// test encoder cache issue, not related to encoder
b, err := json.Marshal(v)
if err != nil {
t.Errorf("failed to marshal %s", err.Error())
t.FailNow()
}
std, err := stdjson.Marshal(v)
if err != nil {
t.Errorf("failed to marshal with encoding/json %s", err.Error())
t.FailNow()
}
if !bytes.Equal(std, b) {
t.Errorf("encoding result not equal to encoding/json")
t.FailNow()
}
}
func TestIssue386(t *testing.T) {
raw := `{"date": null, "platform": "\u6f2b\u753b", "images": {"small": "https://lain.bgm.tv/pic/cover/s/d2/a1/80048_jp.jpg", "grid": "https://lain.bgm.tv/pic/cover/g/d2/a1/80048_jp.jpg", "large": "https://lain.bgm.tv/pic/cover/l/d2/a1/80048_jp.jpg", "medium": "https://lain.bgm.tv/pic/cover/m/d2/a1/80048_jp.jpg", "common": "https://lain.bgm.tv/pic/cover/c/d2/a1/80048_jp.jpg"}, "summary": "\u5929\u624d\u8a2d\u8a08\u58eb\u30fb\u5929\u5bae\uff08\u3042\u307e\u307f\u3084\uff09\u3092\u62b1\u3048\u308b\u6751\u96e8\u7dcf\u5408\u4f01\u753b\u306f\u3001\u771f\u6c34\u5efa\u8a2d\u3068\u63d0\u643a\u3057\u3066\u300c\u3055\u304d\u305f\u307e\u30a2\u30ea\u30fc\u30ca\u300d\u306e\u30b3\u30f3\u30da\u306b\u512a\u52dd\u3059\u308b\u3053\u3068\u306b\u8ced\u3051\u3066\u3044\u305f\u3002\u3057\u304b\u3057\u3001\u73fe\u77e5\u4e8b\u306e\u6d25\u5730\u7530\uff08\u3064\u3061\u3060\uff09\u306f\u5927\u65e5\u5efa\u8a2d\u306b\u512a\u52dd\u3055\u305b\u3088\u3046\u3068\u6697\u8e8d\u3059\u308b\u3002\u305d\u308c\u306f\u73fe\u77e5\u4e8b\u306e\u6d25\u5730\u7530\u3068\u526f\u77e5\u4e8b\u306e\u592a\u7530\uff08\u304a\u304a\u305f\uff09\u306e\u653f\u6cbb\u751f\u547d\u3092\u5de6\u53f3\u3059\u308b\u4e89\u3044\u3068\u306a\u308a\u2026\u2026!?\u3000\u305d\u3057\u3066\u516c\u5171\u4e8b\u696d\u306b\u6e26\u5dfb\u304f\u6df1\u3044\u95c7\u306b\u842c\u7530\u9280\u6b21\u90ce\uff08\u307e\u3093\u3060\u30fb\u304e\u3093\u3058\u308d\u3046\uff09\u306f\u2026\u2026!?", "name": "\u30df\u30ca\u30df\u306e\u5e1d\u738b (48)"}`
var a struct {
Date *string `json:"date"`
Platform *string `json:"platform"`
Summary string `json:"summary"`
Name string `json:"name"`
}
err := json.NewDecoder(strings.NewReader(raw)).Decode(&a)
if err != nil {
t.Error(err)
}
}
type customMapKey string
func (b customMapKey) MarshalJSON() ([]byte, error) {
return []byte("[]"), nil
}
func TestCustomMarshalForMapKey(t *testing.T) {
m := map[customMapKey]string{customMapKey("skipcustom"): "test"}
expected, err := stdjson.Marshal(m)
assertErr(t, err)
got, err := json.Marshal(m)
assertErr(t, err)
assertEq(t, "custom map key", string(expected), string(got))
}

View File

@ -37,5 +37,3 @@ type UnmarshalTypeError = errors.UnmarshalTypeError
type UnsupportedTypeError = errors.UnsupportedTypeError
type UnsupportedValueError = errors.UnsupportedValueError
type PathError = errors.PathError

View File

@ -3,7 +3,6 @@ package vm
import (
"math"
"reflect"
"sort"
"unsafe"
@ -195,15 +194,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
typ = iface.typ
}
if ifacePtr == nil {
isDirectedNil := typ != nil && typ.Kind() == reflect.Struct && !runtime.IfaceIndir(typ)
if !isDirectedNil {
b = appendNullComma(ctx, b)
code = code.Next
break
}
b = appendNullComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(ctx, uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -222,7 +218,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent
indentDiffFromTop := c.Indent - 1
ctx.BaseIndent += code.Indent - indentDiffFromTop
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
@ -406,12 +403,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
break
}
b = appendStructHead(ctx, b)
unorderedMap := (ctx.Option.Flag & encoder.UnorderedMapOption) != 0
mapCtx := encoder.NewMapContext(mlen, unorderedMap)
mapCtx := encoder.NewMapContext(mlen)
mapiterinit(code.Type, uptr, &mapCtx.Iter)
store(ctxptr, code.Idx, uintptr(unsafe.Pointer(mapCtx)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
if unorderedMap {
if (ctx.Option.Flag & encoder.UnorderedMapOption) != 0 {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx.Start = len(b)
@ -709,15 +705,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
u64 := ptrToUint64(p, code.NumBitSize)
u64 := ptrToUint64(p+uintptr(code.Offset), code.NumBitSize)
v := u64 & ((1 << code.NumBitSize) - 1)
if v == 0 {
code = code.NextField
} else {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendInt(ctx, b, p, code)
b = appendInt(ctx, b, p+uintptr(code.Offset), code)
b = append(b, '"')
b = appendComma(ctx, b)
code = code.Next
@ -2958,10 +2953,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3000,10 +2994,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
iface := ptrToInterface(code, p)
@ -3121,10 +3114,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3163,10 +3155,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {

View File

@ -35,7 +35,3 @@ func (d *anonymousFieldDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
p = *(*unsafe.Pointer)(p)
return d.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset))
}
func (d *anonymousFieldDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return d.dec.DecodePath(ctx, cursor, depth)
}

View File

@ -1,7 +1,6 @@
package decoder
import (
"fmt"
"unsafe"
"github.com/goccy/go-json/internal/errors"
@ -19,9 +18,7 @@ type arrayDecoder struct {
}
func newArrayDecoder(dec Decoder, elemType *runtime.Type, alen int, structName, fieldName string) *arrayDecoder {
// workaround to avoid checkptr errors. cannot use `*(*unsafe.Pointer)(unsafe_New(elemType))` directly.
zeroValuePtr := unsafe_New(elemType)
zeroValue := **(**unsafe.Pointer)(unsafe.Pointer(&zeroValuePtr))
zeroValue := *(*unsafe.Pointer)(unsafe_New(elemType))
return &arrayDecoder{
valueDecoder: dec,
elemType: elemType,
@ -170,7 +167,3 @@ func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
}
}
}
func (d *arrayDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: array decoder does not support decode path")
}

View File

@ -1,438 +0,0 @@
package decoder
import (
"fmt"
"reflect"
"strconv"
)
var (
nilValue = reflect.ValueOf(nil)
)
func AssignValue(src, dst reflect.Value) error {
if dst.Type().Kind() != reflect.Ptr {
return fmt.Errorf("invalid dst type. required pointer type: %T", dst.Type())
}
casted, err := castValue(dst.Elem().Type(), src)
if err != nil {
return err
}
dst.Elem().Set(casted)
return nil
}
func castValue(t reflect.Type, v reflect.Value) (reflect.Value, error) {
switch t.Kind() {
case reflect.Int:
vv, err := castInt(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(int(vv.Int())), nil
case reflect.Int8:
vv, err := castInt(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(int8(vv.Int())), nil
case reflect.Int16:
vv, err := castInt(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(int16(vv.Int())), nil
case reflect.Int32:
vv, err := castInt(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(int32(vv.Int())), nil
case reflect.Int64:
return castInt(v)
case reflect.Uint:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uint(vv.Uint())), nil
case reflect.Uint8:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uint8(vv.Uint())), nil
case reflect.Uint16:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uint16(vv.Uint())), nil
case reflect.Uint32:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uint32(vv.Uint())), nil
case reflect.Uint64:
return castUint(v)
case reflect.Uintptr:
vv, err := castUint(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(uintptr(vv.Uint())), nil
case reflect.String:
return castString(v)
case reflect.Bool:
return castBool(v)
case reflect.Float32:
vv, err := castFloat(v)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(float32(vv.Float())), nil
case reflect.Float64:
return castFloat(v)
case reflect.Array:
return castArray(t, v)
case reflect.Slice:
return castSlice(t, v)
case reflect.Map:
return castMap(t, v)
case reflect.Struct:
return castStruct(t, v)
}
return v, nil
}
func castInt(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return reflect.ValueOf(int64(v.Uint())), nil
case reflect.String:
i64, err := strconv.ParseInt(v.String(), 10, 64)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(i64), nil
case reflect.Bool:
if v.Bool() {
return reflect.ValueOf(int64(1)), nil
}
return reflect.ValueOf(int64(0)), nil
case reflect.Float32, reflect.Float64:
return reflect.ValueOf(int64(v.Float())), nil
case reflect.Array:
if v.Len() > 0 {
return castInt(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to int64 from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castInt(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to int64 from empty slice")
case reflect.Interface:
return castInt(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to int64 from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to int64 from struct")
case reflect.Ptr:
return castInt(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to int64 from %s", v.Type().Kind())
}
func castUint(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return reflect.ValueOf(uint64(v.Int())), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v, nil
case reflect.String:
u64, err := strconv.ParseUint(v.String(), 10, 64)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(u64), nil
case reflect.Bool:
if v.Bool() {
return reflect.ValueOf(uint64(1)), nil
}
return reflect.ValueOf(uint64(0)), nil
case reflect.Float32, reflect.Float64:
return reflect.ValueOf(uint64(v.Float())), nil
case reflect.Array:
if v.Len() > 0 {
return castUint(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to uint64 from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castUint(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to uint64 from empty slice")
case reflect.Interface:
return castUint(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to uint64 from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to uint64 from struct")
case reflect.Ptr:
return castUint(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to uint64 from %s", v.Type().Kind())
}
func castString(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return reflect.ValueOf(fmt.Sprint(v.Int())), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return reflect.ValueOf(fmt.Sprint(v.Uint())), nil
case reflect.String:
return v, nil
case reflect.Bool:
if v.Bool() {
return reflect.ValueOf("true"), nil
}
return reflect.ValueOf("false"), nil
case reflect.Float32, reflect.Float64:
return reflect.ValueOf(fmt.Sprint(v.Float())), nil
case reflect.Array:
if v.Len() > 0 {
return castString(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to string from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castString(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to string from empty slice")
case reflect.Interface:
return castString(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to string from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to string from struct")
case reflect.Ptr:
return castString(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to string from %s", v.Type().Kind())
}
func castBool(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch v.Int() {
case 0:
return reflect.ValueOf(false), nil
case 1:
return reflect.ValueOf(true), nil
}
return nilValue, fmt.Errorf("failed to cast to bool from %d", v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
switch v.Uint() {
case 0:
return reflect.ValueOf(false), nil
case 1:
return reflect.ValueOf(true), nil
}
return nilValue, fmt.Errorf("failed to cast to bool from %d", v.Uint())
case reflect.String:
b, err := strconv.ParseBool(v.String())
if err != nil {
return nilValue, err
}
return reflect.ValueOf(b), nil
case reflect.Bool:
return v, nil
case reflect.Float32, reflect.Float64:
switch v.Float() {
case 0:
return reflect.ValueOf(false), nil
case 1:
return reflect.ValueOf(true), nil
}
return nilValue, fmt.Errorf("failed to cast to bool from %f", v.Float())
case reflect.Array:
if v.Len() > 0 {
return castBool(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to string from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castBool(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to string from empty slice")
case reflect.Interface:
return castBool(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to string from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to string from struct")
case reflect.Ptr:
return castBool(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to bool from %s", v.Type().Kind())
}
func castFloat(v reflect.Value) (reflect.Value, error) {
switch v.Type().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return reflect.ValueOf(float64(v.Int())), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return reflect.ValueOf(float64(v.Uint())), nil
case reflect.String:
f64, err := strconv.ParseFloat(v.String(), 64)
if err != nil {
return nilValue, err
}
return reflect.ValueOf(f64), nil
case reflect.Bool:
if v.Bool() {
return reflect.ValueOf(float64(1)), nil
}
return reflect.ValueOf(float64(0)), nil
case reflect.Float32, reflect.Float64:
return v, nil
case reflect.Array:
if v.Len() > 0 {
return castFloat(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to float64 from empty array")
case reflect.Slice:
if v.Len() > 0 {
return castFloat(v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to float64 from empty slice")
case reflect.Interface:
return castFloat(reflect.ValueOf(v.Interface()))
case reflect.Map:
return nilValue, fmt.Errorf("failed to cast to float64 from map")
case reflect.Struct:
return nilValue, fmt.Errorf("failed to cast to float64 from struct")
case reflect.Ptr:
return castFloat(v.Elem())
}
return nilValue, fmt.Errorf("failed to cast to float64 from %s", v.Type().Kind())
}
func castArray(t reflect.Type, v reflect.Value) (reflect.Value, error) {
kind := v.Type().Kind()
if kind == reflect.Interface {
return castArray(t, reflect.ValueOf(v.Interface()))
}
if kind != reflect.Slice && kind != reflect.Array {
return nilValue, fmt.Errorf("failed to cast to array from %s", kind)
}
if t.Elem() == v.Type().Elem() {
return v, nil
}
if t.Len() != v.Len() {
return nilValue, fmt.Errorf("failed to cast [%d]array from slice of %d length", t.Len(), v.Len())
}
ret := reflect.New(t).Elem()
for i := 0; i < v.Len(); i++ {
vv, err := castValue(t.Elem(), v.Index(i))
if err != nil {
return nilValue, err
}
ret.Index(i).Set(vv)
}
return ret, nil
}
func castSlice(t reflect.Type, v reflect.Value) (reflect.Value, error) {
kind := v.Type().Kind()
if kind == reflect.Interface {
return castSlice(t, reflect.ValueOf(v.Interface()))
}
if kind != reflect.Slice && kind != reflect.Array {
return nilValue, fmt.Errorf("failed to cast to slice from %s", kind)
}
if t.Elem() == v.Type().Elem() {
return v, nil
}
ret := reflect.MakeSlice(t, v.Len(), v.Len())
for i := 0; i < v.Len(); i++ {
vv, err := castValue(t.Elem(), v.Index(i))
if err != nil {
return nilValue, err
}
ret.Index(i).Set(vv)
}
return ret, nil
}
func castMap(t reflect.Type, v reflect.Value) (reflect.Value, error) {
ret := reflect.MakeMap(t)
switch v.Type().Kind() {
case reflect.Map:
iter := v.MapRange()
for iter.Next() {
key, err := castValue(t.Key(), iter.Key())
if err != nil {
return nilValue, err
}
value, err := castValue(t.Elem(), iter.Value())
if err != nil {
return nilValue, err
}
ret.SetMapIndex(key, value)
}
return ret, nil
case reflect.Interface:
return castMap(t, reflect.ValueOf(v.Interface()))
case reflect.Slice:
if v.Len() > 0 {
return castMap(t, v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to map from empty slice")
}
return nilValue, fmt.Errorf("failed to cast to map from %s", v.Type().Kind())
}
func castStruct(t reflect.Type, v reflect.Value) (reflect.Value, error) {
ret := reflect.New(t).Elem()
switch v.Type().Kind() {
case reflect.Map:
iter := v.MapRange()
for iter.Next() {
key := iter.Key()
k, err := castString(key)
if err != nil {
return nilValue, err
}
fieldName := k.String()
field, ok := t.FieldByName(fieldName)
if ok {
value, err := castValue(field.Type, iter.Value())
if err != nil {
return nilValue, err
}
ret.FieldByName(fieldName).Set(value)
}
}
return ret, nil
case reflect.Struct:
for i := 0; i < v.Type().NumField(); i++ {
name := v.Type().Field(i).Name
ret.FieldByName(name).Set(v.FieldByName(name))
}
return ret, nil
case reflect.Interface:
return castStruct(t, reflect.ValueOf(v.Interface()))
case reflect.Slice:
if v.Len() > 0 {
return castStruct(t, v.Index(0))
}
return nilValue, fmt.Errorf("failed to cast to struct from empty slice")
default:
return nilValue, fmt.Errorf("failed to cast to struct from %s", v.Type().Kind())
}
}

View File

@ -1,7 +1,6 @@
package decoder
import (
"fmt"
"unsafe"
"github.com/goccy/go-json/internal/errors"
@ -77,7 +76,3 @@ func (d *boolDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.
}
return 0, errors.ErrUnexpectedEndOfJSON("bool", cursor)
}
func (d *boolDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: bool decoder does not support decode path")
}

View File

@ -2,7 +2,6 @@ package decoder
import (
"encoding/base64"
"fmt"
"unsafe"
"github.com/goccy/go-json/internal/errors"
@ -24,8 +23,9 @@ func byteUnmarshalerSliceDecoder(typ *runtime.Type, structName string, fieldName
unmarshalDecoder = newUnmarshalJSONDecoder(runtime.PtrTo(typ), structName, fieldName)
case runtime.PtrTo(typ).Implements(unmarshalTextType):
unmarshalDecoder = newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName)
default:
unmarshalDecoder, _ = compileUint8(typ, structName, fieldName)
}
if unmarshalDecoder == nil {
return nil
}
return newSliceDecoder(unmarshalDecoder, typ, 1, structName, fieldName)
}
@ -79,10 +79,6 @@ func (d *bytesDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
return cursor, nil
}
func (d *bytesDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: []byte decoder does not support decode path")
}
func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Pointer) ([]byte, error) {
c := s.skipWhiteSpace()
if c == '[' {

View File

@ -9,6 +9,7 @@ import (
"unicode"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
@ -24,7 +25,7 @@ func init() {
if typeAddr == nil {
typeAddr = &runtime.TypeAddr{}
}
cachedDecoder = make([]Decoder, typeAddr.AddrRange>>typeAddr.AddrShift+1)
cachedDecoder = make([]Decoder, typeAddr.AddrRange>>typeAddr.AddrShift)
}
func loadDecoderMap() map[uintptr]Decoder {
@ -125,7 +126,13 @@ func compile(typ *runtime.Type, structName, fieldName string, structTypeToDecode
case reflect.Func:
return compileFunc(typ, structName, fieldName)
}
return newInvalidDecoder(typ, structName, fieldName), nil
return nil, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(typ),
Offset: 0,
Struct: structName,
Field: fieldName,
}
}
func isStringTagSupportedType(typ *runtime.Type) bool {
@ -154,9 +161,6 @@ func compileMapKey(typ *runtime.Type, structName, fieldName string, structTypeTo
if runtime.PtrTo(typ).Implements(unmarshalTextType) {
return newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName), nil
}
if typ.Kind() == reflect.String {
return newStringDecoder(structName, fieldName), nil
}
dec, err := compile(typ, structName, fieldName, structTypeToDecoder)
if err != nil {
return nil, err
@ -170,9 +174,17 @@ func compileMapKey(typ *runtime.Type, structName, fieldName string, structTypeTo
case *ptrDecoder:
dec = t.dec
default:
return newInvalidDecoder(typ, structName, fieldName), nil
goto ERROR
}
}
ERROR:
return nil, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(typ),
Offset: 0,
Struct: structName,
Field: fieldName,
}
}
func compilePtr(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
@ -310,21 +322,64 @@ func compileFunc(typ *runtime.Type, strutName, fieldName string) (Decoder, error
return newFuncDecoder(typ, strutName, fieldName), nil
}
func typeToStructTags(typ *runtime.Type) runtime.StructTags {
tags := runtime.StructTags{}
fieldNum := typ.NumField()
for i := 0; i < fieldNum; i++ {
field := typ.Field(i)
if runtime.IsIgnoredStructField(field) {
func removeConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, field reflect.StructField) {
for k, v := range dec.fieldMap {
if _, exists := conflictedMap[k]; exists {
// already conflicted key
continue
}
tags = append(tags, runtime.StructTagFromField(field))
set, exists := fieldMap[k]
if !exists {
fieldSet := &structFieldSet{
dec: v.dec,
offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
continue
}
if set.isTaggedKey {
if v.isTaggedKey {
// conflict tag key
delete(fieldMap, k)
delete(fieldMap, strings.ToLower(k))
conflictedMap[k] = struct{}{}
conflictedMap[strings.ToLower(k)] = struct{}{}
}
} else {
if v.isTaggedKey {
fieldSet := &structFieldSet{
dec: v.dec,
offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
} else {
// conflict tag key
delete(fieldMap, k)
delete(fieldMap, strings.ToLower(k))
conflictedMap[k] = struct{}{}
conflictedMap[strings.ToLower(k)] = struct{}{}
}
}
}
return tags
}
func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
fieldNum := typ.NumField()
conflictedMap := map[string]struct{}{}
fieldMap := map[string]*structFieldSet{}
typeptr := uintptr(unsafe.Pointer(typ))
if dec, exists := structTypeToDecoder[typeptr]; exists {
@ -333,8 +388,6 @@ func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeTo
structDec := newStructDecoder(structName, fieldName, fieldMap)
structTypeToDecoder[typeptr] = structDec
structName = typ.Name()
tags := typeToStructTags(typ)
allFields := []*structFieldSet{}
for i := 0; i < fieldNum; i++ {
field := typ.Field(i)
if runtime.IsIgnoredStructField(field) {
@ -352,19 +405,7 @@ func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeTo
// recursive definition
continue
}
for k, v := range stDec.fieldMap {
if tags.ExistsKey(k) {
continue
}
fieldSet := &structFieldSet{
dec: v.dec,
offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
}
allFields = append(allFields, fieldSet)
}
removeConflictFields(fieldMap, conflictedMap, stDec, field)
} else if pdec, ok := dec.(*ptrDecoder); ok {
contentDec := pdec.contentDecoder()
if pdec.typ == typ {
@ -380,38 +421,60 @@ func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeTo
}
if dec, ok := contentDec.(*structDecoder); ok {
for k, v := range dec.fieldMap {
if tags.ExistsKey(k) {
if _, exists := conflictedMap[k]; exists {
// already conflicted key
continue
}
fieldSet := &structFieldSet{
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
offset: field.Offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
err: fieldSetErr,
set, exists := fieldMap[k]
if !exists {
fieldSet := &structFieldSet{
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
offset: field.Offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
err: fieldSetErr,
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
continue
}
if set.isTaggedKey {
if v.isTaggedKey {
// conflict tag key
delete(fieldMap, k)
delete(fieldMap, strings.ToLower(k))
conflictedMap[k] = struct{}{}
conflictedMap[strings.ToLower(k)] = struct{}{}
}
} else {
if v.isTaggedKey {
fieldSet := &structFieldSet{
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
offset: field.Offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
err: fieldSetErr,
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
} else {
// conflict tag key
delete(fieldMap, k)
delete(fieldMap, strings.ToLower(k))
conflictedMap[k] = struct{}{}
conflictedMap[strings.ToLower(k)] = struct{}{}
}
}
allFields = append(allFields, fieldSet)
}
} else {
fieldSet := &structFieldSet{
dec: pdec,
offset: field.Offset,
isTaggedKey: tag.IsTaggedKey,
key: field.Name,
keyLen: int64(len(field.Name)),
}
allFields = append(allFields, fieldSet)
}
} else {
fieldSet := &structFieldSet{
dec: dec,
offset: field.Offset,
isTaggedKey: tag.IsTaggedKey,
key: field.Name,
keyLen: int64(len(field.Name)),
}
allFields = append(allFields, fieldSet)
}
} else {
if tag.IsString && isStringTagSupportedType(runtime.Type2RType(field.Type)) {
@ -430,15 +493,11 @@ func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeTo
key: key,
keyLen: int64(len(key)),
}
allFields = append(allFields, fieldSet)
}
}
for _, set := range filterDuplicatedFields(allFields) {
fieldMap[set.key] = set
lower := strings.ToLower(set.key)
if _, exists := fieldMap[lower]; !exists {
// first win
fieldMap[lower] = set
fieldMap[key] = fieldSet
lower := strings.ToLower(key)
if _, exists := fieldMap[lower]; !exists {
fieldMap[lower] = fieldSet
}
}
}
delete(structTypeToDecoder, typeptr)
@ -446,42 +505,6 @@ func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeTo
return structDec, nil
}
func filterDuplicatedFields(allFields []*structFieldSet) []*structFieldSet {
fieldMap := map[string][]*structFieldSet{}
for _, field := range allFields {
fieldMap[field.key] = append(fieldMap[field.key], field)
}
duplicatedFieldMap := map[string]struct{}{}
for k, sets := range fieldMap {
sets = filterFieldSets(sets)
if len(sets) != 1 {
duplicatedFieldMap[k] = struct{}{}
}
}
filtered := make([]*structFieldSet, 0, len(allFields))
for _, field := range allFields {
if _, exists := duplicatedFieldMap[field.key]; exists {
continue
}
filtered = append(filtered, field)
}
return filtered
}
func filterFieldSets(sets []*structFieldSet) []*structFieldSet {
if len(sets) == 1 {
return sets
}
filtered := make([]*structFieldSet, 0, len(sets))
for _, set := range sets {
if set.isTaggedKey {
filtered = append(filtered, set)
}
}
return filtered
}
func implementsUnmarshalJSONType(typ *runtime.Type) bool {
return typ.Implements(unmarshalJSONType) || typ.Implements(unmarshalJSONContextType)
}

View File

@ -1,4 +1,3 @@
//go:build !race
// +build !race
package decoder

View File

@ -1,4 +1,3 @@
//go:build race
// +build race
package decoder

View File

@ -156,15 +156,3 @@ func (d *floatDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
d.op(p, f64)
return cursor, nil
}
func (d *floatDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
buf := ctx.Buf
bytes, c, err := d.decodeByte(buf, cursor)
if err != nil {
return nil, 0, err
}
if bytes == nil {
return [][]byte{nullbytes}, c, nil
}
return [][]byte{bytes}, c, nil
}

View File

@ -2,7 +2,6 @@ package decoder
import (
"bytes"
"fmt"
"unsafe"
"github.com/goccy/go-json/internal/errors"
@ -140,7 +139,3 @@ func (d *funcDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.
}
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
}
func (d *funcDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: func decoder does not support decode path")
}

View File

@ -192,15 +192,15 @@ func (d *intDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro
}
switch d.kind {
case reflect.Int8:
if i64 < -1*(1<<7) || (1<<7) <= i64 {
if i64 <= -1*(1<<7) || (1<<7) <= i64 {
return d.typeError(bytes, s.totalOffset())
}
case reflect.Int16:
if i64 < -1*(1<<15) || (1<<15) <= i64 {
if i64 <= -1*(1<<15) || (1<<15) <= i64 {
return d.typeError(bytes, s.totalOffset())
}
case reflect.Int32:
if i64 < -1*(1<<31) || (1<<31) <= i64 {
if i64 <= -1*(1<<31) || (1<<31) <= i64 {
return d.typeError(bytes, s.totalOffset())
}
}
@ -225,22 +225,18 @@ func (d *intDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P
}
switch d.kind {
case reflect.Int8:
if i64 < -1*(1<<7) || (1<<7) <= i64 {
if i64 <= -1*(1<<7) || (1<<7) <= i64 {
return 0, d.typeError(bytes, cursor)
}
case reflect.Int16:
if i64 < -1*(1<<15) || (1<<15) <= i64 {
if i64 <= -1*(1<<15) || (1<<15) <= i64 {
return 0, d.typeError(bytes, cursor)
}
case reflect.Int32:
if i64 < -1*(1<<31) || (1<<31) <= i64 {
if i64 <= -1*(1<<31) || (1<<31) <= i64 {
return 0, d.typeError(bytes, cursor)
}
}
d.op(p, i64)
return cursor, nil
}
func (d *intDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: int decoder does not support decode path")
}

View File

@ -94,7 +94,6 @@ func (d *interfaceDecoder) numDecoder(s *Stream) Decoder {
var (
emptyInterfaceType = runtime.Type2RType(reflect.TypeOf((*interface{})(nil)).Elem())
EmptyInterfaceType = emptyInterfaceType
interfaceMapType = runtime.Type2RType(
reflect.TypeOf((*map[string]interface{})(nil)).Elem(),
)
@ -457,72 +456,3 @@ func (d *interfaceDecoder) decodeEmptyInterface(ctx *RuntimeContext, cursor, dep
}
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
}
func NewPathDecoder() Decoder {
ifaceDecoder := &interfaceDecoder{
typ: emptyInterfaceType,
structName: "",
fieldName: "",
floatDecoder: newFloatDecoder("", "", func(p unsafe.Pointer, v float64) {
*(*interface{})(p) = v
}),
numberDecoder: newNumberDecoder("", "", func(p unsafe.Pointer, v json.Number) {
*(*interface{})(p) = v
}),
stringDecoder: newStringDecoder("", ""),
}
ifaceDecoder.sliceDecoder = newSliceDecoder(
ifaceDecoder,
emptyInterfaceType,
emptyInterfaceType.Size(),
"", "",
)
ifaceDecoder.mapDecoder = newMapDecoder(
interfaceMapType,
stringType,
ifaceDecoder.stringDecoder,
interfaceMapType.Elem(),
ifaceDecoder,
"", "",
)
return ifaceDecoder
}
var (
truebytes = []byte("true")
falsebytes = []byte("false")
)
func (d *interfaceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
buf := ctx.Buf
cursor = skipWhiteSpace(buf, cursor)
switch buf[cursor] {
case '{':
return d.mapDecoder.DecodePath(ctx, cursor, depth)
case '[':
return d.sliceDecoder.DecodePath(ctx, cursor, depth)
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return d.floatDecoder.DecodePath(ctx, cursor, depth)
case '"':
return d.stringDecoder.DecodePath(ctx, cursor, depth)
case 't':
if err := validateTrue(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return [][]byte{truebytes}, cursor, nil
case 'f':
if err := validateFalse(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 5
return [][]byte{falsebytes}, cursor, nil
case 'n':
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return [][]byte{nullbytes}, cursor, nil
}
return nil, cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
}

View File

@ -1,55 +0,0 @@
package decoder
import (
"reflect"
"unsafe"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type invalidDecoder struct {
typ *runtime.Type
kind reflect.Kind
structName string
fieldName string
}
func newInvalidDecoder(typ *runtime.Type, structName, fieldName string) *invalidDecoder {
return &invalidDecoder{
typ: typ,
kind: typ.Kind(),
structName: structName,
fieldName: fieldName,
}
}
func (d *invalidDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
return &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(d.typ),
Offset: s.totalOffset(),
Struct: d.structName,
Field: d.fieldName,
}
}
func (d *invalidDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
return 0, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(d.typ),
Offset: cursor,
Struct: d.structName,
Field: d.fieldName,
}
}
func (d *invalidDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, &errors.UnmarshalTypeError{
Value: "object",
Type: runtime.RType2Type(d.typ),
Offset: cursor,
Struct: d.structName,
Field: d.fieldName,
}
}

View File

@ -87,13 +87,13 @@ func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro
if mapValue == nil {
mapValue = makemap(d.mapType, 0)
}
s.cursor++
if s.equalChar('}') {
if s.buf[s.cursor+1] == '}' {
*(*unsafe.Pointer)(p) = mapValue
s.cursor++
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
@ -117,7 +117,6 @@ func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro
if !s.equalChar(',') {
return errors.ErrExpected("comma after object value", s.totalOffset())
}
s.cursor++
}
}
@ -185,96 +184,3 @@ func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P
cursor++
}
}
func (d *mapDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
buf := ctx.Buf
depth++
if depth > maxDecodeNestingDepth {
return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
cursor = skipWhiteSpace(buf, cursor)
buflen := int64(len(buf))
if buflen < 2 {
return nil, 0, errors.ErrExpected("{} for map", cursor)
}
switch buf[cursor] {
case 'n':
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return [][]byte{nullbytes}, cursor, nil
case '{':
default:
return nil, 0, errors.ErrExpected("{ character for map value", cursor)
}
cursor++
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == '}' {
cursor++
return nil, cursor, nil
}
keyDecoder, ok := d.keyDecoder.(*stringDecoder)
if !ok {
return nil, 0, &errors.UnmarshalTypeError{
Value: "string",
Type: reflect.TypeOf(""),
Offset: cursor,
Struct: d.structName,
Field: d.fieldName,
}
}
ret := [][]byte{}
for {
key, keyCursor, err := keyDecoder.decodeByte(buf, cursor)
if err != nil {
return nil, 0, err
}
cursor = skipWhiteSpace(buf, keyCursor)
if buf[cursor] != ':' {
return nil, 0, errors.ErrExpected("colon after object key", cursor)
}
cursor++
child, found, err := ctx.Option.Path.Field(string(key))
if err != nil {
return nil, 0, err
}
if found {
if child != nil {
oldPath := ctx.Option.Path.node
ctx.Option.Path.node = child
paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth)
if err != nil {
return nil, 0, err
}
ctx.Option.Path.node = oldPath
ret = append(ret, paths...)
cursor = c
} else {
start := cursor
end, err := skipValue(buf, cursor, depth)
if err != nil {
return nil, 0, err
}
ret = append(ret, buf[start:end])
cursor = end
}
} else {
c, err := skipValue(buf, cursor, depth)
if err != nil {
return nil, 0, err
}
cursor = c
}
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == '}' {
cursor++
return ret, cursor, nil
}
if buf[cursor] != ',' {
return nil, 0, errors.ErrExpected("comma after object value", cursor)
}
cursor++
}
}

View File

@ -51,17 +51,6 @@ func (d *numberDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
return cursor, nil
}
func (d *numberDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
if err != nil {
return nil, 0, err
}
if bytes == nil {
return [][]byte{nullbytes}, c, nil
}
return [][]byte{bytes}, c, nil
}
func (d *numberDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
start := s.cursor
for {

View File

@ -7,11 +7,9 @@ type OptionFlags uint8
const (
FirstWinOption OptionFlags = 1 << iota
ContextOption
PathOption
)
type Option struct {
Flags OptionFlags
Context context.Context
Path *Path
}

View File

@ -1,670 +0,0 @@
package decoder
import (
"fmt"
"reflect"
"strconv"
"github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime"
)
type PathString string
func (s PathString) Build() (*Path, error) {
builder := new(PathBuilder)
return builder.Build([]rune(s))
}
type PathBuilder struct {
root PathNode
node PathNode
singleQuotePathSelector bool
doubleQuotePathSelector bool
}
func (b *PathBuilder) Build(buf []rune) (*Path, error) {
node, err := b.build(buf)
if err != nil {
return nil, err
}
return &Path{
node: node,
RootSelectorOnly: node == nil,
SingleQuotePathSelector: b.singleQuotePathSelector,
DoubleQuotePathSelector: b.doubleQuotePathSelector,
}, nil
}
func (b *PathBuilder) build(buf []rune) (PathNode, error) {
if len(buf) == 0 {
return nil, errors.ErrEmptyPath()
}
if buf[0] != '$' {
return nil, errors.ErrInvalidPath("JSON Path must start with a $ character")
}
if len(buf) == 1 {
return nil, nil
}
buf = buf[1:]
offset, err := b.buildNext(buf)
if err != nil {
return nil, err
}
if len(buf) > offset {
return nil, errors.ErrInvalidPath("remain invalid path %q", buf[offset:])
}
return b.root, nil
}
func (b *PathBuilder) buildNextCharIfExists(buf []rune, cursor int) (int, error) {
if len(buf) > cursor {
offset, err := b.buildNext(buf[cursor:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
}
return cursor, nil
}
func (b *PathBuilder) buildNext(buf []rune) (int, error) {
switch buf[0] {
case '.':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
}
offset, err := b.buildSelector(buf[1:])
if err != nil {
return 0, err
}
return offset + 1, nil
case '[':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
}
offset, err := b.buildIndex(buf[1:])
if err != nil {
return 0, err
}
return offset + 1, nil
default:
return 0, errors.ErrInvalidPath("expect dot or left bracket character. but found %c character", buf[0])
}
}
func (b *PathBuilder) buildSelector(buf []rune) (int, error) {
switch buf[0] {
case '.':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with double dot character")
}
offset, err := b.buildPathRecursive(buf[1:])
if err != nil {
return 0, err
}
return 1 + offset, nil
case '[', ']', '$', '*':
return 0, errors.ErrInvalidPath("found invalid path character %c after dot", buf[0])
}
for cursor := 0; cursor < len(buf); cursor++ {
switch buf[cursor] {
case '$', '*', ']':
return 0, errors.ErrInvalidPath("found %c character in field selector context", buf[cursor])
case '.':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
}
selector := buf[:cursor]
b.addSelectorNode(string(selector))
offset, err := b.buildSelector(buf[cursor+1:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
case '[':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
}
selector := buf[:cursor]
b.addSelectorNode(string(selector))
offset, err := b.buildIndex(buf[cursor+1:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
case '"':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with double quote character")
}
offset, err := b.buildQuoteSelector(buf[cursor+1:], DoubleQuotePathSelector)
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
}
}
b.addSelectorNode(string(buf))
return len(buf), nil
}
func (b *PathBuilder) buildQuoteSelector(buf []rune, sel QuotePathSelector) (int, error) {
switch buf[0] {
case '[', ']', '$', '.', '*', '\'', '"':
return 0, errors.ErrInvalidPath("found invalid path character %c after quote", buf[0])
}
for cursor := 0; cursor < len(buf); cursor++ {
switch buf[cursor] {
case '\'':
if sel != SingleQuotePathSelector {
return 0, errors.ErrInvalidPath("found double quote character in field selector with single quote context")
}
if len(buf) <= cursor+1 {
return 0, errors.ErrInvalidPath("JSON Path ends with single quote character in field selector context")
}
if buf[cursor+1] != ']' {
return 0, errors.ErrInvalidPath("expect right bracket for field selector with single quote but found %c", buf[cursor+1])
}
selector := buf[:cursor]
b.addSelectorNode(string(selector))
b.singleQuotePathSelector = true
return b.buildNextCharIfExists(buf, cursor+2)
case '"':
if sel != DoubleQuotePathSelector {
return 0, errors.ErrInvalidPath("found single quote character in field selector with double quote context")
}
selector := buf[:cursor]
b.addSelectorNode(string(selector))
b.doubleQuotePathSelector = true
return b.buildNextCharIfExists(buf, cursor+1)
}
}
return 0, errors.ErrInvalidPath("couldn't find quote character in selector quote path context")
}
func (b *PathBuilder) buildPathRecursive(buf []rune) (int, error) {
switch buf[0] {
case '.', '[', ']', '$', '*':
return 0, errors.ErrInvalidPath("found invalid path character %c after double dot", buf[0])
}
for cursor := 0; cursor < len(buf); cursor++ {
switch buf[cursor] {
case '$', '*', ']':
return 0, errors.ErrInvalidPath("found %c character in field selector context", buf[cursor])
case '.':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with dot character")
}
selector := buf[:cursor]
b.addRecursiveNode(string(selector))
offset, err := b.buildSelector(buf[cursor+1:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
case '[':
if cursor+1 >= len(buf) {
return 0, errors.ErrInvalidPath("JSON Path ends with left bracket character")
}
selector := buf[:cursor]
b.addRecursiveNode(string(selector))
offset, err := b.buildIndex(buf[cursor+1:])
if err != nil {
return 0, err
}
return cursor + 1 + offset, nil
}
}
b.addRecursiveNode(string(buf))
return len(buf), nil
}
func (b *PathBuilder) buildIndex(buf []rune) (int, error) {
switch buf[0] {
case '.', '[', ']', '$':
return 0, errors.ErrInvalidPath("found invalid path character %c after left bracket", buf[0])
case '\'':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with single quote character")
}
offset, err := b.buildQuoteSelector(buf[1:], SingleQuotePathSelector)
if err != nil {
return 0, err
}
return 1 + offset, nil
case '*':
if len(buf) == 1 {
return 0, errors.ErrInvalidPath("JSON Path ends with star character")
}
if buf[1] != ']' {
return 0, errors.ErrInvalidPath("expect right bracket character for index all path but found %c character", buf[1])
}
b.addIndexAllNode()
offset := len("*]")
if len(buf) > 2 {
buildOffset, err := b.buildNext(buf[2:])
if err != nil {
return 0, err
}
return offset + buildOffset, nil
}
return offset, nil
}
for cursor := 0; cursor < len(buf); cursor++ {
switch buf[cursor] {
case ']':
index, err := strconv.ParseInt(string(buf[:cursor]), 10, 64)
if err != nil {
return 0, errors.ErrInvalidPath("%q is unexpected index path", buf[:cursor])
}
b.addIndexNode(int(index))
return b.buildNextCharIfExists(buf, cursor+1)
}
}
return 0, errors.ErrInvalidPath("couldn't find right bracket character in index path context")
}
func (b *PathBuilder) addIndexAllNode() {
node := newPathIndexAllNode()
if b.root == nil {
b.root = node
b.node = node
} else {
b.node = b.node.chain(node)
}
}
func (b *PathBuilder) addRecursiveNode(selector string) {
node := newPathRecursiveNode(selector)
if b.root == nil {
b.root = node
b.node = node
} else {
b.node = b.node.chain(node)
}
}
func (b *PathBuilder) addSelectorNode(name string) {
node := newPathSelectorNode(name)
if b.root == nil {
b.root = node
b.node = node
} else {
b.node = b.node.chain(node)
}
}
func (b *PathBuilder) addIndexNode(idx int) {
node := newPathIndexNode(idx)
if b.root == nil {
b.root = node
b.node = node
} else {
b.node = b.node.chain(node)
}
}
type QuotePathSelector int
const (
SingleQuotePathSelector QuotePathSelector = 1
DoubleQuotePathSelector QuotePathSelector = 2
)
type Path struct {
node PathNode
RootSelectorOnly bool
SingleQuotePathSelector bool
DoubleQuotePathSelector bool
}
func (p *Path) Field(sel string) (PathNode, bool, error) {
if p.node == nil {
return nil, false, nil
}
return p.node.Field(sel)
}
func (p *Path) Get(src, dst reflect.Value) error {
if p.node == nil {
return nil
}
return p.node.Get(src, dst)
}
func (p *Path) String() string {
if p.node == nil {
return "$"
}
return p.node.String()
}
type PathNode interface {
fmt.Stringer
Index(idx int) (PathNode, bool, error)
Field(fieldName string) (PathNode, bool, error)
Get(src, dst reflect.Value) error
chain(PathNode) PathNode
target() bool
single() bool
}
type BasePathNode struct {
child PathNode
}
func (n *BasePathNode) chain(node PathNode) PathNode {
n.child = node
return node
}
func (n *BasePathNode) target() bool {
return n.child == nil
}
func (n *BasePathNode) single() bool {
return true
}
type PathSelectorNode struct {
*BasePathNode
selector string
}
func newPathSelectorNode(selector string) *PathSelectorNode {
return &PathSelectorNode{
BasePathNode: &BasePathNode{},
selector: selector,
}
}
func (n *PathSelectorNode) Index(idx int) (PathNode, bool, error) {
return nil, false, &errors.PathError{}
}
func (n *PathSelectorNode) Field(fieldName string) (PathNode, bool, error) {
if n.selector == fieldName {
return n.child, true, nil
}
return nil, false, nil
}
func (n *PathSelectorNode) Get(src, dst reflect.Value) error {
switch src.Type().Kind() {
case reflect.Map:
iter := src.MapRange()
for iter.Next() {
key, ok := iter.Key().Interface().(string)
if !ok {
return fmt.Errorf("invalid map key type %T", src.Type().Key())
}
child, found, err := n.Field(key)
if err != nil {
return err
}
if found {
if child != nil {
return child.Get(iter.Value(), dst)
}
return AssignValue(iter.Value(), dst)
}
}
case reflect.Struct:
typ := src.Type()
for i := 0; i < typ.Len(); i++ {
tag := runtime.StructTagFromField(typ.Field(i))
child, found, err := n.Field(tag.Key)
if err != nil {
return err
}
if found {
if child != nil {
return child.Get(src.Field(i), dst)
}
return AssignValue(src.Field(i), dst)
}
}
case reflect.Ptr:
return n.Get(src.Elem(), dst)
case reflect.Interface:
return n.Get(reflect.ValueOf(src.Interface()), dst)
case reflect.Float64, reflect.String, reflect.Bool:
return AssignValue(src, dst)
}
return fmt.Errorf("failed to get %s value from %s", n.selector, src.Type())
}
func (n *PathSelectorNode) String() string {
s := fmt.Sprintf(".%s", n.selector)
if n.child != nil {
s += n.child.String()
}
return s
}
type PathIndexNode struct {
*BasePathNode
selector int
}
func newPathIndexNode(selector int) *PathIndexNode {
return &PathIndexNode{
BasePathNode: &BasePathNode{},
selector: selector,
}
}
func (n *PathIndexNode) Index(idx int) (PathNode, bool, error) {
if n.selector == idx {
return n.child, true, nil
}
return nil, false, nil
}
func (n *PathIndexNode) Field(fieldName string) (PathNode, bool, error) {
return nil, false, &errors.PathError{}
}
func (n *PathIndexNode) Get(src, dst reflect.Value) error {
switch src.Type().Kind() {
case reflect.Array, reflect.Slice:
if src.Len() > n.selector {
if n.child != nil {
return n.child.Get(src.Index(n.selector), dst)
}
return AssignValue(src.Index(n.selector), dst)
}
case reflect.Ptr:
return n.Get(src.Elem(), dst)
case reflect.Interface:
return n.Get(reflect.ValueOf(src.Interface()), dst)
}
return fmt.Errorf("failed to get [%d] value from %s", n.selector, src.Type())
}
func (n *PathIndexNode) String() string {
s := fmt.Sprintf("[%d]", n.selector)
if n.child != nil {
s += n.child.String()
}
return s
}
type PathIndexAllNode struct {
*BasePathNode
}
func newPathIndexAllNode() *PathIndexAllNode {
return &PathIndexAllNode{
BasePathNode: &BasePathNode{},
}
}
func (n *PathIndexAllNode) Index(idx int) (PathNode, bool, error) {
return n.child, true, nil
}
func (n *PathIndexAllNode) Field(fieldName string) (PathNode, bool, error) {
return nil, false, &errors.PathError{}
}
func (n *PathIndexAllNode) Get(src, dst reflect.Value) error {
switch src.Type().Kind() {
case reflect.Array, reflect.Slice:
var arr []interface{}
for i := 0; i < src.Len(); i++ {
var v interface{}
rv := reflect.ValueOf(&v)
if n.child != nil {
if err := n.child.Get(src.Index(i), rv); err != nil {
return err
}
} else {
if err := AssignValue(src.Index(i), rv); err != nil {
return err
}
}
arr = append(arr, v)
}
if err := AssignValue(reflect.ValueOf(arr), dst); err != nil {
return err
}
return nil
case reflect.Ptr:
return n.Get(src.Elem(), dst)
case reflect.Interface:
return n.Get(reflect.ValueOf(src.Interface()), dst)
}
return fmt.Errorf("failed to get all value from %s", src.Type())
}
func (n *PathIndexAllNode) String() string {
s := "[*]"
if n.child != nil {
s += n.child.String()
}
return s
}
type PathRecursiveNode struct {
*BasePathNode
selector string
}
func newPathRecursiveNode(selector string) *PathRecursiveNode {
node := newPathSelectorNode(selector)
return &PathRecursiveNode{
BasePathNode: &BasePathNode{
child: node,
},
selector: selector,
}
}
func (n *PathRecursiveNode) Field(fieldName string) (PathNode, bool, error) {
if n.selector == fieldName {
return n.child, true, nil
}
return nil, false, nil
}
func (n *PathRecursiveNode) Index(_ int) (PathNode, bool, error) {
return n, true, nil
}
func valueToSliceValue(v interface{}) []interface{} {
rv := reflect.ValueOf(v)
ret := []interface{}{}
if rv.Type().Kind() == reflect.Slice || rv.Type().Kind() == reflect.Array {
for i := 0; i < rv.Len(); i++ {
ret = append(ret, rv.Index(i).Interface())
}
return ret
}
return []interface{}{v}
}
func (n *PathRecursiveNode) Get(src, dst reflect.Value) error {
if n.child == nil {
return fmt.Errorf("failed to get by recursive path ..%s", n.selector)
}
var arr []interface{}
switch src.Type().Kind() {
case reflect.Map:
iter := src.MapRange()
for iter.Next() {
key, ok := iter.Key().Interface().(string)
if !ok {
return fmt.Errorf("invalid map key type %T", src.Type().Key())
}
child, found, err := n.Field(key)
if err != nil {
return err
}
if found {
var v interface{}
rv := reflect.ValueOf(&v)
_ = child.Get(iter.Value(), rv)
arr = append(arr, valueToSliceValue(v)...)
} else {
var v interface{}
rv := reflect.ValueOf(&v)
_ = n.Get(iter.Value(), rv)
if v != nil {
arr = append(arr, valueToSliceValue(v)...)
}
}
}
_ = AssignValue(reflect.ValueOf(arr), dst)
return nil
case reflect.Struct:
typ := src.Type()
for i := 0; i < typ.Len(); i++ {
tag := runtime.StructTagFromField(typ.Field(i))
child, found, err := n.Field(tag.Key)
if err != nil {
return err
}
if found {
var v interface{}
rv := reflect.ValueOf(&v)
_ = child.Get(src.Field(i), rv)
arr = append(arr, valueToSliceValue(v)...)
} else {
var v interface{}
rv := reflect.ValueOf(&v)
_ = n.Get(src.Field(i), rv)
if v != nil {
arr = append(arr, valueToSliceValue(v)...)
}
}
}
_ = AssignValue(reflect.ValueOf(arr), dst)
return nil
case reflect.Array, reflect.Slice:
for i := 0; i < src.Len(); i++ {
var v interface{}
rv := reflect.ValueOf(&v)
_ = n.Get(src.Index(i), rv)
if v != nil {
arr = append(arr, valueToSliceValue(v)...)
}
}
_ = AssignValue(reflect.ValueOf(arr), dst)
return nil
case reflect.Ptr:
return n.Get(src.Elem(), dst)
case reflect.Interface:
return n.Get(reflect.ValueOf(src.Interface()), dst)
}
return fmt.Errorf("failed to get %s value from %s", n.selector, src.Type())
}
func (n *PathRecursiveNode) String() string {
s := fmt.Sprintf("..%s", n.selector)
if n.child != nil {
s += n.child.String()
}
return s
}

View File

@ -1,7 +1,6 @@
package decoder
import (
"fmt"
"unsafe"
"github.com/goccy/go-json/internal/runtime"
@ -35,10 +34,6 @@ func (d *ptrDecoder) contentDecoder() Decoder {
//go:linkname unsafe_New reflect.unsafe_New
func unsafe_New(*runtime.Type) unsafe.Pointer
func UnsafeNew(t *runtime.Type) unsafe.Pointer {
return unsafe_New(t)
}
func (d *ptrDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
if s.skipWhiteSpace() == nul {
s.read()
@ -90,7 +85,3 @@ func (d *ptrDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P
cursor = c
return cursor, nil
}
func (d *ptrDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: ptr decoder does not support decode path")
}

View File

@ -299,82 +299,3 @@ func (d *sliceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
}
}
}
func (d *sliceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
buf := ctx.Buf
depth++
if depth > maxDecodeNestingDepth {
return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
}
ret := [][]byte{}
for {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
cursor++
continue
case 'n':
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return [][]byte{nullbytes}, cursor, nil
case '[':
cursor++
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == ']' {
cursor++
return ret, cursor, nil
}
idx := 0
for {
child, found, err := ctx.Option.Path.node.Index(idx)
if err != nil {
return nil, 0, err
}
if found {
if child != nil {
oldPath := ctx.Option.Path.node
ctx.Option.Path.node = child
paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth)
if err != nil {
return nil, 0, err
}
ctx.Option.Path.node = oldPath
ret = append(ret, paths...)
cursor = c
} else {
start := cursor
end, err := skipValue(buf, cursor, depth)
if err != nil {
return nil, 0, err
}
ret = append(ret, buf[start:end])
cursor = end
}
} else {
c, err := skipValue(buf, cursor, depth)
if err != nil {
return nil, 0, err
}
cursor = c
}
cursor = skipWhiteSpace(buf, cursor)
switch buf[cursor] {
case ']':
cursor++
return ret, cursor, nil
case ',':
idx++
default:
return nil, 0, errors.ErrInvalidCharacter(buf[cursor], "slice", cursor)
}
cursor++
}
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return nil, 0, d.errNumber(cursor)
default:
return nil, 0, errors.ErrUnexpectedEndOfJSON("slice", cursor)
}
}
}

View File

@ -103,7 +103,7 @@ func (s *Stream) statForRetry() ([]byte, int64, unsafe.Pointer) {
func (s *Stream) Reset() {
s.reset()
s.bufSize = int64(len(s.buf))
s.bufSize = initBufSize
}
func (s *Stream) More() bool {
@ -138,11 +138,8 @@ func (s *Stream) Token() (interface{}, error) {
s.cursor++
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
bytes := floatBytes(s)
str := *(*string)(unsafe.Pointer(&bytes))
if s.UseNumber {
return json.Number(str), nil
}
f64, err := strconv.ParseFloat(str, 64)
s := *(*string)(unsafe.Pointer(&bytes))
f64, err := strconv.ParseFloat(s, 64)
if err != nil {
return nil, err
}
@ -280,7 +277,7 @@ func (s *Stream) skipObject(depth int64) error {
if char(p, cursor) == nul {
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
_, cursor, p = s.statForRetry()
continue
}
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
@ -343,7 +340,7 @@ func (s *Stream) skipArray(depth int64) error {
if char(p, cursor) == nul {
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
_, cursor, p = s.statForRetry()
continue
}
return errors.ErrUnexpectedEndOfJSON("string of object", cursor)
@ -401,7 +398,7 @@ func (s *Stream) skipValue(depth int64) error {
if char(p, cursor) == nul {
s.cursor = cursor
if s.read() {
_, cursor, p = s.stat()
_, cursor, p = s.statForRetry()
continue
}
return errors.ErrUnexpectedEndOfJSON("value of string", s.totalOffset())
@ -426,6 +423,7 @@ func (s *Stream) skipValue(depth int64) error {
continue
} else if c == nul {
if s.read() {
s.cursor-- // for retry current character
_, cursor, p = s.stat()
continue
}

View File

@ -1,8 +1,6 @@
package decoder
import (
"bytes"
"fmt"
"reflect"
"unicode"
"unicode/utf16"
@ -60,17 +58,6 @@ func (d *stringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
return cursor, nil
}
func (d *stringDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
bytes, c, err := d.decodeByte(ctx.Buf, cursor)
if err != nil {
return nil, 0, err
}
if bytes == nil {
return [][]byte{nullbytes}, c, nil
}
return [][]byte{bytes}, c, nil
}
var (
hexToInt = [256]int{
'0': 0,
@ -106,30 +93,24 @@ func unicodeToRune(code []byte) rune {
return r
}
func readAtLeast(s *Stream, n int64, p *unsafe.Pointer) bool {
for s.cursor+n >= s.length {
if !s.read() {
return false
}
*p = s.bufptr()
}
return true
}
func decodeUnicodeRune(s *Stream, p unsafe.Pointer) (rune, int64, unsafe.Pointer, error) {
const defaultOffset = 5
const surrogateOffset = 11
if !readAtLeast(s, defaultOffset, &p) {
return rune(0), 0, nil, errors.ErrInvalidCharacter(s.char(), "escaped string", s.totalOffset())
if s.cursor+defaultOffset >= s.length {
if !s.read() {
return rune(0), 0, nil, errors.ErrInvalidCharacter(s.char(), "escaped string", s.totalOffset())
}
p = s.bufptr()
}
r := unicodeToRune(s.buf[s.cursor+1 : s.cursor+defaultOffset])
if utf16.IsSurrogate(r) {
if !readAtLeast(s, surrogateOffset, &p) {
return unicode.ReplacementChar, defaultOffset, p, nil
if s.cursor+surrogateOffset >= s.length {
s.read()
p = s.bufptr()
}
if s.buf[s.cursor+defaultOffset] != '\\' || s.buf[s.cursor+defaultOffset+1] != 'u' {
if s.cursor+surrogateOffset >= s.length || s.buf[s.cursor+defaultOffset] != '\\' || s.buf[s.cursor+defaultOffset+1] != 'u' {
return unicode.ReplacementChar, defaultOffset, p, nil
}
r2 := unicodeToRune(s.buf[s.cursor+defaultOffset+2 : s.cursor+surrogateOffset])
@ -182,7 +163,6 @@ RETRY:
if !s.read() {
return nil, errors.ErrInvalidCharacter(s.char(), "escaped string", s.totalOffset())
}
p = s.bufptr()
goto RETRY
default:
return nil, errors.ErrUnexpectedEndOfJSON("string", s.totalOffset())
@ -328,36 +308,49 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
cursor++
start := cursor
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
escaped := 0
for {
switch char(b, cursor) {
case '\\':
escaped++
cursor++
switch char(b, cursor) {
case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
cursor++
case '"':
buf[cursor] = '"'
buf = append(buf[:cursor-1], buf[cursor:]...)
case '\\':
buf[cursor] = '\\'
buf = append(buf[:cursor-1], buf[cursor:]...)
case '/':
buf[cursor] = '/'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'b':
buf[cursor] = '\b'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'f':
buf[cursor] = '\f'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'n':
buf[cursor] = '\n'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'r':
buf[cursor] = '\r'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 't':
buf[cursor] = '\t'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'u':
buflen := int64(len(buf))
if cursor+5 >= buflen {
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
}
for i := int64(1); i <= 4; i++ {
c := char(b, cursor+i)
if !(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')) {
return nil, 0, errors.ErrSyntax(fmt.Sprintf("json: invalid character %c in \\u hexadecimal character escape", c), cursor+i)
}
}
cursor += 5
code := unicodeToRune(buf[cursor+1 : cursor+5])
unicode := []byte(string(code))
buf = append(append(buf[:cursor-1], unicode...), buf[cursor+5:]...)
default:
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
}
continue
case '"':
literal := buf[start:cursor]
if escaped > 0 {
literal = literal[:unescapeString(literal)]
}
cursor++
return literal, cursor, nil
case nul:
@ -376,77 +369,3 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
}
}
}
var unescapeMap = [256]byte{
'"': '"',
'\\': '\\',
'/': '/',
'b': '\b',
'f': '\f',
'n': '\n',
'r': '\r',
't': '\t',
}
func unsafeAdd(ptr unsafe.Pointer, offset int) unsafe.Pointer {
return unsafe.Pointer(uintptr(ptr) + uintptr(offset))
}
func unescapeString(buf []byte) int {
p := (*sliceHeader)(unsafe.Pointer(&buf)).data
end := unsafeAdd(p, len(buf))
src := unsafeAdd(p, bytes.IndexByte(buf, '\\'))
dst := src
for src != end {
c := char(src, 0)
if c == '\\' {
escapeChar := char(src, 1)
if escapeChar != 'u' {
*(*byte)(dst) = unescapeMap[escapeChar]
src = unsafeAdd(src, 2)
dst = unsafeAdd(dst, 1)
} else {
v1 := hexToInt[char(src, 2)]
v2 := hexToInt[char(src, 3)]
v3 := hexToInt[char(src, 4)]
v4 := hexToInt[char(src, 5)]
code := rune((v1 << 12) | (v2 << 8) | (v3 << 4) | v4)
if code >= 0xd800 && code < 0xdc00 && uintptr(unsafeAdd(src, 11)) < uintptr(end) {
if char(src, 6) == '\\' && char(src, 7) == 'u' {
v1 := hexToInt[char(src, 8)]
v2 := hexToInt[char(src, 9)]
v3 := hexToInt[char(src, 10)]
v4 := hexToInt[char(src, 11)]
lo := rune((v1 << 12) | (v2 << 8) | (v3 << 4) | v4)
if lo >= 0xdc00 && lo < 0xe000 {
code = (code-0xd800)<<10 | (lo - 0xdc00) + 0x10000
src = unsafeAdd(src, 6)
}
}
}
var b [utf8.UTFMax]byte
n := utf8.EncodeRune(b[:], code)
switch n {
case 4:
*(*byte)(unsafeAdd(dst, 3)) = b[3]
fallthrough
case 3:
*(*byte)(unsafeAdd(dst, 2)) = b[2]
fallthrough
case 2:
*(*byte)(unsafeAdd(dst, 1)) = b[1]
fallthrough
case 1:
*(*byte)(unsafeAdd(dst, 0)) = b[0]
}
src = unsafeAdd(src, 6)
dst = unsafeAdd(dst, n)
}
} else {
*(*byte)(dst) = c
src = unsafeAdd(src, 1)
dst = unsafeAdd(dst, 1)
}
}
return int(uintptr(dst) - uintptr(p))
}

View File

@ -817,7 +817,3 @@ func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
cursor++
}
}
func (d *structDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: struct decoder does not support decode path")
}

View File

@ -10,7 +10,6 @@ import (
type Decoder interface {
Decode(*RuntimeContext, int64, int64, unsafe.Pointer) (int64, error)
DecodePath(*RuntimeContext, int64, int64) ([][]byte, int64, error)
DecodeStream(*Stream, int64, unsafe.Pointer) error
}

View File

@ -188,7 +188,3 @@ func (d *uintDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.
d.op(p, u64)
return cursor, nil
}
func (d *uintDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: uint decoder does not support decode path")
}

View File

@ -1,9 +1,7 @@
package decoder
import (
"context"
"encoding/json"
"fmt"
"unsafe"
"github.com/goccy/go-json/internal/errors"
@ -48,20 +46,13 @@ func (d *unmarshalJSONDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Poi
typ: d.typ,
ptr: p,
}))
switch v := v.(type) {
case unmarshalerContext:
var ctx context.Context
if (s.Option.Flags & ContextOption) != 0 {
ctx = s.Option.Context
} else {
ctx = context.Background()
}
if err := v.UnmarshalJSON(ctx, dst); err != nil {
if (s.Option.Flags & ContextOption) != 0 {
if err := v.(unmarshalerContext).UnmarshalJSON(s.Option.Context, dst); err != nil {
d.annotateError(s.cursor, err)
return err
}
case json.Unmarshaler:
if err := v.UnmarshalJSON(dst); err != nil {
} else {
if err := v.(json.Unmarshaler).UnmarshalJSON(dst); err != nil {
d.annotateError(s.cursor, err)
return err
}
@ -98,7 +89,3 @@ func (d *unmarshalJSONDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
}
return end, nil
}
func (d *unmarshalJSONDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: unmarshal json decoder does not support decode path")
}

View File

@ -3,7 +3,6 @@ package decoder
import (
"bytes"
"encoding"
"fmt"
"unicode"
"unicode/utf16"
"unicode/utf8"
@ -143,10 +142,6 @@ func (d *unmarshalTextDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
return end, nil
}
func (d *unmarshalTextDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: unmarshal text decoder does not support decode path")
}
func unquoteBytes(s []byte) (t []byte, ok bool) {
length := len(s)
if length < 2 || s[0] != '"' || s[length-1] != '"' {

View File

@ -1,7 +1,6 @@
package decoder
import (
"fmt"
"reflect"
"unsafe"
@ -67,7 +66,3 @@ func (d *wrappedStringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
ctx.Buf = oldBuf
return c, nil
}
func (d *wrappedStringDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
return nil, 0, fmt.Errorf("json: wrapped string decoder does not support decode path")
}

View File

@ -2,7 +2,6 @@ package encoder
import (
"fmt"
"reflect"
"unsafe"
"github.com/goccy/go-json/internal/runtime"
@ -11,7 +10,6 @@ import (
type Code interface {
Kind() CodeKind
ToOpcode(*compileContext) Opcodes
Filter(*FieldQuery) Code
}
type AnonymousCode interface {
@ -84,10 +82,6 @@ func (c *IntCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *IntCode) Filter(_ *FieldQuery) Code {
return c
}
type UintCode struct {
typ *runtime.Type
bitSize uint8
@ -114,10 +108,6 @@ func (c *UintCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *UintCode) Filter(_ *FieldQuery) Code {
return c
}
type FloatCode struct {
typ *runtime.Type
bitSize uint8
@ -150,10 +140,6 @@ func (c *FloatCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *FloatCode) Filter(_ *FieldQuery) Code {
return c
}
type StringCode struct {
typ *runtime.Type
isPtr bool
@ -183,10 +169,6 @@ func (c *StringCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *StringCode) Filter(_ *FieldQuery) Code {
return c
}
type BoolCode struct {
typ *runtime.Type
isPtr bool
@ -208,10 +190,6 @@ func (c *BoolCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *BoolCode) Filter(_ *FieldQuery) Code {
return c
}
type BytesCode struct {
typ *runtime.Type
isPtr bool
@ -233,10 +211,6 @@ func (c *BytesCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *BytesCode) Filter(_ *FieldQuery) Code {
return c
}
type SliceCode struct {
typ *runtime.Type
value Code
@ -271,10 +245,6 @@ func (c *SliceCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{header}.Add(codes...).Add(elemCode).Add(end)
}
func (c *SliceCode) Filter(_ *FieldQuery) Code {
return c
}
type ArrayCode struct {
typ *runtime.Type
value Code
@ -316,10 +286,6 @@ func (c *ArrayCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{header}.Add(codes...).Add(elemCode).Add(end)
}
func (c *ArrayCode) Filter(_ *FieldQuery) Code {
return c
}
type MapCode struct {
typ *runtime.Type
key Code
@ -366,10 +332,6 @@ func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{header}.Add(keyCodes...).Add(value).Add(valueCodes...).Add(key).Add(end)
}
func (c *MapCode) Filter(_ *FieldQuery) Code {
return c
}
type StructCode struct {
typ *runtime.Type
fields []*StructFieldCode
@ -384,7 +346,7 @@ func (c *StructCode) Kind() CodeKind {
}
func (c *StructCode) lastFieldCode(field *StructFieldCode, firstField *Opcode) *Opcode {
if isEmbeddedStruct(field) {
if field.isAnonymous {
return c.lastAnonymousFieldCode(firstField)
}
lastField := firstField
@ -437,13 +399,12 @@ func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
}
if isEndField {
endField := fieldCodes.Last()
if isEmbeddedStruct(field) {
if len(codes) > 0 {
codes.First().End = endField
} else if field.isAnonymous {
firstField.End = endField
lastField := c.lastAnonymousFieldCode(firstField)
lastField.NextField = endField
}
if len(codes) > 0 {
codes.First().End = endField
} else {
firstField.End = endField
}
@ -559,45 +520,6 @@ func (c *StructCode) enableIndirect() {
structCode.enableIndirect()
}
func (c *StructCode) Filter(query *FieldQuery) Code {
fieldMap := map[string]*FieldQuery{}
for _, field := range query.Fields {
fieldMap[field.Name] = field
}
fields := make([]*StructFieldCode, 0, len(c.fields))
for _, field := range c.fields {
query, exists := fieldMap[field.key]
if !exists {
continue
}
fieldCode := &StructFieldCode{
typ: field.typ,
key: field.key,
tag: field.tag,
value: field.value,
offset: field.offset,
isAnonymous: field.isAnonymous,
isTaggedKey: field.isTaggedKey,
isNilableType: field.isNilableType,
isNilCheck: field.isNilCheck,
isAddrForMarshaler: field.isAddrForMarshaler,
isNextOpPtrType: field.isNextOpPtrType,
}
if len(query.Fields) > 0 {
fieldCode.value = fieldCode.value.Filter(query)
}
fields = append(fields, fieldCode)
}
return &StructCode{
typ: c.typ,
fields: fields,
isPtr: c.isPtr,
disableIndirectConversion: c.disableIndirectConversion,
isIndirect: c.isIndirect,
isRecursive: c.isRecursive,
}
}
type StructFieldCode struct {
typ *runtime.Type
key string
@ -610,7 +532,6 @@ type StructFieldCode struct {
isNilCheck bool
isAddrForMarshaler bool
isNextOpPtrType bool
isMarshalerContext bool
}
func (c *StructFieldCode) getStruct() *StructCode {
@ -653,12 +574,8 @@ func (c *StructFieldCode) headerOpcodes(ctx *compileContext, field *Opcode, valu
value := valueCodes.First()
op := optimizeStructHeader(value, c.tag)
field.Op = op
if value.Flags&MarshalerContextFlags != 0 {
field.Flags |= MarshalerContextFlags
}
field.NumBitSize = value.NumBitSize
field.PtrNum = value.PtrNum
field.FieldQuery = value.FieldQuery
fieldCodes := Opcodes{field}
if op.IsMultipleOpHead() {
field.Next = value
@ -673,12 +590,8 @@ func (c *StructFieldCode) fieldOpcodes(ctx *compileContext, field *Opcode, value
value := valueCodes.First()
op := optimizeStructField(value, c.tag)
field.Op = op
if value.Flags&MarshalerContextFlags != 0 {
field.Flags |= MarshalerContextFlags
}
field.NumBitSize = value.NumBitSize
field.PtrNum = value.PtrNum
field.FieldQuery = value.FieldQuery
fieldCodes := Opcodes{field}
if op.IsMultipleOpField() {
@ -732,9 +645,6 @@ func (c *StructFieldCode) flags() OpFlags {
if c.isAnonymous {
flags |= AnonymousKeyFlags
}
if c.isMarshalerContext {
flags |= MarshalerContextFlags
}
return flags
}
@ -815,9 +725,8 @@ func isEnableStructEndOptimization(value Code) bool {
}
type InterfaceCode struct {
typ *runtime.Type
fieldQuery *FieldQuery
isPtr bool
typ *runtime.Type
isPtr bool
}
func (c *InterfaceCode) Kind() CodeKind {
@ -832,7 +741,6 @@ func (c *InterfaceCode) ToOpcode(ctx *compileContext) Opcodes {
default:
code = newOpCode(ctx, c.typ, OpInterface)
}
code.FieldQuery = c.fieldQuery
if c.typ.NumMethod() > 0 {
code.Flags |= NonEmptyInterfaceFlags
}
@ -840,17 +748,8 @@ func (c *InterfaceCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *InterfaceCode) Filter(query *FieldQuery) Code {
return &InterfaceCode{
typ: c.typ,
fieldQuery: query,
isPtr: c.isPtr,
}
}
type MarshalJSONCode struct {
typ *runtime.Type
fieldQuery *FieldQuery
isAddrForMarshaler bool
isNilableType bool
isMarshalerContext bool
@ -862,7 +761,6 @@ func (c *MarshalJSONCode) Kind() CodeKind {
func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes {
code := newOpCode(ctx, c.typ, OpMarshalJSON)
code.FieldQuery = c.fieldQuery
if c.isAddrForMarshaler {
code.Flags |= AddrForMarshalerFlags
}
@ -878,19 +776,8 @@ func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *MarshalJSONCode) Filter(query *FieldQuery) Code {
return &MarshalJSONCode{
typ: c.typ,
fieldQuery: query,
isAddrForMarshaler: c.isAddrForMarshaler,
isNilableType: c.isNilableType,
isMarshalerContext: c.isMarshalerContext,
}
}
type MarshalTextCode struct {
typ *runtime.Type
fieldQuery *FieldQuery
isAddrForMarshaler bool
isNilableType bool
}
@ -901,7 +788,6 @@ func (c *MarshalTextCode) Kind() CodeKind {
func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes {
code := newOpCode(ctx, c.typ, OpMarshalText)
code.FieldQuery = c.fieldQuery
if c.isAddrForMarshaler {
code.Flags |= AddrForMarshalerFlags
}
@ -914,15 +800,6 @@ func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes {
return Opcodes{code}
}
func (c *MarshalTextCode) Filter(query *FieldQuery) Code {
return &MarshalTextCode{
typ: c.typ,
fieldQuery: query,
isAddrForMarshaler: c.isAddrForMarshaler,
isNilableType: c.isNilableType,
}
}
type PtrCode struct {
typ *runtime.Type
value Code
@ -953,14 +830,6 @@ func (c *PtrCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
return codes
}
func (c *PtrCode) Filter(query *FieldQuery) Code {
return &PtrCode{
typ: c.typ,
value: c.value.Filter(query),
ptrNum: c.ptrNum,
}
}
func convertPtrOp(code *Opcode) OpType {
ptrHeadOp := code.Op.HeadToPtrHead()
if code.Op != ptrHeadOp {
@ -1004,14 +873,3 @@ func convertPtrOp(code *Opcode) OpType {
}
return code.Op
}
func isEmbeddedStruct(field *StructFieldCode) bool {
if !field.isAnonymous {
return false
}
t := field.typ
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t.Kind() == reflect.Struct
}

View File

@ -31,7 +31,7 @@ func init() {
if typeAddr == nil {
typeAddr = &runtime.TypeAddr{}
}
cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange>>typeAddr.AddrShift+1)
cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange>>typeAddr.AddrShift)
}
func loadOpcodeMap() map[uintptr]*OpcodeSet {
@ -63,27 +63,6 @@ func compileToGetCodeSetSlowPath(typeptr uintptr) (*OpcodeSet, error) {
return codeSet, nil
}
func getFilteredCodeSetIfNeeded(ctx *RuntimeContext, codeSet *OpcodeSet) (*OpcodeSet, error) {
if (ctx.Option.Flag & ContextOption) == 0 {
return codeSet, nil
}
query := FieldQueryFromContext(ctx.Option.Context)
if query == nil {
return codeSet, nil
}
ctx.Option.Flag |= FieldQueryOption
cacheCodeSet := codeSet.getQueryCache(query.Hash())
if cacheCodeSet != nil {
return cacheCodeSet, nil
}
queryCodeSet, err := newCompiler().codeToOpcodeSet(codeSet.Type, codeSet.Code.Filter(query))
if err != nil {
return nil, err
}
codeSet.setQueryCache(query.Hash(), queryCodeSet)
return queryCodeSet, nil
}
type Compiler struct {
structTypeToCode map[uintptr]*StructCode
}
@ -101,10 +80,6 @@ func (c *Compiler) compile(typeptr uintptr) (*OpcodeSet, error) {
if err != nil {
return nil, err
}
return c.codeToOpcodeSet(typ, code)
}
func (c *Compiler) codeToOpcodeSet(typ *runtime.Type, code Code) (*OpcodeSet, error) {
noescapeKeyCode := c.codeToOpcode(&compileContext{
structTypeToCodes: map[uintptr]Opcodes{},
recursiveCodes: &Opcodes{},
@ -132,8 +107,6 @@ func (c *Compiler) codeToOpcodeSet(typ *runtime.Type, code Code) (*OpcodeSet, er
InterfaceEscapeKeyCode: interfaceEscapeKeyCode,
CodeLength: codeLength,
EndCode: ToEndCode(interfaceNoescapeKeyCode),
Code: code,
QueryCache: map[string]*OpcodeSet{},
}, nil
}
@ -487,10 +460,7 @@ func (c *Compiler) listElemCode(typ *runtime.Type) (Code, error) {
case typ.Kind() == reflect.Map:
return c.ptrCode(runtime.PtrTo(typ))
default:
// isPtr was originally used to indicate whether the type of top level is pointer.
// However, since the slice/array element is a specification that can get the pointer address, explicitly set isPtr to true.
// See here for related issues: https://github.com/goccy/go-json/issues/370
code, err := c.typeToCodeWithPtr(typ, true)
code, err := c.typeToCodeWithPtr(typ, false)
if err != nil {
return nil, err
}
@ -506,6 +476,8 @@ func (c *Compiler) listElemCode(typ *runtime.Type) (Code, error) {
func (c *Compiler) mapKeyCode(typ *runtime.Type) (Code, error) {
switch {
case c.implementsMarshalJSON(typ):
return c.marshalJSONCode(typ)
case c.implementsMarshalText(typ):
return c.marshalTextCode(typ)
}
@ -854,9 +826,6 @@ func (c *Compiler) implementsMarshalText(typ *runtime.Type) bool {
}
func (c *Compiler) isNilableType(typ *runtime.Type) bool {
if !runtime.IfaceIndir(typ) {
return true
}
switch typ.Kind() {
case reflect.Ptr:
return true
@ -889,40 +858,29 @@ func (c *Compiler) codeToOpcode(ctx *compileContext, typ *runtime.Type, code Cod
}
func (c *Compiler) linkRecursiveCode(ctx *compileContext) {
recursiveCodes := map[uintptr]*CompiledCode{}
for _, recursive := range *ctx.recursiveCodes {
typeptr := uintptr(unsafe.Pointer(recursive.Type))
codes := ctx.structTypeToCodes[typeptr]
if recursiveCode, ok := recursiveCodes[typeptr]; ok {
*recursive.Jmp = *recursiveCode
continue
}
code := copyOpcode(codes.First())
compiled := recursive.Jmp
compiled.Code = copyOpcode(codes.First())
code := compiled.Code
code.End.Next = newEndOp(&compileContext{}, recursive.Type)
code.Op = code.Op.PtrHeadToHead()
lastCode := newEndOp(&compileContext{}, recursive.Type)
lastCode.Op = OpRecursiveEnd
// OpRecursiveEnd must set before call TotalLength
code.End.Next = lastCode
beforeLastCode := code.End
lastCode := beforeLastCode.Next
totalLength := code.TotalLength()
// Idx, ElemIdx, Length must set after call TotalLength
lastCode.Idx = uint32((totalLength + 1) * uintptrSize)
lastCode.ElemIdx = lastCode.Idx + uintptrSize
lastCode.Length = lastCode.Idx + 2*uintptrSize
code.End.Next.Op = OpRecursiveEnd
// extend length to alloc slot for elemIdx + length
curTotalLength := uintptr(recursive.TotalLength()) + 3
nextTotalLength := uintptr(totalLength) + 3
compiled := recursive.Jmp
compiled.Code = code
compiled.CurLen = curTotalLength
compiled.NextLen = nextTotalLength
compiled.Linked = true
recursiveCodes[typeptr] = compiled
}
}

View File

@ -3,30 +3,18 @@
package encoder
func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) {
if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr {
codeSet, err := compileToGetCodeSetSlowPath(typeptr)
if err != nil {
return nil, err
}
return getFilteredCodeSetIfNeeded(ctx, codeSet)
func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
if typeptr > typeAddr.MaxTypeAddr {
return compileToGetCodeSetSlowPath(typeptr)
}
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
if err != nil {
return nil, err
}
return filtered, nil
return codeSet, nil
}
codeSet, err := newCompiler().compile(typeptr)
if err != nil {
return nil, err
}
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
if err != nil {
return nil, err
}
cachedOpcodeSets[index] = codeSet
return filtered, nil
return codeSet, nil
}

View File

@ -9,24 +9,15 @@ import (
var setsMu sync.RWMutex
func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) {
if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr {
codeSet, err := compileToGetCodeSetSlowPath(typeptr)
if err != nil {
return nil, err
}
return getFilteredCodeSetIfNeeded(ctx, codeSet)
func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
if typeptr > typeAddr.MaxTypeAddr {
return compileToGetCodeSetSlowPath(typeptr)
}
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
setsMu.RLock()
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
if err != nil {
setsMu.RUnlock()
return nil, err
}
setsMu.RUnlock()
return filtered, nil
return codeSet, nil
}
setsMu.RUnlock()
@ -34,12 +25,8 @@ func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, erro
if err != nil {
return nil, err
}
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
if err != nil {
return nil, err
}
setsMu.Lock()
cachedOpcodeSets[index] = codeSet
setsMu.Unlock()
return filtered, nil
return codeSet, nil
}

View File

@ -44,6 +44,13 @@ var first = [256]uint8{
s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF
}
// acceptRange gives the range of valid values for the second byte in a UTF-8
// sequence.
type acceptRange struct {
lo uint8 // lowest value for second byte.
hi uint8 // highest value for second byte.
}
const (
lineSep = byte(168) //'\u2028'
paragraphSep = byte(169) //'\u2029'
@ -73,31 +80,25 @@ func decodeRuneInString(s string) (decodeRuneState, int) {
return validUTF8State, 1
}
sz := int(x & 7)
var accept acceptRange
switch x >> 4 {
case 0:
accept = acceptRange{locb, hicb}
case 1:
accept = acceptRange{0xA0, hicb}
case 2:
accept = acceptRange{locb, 0x9F}
case 3:
accept = acceptRange{0x90, hicb}
case 4:
accept = acceptRange{locb, 0x8F}
}
if n < sz {
return runeErrorState, 1
}
s1 := s[1]
switch x >> 4 {
case 0:
if s1 < locb || hicb < s1 {
return runeErrorState, 1
}
case 1:
if s1 < 0xA0 || hicb < s1 {
return runeErrorState, 1
}
case 2:
if s1 < locb || 0x9F < s1 {
return runeErrorState, 1
}
case 3:
if s1 < 0x90 || hicb < s1 {
return runeErrorState, 1
}
case 4:
if s1 < locb || 0x8F < s1 {
return runeErrorState, 1
}
if s1 < accept.lo || accept.hi < s1 {
return runeErrorState, 1
}
if sz <= 2 {
return validUTF8State, 2

View File

@ -6,13 +6,11 @@ import (
)
func TestDumpOpcode(t *testing.T) {
ctx := TakeRuntimeContext()
defer ReleaseRuntimeContext(ctx)
var v interface{} = 1
header := (*emptyInterface)(unsafe.Pointer(&v))
typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := CompileToGetCodeSet(ctx, typeptr)
codeSet, err := CompileToGetCodeSet(typeptr)
if err != nil {
t.Fatal(err)
}

View File

@ -101,22 +101,6 @@ type OpcodeSet struct {
InterfaceEscapeKeyCode *Opcode
CodeLength int
EndCode *Opcode
Code Code
QueryCache map[string]*OpcodeSet
cacheMu sync.RWMutex
}
func (s *OpcodeSet) getQueryCache(hash string) *OpcodeSet {
s.cacheMu.RLock()
codeSet := s.QueryCache[hash]
s.cacheMu.RUnlock()
return codeSet
}
func (s *OpcodeSet) setQueryCache(hash string, codeSet *OpcodeSet) {
s.cacheMu.Lock()
s.QueryCache[hash] = codeSet
s.cacheMu.Unlock()
}
type CompiledCode struct {
@ -275,14 +259,12 @@ var mapContextPool = sync.Pool{
},
}
func NewMapContext(mapLen int, unorderedMap bool) *MapContext {
func NewMapContext(mapLen int) *MapContext {
ctx := mapContextPool.Get().(*MapContext)
if !unorderedMap {
if len(ctx.Slice.Items) < mapLen {
ctx.Slice.Items = make([]MapItem, mapLen)
} else {
ctx.Slice.Items = ctx.Slice.Items[:mapLen]
}
if len(ctx.Slice.Items) < mapLen {
ctx.Slice.Items = make([]MapItem, mapLen)
} else {
ctx.Slice.Items = ctx.Slice.Items[:mapLen]
}
ctx.Buf = ctx.Buf[:0]
ctx.Iter = mapIter{}
@ -413,11 +395,7 @@ func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{
if !ok {
return AppendNull(ctx, b), nil
}
stdctx := ctx.Option.Context
if ctx.Option.Flag&FieldQueryOption != 0 {
stdctx = SetFieldQueryToContext(stdctx, code.FieldQuery)
}
b, err := marshaler.MarshalJSON(stdctx)
b, err := marshaler.MarshalJSON(ctx.Option.Context)
if err != nil {
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
}
@ -589,8 +567,6 @@ func IsNilForMarshaler(v interface{}) bool {
return rv.IsNil()
case reflect.Slice:
return rv.IsNil() || rv.Len() == 0
case reflect.String:
return rv.Len() == 0
}
return false
}

View File

@ -39,7 +39,6 @@ type Opcode struct {
Type *runtime.Type // go type
Jmp *CompiledCode // for recursive call
FieldQuery *FieldQuery // field query for Interface / MarshalJSON / MarshalText
ElemIdx uint32 // offset to access array/slice elem
Length uint32 // offset to access slice length or array length
Indent uint32 // indent number
@ -334,7 +333,6 @@ func copyOpcode(code *Opcode) *Opcode {
Idx: c.Idx,
Offset: c.Offset,
Type: c.Type,
FieldQuery: c.FieldQuery,
DisplayIdx: c.DisplayIdx,
DisplayKey: c.DisplayKey,
ElemIdx: c.ElemIdx,
@ -363,7 +361,7 @@ func copyOpcode(code *Opcode) *Opcode {
func setTotalLengthToInterfaceOp(code *Opcode) {
for c := code; !c.IsEnd(); {
if c.Op == OpInterface || c.Op == OpInterfacePtr {
if c.Op == OpInterface {
c.Length = uint32(code.TotalLength())
}
c = c.IterNext()

View File

@ -1,9 +1,6 @@
package encoder
import (
"context"
"io"
)
import "context"
type OptionFlag uint8
@ -14,15 +11,12 @@ const (
DebugOption
ColorizeOption
ContextOption
NormalizeUTF8Option
FieldQueryOption
)
type Option struct {
Flag OptionFlag
ColorScheme *ColorScheme
Context context.Context
DebugOut io.Writer
}
type EncodeFormat struct {

View File

@ -1,135 +0,0 @@
package encoder
import (
"context"
"fmt"
"reflect"
)
var (
Marshal func(interface{}) ([]byte, error)
Unmarshal func([]byte, interface{}) error
)
type FieldQuery struct {
Name string
Fields []*FieldQuery
hash string
}
func (q *FieldQuery) Hash() string {
if q.hash != "" {
return q.hash
}
b, _ := Marshal(q)
q.hash = string(b)
return q.hash
}
func (q *FieldQuery) MarshalJSON() ([]byte, error) {
if q.Name != "" {
if len(q.Fields) > 0 {
return Marshal(map[string][]*FieldQuery{q.Name: q.Fields})
}
return Marshal(q.Name)
}
return Marshal(q.Fields)
}
func (q *FieldQuery) QueryString() (FieldQueryString, error) {
b, err := Marshal(q)
if err != nil {
return "", err
}
return FieldQueryString(b), nil
}
type FieldQueryString string
func (s FieldQueryString) Build() (*FieldQuery, error) {
var query interface{}
if err := Unmarshal([]byte(s), &query); err != nil {
return nil, err
}
return s.build(reflect.ValueOf(query))
}
func (s FieldQueryString) build(v reflect.Value) (*FieldQuery, error) {
switch v.Type().Kind() {
case reflect.String:
return s.buildString(v)
case reflect.Map:
return s.buildMap(v)
case reflect.Slice:
return s.buildSlice(v)
case reflect.Interface:
return s.build(reflect.ValueOf(v.Interface()))
}
return nil, fmt.Errorf("failed to build field query")
}
func (s FieldQueryString) buildString(v reflect.Value) (*FieldQuery, error) {
b := []byte(v.String())
switch b[0] {
case '[', '{':
var query interface{}
if err := Unmarshal(b, &query); err != nil {
return nil, err
}
if str, ok := query.(string); ok {
return &FieldQuery{Name: str}, nil
}
return s.build(reflect.ValueOf(query))
}
return &FieldQuery{Name: string(b)}, nil
}
func (s FieldQueryString) buildSlice(v reflect.Value) (*FieldQuery, error) {
fields := make([]*FieldQuery, 0, v.Len())
for i := 0; i < v.Len(); i++ {
def, err := s.build(v.Index(i))
if err != nil {
return nil, err
}
fields = append(fields, def)
}
return &FieldQuery{Fields: fields}, nil
}
func (s FieldQueryString) buildMap(v reflect.Value) (*FieldQuery, error) {
keys := v.MapKeys()
if len(keys) != 1 {
return nil, fmt.Errorf("failed to build field query object")
}
key := keys[0]
if key.Type().Kind() != reflect.String {
return nil, fmt.Errorf("failed to build field query. invalid object key type")
}
name := key.String()
def, err := s.build(v.MapIndex(key))
if err != nil {
return nil, err
}
return &FieldQuery{
Name: name,
Fields: def.Fields,
}, nil
}
type queryKey struct{}
func FieldQueryFromContext(ctx context.Context) *FieldQuery {
query := ctx.Value(queryKey{})
if query == nil {
return nil
}
q, ok := query.(*FieldQuery)
if !ok {
return nil
}
return q
}
func SetFieldQueryToContext(ctx context.Context, query *FieldQuery) context.Context {
return context.WithValue(ctx, queryKey{}, query)
}

View File

@ -11,6 +11,341 @@ const (
msb = 0x8080808080808080
)
var needEscapeWithHTML = [256]bool{
'"': true,
'&': true,
'<': true,
'>': true,
'\\': true,
0x00: true,
0x01: true,
0x02: true,
0x03: true,
0x04: true,
0x05: true,
0x06: true,
0x07: true,
0x08: true,
0x09: true,
0x0a: true,
0x0b: true,
0x0c: true,
0x0d: true,
0x0e: true,
0x0f: true,
0x10: true,
0x11: true,
0x12: true,
0x13: true,
0x14: true,
0x15: true,
0x16: true,
0x17: true,
0x18: true,
0x19: true,
0x1a: true,
0x1b: true,
0x1c: true,
0x1d: true,
0x1e: true,
0x1f: true,
/* 0x20 - 0x7f */
0x80: true,
0x81: true,
0x82: true,
0x83: true,
0x84: true,
0x85: true,
0x86: true,
0x87: true,
0x88: true,
0x89: true,
0x8a: true,
0x8b: true,
0x8c: true,
0x8d: true,
0x8e: true,
0x8f: true,
0x90: true,
0x91: true,
0x92: true,
0x93: true,
0x94: true,
0x95: true,
0x96: true,
0x97: true,
0x98: true,
0x99: true,
0x9a: true,
0x9b: true,
0x9c: true,
0x9d: true,
0x9e: true,
0x9f: true,
0xa0: true,
0xa1: true,
0xa2: true,
0xa3: true,
0xa4: true,
0xa5: true,
0xa6: true,
0xa7: true,
0xa8: true,
0xa9: true,
0xaa: true,
0xab: true,
0xac: true,
0xad: true,
0xae: true,
0xaf: true,
0xb0: true,
0xb1: true,
0xb2: true,
0xb3: true,
0xb4: true,
0xb5: true,
0xb6: true,
0xb7: true,
0xb8: true,
0xb9: true,
0xba: true,
0xbb: true,
0xbc: true,
0xbd: true,
0xbe: true,
0xbf: true,
0xc0: true,
0xc1: true,
0xc2: true,
0xc3: true,
0xc4: true,
0xc5: true,
0xc6: true,
0xc7: true,
0xc8: true,
0xc9: true,
0xca: true,
0xcb: true,
0xcc: true,
0xcd: true,
0xce: true,
0xcf: true,
0xd0: true,
0xd1: true,
0xd2: true,
0xd3: true,
0xd4: true,
0xd5: true,
0xd6: true,
0xd7: true,
0xd8: true,
0xd9: true,
0xda: true,
0xdb: true,
0xdc: true,
0xdd: true,
0xde: true,
0xdf: true,
0xe0: true,
0xe1: true,
0xe2: true,
0xe3: true,
0xe4: true,
0xe5: true,
0xe6: true,
0xe7: true,
0xe8: true,
0xe9: true,
0xea: true,
0xeb: true,
0xec: true,
0xed: true,
0xee: true,
0xef: true,
0xf0: true,
0xf1: true,
0xf2: true,
0xf3: true,
0xf4: true,
0xf5: true,
0xf6: true,
0xf7: true,
0xf8: true,
0xf9: true,
0xfa: true,
0xfb: true,
0xfc: true,
0xfd: true,
0xfe: true,
0xff: true,
}
var needEscape = [256]bool{
'"': true,
'\\': true,
0x00: true,
0x01: true,
0x02: true,
0x03: true,
0x04: true,
0x05: true,
0x06: true,
0x07: true,
0x08: true,
0x09: true,
0x0a: true,
0x0b: true,
0x0c: true,
0x0d: true,
0x0e: true,
0x0f: true,
0x10: true,
0x11: true,
0x12: true,
0x13: true,
0x14: true,
0x15: true,
0x16: true,
0x17: true,
0x18: true,
0x19: true,
0x1a: true,
0x1b: true,
0x1c: true,
0x1d: true,
0x1e: true,
0x1f: true,
/* 0x20 - 0x7f */
0x80: true,
0x81: true,
0x82: true,
0x83: true,
0x84: true,
0x85: true,
0x86: true,
0x87: true,
0x88: true,
0x89: true,
0x8a: true,
0x8b: true,
0x8c: true,
0x8d: true,
0x8e: true,
0x8f: true,
0x90: true,
0x91: true,
0x92: true,
0x93: true,
0x94: true,
0x95: true,
0x96: true,
0x97: true,
0x98: true,
0x99: true,
0x9a: true,
0x9b: true,
0x9c: true,
0x9d: true,
0x9e: true,
0x9f: true,
0xa0: true,
0xa1: true,
0xa2: true,
0xa3: true,
0xa4: true,
0xa5: true,
0xa6: true,
0xa7: true,
0xa8: true,
0xa9: true,
0xaa: true,
0xab: true,
0xac: true,
0xad: true,
0xae: true,
0xaf: true,
0xb0: true,
0xb1: true,
0xb2: true,
0xb3: true,
0xb4: true,
0xb5: true,
0xb6: true,
0xb7: true,
0xb8: true,
0xb9: true,
0xba: true,
0xbb: true,
0xbc: true,
0xbd: true,
0xbe: true,
0xbf: true,
0xc0: true,
0xc1: true,
0xc2: true,
0xc3: true,
0xc4: true,
0xc5: true,
0xc6: true,
0xc7: true,
0xc8: true,
0xc9: true,
0xca: true,
0xcb: true,
0xcc: true,
0xcd: true,
0xce: true,
0xcf: true,
0xd0: true,
0xd1: true,
0xd2: true,
0xd3: true,
0xd4: true,
0xd5: true,
0xd6: true,
0xd7: true,
0xd8: true,
0xd9: true,
0xda: true,
0xdb: true,
0xdc: true,
0xdd: true,
0xde: true,
0xdf: true,
0xe0: true,
0xe1: true,
0xe2: true,
0xe3: true,
0xe4: true,
0xe5: true,
0xe6: true,
0xe7: true,
0xe8: true,
0xe9: true,
0xea: true,
0xeb: true,
0xec: true,
0xed: true,
0xee: true,
0xef: true,
0xf0: true,
0xf1: true,
0xf2: true,
0xf3: true,
0xf4: true,
0xf5: true,
0xf6: true,
0xf7: true,
0xf8: true,
0xf9: true,
0xfa: true,
0xfb: true,
0xfc: true,
0xfd: true,
0xfe: true,
0xff: true,
}
var hex = "0123456789abcdef"
//nolint:govet
@ -23,19 +358,9 @@ func stringToUint64Slice(s string) []uint64 {
}
func AppendString(ctx *RuntimeContext, buf []byte, s string) []byte {
if ctx.Option.Flag&HTMLEscapeOption != 0 {
if ctx.Option.Flag&NormalizeUTF8Option != 0 {
return appendNormalizedHTMLString(buf, s)
}
return appendHTMLString(buf, s)
if ctx.Option.Flag&HTMLEscapeOption == 0 {
return appendString(buf, s)
}
if ctx.Option.Flag&NormalizeUTF8Option != 0 {
return appendNormalizedString(buf, s)
}
return appendString(buf, s)
}
func appendNormalizedHTMLString(buf []byte, s string) []byte {
valLen := len(s)
if valLen == 0 {
return append(buf, `""`...)
@ -62,7 +387,7 @@ func appendNormalizedHTMLString(buf []byte, s string) []byte {
}
}
for i := len(chunks) * 8; i < valLen; i++ {
if needEscapeHTMLNormalizeUTF8[s[i]] {
if needEscapeWithHTML[s[i]] {
j = i
goto ESCAPE_END
}
@ -74,7 +399,7 @@ ESCAPE_END:
for j < valLen {
c := s[j]
if !needEscapeHTMLNormalizeUTF8[c] {
if !needEscapeWithHTML[c] {
// fast path: most of the time, printable ascii characters are used
j++
continue
@ -117,217 +442,6 @@ ESCAPE_END:
j = j + 1
continue
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
buf = append(buf, s[i:j]...)
buf = append(buf, `\u00`...)
buf = append(buf, hex[c>>4], hex[c&0xF])
i = j + 1
j = j + 1
continue
}
state, size := decodeRuneInString(s[j:])
switch state {
case runeErrorState:
buf = append(buf, s[i:j]...)
buf = append(buf, `\ufffd`...)
i = j + 1
j = j + 1
continue
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
case lineSepState:
buf = append(buf, s[i:j]...)
buf = append(buf, `\u2028`...)
i = j + 3
j = j + 3
continue
case paragraphSepState:
buf = append(buf, s[i:j]...)
buf = append(buf, `\u2029`...)
i = j + 3
j = j + 3
continue
}
j += size
}
return append(append(buf, s[i:]...), '"')
}
func appendHTMLString(buf []byte, s string) []byte {
valLen := len(s)
if valLen == 0 {
return append(buf, `""`...)
}
buf = append(buf, '"')
var (
i, j int
)
if valLen >= 8 {
chunks := stringToUint64Slice(s)
for _, n := range chunks {
// combine masks before checking for the MSB of each byte. We include
// `n` in the mask to check whether any of the *input* byte MSBs were
// set (i.e. the byte was outside the ASCII range).
mask := n | (n - (lsb * 0x20)) |
((n ^ (lsb * '"')) - lsb) |
((n ^ (lsb * '\\')) - lsb) |
((n ^ (lsb * '<')) - lsb) |
((n ^ (lsb * '>')) - lsb) |
((n ^ (lsb * '&')) - lsb)
if (mask & msb) != 0 {
j = bits.TrailingZeros64(mask&msb) / 8
goto ESCAPE_END
}
}
for i := len(chunks) * 8; i < valLen; i++ {
if needEscapeHTML[s[i]] {
j = i
goto ESCAPE_END
}
}
// no found any escape characters.
return append(append(buf, s...), '"')
}
ESCAPE_END:
for j < valLen {
c := s[j]
if !needEscapeHTML[c] {
// fast path: most of the time, printable ascii characters are used
j++
continue
}
switch c {
case '\\', '"':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', c)
i = j + 1
j = j + 1
continue
case '\n':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 'n')
i = j + 1
j = j + 1
continue
case '\r':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 'r')
i = j + 1
j = j + 1
continue
case '\t':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 't')
i = j + 1
j = j + 1
continue
case '<', '>', '&':
buf = append(buf, s[i:j]...)
buf = append(buf, `\u00`...)
buf = append(buf, hex[c>>4], hex[c&0xF])
i = j + 1
j = j + 1
continue
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
buf = append(buf, s[i:j]...)
buf = append(buf, `\u00`...)
buf = append(buf, hex[c>>4], hex[c&0xF])
i = j + 1
j = j + 1
continue
}
j++
}
return append(append(buf, s[i:]...), '"')
}
func appendNormalizedString(buf []byte, s string) []byte {
valLen := len(s)
if valLen == 0 {
return append(buf, `""`...)
}
buf = append(buf, '"')
var (
i, j int
)
if valLen >= 8 {
chunks := stringToUint64Slice(s)
for _, n := range chunks {
// combine masks before checking for the MSB of each byte. We include
// `n` in the mask to check whether any of the *input* byte MSBs were
// set (i.e. the byte was outside the ASCII range).
mask := n | (n - (lsb * 0x20)) |
((n ^ (lsb * '"')) - lsb) |
((n ^ (lsb * '\\')) - lsb)
if (mask & msb) != 0 {
j = bits.TrailingZeros64(mask&msb) / 8
goto ESCAPE_END
}
}
valLen := len(s)
for i := len(chunks) * 8; i < valLen; i++ {
if needEscapeNormalizeUTF8[s[i]] {
j = i
goto ESCAPE_END
}
}
return append(append(buf, s...), '"')
}
ESCAPE_END:
for j < valLen {
c := s[j]
if !needEscapeNormalizeUTF8[c] {
// fast path: most of the time, printable ascii characters are used
j++
continue
}
switch c {
case '\\', '"':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', c)
i = j + 1
j = j + 1
continue
case '\n':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 'n')
i = j + 1
j = j + 1
continue
case '\r':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 'r')
i = j + 1
j = j + 1
continue
case '\t':
buf = append(buf, s[i:j]...)
buf = append(buf, '\\', 't')
i = j + 1
j = j + 1
continue
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
buf = append(buf, s[i:j]...)
@ -443,6 +557,14 @@ ESCAPE_END:
j = j + 1
continue
case '<', '>', '&':
buf = append(buf, s[i:j]...)
buf = append(buf, `\u00`...)
buf = append(buf, hex[c>>4], hex[c&0xF])
i = j + 1
j = j + 1
continue
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
buf = append(buf, s[i:j]...)
@ -452,7 +574,36 @@ ESCAPE_END:
j = j + 1
continue
}
j++
state, size := decodeRuneInString(s[j:])
switch state {
case runeErrorState:
buf = append(buf, s[i:j]...)
buf = append(buf, `\ufffd`...)
i = j + 1
j = j + 1
continue
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
case lineSepState:
buf = append(buf, s[i:j]...)
buf = append(buf, `\u2028`...)
i = j + 3
j = j + 3
continue
case paragraphSepState:
buf = append(buf, s[i:j]...)
buf = append(buf, `\u2029`...)
i = j + 3
j = j + 3
continue
}
j += size
}
return append(append(buf, s[i:]...), '"')

View File

@ -1,415 +0,0 @@
package encoder
var needEscapeHTMLNormalizeUTF8 = [256]bool{
'"': true,
'&': true,
'<': true,
'>': true,
'\\': true,
0x00: true,
0x01: true,
0x02: true,
0x03: true,
0x04: true,
0x05: true,
0x06: true,
0x07: true,
0x08: true,
0x09: true,
0x0a: true,
0x0b: true,
0x0c: true,
0x0d: true,
0x0e: true,
0x0f: true,
0x10: true,
0x11: true,
0x12: true,
0x13: true,
0x14: true,
0x15: true,
0x16: true,
0x17: true,
0x18: true,
0x19: true,
0x1a: true,
0x1b: true,
0x1c: true,
0x1d: true,
0x1e: true,
0x1f: true,
/* 0x20 - 0x7f */
0x80: true,
0x81: true,
0x82: true,
0x83: true,
0x84: true,
0x85: true,
0x86: true,
0x87: true,
0x88: true,
0x89: true,
0x8a: true,
0x8b: true,
0x8c: true,
0x8d: true,
0x8e: true,
0x8f: true,
0x90: true,
0x91: true,
0x92: true,
0x93: true,
0x94: true,
0x95: true,
0x96: true,
0x97: true,
0x98: true,
0x99: true,
0x9a: true,
0x9b: true,
0x9c: true,
0x9d: true,
0x9e: true,
0x9f: true,
0xa0: true,
0xa1: true,
0xa2: true,
0xa3: true,
0xa4: true,
0xa5: true,
0xa6: true,
0xa7: true,
0xa8: true,
0xa9: true,
0xaa: true,
0xab: true,
0xac: true,
0xad: true,
0xae: true,
0xaf: true,
0xb0: true,
0xb1: true,
0xb2: true,
0xb3: true,
0xb4: true,
0xb5: true,
0xb6: true,
0xb7: true,
0xb8: true,
0xb9: true,
0xba: true,
0xbb: true,
0xbc: true,
0xbd: true,
0xbe: true,
0xbf: true,
0xc0: true,
0xc1: true,
0xc2: true,
0xc3: true,
0xc4: true,
0xc5: true,
0xc6: true,
0xc7: true,
0xc8: true,
0xc9: true,
0xca: true,
0xcb: true,
0xcc: true,
0xcd: true,
0xce: true,
0xcf: true,
0xd0: true,
0xd1: true,
0xd2: true,
0xd3: true,
0xd4: true,
0xd5: true,
0xd6: true,
0xd7: true,
0xd8: true,
0xd9: true,
0xda: true,
0xdb: true,
0xdc: true,
0xdd: true,
0xde: true,
0xdf: true,
0xe0: true,
0xe1: true,
0xe2: true,
0xe3: true,
0xe4: true,
0xe5: true,
0xe6: true,
0xe7: true,
0xe8: true,
0xe9: true,
0xea: true,
0xeb: true,
0xec: true,
0xed: true,
0xee: true,
0xef: true,
0xf0: true,
0xf1: true,
0xf2: true,
0xf3: true,
0xf4: true,
0xf5: true,
0xf6: true,
0xf7: true,
0xf8: true,
0xf9: true,
0xfa: true,
0xfb: true,
0xfc: true,
0xfd: true,
0xfe: true,
0xff: true,
}
var needEscapeNormalizeUTF8 = [256]bool{
'"': true,
'\\': true,
0x00: true,
0x01: true,
0x02: true,
0x03: true,
0x04: true,
0x05: true,
0x06: true,
0x07: true,
0x08: true,
0x09: true,
0x0a: true,
0x0b: true,
0x0c: true,
0x0d: true,
0x0e: true,
0x0f: true,
0x10: true,
0x11: true,
0x12: true,
0x13: true,
0x14: true,
0x15: true,
0x16: true,
0x17: true,
0x18: true,
0x19: true,
0x1a: true,
0x1b: true,
0x1c: true,
0x1d: true,
0x1e: true,
0x1f: true,
/* 0x20 - 0x7f */
0x80: true,
0x81: true,
0x82: true,
0x83: true,
0x84: true,
0x85: true,
0x86: true,
0x87: true,
0x88: true,
0x89: true,
0x8a: true,
0x8b: true,
0x8c: true,
0x8d: true,
0x8e: true,
0x8f: true,
0x90: true,
0x91: true,
0x92: true,
0x93: true,
0x94: true,
0x95: true,
0x96: true,
0x97: true,
0x98: true,
0x99: true,
0x9a: true,
0x9b: true,
0x9c: true,
0x9d: true,
0x9e: true,
0x9f: true,
0xa0: true,
0xa1: true,
0xa2: true,
0xa3: true,
0xa4: true,
0xa5: true,
0xa6: true,
0xa7: true,
0xa8: true,
0xa9: true,
0xaa: true,
0xab: true,
0xac: true,
0xad: true,
0xae: true,
0xaf: true,
0xb0: true,
0xb1: true,
0xb2: true,
0xb3: true,
0xb4: true,
0xb5: true,
0xb6: true,
0xb7: true,
0xb8: true,
0xb9: true,
0xba: true,
0xbb: true,
0xbc: true,
0xbd: true,
0xbe: true,
0xbf: true,
0xc0: true,
0xc1: true,
0xc2: true,
0xc3: true,
0xc4: true,
0xc5: true,
0xc6: true,
0xc7: true,
0xc8: true,
0xc9: true,
0xca: true,
0xcb: true,
0xcc: true,
0xcd: true,
0xce: true,
0xcf: true,
0xd0: true,
0xd1: true,
0xd2: true,
0xd3: true,
0xd4: true,
0xd5: true,
0xd6: true,
0xd7: true,
0xd8: true,
0xd9: true,
0xda: true,
0xdb: true,
0xdc: true,
0xdd: true,
0xde: true,
0xdf: true,
0xe0: true,
0xe1: true,
0xe2: true,
0xe3: true,
0xe4: true,
0xe5: true,
0xe6: true,
0xe7: true,
0xe8: true,
0xe9: true,
0xea: true,
0xeb: true,
0xec: true,
0xed: true,
0xee: true,
0xef: true,
0xf0: true,
0xf1: true,
0xf2: true,
0xf3: true,
0xf4: true,
0xf5: true,
0xf6: true,
0xf7: true,
0xf8: true,
0xf9: true,
0xfa: true,
0xfb: true,
0xfc: true,
0xfd: true,
0xfe: true,
0xff: true,
}
var needEscapeHTML = [256]bool{
'"': true,
'&': true,
'<': true,
'>': true,
'\\': true,
0x00: true,
0x01: true,
0x02: true,
0x03: true,
0x04: true,
0x05: true,
0x06: true,
0x07: true,
0x08: true,
0x09: true,
0x0a: true,
0x0b: true,
0x0c: true,
0x0d: true,
0x0e: true,
0x0f: true,
0x10: true,
0x11: true,
0x12: true,
0x13: true,
0x14: true,
0x15: true,
0x16: true,
0x17: true,
0x18: true,
0x19: true,
0x1a: true,
0x1b: true,
0x1c: true,
0x1d: true,
0x1e: true,
0x1f: true,
/* 0x20 - 0xff */
}
var needEscape = [256]bool{
'"': true,
'\\': true,
0x00: true,
0x01: true,
0x02: true,
0x03: true,
0x04: true,
0x05: true,
0x06: true,
0x07: true,
0x08: true,
0x09: true,
0x0a: true,
0x0b: true,
0x0c: true,
0x0d: true,
0x0e: true,
0x0f: true,
0x10: true,
0x11: true,
0x12: true,
0x13: true,
0x14: true,
0x15: true,
0x16: true,
0x17: true,
0x18: true,
0x19: true,
0x1a: true,
0x1b: true,
0x1c: true,
0x1d: true,
0x1e: true,
0x1f: true,
/* 0x20 - 0xff */
}

View File

@ -16,17 +16,16 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet)
}
if err := recover(); err != nil {
w := ctx.Option.DebugOut
fmt.Fprintln(w, "=============[DEBUG]===============")
fmt.Fprintln(w, "* [TYPE]")
fmt.Fprintln(w, codeSet.Type)
fmt.Fprintf(w, "\n")
fmt.Fprintln(w, "* [ALL OPCODE]")
fmt.Fprintln(w, code.Dump())
fmt.Fprintf(w, "\n")
fmt.Fprintln(w, "* [CONTEXT]")
fmt.Fprintf(w, "%+v\n", ctx)
fmt.Fprintln(w, "===================================")
fmt.Println("=============[DEBUG]===============")
fmt.Println("* [TYPE]")
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
fmt.Println("===================================")
panic(err)
}
}()

View File

@ -3,7 +3,6 @@ package vm
import (
"math"
"reflect"
"sort"
"unsafe"
@ -195,15 +194,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
typ = iface.typ
}
if ifacePtr == nil {
isDirectedNil := typ != nil && typ.Kind() == reflect.Struct && !runtime.IfaceIndir(typ)
if !isDirectedNil {
b = appendNullComma(ctx, b)
code = code.Next
break
}
b = appendNullComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(ctx, uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -222,7 +218,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent
indentDiffFromTop := c.Indent - 1
ctx.BaseIndent += code.Indent - indentDiffFromTop
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
@ -406,12 +403,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
break
}
b = appendStructHead(ctx, b)
unorderedMap := (ctx.Option.Flag & encoder.UnorderedMapOption) != 0
mapCtx := encoder.NewMapContext(mlen, unorderedMap)
mapCtx := encoder.NewMapContext(mlen)
mapiterinit(code.Type, uptr, &mapCtx.Iter)
store(ctxptr, code.Idx, uintptr(unsafe.Pointer(mapCtx)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
if unorderedMap {
if (ctx.Option.Flag & encoder.UnorderedMapOption) != 0 {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx.Start = len(b)
@ -709,15 +705,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
u64 := ptrToUint64(p, code.NumBitSize)
u64 := ptrToUint64(p+uintptr(code.Offset), code.NumBitSize)
v := u64 & ((1 << code.NumBitSize) - 1)
if v == 0 {
code = code.NextField
} else {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendInt(ctx, b, p, code)
b = appendInt(ctx, b, p+uintptr(code.Offset), code)
b = append(b, '"')
b = appendComma(ctx, b)
code = code.Next
@ -2958,10 +2953,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3000,10 +2994,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
iface := ptrToInterface(code, p)
@ -3121,10 +3114,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3163,10 +3155,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {

View File

@ -16,17 +16,16 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet)
defer func() {
if err := recover(); err != nil {
w := ctx.Option.DebugOut
fmt.Fprintln(w, "=============[DEBUG]===============")
fmt.Fprintln(w, "* [TYPE]")
fmt.Fprintln(w, codeSet.Type)
fmt.Fprintf(w, "\n")
fmt.Fprintln(w, "* [ALL OPCODE]")
fmt.Fprintln(w, code.Dump())
fmt.Fprintf(w, "\n")
fmt.Fprintln(w, "* [CONTEXT]")
fmt.Fprintf(w, "%+v\n", ctx)
fmt.Fprintln(w, "===================================")
fmt.Println("=============[DEBUG]===============")
fmt.Println("* [TYPE]")
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
fmt.Println("===================================")
panic(err)
}
}()

View File

@ -3,7 +3,6 @@ package vm_color
import (
"math"
"reflect"
"sort"
"unsafe"
@ -195,15 +194,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
typ = iface.typ
}
if ifacePtr == nil {
isDirectedNil := typ != nil && typ.Kind() == reflect.Struct && !runtime.IfaceIndir(typ)
if !isDirectedNil {
b = appendNullComma(ctx, b)
code = code.Next
break
}
b = appendNullComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(ctx, uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -222,7 +218,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent
indentDiffFromTop := c.Indent - 1
ctx.BaseIndent += code.Indent - indentDiffFromTop
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
@ -406,12 +403,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
break
}
b = appendStructHead(ctx, b)
unorderedMap := (ctx.Option.Flag & encoder.UnorderedMapOption) != 0
mapCtx := encoder.NewMapContext(mlen, unorderedMap)
mapCtx := encoder.NewMapContext(mlen)
mapiterinit(code.Type, uptr, &mapCtx.Iter)
store(ctxptr, code.Idx, uintptr(unsafe.Pointer(mapCtx)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
if unorderedMap {
if (ctx.Option.Flag & encoder.UnorderedMapOption) != 0 {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx.Start = len(b)
@ -709,15 +705,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
u64 := ptrToUint64(p, code.NumBitSize)
u64 := ptrToUint64(p+uintptr(code.Offset), code.NumBitSize)
v := u64 & ((1 << code.NumBitSize) - 1)
if v == 0 {
code = code.NextField
} else {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendInt(ctx, b, p, code)
b = appendInt(ctx, b, p+uintptr(code.Offset), code)
b = append(b, '"')
b = appendComma(ctx, b)
code = code.Next
@ -2958,10 +2953,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3000,10 +2994,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
iface := ptrToInterface(code, p)
@ -3121,10 +3114,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3163,10 +3155,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {

View File

@ -16,17 +16,16 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet)
defer func() {
if err := recover(); err != nil {
w := ctx.Option.DebugOut
fmt.Fprintln(w, "=============[DEBUG]===============")
fmt.Fprintln(w, "* [TYPE]")
fmt.Fprintln(w, codeSet.Type)
fmt.Fprintf(w, "\n")
fmt.Fprintln(w, "* [ALL OPCODE]")
fmt.Fprintln(w, code.Dump())
fmt.Fprintf(w, "\n")
fmt.Fprintln(w, "* [CONTEXT]")
fmt.Fprintf(w, "%+v\n", ctx)
fmt.Fprintln(w, "===================================")
fmt.Println("=============[DEBUG]===============")
fmt.Println("* [TYPE]")
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
fmt.Println("===================================")
panic(err)
}
}()

View File

@ -3,7 +3,6 @@ package vm_color_indent
import (
"math"
"reflect"
"sort"
"unsafe"
@ -195,15 +194,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
typ = iface.typ
}
if ifacePtr == nil {
isDirectedNil := typ != nil && typ.Kind() == reflect.Struct && !runtime.IfaceIndir(typ)
if !isDirectedNil {
b = appendNullComma(ctx, b)
code = code.Next
break
}
b = appendNullComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(ctx, uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -222,7 +218,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent
indentDiffFromTop := c.Indent - 1
ctx.BaseIndent += code.Indent - indentDiffFromTop
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
@ -406,12 +403,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
break
}
b = appendStructHead(ctx, b)
unorderedMap := (ctx.Option.Flag & encoder.UnorderedMapOption) != 0
mapCtx := encoder.NewMapContext(mlen, unorderedMap)
mapCtx := encoder.NewMapContext(mlen)
mapiterinit(code.Type, uptr, &mapCtx.Iter)
store(ctxptr, code.Idx, uintptr(unsafe.Pointer(mapCtx)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
if unorderedMap {
if (ctx.Option.Flag & encoder.UnorderedMapOption) != 0 {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx.Start = len(b)
@ -709,15 +705,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
u64 := ptrToUint64(p, code.NumBitSize)
u64 := ptrToUint64(p+uintptr(code.Offset), code.NumBitSize)
v := u64 & ((1 << code.NumBitSize) - 1)
if v == 0 {
code = code.NextField
} else {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendInt(ctx, b, p, code)
b = appendInt(ctx, b, p+uintptr(code.Offset), code)
b = append(b, '"')
b = appendComma(ctx, b)
code = code.Next
@ -2958,10 +2953,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3000,10 +2994,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
iface := ptrToInterface(code, p)
@ -3121,10 +3114,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3163,10 +3155,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {

View File

@ -16,17 +16,16 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet)
defer func() {
if err := recover(); err != nil {
w := ctx.Option.DebugOut
fmt.Fprintln(w, "=============[DEBUG]===============")
fmt.Fprintln(w, "* [TYPE]")
fmt.Fprintln(w, codeSet.Type)
fmt.Fprintf(w, "\n")
fmt.Fprintln(w, "* [ALL OPCODE]")
fmt.Fprintln(w, code.Dump())
fmt.Fprintf(w, "\n")
fmt.Fprintln(w, "* [CONTEXT]")
fmt.Fprintf(w, "%+v\n", ctx)
fmt.Fprintln(w, "===================================")
fmt.Println("=============[DEBUG]===============")
fmt.Println("* [TYPE]")
fmt.Println(codeSet.Type)
fmt.Printf("\n")
fmt.Println("* [ALL OPCODE]")
fmt.Println(code.Dump())
fmt.Printf("\n")
fmt.Println("* [CONTEXT]")
fmt.Printf("%+v\n", ctx)
fmt.Println("===================================")
panic(err)
}
}()

View File

@ -3,7 +3,6 @@ package vm_indent
import (
"math"
"reflect"
"sort"
"unsafe"
@ -195,15 +194,12 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
typ = iface.typ
}
if ifacePtr == nil {
isDirectedNil := typ != nil && typ.Kind() == reflect.Struct && !runtime.IfaceIndir(typ)
if !isDirectedNil {
b = appendNullComma(ctx, b)
code = code.Next
break
}
b = appendNullComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(ctx, uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -222,7 +218,8 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
oldBaseIndent := ctx.BaseIndent
ctx.BaseIndent += code.Indent
indentDiffFromTop := c.Indent - 1
ctx.BaseIndent += code.Indent - indentDiffFromTop
newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen {
@ -406,12 +403,11 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
break
}
b = appendStructHead(ctx, b)
unorderedMap := (ctx.Option.Flag & encoder.UnorderedMapOption) != 0
mapCtx := encoder.NewMapContext(mlen, unorderedMap)
mapCtx := encoder.NewMapContext(mlen)
mapiterinit(code.Type, uptr, &mapCtx.Iter)
store(ctxptr, code.Idx, uintptr(unsafe.Pointer(mapCtx)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(mapCtx))
if unorderedMap {
if (ctx.Option.Flag & encoder.UnorderedMapOption) != 0 {
b = appendMapKeyIndent(ctx, code.Next, b)
} else {
mapCtx.Start = len(b)
@ -709,15 +705,14 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
u64 := ptrToUint64(p, code.NumBitSize)
u64 := ptrToUint64(p+uintptr(code.Offset), code.NumBitSize)
v := u64 & ((1 << code.NumBitSize) - 1)
if v == 0 {
code = code.NextField
} else {
b = appendStructKey(ctx, code, b)
b = append(b, '"')
b = appendInt(ctx, b, p, code)
b = appendInt(ctx, b, p+uintptr(code.Offset), code)
b = append(b, '"')
b = appendComma(ctx, b)
code = code.Next
@ -2958,10 +2953,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3000,10 +2994,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalJSON {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
iface := ptrToInterface(code, p)
@ -3121,10 +3114,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
b = appendStructHead(ctx, b)
}
b = appendStructKey(ctx, code, b)
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {
@ -3163,10 +3155,9 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
if code.Flags&encoder.AnonymousHeadFlags == 0 {
b = appendStructHead(ctx, b)
}
p += uintptr(code.Offset)
if (code.Flags & encoder.IsNilableTypeFlags) != 0 {
if (code.Flags&encoder.IndirectFlags) != 0 || code.Op == encoder.OpStructPtrHeadOmitEmptyMarshalText {
p = ptrToPtr(p)
p = ptrToPtr(p + uintptr(code.Offset))
}
}
if p == 0 && (code.Flags&encoder.NilCheckFlags) != 0 {

View File

@ -162,22 +162,3 @@ func ErrInvalidBeginningOfValue(c byte, cursor int64) *SyntaxError {
Offset: cursor,
}
}
type PathError struct {
msg string
}
func (e *PathError) Error() string {
return fmt.Sprintf("json: invalid path format: %s", e.msg)
}
func ErrInvalidPath(msg string, args ...interface{}) *PathError {
if len(args) != 0 {
return &PathError{msg: fmt.Sprintf(msg, args...)}
}
return &PathError{msg: msg}
}
func ErrEmptyPath() *PathError {
return &PathError{msg: "path is empty"}
}

View File

@ -13,11 +13,7 @@ func getTag(field reflect.StructField) string {
func IsIgnoredStructField(field reflect.StructField) bool {
if field.PkgPath != "" {
if field.Anonymous {
t := field.Type
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
if !(field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct) && field.Type.Kind() != reflect.Struct {
return true
}
} else {

View File

@ -364,8 +364,3 @@ func Valid(data []byte) bool {
}
return decoder.InputOffset() >= int64(len(data))
}
func init() {
encoder.Marshal = Marshal
encoder.Unmarshal = Unmarshal
}

View File

@ -1,8 +1,6 @@
package json
import (
"io"
"github.com/goccy/go-json/internal/decoder"
"github.com/goccy/go-json/internal/encoder"
)
@ -17,23 +15,6 @@ func UnorderedMap() EncodeOptionFunc {
}
}
// DisableHTMLEscape disables escaping of HTML characters ( '&', '<', '>' ) when encoding string.
func DisableHTMLEscape() EncodeOptionFunc {
return func(opt *EncodeOption) {
opt.Flag &= ^encoder.HTMLEscapeOption
}
}
// DisableNormalizeUTF8
// By default, when encoding string, UTF8 characters in the range of 0x80 - 0xFF are processed by applying \ufffd for invalid code and escaping for \u2028 and \u2029.
// This option disables this behaviour. You can expect faster speeds by applying this option, but be careful.
// encoding/json implements here: https://github.com/golang/go/blob/6178d25fc0b28724b1b5aec2b1b74fc06d9294c7/src/encoding/json/encode.go#L1067-L1093.
func DisableNormalizeUTF8() EncodeOptionFunc {
return func(opt *EncodeOption) {
opt.Flag &= ^encoder.NormalizeUTF8Option
}
}
// Debug outputs debug information when panic occurs during encoding.
func Debug() EncodeOptionFunc {
return func(opt *EncodeOption) {
@ -41,13 +22,6 @@ func Debug() EncodeOptionFunc {
}
}
// DebugWith sets the destination to write debug messages.
func DebugWith(w io.Writer) EncodeOptionFunc {
return func(opt *EncodeOption) {
opt.DebugOut = w
}
}
// Colorize add an identifier for coloring to the string of the encoded result.
func Colorize(scheme *ColorScheme) EncodeOptionFunc {
return func(opt *EncodeOption) {

84
path.go
View File

@ -1,84 +0,0 @@
package json
import (
"reflect"
"github.com/goccy/go-json/internal/decoder"
)
// CreatePath creates JSON Path.
//
// JSON Path rule
// $ : root object or element. The JSON Path format must start with this operator, which refers to the outermost level of the JSON-formatted string.
// . : child operator. You can identify child values using dot-notation.
// .. : recursive descent.
// [] : subscript operator. If the JSON object is an array, you can use brackets to specify the array index.
// [*] : all objects/elements for array.
//
// Reserved words must be properly escaped when included in Path.
//
// Escape Rule
// single quote style escape: e.g.) `$['a.b'].c`
// double quote style escape: e.g.) `$."a.b".c`
func CreatePath(p string) (*Path, error) {
path, err := decoder.PathString(p).Build()
if err != nil {
return nil, err
}
return &Path{path: path}, nil
}
// Path represents JSON Path.
type Path struct {
path *decoder.Path
}
// RootSelectorOnly whether only the root selector ($) is used.
func (p *Path) RootSelectorOnly() bool {
return p.path.RootSelectorOnly
}
// UsedSingleQuotePathSelector whether single quote-based escaping was done when building the JSON Path.
func (p *Path) UsedSingleQuotePathSelector() bool {
return p.path.SingleQuotePathSelector
}
// UsedSingleQuotePathSelector whether double quote-based escaping was done when building the JSON Path.
func (p *Path) UsedDoubleQuotePathSelector() bool {
return p.path.DoubleQuotePathSelector
}
// Extract extracts a specific JSON string.
func (p *Path) Extract(data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) {
return extractFromPath(p, data, optFuncs...)
}
// PathString returns original JSON Path string.
func (p *Path) PathString() string {
return p.path.String()
}
// Unmarshal extract and decode the value of the part corresponding to JSON Path from the input data.
func (p *Path) Unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
contents, err := extractFromPath(p, data, optFuncs...)
if err != nil {
return err
}
results := make([]interface{}, 0, len(contents))
for _, content := range contents {
var result interface{}
if err := Unmarshal(content, &result); err != nil {
return err
}
results = append(results, result)
}
if err := decoder.AssignValue(reflect.ValueOf(results), reflect.ValueOf(v)); err != nil {
return err
}
return nil
}
// Get extract and substitute the value of the part corresponding to JSON Path from the input value.
func (p *Path) Get(src, dst interface{}) error {
return p.path.Get(reflect.ValueOf(src), reflect.ValueOf(dst))
}

View File

@ -1,234 +0,0 @@
package json_test
import (
"bytes"
"reflect"
"sort"
"testing"
"github.com/goccy/go-json"
)
func TestExtractPath(t *testing.T) {
src := []byte(`{"a":{"b":10,"c":true},"b":"text"}`)
t.Run("$.a.b", func(t *testing.T) {
path, err := json.CreatePath("$.a.b")
if err != nil {
t.Fatal(err)
}
contents, err := path.Extract(src)
if err != nil {
t.Fatal(err)
}
if len(contents) != 1 {
t.Fatal("failed to extract")
}
if !bytes.Equal(contents[0], []byte("10")) {
t.Fatal("failed to extract")
}
})
t.Run("$.b", func(t *testing.T) {
path, err := json.CreatePath("$.b")
if err != nil {
t.Fatal(err)
}
contents, err := path.Extract(src)
if err != nil {
t.Fatal(err)
}
if len(contents) != 1 {
t.Fatal("failed to extract")
}
if !bytes.Equal(contents[0], []byte(`"text"`)) {
t.Fatal("failed to extract")
}
})
t.Run("$.a", func(t *testing.T) {
path, err := json.CreatePath("$.a")
if err != nil {
t.Fatal(err)
}
contents, err := path.Extract(src)
if err != nil {
t.Fatal(err)
}
if len(contents) != 1 {
t.Fatal("failed to extract")
}
if !bytes.Equal(contents[0], []byte(`{"b":10,"c":true}`)) {
t.Fatal("failed to extract")
}
})
}
func TestUnmarshalPath(t *testing.T) {
t.Run("int", func(t *testing.T) {
src := []byte(`{"a":{"b":10,"c":true},"b":"text"}`)
t.Run("success", func(t *testing.T) {
path, err := json.CreatePath("$.a.b")
if err != nil {
t.Fatal(err)
}
var v int
if err := path.Unmarshal(src, &v); err != nil {
t.Fatal(err)
}
if v != 10 {
t.Fatal("failed to unmarshal path")
}
})
t.Run("failure", func(t *testing.T) {
path, err := json.CreatePath("$.a.c")
if err != nil {
t.Fatal(err)
}
var v map[string]interface{}
if err := path.Unmarshal(src, &v); err == nil {
t.Fatal("expected error")
}
})
})
t.Run("bool", func(t *testing.T) {
src := []byte(`{"a":{"b":10,"c":true},"b":"text"}`)
t.Run("success", func(t *testing.T) {
path, err := json.CreatePath("$.a.c")
if err != nil {
t.Fatal(err)
}
var v bool
if err := path.Unmarshal(src, &v); err != nil {
t.Fatal(err)
}
if !v {
t.Fatal("failed to unmarshal path")
}
})
t.Run("failure", func(t *testing.T) {
path, err := json.CreatePath("$.a.b")
if err != nil {
t.Fatal(err)
}
var v bool
if err := path.Unmarshal(src, &v); err == nil {
t.Fatal("expected error")
}
})
})
t.Run("map", func(t *testing.T) {
src := []byte(`{"a":{"b":10,"c":true},"b":"text"}`)
t.Run("success", func(t *testing.T) {
path, err := json.CreatePath("$.a")
if err != nil {
t.Fatal(err)
}
var v map[string]interface{}
if err := path.Unmarshal(src, &v); err != nil {
t.Fatal(err)
}
if len(v) != 2 {
t.Fatal("failed to decode map")
}
})
})
t.Run("path with single quote selector", func(t *testing.T) {
path, err := json.CreatePath("$['a.b'].c")
if err != nil {
t.Fatal(err)
}
var v string
if err := path.Unmarshal([]byte(`{"a.b": {"c": "world"}}`), &v); err != nil {
t.Fatal(err)
}
if v != "world" {
t.Fatal("failed to unmarshal path")
}
})
t.Run("path with double quote selector", func(t *testing.T) {
path, err := json.CreatePath(`$."a.b".c`)
if err != nil {
t.Fatal(err)
}
var v string
if err := path.Unmarshal([]byte(`{"a.b": {"c": "world"}}`), &v); err != nil {
t.Fatal(err)
}
if v != "world" {
t.Fatal("failed to unmarshal path")
}
})
}
func TestGetPath(t *testing.T) {
t.Run("selector", func(t *testing.T) {
var v interface{}
if err := json.Unmarshal([]byte(`{"a":{"b":10,"c":true},"b":"text"}`), &v); err != nil {
t.Fatal(err)
}
path, err := json.CreatePath("$.a.b")
if err != nil {
t.Fatal(err)
}
var b int
if err := path.Get(v, &b); err != nil {
t.Fatal(err)
}
if b != 10 {
t.Fatalf("failed to decode by json.Get")
}
})
t.Run("index", func(t *testing.T) {
var v interface{}
if err := json.Unmarshal([]byte(`{"a":[{"b":10,"c":true},{"b":"text"}]}`), &v); err != nil {
t.Fatal(err)
}
path, err := json.CreatePath("$.a[0].b")
if err != nil {
t.Fatal(err)
}
var b int
if err := path.Get(v, &b); err != nil {
t.Fatal(err)
}
if b != 10 {
t.Fatalf("failed to decode by json.Get")
}
})
t.Run("indexAll", func(t *testing.T) {
var v interface{}
if err := json.Unmarshal([]byte(`{"a":[{"b":1,"c":true},{"b":2},{"b":3}]}`), &v); err != nil {
t.Fatal(err)
}
path, err := json.CreatePath("$.a[*].b")
if err != nil {
t.Fatal(err)
}
var b []int
if err := path.Get(v, &b); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(b, []int{1, 2, 3}) {
t.Fatalf("failed to decode by json.Get")
}
})
t.Run("recursive", func(t *testing.T) {
var v interface{}
if err := json.Unmarshal([]byte(`{"a":[{"b":1,"c":true},{"b":2},{"b":3}],"a2":{"b":4}}`), &v); err != nil {
t.Fatal(err)
}
path, err := json.CreatePath("$..b")
if err != nil {
t.Fatal(err)
}
var b []int
if err := path.Get(v, &b); err != nil {
t.Fatal(err)
}
sort.Ints(b)
if !reflect.DeepEqual(b, []int{1, 2, 3, 4}) {
t.Fatalf("failed to decode by json.Get")
}
})
}

View File

@ -1,47 +0,0 @@
package json
import (
"github.com/goccy/go-json/internal/encoder"
)
type (
// FieldQuery you can dynamically filter the fields in the structure by creating a FieldQuery,
// adding it to context.Context using SetFieldQueryToContext and then passing it to MarshalContext.
// This is a type-safe operation, so it is faster than filtering using map[string]interface{}.
FieldQuery = encoder.FieldQuery
FieldQueryString = encoder.FieldQueryString
)
var (
// FieldQueryFromContext get current FieldQuery from context.Context.
FieldQueryFromContext = encoder.FieldQueryFromContext
// SetFieldQueryToContext set current FieldQuery to context.Context.
SetFieldQueryToContext = encoder.SetFieldQueryToContext
)
// BuildFieldQuery builds FieldQuery by fieldName or sub field query.
// First, specify the field name that you want to keep in structure type.
// If the field you want to keep is a structure type, by creating a sub field query using BuildSubFieldQuery,
// you can select the fields you want to keep in the structure.
// This description can be written recursively.
func BuildFieldQuery(fields ...FieldQueryString) (*FieldQuery, error) {
query, err := Marshal(fields)
if err != nil {
return nil, err
}
return FieldQueryString(query).Build()
}
// BuildSubFieldQuery builds sub field query.
func BuildSubFieldQuery(name string) *SubFieldQuery {
return &SubFieldQuery{name: name}
}
type SubFieldQuery struct {
name string
}
func (q *SubFieldQuery) Fields(fields ...FieldQueryString) FieldQueryString {
query, _ := Marshal(map[string][]FieldQueryString{q.name: fields})
return FieldQueryString(query)
}

View File

@ -1,121 +0,0 @@
package json_test
import (
"context"
"reflect"
"testing"
"github.com/goccy/go-json"
)
type queryTestX struct {
XA int
XB string
XC *queryTestY
XD bool
XE float32
}
type queryTestY struct {
YA int
YB string
YC *queryTestZ
YD bool
YE float32
}
type queryTestZ struct {
ZA string
ZB bool
ZC int
}
func (z *queryTestZ) MarshalJSON(ctx context.Context) ([]byte, error) {
type _queryTestZ queryTestZ
return json.MarshalContext(ctx, (*_queryTestZ)(z))
}
func TestFieldQuery(t *testing.T) {
query, err := json.BuildFieldQuery(
"XA",
"XB",
json.BuildSubFieldQuery("XC").Fields(
"YA",
"YB",
json.BuildSubFieldQuery("YC").Fields(
"ZA",
"ZB",
),
),
)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(query, &json.FieldQuery{
Fields: []*json.FieldQuery{
{
Name: "XA",
},
{
Name: "XB",
},
{
Name: "XC",
Fields: []*json.FieldQuery{
{
Name: "YA",
},
{
Name: "YB",
},
{
Name: "YC",
Fields: []*json.FieldQuery{
{
Name: "ZA",
},
{
Name: "ZB",
},
},
},
},
},
},
}) {
t.Fatal("cannot get query")
}
queryStr, err := query.QueryString()
if err != nil {
t.Fatal(err)
}
if queryStr != `["XA","XB",{"XC":["YA","YB",{"YC":["ZA","ZB"]}]}]` {
t.Fatalf("failed to create query string. %s", queryStr)
}
ctx := json.SetFieldQueryToContext(context.Background(), query)
b, err := json.MarshalContext(ctx, &queryTestX{
XA: 1,
XB: "xb",
XC: &queryTestY{
YA: 2,
YB: "yb",
YC: &queryTestZ{
ZA: "za",
ZB: true,
ZC: 3,
},
YD: true,
YE: 4,
},
XD: true,
XE: 5,
})
if err != nil {
t.Fatal(err)
}
expected := `{"XA":1,"XB":"xb","XC":{"YA":2,"YB":"yb","YC":{"ZA":"za","ZB":true}}}`
got := string(b)
if expected != got {
t.Fatalf("failed to encode with field query: expected %q but got %q", expected, got)
}
}

View File

@ -11,7 +11,7 @@ func TestOpcodeSize(t *testing.T) {
const uintptrSize = 4 << (^uintptr(0) >> 63)
if uintptrSize == 8 {
size := unsafe.Sizeof(encoder.Opcode{})
if size != 120 {
if size != 112 {
t.Fatalf("unexpected opcode size: expected 112bytes but got %dbytes", size)
}
}

View File

@ -7,7 +7,6 @@ package json_test
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"log"
@ -432,16 +431,6 @@ func TestDecodeInStream(t *testing.T) {
}
}
func TestDecodeStreamUseNumber(t *testing.T) {
dec := json.NewDecoder(strings.NewReader(`3.14`))
dec.UseNumber()
v, err := dec.Token()
if err != nil {
t.Errorf("unexpected error: %#v", err)
}
assertEq(t, "json.Number", "json.Number", fmt.Sprintf("%T", v))
}
// Test from golang.org/issue/11893
func TestHTTPDecoding(t *testing.T) {
const raw = `{ "foo": "bar" }`

View File

@ -1,96 +0,0 @@
package json_test
import (
"context"
"fmt"
"log"
"github.com/goccy/go-json"
)
type User struct {
ID int64
Name string
Age int
Address UserAddressResolver
}
type UserAddress struct {
UserID int64
PostCode string
City string
Address1 string
Address2 string
}
type UserRepository struct {
uaRepo *UserAddressRepository
}
func NewUserRepository() *UserRepository {
return &UserRepository{
uaRepo: NewUserAddressRepository(),
}
}
type UserAddressRepository struct{}
func NewUserAddressRepository() *UserAddressRepository {
return &UserAddressRepository{}
}
type UserAddressResolver func(context.Context) (*UserAddress, error)
func (resolver UserAddressResolver) MarshalJSON(ctx context.Context) ([]byte, error) {
address, err := resolver(ctx)
if err != nil {
return nil, err
}
return json.MarshalContext(ctx, address)
}
func (r *UserRepository) FindByID(ctx context.Context, id int64) (*User, error) {
user := &User{ID: id, Name: "Ken", Age: 20}
// resolve relation from User to UserAddress
user.Address = func(ctx context.Context) (*UserAddress, error) {
return r.uaRepo.FindByUserID(ctx, user.ID)
}
return user, nil
}
func (*UserAddressRepository) FindByUserID(ctx context.Context, id int64) (*UserAddress, error) {
return &UserAddress{
UserID: id,
City: "A",
Address1: "foo",
Address2: "bar",
}, nil
}
func Example_fieldQuery() {
ctx := context.Background()
userRepo := NewUserRepository()
user, err := userRepo.FindByID(ctx, 1)
if err != nil {
log.Fatal(err)
}
query, err := json.BuildFieldQuery(
"Name",
"Age",
json.BuildSubFieldQuery("Address").Fields(
"City",
),
)
if err != nil {
log.Fatal(err)
}
ctx = json.SetFieldQueryToContext(ctx, query)
b, err := json.MarshalContext(ctx, user)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
// Output:
// {"Name":"Ken","Age":20,"Address":{"City":"A"}}
}