forked from mirror/go-json
Compare commits
No commits in common. "master" and "feature/fix-escape-string" have entirely different histories.
master
...
feature/fi
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -48,14 +48,6 @@ linters:
|
|||
- nlreturn
|
||||
- testpackage
|
||||
- wsl
|
||||
- varnamelen
|
||||
- nilnil
|
||||
- ireturn
|
||||
- govet
|
||||
- forcetypeassert
|
||||
- cyclop
|
||||
- containedctx
|
||||
- revive
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
|
|
126
CHANGELOG.md
126
CHANGELOG.md
|
@ -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 )
|
||||
|
|
4
Makefile
4
Makefile
|
@ -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; \
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:] + "]")
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
31
decode.go
31
decode.go
|
@ -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)
|
||||
|
|
239
decode_test.go
239
decode_test.go
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
version: '2'
|
||||
services:
|
||||
go-json:
|
||||
image: golang:1.18
|
||||
image: golang:1.17
|
||||
volumes:
|
||||
- '.:/go/src/go-json'
|
||||
deploy:
|
||||
|
|
17
encode.go
17
encode.go
|
@ -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
|
||||
}
|
||||
|
|
375
encode_test.go
375
encode_test.go
|
@ -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))
|
||||
}
|
||||
|
|
2
error.go
2
error.go
|
@ -37,5 +37,3 @@ type UnmarshalTypeError = errors.UnmarshalTypeError
|
|||
type UnsupportedTypeError = errors.UnsupportedTypeError
|
||||
|
||||
type UnsupportedValueError = errors.UnsupportedValueError
|
||||
|
||||
type PathError = errors.PathError
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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 == '[' {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
package decoder
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//go:build race
|
||||
// +build race
|
||||
|
||||
package decoder
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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++
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -7,11 +7,9 @@ type OptionFlags uint8
|
|||
const (
|
||||
FirstWinOption OptionFlags = 1 << iota
|
||||
ContextOption
|
||||
PathOption
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
Flags OptionFlags
|
||||
Context context.Context
|
||||
Path *Path
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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] != '"' {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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:]...), '"')
|
||||
|
|
|
@ -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 */
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
5
json.go
5
json.go
|
@ -364,8 +364,3 @@ func Valid(data []byte) bool {
|
|||
}
|
||||
return decoder.InputOffset() >= int64(len(data))
|
||||
}
|
||||
|
||||
func init() {
|
||||
encoder.Marshal = Marshal
|
||||
encoder.Unmarshal = Unmarshal
|
||||
}
|
||||
|
|
26
option.go
26
option.go
|
@ -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
84
path.go
|
@ -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))
|
||||
}
|
234
path_test.go
234
path_test.go
|
@ -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")
|
||||
}
|
||||
})
|
||||
}
|
47
query.go
47
query.go
|
@ -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)
|
||||
}
|
121
query_test.go
121
query_test.go
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" }`
|
||||
|
|
|
@ -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"}}
|
||||
}
|
Loading…
Reference in New Issue