forked from mirror/go-json
Compare commits
2 Commits
master
...
feature/ge
Author | SHA1 | Date |
---|---|---|
Masaaki Goshima | 753dc64dff | |
Masaaki Goshima | 796f23fb0d |
|
@ -1,9 +1,5 @@
|
|||
name: Go
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
name: Build on limited environment
|
||||
|
@ -18,7 +14,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
os: [ "ubuntu-latest", "macos-latest", "windows-latest" ]
|
||||
go-version: [ "1.16", "1.17", "1.18" ]
|
||||
go-version: [ "1.14", "1.15", "1.16" ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: setup Go ${{ matrix.go-version }}
|
||||
|
@ -42,13 +38,13 @@ jobs:
|
|||
- name: setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.16
|
||||
- name: checkout ( feature )
|
||||
uses: actions/checkout@v2
|
||||
- name: run benchmark ( feature )
|
||||
run: cd benchmarks && go test -bench GoJson | tee $HOME/new.txt
|
||||
- name: install benchstat
|
||||
run: go install golang.org/x/perf/cmd/benchstat@latest
|
||||
- name: install benchcmp
|
||||
run: go get -u golang.org/x/tools/cmd/benchcmp
|
||||
- name: checkout ( master )
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
|
@ -56,7 +52,7 @@ jobs:
|
|||
- name: run benchmark ( master )
|
||||
run: cd benchmarks && go test -bench GoJson | tee $HOME/old.txt
|
||||
- name: compare benchmark results
|
||||
run: benchstat $HOME/old.txt $HOME/new.txt
|
||||
run: benchcmp $HOME/old.txt $HOME/new.txt
|
||||
coverage:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -64,12 +60,11 @@ jobs:
|
|||
- name: setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.18
|
||||
go-version: 1.16
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: measure coverage
|
||||
run: make cover
|
||||
- uses: codecov/codecov-action@v2
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
- name: report coverage
|
||||
run: |
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
name: lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
on: [pull_request, push]
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
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
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
cover.html
|
||||
cover.out
|
||||
credentials.json
|
||||
benchgraph-token.json
|
||||
|
|
|
@ -48,14 +48,6 @@ linters:
|
|||
- nlreturn
|
||||
- testpackage
|
||||
- wsl
|
||||
- varnamelen
|
||||
- nilnil
|
||||
- ireturn
|
||||
- govet
|
||||
- forcetypeassert
|
||||
- cyclop
|
||||
- containedctx
|
||||
- revive
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
|
|
234
CHANGELOG.md
234
CHANGELOG.md
|
@ -1,237 +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 )
|
||||
|
||||
# v0.8.0 - 2021/12/02
|
||||
|
||||
* Fix embedded field conflict behavior ( #300 )
|
||||
* Refactor compiler for encoder ( #301 #302 )
|
||||
|
||||
# v0.7.10 - 2021/10/16
|
||||
|
||||
* Fix conversion from pointer to uint64 ( #294 )
|
||||
|
||||
# v0.7.9 - 2021/09/28
|
||||
|
||||
* Fix encoding of nil value about interface type that has method ( #291 )
|
||||
|
||||
# v0.7.8 - 2021/09/01
|
||||
|
||||
* Fix mapassign_faststr for indirect struct type ( #283 )
|
||||
* Fix encoding of not empty interface type ( #284 )
|
||||
* Fix encoding of empty struct interface type ( #286 )
|
||||
|
||||
# v0.7.7 - 2021/08/25
|
||||
|
||||
* Fix invalid utf8 on stream decoder ( #279 )
|
||||
* Fix buffer length bug on string stream decoder ( #280 )
|
||||
|
||||
Thank you @orisano !!
|
||||
|
||||
# v0.7.6 - 2021/08/13
|
||||
|
||||
* Fix nil slice assignment ( #276 )
|
||||
* Improve error message ( #277 )
|
||||
|
||||
# v0.7.5 - 2021/08/12
|
||||
|
||||
* Fix encoding of embedded struct with tags ( #265 )
|
||||
* Fix encoding of embedded struct that isn't first field ( #272 )
|
||||
* Fix decoding of binary type with escaped char ( #273 )
|
||||
|
||||
# v0.7.4 - 2021/07/06
|
||||
|
||||
* Fix encoding of indirect layout structure ( #264 )
|
||||
|
||||
# v0.7.3 - 2021/06/29
|
||||
|
||||
* Fix encoding of pointer type in empty interface ( #262 )
|
||||
|
||||
# v0.7.2 - 2021/06/26
|
||||
|
||||
### Fix decoder
|
||||
|
||||
* Add decoder for func type to fix decoding of nil function value ( #257 )
|
||||
* Fix stream decoding of []byte type ( #258 )
|
||||
|
||||
### Performance
|
||||
|
||||
* Improve decoding performance of map[string]interface{} type ( use `mapassign_faststr` ) ( #256 )
|
||||
* Improve encoding performance of empty interface type ( remove recursive calling of `vm.Run` ) ( #259 )
|
||||
|
||||
### Benchmark
|
||||
|
||||
* Add bytedance/sonic as benchmark target ( #254 )
|
||||
|
||||
# v0.7.1 - 2021/06/18
|
||||
|
||||
### Fix decoder
|
||||
|
||||
* Fix error when unmarshal empty array ( #253 )
|
||||
|
||||
# v0.7.0 - 2021/06/12
|
||||
|
||||
### Support context for MarshalJSON and UnmarshalJSON ( #248 )
|
||||
|
||||
* json.MarshalContext(context.Context, interface{}, ...json.EncodeOption) ([]byte, error)
|
||||
* json.NewEncoder(io.Writer).EncodeContext(context.Context, interface{}, ...json.EncodeOption) error
|
||||
* json.UnmarshalContext(context.Context, []byte, interface{}, ...json.DecodeOption) error
|
||||
* json.NewDecoder(io.Reader).DecodeContext(context.Context, interface{}) error
|
||||
|
||||
```go
|
||||
type MarshalerContext interface {
|
||||
MarshalJSON(context.Context) ([]byte, error)
|
||||
}
|
||||
|
||||
type UnmarshalerContext interface {
|
||||
UnmarshalJSON(context.Context, []byte) error
|
||||
}
|
||||
```
|
||||
|
||||
### Add DecodeFieldPriorityFirstWin option ( #242 )
|
||||
|
||||
In the default behavior, go-json, like encoding/json, will reflect the result of the last evaluation when a field with the same name exists. I've added new options to allow you to change this behavior. `json.DecodeFieldPriorityFirstWin` option reflects the result of the first evaluation if a field with the same name exists. This behavior has a performance advantage as it allows the subsequent strings to be skipped if all fields have been evaluated.
|
||||
|
||||
### Fix encoder
|
||||
|
||||
* Fix indent number contains recursive type ( #249 )
|
||||
* Fix encoding of using empty interface as map key ( #244 )
|
||||
|
||||
### Fix decoder
|
||||
|
||||
* Fix decoding fields containing escaped characters ( #237 )
|
||||
|
||||
### Refactor
|
||||
|
||||
* Move some tests to subdirectory ( #243 )
|
||||
* Refactor package layout for decoder ( #238 )
|
||||
|
||||
# v0.6.1 - 2021/06/02
|
||||
|
||||
### Fix encoder
|
||||
|
|
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; \
|
||||
}
|
||||
|
||||
|
|
11
README.md
11
README.md
|
@ -13,16 +13,17 @@ Fast JSON encoder/decoder compatible with encoding/json for Go
|
|||
```
|
||||
* version ( expected release date )
|
||||
|
||||
* v0.9.0
|
||||
* v0.6.0
|
||||
|
|
||||
| while maintaining compatibility with encoding/json, we will add convenient APIs
|
||||
|
|
||||
v
|
||||
* v1.0.0
|
||||
* v1.0.0 ( 2021/06 )
|
||||
```
|
||||
|
||||
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.6.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
|
||||
|
||||
|
@ -30,8 +31,6 @@ If you have the API you need, please submit your issue [here](https://github.com
|
|||
- Fast ( See [Benchmark section](https://github.com/goccy/go-json#benchmarks) )
|
||||
- Flexible customization with options
|
||||
- Coloring the encoded string
|
||||
- Can propagate context.Context to `MarshalJSON` or `UnmarshalJSON`
|
||||
- Can dynamically filter the fields of the structure type-safely
|
||||
|
||||
# Installation
|
||||
|
||||
|
@ -184,7 +183,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.
|
||||
|
|
|
@ -33,16 +33,6 @@ func Benchmark_Decode_SmallStruct_Unmarshal_FastJson(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_SmallStruct_Unmarshal_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := SmallPayload{}
|
||||
if err := segmentiojson.Unmarshal(SmallFixture, &result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_SmallStruct_Unmarshal_JsonIter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
|
@ -73,6 +63,16 @@ func Benchmark_Decode_SmallStruct_Unmarshal_GoJayUnsafe(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_SmallStruct_Unmarshal_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := SmallPayload{}
|
||||
if err := segmentiojson.Unmarshal(SmallFixture, &result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_SmallStruct_Unmarshal_GoJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
@ -105,18 +105,6 @@ func Benchmark_Decode_SmallStruct_Stream_EncodingJson(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_SmallStruct_Stream_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(SmallFixture)
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := SmallPayload{}
|
||||
reader.Reset(SmallFixture)
|
||||
if err := segmentiojson.NewDecoder(reader).Decode(&result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_SmallStruct_Stream_JsonIter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(SmallFixture)
|
||||
|
@ -141,6 +129,18 @@ func Benchmark_Decode_SmallStruct_Stream_GoJay(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_SmallStruct_Stream_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(SmallFixture)
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := SmallPayload{}
|
||||
reader.Reset(SmallFixture)
|
||||
if err := segmentiojson.NewDecoder(reader).Decode(&result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_SmallStruct_Stream_GoJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(SmallFixture)
|
||||
|
@ -174,16 +174,6 @@ func Benchmark_Decode_MediumStruct_Unmarshal_FastJson(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_MediumStruct_Unmarshal_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := MediumPayload{}
|
||||
if err := segmentiojson.Unmarshal(MediumFixture, &result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_MediumStruct_Unmarshal_JsonIter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
|
@ -214,6 +204,16 @@ func Benchmark_Decode_MediumStruct_Unmarshal_GoJayUnsafe(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_MediumStruct_Unmarshal_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := MediumPayload{}
|
||||
if err := segmentiojson.Unmarshal(MediumFixture, &result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_MediumStruct_Unmarshal_GoJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
@ -246,18 +246,6 @@ func Benchmark_Decode_MediumStruct_Stream_EncodingJson(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_MediumStruct_Stream_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(MediumFixture)
|
||||
for n := 0; n < b.N; n++ {
|
||||
reader.Reset(MediumFixture)
|
||||
result := MediumPayload{}
|
||||
if err := segmentiojson.NewDecoder(reader).Decode(&result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_MediumStruct_Stream_JsonIter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(MediumFixture)
|
||||
|
@ -282,6 +270,18 @@ func Benchmark_Decode_MediumStruct_Stream_GoJay(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_MediumStruct_Stream_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(MediumFixture)
|
||||
for n := 0; n < b.N; n++ {
|
||||
reader.Reset(MediumFixture)
|
||||
result := MediumPayload{}
|
||||
if err := segmentiojson.NewDecoder(reader).Decode(&result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_MediumStruct_Stream_GoJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(MediumFixture)
|
||||
|
@ -315,16 +315,6 @@ func Benchmark_Decode_LargeStruct_Unmarshal_FastJson(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_LargeStruct_Unmarshal_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := LargePayload{}
|
||||
if err := segmentiojson.Unmarshal(LargeFixture, &result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_LargeStruct_Unmarshal_JsonIter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
|
@ -355,6 +345,16 @@ func Benchmark_Decode_LargeStruct_Unmarshal_GoJayUnsafe(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_LargeStruct_Unmarshal_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := LargePayload{}
|
||||
if err := segmentiojson.Unmarshal(LargeFixture, &result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_LargeStruct_Unmarshal_GoJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
@ -415,18 +415,6 @@ func Benchmark_Decode_LargeStruct_Stream_EncodingJson(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_LargeStruct_Stream_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(LargeFixture)
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := LargePayload{}
|
||||
reader.Reset(LargeFixture)
|
||||
if err := segmentiojson.NewDecoder(reader).Decode(&result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_LargeStruct_Stream_JsonIter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(LargeFixture)
|
||||
|
@ -451,6 +439,18 @@ func Benchmark_Decode_LargeStruct_Stream_GoJay(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_LargeStruct_Stream_SegmentioJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(LargeFixture)
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := LargePayload{}
|
||||
reader.Reset(LargeFixture)
|
||||
if err := segmentiojson.NewDecoder(reader).Decode(&result); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Decode_LargeStruct_Stream_GoJson(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
reader := bytes.NewReader(LargeFixture)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,10 @@ go 1.12
|
|||
require (
|
||||
github.com/francoispqt/gojay v1.2.13
|
||||
github.com/goccy/go-json v0.0.0-00010101000000-000000000000
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/json-iterator/go v1.1.9
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe
|
||||
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7
|
||||
github.com/segmentio/encoding v0.2.4
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/valyala/fastjson v1.6.3
|
||||
github.com/wI2L/jettison v0.7.1
|
||||
)
|
||||
|
|
|
@ -47,9 +47,8 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
|
|||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
|
@ -114,10 +113,10 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh
|
|||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
|
||||
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
|
@ -187,9 +186,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
74
decode.go
74
decode.go
|
@ -1,7 +1,6 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
@ -40,7 +39,7 @@ func unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
|
|||
}
|
||||
ctx := decoder.TakeRuntimeContext()
|
||||
ctx.Buf = src
|
||||
ctx.Option.Flags = 0
|
||||
ctx.Option.Flag = 0
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(ctx.Option)
|
||||
}
|
||||
|
@ -53,67 +52,6 @@ func unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
|
|||
return validateEndBuf(src, cursor)
|
||||
}
|
||||
|
||||
func unmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
|
||||
src := make([]byte, len(data)+1) // append nul byte to the end
|
||||
copy(src, data)
|
||||
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
|
||||
if err := validateType(header.typ, uintptr(header.ptr)); err != nil {
|
||||
return err
|
||||
}
|
||||
dec, err := decoder.CompileToGetDecoder(header.typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rctx := decoder.TakeRuntimeContext()
|
||||
rctx.Buf = src
|
||||
rctx.Option.Flags = 0
|
||||
rctx.Option.Flags |= decoder.ContextOption
|
||||
rctx.Option.Context = ctx
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(rctx.Option)
|
||||
}
|
||||
cursor, err := dec.Decode(rctx, 0, 0, header.ptr)
|
||||
if err != nil {
|
||||
decoder.ReleaseRuntimeContext(rctx)
|
||||
return err
|
||||
}
|
||||
decoder.ReleaseRuntimeContext(rctx)
|
||||
return validateEndBuf(src, cursor)
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -130,7 +68,7 @@ func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc)
|
|||
|
||||
ctx := decoder.TakeRuntimeContext()
|
||||
ctx.Buf = src
|
||||
ctx.Option.Flags = 0
|
||||
ctx.Option.Flag = 0
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(ctx.Option)
|
||||
}
|
||||
|
@ -199,14 +137,6 @@ func (d *Decoder) Decode(v interface{}) error {
|
|||
return d.DecodeWithOption(v)
|
||||
}
|
||||
|
||||
// DecodeContext reads the next JSON-encoded value from its
|
||||
// input and stores it in the value pointed to by v with context.Context.
|
||||
func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error {
|
||||
d.s.Option.Flags |= decoder.ContextOption
|
||||
d.s.Option.Context = ctx
|
||||
return d.DecodeWithOption(v)
|
||||
}
|
||||
|
||||
func (d *Decoder) DecodeWithOption(v interface{}, optFuncs ...DecodeOptionFunc) error {
|
||||
header := (*emptyInterface)(unsafe.Pointer(&v))
|
||||
typ := header.typ
|
||||
|
|
409
decode_test.go
409
decode_test.go
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding"
|
||||
stdjson "encoding/json"
|
||||
"errors"
|
||||
|
@ -152,7 +151,6 @@ func Test_Decoder(t *testing.T) {
|
|||
B string `json:"str"`
|
||||
C bool
|
||||
D *T
|
||||
E func()
|
||||
}
|
||||
content := []byte(`
|
||||
{
|
||||
|
@ -163,8 +161,7 @@ func Test_Decoder(t *testing.T) {
|
|||
"aa": 2,
|
||||
"bb": "world",
|
||||
"cc": true
|
||||
},
|
||||
"e" : null
|
||||
}
|
||||
}`)
|
||||
assertErr(t, json.Unmarshal(content, &v))
|
||||
assertEq(t, "struct.A", 123, v.A)
|
||||
|
@ -173,7 +170,6 @@ func Test_Decoder(t *testing.T) {
|
|||
assertEq(t, "struct.D.AA", 2, v.D.AA)
|
||||
assertEq(t, "struct.D.BB", "world", v.D.BB)
|
||||
assertEq(t, "struct.D.CC", true, v.D.CC)
|
||||
assertEq(t, "struct.E", true, v.E == nil)
|
||||
t.Run("struct.field null", func(t *testing.T) {
|
||||
var v struct {
|
||||
A string
|
||||
|
@ -182,9 +178,8 @@ func Test_Decoder(t *testing.T) {
|
|||
D map[string]interface{}
|
||||
E [2]string
|
||||
F interface{}
|
||||
G func()
|
||||
}
|
||||
assertErr(t, json.Unmarshal([]byte(`{"a":null,"b":null,"c":null,"d":null,"e":null,"f":null,"g":null}`), &v))
|
||||
assertErr(t, json.Unmarshal([]byte(`{"a":null,"b":null,"c":null,"d":null,"e":null,"f":null}`), &v))
|
||||
assertEq(t, "string", v.A, "")
|
||||
assertNeq(t, "[]string", v.B, nil)
|
||||
assertEq(t, "[]string", len(v.B), 0)
|
||||
|
@ -195,7 +190,6 @@ func Test_Decoder(t *testing.T) {
|
|||
assertNeq(t, "array", v.E, nil)
|
||||
assertEq(t, "array", len(v.E), 2)
|
||||
assertEq(t, "interface{}", v.F, nil)
|
||||
assertEq(t, "nilfunc", true, v.G == nil)
|
||||
})
|
||||
})
|
||||
t.Run("interface", func(t *testing.T) {
|
||||
|
@ -244,11 +238,6 @@ func Test_Decoder(t *testing.T) {
|
|||
assertEq(t, "interface", nil, v)
|
||||
})
|
||||
})
|
||||
t.Run("func", func(t *testing.T) {
|
||||
var v func()
|
||||
assertErr(t, json.Unmarshal([]byte(`null`), &v))
|
||||
assertEq(t, "nilfunc", true, v == nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue98(t *testing.T) {
|
||||
|
@ -1270,7 +1259,7 @@ var unmarshalTests = []unmarshalTest{
|
|||
in: `invalid`, // 143
|
||||
ptr: new(json.Number),
|
||||
err: json.NewSyntaxError(
|
||||
`invalid character 'i' looking for beginning of value`,
|
||||
`json: json.Number unexpected end of JSON input`,
|
||||
1,
|
||||
),
|
||||
},
|
||||
|
@ -1317,12 +1306,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 {
|
||||
|
@ -2004,8 +1987,8 @@ type wrongStringTest struct {
|
|||
}
|
||||
|
||||
var wrongStringTests = []wrongStringTest{
|
||||
{`{"result":"x"}`, `invalid character 'x' looking for beginning of value`},
|
||||
{`{"result":"foo"}`, `invalid character 'f' looking for beginning of value`},
|
||||
{`{"result":"x"}`, `not at beginning of value`},
|
||||
{`{"result":"foo"}`, `not at beginning of value`},
|
||||
{`{"result":"123"}`, `json: cannot unmarshal number into Go struct field WrongString.Message of type string`},
|
||||
{`{"result":123}`, `json: cannot unmarshal number into Go struct field WrongString.Message of type string`},
|
||||
{`{"result":"\""}`, `json: string unexpected end of JSON input`},
|
||||
|
@ -2697,10 +2680,10 @@ func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) {
|
|||
err error
|
||||
}{{
|
||||
in: `1 false null :`,
|
||||
err: json.NewSyntaxError("invalid character '\x00' looking for beginning of value", 14),
|
||||
err: json.NewSyntaxError("not at beginning of value", 14),
|
||||
}, {
|
||||
in: `1 [] [,]`,
|
||||
err: json.NewSyntaxError("invalid character ',' looking for beginning of value", 6),
|
||||
err: json.NewSyntaxError("not at beginning of value", 6),
|
||||
}, {
|
||||
in: `1 [] [true:]`,
|
||||
err: json.NewSyntaxError("json: slice unexpected end of JSON input", 10),
|
||||
|
@ -3637,381 +3620,3 @@ func TestDecodeEscapedCharField(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
type unmarshalContextKey struct{}
|
||||
|
||||
type unmarshalContextStructType struct {
|
||||
v int
|
||||
}
|
||||
|
||||
func (t *unmarshalContextStructType) UnmarshalJSON(ctx context.Context, b []byte) error {
|
||||
v := ctx.Value(unmarshalContextKey{})
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("failed to propagate parent context.Context")
|
||||
}
|
||||
if s != "hello" {
|
||||
return fmt.Errorf("failed to propagate parent context.Context")
|
||||
}
|
||||
t.v = 100
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestDecodeContextOption(t *testing.T) {
|
||||
src := []byte("10")
|
||||
buf := bytes.NewBuffer(src)
|
||||
|
||||
t.Run("UnmarshalContext", func(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), unmarshalContextKey{}, "hello")
|
||||
var v unmarshalContextStructType
|
||||
if err := json.UnmarshalContext(ctx, src, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.v != 100 {
|
||||
t.Fatal("failed to decode with context")
|
||||
}
|
||||
})
|
||||
t.Run("DecodeContext", func(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), unmarshalContextKey{}, "hello")
|
||||
var v unmarshalContextStructType
|
||||
if err := json.NewDecoder(buf).DecodeContext(ctx, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v.v != 100 {
|
||||
t.Fatal("failed to decode with context")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue251(t *testing.T) {
|
||||
array := [3]int{1, 2, 3}
|
||||
err := stdjson.Unmarshal([]byte("[ ]"), &array)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(array)
|
||||
|
||||
array = [3]int{1, 2, 3}
|
||||
err = json.Unmarshal([]byte("[ ]"), &array)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(array)
|
||||
|
||||
array = [3]int{1, 2, 3}
|
||||
err = json.NewDecoder(strings.NewReader(`[ ]`)).Decode(&array)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(array)
|
||||
}
|
||||
|
||||
func TestDecodeBinaryTypeWithEscapedChar(t *testing.T) {
|
||||
type T struct {
|
||||
Msg []byte `json:"msg"`
|
||||
}
|
||||
content := []byte(`{"msg":"aGVsbG8K\n"}`)
|
||||
t.Run("unmarshal", func(t *testing.T) {
|
||||
var expected T
|
||||
if err := stdjson.Unmarshal(content, &expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var got T
|
||||
if err := json.Unmarshal(content, &got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected.Msg, got.Msg) {
|
||||
t.Fatalf("failed to decode binary type with escaped char. expected %q but got %q", expected.Msg, got.Msg)
|
||||
}
|
||||
})
|
||||
t.Run("stream", func(t *testing.T) {
|
||||
var expected T
|
||||
if err := stdjson.NewDecoder(bytes.NewBuffer(content)).Decode(&expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var got T
|
||||
if err := json.NewDecoder(bytes.NewBuffer(content)).Decode(&got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected.Msg, got.Msg) {
|
||||
t.Fatalf("failed to decode binary type with escaped char. expected %q but got %q", expected.Msg, got.Msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue282(t *testing.T) {
|
||||
var J = []byte(`{
|
||||
"a": {},
|
||||
"b": {},
|
||||
"c": {},
|
||||
"d": {},
|
||||
"e": {},
|
||||
"f": {},
|
||||
"g": {},
|
||||
"h": {
|
||||
"m": "1"
|
||||
},
|
||||
"i": {}
|
||||
}`)
|
||||
|
||||
type T4 struct {
|
||||
F0 string
|
||||
F1 string
|
||||
F2 string
|
||||
F3 string
|
||||
F4 string
|
||||
F5 string
|
||||
F6 int
|
||||
}
|
||||
type T3 struct {
|
||||
F0 string
|
||||
F1 T4
|
||||
}
|
||||
type T2 struct {
|
||||
F0 string `json:"m"`
|
||||
F1 T3
|
||||
}
|
||||
type T0 map[string]T2
|
||||
|
||||
// T2 size is 136 bytes. This is indirect type.
|
||||
var v T0
|
||||
if err := json.Unmarshal(J, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if v["h"].F0 != "1" {
|
||||
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.16
|
||||
volumes:
|
||||
- '.:/go/src/go-json'
|
||||
deploy:
|
||||
|
|
58
encode.go
58
encode.go
|
@ -1,9 +1,7 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
|
@ -37,7 +35,6 @@ func (e *Encoder) Encode(v interface{}) error {
|
|||
// EncodeWithOption call Encode with EncodeOption.
|
||||
func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc) error {
|
||||
ctx := encoder.TakeRuntimeContext()
|
||||
ctx.Option.Flag = 0
|
||||
|
||||
err := e.encodeWithOption(ctx, v, optFuncs...)
|
||||
|
||||
|
@ -45,25 +42,11 @@ func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc)
|
|||
return err
|
||||
}
|
||||
|
||||
// EncodeContext call Encode with context.Context and EncodeOption.
|
||||
func (e *Encoder) EncodeContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) error {
|
||||
rctx := encoder.TakeRuntimeContext()
|
||||
rctx.Option.Flag = 0
|
||||
rctx.Option.Flag |= encoder.ContextOption
|
||||
rctx.Option.Context = ctx
|
||||
|
||||
err := e.encodeWithOption(rctx, v, optFuncs...)
|
||||
|
||||
encoder.ReleaseRuntimeContext(rctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Encoder) encodeWithOption(ctx *encoder.RuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
|
||||
ctx.Option.Flag = 0
|
||||
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)
|
||||
}
|
||||
|
@ -111,38 +94,11 @@ func (e *Encoder) SetIndent(prefix, indent string) {
|
|||
e.enabledIndent = true
|
||||
}
|
||||
|
||||
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.Context = ctx
|
||||
for _, optFunc := range optFuncs {
|
||||
optFunc(rctx.Option)
|
||||
}
|
||||
|
||||
buf, err := encode(rctx, v)
|
||||
if err != nil {
|
||||
encoder.ReleaseRuntimeContext(rctx)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// this line exists to escape call of `runtime.makeslicecopy` .
|
||||
// if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`,
|
||||
// dst buffer size and src buffer size are differrent.
|
||||
// in this case, compiler uses `runtime.makeslicecopy`, but it is slow.
|
||||
buf = buf[:len(buf)-1]
|
||||
copied := make([]byte, len(buf))
|
||||
copy(copied, buf)
|
||||
|
||||
encoder.ReleaseRuntimeContext(rctx)
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
func marshal(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
|
||||
ctx := encoder.TakeRuntimeContext()
|
||||
|
||||
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 +125,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 +149,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 +179,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 +207,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 +234,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
|
||||
}
|
||||
|
|
735
encode_test.go
735
encode_test.go
|
@ -2,19 +2,15 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding"
|
||||
stdjson "encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -194,59 +190,6 @@ func Test_Marshal(t *testing.T) {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("embedded with tag", func(t *testing.T) {
|
||||
type T struct {
|
||||
A string `json:"a"`
|
||||
}
|
||||
type U struct {
|
||||
*T `json:"t"`
|
||||
B string `json:"b"`
|
||||
}
|
||||
type T2 struct {
|
||||
A string `json:"a,omitempty"`
|
||||
}
|
||||
type U2 struct {
|
||||
*T2 `json:"t,omitempty"`
|
||||
B string `json:"b,omitempty"`
|
||||
}
|
||||
t.Run("exists field", func(t *testing.T) {
|
||||
bytes, err := json.Marshal(&U{
|
||||
T: &T{
|
||||
A: "aaa",
|
||||
},
|
||||
B: "bbb",
|
||||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "embedded", `{"t":{"a":"aaa"},"b":"bbb"}`, string(bytes))
|
||||
t.Run("omitempty", func(t *testing.T) {
|
||||
bytes, err := json.Marshal(&U2{
|
||||
T2: &T2{
|
||||
A: "aaa",
|
||||
},
|
||||
B: "bbb",
|
||||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "embedded", `{"t":{"a":"aaa"},"b":"bbb"}`, string(bytes))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("none field", func(t *testing.T) {
|
||||
bytes, err := json.Marshal(&U{
|
||||
B: "bbb",
|
||||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "embedded", `{"t":null,"b":"bbb"}`, string(bytes))
|
||||
t.Run("omitempty", func(t *testing.T) {
|
||||
bytes, err := json.Marshal(&U2{
|
||||
B: "bbb",
|
||||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "embedded", `{"b":"bbb"}`, string(bytes))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("omitempty", func(t *testing.T) {
|
||||
type T struct {
|
||||
A int `json:",omitempty"`
|
||||
|
@ -480,8 +423,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 +496,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 +956,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 +969,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
|
||||
|
@ -1993,639 +1918,3 @@ func TestEncodeMapKeyTypeInterface(t *testing.T) {
|
|||
t.Fatal("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
type marshalContextKey struct{}
|
||||
|
||||
type marshalContextStructType struct{}
|
||||
|
||||
func (t *marshalContextStructType) MarshalJSON(ctx context.Context) ([]byte, error) {
|
||||
v := ctx.Value(marshalContextKey{})
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to propagate parent context.Context")
|
||||
}
|
||||
if s != "hello" {
|
||||
return nil, fmt.Errorf("failed to propagate parent context.Context")
|
||||
}
|
||||
return []byte(`"success"`), nil
|
||||
}
|
||||
|
||||
func TestEncodeContextOption(t *testing.T) {
|
||||
t.Run("MarshalContext", func(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), marshalContextKey{}, "hello")
|
||||
b, err := json.MarshalContext(ctx, &marshalContextStructType{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(b) != `"success"` {
|
||||
t.Fatal("failed to encode with MarshalerContext")
|
||||
}
|
||||
})
|
||||
t.Run("EncodeContext", func(t *testing.T) {
|
||||
ctx := context.WithValue(context.Background(), marshalContextKey{}, "hello")
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
if err := json.NewEncoder(buf).EncodeContext(ctx, &marshalContextStructType{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if buf.String() != "\"success\"\n" {
|
||||
t.Fatal("failed to encode with EncodeContext")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestInterfaceWithPointer(t *testing.T) {
|
||||
var (
|
||||
ivalue int = 10
|
||||
uvalue uint = 20
|
||||
svalue string = "value"
|
||||
bvalue bool = true
|
||||
fvalue float32 = 3.14
|
||||
nvalue json.Number = "1.23"
|
||||
structv = struct{ A int }{A: 10}
|
||||
slice = []int{1, 2, 3, 4}
|
||||
array = [4]int{1, 2, 3, 4}
|
||||
mapvalue = map[string]int{"a": 1}
|
||||
)
|
||||
data := map[string]interface{}{
|
||||
"ivalue": ivalue,
|
||||
"uvalue": uvalue,
|
||||
"svalue": svalue,
|
||||
"bvalue": bvalue,
|
||||
"fvalue": fvalue,
|
||||
"nvalue": nvalue,
|
||||
"struct": structv,
|
||||
"slice": slice,
|
||||
"array": array,
|
||||
"map": mapvalue,
|
||||
"pivalue": &ivalue,
|
||||
"puvalue": &uvalue,
|
||||
"psvalue": &svalue,
|
||||
"pbvalue": &bvalue,
|
||||
"pfvalue": &fvalue,
|
||||
"pnvalue": &nvalue,
|
||||
"pstruct": &structv,
|
||||
"pslice": &slice,
|
||||
"parray": &array,
|
||||
"pmap": &mapvalue,
|
||||
}
|
||||
expected, err := stdjson.Marshal(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertEq(t, "interface{}", string(expected), string(actual))
|
||||
}
|
||||
|
||||
func TestIssue263(t *testing.T) {
|
||||
type Foo struct {
|
||||
A []string `json:"a"`
|
||||
B int `json:"b"`
|
||||
}
|
||||
|
||||
type MyStruct struct {
|
||||
Foo *Foo `json:"foo,omitempty"`
|
||||
}
|
||||
|
||||
s := MyStruct{
|
||||
Foo: &Foo{
|
||||
A: []string{"ls -lah"},
|
||||
B: 0,
|
||||
},
|
||||
}
|
||||
expected, err := stdjson.Marshal(s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, actual) {
|
||||
t.Fatalf("expected:[%s] but got:[%s]", string(expected), string(actual))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmbeddedNotFirstField(t *testing.T) {
|
||||
type Embedded struct {
|
||||
Has bool `json:"has"`
|
||||
}
|
||||
type T struct {
|
||||
X int `json:"is"`
|
||||
Embedded `json:"child"`
|
||||
}
|
||||
p := T{X: 10, Embedded: Embedded{Has: true}}
|
||||
expected, err := stdjson.Marshal(&p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := json.Marshal(&p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, got) {
|
||||
t.Fatalf("failed to encode embedded structure. expected = %q but got %q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
type implementedMethodIface interface {
|
||||
M()
|
||||
}
|
||||
|
||||
type implementedIfaceType struct {
|
||||
A int
|
||||
B string
|
||||
}
|
||||
|
||||
func (implementedIfaceType) M() {}
|
||||
|
||||
func TestImplementedMethodInterfaceType(t *testing.T) {
|
||||
data := []implementedIfaceType{implementedIfaceType{}}
|
||||
expected, err := stdjson.Marshal(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, got) {
|
||||
t.Fatalf("failed to encode implemented method interface type. expected:[%q] but got:[%q]", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyStructInterface(t *testing.T) {
|
||||
expected, err := stdjson.Marshal([]interface{}{struct{}{}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := json.Marshal([]interface{}{struct{}{}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, got) {
|
||||
t.Fatalf("failed to encode empty struct interface. expected:[%q] but got:[%q]", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue290(t *testing.T) {
|
||||
type Issue290 interface {
|
||||
A()
|
||||
}
|
||||
var a struct {
|
||||
A Issue290
|
||||
}
|
||||
expected, err := stdjson.Marshal(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, got) {
|
||||
t.Fatalf("failed to encode non empty interface. expected = %q but got %q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue299(t *testing.T) {
|
||||
t.Run("conflict second field", func(t *testing.T) {
|
||||
type Embedded struct {
|
||||
ID string `json:"id"`
|
||||
Name map[string]string `json:"name"`
|
||||
}
|
||||
type Container struct {
|
||||
Embedded
|
||||
Name string `json:"name"`
|
||||
}
|
||||
c := &Container{
|
||||
Embedded: Embedded{
|
||||
ID: "1",
|
||||
Name: map[string]string{"en": "Hello", "es": "Hola"},
|
||||
},
|
||||
Name: "Hi",
|
||||
}
|
||||
expected, _ := stdjson.Marshal(c)
|
||||
got, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, got) {
|
||||
t.Fatalf("expected %q but got %q", expected, got)
|
||||
}
|
||||
})
|
||||
t.Run("conflict map field", func(t *testing.T) {
|
||||
type Embedded struct {
|
||||
Name map[string]string `json:"name"`
|
||||
}
|
||||
type Container struct {
|
||||
Embedded
|
||||
Name string `json:"name"`
|
||||
}
|
||||
c := &Container{
|
||||
Embedded: Embedded{
|
||||
Name: map[string]string{"en": "Hello", "es": "Hola"},
|
||||
},
|
||||
Name: "Hi",
|
||||
}
|
||||
expected, _ := stdjson.Marshal(c)
|
||||
got, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, got) {
|
||||
t.Fatalf("expected %q but got %q", expected, got)
|
||||
}
|
||||
})
|
||||
t.Run("conflict slice field", func(t *testing.T) {
|
||||
type Embedded struct {
|
||||
Name []string `json:"name"`
|
||||
}
|
||||
type Container struct {
|
||||
Embedded
|
||||
Name string `json:"name"`
|
||||
}
|
||||
c := &Container{
|
||||
Embedded: Embedded{
|
||||
Name: []string{"Hello"},
|
||||
},
|
||||
Name: "Hi",
|
||||
}
|
||||
expected, _ := stdjson.Marshal(c)
|
||||
got, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, got) {
|
||||
t.Fatalf("expected %q but got %q", expected, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecursivePtrHead(t *testing.T) {
|
||||
type User struct {
|
||||
Account *string `json:"account"`
|
||||
Password *string `json:"password"`
|
||||
Nickname *string `json:"nickname"`
|
||||
Address *string `json:"address,omitempty"`
|
||||
Friends []*User `json:"friends,omitempty"`
|
||||
}
|
||||
user1Account, user1Password, user1Nickname := "abcdef", "123456", "user1"
|
||||
user1 := &User{
|
||||
Account: &user1Account,
|
||||
Password: &user1Password,
|
||||
Nickname: &user1Nickname,
|
||||
Address: nil,
|
||||
}
|
||||
user2Account, user2Password, user2Nickname := "ghijkl", "123456", "user2"
|
||||
user2 := &User{
|
||||
Account: &user2Account,
|
||||
Password: &user2Password,
|
||||
Nickname: &user2Nickname,
|
||||
Address: nil,
|
||||
}
|
||||
user1.Friends = []*User{user2}
|
||||
expected, err := stdjson.Marshal(user1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := json.Marshal(user1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(expected, got) {
|
||||
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
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
module benchgraph
|
||||
|
||||
go 1.15
|
||||
|
||||
replace github.com/goccy/go-json => ../../../
|
||||
|
||||
require (
|
||||
github.com/goccy/go-json v0.0.0-00010101000000-000000000000
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
||||
golang.org/x/tools v0.1.1
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
google.golang.org/api v0.47.0
|
||||
)
|
|
@ -0,0 +1,480 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.47.0 h1:sQLWZQvP6jPGIP4JGPkJu4zHswrv81iobiyszr3b/0I=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384 h1:z+j74wi4yV+P7EtK9gPLGukOk7mFOy9wMQaC0wNb7eY=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.1 h1:ARnQJNWxGyYJpdf/JXscNlQr/uv607ZPU9Z7ogHi+iI=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
@ -0,0 +1,640 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/tools/benchmark/parse"
|
||||
"golang.org/x/xerrors"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/sheets/v4"
|
||||
"google.golang.org/api/slides/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
benchgraphToken = "benchgraph-token.json"
|
||||
)
|
||||
|
||||
type BenchmarkCodec string
|
||||
|
||||
const (
|
||||
UnknownCodec BenchmarkCodec = "Unknown"
|
||||
Encoder BenchmarkCodec = "Encode"
|
||||
Decoder BenchmarkCodec = "Decode"
|
||||
)
|
||||
|
||||
func (c BenchmarkCodec) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
type BenchmarkKind string
|
||||
|
||||
const (
|
||||
UnknownKind BenchmarkKind = "Unknown"
|
||||
SmallStruct BenchmarkKind = "SmallStruct"
|
||||
SmallStructCached BenchmarkKind = "SmallStructCached"
|
||||
MediumStruct BenchmarkKind = "MediumStruct"
|
||||
MediumStructCached BenchmarkKind = "MediumStructCached"
|
||||
LargeStruct BenchmarkKind = "LargeStruct"
|
||||
LargeStructCached BenchmarkKind = "LargeStructCached"
|
||||
)
|
||||
|
||||
func (k BenchmarkKind) String() string {
|
||||
return string(k)
|
||||
}
|
||||
|
||||
type BenchmarkTarget string
|
||||
|
||||
const (
|
||||
UnknownTarget BenchmarkTarget = "Unknown"
|
||||
EncodingJson BenchmarkTarget = "EncodingJson"
|
||||
GoJson BenchmarkTarget = "GoJson"
|
||||
GoJsonNoEscape BenchmarkTarget = "GoJsonNoEscape"
|
||||
GoJsonColored BenchmarkTarget = "GoJsonColored"
|
||||
FFJson BenchmarkTarget = "FFJson"
|
||||
JsonIter BenchmarkTarget = "JsonIter"
|
||||
EasyJson BenchmarkTarget = "EasyJson"
|
||||
Jettison BenchmarkTarget = "Jettison"
|
||||
GoJay BenchmarkTarget = "GoJay"
|
||||
SegmentioJson BenchmarkTarget = "SegmentioJson"
|
||||
)
|
||||
|
||||
func (t BenchmarkTarget) String() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
func (t BenchmarkTarget) DisplayName() string {
|
||||
switch t {
|
||||
case EncodingJson:
|
||||
return "encoding/json"
|
||||
case GoJson:
|
||||
return "goccy/go-json"
|
||||
case GoJsonNoEscape:
|
||||
return "goccy/go-json (noescape)"
|
||||
case GoJsonColored:
|
||||
return "goccy/go-json (colored)"
|
||||
case FFJson:
|
||||
return "pquerna/ffjson"
|
||||
case JsonIter:
|
||||
return "json-iterator/go"
|
||||
case EasyJson:
|
||||
return "mailru/easyjson"
|
||||
case Jettison:
|
||||
return "wl2L/jettison"
|
||||
case GoJay:
|
||||
return "francoispqt/gojay"
|
||||
case SegmentioJson:
|
||||
return "segmentio/encoding/json"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
var credFile string
|
||||
|
||||
func init() {
|
||||
// How to create credentials.json: https://developers.google.com/workspace/guides/create-credentials#desktop
|
||||
flag.StringVar(&credFile, "cred", "credentials.json", "specify to path to credential file to use google spreadsheets and slides APIs")
|
||||
}
|
||||
|
||||
func stringptr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func floatptr(v float64) *float64 {
|
||||
return &v
|
||||
}
|
||||
|
||||
func createClient(ctx context.Context) (*http.Client, error) {
|
||||
b, err := ioutil.ReadFile(credFile)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to read credential file: %w", err)
|
||||
}
|
||||
|
||||
// If modifying these scopes, delete your previously saved token.json.
|
||||
config, err := google.ConfigFromJSON(b, sheets.SpreadsheetsScope, slides.PresentationsScope)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to create config from scope: %w", err)
|
||||
}
|
||||
|
||||
// The file token.json stores the user's access and refresh tokens, and is
|
||||
// created automatically when the authorization flow completes for the first time.
|
||||
tok, err := tokenFromFile()
|
||||
if err != nil {
|
||||
tok = getTokenFromWeb(ctx, config)
|
||||
if err := saveToken(tok); err != nil {
|
||||
return nil, xerrors.Errorf("failed to save token: %w", err)
|
||||
}
|
||||
}
|
||||
return config.Client(ctx, tok), nil
|
||||
}
|
||||
|
||||
// Request a token from the web, then returns the retrieved token.
|
||||
func getTokenFromWeb(ctx context.Context, config *oauth2.Config) *oauth2.Token {
|
||||
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
|
||||
fmt.Printf("Go to the following link in your browser then type the "+
|
||||
"authorization code: \n%v\n", authURL)
|
||||
|
||||
var authCode string
|
||||
if _, err := fmt.Scan(&authCode); err != nil {
|
||||
log.Fatalf("Unable to read authorization code: %v", err)
|
||||
}
|
||||
|
||||
tok, err := config.Exchange(ctx, authCode)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to retrieve token from web: %v", err)
|
||||
}
|
||||
return tok
|
||||
}
|
||||
|
||||
func tokenFromFile() (*oauth2.Token, error) {
|
||||
f, err := os.Open(benchgraphToken)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to open %s: %w", benchgraphToken, err)
|
||||
}
|
||||
defer f.Close()
|
||||
tok := &oauth2.Token{}
|
||||
if err := json.NewDecoder(f).Decode(tok); err != nil {
|
||||
return nil, xerrors.Errorf("failed to decode token: %w", err)
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
func saveToken(token *oauth2.Token) error {
|
||||
log.Printf("Saving credential file to: %s\n", benchgraphToken)
|
||||
f, err := os.OpenFile(benchgraphToken, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create file for saving token: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
if err := json.NewEncoder(f).Encode(token); err != nil {
|
||||
return xerrors.Errorf("failed to write token: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createSpreadsheet(svc *sheets.Service, title string, headers []string, targets []string, data map[string][]float64) (*sheets.Spreadsheet, error) {
|
||||
rows := []*sheets.RowData{}
|
||||
headerRow := &sheets.RowData{Values: []*sheets.CellData{{UserEnteredValue: &sheets.ExtendedValue{}}}}
|
||||
for _, header := range headers {
|
||||
headerRow.Values = append(headerRow.Values, &sheets.CellData{
|
||||
UserEnteredValue: &sheets.ExtendedValue{
|
||||
StringValue: stringptr(header),
|
||||
},
|
||||
})
|
||||
}
|
||||
rows = append(rows, headerRow)
|
||||
for _, target := range targets {
|
||||
target := target
|
||||
targetRow := &sheets.RowData{
|
||||
Values: []*sheets.CellData{
|
||||
{
|
||||
UserEnteredValue: &sheets.ExtendedValue{
|
||||
StringValue: stringptr(target),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, v := range data[target] {
|
||||
v := v
|
||||
targetRow.Values = append(targetRow.Values, &sheets.CellData{
|
||||
UserEnteredValue: &sheets.ExtendedValue{
|
||||
NumberValue: floatptr(v),
|
||||
},
|
||||
})
|
||||
}
|
||||
rows = append(rows, targetRow)
|
||||
}
|
||||
sheetsData := []*sheets.Sheet{
|
||||
{
|
||||
Properties: &sheets.SheetProperties{
|
||||
Title: "benchmark",
|
||||
SheetType: "GRID",
|
||||
},
|
||||
Data: []*sheets.GridData{{RowData: rows}},
|
||||
},
|
||||
}
|
||||
spreadSheet, err := svc.Spreadsheets.Create(&sheets.Spreadsheet{
|
||||
Properties: &sheets.SpreadsheetProperties{Title: title},
|
||||
Sheets: sheetsData,
|
||||
}).Do()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to create spreadsheet: %w", err)
|
||||
}
|
||||
spreadSheet.Sheets[0].Data = sheetsData[0].Data
|
||||
return spreadSheet, nil
|
||||
}
|
||||
|
||||
func generateChart(svc *sheets.Service, spreadSheet *sheets.Spreadsheet, title string) (*sheets.EmbeddedChart, error) {
|
||||
sheet := spreadSheet.Sheets[0]
|
||||
rows := sheet.Data[0].RowData
|
||||
rowSize := int64(len(rows))
|
||||
colSize := int64(len(rows[0].Values))
|
||||
series := []*sheets.BasicChartSeries{}
|
||||
for i := int64(1); i < colSize; i++ {
|
||||
series = append(series, &sheets.BasicChartSeries{
|
||||
Series: &sheets.ChartData{
|
||||
SourceRange: &sheets.ChartSourceRange{
|
||||
Sources: []*sheets.GridRange{
|
||||
{
|
||||
SheetId: sheet.Properties.SheetId,
|
||||
StartColumnIndex: i,
|
||||
EndColumnIndex: i + 1,
|
||||
StartRowIndex: 0,
|
||||
EndRowIndex: rowSize,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
TargetAxis: "BOTTOM_AXIS",
|
||||
})
|
||||
}
|
||||
res, err := svc.Spreadsheets.BatchUpdate(spreadSheet.SpreadsheetId, &sheets.BatchUpdateSpreadsheetRequest{
|
||||
Requests: []*sheets.Request{
|
||||
{
|
||||
AddChart: &sheets.AddChartRequest{
|
||||
Chart: &sheets.EmbeddedChart{
|
||||
Position: &sheets.EmbeddedObjectPosition{
|
||||
NewSheet: true,
|
||||
},
|
||||
Spec: &sheets.ChartSpec{
|
||||
Title: title,
|
||||
BasicChart: &sheets.BasicChartSpec{
|
||||
ChartType: "BAR",
|
||||
LegendPosition: "TOP_LEGEND",
|
||||
Domains: []*sheets.BasicChartDomain{
|
||||
{
|
||||
Domain: &sheets.ChartData{
|
||||
SourceRange: &sheets.ChartSourceRange{
|
||||
Sources: []*sheets.GridRange{
|
||||
{
|
||||
SheetId: sheet.Properties.SheetId,
|
||||
StartColumnIndex: 0,
|
||||
EndColumnIndex: 1,
|
||||
StartRowIndex: 0,
|
||||
EndRowIndex: rowSize,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Series: series,
|
||||
HeaderCount: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).Do()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to generate chart: %w", err)
|
||||
}
|
||||
|
||||
for _, rep := range res.Replies {
|
||||
if rep.AddChart == nil {
|
||||
continue
|
||||
}
|
||||
return rep.AddChart.Chart, nil
|
||||
}
|
||||
return nil, xerrors.Errorf("failed to find chartID")
|
||||
}
|
||||
|
||||
func createPresentationWithEmptySlide(presentationService *slides.PresentationsService) (*slides.Presentation, string, error) {
|
||||
presentation, err := presentationService.Create(&slides.Presentation{}).Do()
|
||||
if err != nil {
|
||||
return nil, "", xerrors.Errorf("failed to create presentation: %w", err)
|
||||
}
|
||||
res, err := presentationService.BatchUpdate(presentation.PresentationId, &slides.BatchUpdatePresentationRequest{
|
||||
Requests: []*slides.Request{
|
||||
{
|
||||
CreateSlide: &slides.CreateSlideRequest{
|
||||
InsertionIndex: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}).Do()
|
||||
if err != nil {
|
||||
return nil, "", xerrors.Errorf("failed to update presentation: %w", err)
|
||||
}
|
||||
for _, rep := range res.Replies {
|
||||
if rep.CreateSlide == nil {
|
||||
continue
|
||||
}
|
||||
return presentation, rep.CreateSlide.ObjectId, nil
|
||||
}
|
||||
return nil, "", xerrors.Errorf("failed to find slide objectID")
|
||||
}
|
||||
|
||||
func addChartToPresentation(presentationService *slides.PresentationsService, presentation *slides.Presentation, slideID string, spreadSheetID string, chart *sheets.EmbeddedChart) error {
|
||||
if _, err := presentationService.BatchUpdate(presentation.PresentationId, &slides.BatchUpdatePresentationRequest{
|
||||
Requests: []*slides.Request{
|
||||
{
|
||||
CreateSheetsChart: &slides.CreateSheetsChartRequest{
|
||||
LinkingMode: "LINKED",
|
||||
SpreadsheetId: spreadSheetID,
|
||||
ChartId: chart.ChartId,
|
||||
ElementProperties: &slides.PageElementProperties{
|
||||
PageObjectId: slideID,
|
||||
Size: &slides.Size{
|
||||
Width: &slides.Dimension{
|
||||
Magnitude: 1200,
|
||||
Unit: "PT",
|
||||
},
|
||||
Height: &slides.Dimension{
|
||||
Magnitude: 800,
|
||||
Unit: "PT",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).Do(); err != nil {
|
||||
return xerrors.Errorf("failed to add chart: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadChartImage(presentationService *slides.PresentationsService, presentation *slides.Presentation, path string) error {
|
||||
gotPresentation, err := presentationService.Get(presentation.PresentationId).Do()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get presentation: %w", err)
|
||||
}
|
||||
for _, slide := range gotPresentation.Slides {
|
||||
for _, pe := range slide.PageElements {
|
||||
if pe.SheetsChart == nil {
|
||||
continue
|
||||
}
|
||||
if err := downloadImage(pe.SheetsChart.ContentUrl, path); err != nil {
|
||||
return xerrors.Errorf("failed to download image: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadImage(url, path string) error {
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get content from %s: %w", url, err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
if _, err := io.Copy(file, res.Body); err != nil {
|
||||
return xerrors.Errorf("failed to copy file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type BenchmarkData struct {
|
||||
*parse.Benchmark
|
||||
Codec BenchmarkCodec
|
||||
Kind BenchmarkKind
|
||||
Target BenchmarkTarget
|
||||
}
|
||||
|
||||
func toBenchmarkCodec(name string) (BenchmarkCodec, error) {
|
||||
switch {
|
||||
case strings.Contains(name, "_Encode_"):
|
||||
return Encoder, nil
|
||||
case strings.Contains(name, "_Decoder_"):
|
||||
return Decoder, nil
|
||||
default:
|
||||
return UnknownCodec, xerrors.Errorf("cannot find codec from %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
func toBenchmarkKind(name string) (BenchmarkKind, error) {
|
||||
switch {
|
||||
case strings.Contains(name, "_SmallStruct_"):
|
||||
return SmallStruct, nil
|
||||
case strings.Contains(name, "_MediumStruct_"):
|
||||
return MediumStruct, nil
|
||||
case strings.Contains(name, "_LargeStruct_"):
|
||||
return LargeStruct, nil
|
||||
case strings.Contains(name, "_SmallStructCached_"):
|
||||
return SmallStructCached, nil
|
||||
case strings.Contains(name, "_MediumStructCached_"):
|
||||
return MediumStructCached, nil
|
||||
case strings.Contains(name, "_LargeStructCached_"):
|
||||
return LargeStructCached, nil
|
||||
default:
|
||||
return UnknownKind, xerrors.Errorf("cannot find kind from %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
func toBenchmarkTarget(name string) (BenchmarkTarget, error) {
|
||||
v := strings.ToLower(name)
|
||||
switch {
|
||||
case strings.Contains(v, strings.ToLower("EncodingJson")):
|
||||
return EncodingJson, nil
|
||||
case strings.Contains(v, strings.ToLower("GoJson")):
|
||||
switch {
|
||||
case strings.Contains(v, strings.ToLower("NoEscape")):
|
||||
return GoJsonNoEscape, nil
|
||||
case strings.Contains(v, strings.ToLower("Colored")):
|
||||
return GoJsonColored, nil
|
||||
}
|
||||
return GoJson, nil
|
||||
case strings.Contains(v, strings.ToLower("FFJson")):
|
||||
return FFJson, nil
|
||||
case strings.Contains(v, strings.ToLower("JsonIter")):
|
||||
return JsonIter, nil
|
||||
case strings.Contains(v, strings.ToLower("EasyJson")):
|
||||
return EasyJson, nil
|
||||
case strings.Contains(v, strings.ToLower("Jettison")):
|
||||
return Jettison, nil
|
||||
case strings.Contains(v, strings.ToLower("GoJay")):
|
||||
return GoJay, nil
|
||||
case strings.Contains(v, strings.ToLower("SegmentioJson")):
|
||||
return SegmentioJson, nil
|
||||
default:
|
||||
return UnknownTarget, xerrors.Errorf("cannot find target from %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
func createBenchmarkData(bench *parse.Benchmark) (*BenchmarkData, error) {
|
||||
codec, err := toBenchmarkCodec(bench.Name)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to convert benchmark codec: %w", err)
|
||||
}
|
||||
kind, err := toBenchmarkKind(bench.Name)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to convert benchmark kind: %w", err)
|
||||
}
|
||||
target, err := toBenchmarkTarget(bench.Name)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to convert benchmark target: %w", err)
|
||||
}
|
||||
return &BenchmarkData{
|
||||
Benchmark: bench,
|
||||
Codec: codec,
|
||||
Kind: kind,
|
||||
Target: target,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createAllBenchmarkData(data string) ([]*BenchmarkData, error) {
|
||||
allBenchData := []*BenchmarkData{}
|
||||
for _, line := range strings.Split(data, "\n") {
|
||||
if !strings.HasPrefix(line, "Benchmark") {
|
||||
continue
|
||||
}
|
||||
bench, err := parse.ParseLine(line)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to parse line: %w", err)
|
||||
}
|
||||
benchData, err := createBenchmarkData(bench)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to create benchmark data: %w", err)
|
||||
}
|
||||
allBenchData = append(allBenchData, benchData)
|
||||
}
|
||||
return allBenchData, nil
|
||||
}
|
||||
|
||||
type Graph struct {
|
||||
Title string
|
||||
Codec BenchmarkCodec
|
||||
Kinds []BenchmarkKind
|
||||
}
|
||||
|
||||
func (g *Graph) existsKind(target BenchmarkKind) bool {
|
||||
for _, kind := range g.Kinds {
|
||||
if kind == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func generateBenchmarkGraph(ctx context.Context, client *http.Client, g *Graph, data []*BenchmarkData) error {
|
||||
headers := []string{}
|
||||
for _, kind := range g.Kinds {
|
||||
headers = append(headers, kind.String())
|
||||
}
|
||||
targetMap := map[string][]*BenchmarkData{}
|
||||
targetToData := map[string][]float64{}
|
||||
for _, v := range data {
|
||||
if g.Codec != v.Codec {
|
||||
continue
|
||||
}
|
||||
if !g.existsKind(v.Kind) {
|
||||
continue
|
||||
}
|
||||
name := v.Target.DisplayName()
|
||||
targetMap[name] = append(targetMap[name], v)
|
||||
targetToData[name] = append(targetToData[name], v.NsPerOp)
|
||||
}
|
||||
targets := []string{}
|
||||
for k := range targetMap {
|
||||
targets = append(targets, k)
|
||||
}
|
||||
|
||||
sheetService, err := sheets.NewService(ctx, option.WithHTTPClient(client))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create service for sheet: %w", err)
|
||||
}
|
||||
spreadSheet, err := createSpreadsheet(sheetService, g.Title, headers, targets, targetToData)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create spreadsheet: %w", err)
|
||||
}
|
||||
chart, err := generateChart(sheetService, spreadSheet, g.Title)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to generate chart: %w", err)
|
||||
}
|
||||
log.Println("spreadSheetID = ", spreadSheet.SpreadsheetId, "chartID = ", chart.ChartId)
|
||||
|
||||
slideService, err := slides.NewService(ctx, option.WithHTTPClient(client))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create service for slide: %w", err)
|
||||
}
|
||||
presentationService := slides.NewPresentationsService(slideService)
|
||||
presentation, slideID, err := createPresentationWithEmptySlide(presentationService)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create presentation with slide: %w", err)
|
||||
}
|
||||
if err := addChartToPresentation(presentationService, presentation, slideID, spreadSheet.SpreadsheetId, chart); err != nil {
|
||||
return xerrors.Errorf("failed to add chart to presentation: %w", err)
|
||||
}
|
||||
if err := downloadChartImage(presentationService, presentation, "bench.png"); err != nil {
|
||||
return xerrors.Errorf("failed to download chart image: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func run(args []string) error {
|
||||
benchData, err := createAllBenchmarkData(`
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
pkg: benchmark
|
||||
Benchmark_Encode_SmallStruct_EncodingJson-16 2135164 555 ns/op 256 B/op 2 allocs/op
|
||||
Benchmark_Encode_SmallStruct_FFJson-16 1348426 935 ns/op 584 B/op 9 allocs/op
|
||||
Benchmark_Encode_SmallStruct_JsonIter-16 1970002 598 ns/op 264 B/op 3 allocs/op
|
||||
Benchmark_Encode_SmallStruct_EasyJson-16 2202872 547 ns/op 720 B/op 4 allocs/op
|
||||
Benchmark_Encode_SmallStruct_Jettison-16 2610375 461 ns/op 256 B/op 2 allocs/op
|
||||
Benchmark_Encode_SmallStruct_GoJay-16 2763138 428 ns/op 624 B/op 2 allocs/op
|
||||
Benchmark_Encode_SmallStruct_SegmentioJson-16 4124536 291 ns/op 256 B/op 2 allocs/op
|
||||
Benchmark_Encode_SmallStruct_GoJsonColored-16 2530636 475 ns/op 432 B/op 2 allocs/op
|
||||
Benchmark_Encode_SmallStruct_GoJson-16 4308301 282 ns/op 256 B/op 2 allocs/op
|
||||
Benchmark_Encode_SmallStruct_GoJsonNoEscape-16 5406490 215 ns/op 144 B/op 1 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_EncodingJson-16 2386401 510 ns/op 144 B/op 1 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_FFJson-16 1450132 829 ns/op 472 B/op 8 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_JsonIter-16 2279529 526 ns/op 152 B/op 2 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_EasyJson-16 2225763 543 ns/op 720 B/op 4 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_Jettison-16 3059923 387 ns/op 144 B/op 1 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_GoJay-16 3187108 372 ns/op 512 B/op 1 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_SegmentioJson-16 5128329 229 ns/op 144 B/op 1 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_GoJsonColored-16 3028646 403 ns/op 320 B/op 1 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_GoJson-16 5458942 215 ns/op 144 B/op 1 allocs/op
|
||||
Benchmark_Encode_SmallStructCached_GoJsonNoEscape-16 5725311 210 ns/op 144 B/op 1 allocs/op
|
||||
PASS
|
||||
ok benchmark 33.928s
|
||||
`)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to parse benchmark data: %w", err)
|
||||
}
|
||||
if benchData == nil {
|
||||
return nil
|
||||
}
|
||||
ctx := context.Background()
|
||||
client, err := createClient(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create client: %w", err)
|
||||
}
|
||||
graphs := []*Graph{
|
||||
{
|
||||
Title: "Encoding Benchmark ( Small / Medium Struct )",
|
||||
Codec: Encoder,
|
||||
Kinds: []BenchmarkKind{SmallStruct, MediumStruct},
|
||||
},
|
||||
}
|
||||
for _, graph := range graphs {
|
||||
if err := generateBenchmarkGraph(ctx, client, graph, benchData); err != nil {
|
||||
return xerrors.Errorf("failed to generate benchmark graph: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := run(os.Args); err != nil {
|
||||
log.Fatalf("%+v", err)
|
||||
}
|
||||
}
|
|
@ -120,7 +120,7 @@ func (t OpType) HeadToOmitEmptyHead() OpType {
|
|||
}
|
||||
|
||||
func (t OpType) PtrHeadToHead() OpType {
|
||||
idx := strings.Index(t.String(), "PtrHead")
|
||||
idx := strings.Index(t.String(), "Ptr")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ func (t OpType) FieldToOmitEmptyField() OpType {
|
|||
createOpType("Recursive", "Op"),
|
||||
createOpType("RecursivePtr", "Op"),
|
||||
createOpType("RecursiveEnd", "Op"),
|
||||
createOpType("InterfaceEnd", "Op"),
|
||||
createOpType("StructAnonymousEnd", "StructEnd"),
|
||||
}
|
||||
for _, typ := range primitiveTypesUpper {
|
||||
typ := typ
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,
|
||||
|
@ -49,16 +46,8 @@ func (d *arrayDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) er
|
|||
return nil
|
||||
case '[':
|
||||
idx := 0
|
||||
s.cursor++
|
||||
if s.skipWhiteSpace() == ']' {
|
||||
for idx < d.alen {
|
||||
*(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue
|
||||
idx++
|
||||
}
|
||||
s.cursor++
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
s.cursor++
|
||||
if idx < d.alen {
|
||||
if err := d.valueDecoder.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size)); err != nil {
|
||||
return err
|
||||
|
@ -78,11 +67,9 @@ func (d *arrayDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) er
|
|||
s.cursor++
|
||||
return nil
|
||||
case ',':
|
||||
s.cursor++
|
||||
continue
|
||||
case nul:
|
||||
if s.read() {
|
||||
s.cursor++
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
|
@ -124,17 +111,8 @@ func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
|
|||
return cursor, nil
|
||||
case '[':
|
||||
idx := 0
|
||||
cursor++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == ']' {
|
||||
for idx < d.alen {
|
||||
*(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue
|
||||
idx++
|
||||
}
|
||||
cursor++
|
||||
return cursor, nil
|
||||
}
|
||||
for {
|
||||
cursor++
|
||||
if idx < d.alen {
|
||||
c, err := d.valueDecoder.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size))
|
||||
if err != nil {
|
||||
|
@ -159,7 +137,6 @@ func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
|
|||
cursor++
|
||||
return cursor, nil
|
||||
case ',':
|
||||
cursor++
|
||||
continue
|
||||
default:
|
||||
return 0, errors.ErrInvalidCharacter(buf[cursor], "array", cursor)
|
||||
|
@ -170,7 +147,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"
|
||||
|
@ -10,11 +9,10 @@ import (
|
|||
)
|
||||
|
||||
type bytesDecoder struct {
|
||||
typ *runtime.Type
|
||||
sliceDecoder Decoder
|
||||
stringDecoder *stringDecoder
|
||||
structName string
|
||||
fieldName string
|
||||
typ *runtime.Type
|
||||
sliceDecoder Decoder
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func byteUnmarshalerSliceDecoder(typ *runtime.Type, structName string, fieldName string) Decoder {
|
||||
|
@ -24,19 +22,19 @@ 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)
|
||||
}
|
||||
|
||||
func newBytesDecoder(typ *runtime.Type, structName string, fieldName string) *bytesDecoder {
|
||||
return &bytesDecoder{
|
||||
typ: typ,
|
||||
sliceDecoder: byteUnmarshalerSliceDecoder(typ, structName, fieldName),
|
||||
stringDecoder: newStringDecoder(structName, fieldName),
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
typ: typ,
|
||||
sliceDecoder: byteUnmarshalerSliceDecoder(typ, structName, fieldName),
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,11 +49,10 @@ func (d *bytesDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) er
|
|||
}
|
||||
decodedLen := base64.StdEncoding.DecodedLen(len(bytes))
|
||||
buf := make([]byte, decodedLen)
|
||||
n, err := base64.StdEncoding.Decode(buf, bytes)
|
||||
if err != nil {
|
||||
if _, err := base64.StdEncoding.Decode(buf, bytes); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*[]byte)(p) = buf[:n]
|
||||
*(*[]byte)(p) = buf
|
||||
s.reset()
|
||||
return nil
|
||||
}
|
||||
|
@ -79,40 +76,101 @@ 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 binaryBytes(s *Stream) ([]byte, error) {
|
||||
s.cursor++
|
||||
start := s.cursor
|
||||
for {
|
||||
switch s.char() {
|
||||
case '"':
|
||||
literal := s.buf[start:s.cursor]
|
||||
s.cursor++
|
||||
return literal, nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
}
|
||||
s.cursor++
|
||||
}
|
||||
ERROR:
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("[]byte", s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Pointer) ([]byte, error) {
|
||||
c := s.skipWhiteSpace()
|
||||
if c == '[' {
|
||||
if d.sliceDecoder == nil {
|
||||
return nil, &errors.UnmarshalTypeError{
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
for {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
s.cursor++
|
||||
continue
|
||||
case '"':
|
||||
return binaryBytes(s)
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case '[':
|
||||
if d.sliceDecoder == nil {
|
||||
return nil, &errors.UnmarshalTypeError{
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
}
|
||||
if err := d.sliceDecoder.DecodeStream(s, depth, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
case nul:
|
||||
if s.read() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
err := d.sliceDecoder.DecodeStream(s, depth, p)
|
||||
return nil, err
|
||||
break
|
||||
}
|
||||
return d.stringDecoder.decodeStreamByte(s)
|
||||
return nil, errors.ErrNotAtBeginningOfValue(s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *bytesDecoder) decodeBinary(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) ([]byte, int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
if buf[cursor] == '[' {
|
||||
if d.sliceDecoder == nil {
|
||||
return nil, 0, &errors.UnmarshalTypeError{
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: cursor,
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
cursor++
|
||||
case '"':
|
||||
cursor++
|
||||
start := cursor
|
||||
for {
|
||||
switch buf[cursor] {
|
||||
case '"':
|
||||
literal := buf[start:cursor]
|
||||
cursor++
|
||||
return literal, cursor, nil
|
||||
case nul:
|
||||
return nil, 0, errors.ErrUnexpectedEndOfJSON("[]byte", cursor)
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
case '[':
|
||||
if d.sliceDecoder == nil {
|
||||
return nil, 0, &errors.UnmarshalTypeError{
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
c, err := d.sliceDecoder.Decode(ctx, cursor, depth, p)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return nil, c, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return nil, cursor, nil
|
||||
default:
|
||||
return nil, 0, errors.ErrNotAtBeginningOfValue(cursor)
|
||||
}
|
||||
c, err := d.sliceDecoder.Decode(ctx, cursor, depth, p)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return nil, c, nil
|
||||
}
|
||||
return d.stringDecoder.decodeByte(buf, cursor)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -59,7 +60,7 @@ func compileToGetDecoderSlowPath(typeptr uintptr, typ *runtime.Type) (Decoder, e
|
|||
|
||||
func compileHead(typ *runtime.Type, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
switch {
|
||||
case implementsUnmarshalJSONType(runtime.PtrTo(typ)):
|
||||
case runtime.PtrTo(typ).Implements(unmarshalJSONType):
|
||||
return newUnmarshalJSONDecoder(runtime.PtrTo(typ), "", ""), nil
|
||||
case runtime.PtrTo(typ).Implements(unmarshalTextType):
|
||||
return newUnmarshalTextDecoder(runtime.PtrTo(typ), "", ""), nil
|
||||
|
@ -69,7 +70,7 @@ func compileHead(typ *runtime.Type, structTypeToDecoder map[uintptr]Decoder) (De
|
|||
|
||||
func compile(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
switch {
|
||||
case implementsUnmarshalJSONType(runtime.PtrTo(typ)):
|
||||
case runtime.PtrTo(typ).Implements(unmarshalJSONType):
|
||||
return newUnmarshalJSONDecoder(runtime.PtrTo(typ), structName, fieldName), nil
|
||||
case runtime.PtrTo(typ).Implements(unmarshalTextType):
|
||||
return newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName), nil
|
||||
|
@ -122,15 +123,17 @@ func compile(typ *runtime.Type, structName, fieldName string, structTypeToDecode
|
|||
return compileFloat32(structName, fieldName)
|
||||
case reflect.Float64:
|
||||
return compileFloat64(structName, fieldName)
|
||||
case reflect.Func:
|
||||
return compileFunc(typ, structName, fieldName)
|
||||
}
|
||||
return newInvalidDecoder(typ, structName, fieldName), nil
|
||||
return nil, &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(typ),
|
||||
Offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func isStringTagSupportedType(typ *runtime.Type) bool {
|
||||
switch {
|
||||
case implementsUnmarshalJSONType(runtime.PtrTo(typ)):
|
||||
case runtime.PtrTo(typ).Implements(unmarshalJSONType):
|
||||
return false
|
||||
case runtime.PtrTo(typ).Implements(unmarshalTextType):
|
||||
return false
|
||||
|
@ -154,9 +157,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 +170,15 @@ 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,
|
||||
}
|
||||
}
|
||||
|
||||
func compilePtr(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) {
|
||||
|
@ -306,25 +312,64 @@ func compileInterface(typ *runtime.Type, structName, fieldName string) (Decoder,
|
|||
return newInterfaceDecoder(typ, structName, fieldName), nil
|
||||
}
|
||||
|
||||
func compileFunc(typ *runtime.Type, strutName, fieldName string) (Decoder, error) {
|
||||
return newFuncDecoder(typ, strutName, fieldName), nil
|
||||
}
|
||||
|
||||
func typeToStructTags(typ *runtime.Type) runtime.StructTags {
|
||||
tags := runtime.StructTags{}
|
||||
fieldNum := typ.NumField()
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
field := typ.Field(i)
|
||||
if runtime.IsIgnoredStructField(field) {
|
||||
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 +378,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 +395,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 +411,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,58 +483,14 @@ 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)
|
||||
structDec.tryOptimize()
|
||||
return structDec, nil
|
||||
}
|
||||
|
||||
func filterDuplicatedFields(allFields []*structFieldSet) []*structFieldSet {
|
||||
fieldMap := map[string][]*structFieldSet{}
|
||||
for _, field := range allFields {
|
||||
fieldMap[field.key] = append(fieldMap[field.key], field)
|
||||
}
|
||||
duplicatedFieldMap := map[string]struct{}{}
|
||||
for k, sets := range fieldMap {
|
||||
sets = filterFieldSets(sets)
|
||||
if len(sets) != 1 {
|
||||
duplicatedFieldMap[k] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
filtered := make([]*structFieldSet, 0, len(allFields))
|
||||
for _, field := range allFields {
|
||||
if _, exists := duplicatedFieldMap[field.key]; exists {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, field)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func filterFieldSets(sets []*structFieldSet) []*structFieldSet {
|
||||
if len(sets) == 1 {
|
||||
return sets
|
||||
}
|
||||
filtered := make([]*structFieldSet, 0, len(sets))
|
||||
for _, set := range sets {
|
||||
if set.isTaggedKey {
|
||||
filtered = append(filtered, set)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func implementsUnmarshalJSONType(typ *runtime.Type) bool {
|
||||
return typ.Implements(unmarshalJSONType) || typ.Implements(unmarshalJSONContextType)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
type funcDecoder struct {
|
||||
typ *runtime.Type
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newFuncDecoder(typ *runtime.Type, structName, fieldName string) *funcDecoder {
|
||||
fnDecoder := &funcDecoder{typ, structName, fieldName}
|
||||
return fnDecoder
|
||||
}
|
||||
|
||||
func (d *funcDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
s.skipWhiteSpace()
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
src := s.buf[start:s.cursor]
|
||||
if len(src) > 0 {
|
||||
switch src[0] {
|
||||
case '"':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "string",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case '[':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "array",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case '{':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "number",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
case 'n':
|
||||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return nil
|
||||
case 't':
|
||||
if err := trueBytes(s); err == nil {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "boolean",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
}
|
||||
case 'f':
|
||||
if err := falseBytes(s); err == nil {
|
||||
return &errors.UnmarshalTypeError{
|
||||
Value: "boolean",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: s.totalOffset(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.ErrInvalidBeginningOfValue(s.buf[s.cursor], s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *funcDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src := buf[start:end]
|
||||
if len(src) > 0 {
|
||||
switch src[0] {
|
||||
case '"':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "string",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case '[':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "array",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case '{':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "object",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "number",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
case 'n':
|
||||
if bytes.Equal(src, nullbytes) {
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return end, nil
|
||||
}
|
||||
case 't':
|
||||
if err := validateTrue(buf, start); err == nil {
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "boolean",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
}
|
||||
case 'f':
|
||||
if err := validateFalse(buf, start); err == nil {
|
||||
return 0, &errors.UnmarshalTypeError{
|
||||
Value: "boolean",
|
||||
Type: runtime.RType2Type(d.typ),
|
||||
Offset: start,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
func (d *funcDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: func decoder does not support decode path")
|
||||
}
|
|
@ -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(),
|
||||
)
|
||||
|
@ -118,21 +117,6 @@ func decodeStreamUnmarshaler(s *Stream, depth int64, unmarshaler json.Unmarshale
|
|||
return nil
|
||||
}
|
||||
|
||||
func decodeStreamUnmarshalerContext(s *Stream, depth int64, unmarshaler unmarshalerContext) error {
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
return err
|
||||
}
|
||||
src := s.buf[start:s.cursor]
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
if err := unmarshaler.UnmarshalJSON(s.Option.Context, dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeUnmarshaler(buf []byte, cursor, depth int64, unmarshaler json.Unmarshaler) (int64, error) {
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
|
@ -150,23 +134,6 @@ func decodeUnmarshaler(buf []byte, cursor, depth int64, unmarshaler json.Unmarsh
|
|||
return end, nil
|
||||
}
|
||||
|
||||
func decodeUnmarshalerContext(ctx *RuntimeContext, buf []byte, cursor, depth int64, unmarshaler unmarshalerContext) (int64, error) {
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
start := cursor
|
||||
end, err := skipValue(buf, cursor, depth)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
src := buf[start:end]
|
||||
dst := make([]byte, len(src))
|
||||
copy(dst, src)
|
||||
|
||||
if err := unmarshaler.UnmarshalJSON(ctx.Option.Context, dst); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return end, nil
|
||||
}
|
||||
|
||||
func decodeStreamTextUnmarshaler(s *Stream, depth int64, unmarshaler encoding.TextUnmarshaler, p unsafe.Pointer) error {
|
||||
start := s.cursor
|
||||
if err := s.skipValue(depth); err != nil {
|
||||
|
@ -278,7 +245,7 @@ func (d *interfaceDecoder) decodeStreamEmptyInterface(s *Stream, depth int64, p
|
|||
}
|
||||
break
|
||||
}
|
||||
return errors.ErrInvalidBeginningOfValue(c, s.totalOffset())
|
||||
return errors.ErrNotAtBeginningOfValue(s.totalOffset())
|
||||
}
|
||||
|
||||
type emptyInterface struct {
|
||||
|
@ -293,9 +260,6 @@ func (d *interfaceDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer
|
|||
}))
|
||||
rv := reflect.ValueOf(runtimeInterfaceValue)
|
||||
if rv.NumMethod() > 0 && rv.CanInterface() {
|
||||
if u, ok := rv.Interface().(unmarshalerContext); ok {
|
||||
return decodeStreamUnmarshalerContext(s, depth, u)
|
||||
}
|
||||
if u, ok := rv.Interface().(json.Unmarshaler); ok {
|
||||
return decodeStreamUnmarshaler(s, depth, u)
|
||||
}
|
||||
|
@ -353,9 +317,6 @@ func (d *interfaceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p un
|
|||
}))
|
||||
rv := reflect.ValueOf(runtimeInterfaceValue)
|
||||
if rv.NumMethod() > 0 && rv.CanInterface() {
|
||||
if u, ok := rv.Interface().(unmarshalerContext); ok {
|
||||
return decodeUnmarshalerContext(ctx, buf, cursor, depth, u)
|
||||
}
|
||||
if u, ok := rv.Interface().(json.Unmarshaler); ok {
|
||||
return decodeUnmarshaler(buf, cursor, depth, u)
|
||||
}
|
||||
|
@ -455,74 +416,5 @@ func (d *interfaceDecoder) decodeEmptyInterface(ctx *RuntimeContext, cursor, dep
|
|||
**(**interface{})(unsafe.Pointer(&p)) = nil
|
||||
return cursor, nil
|
||||
}
|
||||
return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
|
||||
}
|
||||
|
||||
func NewPathDecoder() Decoder {
|
||||
ifaceDecoder := &interfaceDecoder{
|
||||
typ: emptyInterfaceType,
|
||||
structName: "",
|
||||
fieldName: "",
|
||||
floatDecoder: newFloatDecoder("", "", func(p unsafe.Pointer, v float64) {
|
||||
*(*interface{})(p) = v
|
||||
}),
|
||||
numberDecoder: newNumberDecoder("", "", func(p unsafe.Pointer, v json.Number) {
|
||||
*(*interface{})(p) = v
|
||||
}),
|
||||
stringDecoder: newStringDecoder("", ""),
|
||||
}
|
||||
ifaceDecoder.sliceDecoder = newSliceDecoder(
|
||||
ifaceDecoder,
|
||||
emptyInterfaceType,
|
||||
emptyInterfaceType.Size(),
|
||||
"", "",
|
||||
)
|
||||
ifaceDecoder.mapDecoder = newMapDecoder(
|
||||
interfaceMapType,
|
||||
stringType,
|
||||
ifaceDecoder.stringDecoder,
|
||||
interfaceMapType.Elem(),
|
||||
ifaceDecoder,
|
||||
"", "",
|
||||
)
|
||||
return ifaceDecoder
|
||||
}
|
||||
|
||||
var (
|
||||
truebytes = []byte("true")
|
||||
falsebytes = []byte("false")
|
||||
)
|
||||
|
||||
func (d *interfaceDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
buf := ctx.Buf
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
switch buf[cursor] {
|
||||
case '{':
|
||||
return d.mapDecoder.DecodePath(ctx, cursor, depth)
|
||||
case '[':
|
||||
return d.sliceDecoder.DecodePath(ctx, cursor, depth)
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
return d.floatDecoder.DecodePath(ctx, cursor, depth)
|
||||
case '"':
|
||||
return d.stringDecoder.DecodePath(ctx, cursor, depth)
|
||||
case 't':
|
||||
if err := validateTrue(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return [][]byte{truebytes}, cursor, nil
|
||||
case 'f':
|
||||
if err := validateFalse(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 5
|
||||
return [][]byte{falsebytes}, cursor, nil
|
||||
case 'n':
|
||||
if err := validateNull(buf, cursor); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
cursor += 4
|
||||
return [][]byte{nullbytes}, cursor, nil
|
||||
}
|
||||
return nil, cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
|
||||
return cursor, errors.ErrNotAtBeginningOfValue(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,
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/errors"
|
||||
|
@ -9,62 +8,33 @@ import (
|
|||
)
|
||||
|
||||
type mapDecoder struct {
|
||||
mapType *runtime.Type
|
||||
keyType *runtime.Type
|
||||
valueType *runtime.Type
|
||||
canUseAssignFaststrType bool
|
||||
keyDecoder Decoder
|
||||
valueDecoder Decoder
|
||||
structName string
|
||||
fieldName string
|
||||
mapType *runtime.Type
|
||||
keyType *runtime.Type
|
||||
valueType *runtime.Type
|
||||
keyDecoder Decoder
|
||||
valueDecoder Decoder
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder {
|
||||
return &mapDecoder{
|
||||
mapType: mapType,
|
||||
keyDecoder: keyDec,
|
||||
keyType: keyType,
|
||||
canUseAssignFaststrType: canUseAssignFaststrType(keyType, valueType),
|
||||
valueType: valueType,
|
||||
valueDecoder: valueDec,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
mapType: mapType,
|
||||
keyDecoder: keyDec,
|
||||
keyType: keyType,
|
||||
valueType: valueType,
|
||||
valueDecoder: valueDec,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
mapMaxElemSize = 128
|
||||
)
|
||||
|
||||
// See detail: https://github.com/goccy/go-json/pull/283
|
||||
func canUseAssignFaststrType(key *runtime.Type, value *runtime.Type) bool {
|
||||
indirectElem := value.Size() > mapMaxElemSize
|
||||
if indirectElem {
|
||||
return false
|
||||
}
|
||||
return key.Kind() == reflect.String
|
||||
}
|
||||
|
||||
//go:linkname makemap reflect.makemap
|
||||
func makemap(*runtime.Type, int) unsafe.Pointer
|
||||
|
||||
//nolint:golint
|
||||
//go:linkname mapassign_faststr runtime.mapassign_faststr
|
||||
//go:noescape
|
||||
func mapassign_faststr(t *runtime.Type, m unsafe.Pointer, s string) unsafe.Pointer
|
||||
|
||||
//go:linkname mapassign reflect.mapassign
|
||||
//go:noescape
|
||||
func mapassign(t *runtime.Type, m unsafe.Pointer, k, v unsafe.Pointer)
|
||||
|
||||
func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) {
|
||||
if d.canUseAssignFaststrType {
|
||||
mapV := mapassign_faststr(t, m, *(*string)(k))
|
||||
typedmemmove(d.valueType, mapV, v)
|
||||
} else {
|
||||
mapassign(t, m, k, v)
|
||||
}
|
||||
}
|
||||
func mapassign(t *runtime.Type, m unsafe.Pointer, key, val unsafe.Pointer)
|
||||
|
||||
func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error {
|
||||
depth++
|
||||
|
@ -87,13 +57,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
|
||||
|
@ -107,7 +77,7 @@ func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) erro
|
|||
if err := d.valueDecoder.DecodeStream(s, depth, v); err != nil {
|
||||
return err
|
||||
}
|
||||
d.mapassign(d.mapType, mapValue, k, v)
|
||||
mapassign(d.mapType, mapValue, k, v)
|
||||
s.skipWhiteSpace()
|
||||
if s.equalChar('}') {
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
|
||||
|
@ -117,7 +87,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++
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,7 +141,7 @@ func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.mapassign(d.mapType, mapValue, k, v)
|
||||
mapassign(d.mapType, mapValue, k, v)
|
||||
cursor = skipWhiteSpace(buf, valueCursor)
|
||||
if buf[cursor] == '}' {
|
||||
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue
|
||||
|
@ -185,96 +154,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,19 +51,7 @@ 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 {
|
||||
switch s.char() {
|
||||
case ' ', '\n', '\t', '\r':
|
||||
|
@ -88,9 +76,6 @@ func (d *numberDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
ERROR:
|
||||
if s.cursor == start {
|
||||
return nil, errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
|
||||
}
|
||||
return nil, errors.ErrUnexpectedEndOfJSON("json.Number", s.totalOffset())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
package decoder
|
||||
|
||||
import "context"
|
||||
|
||||
type OptionFlags uint8
|
||||
type OptionFlag int
|
||||
|
||||
const (
|
||||
FirstWinOption OptionFlags = 1 << iota
|
||||
ContextOption
|
||||
PathOption
|
||||
FirstWinOption OptionFlag = 1 << iota
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
Flags OptionFlags
|
||||
Context context.Context
|
||||
Path *Path
|
||||
Flag OptionFlag
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -9,13 +9,6 @@ import (
|
|||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
sliceType = runtime.Type2RType(
|
||||
reflect.TypeOf((*sliceHeader)(nil)).Elem(),
|
||||
)
|
||||
nilSlice = unsafe.Pointer(&sliceHeader{})
|
||||
)
|
||||
|
||||
type sliceDecoder struct {
|
||||
elemType *runtime.Type
|
||||
isElemPointerType bool
|
||||
|
@ -114,7 +107,7 @@ func (d *sliceDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) er
|
|||
if err := nullBytes(s); err != nil {
|
||||
return err
|
||||
}
|
||||
typedmemmove(sliceType, p, nilSlice)
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return nil
|
||||
case '[':
|
||||
s.cursor++
|
||||
|
@ -223,7 +216,7 @@ func (d *sliceDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe
|
|||
return 0, err
|
||||
}
|
||||
cursor += 4
|
||||
typedmemmove(sliceType, p, nilSlice)
|
||||
*(*unsafe.Pointer)(p) = nil
|
||||
return cursor, nil
|
||||
case '[':
|
||||
cursor++
|
||||
|
@ -299,82 +292,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())
|
||||
|
@ -190,7 +170,6 @@ RETRY:
|
|||
s.buf = append(s.buf[:s.cursor-1], s.buf[s.cursor:]...)
|
||||
s.length--
|
||||
s.cursor--
|
||||
p = s.bufptr()
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -259,23 +238,14 @@ func stringBytes(s *Stream) ([]byte, error) {
|
|||
fallthrough
|
||||
default:
|
||||
// multi bytes character
|
||||
if !utf8.FullRune(s.buf[cursor : len(s.buf)-1]) {
|
||||
s.cursor = cursor
|
||||
if s.read() {
|
||||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
goto ERROR
|
||||
}
|
||||
r, size := utf8.DecodeRune(s.buf[cursor:])
|
||||
r, _ := utf8.DecodeRune(s.buf[cursor:])
|
||||
b := []byte(string(r))
|
||||
if r == utf8.RuneError {
|
||||
s.buf = append(append(append([]byte{}, s.buf[:cursor]...), runeErrBytes...), s.buf[cursor+1:]...)
|
||||
cursor += runeErrBytesLen
|
||||
s.length += runeErrBytesLen
|
||||
s.buf = append(append(append([]byte{}, s.buf[:cursor]...), b...), s.buf[cursor+1:]...)
|
||||
_, _, p = s.stat()
|
||||
} else {
|
||||
cursor += int64(size)
|
||||
}
|
||||
cursor += int64(len(b))
|
||||
s.length += int64(len(b))
|
||||
continue
|
||||
}
|
||||
cursor++
|
||||
|
@ -310,7 +280,7 @@ func (d *stringDecoder) decodeStreamByte(s *Stream) ([]byte, error) {
|
|||
}
|
||||
break
|
||||
}
|
||||
return nil, errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
|
||||
return nil, errors.ErrNotAtBeginningOfValue(s.totalOffset())
|
||||
}
|
||||
|
||||
func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
|
||||
|
@ -328,36 +298,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:
|
||||
|
@ -372,81 +355,7 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
|
|||
cursor += 4
|
||||
return nil, cursor, nil
|
||||
default:
|
||||
return nil, 0, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor)
|
||||
return nil, 0, errors.ErrNotAtBeginningOfValue(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unescapeMap = [256]byte{
|
||||
'"': '"',
|
||||
'\\': '\\',
|
||||
'/': '/',
|
||||
'b': '\b',
|
||||
'f': '\f',
|
||||
'n': '\n',
|
||||
'r': '\r',
|
||||
't': '\t',
|
||||
}
|
||||
|
||||
func unsafeAdd(ptr unsafe.Pointer, offset int) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(ptr) + uintptr(offset))
|
||||
}
|
||||
|
||||
func unescapeString(buf []byte) int {
|
||||
p := (*sliceHeader)(unsafe.Pointer(&buf)).data
|
||||
end := unsafeAdd(p, len(buf))
|
||||
src := unsafeAdd(p, bytes.IndexByte(buf, '\\'))
|
||||
dst := src
|
||||
for src != end {
|
||||
c := char(src, 0)
|
||||
if c == '\\' {
|
||||
escapeChar := char(src, 1)
|
||||
if escapeChar != 'u' {
|
||||
*(*byte)(dst) = unescapeMap[escapeChar]
|
||||
src = unsafeAdd(src, 2)
|
||||
dst = unsafeAdd(dst, 1)
|
||||
} else {
|
||||
v1 := hexToInt[char(src, 2)]
|
||||
v2 := hexToInt[char(src, 3)]
|
||||
v3 := hexToInt[char(src, 4)]
|
||||
v4 := hexToInt[char(src, 5)]
|
||||
code := rune((v1 << 12) | (v2 << 8) | (v3 << 4) | v4)
|
||||
if code >= 0xd800 && code < 0xdc00 && uintptr(unsafeAdd(src, 11)) < uintptr(end) {
|
||||
if char(src, 6) == '\\' && char(src, 7) == 'u' {
|
||||
v1 := hexToInt[char(src, 8)]
|
||||
v2 := hexToInt[char(src, 9)]
|
||||
v3 := hexToInt[char(src, 10)]
|
||||
v4 := hexToInt[char(src, 11)]
|
||||
lo := rune((v1 << 12) | (v2 << 8) | (v3 << 4) | v4)
|
||||
if lo >= 0xdc00 && lo < 0xe000 {
|
||||
code = (code-0xd800)<<10 | (lo - 0xdc00) + 0x10000
|
||||
src = unsafeAdd(src, 6)
|
||||
}
|
||||
}
|
||||
}
|
||||
var b [utf8.UTFMax]byte
|
||||
n := utf8.EncodeRune(b[:], code)
|
||||
switch n {
|
||||
case 4:
|
||||
*(*byte)(unsafeAdd(dst, 3)) = b[3]
|
||||
fallthrough
|
||||
case 3:
|
||||
*(*byte)(unsafeAdd(dst, 2)) = b[2]
|
||||
fallthrough
|
||||
case 2:
|
||||
*(*byte)(unsafeAdd(dst, 1)) = b[1]
|
||||
fallthrough
|
||||
case 1:
|
||||
*(*byte)(unsafeAdd(dst, 0)) = b[0]
|
||||
}
|
||||
src = unsafeAdd(src, 6)
|
||||
dst = unsafeAdd(dst, n)
|
||||
}
|
||||
} else {
|
||||
*(*byte)(dst) = c
|
||||
src = unsafeAdd(src, 1)
|
||||
dst = unsafeAdd(dst, 1)
|
||||
}
|
||||
}
|
||||
return int(uintptr(dst) - uintptr(p))
|
||||
}
|
||||
|
|
|
@ -261,7 +261,7 @@ func decodeKeyByBitmapUint8(d *structDecoder, buf []byte, cursor int64) (int64,
|
|||
cursor++
|
||||
}
|
||||
default:
|
||||
return cursor, nil, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
|
||||
return cursor, nil, errors.ErrNotAtBeginningOfValue(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -324,7 +324,7 @@ func decodeKeyByBitmapUint16(d *structDecoder, buf []byte, cursor int64) (int64,
|
|||
cursor++
|
||||
}
|
||||
default:
|
||||
return cursor, nil, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
|
||||
return cursor, nil, errors.ErrNotAtBeginningOfValue(cursor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +376,7 @@ func decodeKeyByBitmapUint8Stream(d *structDecoder, s *Stream) (*structFieldSet,
|
|||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
|
||||
return nil, "", errors.ErrNotAtBeginningOfValue(s.totalOffset())
|
||||
case '"':
|
||||
cursor++
|
||||
FIRST_CHAR:
|
||||
|
@ -443,7 +443,7 @@ func decodeKeyByBitmapUint8Stream(d *structDecoder, s *Stream) (*structFieldSet,
|
|||
cursor++
|
||||
}
|
||||
default:
|
||||
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
|
||||
return nil, "", errors.ErrNotAtBeginningOfValue(s.totalOffset())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -463,7 +463,7 @@ func decodeKeyByBitmapUint16Stream(d *structDecoder, s *Stream) (*structFieldSet
|
|||
_, cursor, p = s.stat()
|
||||
continue
|
||||
}
|
||||
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
|
||||
return nil, "", errors.ErrNotAtBeginningOfValue(s.totalOffset())
|
||||
case '"':
|
||||
cursor++
|
||||
FIRST_CHAR:
|
||||
|
@ -530,7 +530,7 @@ func decodeKeyByBitmapUint16Stream(d *structDecoder, s *Stream) (*structFieldSet
|
|||
cursor++
|
||||
}
|
||||
default:
|
||||
return nil, "", errors.ErrInvalidBeginningOfValue(char(p, cursor), s.totalOffset())
|
||||
return nil, "", errors.ErrNotAtBeginningOfValue(s.totalOffset())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -653,7 +653,7 @@ func (d *structDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) e
|
|||
return nil
|
||||
default:
|
||||
if s.char() != '{' {
|
||||
return errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset())
|
||||
return errors.ErrNotAtBeginningOfValue(s.totalOffset())
|
||||
}
|
||||
}
|
||||
s.cursor++
|
||||
|
@ -665,7 +665,7 @@ func (d *structDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) e
|
|||
seenFields map[int]struct{}
|
||||
seenFieldNum int
|
||||
)
|
||||
firstWin := (s.Option.Flags & FirstWinOption) != 0
|
||||
firstWin := (s.Option.Flag & FirstWinOption) != 0
|
||||
if firstWin {
|
||||
seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
|
||||
}
|
||||
|
@ -740,7 +740,7 @@ func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
|
|||
return cursor, nil
|
||||
case '{':
|
||||
default:
|
||||
return 0, errors.ErrInvalidBeginningOfValue(char(b, cursor), cursor)
|
||||
return 0, errors.ErrNotAtBeginningOfValue(cursor)
|
||||
}
|
||||
cursor++
|
||||
cursor = skipWhiteSpace(buf, cursor)
|
||||
|
@ -752,7 +752,7 @@ func (d *structDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsaf
|
|||
seenFields map[int]struct{}
|
||||
seenFieldNum int
|
||||
)
|
||||
firstWin := (ctx.Option.Flags & FirstWinOption) != 0
|
||||
firstWin := (ctx.Option.Flag & FirstWinOption) != 0
|
||||
if firstWin {
|
||||
seenFields = make(map[int]struct{}, d.fieldUniqueNameNum)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package decoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
@ -10,7 +9,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
|
||||
}
|
||||
|
||||
|
@ -19,12 +17,7 @@ const (
|
|||
maxDecodeNestingDepth = 10000
|
||||
)
|
||||
|
||||
type unmarshalerContext interface {
|
||||
UnmarshalJSON(context.Context, []byte) error
|
||||
}
|
||||
|
||||
var (
|
||||
unmarshalJSONType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
|
||||
unmarshalJSONContextType = reflect.TypeOf((*unmarshalerContext)(nil)).Elem()
|
||||
unmarshalTextType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
unmarshalJSONType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
|
||||
unmarshalTextType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
|
|
@ -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,23 +46,9 @@ 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 {
|
||||
d.annotateError(s.cursor, err)
|
||||
return err
|
||||
}
|
||||
case json.Unmarshaler:
|
||||
if err := v.UnmarshalJSON(dst); err != nil {
|
||||
d.annotateError(s.cursor, err)
|
||||
return err
|
||||
}
|
||||
if err := v.(json.Unmarshaler).UnmarshalJSON(dst); err != nil {
|
||||
d.annotateError(s.cursor, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -85,20 +69,9 @@ func (d *unmarshalJSONDecoder) Decode(ctx *RuntimeContext, cursor, depth int64,
|
|||
typ: d.typ,
|
||||
ptr: p,
|
||||
}))
|
||||
if (ctx.Option.Flags & ContextOption) != 0 {
|
||||
if err := v.(unmarshalerContext).UnmarshalJSON(ctx.Option.Context, dst); err != nil {
|
||||
d.annotateError(cursor, err)
|
||||
return 0, err
|
||||
}
|
||||
} else {
|
||||
if err := v.(json.Unmarshaler).UnmarshalJSON(dst); err != nil {
|
||||
d.annotateError(cursor, err)
|
||||
return 0, err
|
||||
}
|
||||
if err := v.(json.Unmarshaler).UnmarshalJSON(dst); err != nil {
|
||||
d.annotateError(cursor, err)
|
||||
return 0, err
|
||||
}
|
||||
return end, nil
|
||||
}
|
||||
|
||||
func (d *unmarshalJSONDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) {
|
||||
return nil, 0, fmt.Errorf("json: unmarshal json decoder does not support decode path")
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,32 +1,49 @@
|
|||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
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)
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
|
||||
if typeptr > typeAddr.MaxTypeAddr {
|
||||
return compileToGetCodeSetSlowPath(typeptr)
|
||||
}
|
||||
index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift
|
||||
if codeSet := cachedOpcodeSets[index]; codeSet != nil {
|
||||
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return filtered, nil
|
||||
return codeSet, nil
|
||||
}
|
||||
codeSet, err := newCompiler().compile(typeptr)
|
||||
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
|
||||
|
||||
noescapeKeyCode, err := compileHead(&compileContext{
|
||||
typ: copiedType,
|
||||
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
escapeKeyCode, err := compileHead(&compileContext{
|
||||
typ: copiedType,
|
||||
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
|
||||
escapeKey: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
noescapeKeyCode = copyOpcode(noescapeKeyCode)
|
||||
escapeKeyCode = copyOpcode(escapeKeyCode)
|
||||
codeLength := noescapeKeyCode.TotalLength()
|
||||
codeSet := &OpcodeSet{
|
||||
Type: copiedType,
|
||||
NoescapeKeyCode: noescapeKeyCode,
|
||||
EscapeKeyCode: escapeKeyCode,
|
||||
CodeLength: codeLength,
|
||||
}
|
||||
cachedOpcodeSets[index] = codeSet
|
||||
return filtered, nil
|
||||
return codeSet, nil
|
||||
}
|
||||
|
|
|
@ -1,45 +1,58 @@
|
|||
//go:build race
|
||||
// +build race
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goccy/go-json/internal/runtime"
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
codeSet, err := newCompiler().compile(typeptr)
|
||||
// noescape trick for header.typ ( reflect.*rtype )
|
||||
copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
|
||||
|
||||
noescapeKeyCode, err := compileHead(&compileContext{
|
||||
typ: copiedType,
|
||||
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet)
|
||||
escapeKeyCode, err := compileHead(&compileContext{
|
||||
typ: copiedType,
|
||||
structTypeToCompiledCode: map[uintptr]*CompiledCode{},
|
||||
escapeKey: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
noescapeKeyCode = copyOpcode(noescapeKeyCode)
|
||||
escapeKeyCode = copyOpcode(escapeKeyCode)
|
||||
codeLength := noescapeKeyCode.TotalLength()
|
||||
codeSet := &OpcodeSet{
|
||||
Type: copiedType,
|
||||
NoescapeKeyCode: noescapeKeyCode,
|
||||
EscapeKeyCode: escapeKeyCode,
|
||||
CodeLength: codeLength,
|
||||
}
|
||||
setsMu.Lock()
|
||||
cachedOpcodeSets[index] = codeSet
|
||||
setsMu.Unlock()
|
||||
return filtered, nil
|
||||
return codeSet, nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
|
@ -9,20 +8,44 @@ import (
|
|||
)
|
||||
|
||||
type compileContext struct {
|
||||
opcodeIndex uint32
|
||||
ptrIndex int
|
||||
indent uint32
|
||||
escapeKey bool
|
||||
structTypeToCodes map[uintptr]Opcodes
|
||||
recursiveCodes *Opcodes
|
||||
typ *runtime.Type
|
||||
opcodeIndex uint32
|
||||
ptrIndex int
|
||||
indent uint32
|
||||
escapeKey bool
|
||||
structTypeToCompiledCode map[uintptr]*CompiledCode
|
||||
|
||||
parent *compileContext
|
||||
}
|
||||
|
||||
func (c *compileContext) incIndent() {
|
||||
c.indent++
|
||||
func (c *compileContext) context() *compileContext {
|
||||
return &compileContext{
|
||||
typ: c.typ,
|
||||
opcodeIndex: c.opcodeIndex,
|
||||
ptrIndex: c.ptrIndex,
|
||||
indent: c.indent,
|
||||
escapeKey: c.escapeKey,
|
||||
structTypeToCompiledCode: c.structTypeToCompiledCode,
|
||||
parent: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compileContext) decIndent() {
|
||||
c.indent--
|
||||
func (c *compileContext) withType(typ *runtime.Type) *compileContext {
|
||||
ctx := c.context()
|
||||
ctx.typ = typ
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *compileContext) incIndent() *compileContext {
|
||||
ctx := c.context()
|
||||
ctx.indent++
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *compileContext) decIndent() *compileContext {
|
||||
ctx := c.context()
|
||||
ctx.indent--
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (c *compileContext) incIndex() {
|
||||
|
@ -37,18 +60,30 @@ func (c *compileContext) decIndex() {
|
|||
|
||||
func (c *compileContext) incOpcodeIndex() {
|
||||
c.opcodeIndex++
|
||||
if c.parent != nil {
|
||||
c.parent.incOpcodeIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compileContext) decOpcodeIndex() {
|
||||
c.opcodeIndex--
|
||||
if c.parent != nil {
|
||||
c.parent.decOpcodeIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compileContext) incPtrIndex() {
|
||||
c.ptrIndex++
|
||||
if c.parent != nil {
|
||||
c.parent.incPtrIndex()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compileContext) decPtrIndex() {
|
||||
c.ptrIndex--
|
||||
if c.parent != nil {
|
||||
c.parent.decPtrIndex()
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -69,7 +104,6 @@ var (
|
|||
)
|
||||
|
||||
type RuntimeContext struct {
|
||||
Context context.Context
|
||||
Buf []byte
|
||||
MarshalBuf []byte
|
||||
Ptrs []uintptr
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
package encoder
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
const (
|
||||
// The default lowest and highest continuation byte.
|
||||
locb = 128 //0b10000000
|
||||
hicb = 191 //0b10111111
|
||||
|
||||
// These names of these constants are chosen to give nice alignment in the
|
||||
// table below. The first nibble is an index into acceptRanges or F for
|
||||
// special one-byte cases. The second nibble is the Rune length or the
|
||||
// Status for the special one-byte case.
|
||||
xx = 0xF1 // invalid: size 1
|
||||
as = 0xF0 // ASCII: size 1
|
||||
s1 = 0x02 // accept 0, size 2
|
||||
s2 = 0x13 // accept 1, size 3
|
||||
s3 = 0x03 // accept 0, size 3
|
||||
s4 = 0x23 // accept 2, size 3
|
||||
s5 = 0x34 // accept 3, size 4
|
||||
s6 = 0x04 // accept 0, size 4
|
||||
s7 = 0x44 // accept 4, size 4
|
||||
)
|
||||
|
||||
// first is information about the first byte in a UTF-8 sequence.
|
||||
var first = [256]uint8{
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F
|
||||
as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF
|
||||
xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF
|
||||
xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF
|
||||
s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF
|
||||
s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF
|
||||
s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF
|
||||
}
|
||||
|
||||
const (
|
||||
lineSep = byte(168) //'\u2028'
|
||||
paragraphSep = byte(169) //'\u2029'
|
||||
)
|
||||
|
||||
type decodeRuneState int
|
||||
|
||||
const (
|
||||
validUTF8State decodeRuneState = iota
|
||||
runeErrorState
|
||||
lineSepState
|
||||
paragraphSepState
|
||||
)
|
||||
|
||||
func decodeRuneInString(s string) (decodeRuneState, int) {
|
||||
n := len(s)
|
||||
s0 := s[0]
|
||||
x := first[s0]
|
||||
if x >= as {
|
||||
// The following code simulates an additional check for x == xx and
|
||||
// handling the ASCII and invalid cases accordingly. This mask-and-or
|
||||
// approach prevents an additional branch.
|
||||
mask := rune(x) << 31 >> 31 // Create 0x0000 or 0xFFFF.
|
||||
if rune(s[0])&^mask|utf8.RuneError&mask == utf8.RuneError {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
return validUTF8State, 1
|
||||
}
|
||||
sz := int(x & 7)
|
||||
if n < sz {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
s1 := s[1]
|
||||
switch x >> 4 {
|
||||
case 0:
|
||||
if s1 < locb || hicb < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
case 1:
|
||||
if s1 < 0xA0 || hicb < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
case 2:
|
||||
if s1 < locb || 0x9F < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
case 3:
|
||||
if s1 < 0x90 || hicb < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
case 4:
|
||||
if s1 < locb || 0x8F < s1 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
}
|
||||
if sz <= 2 {
|
||||
return validUTF8State, 2
|
||||
}
|
||||
s2 := s[2]
|
||||
if s2 < locb || hicb < s2 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
if sz <= 3 {
|
||||
// separator character prefixes: [2]byte{226, 128}
|
||||
if s0 == 226 && s1 == 128 {
|
||||
switch s2 {
|
||||
case lineSep:
|
||||
return lineSepState, 3
|
||||
case paragraphSep:
|
||||
return paragraphSepState, 3
|
||||
}
|
||||
}
|
||||
return validUTF8State, 3
|
||||
}
|
||||
s3 := s[3]
|
||||
if s3 < locb || hicb < s3 {
|
||||
return runeErrorState, 1
|
||||
}
|
||||
return validUTF8State, 4
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -94,29 +94,10 @@ func (t OpType) IsMultipleOpField() bool {
|
|||
}
|
||||
|
||||
type OpcodeSet struct {
|
||||
Type *runtime.Type
|
||||
NoescapeKeyCode *Opcode
|
||||
EscapeKeyCode *Opcode
|
||||
InterfaceNoescapeKeyCode *Opcode
|
||||
InterfaceEscapeKeyCode *Opcode
|
||||
CodeLength int
|
||||
EndCode *Opcode
|
||||
Code Code
|
||||
QueryCache map[string]*OpcodeSet
|
||||
cacheMu sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *OpcodeSet) getQueryCache(hash string) *OpcodeSet {
|
||||
s.cacheMu.RLock()
|
||||
codeSet := s.QueryCache[hash]
|
||||
s.cacheMu.RUnlock()
|
||||
return codeSet
|
||||
}
|
||||
|
||||
func (s *OpcodeSet) setQueryCache(hash string, codeSet *OpcodeSet) {
|
||||
s.cacheMu.Lock()
|
||||
s.QueryCache[hash] = codeSet
|
||||
s.cacheMu.Unlock()
|
||||
Type *runtime.Type
|
||||
NoescapeKeyCode *Opcode
|
||||
EscapeKeyCode *Opcode
|
||||
CodeLength int
|
||||
}
|
||||
|
||||
type CompiledCode struct {
|
||||
|
@ -238,56 +219,33 @@ func (m *Mapslice) Swap(i, j int) {
|
|||
m.Items[i], m.Items[j] = m.Items[j], m.Items[i]
|
||||
}
|
||||
|
||||
//nolint:structcheck,unused
|
||||
type mapIter struct {
|
||||
key unsafe.Pointer
|
||||
elem unsafe.Pointer
|
||||
t unsafe.Pointer
|
||||
h unsafe.Pointer
|
||||
buckets unsafe.Pointer
|
||||
bptr unsafe.Pointer
|
||||
overflow unsafe.Pointer
|
||||
oldoverflow unsafe.Pointer
|
||||
startBucket uintptr
|
||||
offset uint8
|
||||
wrapped bool
|
||||
B uint8
|
||||
i uint8
|
||||
bucket uintptr
|
||||
checkBucket uintptr
|
||||
}
|
||||
|
||||
type MapContext struct {
|
||||
Start int
|
||||
First int
|
||||
Idx int
|
||||
Pos []int
|
||||
Slice *Mapslice
|
||||
Buf []byte
|
||||
Len int
|
||||
Iter mapIter
|
||||
}
|
||||
|
||||
var mapContextPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &MapContext{
|
||||
Slice: &Mapslice{},
|
||||
}
|
||||
return &MapContext{}
|
||||
},
|
||||
}
|
||||
|
||||
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 ctx.Slice == nil {
|
||||
ctx.Slice = &Mapslice{
|
||||
Items: make([]MapItem, 0, mapLen),
|
||||
}
|
||||
}
|
||||
if cap(ctx.Pos) < (mapLen*2 + 1) {
|
||||
ctx.Pos = make([]int, 0, mapLen*2+1)
|
||||
ctx.Slice.Items = make([]MapItem, 0, mapLen)
|
||||
} else {
|
||||
ctx.Pos = ctx.Pos[:0]
|
||||
ctx.Slice.Items = ctx.Slice.Items[:0]
|
||||
}
|
||||
ctx.Buf = ctx.Buf[:0]
|
||||
ctx.Iter = mapIter{}
|
||||
ctx.Idx = 0
|
||||
ctx.Len = mapLen
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
@ -295,17 +253,17 @@ func ReleaseMapContext(c *MapContext) {
|
|||
mapContextPool.Put(c)
|
||||
}
|
||||
|
||||
//go:linkname MapIterInit runtime.mapiterinit
|
||||
//go:linkname MapIterInit reflect.mapiterinit
|
||||
//go:noescape
|
||||
func MapIterInit(mapType *runtime.Type, m unsafe.Pointer, it *mapIter)
|
||||
func MapIterInit(mapType *runtime.Type, m unsafe.Pointer) unsafe.Pointer
|
||||
|
||||
//go:linkname MapIterKey reflect.mapiterkey
|
||||
//go:noescape
|
||||
func MapIterKey(it *mapIter) unsafe.Pointer
|
||||
func MapIterKey(it unsafe.Pointer) unsafe.Pointer
|
||||
|
||||
//go:linkname MapIterNext reflect.mapiternext
|
||||
//go:noescape
|
||||
func MapIterNext(it *mapIter)
|
||||
func MapIterNext(it unsafe.Pointer)
|
||||
|
||||
//go:linkname MapLen reflect.maplen
|
||||
//go:noescape
|
||||
|
@ -407,31 +365,13 @@ func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{
|
|||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
var bb []byte
|
||||
if (code.Flags & MarshalerContextFlags) != 0 {
|
||||
marshaler, ok := v.(marshalerContext)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
stdctx := ctx.Option.Context
|
||||
if ctx.Option.Flag&FieldQueryOption != 0 {
|
||||
stdctx = SetFieldQueryToContext(stdctx, code.FieldQuery)
|
||||
}
|
||||
b, err := marshaler.MarshalJSON(stdctx)
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
bb = b
|
||||
} else {
|
||||
marshaler, ok := v.(json.Marshaler)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
b, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
bb = b
|
||||
marshaler, ok := v.(json.Marshaler)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
bb, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
marshalBuf := ctx.MarshalBuf[:0]
|
||||
marshalBuf = append(append(marshalBuf, bb...), nul)
|
||||
|
@ -455,27 +395,13 @@ func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v inte
|
|||
}
|
||||
}
|
||||
v = rv.Interface()
|
||||
var bb []byte
|
||||
if (code.Flags & MarshalerContextFlags) != 0 {
|
||||
marshaler, ok := v.(marshalerContext)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
b, err := marshaler.MarshalJSON(ctx.Option.Context)
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
bb = b
|
||||
} else {
|
||||
marshaler, ok := v.(json.Marshaler)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
b, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
bb = b
|
||||
marshaler, ok := v.(json.Marshaler)
|
||||
if !ok {
|
||||
return AppendNull(ctx, b), nil
|
||||
}
|
||||
bb, err := marshaler.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
|
||||
}
|
||||
marshalBuf := ctx.MarshalBuf[:0]
|
||||
marshalBuf = append(append(marshalBuf, bb...), nul)
|
||||
|
@ -589,8 +515,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
|
||||
}
|
||||
|
|
|
@ -53,18 +53,7 @@ func numMask(numBitSize uint8) uint64 {
|
|||
return 1<<numBitSize - 1
|
||||
}
|
||||
|
||||
func AppendInt(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte {
|
||||
var u64 uint64
|
||||
switch code.NumBitSize {
|
||||
case 8:
|
||||
u64 = (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
u64 = (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
u64 = (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
u64 = **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
func AppendInt(_ *RuntimeContext, out []byte, u64 uint64, code *Opcode) []byte {
|
||||
mask := numMask(code.NumBitSize)
|
||||
n := u64 & mask
|
||||
negative := (u64>>(code.NumBitSize-1))&1 == 1
|
||||
|
@ -107,18 +96,7 @@ func AppendInt(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte {
|
|||
return append(out, b[i:]...)
|
||||
}
|
||||
|
||||
func AppendUint(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte {
|
||||
var u64 uint64
|
||||
switch code.NumBitSize {
|
||||
case 8:
|
||||
u64 = (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
u64 = (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
u64 = (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
u64 = **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
func AppendUint(_ *RuntimeContext, out []byte, u64 uint64, code *Opcode) []byte {
|
||||
mask := numMask(code.NumBitSize)
|
||||
n := u64 & mask
|
||||
if n < 10 {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//go:build !go1.13
|
||||
// +build !go1.13
|
||||
|
||||
package encoder
|
||||
|
@ -6,4 +5,4 @@ package encoder
|
|||
import "unsafe"
|
||||
|
||||
//go:linkname MapIterValue reflect.mapitervalue
|
||||
func MapIterValue(it *mapIter) unsafe.Pointer
|
||||
func MapIterValue(it unsafe.Pointer) unsafe.Pointer
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
package encoder
|
||||
|
@ -6,4 +5,4 @@ package encoder
|
|||
import "unsafe"
|
||||
|
||||
//go:linkname MapIterValue reflect.mapiterelem
|
||||
func MapIterValue(it *mapIter) unsafe.Pointer
|
||||
func MapIterValue(it unsafe.Pointer) unsafe.Pointer
|
||||
|
|
|
@ -10,19 +10,17 @@ import (
|
|||
|
||||
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||
|
||||
type OpFlags uint16
|
||||
type OpFlags uint8
|
||||
|
||||
const (
|
||||
AnonymousHeadFlags OpFlags = 1 << 0
|
||||
AnonymousKeyFlags OpFlags = 1 << 1
|
||||
IndirectFlags OpFlags = 1 << 2
|
||||
IsTaggedKeyFlags OpFlags = 1 << 3
|
||||
NilCheckFlags OpFlags = 1 << 4
|
||||
AddrForMarshalerFlags OpFlags = 1 << 5
|
||||
IsNextOpPtrTypeFlags OpFlags = 1 << 6
|
||||
IsNilableTypeFlags OpFlags = 1 << 7
|
||||
MarshalerContextFlags OpFlags = 1 << 8
|
||||
NonEmptyInterfaceFlags OpFlags = 1 << 9
|
||||
AnonymousHeadFlags OpFlags = 1 << 0
|
||||
AnonymousKeyFlags OpFlags = 1 << 1
|
||||
IndirectFlags OpFlags = 1 << 2
|
||||
IsTaggedKeyFlags OpFlags = 1 << 3
|
||||
NilCheckFlags OpFlags = 1 << 4
|
||||
AddrForMarshalerFlags OpFlags = 1 << 5
|
||||
IsNextOpPtrTypeFlags OpFlags = 1 << 6
|
||||
IsNilableTypeFlags OpFlags = 1 << 7
|
||||
)
|
||||
|
||||
type Opcode struct {
|
||||
|
@ -34,62 +32,31 @@ type Opcode struct {
|
|||
Key string // struct field key
|
||||
Offset uint32 // offset size from struct header
|
||||
PtrNum uint8 // pointer number: e.g. double pointer is 2.
|
||||
NumBitSize uint8
|
||||
Flags OpFlags
|
||||
NumBitSize uint8
|
||||
_ [1]uint8 // 1
|
||||
|
||||
Type *runtime.Type // go type
|
||||
PrevField *Opcode // prev struct field
|
||||
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
|
||||
ElemIdx uint32 // offset to access array/slice/map elem
|
||||
Length uint32 // offset to access slice/map length or array length
|
||||
MapIter uint32 // offset to access map iterator
|
||||
MapPos uint32 // offset to access position list for sorted map
|
||||
Indent uint32 // indent number
|
||||
Size uint32 // array/slice elem size
|
||||
DisplayIdx uint32 // opcode index
|
||||
DisplayKey string // key text to display
|
||||
}
|
||||
|
||||
func (c *Opcode) Validate() error {
|
||||
var prevIdx uint32
|
||||
for code := c; !code.IsEnd(); {
|
||||
if prevIdx != 0 {
|
||||
if code.DisplayIdx != prevIdx+1 {
|
||||
return fmt.Errorf(
|
||||
"invalid index. previous display index is %d but next is %d. dump = %s",
|
||||
prevIdx, code.DisplayIdx, c.Dump(),
|
||||
)
|
||||
}
|
||||
}
|
||||
prevIdx = code.DisplayIdx
|
||||
code = code.IterNext()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Opcode) IterNext() *Opcode {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
switch c.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
return c.End
|
||||
default:
|
||||
return c.Next
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Opcode) IsEnd() bool {
|
||||
if c == nil {
|
||||
return true
|
||||
}
|
||||
return c.Op == OpEnd || c.Op == OpInterfaceEnd || c.Op == OpRecursiveEnd
|
||||
}
|
||||
|
||||
func (c *Opcode) MaxIdx() uint32 {
|
||||
max := uint32(0)
|
||||
for _, value := range []uint32{
|
||||
c.Idx,
|
||||
c.ElemIdx,
|
||||
c.Length,
|
||||
c.MapIter,
|
||||
c.MapPos,
|
||||
c.Size,
|
||||
} {
|
||||
if max < value {
|
||||
|
@ -305,109 +272,89 @@ func (c *Opcode) ToFieldType(isString bool) OpType {
|
|||
return OpStructField
|
||||
}
|
||||
|
||||
func newOpCode(ctx *compileContext, typ *runtime.Type, op OpType) *Opcode {
|
||||
return newOpCodeWithNext(ctx, typ, op, newEndOp(ctx, typ))
|
||||
func newOpCode(ctx *compileContext, op OpType) *Opcode {
|
||||
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
|
||||
}
|
||||
|
||||
func opcodeOffset(idx int) uint32 {
|
||||
return uint32(idx) * uintptrSize
|
||||
}
|
||||
|
||||
func getCodeAddrByIdx(head *Opcode, idx uint32) *Opcode {
|
||||
addr := uintptr(unsafe.Pointer(head)) + uintptr(idx)*unsafe.Sizeof(Opcode{})
|
||||
return *(**Opcode)(unsafe.Pointer(&addr))
|
||||
}
|
||||
|
||||
func copyOpcode(code *Opcode) *Opcode {
|
||||
codeNum := ToEndCode(code).DisplayIdx + 1
|
||||
codeSlice := make([]Opcode, codeNum)
|
||||
head := (*Opcode)((*runtime.SliceHeader)(unsafe.Pointer(&codeSlice)).Data)
|
||||
ptr := head
|
||||
c := code
|
||||
for {
|
||||
*ptr = Opcode{
|
||||
Op: c.Op,
|
||||
Key: c.Key,
|
||||
PtrNum: c.PtrNum,
|
||||
NumBitSize: c.NumBitSize,
|
||||
Flags: c.Flags,
|
||||
Idx: c.Idx,
|
||||
Offset: c.Offset,
|
||||
Type: c.Type,
|
||||
FieldQuery: c.FieldQuery,
|
||||
DisplayIdx: c.DisplayIdx,
|
||||
DisplayKey: c.DisplayKey,
|
||||
ElemIdx: c.ElemIdx,
|
||||
Length: c.Length,
|
||||
Size: c.Size,
|
||||
Indent: c.Indent,
|
||||
Jmp: c.Jmp,
|
||||
}
|
||||
if c.End != nil {
|
||||
ptr.End = getCodeAddrByIdx(head, c.End.DisplayIdx)
|
||||
}
|
||||
if c.NextField != nil {
|
||||
ptr.NextField = getCodeAddrByIdx(head, c.NextField.DisplayIdx)
|
||||
}
|
||||
if c.Next != nil {
|
||||
ptr.Next = getCodeAddrByIdx(head, c.Next.DisplayIdx)
|
||||
}
|
||||
if c.IsEnd() {
|
||||
break
|
||||
}
|
||||
ptr = getCodeAddrByIdx(head, c.DisplayIdx+1)
|
||||
c = c.IterNext()
|
||||
}
|
||||
return head
|
||||
codeMap := map[uintptr]*Opcode{}
|
||||
return code.copy(codeMap)
|
||||
}
|
||||
|
||||
func setTotalLengthToInterfaceOp(code *Opcode) {
|
||||
for c := code; !c.IsEnd(); {
|
||||
if c.Op == OpInterface || c.Op == OpInterfacePtr {
|
||||
c.Length = uint32(code.TotalLength())
|
||||
}
|
||||
c = c.IterNext()
|
||||
}
|
||||
}
|
||||
|
||||
func ToEndCode(code *Opcode) *Opcode {
|
||||
c := code
|
||||
for !c.IsEnd() {
|
||||
c = c.IterNext()
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func copyToInterfaceOpcode(code *Opcode) *Opcode {
|
||||
copied := copyOpcode(code)
|
||||
c := copied
|
||||
c = ToEndCode(c)
|
||||
c.Idx += uintptrSize
|
||||
c.ElemIdx = c.Idx + uintptrSize
|
||||
c.Length = c.Idx + 2*uintptrSize
|
||||
c.Op = OpInterfaceEnd
|
||||
return copied
|
||||
}
|
||||
|
||||
func newOpCodeWithNext(ctx *compileContext, typ *runtime.Type, op OpType, next *Opcode) *Opcode {
|
||||
func newOpCodeWithNext(ctx *compileContext, op OpType, next *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: op,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
Next: next,
|
||||
Type: typ,
|
||||
Type: ctx.typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newEndOp(ctx *compileContext, typ *runtime.Type) *Opcode {
|
||||
return newOpCodeWithNext(ctx, typ, OpEnd, nil)
|
||||
func newEndOp(ctx *compileContext) *Opcode {
|
||||
return newOpCodeWithNext(ctx, OpEnd, nil)
|
||||
}
|
||||
|
||||
func (c *Opcode) copy(codeMap map[uintptr]*Opcode) *Opcode {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
addr := uintptr(unsafe.Pointer(c))
|
||||
if code, exists := codeMap[addr]; exists {
|
||||
return code
|
||||
}
|
||||
copied := &Opcode{
|
||||
Op: c.Op,
|
||||
Key: c.Key,
|
||||
PtrNum: c.PtrNum,
|
||||
NumBitSize: c.NumBitSize,
|
||||
Flags: c.Flags,
|
||||
Idx: c.Idx,
|
||||
Offset: c.Offset,
|
||||
Type: c.Type,
|
||||
DisplayIdx: c.DisplayIdx,
|
||||
DisplayKey: c.DisplayKey,
|
||||
ElemIdx: c.ElemIdx,
|
||||
Length: c.Length,
|
||||
MapIter: c.MapIter,
|
||||
MapPos: c.MapPos,
|
||||
Size: c.Size,
|
||||
Indent: c.Indent,
|
||||
}
|
||||
codeMap[addr] = copied
|
||||
copied.End = c.End.copy(codeMap)
|
||||
copied.PrevField = c.PrevField.copy(codeMap)
|
||||
copied.NextField = c.NextField.copy(codeMap)
|
||||
copied.Next = c.Next.copy(codeMap)
|
||||
copied.Jmp = c.Jmp
|
||||
return copied
|
||||
}
|
||||
|
||||
func (c *Opcode) BeforeLastCode() *Opcode {
|
||||
code := c
|
||||
for {
|
||||
var nextCode *Opcode
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
nextCode = code.End
|
||||
default:
|
||||
nextCode = code.Next
|
||||
}
|
||||
if nextCode.Op == OpEnd {
|
||||
return code
|
||||
}
|
||||
code = nextCode
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Opcode) TotalLength() int {
|
||||
var idx int
|
||||
code := c
|
||||
for !code.IsEnd() {
|
||||
for code := c; code.Op != OpEnd; {
|
||||
maxIdx := int(code.MaxIdx() / uintptrSize)
|
||||
if idx < maxIdx {
|
||||
idx = maxIdx
|
||||
|
@ -415,15 +362,52 @@ func (c *Opcode) TotalLength() int {
|
|||
if code.Op == OpRecursiveEnd {
|
||||
break
|
||||
}
|
||||
code = code.IterNext()
|
||||
}
|
||||
maxIdx := int(code.MaxIdx() / uintptrSize)
|
||||
if idx < maxIdx {
|
||||
idx = maxIdx
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
code = code.End
|
||||
default:
|
||||
code = code.Next
|
||||
}
|
||||
}
|
||||
return idx + 1
|
||||
}
|
||||
|
||||
func (c *Opcode) decOpcodeIndex() {
|
||||
for code := c; code.Op != OpEnd; {
|
||||
code.DisplayIdx--
|
||||
if code.Idx > 0 {
|
||||
code.Idx -= uintptrSize
|
||||
}
|
||||
if code.ElemIdx > 0 {
|
||||
code.ElemIdx -= uintptrSize
|
||||
}
|
||||
if code.MapIter > 0 {
|
||||
code.MapIter -= uintptrSize
|
||||
}
|
||||
if code.Length > 0 && code.Op.CodeType() != CodeArrayHead && code.Op.CodeType() != CodeArrayElem {
|
||||
code.Length -= uintptrSize
|
||||
}
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
code = code.End
|
||||
default:
|
||||
code = code.Next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Opcode) decIndent() {
|
||||
for code := c; code.Op != OpEnd; {
|
||||
code.Indent--
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
code = code.End
|
||||
default:
|
||||
code = code.Next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpHead(code *Opcode) string {
|
||||
var length uint32
|
||||
if code.Op.CodeType() == CodeArrayHead {
|
||||
|
@ -432,7 +416,7 @@ func (c *Opcode) dumpHead(code *Opcode) string {
|
|||
length = code.Length / uintptrSize
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
|
||||
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
|
@ -444,21 +428,26 @@ func (c *Opcode) dumpHead(code *Opcode) string {
|
|||
|
||||
func (c *Opcode) dumpMapHead(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d])`,
|
||||
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.ElemIdx/uintptrSize,
|
||||
code.Length/uintptrSize,
|
||||
code.MapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpMapEnd(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d])`,
|
||||
`[%d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.MapPos/uintptrSize,
|
||||
code.Length/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -470,7 +459,7 @@ func (c *Opcode) dumpElem(code *Opcode) string {
|
|||
length = code.Length / uintptrSize
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
|
||||
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
|
@ -483,7 +472,7 @@ func (c *Opcode) dumpElem(code *Opcode) string {
|
|||
|
||||
func (c *Opcode) dumpField(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d][key:%s][offset:%d])`,
|
||||
`[%d]%s%s ([idx:%d][key:%s][offset:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
|
@ -495,27 +484,31 @@ func (c *Opcode) dumpField(code *Opcode) string {
|
|||
|
||||
func (c *Opcode) dumpKey(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d])`,
|
||||
`[%d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.ElemIdx/uintptrSize,
|
||||
code.Length/uintptrSize,
|
||||
code.MapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) dumpValue(code *Opcode) string {
|
||||
return fmt.Sprintf(
|
||||
`[%03d]%s%s ([idx:%d])`,
|
||||
`[%d]%s%s ([idx:%d][mapIter:%d])`,
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
code.Idx/uintptrSize,
|
||||
code.MapIter/uintptrSize,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Opcode) Dump() string {
|
||||
codes := []string{}
|
||||
for code := c; !code.IsEnd(); {
|
||||
for code := c; code.Op != OpEnd; {
|
||||
switch code.Op.CodeType() {
|
||||
case CodeSliceHead:
|
||||
codes = append(codes, c.dumpHead(code))
|
||||
|
@ -543,7 +536,7 @@ func (c *Opcode) Dump() string {
|
|||
code = code.Next
|
||||
default:
|
||||
codes = append(codes, fmt.Sprintf(
|
||||
"[%03d]%s%s ([idx:%d])",
|
||||
"[%d]%s%s ([idx:%d])",
|
||||
code.DisplayIdx,
|
||||
strings.Repeat("-", int(code.Indent)),
|
||||
code.Op,
|
||||
|
@ -555,7 +548,44 @@ func (c *Opcode) Dump() string {
|
|||
return strings.Join(codes, "\n")
|
||||
}
|
||||
|
||||
func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
|
||||
func prevField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
|
||||
if _, exists := removedFields[code]; exists {
|
||||
return prevField(code.PrevField, removedFields)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func nextField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
|
||||
if _, exists := removedFields[code]; exists {
|
||||
return nextField(code.NextField, removedFields)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func linkPrevToNextField(cur *Opcode, removedFields map[*Opcode]struct{}) {
|
||||
prev := prevField(cur.PrevField, removedFields)
|
||||
prev.NextField = nextField(cur.NextField, removedFields)
|
||||
code := prev
|
||||
fcode := cur
|
||||
for {
|
||||
var nextCode *Opcode
|
||||
switch code.Op.CodeType() {
|
||||
case CodeArrayElem, CodeSliceElem, CodeMapKey:
|
||||
nextCode = code.End
|
||||
default:
|
||||
nextCode = code.Next
|
||||
}
|
||||
if nextCode == fcode {
|
||||
code.Next = fcode.Next
|
||||
break
|
||||
} else if nextCode.Op == OpEnd {
|
||||
break
|
||||
}
|
||||
code = nextCode
|
||||
}
|
||||
}
|
||||
|
||||
func newSliceHeaderCode(ctx *compileContext) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
|
@ -563,7 +593,6 @@ func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
|
|||
length := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpSlice,
|
||||
Type: typ,
|
||||
Idx: idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: elemIdx,
|
||||
|
@ -572,10 +601,9 @@ func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
|
|||
}
|
||||
}
|
||||
|
||||
func newSliceElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, size uintptr) *Opcode {
|
||||
func newSliceElemCode(ctx *compileContext, head *Opcode, size uintptr) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpSliceElem,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: head.ElemIdx,
|
||||
|
@ -585,13 +613,12 @@ func newSliceElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, size
|
|||
}
|
||||
}
|
||||
|
||||
func newArrayHeaderCode(ctx *compileContext, typ *runtime.Type, alen int) *Opcode {
|
||||
func newArrayHeaderCode(ctx *compileContext, alen int) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpArray,
|
||||
Type: typ,
|
||||
Idx: idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: elemIdx,
|
||||
|
@ -600,10 +627,9 @@ func newArrayHeaderCode(ctx *compileContext, typ *runtime.Type, alen int) *Opcod
|
|||
}
|
||||
}
|
||||
|
||||
func newArrayElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, length int, size uintptr) *Opcode {
|
||||
func newArrayElemCode(ctx *compileContext, head *Opcode, length int, size uintptr) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpArrayElem,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: head.ElemIdx,
|
||||
|
@ -613,55 +639,82 @@ func newArrayElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, leng
|
|||
}
|
||||
}
|
||||
|
||||
func newMapHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
|
||||
func newMapHeaderCode(ctx *compileContext) *Opcode {
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
elemIdx := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
length := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
mapIter := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpMap,
|
||||
Type: typ,
|
||||
Idx: idx,
|
||||
Type: ctx.typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: elemIdx,
|
||||
Length: length,
|
||||
MapIter: mapIter,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapKeyCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
|
||||
func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpMapKey,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: head.ElemIdx,
|
||||
Length: head.Length,
|
||||
MapIter: head.MapIter,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapValueCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
|
||||
func newMapValueCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpMapValue,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
ElemIdx: head.ElemIdx,
|
||||
Length: head.Length,
|
||||
MapIter: head.MapIter,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newMapEndCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
|
||||
func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
|
||||
mapPos := opcodeOffset(ctx.ptrIndex)
|
||||
ctx.incPtrIndex()
|
||||
idx := opcodeOffset(ctx.ptrIndex)
|
||||
return &Opcode{
|
||||
Op: OpMapEnd,
|
||||
Type: typ,
|
||||
Idx: head.Idx,
|
||||
Idx: idx,
|
||||
Next: newEndOp(ctx),
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Length: head.Length,
|
||||
MapPos: mapPos,
|
||||
Indent: ctx.indent,
|
||||
Next: newEndOp(ctx, typ),
|
||||
}
|
||||
}
|
||||
|
||||
func newRecursiveCode(ctx *compileContext, typ *runtime.Type, jmp *CompiledCode) *Opcode {
|
||||
func newInterfaceCode(ctx *compileContext) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpInterface,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
Next: newEndOp(ctx),
|
||||
Type: ctx.typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
}
|
||||
}
|
||||
|
||||
func newRecursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
|
||||
return &Opcode{
|
||||
Op: OpRecursive,
|
||||
Type: typ,
|
||||
Idx: opcodeOffset(ctx.ptrIndex),
|
||||
Next: newEndOp(ctx, typ),
|
||||
Next: newEndOp(ctx),
|
||||
Type: ctx.typ,
|
||||
DisplayIdx: ctx.opcodeIndex,
|
||||
Indent: ctx.indent,
|
||||
Jmp: jmp,
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
type OptionFlag uint8
|
||||
|
||||
const (
|
||||
|
@ -13,16 +8,11 @@ const (
|
|||
UnorderedMapOption
|
||||
DebugOption
|
||||
ColorizeOption
|
||||
ContextOption
|
||||
NormalizeUTF8Option
|
||||
FieldQueryOption
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
Flag OptionFlag
|
||||
ColorScheme *ColorScheme
|
||||
Context context.Context
|
||||
DebugOut io.Writer
|
||||
}
|
||||
|
||||
type EncodeFormat struct {
|
||||
|
|
|
@ -36,7 +36,7 @@ var opTypeStrings = [400]string{
|
|||
"Recursive",
|
||||
"RecursivePtr",
|
||||
"RecursiveEnd",
|
||||
"InterfaceEnd",
|
||||
"StructAnonymousEnd",
|
||||
"Int",
|
||||
"Uint",
|
||||
"Float32",
|
||||
|
@ -441,7 +441,7 @@ const (
|
|||
OpRecursive OpType = 10
|
||||
OpRecursivePtr OpType = 11
|
||||
OpRecursiveEnd OpType = 12
|
||||
OpInterfaceEnd OpType = 13
|
||||
OpStructAnonymousEnd OpType = 13
|
||||
OpInt OpType = 14
|
||||
OpUint OpType = 15
|
||||
OpFloat32 OpType = 16
|
||||
|
@ -894,7 +894,7 @@ func (t OpType) HeadToOmitEmptyHead() OpType {
|
|||
}
|
||||
|
||||
func (t OpType) PtrHeadToHead() OpType {
|
||||
idx := strings.Index(t.String(), "PtrHead")
|
||||
idx := strings.Index(t.String(), "Ptr")
|
||||
if idx == -1 {
|
||||
return t
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -3,6 +3,7 @@ package encoder
|
|||
import (
|
||||
"math/bits"
|
||||
"reflect"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -11,8 +12,390 @@ 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"
|
||||
|
||||
// escapeIndex finds the index of the first char in `s` that requires escaping.
|
||||
// A char requires escaping if it's outside of the range of [0x20, 0x7F] or if
|
||||
// it includes a double quote or backslash.
|
||||
// If no chars in `s` require escaping, the return value is -1.
|
||||
func escapeIndex(s string) int {
|
||||
chunks := stringToUint64Slice(s)
|
||||
for _, n := range chunks {
|
||||
// combine masks before checking for the MSB of each byte. We include
|
||||
// `n` in the mask to check whether any of the *input* byte MSBs were
|
||||
// set (i.e. the byte was outside the ASCII range).
|
||||
mask := n | below(n, 0x20) | contains(n, '"') | contains(n, '\\')
|
||||
if (mask & msb) != 0 {
|
||||
return bits.TrailingZeros64(mask&msb) / 8
|
||||
}
|
||||
}
|
||||
|
||||
valLen := len(s)
|
||||
for i := len(chunks) * 8; i < valLen; i++ {
|
||||
if needEscape[s[i]] {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// below return a mask that can be used to determine if any of the bytes
|
||||
// in `n` are below `b`. If a byte's MSB is set in the mask then that byte was
|
||||
// below `b`. The result is only valid if `b`, and each byte in `n`, is below
|
||||
// 0x80.
|
||||
func below(n uint64, b byte) uint64 {
|
||||
return n - expand(b)
|
||||
}
|
||||
|
||||
// contains returns a mask that can be used to determine if any of the
|
||||
// bytes in `n` are equal to `b`. If a byte's MSB is set in the mask then
|
||||
// that byte is equal to `b`. The result is only valid if `b`, and each
|
||||
// byte in `n`, is below 0x80.
|
||||
func contains(n uint64, b byte) uint64 {
|
||||
return (n ^ expand(b)) - lsb
|
||||
}
|
||||
|
||||
// expand puts the specified byte into each of the 8 bytes of a uint64.
|
||||
func expand(b byte) uint64 {
|
||||
return lsb * uint64(b)
|
||||
}
|
||||
|
||||
//nolint:govet
|
||||
func stringToUint64Slice(s string) []uint64 {
|
||||
return *(*[]uint64)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
|
@ -23,19 +406,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 +435,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 +447,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
|
||||
|
@ -116,9 +489,10 @@ ESCAPE_END:
|
|||
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
|
||||
// This encodes bytes < 0x20 except for \t, \n and \r.
|
||||
if c < 0x20 {
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
|
@ -126,14 +500,19 @@ ESCAPE_END:
|
|||
j = j + 1
|
||||
continue
|
||||
}
|
||||
state, size := decodeRuneInString(s[j:])
|
||||
switch state {
|
||||
case runeErrorState:
|
||||
|
||||
r, size := utf8.DecodeRuneInString(s[j:])
|
||||
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\ufffd`...)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
i = j + size
|
||||
j = j + size
|
||||
continue
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '\u2028', '\u2029':
|
||||
// U+2028 is LINE SEPARATOR.
|
||||
// U+2029 is PARAGRAPH SEPARATOR.
|
||||
// They are both technically valid characters in JSON strings,
|
||||
|
@ -141,231 +520,14 @@ ESCAPE_END:
|
|||
// 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++
|
||||
buf = append(buf, `\u202`...)
|
||||
buf = append(buf, hex[r&0xF])
|
||||
i = j + size
|
||||
j = j + size
|
||||
continue
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\\', '"':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', c)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\n':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'n')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\r':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'r')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\t':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 't')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '<', '>', '&':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
return append(append(buf, s[i:]...), '"')
|
||||
}
|
||||
|
||||
func appendNormalizedString(buf []byte, s string) []byte {
|
||||
valLen := len(s)
|
||||
if valLen == 0 {
|
||||
return append(buf, `""`...)
|
||||
}
|
||||
buf = append(buf, '"')
|
||||
var (
|
||||
i, j int
|
||||
)
|
||||
if valLen >= 8 {
|
||||
chunks := stringToUint64Slice(s)
|
||||
for _, n := range chunks {
|
||||
// combine masks before checking for the MSB of each byte. We include
|
||||
// `n` in the mask to check whether any of the *input* byte MSBs were
|
||||
// set (i.e. the byte was outside the ASCII range).
|
||||
mask := n | (n - (lsb * 0x20)) |
|
||||
((n ^ (lsb * '"')) - lsb) |
|
||||
((n ^ (lsb * '\\')) - lsb)
|
||||
if (mask & msb) != 0 {
|
||||
j = bits.TrailingZeros64(mask&msb) / 8
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
valLen := len(s)
|
||||
for i := len(chunks) * 8; i < valLen; i++ {
|
||||
if needEscapeNormalizeUTF8[s[i]] {
|
||||
j = i
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
return append(append(buf, s...), '"')
|
||||
}
|
||||
ESCAPE_END:
|
||||
for j < valLen {
|
||||
c := s[j]
|
||||
|
||||
if !needEscapeNormalizeUTF8[c] {
|
||||
// fast path: most of the time, printable ascii characters are used
|
||||
j++
|
||||
continue
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\\', '"':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', c)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\n':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'n')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\r':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 'r')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case '\t':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, '\\', 't')
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
|
||||
case 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x0F, // 0x00-0x0F
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F: // 0x10-0x1F
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
|
||||
state, size := decodeRuneInString(s[j:])
|
||||
switch state {
|
||||
case runeErrorState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\ufffd`...)
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
// U+2028 is LINE SEPARATOR.
|
||||
// U+2029 is PARAGRAPH SEPARATOR.
|
||||
// They are both technically valid characters in JSON strings,
|
||||
// but don't work in JSONP, which has to be evaluated as JavaScript,
|
||||
// and can lead to security holes there. It is valid JSON to
|
||||
// escape them, so we do so unconditionally.
|
||||
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
|
||||
case lineSepState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u2028`...)
|
||||
i = j + 3
|
||||
j = j + 3
|
||||
continue
|
||||
case paragraphSepState:
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u2029`...)
|
||||
i = j + 3
|
||||
j = j + 3
|
||||
continue
|
||||
}
|
||||
j += size
|
||||
}
|
||||
|
||||
|
@ -378,37 +540,19 @@ func appendString(buf []byte, s string) []byte {
|
|||
return append(buf, `""`...)
|
||||
}
|
||||
buf = append(buf, '"')
|
||||
var (
|
||||
i, j int
|
||||
)
|
||||
var escapeIdx 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
|
||||
}
|
||||
if escapeIdx = escapeIndex(s); escapeIdx < 0 {
|
||||
return append(append(buf, s...), '"')
|
||||
}
|
||||
valLen := len(s)
|
||||
for i := len(chunks) * 8; i < valLen; i++ {
|
||||
if needEscape[s[i]] {
|
||||
j = i
|
||||
goto ESCAPE_END
|
||||
}
|
||||
}
|
||||
return append(append(buf, s...), '"')
|
||||
}
|
||||
ESCAPE_END:
|
||||
|
||||
i := 0
|
||||
j := escapeIdx
|
||||
for j < valLen {
|
||||
c := s[j]
|
||||
|
||||
if !needEscape[c] {
|
||||
if c >= 0x20 && c <= 0x7f && c != '\\' && c != '"' {
|
||||
// fast path: most of the time, printable ascii characters are used
|
||||
j++
|
||||
continue
|
||||
|
@ -443,8 +587,7 @@ 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
|
||||
case '<', '>', '&':
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
|
@ -452,7 +595,45 @@ ESCAPE_END:
|
|||
j = j + 1
|
||||
continue
|
||||
}
|
||||
j++
|
||||
|
||||
// This encodes bytes < 0x20 except for \t, \n and \r.
|
||||
if c < 0x20 {
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u00`...)
|
||||
buf = append(buf, hex[c>>4], hex[c&0xF])
|
||||
i = j + 1
|
||||
j = j + 1
|
||||
continue
|
||||
}
|
||||
|
||||
r, size := utf8.DecodeRuneInString(s[j:])
|
||||
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\ufffd`...)
|
||||
i = j + size
|
||||
j = j + size
|
||||
continue
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '\u2028', '\u2029':
|
||||
// U+2028 is LINE SEPARATOR.
|
||||
// U+2029 is PARAGRAPH SEPARATOR.
|
||||
// They are both technically valid characters in JSON strings,
|
||||
// but don't work in JSONP, which has to be evaluated as JavaScript,
|
||||
// and can lead to security holes there. It is valid JSON to
|
||||
// escape them, so we do so unconditionally.
|
||||
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
|
||||
buf = append(buf, s[i:j]...)
|
||||
buf = append(buf, `\u202`...)
|
||||
buf = append(buf, hex[r&0xF])
|
||||
i = j + size
|
||||
j = j + size
|
||||
continue
|
||||
}
|
||||
|
||||
j += size
|
||||
}
|
||||
|
||||
return append(append(buf, s[i:]...), '"')
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -33,15 +33,6 @@ type emptyInterface struct {
|
|||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type nonEmptyInterface struct {
|
||||
itab *struct {
|
||||
ityp *runtime.Type // static interface type
|
||||
typ *runtime.Type // dynamic concrete type
|
||||
// unused fields...
|
||||
}
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func errUnimplementedOp(op encoder.OpType) error {
|
||||
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
|
||||
}
|
||||
|
@ -68,19 +59,7 @@ func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
|
|||
return p
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr, bitSize uint8) uint64 {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
return (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
return (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
return (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
return **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
|
@ -126,10 +105,6 @@ func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
|||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendNullComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, "null,"...)
|
||||
}
|
||||
|
||||
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = ':'
|
||||
|
@ -148,6 +123,39 @@ func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte
|
|||
return b
|
||||
}
|
||||
|
||||
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, _ *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
|
||||
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
|
||||
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalLength := uintptr(codeSet.CodeLength)
|
||||
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
|
||||
|
||||
curlen := uintptr(len(ctx.Ptrs))
|
||||
offsetNum := ptrOffset / uintptrSize
|
||||
|
||||
newLen := offsetNum + totalLength + nextTotalLength
|
||||
if curlen < newLen {
|
||||
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
|
||||
}
|
||||
oldPtrs := ctx.Ptrs
|
||||
|
||||
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
|
||||
newPtrs[0] = uintptr(iface.ptr)
|
||||
|
||||
ctx.Ptrs = newPtrs
|
||||
|
||||
bb, err := Run(ctx, b, ifaceCodeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.Ptrs = oldPtrs
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
return encoder.AppendMarshalJSON(ctx, code, b, v)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -26,15 +26,6 @@ type emptyInterface struct {
|
|||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type nonEmptyInterface struct {
|
||||
itab *struct {
|
||||
ityp *runtime.Type // static interface type
|
||||
typ *runtime.Type // dynamic concrete type
|
||||
// unused fields...
|
||||
}
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func errUnimplementedOp(op encoder.OpType) error {
|
||||
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
|
||||
}
|
||||
|
@ -61,19 +52,7 @@ func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
|
|||
return p
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr, bitSize uint8) uint64 {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
return (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
return (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
return (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
return **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
|
@ -104,17 +83,17 @@ func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
|
|||
}))
|
||||
}
|
||||
|
||||
func appendInt(ctx *encoder.RuntimeContext, b []byte, p uintptr, code *encoder.Opcode) []byte {
|
||||
func appendInt(ctx *encoder.RuntimeContext, b []byte, v uint64, code *encoder.Opcode) []byte {
|
||||
format := ctx.Option.ColorScheme.Int
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendInt(ctx, b, p, code)
|
||||
b = encoder.AppendInt(ctx, b, v, code)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendUint(ctx *encoder.RuntimeContext, b []byte, p uintptr, code *encoder.Opcode) []byte {
|
||||
func appendUint(ctx *encoder.RuntimeContext, b []byte, v uint64, code *encoder.Opcode) []byte {
|
||||
format := ctx.Option.ColorScheme.Uint
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendUint(ctx, b, p, code)
|
||||
b = encoder.AppendUint(ctx, b, v, code)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
|
@ -178,13 +157,6 @@ func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
|||
return append(b, ',')
|
||||
}
|
||||
|
||||
func appendNullComma(ctx *encoder.RuntimeContext, b []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.Null
|
||||
b = append(b, format.Header...)
|
||||
b = append(b, "null"...)
|
||||
return append(append(b, format.Footer...), ',')
|
||||
}
|
||||
|
||||
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
last := len(b) - 1
|
||||
b[last] = ':'
|
||||
|
@ -204,6 +176,39 @@ func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte
|
|||
return b
|
||||
}
|
||||
|
||||
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, _ *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
|
||||
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
|
||||
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalLength := uintptr(codeSet.CodeLength)
|
||||
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
|
||||
|
||||
curlen := uintptr(len(ctx.Ptrs))
|
||||
offsetNum := ptrOffset / uintptrSize
|
||||
|
||||
newLen := offsetNum + totalLength + nextTotalLength
|
||||
if curlen < newLen {
|
||||
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
|
||||
}
|
||||
oldPtrs := ctx.Ptrs
|
||||
|
||||
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
|
||||
newPtrs[0] = uintptr(iface.ptr)
|
||||
|
||||
ctx.Ptrs = newPtrs
|
||||
|
||||
bb, err := Run(ctx, b, ifaceCodeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.Ptrs = oldPtrs
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) {
|
||||
return encoder.AppendMarshalJSON(ctx, code, b, v)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -28,15 +28,6 @@ type emptyInterface struct {
|
|||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type nonEmptyInterface struct {
|
||||
itab *struct {
|
||||
ityp *runtime.Type // static interface type
|
||||
typ *runtime.Type // dynamic concrete type
|
||||
// unused fields...
|
||||
}
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func errUnimplementedOp(op encoder.OpType) error {
|
||||
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
|
||||
}
|
||||
|
@ -63,20 +54,7 @@ func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
|
|||
return p
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr, bitSize uint8) uint64 {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
return (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
return (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
return (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
return **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
|
@ -107,17 +85,17 @@ func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} {
|
|||
}))
|
||||
}
|
||||
|
||||
func appendInt(ctx *encoder.RuntimeContext, b []byte, p uintptr, code *encoder.Opcode) []byte {
|
||||
func appendInt(ctx *encoder.RuntimeContext, b []byte, v uint64, code *encoder.Opcode) []byte {
|
||||
format := ctx.Option.ColorScheme.Int
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendInt(ctx, b, p, code)
|
||||
b = encoder.AppendInt(ctx, b, v, code)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
func appendUint(ctx *encoder.RuntimeContext, b []byte, p uintptr, code *encoder.Opcode) []byte {
|
||||
func appendUint(ctx *encoder.RuntimeContext, b []byte, v uint64, code *encoder.Opcode) []byte {
|
||||
format := ctx.Option.ColorScheme.Uint
|
||||
b = append(b, format.Header...)
|
||||
b = encoder.AppendUint(ctx, b, p, code)
|
||||
b = encoder.AppendUint(ctx, b, v, code)
|
||||
return append(b, format.Footer...)
|
||||
}
|
||||
|
||||
|
@ -181,17 +159,47 @@ func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
|||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func appendNullComma(ctx *encoder.RuntimeContext, b []byte) []byte {
|
||||
format := ctx.Option.ColorScheme.Null
|
||||
b = append(b, format.Header...)
|
||||
b = append(b, "null"...)
|
||||
return append(append(b, format.Footer...), ',', '\n')
|
||||
}
|
||||
|
||||
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, ':', ' ')
|
||||
}
|
||||
|
||||
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
|
||||
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
|
||||
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalLength := uintptr(codeSet.CodeLength)
|
||||
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
|
||||
|
||||
curlen := uintptr(len(ctx.Ptrs))
|
||||
offsetNum := ptrOffset / uintptrSize
|
||||
|
||||
newLen := offsetNum + totalLength + nextTotalLength
|
||||
if curlen < newLen {
|
||||
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
|
||||
}
|
||||
oldPtrs := ctx.Ptrs
|
||||
|
||||
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
|
||||
newPtrs[0] = uintptr(iface.ptr)
|
||||
|
||||
ctx.Ptrs = newPtrs
|
||||
|
||||
oldBaseIndent := ctx.BaseIndent
|
||||
ctx.BaseIndent = code.Indent
|
||||
bb, err := Run(ctx, b, ifaceCodeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.BaseIndent = oldBaseIndent
|
||||
|
||||
ctx.Ptrs = oldPtrs
|
||||
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
|
||||
b = appendIndent(ctx, b, code.Indent+1)
|
||||
b = append(b, key...)
|
||||
|
@ -284,7 +292,7 @@ func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uin
|
|||
}
|
||||
|
||||
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {
|
||||
store(ctxptr, code.Length, indent)
|
||||
store(ctxptr, code.End.Next.Length, indent)
|
||||
}
|
||||
|
||||
func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -35,15 +35,6 @@ type emptyInterface struct {
|
|||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
type nonEmptyInterface struct {
|
||||
itab *struct {
|
||||
ityp *runtime.Type // static interface type
|
||||
typ *runtime.Type // dynamic concrete type
|
||||
// unused fields...
|
||||
}
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
|
||||
func errUnimplementedOp(op encoder.OpType) error {
|
||||
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
|
||||
}
|
||||
|
@ -70,19 +61,7 @@ func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr {
|
|||
return p
|
||||
}
|
||||
|
||||
func ptrToUint64(p uintptr, bitSize uint8) uint64 {
|
||||
switch bitSize {
|
||||
case 8:
|
||||
return (uint64)(**(**uint8)(unsafe.Pointer(&p)))
|
||||
case 16:
|
||||
return (uint64)(**(**uint16)(unsafe.Pointer(&p)))
|
||||
case 32:
|
||||
return (uint64)(**(**uint32)(unsafe.Pointer(&p)))
|
||||
case 64:
|
||||
return **(**uint64)(unsafe.Pointer(&p))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func ptrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
|
||||
func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
|
||||
func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
|
||||
|
@ -128,14 +107,47 @@ func appendComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
|||
return append(b, ',', '\n')
|
||||
}
|
||||
|
||||
func appendNullComma(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, "null,\n"...)
|
||||
}
|
||||
|
||||
func appendColon(_ *encoder.RuntimeContext, b []byte) []byte {
|
||||
return append(b, ':', ' ')
|
||||
}
|
||||
|
||||
func appendInterface(ctx *encoder.RuntimeContext, codeSet *encoder.OpcodeSet, code *encoder.Opcode, b []byte, iface *emptyInterface, ptrOffset uintptr) ([]byte, error) {
|
||||
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
|
||||
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
totalLength := uintptr(codeSet.CodeLength)
|
||||
nextTotalLength := uintptr(ifaceCodeSet.CodeLength)
|
||||
|
||||
curlen := uintptr(len(ctx.Ptrs))
|
||||
offsetNum := ptrOffset / uintptrSize
|
||||
|
||||
newLen := offsetNum + totalLength + nextTotalLength
|
||||
if curlen < newLen {
|
||||
ctx.Ptrs = append(ctx.Ptrs, make([]uintptr, newLen-curlen)...)
|
||||
}
|
||||
oldPtrs := ctx.Ptrs
|
||||
|
||||
newPtrs := ctx.Ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
|
||||
newPtrs[0] = uintptr(iface.ptr)
|
||||
|
||||
ctx.Ptrs = newPtrs
|
||||
|
||||
oldBaseIndent := ctx.BaseIndent
|
||||
ctx.BaseIndent = code.Indent
|
||||
bb, err := Run(ctx, b, ifaceCodeSet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx.BaseIndent = oldBaseIndent
|
||||
|
||||
ctx.Ptrs = oldPtrs
|
||||
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte {
|
||||
b = appendIndent(ctx, b, code.Indent+1)
|
||||
b = append(b, key...)
|
||||
|
@ -217,7 +229,7 @@ func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uin
|
|||
}
|
||||
|
||||
func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) {
|
||||
store(ctxptr, code.Length, indent)
|
||||
store(ctxptr, code.End.Next.Length, indent)
|
||||
}
|
||||
|
||||
func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -155,29 +155,3 @@ func ErrInvalidCharacter(c byte, context string, cursor int64) *SyntaxError {
|
|||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrInvalidBeginningOfValue(c byte, cursor int64) *SyntaxError {
|
||||
return &SyntaxError{
|
||||
msg: fmt.Sprintf("invalid character '%c' looking for beginning of value", c),
|
||||
Offset: cursor,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
32
json.go
32
json.go
|
@ -2,7 +2,6 @@ package json
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/goccy/go-json/internal/encoder"
|
||||
|
@ -14,12 +13,6 @@ type Marshaler interface {
|
|||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
// MarshalerContext is the interface implemented by types that
|
||||
// can marshal themselves into valid JSON with context.Context.
|
||||
type MarshalerContext interface {
|
||||
MarshalJSON(context.Context) ([]byte, error)
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface implemented by types
|
||||
// that can unmarshal a JSON description of themselves.
|
||||
// The input can be assumed to be a valid encoding of
|
||||
|
@ -32,12 +25,6 @@ type Unmarshaler interface {
|
|||
UnmarshalJSON([]byte) error
|
||||
}
|
||||
|
||||
// UnmarshalerContext is the interface implemented by types
|
||||
// that can unmarshal with context.Context a JSON description of themselves.
|
||||
type UnmarshalerContext interface {
|
||||
UnmarshalJSON(context.Context, []byte) error
|
||||
}
|
||||
|
||||
// Marshal returns the JSON encoding of v.
|
||||
//
|
||||
// Marshal traverses the value v recursively.
|
||||
|
@ -171,16 +158,11 @@ func Marshal(v interface{}) ([]byte, error) {
|
|||
return MarshalWithOption(v)
|
||||
}
|
||||
|
||||
// MarshalNoEscape returns the JSON encoding of v and doesn't escape v.
|
||||
// MarshalNoEscape
|
||||
func MarshalNoEscape(v interface{}) ([]byte, error) {
|
||||
return marshalNoEscape(v)
|
||||
}
|
||||
|
||||
// MarshalContext returns the JSON encoding of v with context.Context and EncodeOption.
|
||||
func MarshalContext(ctx context.Context, v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
|
||||
return marshalContext(ctx, v, optFuncs...)
|
||||
}
|
||||
|
||||
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
|
||||
func MarshalWithOption(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
|
||||
return marshal(v, optFuncs...)
|
||||
|
@ -276,13 +258,6 @@ func Unmarshal(data []byte, v interface{}) error {
|
|||
return unmarshal(data, v)
|
||||
}
|
||||
|
||||
// UnmarshalContext parses the JSON-encoded data and stores the result
|
||||
// in the value pointed to by v. If you implement the UnmarshalerContext interface,
|
||||
// call it with ctx as an argument.
|
||||
func UnmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
|
||||
return unmarshalContext(ctx, data, v)
|
||||
}
|
||||
|
||||
func UnmarshalWithOption(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error {
|
||||
return unmarshal(data, v, optFuncs...)
|
||||
}
|
||||
|
@ -364,8 +339,3 @@ func Valid(data []byte) bool {
|
|||
}
|
||||
return decoder.InputOffset() >= int64(len(data))
|
||||
}
|
||||
|
||||
func init() {
|
||||
encoder.Marshal = Marshal
|
||||
encoder.Unmarshal = Unmarshal
|
||||
}
|
||||
|
|
28
option.go
28
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) {
|
||||
|
@ -67,6 +41,6 @@ type DecodeOptionFunc func(*DecodeOption)
|
|||
// This behavior has a performance advantage as it allows the subsequent strings to be skipped if all fields have been evaluated.
|
||||
func DecodeFieldPriorityFirstWin() DecodeOptionFunc {
|
||||
return func(opt *DecodeOption) {
|
||||
opt.Flags |= decoder.FirstWinOption
|
||||
opt.Flag |= decoder.FirstWinOption
|
||||
}
|
||||
}
|
||||
|
|
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,8 +11,8 @@ func TestOpcodeSize(t *testing.T) {
|
|||
const uintptrSize = 4 << (^uintptr(0) >> 63)
|
||||
if uintptrSize == 8 {
|
||||
size := unsafe.Sizeof(encoder.Opcode{})
|
||||
if size != 120 {
|
||||
t.Fatalf("unexpected opcode size: expected 112bytes but got %dbytes", size)
|
||||
if size != 128 {
|
||||
t.Fatalf("unexpected opcode size: expected 128bytes but got %dbytes", size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package json_test
|
|||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
@ -15,7 +14,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -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" }`
|
||||
|
@ -513,29 +502,3 @@ func TestGzipStreaming(t *testing.T) {
|
|||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLongUTF8(t *testing.T) {
|
||||
want := strings.Repeat("あ", 342)
|
||||
r := strings.NewReader(strconv.Quote(want))
|
||||
|
||||
var got string
|
||||
if err := json.NewDecoder(r).Decode(&got); err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("string %q; want = %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue278(t *testing.T) {
|
||||
a := `{"嗷嗷":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\u55f7"}`
|
||||
r := strings.NewReader(a)
|
||||
var m map[string]string
|
||||
if err := json.NewDecoder(r).Decode(&m); err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
want := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\u55f7"
|
||||
if got := m["嗷嗷"]; got != want {
|
||||
t.Errorf("string %q; want = %q", got, want)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -1841,21 +1840,19 @@ func TestCoverArray(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package json_test
|
|||
import (
|
||||
"bytes"
|
||||
stdjson "encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -2654,21 +2653,19 @@ func TestCoverBool(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -1808,21 +1807,19 @@ func TestCoverBytes(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -2335,21 +2334,19 @@ func TestCoverFloat32(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -2335,21 +2334,19 @@ func TestCoverFloat64(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -1812,21 +1811,19 @@ func TestCoverInt16(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -1789,21 +1788,19 @@ func TestCoverInt32(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -1789,21 +1788,19 @@ func TestCoverInt64(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -1789,21 +1788,19 @@ func TestCoverInt8(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%T): %+v: %s", test.name, htmlEscape, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%T): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package json_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
|
@ -632,56 +631,6 @@ func TestCoverInt(t *testing.T) {
|
|||
}{A: -1}},
|
||||
},
|
||||
|
||||
// HeadIntNotRootMultiFields
|
||||
{
|
||||
name: "HeadIntNotRootMultiFields",
|
||||
data: struct {
|
||||
A struct {
|
||||
A int `json:"a"`
|
||||
B int `json:"b"`
|
||||
}
|
||||
}{A: struct {
|
||||
A int `json:"a"`
|
||||
B int `json:"b"`
|
||||
}{A: -1, B: 1}},
|
||||
},
|
||||
{
|
||||
name: "HeadIntNotRootOmitEmptyMultiFields",
|
||||
data: struct {
|
||||
A struct {
|
||||
A int `json:"a,omitempty"`
|
||||
B int `json:"b,omitempty"`
|
||||
}
|
||||
}{A: struct {
|
||||
A int `json:"a,omitempty"`
|
||||
B int `json:"b,omitempty"`
|
||||
}{A: -1, B: 1}},
|
||||
},
|
||||
{
|
||||
name: "HeadIntNotRootStringMultiFields",
|
||||
data: struct {
|
||||
A struct {
|
||||
A int `json:"a,string"`
|
||||
B int `json:"b,string"`
|
||||
}
|
||||
}{A: struct {
|
||||
A int `json:"a,string"`
|
||||
B int `json:"b,string"`
|
||||
}{A: -1, B: 1}},
|
||||
},
|
||||
{
|
||||
name: "HeadIntNotRootStringOmitEmptyMultiFields",
|
||||
data: struct {
|
||||
A struct {
|
||||
A int `json:"a,string,omitempty"`
|
||||
B int `json:"b,string,omitempty"`
|
||||
}
|
||||
}{A: struct {
|
||||
A int `json:"a,string,omitempty"`
|
||||
B int `json:"b,string,omitempty"`
|
||||
}{A: -1, B: 1}},
|
||||
},
|
||||
|
||||
// HeadIntPtrNotRoot
|
||||
{
|
||||
name: "HeadIntPtrNotRoot",
|
||||
|
@ -842,56 +791,6 @@ func TestCoverInt(t *testing.T) {
|
|||
}{A: -1})},
|
||||
},
|
||||
|
||||
// PtrHeadIntNotRootMultiFields
|
||||
{
|
||||
name: "PtrHeadIntNotRootMultiFields",
|
||||
data: struct {
|
||||
A *struct {
|
||||
A int `json:"a"`
|
||||
B int `json:"b"`
|
||||
}
|
||||
}{A: &(struct {
|
||||
A int `json:"a"`
|
||||
B int `json:"b"`
|
||||
}{A: -1, B: 1})},
|
||||
},
|
||||
{
|
||||
name: "PtrHeadIntNotRootOmitEmptyMultiFields",
|
||||
data: struct {
|
||||
A *struct {
|
||||
A int `json:"a,omitempty"`
|
||||
B int `json:"b,omitempty"`
|
||||
}
|
||||
}{A: &(struct {
|
||||
A int `json:"a,omitempty"`
|
||||
B int `json:"b,omitempty"`
|
||||
}{A: -1, B: 1})},
|
||||
},
|
||||
{
|
||||
name: "PtrHeadIntNotRootStringMultiFields",
|
||||
data: struct {
|
||||
A *struct {
|
||||
A int `json:"a,string"`
|
||||
B int `json:"b,string"`
|
||||
}
|
||||
}{A: &(struct {
|
||||
A int `json:"a,string"`
|
||||
B int `json:"b,string"`
|
||||
}{A: -1, B: 1})},
|
||||
},
|
||||
{
|
||||
name: "PtrHeadIntNotRootStringOmitEmptyMultiFields",
|
||||
data: struct {
|
||||
A *struct {
|
||||
A int `json:"a,string,omitempty"`
|
||||
B int `json:"b,string,omitempty"`
|
||||
}
|
||||
}{A: &(struct {
|
||||
A int `json:"a,string,omitempty"`
|
||||
B int `json:"b,string,omitempty"`
|
||||
}{A: -1, B: 1})},
|
||||
},
|
||||
|
||||
// PtrHeadIntPtrNotRoot
|
||||
{
|
||||
name: "PtrHeadIntPtrNotRoot",
|
||||
|
@ -2411,21 +2310,19 @@ func TestCoverInt(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
for _, indent := range []bool{true, false} {
|
||||
for _, htmlEscape := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("%s_indent_%t_escape_%t", test.name, indent, htmlEscape), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
})
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetEscapeHTML(htmlEscape)
|
||||
if indent {
|
||||
enc.SetIndent("", " ")
|
||||
}
|
||||
if err := enc.Encode(test.data); err != nil {
|
||||
t.Fatalf("%s(htmlEscape:%v,indent:%v): %+v: %s", test.name, htmlEscape, indent, test.data, err)
|
||||
}
|
||||
stdresult := encodeByEncodingJSON(test.data, indent, htmlEscape)
|
||||
if buf.String() != stdresult {
|
||||
t.Errorf("%s(htmlEscape:%v,indent:%v): doesn't compatible with encoding/json. expected %q but got %q", test.name, htmlEscape, indent, stdresult, buf.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue