├── .codecov.yml ├── .github ├── FUNDING.yml └── workflows │ └── go.yml ├── .gitignore ├── .golangci.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── benchmarks ├── bench_test.go ├── decode_test.go ├── encode_test.go ├── go.mod ├── go.sum ├── large_payload.go ├── large_payload_easyjson.go ├── medium_payload.go ├── medium_payload_easyjson.go ├── path_test.go ├── slow_reader_test.go ├── small_payload.go ├── small_payload_easyjson.go ├── small_payload_ffjson.go └── testdata │ └── code.json.gz ├── color.go ├── color_test.go ├── decode.go ├── decode_test.go ├── docker-compose.yml ├── encode.go ├── encode_test.go ├── error.go ├── export_test.go ├── go.mod ├── go.sum ├── helper_test.go ├── internal ├── cmd │ └── generator │ │ ├── main.go │ │ └── vm.go.tmpl ├── decoder │ ├── anonymous_field.go │ ├── array.go │ ├── assign.go │ ├── bool.go │ ├── bytes.go │ ├── compile.go │ ├── compile_norace.go │ ├── compile_race.go │ ├── context.go │ ├── float.go │ ├── func.go │ ├── int.go │ ├── interface.go │ ├── invalid.go │ ├── map.go │ ├── number.go │ ├── option.go │ ├── path.go │ ├── ptr.go │ ├── slice.go │ ├── stream.go │ ├── string.go │ ├── struct.go │ ├── type.go │ ├── uint.go │ ├── unmarshal_json.go │ ├── unmarshal_text.go │ └── wrapped_string.go ├── encoder │ ├── code.go │ ├── compact.go │ ├── compiler.go │ ├── compiler_norace.go │ ├── compiler_race.go │ ├── context.go │ ├── decode_rune.go │ ├── encode_opcode_test.go │ ├── encoder.go │ ├── indent.go │ ├── int.go │ ├── map112.go │ ├── map113.go │ ├── opcode.go │ ├── option.go │ ├── optype.go │ ├── query.go │ ├── string.go │ ├── string_table.go │ ├── vm │ │ ├── debug_vm.go │ │ ├── hack.go │ │ ├── util.go │ │ └── vm.go │ ├── vm_color │ │ ├── debug_vm.go │ │ ├── hack.go │ │ ├── util.go │ │ └── vm.go │ ├── vm_color_indent │ │ ├── debug_vm.go │ │ ├── util.go │ │ └── vm.go │ └── vm_indent │ │ ├── debug_vm.go │ │ ├── hack.go │ │ ├── util.go │ │ └── vm.go ├── errors │ └── error.go └── runtime │ ├── rtype.go │ ├── struct_field.go │ └── type.go ├── json.go ├── json_test.go ├── number_test.go ├── option.go ├── path.go ├── path_test.go ├── query.go ├── query_test.go ├── size_test.go ├── stream_test.go ├── tagkey_test.go └── test ├── cover ├── cover_array_test.go ├── cover_bool_test.go ├── cover_bytes_test.go ├── cover_float32_test.go ├── cover_float64_test.go ├── cover_helper_test.go ├── cover_int16_test.go ├── cover_int32_test.go ├── cover_int64_test.go ├── cover_int8_test.go ├── cover_int_test.go ├── cover_map_test.go ├── cover_marshal_json_test.go ├── cover_marshal_text_test.go ├── cover_number_test.go ├── cover_slice_test.go ├── cover_string_test.go ├── cover_uint16_test.go ├── cover_uint32_test.go ├── cover_uint64_test.go ├── cover_uint8_test.go └── cover_uint_test.go └── example ├── example_marshaling_test.go ├── example_query_test.go ├── example_test.go └── example_text_marshaling_test.go /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "70...100" 8 | 9 | status: 10 | project: 11 | default: 12 | target: 70% 13 | threshold: 2% 14 | patch: off 15 | changes: no 16 | 17 | parsers: 18 | gcov: 19 | branch_detection: 20 | conditional: yes 21 | loop: yes 22 | method: no 23 | macro: no 24 | 25 | comment: 26 | layout: "header,diff" 27 | behavior: default 28 | require_changes: no 29 | 30 | ignore: 31 | - internal/encoder/vm_color 32 | - internal/encoder/vm_color_indent 33 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [goccy] 2 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | jobs: 8 | build: 9 | name: Build on limited environment 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: checkout 13 | uses: actions/checkout@v3 14 | - name: build 15 | run: docker compose run go-json 16 | 17 | test: 18 | name: Test 19 | strategy: 20 | matrix: 21 | os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] 22 | go-version: [ "1.19", "1.20", "1.21" ] 23 | runs-on: ${{ matrix.os }} 24 | steps: 25 | - name: setup Go ${{ matrix.go-version }} 26 | uses: actions/setup-go@v3 27 | with: 28 | go-version: ${{ matrix.go-version }} 29 | - name: checkout 30 | uses: actions/checkout@v3 31 | - name: simple test 32 | run: go test -v ./... -count=1 33 | - name: test with GC pressure 34 | run: go test -v ./... -count=1 35 | env: 36 | GOGC: 1 37 | - name: test with race detector 38 | run: go test -v -race ./... -count=1 39 | 40 | lint: 41 | name: Lint 42 | runs-on: ubuntu-latest 43 | steps: 44 | - name: checkout 45 | uses: actions/checkout@v3 46 | - name: setup Go 47 | uses: actions/setup-go@v3 48 | with: 49 | go-version: '1.21' 50 | - name: lint 51 | run: | 52 | make lint 53 | bench: 54 | name: Benchmark 55 | runs-on: ubuntu-latest 56 | steps: 57 | - name: setup Go 58 | uses: actions/setup-go@v3 59 | with: 60 | go-version: '1.21' 61 | - name: checkout ( feature ) 62 | uses: actions/checkout@v3 63 | - name: run benchmark ( feature ) 64 | run: cd benchmarks && go test -bench GoJson | tee $HOME/new.txt 65 | - name: install benchstat 66 | run: go install golang.org/x/perf/cmd/benchstat@latest 67 | - name: checkout ( master ) 68 | uses: actions/checkout@v3 69 | with: 70 | ref: master 71 | - name: run benchmark ( master ) 72 | run: cd benchmarks && go test -bench GoJson | tee $HOME/old.txt 73 | - name: compare benchmark results 74 | run: benchstat $HOME/old.txt $HOME/new.txt 75 | 76 | coverage: 77 | name: Coverage 78 | runs-on: ubuntu-latest 79 | steps: 80 | - name: checkout 81 | uses: actions/checkout@v3 82 | - name: setup Go 83 | uses: actions/setup-go@v3 84 | with: 85 | go-version: '1.21' 86 | - name: measure coverage 87 | run: make cover 88 | - uses: codecov/codecov-action@v4 89 | with: 90 | fail_ci_if_error: true 91 | verbose: true 92 | token: ${{ secrets.CODECOV_TOKEN }} 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cover.html 2 | cover.out 3 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | skip-files: 3 | - encode_optype.go 4 | - ".*_test\\.go$" 5 | 6 | linters-settings: 7 | govet: 8 | enable-all: true 9 | disable: 10 | - shadow 11 | 12 | linters: 13 | enable-all: true 14 | disable: 15 | - dogsled 16 | - dupl 17 | - exhaustive 18 | - exhaustivestruct 19 | - errorlint 20 | - forbidigo 21 | - funlen 22 | - gci 23 | - gochecknoglobals 24 | - gochecknoinits 25 | - gocognit 26 | - gocritic 27 | - gocyclo 28 | - godot 29 | - godox 30 | - goerr113 31 | - gofumpt 32 | - gomnd 33 | - gosec 34 | - ifshort 35 | - lll 36 | - makezero 37 | - nakedret 38 | - nestif 39 | - nlreturn 40 | - paralleltest 41 | - testpackage 42 | - thelper 43 | - wrapcheck 44 | - interfacer 45 | - lll 46 | - nakedret 47 | - nestif 48 | - nlreturn 49 | - testpackage 50 | - wsl 51 | - varnamelen 52 | - nilnil 53 | - ireturn 54 | - govet 55 | - forcetypeassert 56 | - cyclop 57 | - containedctx 58 | - revive 59 | - nosnakecase 60 | - exhaustruct 61 | - depguard 62 | 63 | issues: 64 | exclude-rules: 65 | # not needed 66 | - path: /*.go 67 | text: "ST1003: should not use underscores in package names" 68 | linters: 69 | - stylecheck 70 | - path: /*.go 71 | text: "don't use an underscore in package name" 72 | linters: 73 | - golint 74 | - path: rtype.go 75 | linters: 76 | - golint 77 | - stylecheck 78 | - path: error.go 79 | linters: 80 | - staticcheck 81 | 82 | # Maximum issues count per one linter. Set to 0 to disable. Default is 50. 83 | max-issues-per-linter: 0 84 | 85 | # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. 86 | max-same-issues: 0 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Masaaki Goshima 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKG := github.com/goccy/go-json 2 | 3 | BIN_DIR := $(CURDIR)/bin 4 | PKGS := $(shell go list ./... | grep -v internal/cmd|grep -v test) 5 | COVER_PKGS := $(foreach pkg,$(PKGS),$(subst $(PKG),.,$(pkg))) 6 | 7 | COMMA := , 8 | EMPTY := 9 | SPACE := $(EMPTY) $(EMPTY) 10 | COVERPKG_OPT := $(subst $(SPACE),$(COMMA),$(COVER_PKGS)) 11 | 12 | $(BIN_DIR): 13 | @mkdir -p $(BIN_DIR) 14 | 15 | .PHONY: cover 16 | cover: 17 | go test -coverpkg=$(COVERPKG_OPT) -coverprofile=cover.out ./... 18 | 19 | .PHONY: cover-html 20 | cover-html: cover 21 | go tool cover -html=cover.out 22 | 23 | .PHONY: lint 24 | lint: golangci-lint 25 | $(BIN_DIR)/golangci-lint run 26 | 27 | golangci-lint: | $(BIN_DIR) 28 | @{ \ 29 | set -e; \ 30 | GOLANGCI_LINT_TMP_DIR=$$(mktemp -d); \ 31 | cd $$GOLANGCI_LINT_TMP_DIR; \ 32 | go mod init tmp; \ 33 | GOBIN=$(BIN_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.2; \ 34 | rm -rf $$GOLANGCI_LINT_TMP_DIR; \ 35 | } 36 | 37 | .PHONY: generate 38 | generate: 39 | go generate ./internal/... 40 | -------------------------------------------------------------------------------- /benchmarks/go.mod: -------------------------------------------------------------------------------- 1 | module benchmark 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/francoispqt/gojay v1.2.13 7 | github.com/goccy/go-json v0.0.0-00010101000000-000000000000 8 | github.com/json-iterator/go v1.1.10 9 | github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe 10 | github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 11 | github.com/segmentio/encoding v0.2.4 12 | github.com/valyala/fastjson v1.6.3 13 | github.com/wI2L/jettison v0.7.1 14 | ) 15 | 16 | require ( 17 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 18 | github.com/modern-go/reflect2 v1.0.1 // indirect 19 | github.com/stretchr/testify v1.7.0 // indirect 20 | ) 21 | 22 | replace github.com/goccy/go-json => ../ 23 | -------------------------------------------------------------------------------- /benchmarks/path_test.go: -------------------------------------------------------------------------------- 1 | package benchmark 2 | 3 | import ( 4 | "testing" 5 | 6 | gojson "github.com/goccy/go-json" 7 | ) 8 | 9 | func Benchmark_Decode_SmallStruct_UnmarshalPath_GoJson(b *testing.B) { 10 | path, err := gojson.CreatePath("$.st") 11 | if err != nil { 12 | b.Fatal(err) 13 | } 14 | b.ReportAllocs() 15 | for i := 0; i < b.N; i++ { 16 | var v int 17 | if err := path.Unmarshal(SmallFixture, &v); err != nil { 18 | b.Fatal(err) 19 | } 20 | if v != 1 { 21 | b.Fatal("failed to unmarshal path") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /benchmarks/slow_reader_test.go: -------------------------------------------------------------------------------- 1 | package benchmark 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | "testing" 9 | 10 | gojson "github.com/goccy/go-json" 11 | ) 12 | 13 | // Benchmark decoding from a slow io.Reader that never fills the buffer completely 14 | func Benchmark_Decode_SlowReader_EncodingJson(b *testing.B) { 15 | var expected LargePayload 16 | if err := json.Unmarshal(LargeFixture, &expected); err != nil { 17 | b.Fatal(err) 18 | } 19 | for _, chunkSize := range [5]int{16384, 4096, 1024, 256, 64} { 20 | b.Run(fmt.Sprintf("chunksize %v", chunkSize), func(b *testing.B) { 21 | b.ReportAllocs() 22 | for i := 0; i < b.N; i++ { 23 | index = 0 24 | var got LargePayload 25 | if err := json.NewDecoder(slowReader{chunkSize: chunkSize}).Decode(&got); err != nil { 26 | b.Fatal(err) 27 | } 28 | if !reflect.DeepEqual(expected, got) { 29 | b.Fatalf("failed to decode. expected:[%+v] but got:[%+v]", expected, got) 30 | } 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func Benchmark_Decode_SlowReader_GoJson(b *testing.B) { 37 | var expected LargePayload 38 | if err := json.Unmarshal(LargeFixture, &expected); err != nil { 39 | b.Fatal(err) 40 | } 41 | for _, chunkSize := range []int{16384, 4096, 1024, 256, 64} { 42 | b.Run(fmt.Sprintf("chunksize %v", chunkSize), func(b *testing.B) { 43 | b.ReportAllocs() 44 | for i := 0; i < b.N; i++ { 45 | index = 0 46 | var got LargePayload 47 | if err := gojson.NewDecoder(slowReader{chunkSize: chunkSize}).Decode(&got); err != nil { 48 | b.Fatal(err) 49 | } 50 | if !reflect.DeepEqual(expected, got) { 51 | b.Fatalf("failed to decode. expected:[%+v] but got:[%+v]", expected, got) 52 | } 53 | } 54 | }) 55 | } 56 | } 57 | 58 | type slowReader struct { 59 | chunkSize int 60 | } 61 | 62 | var index int 63 | 64 | func (s slowReader) Read(p []byte) (n int, err error) { 65 | smallBuf := make([]byte, Min(s.chunkSize, len(p))) 66 | x := bytes.NewReader(LargeFixture) 67 | n, err = x.ReadAt(smallBuf, int64(index)) 68 | index += n 69 | copy(p, smallBuf) 70 | return 71 | } 72 | 73 | func Min(x, y int) int { 74 | if x < y { 75 | return x 76 | } 77 | return y 78 | } 79 | -------------------------------------------------------------------------------- /benchmarks/small_payload.go: -------------------------------------------------------------------------------- 1 | package benchmark 2 | 3 | import "github.com/francoispqt/gojay" 4 | 5 | var SmallFixture = []byte(`{"st": 1,"sid": 486,"tt": "active","gr": 0,"uuid": "de305d54-75b4-431b-adb2-eb6b9e546014","ip": "127.0.0.1","ua": "user_agent","tz": -6,"v": 1}`) 6 | 7 | // ffjson:skip 8 | type SmallPayload struct { 9 | St int 10 | Sid int 11 | Tt string 12 | Gr int 13 | Uuid string 14 | Ip string 15 | Ua string 16 | Tz int 17 | V int 18 | } 19 | 20 | type SmallPayloadFFJson struct { 21 | St int 22 | Sid int 23 | Tt string 24 | Gr int 25 | Uuid string 26 | Ip string 27 | Ua string 28 | Tz int 29 | V int 30 | } 31 | 32 | //easyjson:json 33 | type SmallPayloadEasyJson struct { 34 | St int 35 | Sid int 36 | Tt string 37 | Gr int 38 | Uuid string 39 | Ip string 40 | Ua string 41 | Tz int 42 | V int 43 | } 44 | 45 | func (t *SmallPayload) MarshalJSONObject(enc *gojay.Encoder) { 46 | enc.AddIntKey("st", t.St) 47 | enc.AddIntKey("sid", t.Sid) 48 | enc.AddStringKey("tt", t.Tt) 49 | enc.AddIntKey("gr", t.Gr) 50 | enc.AddStringKey("uuid", t.Uuid) 51 | enc.AddStringKey("ip", t.Ip) 52 | enc.AddStringKey("ua", t.Ua) 53 | enc.AddIntKey("tz", t.Tz) 54 | enc.AddIntKey("v", t.V) 55 | } 56 | 57 | func (t *SmallPayload) IsNil() bool { 58 | return t == nil 59 | } 60 | 61 | func (t *SmallPayload) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { 62 | switch key { 63 | case "st": 64 | return dec.AddInt(&t.St) 65 | case "sid": 66 | return dec.AddInt(&t.Sid) 67 | case "gr": 68 | return dec.AddInt(&t.Gr) 69 | case "tz": 70 | return dec.AddInt(&t.Tz) 71 | case "v": 72 | return dec.AddInt(&t.V) 73 | case "tt": 74 | return dec.AddString(&t.Tt) 75 | case "uuid": 76 | return dec.AddString(&t.Uuid) 77 | case "ip": 78 | return dec.AddString(&t.Ip) 79 | case "ua": 80 | return dec.AddString(&t.Ua) 81 | } 82 | return nil 83 | } 84 | 85 | func (t *SmallPayload) NKeys() int { 86 | return 9 87 | } 88 | 89 | func NewSmallPayload() *SmallPayload { 90 | return &SmallPayload{ 91 | St: 1, 92 | Sid: 2, 93 | Tt: "TestString", 94 | Gr: 4, 95 | Uuid: "8f9a65eb-4807-4d57-b6e0-bda5d62f1429", 96 | Ip: "127.0.0.1", 97 | Ua: "Mozilla", 98 | Tz: 8, 99 | V: 6, 100 | } 101 | } 102 | 103 | func NewSmallPayloadEasyJson() *SmallPayloadEasyJson { 104 | return &SmallPayloadEasyJson{ 105 | St: 1, 106 | Sid: 2, 107 | Tt: "TestString", 108 | Gr: 4, 109 | Uuid: "8f9a65eb-4807-4d57-b6e0-bda5d62f1429", 110 | Ip: "127.0.0.1", 111 | Ua: "Mozilla", 112 | Tz: 8, 113 | V: 6, 114 | } 115 | } 116 | 117 | func NewSmallPayloadFFJson() *SmallPayloadFFJson { 118 | return &SmallPayloadFFJson{ 119 | St: 1, 120 | Sid: 2, 121 | Tt: "TestString", 122 | Gr: 4, 123 | Uuid: "8f9a65eb-4807-4d57-b6e0-bda5d62f1429", 124 | Ip: "127.0.0.1", 125 | Ua: "Mozilla", 126 | Tz: 8, 127 | V: 6, 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /benchmarks/small_payload_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package benchmark 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson21677a1cDecodeBenchmark(in *jlexer.Lexer, out *SmallPayloadEasyJson) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeString() 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "St": 40 | out.St = int(in.Int()) 41 | case "Sid": 42 | out.Sid = int(in.Int()) 43 | case "Tt": 44 | out.Tt = string(in.String()) 45 | case "Gr": 46 | out.Gr = int(in.Int()) 47 | case "Uuid": 48 | out.Uuid = string(in.String()) 49 | case "Ip": 50 | out.Ip = string(in.String()) 51 | case "Ua": 52 | out.Ua = string(in.String()) 53 | case "Tz": 54 | out.Tz = int(in.Int()) 55 | case "V": 56 | out.V = int(in.Int()) 57 | default: 58 | in.SkipRecursive() 59 | } 60 | in.WantComma() 61 | } 62 | in.Delim('}') 63 | if isTopLevel { 64 | in.Consumed() 65 | } 66 | } 67 | func easyjson21677a1cEncodeBenchmark(out *jwriter.Writer, in SmallPayloadEasyJson) { 68 | out.RawByte('{') 69 | first := true 70 | _ = first 71 | { 72 | const prefix string = ",\"St\":" 73 | if first { 74 | first = false 75 | out.RawString(prefix[1:]) 76 | } else { 77 | out.RawString(prefix) 78 | } 79 | out.Int(int(in.St)) 80 | } 81 | { 82 | const prefix string = ",\"Sid\":" 83 | if first { 84 | first = false 85 | out.RawString(prefix[1:]) 86 | } else { 87 | out.RawString(prefix) 88 | } 89 | out.Int(int(in.Sid)) 90 | } 91 | { 92 | const prefix string = ",\"Tt\":" 93 | if first { 94 | first = false 95 | out.RawString(prefix[1:]) 96 | } else { 97 | out.RawString(prefix) 98 | } 99 | out.String(string(in.Tt)) 100 | } 101 | { 102 | const prefix string = ",\"Gr\":" 103 | if first { 104 | first = false 105 | out.RawString(prefix[1:]) 106 | } else { 107 | out.RawString(prefix) 108 | } 109 | out.Int(int(in.Gr)) 110 | } 111 | { 112 | const prefix string = ",\"Uuid\":" 113 | if first { 114 | first = false 115 | out.RawString(prefix[1:]) 116 | } else { 117 | out.RawString(prefix) 118 | } 119 | out.String(string(in.Uuid)) 120 | } 121 | { 122 | const prefix string = ",\"Ip\":" 123 | if first { 124 | first = false 125 | out.RawString(prefix[1:]) 126 | } else { 127 | out.RawString(prefix) 128 | } 129 | out.String(string(in.Ip)) 130 | } 131 | { 132 | const prefix string = ",\"Ua\":" 133 | if first { 134 | first = false 135 | out.RawString(prefix[1:]) 136 | } else { 137 | out.RawString(prefix) 138 | } 139 | out.String(string(in.Ua)) 140 | } 141 | { 142 | const prefix string = ",\"Tz\":" 143 | if first { 144 | first = false 145 | out.RawString(prefix[1:]) 146 | } else { 147 | out.RawString(prefix) 148 | } 149 | out.Int(int(in.Tz)) 150 | } 151 | { 152 | const prefix string = ",\"V\":" 153 | if first { 154 | first = false 155 | out.RawString(prefix[1:]) 156 | } else { 157 | out.RawString(prefix) 158 | } 159 | out.Int(int(in.V)) 160 | } 161 | out.RawByte('}') 162 | } 163 | 164 | // MarshalJSON supports json.Marshaler interface 165 | func (v SmallPayloadEasyJson) MarshalJSON() ([]byte, error) { 166 | w := jwriter.Writer{} 167 | easyjson21677a1cEncodeBenchmark(&w, v) 168 | return w.Buffer.BuildBytes(), w.Error 169 | } 170 | 171 | // MarshalEasyJSON supports easyjson.Marshaler interface 172 | func (v SmallPayloadEasyJson) MarshalEasyJSON(w *jwriter.Writer) { 173 | easyjson21677a1cEncodeBenchmark(w, v) 174 | } 175 | 176 | // UnmarshalJSON supports json.Unmarshaler interface 177 | func (v *SmallPayloadEasyJson) UnmarshalJSON(data []byte) error { 178 | r := jlexer.Lexer{Data: data} 179 | easyjson21677a1cDecodeBenchmark(&r, v) 180 | return r.Error() 181 | } 182 | 183 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 184 | func (v *SmallPayloadEasyJson) UnmarshalEasyJSON(l *jlexer.Lexer) { 185 | easyjson21677a1cDecodeBenchmark(l, v) 186 | } 187 | -------------------------------------------------------------------------------- /benchmarks/testdata/code.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goccy/go-json/9872089c316cfe2d0f29b331b75d45bf6d522d96/benchmarks/testdata/code.json.gz -------------------------------------------------------------------------------- /color.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/goccy/go-json/internal/encoder" 7 | ) 8 | 9 | type ( 10 | ColorFormat = encoder.ColorFormat 11 | ColorScheme = encoder.ColorScheme 12 | ) 13 | 14 | const escape = "\x1b" 15 | 16 | type colorAttr int 17 | 18 | //nolint:deadcode,varcheck 19 | const ( 20 | fgBlackColor colorAttr = iota + 30 21 | fgRedColor 22 | fgGreenColor 23 | fgYellowColor 24 | fgBlueColor 25 | fgMagentaColor 26 | fgCyanColor 27 | fgWhiteColor 28 | ) 29 | 30 | //nolint:deadcode,varcheck 31 | const ( 32 | fgHiBlackColor colorAttr = iota + 90 33 | fgHiRedColor 34 | fgHiGreenColor 35 | fgHiYellowColor 36 | fgHiBlueColor 37 | fgHiMagentaColor 38 | fgHiCyanColor 39 | fgHiWhiteColor 40 | ) 41 | 42 | func createColorFormat(attr colorAttr) ColorFormat { 43 | return ColorFormat{ 44 | Header: wrapColor(attr), 45 | Footer: resetColor(), 46 | } 47 | } 48 | 49 | func wrapColor(attr colorAttr) string { 50 | return fmt.Sprintf("%s[%dm", escape, attr) 51 | } 52 | 53 | func resetColor() string { 54 | return wrapColor(colorAttr(0)) 55 | } 56 | 57 | var ( 58 | DefaultColorScheme = &ColorScheme{ 59 | Int: createColorFormat(fgHiMagentaColor), 60 | Uint: createColorFormat(fgHiMagentaColor), 61 | Float: createColorFormat(fgHiMagentaColor), 62 | Bool: createColorFormat(fgHiYellowColor), 63 | String: createColorFormat(fgHiGreenColor), 64 | Binary: createColorFormat(fgHiRedColor), 65 | ObjectKey: createColorFormat(fgHiCyanColor), 66 | Null: createColorFormat(fgBlueColor), 67 | } 68 | ) 69 | -------------------------------------------------------------------------------- /color_test.go: -------------------------------------------------------------------------------- 1 | package json_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/goccy/go-json" 7 | ) 8 | 9 | func TestColorize(t *testing.T) { 10 | v := struct { 11 | A int 12 | B uint 13 | C float32 14 | D string 15 | E bool 16 | F []byte 17 | G []int 18 | H *struct{} 19 | I map[string]interface{} 20 | }{ 21 | A: 123, 22 | B: 456, 23 | C: 3.14, 24 | D: "hello", 25 | E: true, 26 | F: []byte("binary"), 27 | G: []int{1, 2, 3, 4}, 28 | H: nil, 29 | I: map[string]interface{}{ 30 | "mapA": -10, 31 | "mapB": 10, 32 | "mapC": nil, 33 | }, 34 | } 35 | t.Run("marshal with color", func(t *testing.T) { 36 | b, err := json.MarshalWithOption(v, json.Colorize(json.DefaultColorScheme)) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | t.Log(string(b)) 41 | }) 42 | t.Run("marshal indent with color", func(t *testing.T) { 43 | b, err := json.MarshalIndentWithOption(v, "", "\t", json.Colorize(json.DefaultColorScheme)) 44 | if err != nil { 45 | t.Fatal(err) 46 | } 47 | t.Log("\n" + string(b)) 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /decode.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "reflect" 8 | "unsafe" 9 | 10 | "github.com/goccy/go-json/internal/decoder" 11 | "github.com/goccy/go-json/internal/errors" 12 | "github.com/goccy/go-json/internal/runtime" 13 | ) 14 | 15 | type Decoder struct { 16 | s *decoder.Stream 17 | } 18 | 19 | const ( 20 | nul = '\000' 21 | ) 22 | 23 | type emptyInterface struct { 24 | typ *runtime.Type 25 | ptr unsafe.Pointer 26 | } 27 | 28 | func unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { 29 | src := make([]byte, len(data)+1) // append nul byte to the end 30 | copy(src, data) 31 | 32 | header := (*emptyInterface)(unsafe.Pointer(&v)) 33 | 34 | if err := validateType(header.typ, uintptr(header.ptr)); err != nil { 35 | return err 36 | } 37 | dec, err := decoder.CompileToGetDecoder(header.typ) 38 | if err != nil { 39 | return err 40 | } 41 | ctx := decoder.TakeRuntimeContext() 42 | ctx.Buf = src 43 | ctx.Option.Flags = 0 44 | for _, optFunc := range optFuncs { 45 | optFunc(ctx.Option) 46 | } 47 | cursor, err := dec.Decode(ctx, 0, 0, header.ptr) 48 | if err != nil { 49 | decoder.ReleaseRuntimeContext(ctx) 50 | return err 51 | } 52 | decoder.ReleaseRuntimeContext(ctx) 53 | return validateEndBuf(src, cursor) 54 | } 55 | 56 | func unmarshalContext(ctx context.Context, data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { 57 | src := make([]byte, len(data)+1) // append nul byte to the end 58 | copy(src, data) 59 | 60 | header := (*emptyInterface)(unsafe.Pointer(&v)) 61 | 62 | if err := validateType(header.typ, uintptr(header.ptr)); err != nil { 63 | return err 64 | } 65 | dec, err := decoder.CompileToGetDecoder(header.typ) 66 | if err != nil { 67 | return err 68 | } 69 | rctx := decoder.TakeRuntimeContext() 70 | rctx.Buf = src 71 | rctx.Option.Flags = 0 72 | rctx.Option.Flags |= decoder.ContextOption 73 | rctx.Option.Context = ctx 74 | for _, optFunc := range optFuncs { 75 | optFunc(rctx.Option) 76 | } 77 | cursor, err := dec.Decode(rctx, 0, 0, header.ptr) 78 | if err != nil { 79 | decoder.ReleaseRuntimeContext(rctx) 80 | return err 81 | } 82 | decoder.ReleaseRuntimeContext(rctx) 83 | return validateEndBuf(src, cursor) 84 | } 85 | 86 | var ( 87 | pathDecoder = decoder.NewPathDecoder() 88 | ) 89 | 90 | func extractFromPath(path *Path, data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) { 91 | if path.path.RootSelectorOnly { 92 | return [][]byte{data}, nil 93 | } 94 | src := make([]byte, len(data)+1) // append nul byte to the end 95 | copy(src, data) 96 | 97 | ctx := decoder.TakeRuntimeContext() 98 | ctx.Buf = src 99 | ctx.Option.Flags = 0 100 | ctx.Option.Flags |= decoder.PathOption 101 | ctx.Option.Path = path.path 102 | for _, optFunc := range optFuncs { 103 | optFunc(ctx.Option) 104 | } 105 | paths, cursor, err := pathDecoder.DecodePath(ctx, 0, 0) 106 | if err != nil { 107 | decoder.ReleaseRuntimeContext(ctx) 108 | return nil, err 109 | } 110 | decoder.ReleaseRuntimeContext(ctx) 111 | if err := validateEndBuf(src, cursor); err != nil { 112 | return nil, err 113 | } 114 | return paths, nil 115 | } 116 | 117 | func unmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { 118 | src := make([]byte, len(data)+1) // append nul byte to the end 119 | copy(src, data) 120 | 121 | header := (*emptyInterface)(unsafe.Pointer(&v)) 122 | 123 | if err := validateType(header.typ, uintptr(header.ptr)); err != nil { 124 | return err 125 | } 126 | dec, err := decoder.CompileToGetDecoder(header.typ) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | ctx := decoder.TakeRuntimeContext() 132 | ctx.Buf = src 133 | ctx.Option.Flags = 0 134 | for _, optFunc := range optFuncs { 135 | optFunc(ctx.Option) 136 | } 137 | cursor, err := dec.Decode(ctx, 0, 0, noescape(header.ptr)) 138 | if err != nil { 139 | decoder.ReleaseRuntimeContext(ctx) 140 | return err 141 | } 142 | decoder.ReleaseRuntimeContext(ctx) 143 | return validateEndBuf(src, cursor) 144 | } 145 | 146 | func validateEndBuf(src []byte, cursor int64) error { 147 | for { 148 | switch src[cursor] { 149 | case ' ', '\t', '\n', '\r': 150 | cursor++ 151 | continue 152 | case nul: 153 | return nil 154 | } 155 | return errors.ErrSyntax( 156 | fmt.Sprintf("invalid character '%c' after top-level value", src[cursor]), 157 | cursor+1, 158 | ) 159 | } 160 | } 161 | 162 | //nolint:staticcheck 163 | //go:nosplit 164 | func noescape(p unsafe.Pointer) unsafe.Pointer { 165 | x := uintptr(p) 166 | return unsafe.Pointer(x ^ 0) 167 | } 168 | 169 | func validateType(typ *runtime.Type, p uintptr) error { 170 | if typ == nil || typ.Kind() != reflect.Ptr || p == 0 { 171 | return &InvalidUnmarshalError{Type: runtime.RType2Type(typ)} 172 | } 173 | return nil 174 | } 175 | 176 | // NewDecoder returns a new decoder that reads from r. 177 | // 178 | // The decoder introduces its own buffering and may 179 | // read data from r beyond the JSON values requested. 180 | func NewDecoder(r io.Reader) *Decoder { 181 | s := decoder.NewStream(r) 182 | return &Decoder{ 183 | s: s, 184 | } 185 | } 186 | 187 | // Buffered returns a reader of the data remaining in the Decoder's 188 | // buffer. The reader is valid until the next call to Decode. 189 | func (d *Decoder) Buffered() io.Reader { 190 | return d.s.Buffered() 191 | } 192 | 193 | // Decode reads the next JSON-encoded value from its 194 | // input and stores it in the value pointed to by v. 195 | // 196 | // See the documentation for Unmarshal for details about 197 | // the conversion of JSON into a Go value. 198 | func (d *Decoder) Decode(v interface{}) error { 199 | return d.DecodeWithOption(v) 200 | } 201 | 202 | // DecodeContext reads the next JSON-encoded value from its 203 | // input and stores it in the value pointed to by v with context.Context. 204 | func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error { 205 | d.s.Option.Flags |= decoder.ContextOption 206 | d.s.Option.Context = ctx 207 | return d.DecodeWithOption(v) 208 | } 209 | 210 | func (d *Decoder) DecodeWithOption(v interface{}, optFuncs ...DecodeOptionFunc) error { 211 | header := (*emptyInterface)(unsafe.Pointer(&v)) 212 | typ := header.typ 213 | ptr := uintptr(header.ptr) 214 | typeptr := uintptr(unsafe.Pointer(typ)) 215 | // noescape trick for header.typ ( reflect.*rtype ) 216 | copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr)) 217 | 218 | if err := validateType(copiedType, ptr); err != nil { 219 | return err 220 | } 221 | 222 | dec, err := decoder.CompileToGetDecoder(typ) 223 | if err != nil { 224 | return err 225 | } 226 | if err := d.s.PrepareForDecode(); err != nil { 227 | return err 228 | } 229 | s := d.s 230 | for _, optFunc := range optFuncs { 231 | optFunc(s.Option) 232 | } 233 | if err := dec.DecodeStream(s, 0, header.ptr); err != nil { 234 | return err 235 | } 236 | s.Reset() 237 | return nil 238 | } 239 | 240 | func (d *Decoder) More() bool { 241 | return d.s.More() 242 | } 243 | 244 | func (d *Decoder) Token() (Token, error) { 245 | return d.s.Token() 246 | } 247 | 248 | // DisallowUnknownFields causes the Decoder to return an error when the destination 249 | // is a struct and the input contains object keys which do not match any 250 | // non-ignored, exported fields in the destination. 251 | func (d *Decoder) DisallowUnknownFields() { 252 | d.s.DisallowUnknownFields = true 253 | } 254 | 255 | func (d *Decoder) InputOffset() int64 { 256 | return d.s.TotalOffset() 257 | } 258 | 259 | // UseNumber causes the Decoder to unmarshal a number into an interface{} as a 260 | // Number instead of as a float64. 261 | func (d *Decoder) UseNumber() { 262 | d.s.UseNumber = true 263 | } 264 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | go-json: 4 | image: golang:1.18 5 | volumes: 6 | - '.:/go/src/go-json' 7 | deploy: 8 | resources: 9 | limits: 10 | memory: 620M 11 | working_dir: /go/src/go-json 12 | command: | 13 | sh -c "go test -c . && ls go-json.test" 14 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "github.com/goccy/go-json/internal/errors" 5 | ) 6 | 7 | // Before Go 1.2, an InvalidUTF8Error was returned by Marshal when 8 | // attempting to encode a string value with invalid UTF-8 sequences. 9 | // As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by 10 | // replacing invalid bytes with the Unicode replacement rune U+FFFD. 11 | // 12 | // Deprecated: No longer used; kept for compatibility. 13 | type InvalidUTF8Error = errors.InvalidUTF8Error 14 | 15 | // An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. 16 | // (The argument to Unmarshal must be a non-nil pointer.) 17 | type InvalidUnmarshalError = errors.InvalidUnmarshalError 18 | 19 | // A MarshalerError represents an error from calling a MarshalJSON or MarshalText method. 20 | type MarshalerError = errors.MarshalerError 21 | 22 | // A SyntaxError is a description of a JSON syntax error. 23 | type SyntaxError = errors.SyntaxError 24 | 25 | // An UnmarshalFieldError describes a JSON object key that 26 | // led to an unexported (and therefore unwritable) struct field. 27 | // 28 | // Deprecated: No longer used; kept for compatibility. 29 | type UnmarshalFieldError = errors.UnmarshalFieldError 30 | 31 | // An UnmarshalTypeError describes a JSON value that was 32 | // not appropriate for a value of a specific Go type. 33 | type UnmarshalTypeError = errors.UnmarshalTypeError 34 | 35 | // An UnsupportedTypeError is returned by Marshal when attempting 36 | // to encode an unsupported value type. 37 | type UnsupportedTypeError = errors.UnsupportedTypeError 38 | 39 | type UnsupportedValueError = errors.UnsupportedValueError 40 | 41 | type PathError = errors.PathError 42 | -------------------------------------------------------------------------------- /export_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "github.com/goccy/go-json/internal/errors" 5 | ) 6 | 7 | var ( 8 | NewSyntaxError = errors.ErrSyntax 9 | NewMarshalerError = errors.ErrMarshaler 10 | ) 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/goccy/go-json 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/goccy/go-json/9872089c316cfe2d0f29b331b75d45bf6d522d96/go.sum -------------------------------------------------------------------------------- /helper_test.go: -------------------------------------------------------------------------------- 1 | package json_test 2 | 3 | import "testing" 4 | 5 | func assertErr(t *testing.T, err error) { 6 | t.Helper() 7 | if err != nil { 8 | t.Fatalf("%+v", err) 9 | } 10 | } 11 | 12 | func assertEq(t *testing.T, msg string, exp interface{}, act interface{}) { 13 | t.Helper() 14 | if exp != act { 15 | t.Fatalf("failed to test for %s. exp=[%v] but act=[%v]", msg, exp, act) 16 | } 17 | } 18 | 19 | func assertNeq(t *testing.T, msg string, exp interface{}, act interface{}) { 20 | t.Helper() 21 | if exp == act { 22 | t.Fatalf("failed to test for %s. expected value is not [%v] but got same value", msg, act) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /internal/decoder/anonymous_field.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "github.com/goccy/go-json/internal/runtime" 7 | ) 8 | 9 | type anonymousFieldDecoder struct { 10 | structType *runtime.Type 11 | offset uintptr 12 | dec Decoder 13 | } 14 | 15 | func newAnonymousFieldDecoder(structType *runtime.Type, offset uintptr, dec Decoder) *anonymousFieldDecoder { 16 | return &anonymousFieldDecoder{ 17 | structType: structType, 18 | offset: offset, 19 | dec: dec, 20 | } 21 | } 22 | 23 | func (d *anonymousFieldDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 24 | if *(*unsafe.Pointer)(p) == nil { 25 | *(*unsafe.Pointer)(p) = unsafe_New(d.structType) 26 | } 27 | p = *(*unsafe.Pointer)(p) 28 | return d.dec.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+d.offset)) 29 | } 30 | 31 | func (d *anonymousFieldDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 32 | if *(*unsafe.Pointer)(p) == nil { 33 | *(*unsafe.Pointer)(p) = unsafe_New(d.structType) 34 | } 35 | p = *(*unsafe.Pointer)(p) 36 | return d.dec.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+d.offset)) 37 | } 38 | 39 | func (d *anonymousFieldDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 40 | return d.dec.DecodePath(ctx, cursor, depth) 41 | } 42 | -------------------------------------------------------------------------------- /internal/decoder/array.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | 7 | "github.com/goccy/go-json/internal/errors" 8 | "github.com/goccy/go-json/internal/runtime" 9 | ) 10 | 11 | type arrayDecoder struct { 12 | elemType *runtime.Type 13 | size uintptr 14 | valueDecoder Decoder 15 | alen int 16 | structName string 17 | fieldName string 18 | zeroValue unsafe.Pointer 19 | } 20 | 21 | func newArrayDecoder(dec Decoder, elemType *runtime.Type, alen int, structName, fieldName string) *arrayDecoder { 22 | // workaround to avoid checkptr errors. cannot use `*(*unsafe.Pointer)(unsafe_New(elemType))` directly. 23 | zeroValuePtr := unsafe_New(elemType) 24 | zeroValue := **(**unsafe.Pointer)(unsafe.Pointer(&zeroValuePtr)) 25 | return &arrayDecoder{ 26 | valueDecoder: dec, 27 | elemType: elemType, 28 | size: elemType.Size(), 29 | alen: alen, 30 | structName: structName, 31 | fieldName: fieldName, 32 | zeroValue: zeroValue, 33 | } 34 | } 35 | 36 | func (d *arrayDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 37 | depth++ 38 | if depth > maxDecodeNestingDepth { 39 | return errors.ErrExceededMaxDepth(s.char(), s.cursor) 40 | } 41 | 42 | for { 43 | switch s.char() { 44 | case ' ', '\n', '\t', '\r': 45 | case 'n': 46 | if err := nullBytes(s); err != nil { 47 | return err 48 | } 49 | return nil 50 | case '[': 51 | idx := 0 52 | s.cursor++ 53 | if s.skipWhiteSpace() == ']' { 54 | for idx < d.alen { 55 | *(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue 56 | idx++ 57 | } 58 | s.cursor++ 59 | return nil 60 | } 61 | for { 62 | if idx < d.alen { 63 | if err := d.valueDecoder.DecodeStream(s, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size)); err != nil { 64 | return err 65 | } 66 | } else { 67 | if err := s.skipValue(depth); err != nil { 68 | return err 69 | } 70 | } 71 | idx++ 72 | switch s.skipWhiteSpace() { 73 | case ']': 74 | for idx < d.alen { 75 | *(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue 76 | idx++ 77 | } 78 | s.cursor++ 79 | return nil 80 | case ',': 81 | s.cursor++ 82 | continue 83 | case nul: 84 | if s.read() { 85 | s.cursor++ 86 | continue 87 | } 88 | goto ERROR 89 | default: 90 | goto ERROR 91 | } 92 | } 93 | case nul: 94 | if s.read() { 95 | continue 96 | } 97 | goto ERROR 98 | default: 99 | goto ERROR 100 | } 101 | s.cursor++ 102 | } 103 | ERROR: 104 | return errors.ErrUnexpectedEndOfJSON("array", s.totalOffset()) 105 | } 106 | 107 | func (d *arrayDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 108 | buf := ctx.Buf 109 | depth++ 110 | if depth > maxDecodeNestingDepth { 111 | return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 112 | } 113 | 114 | for { 115 | switch buf[cursor] { 116 | case ' ', '\n', '\t', '\r': 117 | cursor++ 118 | continue 119 | case 'n': 120 | if err := validateNull(buf, cursor); err != nil { 121 | return 0, err 122 | } 123 | cursor += 4 124 | return cursor, nil 125 | case '[': 126 | idx := 0 127 | cursor++ 128 | cursor = skipWhiteSpace(buf, cursor) 129 | if buf[cursor] == ']' { 130 | for idx < d.alen { 131 | *(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue 132 | idx++ 133 | } 134 | cursor++ 135 | return cursor, nil 136 | } 137 | for { 138 | if idx < d.alen { 139 | c, err := d.valueDecoder.Decode(ctx, cursor, depth, unsafe.Pointer(uintptr(p)+uintptr(idx)*d.size)) 140 | if err != nil { 141 | return 0, err 142 | } 143 | cursor = c 144 | } else { 145 | c, err := skipValue(buf, cursor, depth) 146 | if err != nil { 147 | return 0, err 148 | } 149 | cursor = c 150 | } 151 | idx++ 152 | cursor = skipWhiteSpace(buf, cursor) 153 | switch buf[cursor] { 154 | case ']': 155 | for idx < d.alen { 156 | *(*unsafe.Pointer)(unsafe.Pointer(uintptr(p) + uintptr(idx)*d.size)) = d.zeroValue 157 | idx++ 158 | } 159 | cursor++ 160 | return cursor, nil 161 | case ',': 162 | cursor++ 163 | continue 164 | default: 165 | return 0, errors.ErrInvalidCharacter(buf[cursor], "array", cursor) 166 | } 167 | } 168 | default: 169 | return 0, errors.ErrUnexpectedEndOfJSON("array", cursor) 170 | } 171 | } 172 | } 173 | 174 | func (d *arrayDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 175 | return nil, 0, fmt.Errorf("json: array decoder does not support decode path") 176 | } 177 | -------------------------------------------------------------------------------- /internal/decoder/bool.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | 7 | "github.com/goccy/go-json/internal/errors" 8 | ) 9 | 10 | type boolDecoder struct { 11 | structName string 12 | fieldName string 13 | } 14 | 15 | func newBoolDecoder(structName, fieldName string) *boolDecoder { 16 | return &boolDecoder{structName: structName, fieldName: fieldName} 17 | } 18 | 19 | func (d *boolDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 20 | c := s.skipWhiteSpace() 21 | for { 22 | switch c { 23 | case 't': 24 | if err := trueBytes(s); err != nil { 25 | return err 26 | } 27 | **(**bool)(unsafe.Pointer(&p)) = true 28 | return nil 29 | case 'f': 30 | if err := falseBytes(s); err != nil { 31 | return err 32 | } 33 | **(**bool)(unsafe.Pointer(&p)) = false 34 | return nil 35 | case 'n': 36 | if err := nullBytes(s); err != nil { 37 | return err 38 | } 39 | return nil 40 | case nul: 41 | if s.read() { 42 | c = s.char() 43 | continue 44 | } 45 | goto ERROR 46 | } 47 | break 48 | } 49 | ERROR: 50 | return errors.ErrUnexpectedEndOfJSON("bool", s.totalOffset()) 51 | } 52 | 53 | func (d *boolDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 54 | buf := ctx.Buf 55 | cursor = skipWhiteSpace(buf, cursor) 56 | switch buf[cursor] { 57 | case 't': 58 | if err := validateTrue(buf, cursor); err != nil { 59 | return 0, err 60 | } 61 | cursor += 4 62 | **(**bool)(unsafe.Pointer(&p)) = true 63 | return cursor, nil 64 | case 'f': 65 | if err := validateFalse(buf, cursor); err != nil { 66 | return 0, err 67 | } 68 | cursor += 5 69 | **(**bool)(unsafe.Pointer(&p)) = false 70 | return cursor, nil 71 | case 'n': 72 | if err := validateNull(buf, cursor); err != nil { 73 | return 0, err 74 | } 75 | cursor += 4 76 | return cursor, nil 77 | } 78 | return 0, errors.ErrUnexpectedEndOfJSON("bool", cursor) 79 | } 80 | 81 | func (d *boolDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 82 | return nil, 0, fmt.Errorf("json: bool decoder does not support decode path") 83 | } 84 | -------------------------------------------------------------------------------- /internal/decoder/bytes.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/errors" 9 | "github.com/goccy/go-json/internal/runtime" 10 | ) 11 | 12 | type bytesDecoder struct { 13 | typ *runtime.Type 14 | sliceDecoder Decoder 15 | stringDecoder *stringDecoder 16 | structName string 17 | fieldName string 18 | } 19 | 20 | func byteUnmarshalerSliceDecoder(typ *runtime.Type, structName string, fieldName string) Decoder { 21 | var unmarshalDecoder Decoder 22 | switch { 23 | case runtime.PtrTo(typ).Implements(unmarshalJSONType): 24 | unmarshalDecoder = newUnmarshalJSONDecoder(runtime.PtrTo(typ), structName, fieldName) 25 | case runtime.PtrTo(typ).Implements(unmarshalTextType): 26 | unmarshalDecoder = newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName) 27 | default: 28 | unmarshalDecoder, _ = compileUint8(typ, structName, fieldName) 29 | } 30 | return newSliceDecoder(unmarshalDecoder, typ, 1, structName, fieldName) 31 | } 32 | 33 | func newBytesDecoder(typ *runtime.Type, structName string, fieldName string) *bytesDecoder { 34 | return &bytesDecoder{ 35 | typ: typ, 36 | sliceDecoder: byteUnmarshalerSliceDecoder(typ, structName, fieldName), 37 | stringDecoder: newStringDecoder(structName, fieldName), 38 | structName: structName, 39 | fieldName: fieldName, 40 | } 41 | } 42 | 43 | func (d *bytesDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 44 | bytes, err := d.decodeStreamBinary(s, depth, p) 45 | if err != nil { 46 | return err 47 | } 48 | if bytes == nil { 49 | s.reset() 50 | return nil 51 | } 52 | decodedLen := base64.StdEncoding.DecodedLen(len(bytes)) 53 | buf := make([]byte, decodedLen) 54 | n, err := base64.StdEncoding.Decode(buf, bytes) 55 | if err != nil { 56 | return err 57 | } 58 | *(*[]byte)(p) = buf[:n] 59 | s.reset() 60 | return nil 61 | } 62 | 63 | func (d *bytesDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 64 | bytes, c, err := d.decodeBinary(ctx, cursor, depth, p) 65 | if err != nil { 66 | return 0, err 67 | } 68 | if bytes == nil { 69 | return c, nil 70 | } 71 | cursor = c 72 | decodedLen := base64.StdEncoding.DecodedLen(len(bytes)) 73 | b := make([]byte, decodedLen) 74 | n, err := base64.StdEncoding.Decode(b, bytes) 75 | if err != nil { 76 | return 0, err 77 | } 78 | *(*[]byte)(p) = b[:n] 79 | return cursor, nil 80 | } 81 | 82 | func (d *bytesDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 83 | return nil, 0, fmt.Errorf("json: []byte decoder does not support decode path") 84 | } 85 | 86 | func (d *bytesDecoder) decodeStreamBinary(s *Stream, depth int64, p unsafe.Pointer) ([]byte, error) { 87 | c := s.skipWhiteSpace() 88 | if c == '[' { 89 | if d.sliceDecoder == nil { 90 | return nil, &errors.UnmarshalTypeError{ 91 | Type: runtime.RType2Type(d.typ), 92 | Offset: s.totalOffset(), 93 | } 94 | } 95 | err := d.sliceDecoder.DecodeStream(s, depth, p) 96 | return nil, err 97 | } 98 | return d.stringDecoder.decodeStreamByte(s) 99 | } 100 | 101 | func (d *bytesDecoder) decodeBinary(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) ([]byte, int64, error) { 102 | buf := ctx.Buf 103 | cursor = skipWhiteSpace(buf, cursor) 104 | if buf[cursor] == '[' { 105 | if d.sliceDecoder == nil { 106 | return nil, 0, &errors.UnmarshalTypeError{ 107 | Type: runtime.RType2Type(d.typ), 108 | Offset: cursor, 109 | } 110 | } 111 | c, err := d.sliceDecoder.Decode(ctx, cursor, depth, p) 112 | if err != nil { 113 | return nil, 0, err 114 | } 115 | return nil, c, nil 116 | } 117 | return d.stringDecoder.decodeByte(buf, cursor) 118 | } 119 | -------------------------------------------------------------------------------- /internal/decoder/compile_norace.go: -------------------------------------------------------------------------------- 1 | //go:build !race 2 | // +build !race 3 | 4 | package decoder 5 | 6 | import ( 7 | "unsafe" 8 | 9 | "github.com/goccy/go-json/internal/runtime" 10 | ) 11 | 12 | func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) { 13 | initDecoder() 14 | typeptr := uintptr(unsafe.Pointer(typ)) 15 | if typeptr > typeAddr.MaxTypeAddr { 16 | return compileToGetDecoderSlowPath(typeptr, typ) 17 | } 18 | 19 | index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift 20 | if dec := cachedDecoder[index]; dec != nil { 21 | return dec, nil 22 | } 23 | 24 | dec, err := compileHead(typ, map[uintptr]Decoder{}) 25 | if err != nil { 26 | return nil, err 27 | } 28 | cachedDecoder[index] = dec 29 | return dec, nil 30 | } 31 | -------------------------------------------------------------------------------- /internal/decoder/compile_race.go: -------------------------------------------------------------------------------- 1 | //go:build race 2 | // +build race 3 | 4 | package decoder 5 | 6 | import ( 7 | "sync" 8 | "unsafe" 9 | 10 | "github.com/goccy/go-json/internal/runtime" 11 | ) 12 | 13 | var decMu sync.RWMutex 14 | 15 | func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) { 16 | initDecoder() 17 | typeptr := uintptr(unsafe.Pointer(typ)) 18 | if typeptr > typeAddr.MaxTypeAddr { 19 | return compileToGetDecoderSlowPath(typeptr, typ) 20 | } 21 | 22 | index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift 23 | decMu.RLock() 24 | if dec := cachedDecoder[index]; dec != nil { 25 | decMu.RUnlock() 26 | return dec, nil 27 | } 28 | decMu.RUnlock() 29 | 30 | dec, err := compileHead(typ, map[uintptr]Decoder{}) 31 | if err != nil { 32 | return nil, err 33 | } 34 | decMu.Lock() 35 | cachedDecoder[index] = dec 36 | decMu.Unlock() 37 | return dec, nil 38 | } 39 | -------------------------------------------------------------------------------- /internal/decoder/context.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "sync" 5 | "unsafe" 6 | 7 | "github.com/goccy/go-json/internal/errors" 8 | ) 9 | 10 | type RuntimeContext struct { 11 | Buf []byte 12 | Option *Option 13 | } 14 | 15 | var ( 16 | runtimeContextPool = sync.Pool{ 17 | New: func() interface{} { 18 | return &RuntimeContext{ 19 | Option: &Option{}, 20 | } 21 | }, 22 | } 23 | ) 24 | 25 | func TakeRuntimeContext() *RuntimeContext { 26 | return runtimeContextPool.Get().(*RuntimeContext) 27 | } 28 | 29 | func ReleaseRuntimeContext(ctx *RuntimeContext) { 30 | runtimeContextPool.Put(ctx) 31 | } 32 | 33 | var ( 34 | isWhiteSpace = [256]bool{} 35 | ) 36 | 37 | func init() { 38 | isWhiteSpace[' '] = true 39 | isWhiteSpace['\n'] = true 40 | isWhiteSpace['\t'] = true 41 | isWhiteSpace['\r'] = true 42 | } 43 | 44 | func char(ptr unsafe.Pointer, offset int64) byte { 45 | return *(*byte)(unsafe.Pointer(uintptr(ptr) + uintptr(offset))) 46 | } 47 | 48 | func skipWhiteSpace(buf []byte, cursor int64) int64 { 49 | for isWhiteSpace[buf[cursor]] { 50 | cursor++ 51 | } 52 | return cursor 53 | } 54 | 55 | func skipObject(buf []byte, cursor, depth int64) (int64, error) { 56 | braceCount := 1 57 | for { 58 | switch buf[cursor] { 59 | case '{': 60 | braceCount++ 61 | depth++ 62 | if depth > maxDecodeNestingDepth { 63 | return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 64 | } 65 | case '}': 66 | depth-- 67 | braceCount-- 68 | if braceCount == 0 { 69 | return cursor + 1, nil 70 | } 71 | case '[': 72 | depth++ 73 | if depth > maxDecodeNestingDepth { 74 | return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 75 | } 76 | case ']': 77 | depth-- 78 | case '"': 79 | for { 80 | cursor++ 81 | switch buf[cursor] { 82 | case '\\': 83 | cursor++ 84 | if buf[cursor] == nul { 85 | return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor) 86 | } 87 | case '"': 88 | goto SWITCH_OUT 89 | case nul: 90 | return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor) 91 | } 92 | } 93 | case nul: 94 | return 0, errors.ErrUnexpectedEndOfJSON("object of object", cursor) 95 | } 96 | SWITCH_OUT: 97 | cursor++ 98 | } 99 | } 100 | 101 | func skipArray(buf []byte, cursor, depth int64) (int64, error) { 102 | bracketCount := 1 103 | for { 104 | switch buf[cursor] { 105 | case '[': 106 | bracketCount++ 107 | depth++ 108 | if depth > maxDecodeNestingDepth { 109 | return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 110 | } 111 | case ']': 112 | bracketCount-- 113 | depth-- 114 | if bracketCount == 0 { 115 | return cursor + 1, nil 116 | } 117 | case '{': 118 | depth++ 119 | if depth > maxDecodeNestingDepth { 120 | return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 121 | } 122 | case '}': 123 | depth-- 124 | case '"': 125 | for { 126 | cursor++ 127 | switch buf[cursor] { 128 | case '\\': 129 | cursor++ 130 | if buf[cursor] == nul { 131 | return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor) 132 | } 133 | case '"': 134 | goto SWITCH_OUT 135 | case nul: 136 | return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor) 137 | } 138 | } 139 | case nul: 140 | return 0, errors.ErrUnexpectedEndOfJSON("array of object", cursor) 141 | } 142 | SWITCH_OUT: 143 | cursor++ 144 | } 145 | } 146 | 147 | func skipValue(buf []byte, cursor, depth int64) (int64, error) { 148 | for { 149 | switch buf[cursor] { 150 | case ' ', '\t', '\n', '\r': 151 | cursor++ 152 | continue 153 | case '{': 154 | return skipObject(buf, cursor+1, depth+1) 155 | case '[': 156 | return skipArray(buf, cursor+1, depth+1) 157 | case '"': 158 | for { 159 | cursor++ 160 | switch buf[cursor] { 161 | case '\\': 162 | cursor++ 163 | if buf[cursor] == nul { 164 | return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor) 165 | } 166 | case '"': 167 | return cursor + 1, nil 168 | case nul: 169 | return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor) 170 | } 171 | } 172 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 173 | for { 174 | cursor++ 175 | if floatTable[buf[cursor]] { 176 | continue 177 | } 178 | break 179 | } 180 | return cursor, nil 181 | case 't': 182 | if err := validateTrue(buf, cursor); err != nil { 183 | return 0, err 184 | } 185 | cursor += 4 186 | return cursor, nil 187 | case 'f': 188 | if err := validateFalse(buf, cursor); err != nil { 189 | return 0, err 190 | } 191 | cursor += 5 192 | return cursor, nil 193 | case 'n': 194 | if err := validateNull(buf, cursor); err != nil { 195 | return 0, err 196 | } 197 | cursor += 4 198 | return cursor, nil 199 | default: 200 | return cursor, errors.ErrUnexpectedEndOfJSON("null", cursor) 201 | } 202 | } 203 | } 204 | 205 | func validateTrue(buf []byte, cursor int64) error { 206 | if cursor+3 >= int64(len(buf)) { 207 | return errors.ErrUnexpectedEndOfJSON("true", cursor) 208 | } 209 | if buf[cursor+1] != 'r' { 210 | return errors.ErrInvalidCharacter(buf[cursor+1], "true", cursor) 211 | } 212 | if buf[cursor+2] != 'u' { 213 | return errors.ErrInvalidCharacter(buf[cursor+2], "true", cursor) 214 | } 215 | if buf[cursor+3] != 'e' { 216 | return errors.ErrInvalidCharacter(buf[cursor+3], "true", cursor) 217 | } 218 | return nil 219 | } 220 | 221 | func validateFalse(buf []byte, cursor int64) error { 222 | if cursor+4 >= int64(len(buf)) { 223 | return errors.ErrUnexpectedEndOfJSON("false", cursor) 224 | } 225 | if buf[cursor+1] != 'a' { 226 | return errors.ErrInvalidCharacter(buf[cursor+1], "false", cursor) 227 | } 228 | if buf[cursor+2] != 'l' { 229 | return errors.ErrInvalidCharacter(buf[cursor+2], "false", cursor) 230 | } 231 | if buf[cursor+3] != 's' { 232 | return errors.ErrInvalidCharacter(buf[cursor+3], "false", cursor) 233 | } 234 | if buf[cursor+4] != 'e' { 235 | return errors.ErrInvalidCharacter(buf[cursor+4], "false", cursor) 236 | } 237 | return nil 238 | } 239 | 240 | func validateNull(buf []byte, cursor int64) error { 241 | if cursor+3 >= int64(len(buf)) { 242 | return errors.ErrUnexpectedEndOfJSON("null", cursor) 243 | } 244 | if buf[cursor+1] != 'u' { 245 | return errors.ErrInvalidCharacter(buf[cursor+1], "null", cursor) 246 | } 247 | if buf[cursor+2] != 'l' { 248 | return errors.ErrInvalidCharacter(buf[cursor+2], "null", cursor) 249 | } 250 | if buf[cursor+3] != 'l' { 251 | return errors.ErrInvalidCharacter(buf[cursor+3], "null", cursor) 252 | } 253 | return nil 254 | } 255 | -------------------------------------------------------------------------------- /internal/decoder/float.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "strconv" 5 | "unsafe" 6 | 7 | "github.com/goccy/go-json/internal/errors" 8 | ) 9 | 10 | type floatDecoder struct { 11 | op func(unsafe.Pointer, float64) 12 | structName string 13 | fieldName string 14 | } 15 | 16 | func newFloatDecoder(structName, fieldName string, op func(unsafe.Pointer, float64)) *floatDecoder { 17 | return &floatDecoder{op: op, structName: structName, fieldName: fieldName} 18 | } 19 | 20 | var ( 21 | floatTable = [256]bool{ 22 | '0': true, 23 | '1': true, 24 | '2': true, 25 | '3': true, 26 | '4': true, 27 | '5': true, 28 | '6': true, 29 | '7': true, 30 | '8': true, 31 | '9': true, 32 | '.': true, 33 | 'e': true, 34 | 'E': true, 35 | '+': true, 36 | '-': true, 37 | } 38 | 39 | validEndNumberChar = [256]bool{ 40 | nul: true, 41 | ' ': true, 42 | '\t': true, 43 | '\r': true, 44 | '\n': true, 45 | ',': true, 46 | ':': true, 47 | '}': true, 48 | ']': true, 49 | } 50 | ) 51 | 52 | func floatBytes(s *Stream) []byte { 53 | start := s.cursor 54 | for { 55 | s.cursor++ 56 | if floatTable[s.char()] { 57 | continue 58 | } else if s.char() == nul { 59 | if s.read() { 60 | s.cursor-- // for retry current character 61 | continue 62 | } 63 | } 64 | break 65 | } 66 | return s.buf[start:s.cursor] 67 | } 68 | 69 | func (d *floatDecoder) decodeStreamByte(s *Stream) ([]byte, error) { 70 | for { 71 | switch s.char() { 72 | case ' ', '\n', '\t', '\r': 73 | s.cursor++ 74 | continue 75 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 76 | return floatBytes(s), nil 77 | case 'n': 78 | if err := nullBytes(s); err != nil { 79 | return nil, err 80 | } 81 | return nil, nil 82 | case nul: 83 | if s.read() { 84 | continue 85 | } 86 | goto ERROR 87 | default: 88 | goto ERROR 89 | } 90 | } 91 | ERROR: 92 | return nil, errors.ErrUnexpectedEndOfJSON("float", s.totalOffset()) 93 | } 94 | 95 | func (d *floatDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) { 96 | for { 97 | switch buf[cursor] { 98 | case ' ', '\n', '\t', '\r': 99 | cursor++ 100 | continue 101 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 102 | start := cursor 103 | cursor++ 104 | for floatTable[buf[cursor]] { 105 | cursor++ 106 | } 107 | num := buf[start:cursor] 108 | return num, cursor, nil 109 | case 'n': 110 | if err := validateNull(buf, cursor); err != nil { 111 | return nil, 0, err 112 | } 113 | cursor += 4 114 | return nil, cursor, nil 115 | default: 116 | return nil, 0, errors.ErrUnexpectedEndOfJSON("float", cursor) 117 | } 118 | } 119 | } 120 | 121 | func (d *floatDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 122 | bytes, err := d.decodeStreamByte(s) 123 | if err != nil { 124 | return err 125 | } 126 | if bytes == nil { 127 | return nil 128 | } 129 | str := *(*string)(unsafe.Pointer(&bytes)) 130 | f64, err := strconv.ParseFloat(str, 64) 131 | if err != nil { 132 | return errors.ErrSyntax(err.Error(), s.totalOffset()) 133 | } 134 | d.op(p, f64) 135 | return nil 136 | } 137 | 138 | func (d *floatDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 139 | buf := ctx.Buf 140 | bytes, c, err := d.decodeByte(buf, cursor) 141 | if err != nil { 142 | return 0, err 143 | } 144 | if bytes == nil { 145 | return c, nil 146 | } 147 | cursor = c 148 | if !validEndNumberChar[buf[cursor]] { 149 | return 0, errors.ErrUnexpectedEndOfJSON("float", cursor) 150 | } 151 | s := *(*string)(unsafe.Pointer(&bytes)) 152 | f64, err := strconv.ParseFloat(s, 64) 153 | if err != nil { 154 | return 0, errors.ErrSyntax(err.Error(), cursor) 155 | } 156 | d.op(p, f64) 157 | return cursor, nil 158 | } 159 | 160 | func (d *floatDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 161 | buf := ctx.Buf 162 | bytes, c, err := d.decodeByte(buf, cursor) 163 | if err != nil { 164 | return nil, 0, err 165 | } 166 | if bytes == nil { 167 | return [][]byte{nullbytes}, c, nil 168 | } 169 | return [][]byte{bytes}, c, nil 170 | } 171 | -------------------------------------------------------------------------------- /internal/decoder/func.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/errors" 9 | "github.com/goccy/go-json/internal/runtime" 10 | ) 11 | 12 | type funcDecoder struct { 13 | typ *runtime.Type 14 | structName string 15 | fieldName string 16 | } 17 | 18 | func newFuncDecoder(typ *runtime.Type, structName, fieldName string) *funcDecoder { 19 | fnDecoder := &funcDecoder{typ, structName, fieldName} 20 | return fnDecoder 21 | } 22 | 23 | func (d *funcDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 24 | s.skipWhiteSpace() 25 | start := s.cursor 26 | if err := s.skipValue(depth); err != nil { 27 | return err 28 | } 29 | src := s.buf[start:s.cursor] 30 | if len(src) > 0 { 31 | switch src[0] { 32 | case '"': 33 | return &errors.UnmarshalTypeError{ 34 | Value: "string", 35 | Type: runtime.RType2Type(d.typ), 36 | Offset: s.totalOffset(), 37 | } 38 | case '[': 39 | return &errors.UnmarshalTypeError{ 40 | Value: "array", 41 | Type: runtime.RType2Type(d.typ), 42 | Offset: s.totalOffset(), 43 | } 44 | case '{': 45 | return &errors.UnmarshalTypeError{ 46 | Value: "object", 47 | Type: runtime.RType2Type(d.typ), 48 | Offset: s.totalOffset(), 49 | } 50 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 51 | return &errors.UnmarshalTypeError{ 52 | Value: "number", 53 | Type: runtime.RType2Type(d.typ), 54 | Offset: s.totalOffset(), 55 | } 56 | case 'n': 57 | if err := nullBytes(s); err != nil { 58 | return err 59 | } 60 | *(*unsafe.Pointer)(p) = nil 61 | return nil 62 | case 't': 63 | if err := trueBytes(s); err == nil { 64 | return &errors.UnmarshalTypeError{ 65 | Value: "boolean", 66 | Type: runtime.RType2Type(d.typ), 67 | Offset: s.totalOffset(), 68 | } 69 | } 70 | case 'f': 71 | if err := falseBytes(s); err == nil { 72 | return &errors.UnmarshalTypeError{ 73 | Value: "boolean", 74 | Type: runtime.RType2Type(d.typ), 75 | Offset: s.totalOffset(), 76 | } 77 | } 78 | } 79 | } 80 | return errors.ErrInvalidBeginningOfValue(s.buf[s.cursor], s.totalOffset()) 81 | } 82 | 83 | func (d *funcDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 84 | buf := ctx.Buf 85 | cursor = skipWhiteSpace(buf, cursor) 86 | start := cursor 87 | end, err := skipValue(buf, cursor, depth) 88 | if err != nil { 89 | return 0, err 90 | } 91 | src := buf[start:end] 92 | if len(src) > 0 { 93 | switch src[0] { 94 | case '"': 95 | return 0, &errors.UnmarshalTypeError{ 96 | Value: "string", 97 | Type: runtime.RType2Type(d.typ), 98 | Offset: start, 99 | } 100 | case '[': 101 | return 0, &errors.UnmarshalTypeError{ 102 | Value: "array", 103 | Type: runtime.RType2Type(d.typ), 104 | Offset: start, 105 | } 106 | case '{': 107 | return 0, &errors.UnmarshalTypeError{ 108 | Value: "object", 109 | Type: runtime.RType2Type(d.typ), 110 | Offset: start, 111 | } 112 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 113 | return 0, &errors.UnmarshalTypeError{ 114 | Value: "number", 115 | Type: runtime.RType2Type(d.typ), 116 | Offset: start, 117 | } 118 | case 'n': 119 | if bytes.Equal(src, nullbytes) { 120 | *(*unsafe.Pointer)(p) = nil 121 | return end, nil 122 | } 123 | case 't': 124 | if err := validateTrue(buf, start); err == nil { 125 | return 0, &errors.UnmarshalTypeError{ 126 | Value: "boolean", 127 | Type: runtime.RType2Type(d.typ), 128 | Offset: start, 129 | } 130 | } 131 | case 'f': 132 | if err := validateFalse(buf, start); err == nil { 133 | return 0, &errors.UnmarshalTypeError{ 134 | Value: "boolean", 135 | Type: runtime.RType2Type(d.typ), 136 | Offset: start, 137 | } 138 | } 139 | } 140 | } 141 | return cursor, errors.ErrInvalidBeginningOfValue(buf[cursor], cursor) 142 | } 143 | 144 | func (d *funcDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 145 | return nil, 0, fmt.Errorf("json: func decoder does not support decode path") 146 | } 147 | -------------------------------------------------------------------------------- /internal/decoder/int.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/errors" 9 | "github.com/goccy/go-json/internal/runtime" 10 | ) 11 | 12 | type intDecoder struct { 13 | typ *runtime.Type 14 | kind reflect.Kind 15 | op func(unsafe.Pointer, int64) 16 | structName string 17 | fieldName string 18 | } 19 | 20 | func newIntDecoder(typ *runtime.Type, structName, fieldName string, op func(unsafe.Pointer, int64)) *intDecoder { 21 | return &intDecoder{ 22 | typ: typ, 23 | kind: typ.Kind(), 24 | op: op, 25 | structName: structName, 26 | fieldName: fieldName, 27 | } 28 | } 29 | 30 | func (d *intDecoder) typeError(buf []byte, offset int64) *errors.UnmarshalTypeError { 31 | return &errors.UnmarshalTypeError{ 32 | Value: fmt.Sprintf("number %s", string(buf)), 33 | Type: runtime.RType2Type(d.typ), 34 | Struct: d.structName, 35 | Field: d.fieldName, 36 | Offset: offset, 37 | } 38 | } 39 | 40 | var ( 41 | pow10i64 = [...]int64{ 42 | 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, 43 | 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 44 | } 45 | pow10i64Len = len(pow10i64) 46 | ) 47 | 48 | func (d *intDecoder) parseInt(b []byte) (int64, error) { 49 | isNegative := false 50 | if b[0] == '-' { 51 | b = b[1:] 52 | isNegative = true 53 | } 54 | maxDigit := len(b) 55 | if maxDigit > pow10i64Len { 56 | return 0, fmt.Errorf("invalid length of number") 57 | } 58 | sum := int64(0) 59 | for i := 0; i < maxDigit; i++ { 60 | c := int64(b[i]) - 48 61 | digitValue := pow10i64[maxDigit-i-1] 62 | sum += c * digitValue 63 | } 64 | if isNegative { 65 | return -1 * sum, nil 66 | } 67 | return sum, nil 68 | } 69 | 70 | var ( 71 | numTable = [256]bool{ 72 | '0': true, 73 | '1': true, 74 | '2': true, 75 | '3': true, 76 | '4': true, 77 | '5': true, 78 | '6': true, 79 | '7': true, 80 | '8': true, 81 | '9': true, 82 | } 83 | ) 84 | 85 | var ( 86 | numZeroBuf = []byte{'0'} 87 | ) 88 | 89 | func (d *intDecoder) decodeStreamByte(s *Stream) ([]byte, error) { 90 | for { 91 | switch s.char() { 92 | case ' ', '\n', '\t', '\r': 93 | s.cursor++ 94 | continue 95 | case '-': 96 | start := s.cursor 97 | for { 98 | s.cursor++ 99 | if numTable[s.char()] { 100 | continue 101 | } else if s.char() == nul { 102 | if s.read() { 103 | s.cursor-- // for retry current character 104 | continue 105 | } 106 | } 107 | break 108 | } 109 | num := s.buf[start:s.cursor] 110 | if len(num) < 2 { 111 | goto ERROR 112 | } 113 | return num, nil 114 | case '0': 115 | s.cursor++ 116 | return numZeroBuf, nil 117 | case '1', '2', '3', '4', '5', '6', '7', '8', '9': 118 | start := s.cursor 119 | for { 120 | s.cursor++ 121 | if numTable[s.char()] { 122 | continue 123 | } else if s.char() == nul { 124 | if s.read() { 125 | s.cursor-- // for retry current character 126 | continue 127 | } 128 | } 129 | break 130 | } 131 | num := s.buf[start:s.cursor] 132 | return num, nil 133 | case 'n': 134 | if err := nullBytes(s); err != nil { 135 | return nil, err 136 | } 137 | return nil, nil 138 | case nul: 139 | if s.read() { 140 | continue 141 | } 142 | goto ERROR 143 | default: 144 | return nil, d.typeError([]byte{s.char()}, s.totalOffset()) 145 | } 146 | } 147 | ERROR: 148 | return nil, errors.ErrUnexpectedEndOfJSON("number(integer)", s.totalOffset()) 149 | } 150 | 151 | func (d *intDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) { 152 | b := (*sliceHeader)(unsafe.Pointer(&buf)).data 153 | for { 154 | switch char(b, cursor) { 155 | case ' ', '\n', '\t', '\r': 156 | cursor++ 157 | continue 158 | case '0': 159 | cursor++ 160 | return numZeroBuf, cursor, nil 161 | case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9': 162 | start := cursor 163 | cursor++ 164 | for numTable[char(b, cursor)] { 165 | cursor++ 166 | } 167 | num := buf[start:cursor] 168 | return num, cursor, nil 169 | case 'n': 170 | if err := validateNull(buf, cursor); err != nil { 171 | return nil, 0, err 172 | } 173 | cursor += 4 174 | return nil, cursor, nil 175 | default: 176 | return nil, 0, d.typeError([]byte{char(b, cursor)}, cursor) 177 | } 178 | } 179 | } 180 | 181 | func (d *intDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 182 | bytes, err := d.decodeStreamByte(s) 183 | if err != nil { 184 | return err 185 | } 186 | if bytes == nil { 187 | return nil 188 | } 189 | i64, err := d.parseInt(bytes) 190 | if err != nil { 191 | return d.typeError(bytes, s.totalOffset()) 192 | } 193 | switch d.kind { 194 | case reflect.Int8: 195 | if i64 < -1*(1<<7) || (1<<7) <= i64 { 196 | return d.typeError(bytes, s.totalOffset()) 197 | } 198 | case reflect.Int16: 199 | if i64 < -1*(1<<15) || (1<<15) <= i64 { 200 | return d.typeError(bytes, s.totalOffset()) 201 | } 202 | case reflect.Int32: 203 | if i64 < -1*(1<<31) || (1<<31) <= i64 { 204 | return d.typeError(bytes, s.totalOffset()) 205 | } 206 | } 207 | d.op(p, i64) 208 | s.reset() 209 | return nil 210 | } 211 | 212 | func (d *intDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 213 | bytes, c, err := d.decodeByte(ctx.Buf, cursor) 214 | if err != nil { 215 | return 0, err 216 | } 217 | if bytes == nil { 218 | return c, nil 219 | } 220 | cursor = c 221 | 222 | i64, err := d.parseInt(bytes) 223 | if err != nil { 224 | return 0, d.typeError(bytes, cursor) 225 | } 226 | switch d.kind { 227 | case reflect.Int8: 228 | if i64 < -1*(1<<7) || (1<<7) <= i64 { 229 | return 0, d.typeError(bytes, cursor) 230 | } 231 | case reflect.Int16: 232 | if i64 < -1*(1<<15) || (1<<15) <= i64 { 233 | return 0, d.typeError(bytes, cursor) 234 | } 235 | case reflect.Int32: 236 | if i64 < -1*(1<<31) || (1<<31) <= i64 { 237 | return 0, d.typeError(bytes, cursor) 238 | } 239 | } 240 | d.op(p, i64) 241 | return cursor, nil 242 | } 243 | 244 | func (d *intDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 245 | return nil, 0, fmt.Errorf("json: int decoder does not support decode path") 246 | } 247 | -------------------------------------------------------------------------------- /internal/decoder/invalid.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | 7 | "github.com/goccy/go-json/internal/errors" 8 | "github.com/goccy/go-json/internal/runtime" 9 | ) 10 | 11 | type invalidDecoder struct { 12 | typ *runtime.Type 13 | kind reflect.Kind 14 | structName string 15 | fieldName string 16 | } 17 | 18 | func newInvalidDecoder(typ *runtime.Type, structName, fieldName string) *invalidDecoder { 19 | return &invalidDecoder{ 20 | typ: typ, 21 | kind: typ.Kind(), 22 | structName: structName, 23 | fieldName: fieldName, 24 | } 25 | } 26 | 27 | func (d *invalidDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 28 | return &errors.UnmarshalTypeError{ 29 | Value: "object", 30 | Type: runtime.RType2Type(d.typ), 31 | Offset: s.totalOffset(), 32 | Struct: d.structName, 33 | Field: d.fieldName, 34 | } 35 | } 36 | 37 | func (d *invalidDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 38 | return 0, &errors.UnmarshalTypeError{ 39 | Value: "object", 40 | Type: runtime.RType2Type(d.typ), 41 | Offset: cursor, 42 | Struct: d.structName, 43 | Field: d.fieldName, 44 | } 45 | } 46 | 47 | func (d *invalidDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 48 | return nil, 0, &errors.UnmarshalTypeError{ 49 | Value: "object", 50 | Type: runtime.RType2Type(d.typ), 51 | Offset: cursor, 52 | Struct: d.structName, 53 | Field: d.fieldName, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /internal/decoder/map.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | 7 | "github.com/goccy/go-json/internal/errors" 8 | "github.com/goccy/go-json/internal/runtime" 9 | ) 10 | 11 | type mapDecoder struct { 12 | mapType *runtime.Type 13 | keyType *runtime.Type 14 | valueType *runtime.Type 15 | canUseAssignFaststrType bool 16 | keyDecoder Decoder 17 | valueDecoder Decoder 18 | structName string 19 | fieldName string 20 | } 21 | 22 | func newMapDecoder(mapType *runtime.Type, keyType *runtime.Type, keyDec Decoder, valueType *runtime.Type, valueDec Decoder, structName, fieldName string) *mapDecoder { 23 | return &mapDecoder{ 24 | mapType: mapType, 25 | keyDecoder: keyDec, 26 | keyType: keyType, 27 | canUseAssignFaststrType: canUseAssignFaststrType(keyType, valueType), 28 | valueType: valueType, 29 | valueDecoder: valueDec, 30 | structName: structName, 31 | fieldName: fieldName, 32 | } 33 | } 34 | 35 | const ( 36 | mapMaxElemSize = 128 37 | ) 38 | 39 | // See detail: https://github.com/goccy/go-json/pull/283 40 | func canUseAssignFaststrType(key *runtime.Type, value *runtime.Type) bool { 41 | indirectElem := value.Size() > mapMaxElemSize 42 | if indirectElem { 43 | return false 44 | } 45 | return key.Kind() == reflect.String 46 | } 47 | 48 | //go:linkname makemap reflect.makemap 49 | func makemap(*runtime.Type, int) unsafe.Pointer 50 | 51 | //nolint:golint 52 | //go:linkname mapassign_faststr runtime.mapassign_faststr 53 | //go:noescape 54 | func mapassign_faststr(t *runtime.Type, m unsafe.Pointer, s string) unsafe.Pointer 55 | 56 | //go:linkname mapassign reflect.mapassign 57 | //go:noescape 58 | func mapassign(t *runtime.Type, m unsafe.Pointer, k, v unsafe.Pointer) 59 | 60 | func (d *mapDecoder) mapassign(t *runtime.Type, m, k, v unsafe.Pointer) { 61 | if d.canUseAssignFaststrType { 62 | mapV := mapassign_faststr(t, m, *(*string)(k)) 63 | typedmemmove(d.valueType, mapV, v) 64 | } else { 65 | mapassign(t, m, k, v) 66 | } 67 | } 68 | 69 | func (d *mapDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 70 | depth++ 71 | if depth > maxDecodeNestingDepth { 72 | return errors.ErrExceededMaxDepth(s.char(), s.cursor) 73 | } 74 | 75 | switch s.skipWhiteSpace() { 76 | case 'n': 77 | if err := nullBytes(s); err != nil { 78 | return err 79 | } 80 | **(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil 81 | return nil 82 | case '{': 83 | default: 84 | return errors.ErrExpected("{ character for map value", s.totalOffset()) 85 | } 86 | mapValue := *(*unsafe.Pointer)(p) 87 | if mapValue == nil { 88 | mapValue = makemap(d.mapType, 0) 89 | } 90 | s.cursor++ 91 | if s.skipWhiteSpace() == '}' { 92 | *(*unsafe.Pointer)(p) = mapValue 93 | s.cursor++ 94 | return nil 95 | } 96 | for { 97 | k := unsafe_New(d.keyType) 98 | if err := d.keyDecoder.DecodeStream(s, depth, k); err != nil { 99 | return err 100 | } 101 | s.skipWhiteSpace() 102 | if !s.equalChar(':') { 103 | return errors.ErrExpected("colon after object key", s.totalOffset()) 104 | } 105 | s.cursor++ 106 | v := unsafe_New(d.valueType) 107 | if err := d.valueDecoder.DecodeStream(s, depth, v); err != nil { 108 | return err 109 | } 110 | d.mapassign(d.mapType, mapValue, k, v) 111 | s.skipWhiteSpace() 112 | if s.equalChar('}') { 113 | **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue 114 | s.cursor++ 115 | return nil 116 | } 117 | if !s.equalChar(',') { 118 | return errors.ErrExpected("comma after object value", s.totalOffset()) 119 | } 120 | s.cursor++ 121 | } 122 | } 123 | 124 | func (d *mapDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 125 | buf := ctx.Buf 126 | depth++ 127 | if depth > maxDecodeNestingDepth { 128 | return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 129 | } 130 | 131 | cursor = skipWhiteSpace(buf, cursor) 132 | buflen := int64(len(buf)) 133 | if buflen < 2 { 134 | return 0, errors.ErrExpected("{} for map", cursor) 135 | } 136 | switch buf[cursor] { 137 | case 'n': 138 | if err := validateNull(buf, cursor); err != nil { 139 | return 0, err 140 | } 141 | cursor += 4 142 | **(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil 143 | return cursor, nil 144 | case '{': 145 | default: 146 | return 0, errors.ErrExpected("{ character for map value", cursor) 147 | } 148 | cursor++ 149 | cursor = skipWhiteSpace(buf, cursor) 150 | mapValue := *(*unsafe.Pointer)(p) 151 | if mapValue == nil { 152 | mapValue = makemap(d.mapType, 0) 153 | } 154 | if buf[cursor] == '}' { 155 | **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue 156 | cursor++ 157 | return cursor, nil 158 | } 159 | for { 160 | k := unsafe_New(d.keyType) 161 | keyCursor, err := d.keyDecoder.Decode(ctx, cursor, depth, k) 162 | if err != nil { 163 | return 0, err 164 | } 165 | cursor = skipWhiteSpace(buf, keyCursor) 166 | if buf[cursor] != ':' { 167 | return 0, errors.ErrExpected("colon after object key", cursor) 168 | } 169 | cursor++ 170 | v := unsafe_New(d.valueType) 171 | valueCursor, err := d.valueDecoder.Decode(ctx, cursor, depth, v) 172 | if err != nil { 173 | return 0, err 174 | } 175 | d.mapassign(d.mapType, mapValue, k, v) 176 | cursor = skipWhiteSpace(buf, valueCursor) 177 | if buf[cursor] == '}' { 178 | **(**unsafe.Pointer)(unsafe.Pointer(&p)) = mapValue 179 | cursor++ 180 | return cursor, nil 181 | } 182 | if buf[cursor] != ',' { 183 | return 0, errors.ErrExpected("comma after object value", cursor) 184 | } 185 | cursor++ 186 | } 187 | } 188 | 189 | func (d *mapDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 190 | buf := ctx.Buf 191 | depth++ 192 | if depth > maxDecodeNestingDepth { 193 | return nil, 0, errors.ErrExceededMaxDepth(buf[cursor], cursor) 194 | } 195 | 196 | cursor = skipWhiteSpace(buf, cursor) 197 | buflen := int64(len(buf)) 198 | if buflen < 2 { 199 | return nil, 0, errors.ErrExpected("{} for map", cursor) 200 | } 201 | switch buf[cursor] { 202 | case 'n': 203 | if err := validateNull(buf, cursor); err != nil { 204 | return nil, 0, err 205 | } 206 | cursor += 4 207 | return [][]byte{nullbytes}, cursor, nil 208 | case '{': 209 | default: 210 | return nil, 0, errors.ErrExpected("{ character for map value", cursor) 211 | } 212 | cursor++ 213 | cursor = skipWhiteSpace(buf, cursor) 214 | if buf[cursor] == '}' { 215 | cursor++ 216 | return nil, cursor, nil 217 | } 218 | keyDecoder, ok := d.keyDecoder.(*stringDecoder) 219 | if !ok { 220 | return nil, 0, &errors.UnmarshalTypeError{ 221 | Value: "string", 222 | Type: reflect.TypeOf(""), 223 | Offset: cursor, 224 | Struct: d.structName, 225 | Field: d.fieldName, 226 | } 227 | } 228 | ret := [][]byte{} 229 | for { 230 | key, keyCursor, err := keyDecoder.decodeByte(buf, cursor) 231 | if err != nil { 232 | return nil, 0, err 233 | } 234 | cursor = skipWhiteSpace(buf, keyCursor) 235 | if buf[cursor] != ':' { 236 | return nil, 0, errors.ErrExpected("colon after object key", cursor) 237 | } 238 | cursor++ 239 | child, found, err := ctx.Option.Path.Field(string(key)) 240 | if err != nil { 241 | return nil, 0, err 242 | } 243 | if found { 244 | if child != nil { 245 | oldPath := ctx.Option.Path.node 246 | ctx.Option.Path.node = child 247 | paths, c, err := d.valueDecoder.DecodePath(ctx, cursor, depth) 248 | if err != nil { 249 | return nil, 0, err 250 | } 251 | ctx.Option.Path.node = oldPath 252 | ret = append(ret, paths...) 253 | cursor = c 254 | } else { 255 | start := cursor 256 | end, err := skipValue(buf, cursor, depth) 257 | if err != nil { 258 | return nil, 0, err 259 | } 260 | ret = append(ret, buf[start:end]) 261 | cursor = end 262 | } 263 | } else { 264 | c, err := skipValue(buf, cursor, depth) 265 | if err != nil { 266 | return nil, 0, err 267 | } 268 | cursor = c 269 | } 270 | cursor = skipWhiteSpace(buf, cursor) 271 | if buf[cursor] == '}' { 272 | cursor++ 273 | return ret, cursor, nil 274 | } 275 | if buf[cursor] != ',' { 276 | return nil, 0, errors.ErrExpected("comma after object value", cursor) 277 | } 278 | cursor++ 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /internal/decoder/number.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "encoding/json" 5 | "strconv" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/errors" 9 | ) 10 | 11 | type numberDecoder struct { 12 | stringDecoder *stringDecoder 13 | op func(unsafe.Pointer, json.Number) 14 | structName string 15 | fieldName string 16 | } 17 | 18 | func newNumberDecoder(structName, fieldName string, op func(unsafe.Pointer, json.Number)) *numberDecoder { 19 | return &numberDecoder{ 20 | stringDecoder: newStringDecoder(structName, fieldName), 21 | op: op, 22 | structName: structName, 23 | fieldName: fieldName, 24 | } 25 | } 26 | 27 | func (d *numberDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 28 | bytes, err := d.decodeStreamByte(s) 29 | if err != nil { 30 | return err 31 | } 32 | if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil { 33 | return errors.ErrSyntax(err.Error(), s.totalOffset()) 34 | } 35 | d.op(p, json.Number(string(bytes))) 36 | s.reset() 37 | return nil 38 | } 39 | 40 | func (d *numberDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 41 | bytes, c, err := d.decodeByte(ctx.Buf, cursor) 42 | if err != nil { 43 | return 0, err 44 | } 45 | if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&bytes)), 64); err != nil { 46 | return 0, errors.ErrSyntax(err.Error(), c) 47 | } 48 | cursor = c 49 | s := *(*string)(unsafe.Pointer(&bytes)) 50 | d.op(p, json.Number(s)) 51 | return cursor, nil 52 | } 53 | 54 | func (d *numberDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 55 | bytes, c, err := d.decodeByte(ctx.Buf, cursor) 56 | if err != nil { 57 | return nil, 0, err 58 | } 59 | if bytes == nil { 60 | return [][]byte{nullbytes}, c, nil 61 | } 62 | return [][]byte{bytes}, c, nil 63 | } 64 | 65 | func (d *numberDecoder) decodeStreamByte(s *Stream) ([]byte, error) { 66 | start := s.cursor 67 | for { 68 | switch s.char() { 69 | case ' ', '\n', '\t', '\r': 70 | s.cursor++ 71 | continue 72 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 73 | return floatBytes(s), nil 74 | case 'n': 75 | if err := nullBytes(s); err != nil { 76 | return nil, err 77 | } 78 | return nil, nil 79 | case '"': 80 | return d.stringDecoder.decodeStreamByte(s) 81 | case nul: 82 | if s.read() { 83 | continue 84 | } 85 | goto ERROR 86 | default: 87 | goto ERROR 88 | } 89 | } 90 | ERROR: 91 | if s.cursor == start { 92 | return nil, errors.ErrInvalidBeginningOfValue(s.char(), s.totalOffset()) 93 | } 94 | return nil, errors.ErrUnexpectedEndOfJSON("json.Number", s.totalOffset()) 95 | } 96 | 97 | func (d *numberDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) { 98 | for { 99 | switch buf[cursor] { 100 | case ' ', '\n', '\t', '\r': 101 | cursor++ 102 | continue 103 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 104 | start := cursor 105 | cursor++ 106 | for floatTable[buf[cursor]] { 107 | cursor++ 108 | } 109 | num := buf[start:cursor] 110 | return num, cursor, nil 111 | case 'n': 112 | if err := validateNull(buf, cursor); err != nil { 113 | return nil, 0, err 114 | } 115 | cursor += 4 116 | return nil, cursor, nil 117 | case '"': 118 | return d.stringDecoder.decodeByte(buf, cursor) 119 | default: 120 | return nil, 0, errors.ErrUnexpectedEndOfJSON("json.Number", cursor) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /internal/decoder/option.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import "context" 4 | 5 | type OptionFlags uint8 6 | 7 | const ( 8 | FirstWinOption OptionFlags = 1 << iota 9 | ContextOption 10 | PathOption 11 | ) 12 | 13 | type Option struct { 14 | Flags OptionFlags 15 | Context context.Context 16 | Path *Path 17 | } 18 | -------------------------------------------------------------------------------- /internal/decoder/ptr.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | 7 | "github.com/goccy/go-json/internal/runtime" 8 | ) 9 | 10 | type ptrDecoder struct { 11 | dec Decoder 12 | typ *runtime.Type 13 | structName string 14 | fieldName string 15 | } 16 | 17 | func newPtrDecoder(dec Decoder, typ *runtime.Type, structName, fieldName string) *ptrDecoder { 18 | return &ptrDecoder{ 19 | dec: dec, 20 | typ: typ, 21 | structName: structName, 22 | fieldName: fieldName, 23 | } 24 | } 25 | 26 | func (d *ptrDecoder) contentDecoder() Decoder { 27 | dec, ok := d.dec.(*ptrDecoder) 28 | if !ok { 29 | return d.dec 30 | } 31 | return dec.contentDecoder() 32 | } 33 | 34 | //nolint:golint 35 | //go:linkname unsafe_New reflect.unsafe_New 36 | func unsafe_New(*runtime.Type) unsafe.Pointer 37 | 38 | func UnsafeNew(t *runtime.Type) unsafe.Pointer { 39 | return unsafe_New(t) 40 | } 41 | 42 | func (d *ptrDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 43 | if s.skipWhiteSpace() == nul { 44 | s.read() 45 | } 46 | if s.char() == 'n' { 47 | if err := nullBytes(s); err != nil { 48 | return err 49 | } 50 | *(*unsafe.Pointer)(p) = nil 51 | return nil 52 | } 53 | var newptr unsafe.Pointer 54 | if *(*unsafe.Pointer)(p) == nil { 55 | newptr = unsafe_New(d.typ) 56 | *(*unsafe.Pointer)(p) = newptr 57 | } else { 58 | newptr = *(*unsafe.Pointer)(p) 59 | } 60 | if err := d.dec.DecodeStream(s, depth, newptr); err != nil { 61 | return err 62 | } 63 | return nil 64 | } 65 | 66 | func (d *ptrDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 67 | buf := ctx.Buf 68 | cursor = skipWhiteSpace(buf, cursor) 69 | if buf[cursor] == 'n' { 70 | if err := validateNull(buf, cursor); err != nil { 71 | return 0, err 72 | } 73 | if p != nil { 74 | *(*unsafe.Pointer)(p) = nil 75 | } 76 | cursor += 4 77 | return cursor, nil 78 | } 79 | var newptr unsafe.Pointer 80 | if *(*unsafe.Pointer)(p) == nil { 81 | newptr = unsafe_New(d.typ) 82 | *(*unsafe.Pointer)(p) = newptr 83 | } else { 84 | newptr = *(*unsafe.Pointer)(p) 85 | } 86 | c, err := d.dec.Decode(ctx, cursor, depth, newptr) 87 | if err != nil { 88 | *(*unsafe.Pointer)(p) = nil 89 | return 0, err 90 | } 91 | cursor = c 92 | return cursor, nil 93 | } 94 | 95 | func (d *ptrDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 96 | return nil, 0, fmt.Errorf("json: ptr decoder does not support decode path") 97 | } 98 | -------------------------------------------------------------------------------- /internal/decoder/type.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "context" 5 | "encoding" 6 | "encoding/json" 7 | "reflect" 8 | "unsafe" 9 | ) 10 | 11 | type Decoder interface { 12 | Decode(*RuntimeContext, int64, int64, unsafe.Pointer) (int64, error) 13 | DecodePath(*RuntimeContext, int64, int64) ([][]byte, int64, error) 14 | DecodeStream(*Stream, int64, unsafe.Pointer) error 15 | } 16 | 17 | const ( 18 | nul = '\000' 19 | maxDecodeNestingDepth = 10000 20 | ) 21 | 22 | type unmarshalerContext interface { 23 | UnmarshalJSON(context.Context, []byte) error 24 | } 25 | 26 | var ( 27 | unmarshalJSONType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() 28 | unmarshalJSONContextType = reflect.TypeOf((*unmarshalerContext)(nil)).Elem() 29 | unmarshalTextType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() 30 | ) 31 | -------------------------------------------------------------------------------- /internal/decoder/uint.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/errors" 9 | "github.com/goccy/go-json/internal/runtime" 10 | ) 11 | 12 | type uintDecoder struct { 13 | typ *runtime.Type 14 | kind reflect.Kind 15 | op func(unsafe.Pointer, uint64) 16 | structName string 17 | fieldName string 18 | } 19 | 20 | func newUintDecoder(typ *runtime.Type, structName, fieldName string, op func(unsafe.Pointer, uint64)) *uintDecoder { 21 | return &uintDecoder{ 22 | typ: typ, 23 | kind: typ.Kind(), 24 | op: op, 25 | structName: structName, 26 | fieldName: fieldName, 27 | } 28 | } 29 | 30 | func (d *uintDecoder) typeError(buf []byte, offset int64) *errors.UnmarshalTypeError { 31 | return &errors.UnmarshalTypeError{ 32 | Value: fmt.Sprintf("number %s", string(buf)), 33 | Type: runtime.RType2Type(d.typ), 34 | Offset: offset, 35 | } 36 | } 37 | 38 | var ( 39 | pow10u64 = [...]uint64{ 40 | 1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09, 41 | 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 42 | } 43 | pow10u64Len = len(pow10u64) 44 | ) 45 | 46 | func (d *uintDecoder) parseUint(b []byte) (uint64, error) { 47 | maxDigit := len(b) 48 | if maxDigit > pow10u64Len { 49 | return 0, fmt.Errorf("invalid length of number") 50 | } 51 | sum := uint64(0) 52 | for i := 0; i < maxDigit; i++ { 53 | c := uint64(b[i]) - 48 54 | digitValue := pow10u64[maxDigit-i-1] 55 | sum += c * digitValue 56 | } 57 | return sum, nil 58 | } 59 | 60 | func (d *uintDecoder) decodeStreamByte(s *Stream) ([]byte, error) { 61 | for { 62 | switch s.char() { 63 | case ' ', '\n', '\t', '\r': 64 | s.cursor++ 65 | continue 66 | case '0': 67 | s.cursor++ 68 | return numZeroBuf, nil 69 | case '1', '2', '3', '4', '5', '6', '7', '8', '9': 70 | start := s.cursor 71 | for { 72 | s.cursor++ 73 | if numTable[s.char()] { 74 | continue 75 | } else if s.char() == nul { 76 | if s.read() { 77 | s.cursor-- // for retry current character 78 | continue 79 | } 80 | } 81 | break 82 | } 83 | num := s.buf[start:s.cursor] 84 | return num, nil 85 | case 'n': 86 | if err := nullBytes(s); err != nil { 87 | return nil, err 88 | } 89 | return nil, nil 90 | case nul: 91 | if s.read() { 92 | continue 93 | } 94 | default: 95 | return nil, d.typeError([]byte{s.char()}, s.totalOffset()) 96 | } 97 | break 98 | } 99 | return nil, errors.ErrUnexpectedEndOfJSON("number(unsigned integer)", s.totalOffset()) 100 | } 101 | 102 | func (d *uintDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) { 103 | for { 104 | switch buf[cursor] { 105 | case ' ', '\n', '\t', '\r': 106 | cursor++ 107 | continue 108 | case '0': 109 | cursor++ 110 | return numZeroBuf, cursor, nil 111 | case '1', '2', '3', '4', '5', '6', '7', '8', '9': 112 | start := cursor 113 | cursor++ 114 | for numTable[buf[cursor]] { 115 | cursor++ 116 | } 117 | num := buf[start:cursor] 118 | return num, cursor, nil 119 | case 'n': 120 | if err := validateNull(buf, cursor); err != nil { 121 | return nil, 0, err 122 | } 123 | cursor += 4 124 | return nil, cursor, nil 125 | default: 126 | return nil, 0, d.typeError([]byte{buf[cursor]}, cursor) 127 | } 128 | } 129 | } 130 | 131 | func (d *uintDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 132 | bytes, err := d.decodeStreamByte(s) 133 | if err != nil { 134 | return err 135 | } 136 | if bytes == nil { 137 | return nil 138 | } 139 | u64, err := d.parseUint(bytes) 140 | if err != nil { 141 | return d.typeError(bytes, s.totalOffset()) 142 | } 143 | switch d.kind { 144 | case reflect.Uint8: 145 | if (1 << 8) <= u64 { 146 | return d.typeError(bytes, s.totalOffset()) 147 | } 148 | case reflect.Uint16: 149 | if (1 << 16) <= u64 { 150 | return d.typeError(bytes, s.totalOffset()) 151 | } 152 | case reflect.Uint32: 153 | if (1 << 32) <= u64 { 154 | return d.typeError(bytes, s.totalOffset()) 155 | } 156 | } 157 | d.op(p, u64) 158 | return nil 159 | } 160 | 161 | func (d *uintDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 162 | bytes, c, err := d.decodeByte(ctx.Buf, cursor) 163 | if err != nil { 164 | return 0, err 165 | } 166 | if bytes == nil { 167 | return c, nil 168 | } 169 | cursor = c 170 | u64, err := d.parseUint(bytes) 171 | if err != nil { 172 | return 0, d.typeError(bytes, cursor) 173 | } 174 | switch d.kind { 175 | case reflect.Uint8: 176 | if (1 << 8) <= u64 { 177 | return 0, d.typeError(bytes, cursor) 178 | } 179 | case reflect.Uint16: 180 | if (1 << 16) <= u64 { 181 | return 0, d.typeError(bytes, cursor) 182 | } 183 | case reflect.Uint32: 184 | if (1 << 32) <= u64 { 185 | return 0, d.typeError(bytes, cursor) 186 | } 187 | } 188 | d.op(p, u64) 189 | return cursor, nil 190 | } 191 | 192 | func (d *uintDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 193 | return nil, 0, fmt.Errorf("json: uint decoder does not support decode path") 194 | } 195 | -------------------------------------------------------------------------------- /internal/decoder/unmarshal_json.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "unsafe" 8 | 9 | "github.com/goccy/go-json/internal/errors" 10 | "github.com/goccy/go-json/internal/runtime" 11 | ) 12 | 13 | type unmarshalJSONDecoder struct { 14 | typ *runtime.Type 15 | structName string 16 | fieldName string 17 | } 18 | 19 | func newUnmarshalJSONDecoder(typ *runtime.Type, structName, fieldName string) *unmarshalJSONDecoder { 20 | return &unmarshalJSONDecoder{ 21 | typ: typ, 22 | structName: structName, 23 | fieldName: fieldName, 24 | } 25 | } 26 | 27 | func (d *unmarshalJSONDecoder) annotateError(cursor int64, err error) { 28 | switch e := err.(type) { 29 | case *errors.UnmarshalTypeError: 30 | e.Struct = d.structName 31 | e.Field = d.fieldName 32 | case *errors.SyntaxError: 33 | e.Offset = cursor 34 | } 35 | } 36 | 37 | func (d *unmarshalJSONDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 38 | s.skipWhiteSpace() 39 | start := s.cursor 40 | if err := s.skipValue(depth); err != nil { 41 | return err 42 | } 43 | src := s.buf[start:s.cursor] 44 | dst := make([]byte, len(src)) 45 | copy(dst, src) 46 | 47 | v := *(*interface{})(unsafe.Pointer(&emptyInterface{ 48 | typ: d.typ, 49 | ptr: p, 50 | })) 51 | switch v := v.(type) { 52 | case unmarshalerContext: 53 | var ctx context.Context 54 | if (s.Option.Flags & ContextOption) != 0 { 55 | ctx = s.Option.Context 56 | } else { 57 | ctx = context.Background() 58 | } 59 | if err := v.UnmarshalJSON(ctx, dst); err != nil { 60 | d.annotateError(s.cursor, err) 61 | return err 62 | } 63 | case json.Unmarshaler: 64 | if err := v.UnmarshalJSON(dst); err != nil { 65 | d.annotateError(s.cursor, err) 66 | return err 67 | } 68 | } 69 | return nil 70 | } 71 | 72 | func (d *unmarshalJSONDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 73 | buf := ctx.Buf 74 | cursor = skipWhiteSpace(buf, cursor) 75 | start := cursor 76 | end, err := skipValue(buf, cursor, depth) 77 | if err != nil { 78 | return 0, err 79 | } 80 | src := buf[start:end] 81 | dst := make([]byte, len(src)) 82 | copy(dst, src) 83 | 84 | v := *(*interface{})(unsafe.Pointer(&emptyInterface{ 85 | typ: d.typ, 86 | ptr: p, 87 | })) 88 | if (ctx.Option.Flags & ContextOption) != 0 { 89 | if err := v.(unmarshalerContext).UnmarshalJSON(ctx.Option.Context, dst); err != nil { 90 | d.annotateError(cursor, err) 91 | return 0, err 92 | } 93 | } else { 94 | if err := v.(json.Unmarshaler).UnmarshalJSON(dst); err != nil { 95 | d.annotateError(cursor, err) 96 | return 0, err 97 | } 98 | } 99 | return end, nil 100 | } 101 | 102 | func (d *unmarshalJSONDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 103 | return nil, 0, fmt.Errorf("json: unmarshal json decoder does not support decode path") 104 | } 105 | -------------------------------------------------------------------------------- /internal/decoder/unmarshal_text.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "bytes" 5 | "encoding" 6 | "fmt" 7 | "unicode" 8 | "unicode/utf16" 9 | "unicode/utf8" 10 | "unsafe" 11 | 12 | "github.com/goccy/go-json/internal/errors" 13 | "github.com/goccy/go-json/internal/runtime" 14 | ) 15 | 16 | type unmarshalTextDecoder struct { 17 | typ *runtime.Type 18 | structName string 19 | fieldName string 20 | } 21 | 22 | func newUnmarshalTextDecoder(typ *runtime.Type, structName, fieldName string) *unmarshalTextDecoder { 23 | return &unmarshalTextDecoder{ 24 | typ: typ, 25 | structName: structName, 26 | fieldName: fieldName, 27 | } 28 | } 29 | 30 | func (d *unmarshalTextDecoder) annotateError(cursor int64, err error) { 31 | switch e := err.(type) { 32 | case *errors.UnmarshalTypeError: 33 | e.Struct = d.structName 34 | e.Field = d.fieldName 35 | case *errors.SyntaxError: 36 | e.Offset = cursor 37 | } 38 | } 39 | 40 | var ( 41 | nullbytes = []byte(`null`) 42 | ) 43 | 44 | func (d *unmarshalTextDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 45 | s.skipWhiteSpace() 46 | start := s.cursor 47 | if err := s.skipValue(depth); err != nil { 48 | return err 49 | } 50 | src := s.buf[start:s.cursor] 51 | if len(src) > 0 { 52 | switch src[0] { 53 | case '[': 54 | return &errors.UnmarshalTypeError{ 55 | Value: "array", 56 | Type: runtime.RType2Type(d.typ), 57 | Offset: s.totalOffset(), 58 | } 59 | case '{': 60 | return &errors.UnmarshalTypeError{ 61 | Value: "object", 62 | Type: runtime.RType2Type(d.typ), 63 | Offset: s.totalOffset(), 64 | } 65 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 66 | return &errors.UnmarshalTypeError{ 67 | Value: "number", 68 | Type: runtime.RType2Type(d.typ), 69 | Offset: s.totalOffset(), 70 | } 71 | case 'n': 72 | if bytes.Equal(src, nullbytes) { 73 | *(*unsafe.Pointer)(p) = nil 74 | return nil 75 | } 76 | } 77 | } 78 | dst := make([]byte, len(src)) 79 | copy(dst, src) 80 | 81 | if b, ok := unquoteBytes(dst); ok { 82 | dst = b 83 | } 84 | v := *(*interface{})(unsafe.Pointer(&emptyInterface{ 85 | typ: d.typ, 86 | ptr: p, 87 | })) 88 | if err := v.(encoding.TextUnmarshaler).UnmarshalText(dst); err != nil { 89 | d.annotateError(s.cursor, err) 90 | return err 91 | } 92 | return nil 93 | } 94 | 95 | func (d *unmarshalTextDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 96 | buf := ctx.Buf 97 | cursor = skipWhiteSpace(buf, cursor) 98 | start := cursor 99 | end, err := skipValue(buf, cursor, depth) 100 | if err != nil { 101 | return 0, err 102 | } 103 | src := buf[start:end] 104 | if len(src) > 0 { 105 | switch src[0] { 106 | case '[': 107 | return 0, &errors.UnmarshalTypeError{ 108 | Value: "array", 109 | Type: runtime.RType2Type(d.typ), 110 | Offset: start, 111 | } 112 | case '{': 113 | return 0, &errors.UnmarshalTypeError{ 114 | Value: "object", 115 | Type: runtime.RType2Type(d.typ), 116 | Offset: start, 117 | } 118 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 119 | return 0, &errors.UnmarshalTypeError{ 120 | Value: "number", 121 | Type: runtime.RType2Type(d.typ), 122 | Offset: start, 123 | } 124 | case 'n': 125 | if bytes.Equal(src, nullbytes) { 126 | *(*unsafe.Pointer)(p) = nil 127 | return end, nil 128 | } 129 | } 130 | } 131 | 132 | if s, ok := unquoteBytes(src); ok { 133 | src = s 134 | } 135 | v := *(*interface{})(unsafe.Pointer(&emptyInterface{ 136 | typ: d.typ, 137 | ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)), 138 | })) 139 | if err := v.(encoding.TextUnmarshaler).UnmarshalText(src); err != nil { 140 | d.annotateError(cursor, err) 141 | return 0, err 142 | } 143 | return end, nil 144 | } 145 | 146 | func (d *unmarshalTextDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 147 | return nil, 0, fmt.Errorf("json: unmarshal text decoder does not support decode path") 148 | } 149 | 150 | func unquoteBytes(s []byte) (t []byte, ok bool) { //nolint: nonamedreturns 151 | length := len(s) 152 | if length < 2 || s[0] != '"' || s[length-1] != '"' { 153 | return 154 | } 155 | s = s[1 : length-1] 156 | length -= 2 157 | 158 | // Check for unusual characters. If there are none, 159 | // then no unquoting is needed, so return a slice of the 160 | // original bytes. 161 | r := 0 162 | for r < length { 163 | c := s[r] 164 | if c == '\\' || c == '"' || c < ' ' { 165 | break 166 | } 167 | if c < utf8.RuneSelf { 168 | r++ 169 | continue 170 | } 171 | rr, size := utf8.DecodeRune(s[r:]) 172 | if rr == utf8.RuneError && size == 1 { 173 | break 174 | } 175 | r += size 176 | } 177 | if r == length { 178 | return s, true 179 | } 180 | 181 | b := make([]byte, length+2*utf8.UTFMax) 182 | w := copy(b, s[0:r]) 183 | for r < length { 184 | // Out of room? Can only happen if s is full of 185 | // malformed UTF-8 and we're replacing each 186 | // byte with RuneError. 187 | if w >= len(b)-2*utf8.UTFMax { 188 | nb := make([]byte, (len(b)+utf8.UTFMax)*2) 189 | copy(nb, b[0:w]) 190 | b = nb 191 | } 192 | switch c := s[r]; { 193 | case c == '\\': 194 | r++ 195 | if r >= length { 196 | return 197 | } 198 | switch s[r] { 199 | default: 200 | return 201 | case '"', '\\', '/', '\'': 202 | b[w] = s[r] 203 | r++ 204 | w++ 205 | case 'b': 206 | b[w] = '\b' 207 | r++ 208 | w++ 209 | case 'f': 210 | b[w] = '\f' 211 | r++ 212 | w++ 213 | case 'n': 214 | b[w] = '\n' 215 | r++ 216 | w++ 217 | case 'r': 218 | b[w] = '\r' 219 | r++ 220 | w++ 221 | case 't': 222 | b[w] = '\t' 223 | r++ 224 | w++ 225 | case 'u': 226 | r-- 227 | rr := getu4(s[r:]) 228 | if rr < 0 { 229 | return 230 | } 231 | r += 6 232 | if utf16.IsSurrogate(rr) { 233 | rr1 := getu4(s[r:]) 234 | if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { 235 | // A valid pair; consume. 236 | r += 6 237 | w += utf8.EncodeRune(b[w:], dec) 238 | break 239 | } 240 | // Invalid surrogate; fall back to replacement rune. 241 | rr = unicode.ReplacementChar 242 | } 243 | w += utf8.EncodeRune(b[w:], rr) 244 | } 245 | 246 | // Quote, control characters are invalid. 247 | case c == '"', c < ' ': 248 | return 249 | 250 | // ASCII 251 | case c < utf8.RuneSelf: 252 | b[w] = c 253 | r++ 254 | w++ 255 | 256 | // Coerce to well-formed UTF-8. 257 | default: 258 | rr, size := utf8.DecodeRune(s[r:]) 259 | r += size 260 | w += utf8.EncodeRune(b[w:], rr) 261 | } 262 | } 263 | return b[0:w], true 264 | } 265 | 266 | func getu4(s []byte) rune { 267 | if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { 268 | return -1 269 | } 270 | var r rune 271 | for _, c := range s[2:6] { 272 | switch { 273 | case '0' <= c && c <= '9': 274 | c = c - '0' 275 | case 'a' <= c && c <= 'f': 276 | c = c - 'a' + 10 277 | case 'A' <= c && c <= 'F': 278 | c = c - 'A' + 10 279 | default: 280 | return -1 281 | } 282 | r = r*16 + rune(c) 283 | } 284 | return r 285 | } 286 | -------------------------------------------------------------------------------- /internal/decoder/wrapped_string.go: -------------------------------------------------------------------------------- 1 | package decoder 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/runtime" 9 | ) 10 | 11 | type wrappedStringDecoder struct { 12 | typ *runtime.Type 13 | dec Decoder 14 | stringDecoder *stringDecoder 15 | structName string 16 | fieldName string 17 | isPtrType bool 18 | } 19 | 20 | func newWrappedStringDecoder(typ *runtime.Type, dec Decoder, structName, fieldName string) *wrappedStringDecoder { 21 | return &wrappedStringDecoder{ 22 | typ: typ, 23 | dec: dec, 24 | stringDecoder: newStringDecoder(structName, fieldName), 25 | structName: structName, 26 | fieldName: fieldName, 27 | isPtrType: typ.Kind() == reflect.Ptr, 28 | } 29 | } 30 | 31 | func (d *wrappedStringDecoder) DecodeStream(s *Stream, depth int64, p unsafe.Pointer) error { 32 | bytes, err := d.stringDecoder.decodeStreamByte(s) 33 | if err != nil { 34 | return err 35 | } 36 | if bytes == nil { 37 | if d.isPtrType { 38 | *(*unsafe.Pointer)(p) = nil 39 | } 40 | return nil 41 | } 42 | b := make([]byte, len(bytes)+1) 43 | copy(b, bytes) 44 | if _, err := d.dec.Decode(&RuntimeContext{Buf: b}, 0, depth, p); err != nil { 45 | return err 46 | } 47 | return nil 48 | } 49 | 50 | func (d *wrappedStringDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.Pointer) (int64, error) { 51 | bytes, c, err := d.stringDecoder.decodeByte(ctx.Buf, cursor) 52 | if err != nil { 53 | return 0, err 54 | } 55 | if bytes == nil { 56 | if d.isPtrType { 57 | *(*unsafe.Pointer)(p) = nil 58 | } 59 | return c, nil 60 | } 61 | bytes = append(bytes, nul) 62 | oldBuf := ctx.Buf 63 | ctx.Buf = bytes 64 | if _, err := d.dec.Decode(ctx, 0, depth, p); err != nil { 65 | return 0, err 66 | } 67 | ctx.Buf = oldBuf 68 | return c, nil 69 | } 70 | 71 | func (d *wrappedStringDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int64) ([][]byte, int64, error) { 72 | return nil, 0, fmt.Errorf("json: wrapped string decoder does not support decode path") 73 | } 74 | -------------------------------------------------------------------------------- /internal/encoder/compact.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strconv" 7 | "unsafe" 8 | 9 | "github.com/goccy/go-json/internal/errors" 10 | ) 11 | 12 | var ( 13 | isWhiteSpace = [256]bool{ 14 | ' ': true, 15 | '\n': true, 16 | '\t': true, 17 | '\r': true, 18 | } 19 | isHTMLEscapeChar = [256]bool{ 20 | '<': true, 21 | '>': true, 22 | '&': true, 23 | } 24 | nul = byte('\000') 25 | ) 26 | 27 | func Compact(buf *bytes.Buffer, src []byte, escape bool) error { 28 | if len(src) == 0 { 29 | return errors.ErrUnexpectedEndOfJSON("", 0) 30 | } 31 | buf.Grow(len(src)) 32 | dst := buf.Bytes() 33 | 34 | ctx := TakeRuntimeContext() 35 | ctxBuf := ctx.Buf[:0] 36 | ctxBuf = append(append(ctxBuf, src...), nul) 37 | ctx.Buf = ctxBuf 38 | 39 | if err := compactAndWrite(buf, dst, ctxBuf, escape); err != nil { 40 | ReleaseRuntimeContext(ctx) 41 | return err 42 | } 43 | ReleaseRuntimeContext(ctx) 44 | return nil 45 | } 46 | 47 | func compactAndWrite(buf *bytes.Buffer, dst []byte, src []byte, escape bool) error { 48 | dst, err := compact(dst, src, escape) 49 | if err != nil { 50 | return err 51 | } 52 | if _, err := buf.Write(dst); err != nil { 53 | return err 54 | } 55 | return nil 56 | } 57 | 58 | func compact(dst, src []byte, escape bool) ([]byte, error) { 59 | buf, cursor, err := compactValue(dst, src, 0, escape) 60 | if err != nil { 61 | return nil, err 62 | } 63 | if err := validateEndBuf(src, cursor); err != nil { 64 | return nil, err 65 | } 66 | return buf, nil 67 | } 68 | 69 | func validateEndBuf(src []byte, cursor int64) error { 70 | for { 71 | switch src[cursor] { 72 | case ' ', '\t', '\n', '\r': 73 | cursor++ 74 | continue 75 | case nul: 76 | return nil 77 | } 78 | return errors.ErrSyntax( 79 | fmt.Sprintf("invalid character '%c' after top-level value", src[cursor]), 80 | cursor+1, 81 | ) 82 | } 83 | } 84 | 85 | func skipWhiteSpace(buf []byte, cursor int64) int64 { 86 | LOOP: 87 | if isWhiteSpace[buf[cursor]] { 88 | cursor++ 89 | goto LOOP 90 | } 91 | return cursor 92 | } 93 | 94 | func compactValue(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) { 95 | for { 96 | switch src[cursor] { 97 | case ' ', '\t', '\n', '\r': 98 | cursor++ 99 | continue 100 | case '{': 101 | return compactObject(dst, src, cursor, escape) 102 | case '}': 103 | return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor) 104 | case '[': 105 | return compactArray(dst, src, cursor, escape) 106 | case ']': 107 | return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor) 108 | case '"': 109 | return compactString(dst, src, cursor, escape) 110 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 111 | return compactNumber(dst, src, cursor) 112 | case 't': 113 | return compactTrue(dst, src, cursor) 114 | case 'f': 115 | return compactFalse(dst, src, cursor) 116 | case 'n': 117 | return compactNull(dst, src, cursor) 118 | default: 119 | return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor) 120 | } 121 | } 122 | } 123 | 124 | func compactObject(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) { 125 | if src[cursor] == '{' { 126 | dst = append(dst, '{') 127 | } else { 128 | return nil, 0, errors.ErrExpected("expected { character for object value", cursor) 129 | } 130 | cursor = skipWhiteSpace(src, cursor+1) 131 | if src[cursor] == '}' { 132 | dst = append(dst, '}') 133 | return dst, cursor + 1, nil 134 | } 135 | var err error 136 | for { 137 | cursor = skipWhiteSpace(src, cursor) 138 | dst, cursor, err = compactString(dst, src, cursor, escape) 139 | if err != nil { 140 | return nil, 0, err 141 | } 142 | cursor = skipWhiteSpace(src, cursor) 143 | if src[cursor] != ':' { 144 | return nil, 0, errors.ErrExpected("colon after object key", cursor) 145 | } 146 | dst = append(dst, ':') 147 | dst, cursor, err = compactValue(dst, src, cursor+1, escape) 148 | if err != nil { 149 | return nil, 0, err 150 | } 151 | cursor = skipWhiteSpace(src, cursor) 152 | switch src[cursor] { 153 | case '}': 154 | dst = append(dst, '}') 155 | cursor++ 156 | return dst, cursor, nil 157 | case ',': 158 | dst = append(dst, ',') 159 | default: 160 | return nil, 0, errors.ErrExpected("comma after object value", cursor) 161 | } 162 | cursor++ 163 | } 164 | } 165 | 166 | func compactArray(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) { 167 | if src[cursor] == '[' { 168 | dst = append(dst, '[') 169 | } else { 170 | return nil, 0, errors.ErrExpected("expected [ character for array value", cursor) 171 | } 172 | cursor = skipWhiteSpace(src, cursor+1) 173 | if src[cursor] == ']' { 174 | dst = append(dst, ']') 175 | return dst, cursor + 1, nil 176 | } 177 | var err error 178 | for { 179 | dst, cursor, err = compactValue(dst, src, cursor, escape) 180 | if err != nil { 181 | return nil, 0, err 182 | } 183 | cursor = skipWhiteSpace(src, cursor) 184 | switch src[cursor] { 185 | case ']': 186 | dst = append(dst, ']') 187 | cursor++ 188 | return dst, cursor, nil 189 | case ',': 190 | dst = append(dst, ',') 191 | default: 192 | return nil, 0, errors.ErrExpected("comma after array value", cursor) 193 | } 194 | cursor++ 195 | } 196 | } 197 | 198 | func compactString(dst, src []byte, cursor int64, escape bool) ([]byte, int64, error) { 199 | if src[cursor] != '"' { 200 | return nil, 0, errors.ErrInvalidCharacter(src[cursor], "string", cursor) 201 | } 202 | start := cursor 203 | for { 204 | cursor++ 205 | c := src[cursor] 206 | if escape { 207 | if isHTMLEscapeChar[c] { 208 | dst = append(dst, src[start:cursor]...) 209 | dst = append(dst, `\u00`...) 210 | dst = append(dst, hex[c>>4], hex[c&0xF]) 211 | start = cursor + 1 212 | } else if c == 0xE2 && cursor+2 < int64(len(src)) && src[cursor+1] == 0x80 && src[cursor+2]&^1 == 0xA8 { 213 | dst = append(dst, src[start:cursor]...) 214 | dst = append(dst, `\u202`...) 215 | dst = append(dst, hex[src[cursor+2]&0xF]) 216 | start = cursor + 3 217 | cursor += 2 218 | } 219 | } 220 | switch c { 221 | case '\\': 222 | cursor++ 223 | if src[cursor] == nul { 224 | return nil, 0, errors.ErrUnexpectedEndOfJSON("string", int64(len(src))) 225 | } 226 | case '"': 227 | cursor++ 228 | return append(dst, src[start:cursor]...), cursor, nil 229 | case nul: 230 | return nil, 0, errors.ErrUnexpectedEndOfJSON("string", int64(len(src))) 231 | } 232 | } 233 | } 234 | 235 | func compactNumber(dst, src []byte, cursor int64) ([]byte, int64, error) { 236 | start := cursor 237 | for { 238 | cursor++ 239 | if floatTable[src[cursor]] { 240 | continue 241 | } 242 | break 243 | } 244 | num := src[start:cursor] 245 | if _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&num)), 64); err != nil { 246 | return nil, 0, err 247 | } 248 | dst = append(dst, num...) 249 | return dst, cursor, nil 250 | } 251 | 252 | func compactTrue(dst, src []byte, cursor int64) ([]byte, int64, error) { 253 | if cursor+3 >= int64(len(src)) { 254 | return nil, 0, errors.ErrUnexpectedEndOfJSON("true", cursor) 255 | } 256 | if !bytes.Equal(src[cursor:cursor+4], []byte(`true`)) { 257 | return nil, 0, errors.ErrInvalidCharacter(src[cursor], "true", cursor) 258 | } 259 | dst = append(dst, "true"...) 260 | cursor += 4 261 | return dst, cursor, nil 262 | } 263 | 264 | func compactFalse(dst, src []byte, cursor int64) ([]byte, int64, error) { 265 | if cursor+4 >= int64(len(src)) { 266 | return nil, 0, errors.ErrUnexpectedEndOfJSON("false", cursor) 267 | } 268 | if !bytes.Equal(src[cursor:cursor+5], []byte(`false`)) { 269 | return nil, 0, errors.ErrInvalidCharacter(src[cursor], "false", cursor) 270 | } 271 | dst = append(dst, "false"...) 272 | cursor += 5 273 | return dst, cursor, nil 274 | } 275 | 276 | func compactNull(dst, src []byte, cursor int64) ([]byte, int64, error) { 277 | if cursor+3 >= int64(len(src)) { 278 | return nil, 0, errors.ErrUnexpectedEndOfJSON("null", cursor) 279 | } 280 | if !bytes.Equal(src[cursor:cursor+4], []byte(`null`)) { 281 | return nil, 0, errors.ErrInvalidCharacter(src[cursor], "null", cursor) 282 | } 283 | dst = append(dst, "null"...) 284 | cursor += 4 285 | return dst, cursor, nil 286 | } 287 | -------------------------------------------------------------------------------- /internal/encoder/compiler_norace.go: -------------------------------------------------------------------------------- 1 | //go:build !race 2 | // +build !race 3 | 4 | package encoder 5 | 6 | func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) { 7 | initEncoder() 8 | if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr { 9 | codeSet, err := compileToGetCodeSetSlowPath(typeptr) 10 | if err != nil { 11 | return nil, err 12 | } 13 | return getFilteredCodeSetIfNeeded(ctx, codeSet) 14 | } 15 | index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift 16 | if codeSet := cachedOpcodeSets[index]; codeSet != nil { 17 | filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet) 18 | if err != nil { 19 | return nil, err 20 | } 21 | return filtered, nil 22 | } 23 | codeSet, err := newCompiler().compile(typeptr) 24 | if err != nil { 25 | return nil, err 26 | } 27 | filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet) 28 | if err != nil { 29 | return nil, err 30 | } 31 | cachedOpcodeSets[index] = codeSet 32 | return filtered, nil 33 | } 34 | -------------------------------------------------------------------------------- /internal/encoder/compiler_race.go: -------------------------------------------------------------------------------- 1 | //go:build race 2 | // +build race 3 | 4 | package encoder 5 | 6 | import ( 7 | "sync" 8 | ) 9 | 10 | var setsMu sync.RWMutex 11 | 12 | func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) { 13 | initEncoder() 14 | if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr { 15 | codeSet, err := compileToGetCodeSetSlowPath(typeptr) 16 | if err != nil { 17 | return nil, err 18 | } 19 | return getFilteredCodeSetIfNeeded(ctx, codeSet) 20 | } 21 | index := (typeptr - typeAddr.BaseTypeAddr) >> typeAddr.AddrShift 22 | setsMu.RLock() 23 | if codeSet := cachedOpcodeSets[index]; codeSet != nil { 24 | filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet) 25 | if err != nil { 26 | setsMu.RUnlock() 27 | return nil, err 28 | } 29 | setsMu.RUnlock() 30 | return filtered, nil 31 | } 32 | setsMu.RUnlock() 33 | 34 | codeSet, err := newCompiler().compile(typeptr) 35 | if err != nil { 36 | return nil, err 37 | } 38 | filtered, err := getFilteredCodeSetIfNeeded(ctx, codeSet) 39 | if err != nil { 40 | return nil, err 41 | } 42 | setsMu.Lock() 43 | cachedOpcodeSets[index] = codeSet 44 | setsMu.Unlock() 45 | return filtered, nil 46 | } 47 | -------------------------------------------------------------------------------- /internal/encoder/context.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/runtime" 9 | ) 10 | 11 | type compileContext struct { 12 | opcodeIndex uint32 13 | ptrIndex int 14 | indent uint32 15 | escapeKey bool 16 | structTypeToCodes map[uintptr]Opcodes 17 | recursiveCodes *Opcodes 18 | } 19 | 20 | func (c *compileContext) incIndent() { 21 | c.indent++ 22 | } 23 | 24 | func (c *compileContext) decIndent() { 25 | c.indent-- 26 | } 27 | 28 | func (c *compileContext) incIndex() { 29 | c.incOpcodeIndex() 30 | c.incPtrIndex() 31 | } 32 | 33 | func (c *compileContext) decIndex() { 34 | c.decOpcodeIndex() 35 | c.decPtrIndex() 36 | } 37 | 38 | func (c *compileContext) incOpcodeIndex() { 39 | c.opcodeIndex++ 40 | } 41 | 42 | func (c *compileContext) decOpcodeIndex() { 43 | c.opcodeIndex-- 44 | } 45 | 46 | func (c *compileContext) incPtrIndex() { 47 | c.ptrIndex++ 48 | } 49 | 50 | func (c *compileContext) decPtrIndex() { 51 | c.ptrIndex-- 52 | } 53 | 54 | const ( 55 | bufSize = 1024 56 | ) 57 | 58 | var ( 59 | runtimeContextPool = sync.Pool{ 60 | New: func() interface{} { 61 | return &RuntimeContext{ 62 | Buf: make([]byte, 0, bufSize), 63 | Ptrs: make([]uintptr, 128), 64 | KeepRefs: make([]unsafe.Pointer, 0, 8), 65 | Option: &Option{}, 66 | } 67 | }, 68 | } 69 | ) 70 | 71 | type RuntimeContext struct { 72 | Context context.Context 73 | Buf []byte 74 | MarshalBuf []byte 75 | Ptrs []uintptr 76 | KeepRefs []unsafe.Pointer 77 | SeenPtr []uintptr 78 | BaseIndent uint32 79 | Prefix []byte 80 | IndentStr []byte 81 | Option *Option 82 | } 83 | 84 | func (c *RuntimeContext) Init(p uintptr, codelen int) { 85 | if len(c.Ptrs) < codelen { 86 | c.Ptrs = make([]uintptr, codelen) 87 | } 88 | c.Ptrs[0] = p 89 | c.KeepRefs = c.KeepRefs[:0] 90 | c.SeenPtr = c.SeenPtr[:0] 91 | c.BaseIndent = 0 92 | } 93 | 94 | func (c *RuntimeContext) Ptr() uintptr { 95 | header := (*runtime.SliceHeader)(unsafe.Pointer(&c.Ptrs)) 96 | return uintptr(header.Data) 97 | } 98 | 99 | func TakeRuntimeContext() *RuntimeContext { 100 | return runtimeContextPool.Get().(*RuntimeContext) 101 | } 102 | 103 | func ReleaseRuntimeContext(ctx *RuntimeContext) { 104 | runtimeContextPool.Put(ctx) 105 | } 106 | -------------------------------------------------------------------------------- /internal/encoder/decode_rune.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import "unicode/utf8" 4 | 5 | const ( 6 | // The default lowest and highest continuation byte. 7 | locb = 128 //0b10000000 8 | hicb = 191 //0b10111111 9 | 10 | // These names of these constants are chosen to give nice alignment in the 11 | // table below. The first nibble is an index into acceptRanges or F for 12 | // special one-byte cases. The second nibble is the Rune length or the 13 | // Status for the special one-byte case. 14 | xx = 0xF1 // invalid: size 1 15 | as = 0xF0 // ASCII: size 1 16 | s1 = 0x02 // accept 0, size 2 17 | s2 = 0x13 // accept 1, size 3 18 | s3 = 0x03 // accept 0, size 3 19 | s4 = 0x23 // accept 2, size 3 20 | s5 = 0x34 // accept 3, size 4 21 | s6 = 0x04 // accept 0, size 4 22 | s7 = 0x44 // accept 4, size 4 23 | ) 24 | 25 | // first is information about the first byte in a UTF-8 sequence. 26 | var first = [256]uint8{ 27 | // 1 2 3 4 5 6 7 8 9 A B C D E F 28 | as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F 29 | as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F 30 | as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F 31 | as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F 32 | as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F 33 | as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F 34 | as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F 35 | as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F 36 | // 1 2 3 4 5 6 7 8 9 A B C D E F 37 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F 38 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F 39 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF 40 | xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF 41 | xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF 42 | s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF 43 | s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF 44 | s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF 45 | } 46 | 47 | const ( 48 | lineSep = byte(168) //'\u2028' 49 | paragraphSep = byte(169) //'\u2029' 50 | ) 51 | 52 | type decodeRuneState int 53 | 54 | const ( 55 | validUTF8State decodeRuneState = iota 56 | runeErrorState 57 | lineSepState 58 | paragraphSepState 59 | ) 60 | 61 | func decodeRuneInString(s string) (decodeRuneState, int) { 62 | n := len(s) 63 | s0 := s[0] 64 | x := first[s0] 65 | if x >= as { 66 | // The following code simulates an additional check for x == xx and 67 | // handling the ASCII and invalid cases accordingly. This mask-and-or 68 | // approach prevents an additional branch. 69 | mask := rune(x) << 31 >> 31 // Create 0x0000 or 0xFFFF. 70 | if rune(s[0])&^mask|utf8.RuneError&mask == utf8.RuneError { 71 | return runeErrorState, 1 72 | } 73 | return validUTF8State, 1 74 | } 75 | sz := int(x & 7) 76 | if n < sz { 77 | return runeErrorState, 1 78 | } 79 | s1 := s[1] 80 | switch x >> 4 { 81 | case 0: 82 | if s1 < locb || hicb < s1 { 83 | return runeErrorState, 1 84 | } 85 | case 1: 86 | if s1 < 0xA0 || hicb < s1 { 87 | return runeErrorState, 1 88 | } 89 | case 2: 90 | if s1 < locb || 0x9F < s1 { 91 | return runeErrorState, 1 92 | } 93 | case 3: 94 | if s1 < 0x90 || hicb < s1 { 95 | return runeErrorState, 1 96 | } 97 | case 4: 98 | if s1 < locb || 0x8F < s1 { 99 | return runeErrorState, 1 100 | } 101 | } 102 | if sz <= 2 { 103 | return validUTF8State, 2 104 | } 105 | s2 := s[2] 106 | if s2 < locb || hicb < s2 { 107 | return runeErrorState, 1 108 | } 109 | if sz <= 3 { 110 | // separator character prefixes: [2]byte{226, 128} 111 | if s0 == 226 && s1 == 128 { 112 | switch s2 { 113 | case lineSep: 114 | return lineSepState, 3 115 | case paragraphSep: 116 | return paragraphSepState, 3 117 | } 118 | } 119 | return validUTF8State, 3 120 | } 121 | s3 := s[3] 122 | if s3 < locb || hicb < s3 { 123 | return runeErrorState, 1 124 | } 125 | return validUTF8State, 4 126 | } 127 | -------------------------------------------------------------------------------- /internal/encoder/encode_opcode_test.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import ( 4 | "testing" 5 | "unsafe" 6 | ) 7 | 8 | func TestDumpOpcode(t *testing.T) { 9 | ctx := TakeRuntimeContext() 10 | defer ReleaseRuntimeContext(ctx) 11 | var v interface{} = 1 12 | header := (*emptyInterface)(unsafe.Pointer(&v)) 13 | typ := header.typ 14 | typeptr := uintptr(unsafe.Pointer(typ)) 15 | codeSet, err := CompileToGetCodeSet(ctx, typeptr) 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | codeSet.EscapeKeyCode.Dump() 20 | } 21 | -------------------------------------------------------------------------------- /internal/encoder/indent.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | "github.com/goccy/go-json/internal/errors" 8 | ) 9 | 10 | func takeIndentSrcRuntimeContext(src []byte) (*RuntimeContext, []byte) { 11 | ctx := TakeRuntimeContext() 12 | buf := ctx.Buf[:0] 13 | buf = append(append(buf, src...), nul) 14 | ctx.Buf = buf 15 | return ctx, buf 16 | } 17 | 18 | func Indent(buf *bytes.Buffer, src []byte, prefix, indentStr string) error { 19 | if len(src) == 0 { 20 | return errors.ErrUnexpectedEndOfJSON("", 0) 21 | } 22 | 23 | srcCtx, srcBuf := takeIndentSrcRuntimeContext(src) 24 | dstCtx := TakeRuntimeContext() 25 | dst := dstCtx.Buf[:0] 26 | 27 | dst, err := indentAndWrite(buf, dst, srcBuf, prefix, indentStr) 28 | if err != nil { 29 | ReleaseRuntimeContext(srcCtx) 30 | ReleaseRuntimeContext(dstCtx) 31 | return err 32 | } 33 | dstCtx.Buf = dst 34 | ReleaseRuntimeContext(srcCtx) 35 | ReleaseRuntimeContext(dstCtx) 36 | return nil 37 | } 38 | 39 | func indentAndWrite(buf *bytes.Buffer, dst []byte, src []byte, prefix, indentStr string) ([]byte, error) { 40 | dst, err := doIndent(dst, src, prefix, indentStr, false) 41 | if err != nil { 42 | return nil, err 43 | } 44 | if _, err := buf.Write(dst); err != nil { 45 | return nil, err 46 | } 47 | return dst, nil 48 | } 49 | 50 | func doIndent(dst, src []byte, prefix, indentStr string, escape bool) ([]byte, error) { 51 | buf, cursor, err := indentValue(dst, src, 0, 0, []byte(prefix), []byte(indentStr), escape) 52 | if err != nil { 53 | return nil, err 54 | } 55 | if err := validateEndBuf(src, cursor); err != nil { 56 | return nil, err 57 | } 58 | return buf, nil 59 | } 60 | 61 | func indentValue( 62 | dst []byte, 63 | src []byte, 64 | indentNum int, 65 | cursor int64, 66 | prefix []byte, 67 | indentBytes []byte, 68 | escape bool) ([]byte, int64, error) { 69 | for { 70 | switch src[cursor] { 71 | case ' ', '\t', '\n', '\r': 72 | cursor++ 73 | continue 74 | case '{': 75 | return indentObject(dst, src, indentNum, cursor, prefix, indentBytes, escape) 76 | case '}': 77 | return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor) 78 | case '[': 79 | return indentArray(dst, src, indentNum, cursor, prefix, indentBytes, escape) 80 | case ']': 81 | return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor) 82 | case '"': 83 | return compactString(dst, src, cursor, escape) 84 | case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 85 | return compactNumber(dst, src, cursor) 86 | case 't': 87 | return compactTrue(dst, src, cursor) 88 | case 'f': 89 | return compactFalse(dst, src, cursor) 90 | case 'n': 91 | return compactNull(dst, src, cursor) 92 | default: 93 | return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor) 94 | } 95 | } 96 | } 97 | 98 | func indentObject( 99 | dst []byte, 100 | src []byte, 101 | indentNum int, 102 | cursor int64, 103 | prefix []byte, 104 | indentBytes []byte, 105 | escape bool) ([]byte, int64, error) { 106 | if src[cursor] == '{' { 107 | dst = append(dst, '{') 108 | } else { 109 | return nil, 0, errors.ErrExpected("expected { character for object value", cursor) 110 | } 111 | cursor = skipWhiteSpace(src, cursor+1) 112 | if src[cursor] == '}' { 113 | dst = append(dst, '}') 114 | return dst, cursor + 1, nil 115 | } 116 | indentNum++ 117 | var err error 118 | for { 119 | dst = append(append(dst, '\n'), prefix...) 120 | for i := 0; i < indentNum; i++ { 121 | dst = append(dst, indentBytes...) 122 | } 123 | cursor = skipWhiteSpace(src, cursor) 124 | dst, cursor, err = compactString(dst, src, cursor, escape) 125 | if err != nil { 126 | return nil, 0, err 127 | } 128 | cursor = skipWhiteSpace(src, cursor) 129 | if src[cursor] != ':' { 130 | return nil, 0, errors.ErrSyntax( 131 | fmt.Sprintf("invalid character '%c' after object key", src[cursor]), 132 | cursor+1, 133 | ) 134 | } 135 | dst = append(dst, ':', ' ') 136 | dst, cursor, err = indentValue(dst, src, indentNum, cursor+1, prefix, indentBytes, escape) 137 | if err != nil { 138 | return nil, 0, err 139 | } 140 | cursor = skipWhiteSpace(src, cursor) 141 | switch src[cursor] { 142 | case '}': 143 | dst = append(append(dst, '\n'), prefix...) 144 | for i := 0; i < indentNum-1; i++ { 145 | dst = append(dst, indentBytes...) 146 | } 147 | dst = append(dst, '}') 148 | cursor++ 149 | return dst, cursor, nil 150 | case ',': 151 | dst = append(dst, ',') 152 | default: 153 | return nil, 0, errors.ErrSyntax( 154 | fmt.Sprintf("invalid character '%c' after object key:value pair", src[cursor]), 155 | cursor+1, 156 | ) 157 | } 158 | cursor++ 159 | } 160 | } 161 | 162 | func indentArray( 163 | dst []byte, 164 | src []byte, 165 | indentNum int, 166 | cursor int64, 167 | prefix []byte, 168 | indentBytes []byte, 169 | escape bool) ([]byte, int64, error) { 170 | if src[cursor] == '[' { 171 | dst = append(dst, '[') 172 | } else { 173 | return nil, 0, errors.ErrExpected("expected [ character for array value", cursor) 174 | } 175 | cursor = skipWhiteSpace(src, cursor+1) 176 | if src[cursor] == ']' { 177 | dst = append(dst, ']') 178 | return dst, cursor + 1, nil 179 | } 180 | indentNum++ 181 | var err error 182 | for { 183 | dst = append(append(dst, '\n'), prefix...) 184 | for i := 0; i < indentNum; i++ { 185 | dst = append(dst, indentBytes...) 186 | } 187 | dst, cursor, err = indentValue(dst, src, indentNum, cursor, prefix, indentBytes, escape) 188 | if err != nil { 189 | return nil, 0, err 190 | } 191 | cursor = skipWhiteSpace(src, cursor) 192 | switch src[cursor] { 193 | case ']': 194 | dst = append(append(dst, '\n'), prefix...) 195 | for i := 0; i < indentNum-1; i++ { 196 | dst = append(dst, indentBytes...) 197 | } 198 | dst = append(dst, ']') 199 | cursor++ 200 | return dst, cursor, nil 201 | case ',': 202 | dst = append(dst, ',') 203 | default: 204 | return nil, 0, errors.ErrSyntax( 205 | fmt.Sprintf("invalid character '%c' after array value", src[cursor]), 206 | cursor+1, 207 | ) 208 | } 209 | cursor++ 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /internal/encoder/int.go: -------------------------------------------------------------------------------- 1 | // This files's processing codes are inspired by https://github.com/segmentio/encoding. 2 | // The license notation is as follows. 3 | // 4 | // # MIT License 5 | // 6 | // Copyright (c) 2019 Segment.io, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | package encoder 26 | 27 | import ( 28 | "unsafe" 29 | ) 30 | 31 | var endianness int 32 | 33 | func init() { 34 | var b [2]byte 35 | *(*uint16)(unsafe.Pointer(&b)) = uint16(0xABCD) 36 | 37 | switch b[0] { 38 | case 0xCD: 39 | endianness = 0 // LE 40 | case 0xAB: 41 | endianness = 1 // BE 42 | default: 43 | panic("could not determine endianness") 44 | } 45 | } 46 | 47 | // "00010203...96979899" cast to []uint16 48 | var intLELookup = [100]uint16{ 49 | 0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930, 50 | 0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931, 51 | 0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932, 52 | 0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933, 53 | 0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934, 54 | 0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935, 55 | 0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936, 56 | 0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937, 57 | 0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938, 58 | 0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939, 59 | } 60 | 61 | var intBELookup = [100]uint16{ 62 | 0x3030, 0x3031, 0x3032, 0x3033, 0x3034, 0x3035, 0x3036, 0x3037, 0x3038, 0x3039, 63 | 0x3130, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3138, 0x3139, 64 | 0x3230, 0x3231, 0x3232, 0x3233, 0x3234, 0x3235, 0x3236, 0x3237, 0x3238, 0x3239, 65 | 0x3330, 0x3331, 0x3332, 0x3333, 0x3334, 0x3335, 0x3336, 0x3337, 0x3338, 0x3339, 66 | 0x3430, 0x3431, 0x3432, 0x3433, 0x3434, 0x3435, 0x3436, 0x3437, 0x3438, 0x3439, 67 | 0x3530, 0x3531, 0x3532, 0x3533, 0x3534, 0x3535, 0x3536, 0x3537, 0x3538, 0x3539, 68 | 0x3630, 0x3631, 0x3632, 0x3633, 0x3634, 0x3635, 0x3636, 0x3637, 0x3638, 0x3639, 69 | 0x3730, 0x3731, 0x3732, 0x3733, 0x3734, 0x3735, 0x3736, 0x3737, 0x3738, 0x3739, 70 | 0x3830, 0x3831, 0x3832, 0x3833, 0x3834, 0x3835, 0x3836, 0x3837, 0x3838, 0x3839, 71 | 0x3930, 0x3931, 0x3932, 0x3933, 0x3934, 0x3935, 0x3936, 0x3937, 0x3938, 0x3939, 72 | } 73 | 74 | var intLookup = [2]*[100]uint16{&intLELookup, &intBELookup} 75 | 76 | func numMask(numBitSize uint8) uint64 { 77 | return 1<>(code.NumBitSize-1))&1 == 1 95 | if !negative { 96 | if n < 10 { 97 | return append(out, byte(n+'0')) 98 | } else if n < 100 { 99 | u := intLELookup[n] 100 | return append(out, byte(u), byte(u>>8)) 101 | } 102 | } else { 103 | n = -n & mask 104 | } 105 | 106 | lookup := intLookup[endianness] 107 | 108 | var b [22]byte 109 | u := (*[11]uint16)(unsafe.Pointer(&b)) 110 | i := 11 111 | 112 | for n >= 100 { 113 | j := n % 100 114 | n /= 100 115 | i-- 116 | u[i] = lookup[j] 117 | } 118 | 119 | i-- 120 | u[i] = lookup[n] 121 | 122 | i *= 2 // convert to byte index 123 | if n < 10 { 124 | i++ // remove leading zero 125 | } 126 | if negative { 127 | i-- 128 | b[i] = '-' 129 | } 130 | 131 | return append(out, b[i:]...) 132 | } 133 | 134 | func AppendUint(_ *RuntimeContext, out []byte, p uintptr, code *Opcode) []byte { 135 | var u64 uint64 136 | switch code.NumBitSize { 137 | case 8: 138 | u64 = (uint64)(**(**uint8)(unsafe.Pointer(&p))) 139 | case 16: 140 | u64 = (uint64)(**(**uint16)(unsafe.Pointer(&p))) 141 | case 32: 142 | u64 = (uint64)(**(**uint32)(unsafe.Pointer(&p))) 143 | case 64: 144 | u64 = **(**uint64)(unsafe.Pointer(&p)) 145 | } 146 | mask := numMask(code.NumBitSize) 147 | n := u64 & mask 148 | if n < 10 { 149 | return append(out, byte(n+'0')) 150 | } else if n < 100 { 151 | u := intLELookup[n] 152 | return append(out, byte(u), byte(u>>8)) 153 | } 154 | 155 | lookup := intLookup[endianness] 156 | 157 | var b [22]byte 158 | u := (*[11]uint16)(unsafe.Pointer(&b)) 159 | i := 11 160 | 161 | for n >= 100 { 162 | j := n % 100 163 | n /= 100 164 | i-- 165 | u[i] = lookup[j] 166 | } 167 | 168 | i-- 169 | u[i] = lookup[n] 170 | 171 | i *= 2 // convert to byte index 172 | if n < 10 { 173 | i++ // remove leading zero 174 | } 175 | return append(out, b[i:]...) 176 | } 177 | -------------------------------------------------------------------------------- /internal/encoder/map112.go: -------------------------------------------------------------------------------- 1 | //go:build !go1.13 2 | // +build !go1.13 3 | 4 | package encoder 5 | 6 | import "unsafe" 7 | 8 | //go:linkname MapIterValue reflect.mapitervalue 9 | func MapIterValue(it *mapIter) unsafe.Pointer 10 | -------------------------------------------------------------------------------- /internal/encoder/map113.go: -------------------------------------------------------------------------------- 1 | //go:build go1.13 2 | // +build go1.13 3 | 4 | package encoder 5 | 6 | import "unsafe" 7 | 8 | //go:linkname MapIterValue reflect.mapiterelem 9 | func MapIterValue(it *mapIter) unsafe.Pointer 10 | -------------------------------------------------------------------------------- /internal/encoder/option.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import ( 4 | "context" 5 | "io" 6 | ) 7 | 8 | type OptionFlag uint8 9 | 10 | const ( 11 | HTMLEscapeOption OptionFlag = 1 << iota 12 | IndentOption 13 | UnorderedMapOption 14 | DebugOption 15 | ColorizeOption 16 | ContextOption 17 | NormalizeUTF8Option 18 | FieldQueryOption 19 | ) 20 | 21 | type Option struct { 22 | Flag OptionFlag 23 | ColorScheme *ColorScheme 24 | Context context.Context 25 | DebugOut io.Writer 26 | DebugDOTOut io.WriteCloser 27 | } 28 | 29 | type EncodeFormat struct { 30 | Header string 31 | Footer string 32 | } 33 | 34 | type EncodeFormatScheme struct { 35 | Int EncodeFormat 36 | Uint EncodeFormat 37 | Float EncodeFormat 38 | Bool EncodeFormat 39 | String EncodeFormat 40 | Binary EncodeFormat 41 | ObjectKey EncodeFormat 42 | Null EncodeFormat 43 | } 44 | 45 | type ( 46 | ColorScheme = EncodeFormatScheme 47 | ColorFormat = EncodeFormat 48 | ) 49 | -------------------------------------------------------------------------------- /internal/encoder/query.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | var ( 10 | Marshal func(interface{}) ([]byte, error) 11 | Unmarshal func([]byte, interface{}) error 12 | ) 13 | 14 | type FieldQuery struct { 15 | Name string 16 | Fields []*FieldQuery 17 | hash string 18 | } 19 | 20 | func (q *FieldQuery) Hash() string { 21 | if q.hash != "" { 22 | return q.hash 23 | } 24 | b, _ := Marshal(q) 25 | q.hash = string(b) 26 | return q.hash 27 | } 28 | 29 | func (q *FieldQuery) MarshalJSON() ([]byte, error) { 30 | if q.Name != "" { 31 | if len(q.Fields) > 0 { 32 | return Marshal(map[string][]*FieldQuery{q.Name: q.Fields}) 33 | } 34 | return Marshal(q.Name) 35 | } 36 | return Marshal(q.Fields) 37 | } 38 | 39 | func (q *FieldQuery) QueryString() (FieldQueryString, error) { 40 | b, err := Marshal(q) 41 | if err != nil { 42 | return "", err 43 | } 44 | return FieldQueryString(b), nil 45 | } 46 | 47 | type FieldQueryString string 48 | 49 | func (s FieldQueryString) Build() (*FieldQuery, error) { 50 | var query interface{} 51 | if err := Unmarshal([]byte(s), &query); err != nil { 52 | return nil, err 53 | } 54 | return s.build(reflect.ValueOf(query)) 55 | } 56 | 57 | func (s FieldQueryString) build(v reflect.Value) (*FieldQuery, error) { 58 | switch v.Type().Kind() { 59 | case reflect.String: 60 | return s.buildString(v) 61 | case reflect.Map: 62 | return s.buildMap(v) 63 | case reflect.Slice: 64 | return s.buildSlice(v) 65 | case reflect.Interface: 66 | return s.build(reflect.ValueOf(v.Interface())) 67 | } 68 | return nil, fmt.Errorf("failed to build field query") 69 | } 70 | 71 | func (s FieldQueryString) buildString(v reflect.Value) (*FieldQuery, error) { 72 | b := []byte(v.String()) 73 | switch b[0] { 74 | case '[', '{': 75 | var query interface{} 76 | if err := Unmarshal(b, &query); err != nil { 77 | return nil, err 78 | } 79 | if str, ok := query.(string); ok { 80 | return &FieldQuery{Name: str}, nil 81 | } 82 | return s.build(reflect.ValueOf(query)) 83 | } 84 | return &FieldQuery{Name: string(b)}, nil 85 | } 86 | 87 | func (s FieldQueryString) buildSlice(v reflect.Value) (*FieldQuery, error) { 88 | fields := make([]*FieldQuery, 0, v.Len()) 89 | for i := 0; i < v.Len(); i++ { 90 | def, err := s.build(v.Index(i)) 91 | if err != nil { 92 | return nil, err 93 | } 94 | fields = append(fields, def) 95 | } 96 | return &FieldQuery{Fields: fields}, nil 97 | } 98 | 99 | func (s FieldQueryString) buildMap(v reflect.Value) (*FieldQuery, error) { 100 | keys := v.MapKeys() 101 | if len(keys) != 1 { 102 | return nil, fmt.Errorf("failed to build field query object") 103 | } 104 | key := keys[0] 105 | if key.Type().Kind() != reflect.String { 106 | return nil, fmt.Errorf("failed to build field query. invalid object key type") 107 | } 108 | name := key.String() 109 | def, err := s.build(v.MapIndex(key)) 110 | if err != nil { 111 | return nil, err 112 | } 113 | return &FieldQuery{ 114 | Name: name, 115 | Fields: def.Fields, 116 | }, nil 117 | } 118 | 119 | type queryKey struct{} 120 | 121 | func FieldQueryFromContext(ctx context.Context) *FieldQuery { 122 | query := ctx.Value(queryKey{}) 123 | if query == nil { 124 | return nil 125 | } 126 | q, ok := query.(*FieldQuery) 127 | if !ok { 128 | return nil 129 | } 130 | return q 131 | } 132 | 133 | func SetFieldQueryToContext(ctx context.Context, query *FieldQuery) context.Context { 134 | return context.WithValue(ctx, queryKey{}, query) 135 | } 136 | -------------------------------------------------------------------------------- /internal/encoder/string_table.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | var needEscapeHTMLNormalizeUTF8 = [256]bool{ 4 | '"': true, 5 | '&': true, 6 | '<': true, 7 | '>': true, 8 | '\\': true, 9 | 0x00: true, 10 | 0x01: true, 11 | 0x02: true, 12 | 0x03: true, 13 | 0x04: true, 14 | 0x05: true, 15 | 0x06: true, 16 | 0x07: true, 17 | 0x08: true, 18 | 0x09: true, 19 | 0x0a: true, 20 | 0x0b: true, 21 | 0x0c: true, 22 | 0x0d: true, 23 | 0x0e: true, 24 | 0x0f: true, 25 | 0x10: true, 26 | 0x11: true, 27 | 0x12: true, 28 | 0x13: true, 29 | 0x14: true, 30 | 0x15: true, 31 | 0x16: true, 32 | 0x17: true, 33 | 0x18: true, 34 | 0x19: true, 35 | 0x1a: true, 36 | 0x1b: true, 37 | 0x1c: true, 38 | 0x1d: true, 39 | 0x1e: true, 40 | 0x1f: true, 41 | /* 0x20 - 0x7f */ 42 | 0x80: true, 43 | 0x81: true, 44 | 0x82: true, 45 | 0x83: true, 46 | 0x84: true, 47 | 0x85: true, 48 | 0x86: true, 49 | 0x87: true, 50 | 0x88: true, 51 | 0x89: true, 52 | 0x8a: true, 53 | 0x8b: true, 54 | 0x8c: true, 55 | 0x8d: true, 56 | 0x8e: true, 57 | 0x8f: true, 58 | 0x90: true, 59 | 0x91: true, 60 | 0x92: true, 61 | 0x93: true, 62 | 0x94: true, 63 | 0x95: true, 64 | 0x96: true, 65 | 0x97: true, 66 | 0x98: true, 67 | 0x99: true, 68 | 0x9a: true, 69 | 0x9b: true, 70 | 0x9c: true, 71 | 0x9d: true, 72 | 0x9e: true, 73 | 0x9f: true, 74 | 0xa0: true, 75 | 0xa1: true, 76 | 0xa2: true, 77 | 0xa3: true, 78 | 0xa4: true, 79 | 0xa5: true, 80 | 0xa6: true, 81 | 0xa7: true, 82 | 0xa8: true, 83 | 0xa9: true, 84 | 0xaa: true, 85 | 0xab: true, 86 | 0xac: true, 87 | 0xad: true, 88 | 0xae: true, 89 | 0xaf: true, 90 | 0xb0: true, 91 | 0xb1: true, 92 | 0xb2: true, 93 | 0xb3: true, 94 | 0xb4: true, 95 | 0xb5: true, 96 | 0xb6: true, 97 | 0xb7: true, 98 | 0xb8: true, 99 | 0xb9: true, 100 | 0xba: true, 101 | 0xbb: true, 102 | 0xbc: true, 103 | 0xbd: true, 104 | 0xbe: true, 105 | 0xbf: true, 106 | 0xc0: true, 107 | 0xc1: true, 108 | 0xc2: true, 109 | 0xc3: true, 110 | 0xc4: true, 111 | 0xc5: true, 112 | 0xc6: true, 113 | 0xc7: true, 114 | 0xc8: true, 115 | 0xc9: true, 116 | 0xca: true, 117 | 0xcb: true, 118 | 0xcc: true, 119 | 0xcd: true, 120 | 0xce: true, 121 | 0xcf: true, 122 | 0xd0: true, 123 | 0xd1: true, 124 | 0xd2: true, 125 | 0xd3: true, 126 | 0xd4: true, 127 | 0xd5: true, 128 | 0xd6: true, 129 | 0xd7: true, 130 | 0xd8: true, 131 | 0xd9: true, 132 | 0xda: true, 133 | 0xdb: true, 134 | 0xdc: true, 135 | 0xdd: true, 136 | 0xde: true, 137 | 0xdf: true, 138 | 0xe0: true, 139 | 0xe1: true, 140 | 0xe2: true, 141 | 0xe3: true, 142 | 0xe4: true, 143 | 0xe5: true, 144 | 0xe6: true, 145 | 0xe7: true, 146 | 0xe8: true, 147 | 0xe9: true, 148 | 0xea: true, 149 | 0xeb: true, 150 | 0xec: true, 151 | 0xed: true, 152 | 0xee: true, 153 | 0xef: true, 154 | 0xf0: true, 155 | 0xf1: true, 156 | 0xf2: true, 157 | 0xf3: true, 158 | 0xf4: true, 159 | 0xf5: true, 160 | 0xf6: true, 161 | 0xf7: true, 162 | 0xf8: true, 163 | 0xf9: true, 164 | 0xfa: true, 165 | 0xfb: true, 166 | 0xfc: true, 167 | 0xfd: true, 168 | 0xfe: true, 169 | 0xff: true, 170 | } 171 | 172 | var needEscapeNormalizeUTF8 = [256]bool{ 173 | '"': true, 174 | '\\': true, 175 | 0x00: true, 176 | 0x01: true, 177 | 0x02: true, 178 | 0x03: true, 179 | 0x04: true, 180 | 0x05: true, 181 | 0x06: true, 182 | 0x07: true, 183 | 0x08: true, 184 | 0x09: true, 185 | 0x0a: true, 186 | 0x0b: true, 187 | 0x0c: true, 188 | 0x0d: true, 189 | 0x0e: true, 190 | 0x0f: true, 191 | 0x10: true, 192 | 0x11: true, 193 | 0x12: true, 194 | 0x13: true, 195 | 0x14: true, 196 | 0x15: true, 197 | 0x16: true, 198 | 0x17: true, 199 | 0x18: true, 200 | 0x19: true, 201 | 0x1a: true, 202 | 0x1b: true, 203 | 0x1c: true, 204 | 0x1d: true, 205 | 0x1e: true, 206 | 0x1f: true, 207 | /* 0x20 - 0x7f */ 208 | 0x80: true, 209 | 0x81: true, 210 | 0x82: true, 211 | 0x83: true, 212 | 0x84: true, 213 | 0x85: true, 214 | 0x86: true, 215 | 0x87: true, 216 | 0x88: true, 217 | 0x89: true, 218 | 0x8a: true, 219 | 0x8b: true, 220 | 0x8c: true, 221 | 0x8d: true, 222 | 0x8e: true, 223 | 0x8f: true, 224 | 0x90: true, 225 | 0x91: true, 226 | 0x92: true, 227 | 0x93: true, 228 | 0x94: true, 229 | 0x95: true, 230 | 0x96: true, 231 | 0x97: true, 232 | 0x98: true, 233 | 0x99: true, 234 | 0x9a: true, 235 | 0x9b: true, 236 | 0x9c: true, 237 | 0x9d: true, 238 | 0x9e: true, 239 | 0x9f: true, 240 | 0xa0: true, 241 | 0xa1: true, 242 | 0xa2: true, 243 | 0xa3: true, 244 | 0xa4: true, 245 | 0xa5: true, 246 | 0xa6: true, 247 | 0xa7: true, 248 | 0xa8: true, 249 | 0xa9: true, 250 | 0xaa: true, 251 | 0xab: true, 252 | 0xac: true, 253 | 0xad: true, 254 | 0xae: true, 255 | 0xaf: true, 256 | 0xb0: true, 257 | 0xb1: true, 258 | 0xb2: true, 259 | 0xb3: true, 260 | 0xb4: true, 261 | 0xb5: true, 262 | 0xb6: true, 263 | 0xb7: true, 264 | 0xb8: true, 265 | 0xb9: true, 266 | 0xba: true, 267 | 0xbb: true, 268 | 0xbc: true, 269 | 0xbd: true, 270 | 0xbe: true, 271 | 0xbf: true, 272 | 0xc0: true, 273 | 0xc1: true, 274 | 0xc2: true, 275 | 0xc3: true, 276 | 0xc4: true, 277 | 0xc5: true, 278 | 0xc6: true, 279 | 0xc7: true, 280 | 0xc8: true, 281 | 0xc9: true, 282 | 0xca: true, 283 | 0xcb: true, 284 | 0xcc: true, 285 | 0xcd: true, 286 | 0xce: true, 287 | 0xcf: true, 288 | 0xd0: true, 289 | 0xd1: true, 290 | 0xd2: true, 291 | 0xd3: true, 292 | 0xd4: true, 293 | 0xd5: true, 294 | 0xd6: true, 295 | 0xd7: true, 296 | 0xd8: true, 297 | 0xd9: true, 298 | 0xda: true, 299 | 0xdb: true, 300 | 0xdc: true, 301 | 0xdd: true, 302 | 0xde: true, 303 | 0xdf: true, 304 | 0xe0: true, 305 | 0xe1: true, 306 | 0xe2: true, 307 | 0xe3: true, 308 | 0xe4: true, 309 | 0xe5: true, 310 | 0xe6: true, 311 | 0xe7: true, 312 | 0xe8: true, 313 | 0xe9: true, 314 | 0xea: true, 315 | 0xeb: true, 316 | 0xec: true, 317 | 0xed: true, 318 | 0xee: true, 319 | 0xef: true, 320 | 0xf0: true, 321 | 0xf1: true, 322 | 0xf2: true, 323 | 0xf3: true, 324 | 0xf4: true, 325 | 0xf5: true, 326 | 0xf6: true, 327 | 0xf7: true, 328 | 0xf8: true, 329 | 0xf9: true, 330 | 0xfa: true, 331 | 0xfb: true, 332 | 0xfc: true, 333 | 0xfd: true, 334 | 0xfe: true, 335 | 0xff: true, 336 | } 337 | 338 | var needEscapeHTML = [256]bool{ 339 | '"': true, 340 | '&': true, 341 | '<': true, 342 | '>': true, 343 | '\\': true, 344 | 0x00: true, 345 | 0x01: true, 346 | 0x02: true, 347 | 0x03: true, 348 | 0x04: true, 349 | 0x05: true, 350 | 0x06: true, 351 | 0x07: true, 352 | 0x08: true, 353 | 0x09: true, 354 | 0x0a: true, 355 | 0x0b: true, 356 | 0x0c: true, 357 | 0x0d: true, 358 | 0x0e: true, 359 | 0x0f: true, 360 | 0x10: true, 361 | 0x11: true, 362 | 0x12: true, 363 | 0x13: true, 364 | 0x14: true, 365 | 0x15: true, 366 | 0x16: true, 367 | 0x17: true, 368 | 0x18: true, 369 | 0x19: true, 370 | 0x1a: true, 371 | 0x1b: true, 372 | 0x1c: true, 373 | 0x1d: true, 374 | 0x1e: true, 375 | 0x1f: true, 376 | /* 0x20 - 0xff */ 377 | } 378 | 379 | var needEscape = [256]bool{ 380 | '"': true, 381 | '\\': true, 382 | 0x00: true, 383 | 0x01: true, 384 | 0x02: true, 385 | 0x03: true, 386 | 0x04: true, 387 | 0x05: true, 388 | 0x06: true, 389 | 0x07: true, 390 | 0x08: true, 391 | 0x09: true, 392 | 0x0a: true, 393 | 0x0b: true, 394 | 0x0c: true, 395 | 0x0d: true, 396 | 0x0e: true, 397 | 0x0f: true, 398 | 0x10: true, 399 | 0x11: true, 400 | 0x12: true, 401 | 0x13: true, 402 | 0x14: true, 403 | 0x15: true, 404 | 0x16: true, 405 | 0x17: true, 406 | 0x18: true, 407 | 0x19: true, 408 | 0x1a: true, 409 | 0x1b: true, 410 | 0x1c: true, 411 | 0x1d: true, 412 | 0x1e: true, 413 | 0x1f: true, 414 | /* 0x20 - 0xff */ 415 | } 416 | -------------------------------------------------------------------------------- /internal/encoder/vm/debug_vm.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/goccy/go-json/internal/encoder" 8 | ) 9 | 10 | func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) { 11 | defer func() { 12 | var code *encoder.Opcode 13 | if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 { 14 | code = codeSet.EscapeKeyCode 15 | } else { 16 | code = codeSet.NoescapeKeyCode 17 | } 18 | if wc := ctx.Option.DebugDOTOut; wc != nil { 19 | _, _ = io.WriteString(wc, code.DumpDOT()) 20 | wc.Close() 21 | ctx.Option.DebugDOTOut = nil 22 | } 23 | 24 | if err := recover(); err != nil { 25 | w := ctx.Option.DebugOut 26 | fmt.Fprintln(w, "=============[DEBUG]===============") 27 | fmt.Fprintln(w, "* [TYPE]") 28 | fmt.Fprintln(w, codeSet.Type) 29 | fmt.Fprintf(w, "\n") 30 | fmt.Fprintln(w, "* [ALL OPCODE]") 31 | fmt.Fprintln(w, code.Dump()) 32 | fmt.Fprintf(w, "\n") 33 | fmt.Fprintln(w, "* [CONTEXT]") 34 | fmt.Fprintf(w, "%+v\n", ctx) 35 | fmt.Fprintln(w, "===================================") 36 | panic(err) 37 | } 38 | }() 39 | 40 | return Run(ctx, b, codeSet) 41 | } 42 | -------------------------------------------------------------------------------- /internal/encoder/vm/hack.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | // HACK: compile order 5 | // `vm`, `vm_indent`, `vm_color`, `vm_color_indent` packages uses a lot of memory to compile, 6 | // so forcibly make dependencies and avoid compiling in concurrent. 7 | // dependency order: vm => vm_indent => vm_color => vm_color_indent 8 | _ "github.com/goccy/go-json/internal/encoder/vm_indent" 9 | ) 10 | -------------------------------------------------------------------------------- /internal/encoder/vm/util.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/encoder" 9 | "github.com/goccy/go-json/internal/runtime" 10 | ) 11 | 12 | const uintptrSize = 4 << (^uintptr(0) >> 63) 13 | 14 | var ( 15 | appendInt = encoder.AppendInt 16 | appendUint = encoder.AppendUint 17 | appendFloat32 = encoder.AppendFloat32 18 | appendFloat64 = encoder.AppendFloat64 19 | appendString = encoder.AppendString 20 | appendByteSlice = encoder.AppendByteSlice 21 | appendNumber = encoder.AppendNumber 22 | errUnsupportedValue = encoder.ErrUnsupportedValue 23 | errUnsupportedFloat = encoder.ErrUnsupportedFloat 24 | mapiterinit = encoder.MapIterInit 25 | mapiterkey = encoder.MapIterKey 26 | mapitervalue = encoder.MapIterValue 27 | mapiternext = encoder.MapIterNext 28 | maplen = encoder.MapLen 29 | ) 30 | 31 | type emptyInterface struct { 32 | typ *runtime.Type 33 | ptr unsafe.Pointer 34 | } 35 | 36 | type nonEmptyInterface struct { 37 | itab *struct { 38 | ityp *runtime.Type // static interface type 39 | typ *runtime.Type // dynamic concrete type 40 | // unused fields... 41 | } 42 | ptr unsafe.Pointer 43 | } 44 | 45 | func errUnimplementedOp(op encoder.OpType) error { 46 | return fmt.Errorf("encoder: opcode %s has not been implemented", op) 47 | } 48 | 49 | func load(base uintptr, idx uint32) uintptr { 50 | addr := base + uintptr(idx) 51 | return **(**uintptr)(unsafe.Pointer(&addr)) 52 | } 53 | 54 | func store(base uintptr, idx uint32, p uintptr) { 55 | addr := base + uintptr(idx) 56 | **(**uintptr)(unsafe.Pointer(&addr)) = p 57 | } 58 | 59 | func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr { 60 | addr := base + uintptr(idx) 61 | p := **(**uintptr)(unsafe.Pointer(&addr)) 62 | for i := uint8(0); i < ptrNum; i++ { 63 | if p == 0 { 64 | return 0 65 | } 66 | p = ptrToPtr(p) 67 | } 68 | return p 69 | } 70 | 71 | func ptrToUint64(p uintptr, bitSize uint8) uint64 { 72 | switch bitSize { 73 | case 8: 74 | return (uint64)(**(**uint8)(unsafe.Pointer(&p))) 75 | case 16: 76 | return (uint64)(**(**uint16)(unsafe.Pointer(&p))) 77 | case 32: 78 | return (uint64)(**(**uint32)(unsafe.Pointer(&p))) 79 | case 64: 80 | return **(**uint64)(unsafe.Pointer(&p)) 81 | } 82 | return 0 83 | } 84 | func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) } 85 | func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) } 86 | func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) } 87 | func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) } 88 | func ptrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) } 89 | func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) } 90 | func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) } 91 | func ptrToPtr(p uintptr) uintptr { 92 | return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p))) 93 | } 94 | func ptrToNPtr(p uintptr, ptrNum uint8) uintptr { 95 | for i := uint8(0); i < ptrNum; i++ { 96 | if p == 0 { 97 | return 0 98 | } 99 | p = ptrToPtr(p) 100 | } 101 | return p 102 | } 103 | 104 | func ptrToUnsafePtr(p uintptr) unsafe.Pointer { 105 | return *(*unsafe.Pointer)(unsafe.Pointer(&p)) 106 | } 107 | func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} { 108 | return *(*interface{})(unsafe.Pointer(&emptyInterface{ 109 | typ: code.Type, 110 | ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)), 111 | })) 112 | } 113 | 114 | func appendBool(_ *encoder.RuntimeContext, b []byte, v bool) []byte { 115 | if v { 116 | return append(b, "true"...) 117 | } 118 | return append(b, "false"...) 119 | } 120 | 121 | func appendNull(_ *encoder.RuntimeContext, b []byte) []byte { 122 | return append(b, "null"...) 123 | } 124 | 125 | func appendComma(_ *encoder.RuntimeContext, b []byte) []byte { 126 | return append(b, ',') 127 | } 128 | 129 | func appendNullComma(_ *encoder.RuntimeContext, b []byte) []byte { 130 | return append(b, "null,"...) 131 | } 132 | 133 | func appendColon(_ *encoder.RuntimeContext, b []byte) []byte { 134 | last := len(b) - 1 135 | b[last] = ':' 136 | return b 137 | } 138 | 139 | func appendMapKeyValue(_ *encoder.RuntimeContext, _ *encoder.Opcode, b, key, value []byte) []byte { 140 | b = append(b, key...) 141 | b[len(b)-1] = ':' 142 | return append(b, value...) 143 | } 144 | 145 | func appendMapEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { 146 | b[len(b)-1] = '}' 147 | b = append(b, ',') 148 | return b 149 | } 150 | 151 | func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { 152 | return encoder.AppendMarshalJSON(ctx, code, b, v) 153 | } 154 | 155 | func appendMarshalText(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { 156 | return encoder.AppendMarshalText(ctx, code, b, v) 157 | } 158 | 159 | func appendArrayHead(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { 160 | return append(b, '[') 161 | } 162 | 163 | func appendArrayEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { 164 | last := len(b) - 1 165 | b[last] = ']' 166 | return append(b, ',') 167 | } 168 | 169 | func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte { 170 | return append(b, '[', ']', ',') 171 | } 172 | 173 | func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte { 174 | return append(b, '{', '}', ',') 175 | } 176 | 177 | func appendObjectEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { 178 | last := len(b) - 1 179 | b[last] = '}' 180 | return append(b, ',') 181 | } 182 | 183 | func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte { 184 | return append(b, '{') 185 | } 186 | 187 | func appendStructKey(_ *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 188 | return append(b, code.Key...) 189 | } 190 | 191 | func appendStructEnd(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { 192 | return append(b, '}', ',') 193 | } 194 | 195 | func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 196 | last := len(b) - 1 197 | if b[last] == ',' { 198 | b[last] = '}' 199 | return appendComma(ctx, b) 200 | } 201 | return appendStructEnd(ctx, code, b) 202 | } 203 | 204 | func restoreIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, _ uintptr) {} 205 | func storeIndent(_ uintptr, _ *encoder.Opcode, _ uintptr) {} 206 | func appendMapKeyIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b } 207 | func appendArrayElemIndent(_ *encoder.RuntimeContext, _ *encoder.Opcode, b []byte) []byte { return b } 208 | -------------------------------------------------------------------------------- /internal/encoder/vm_color/debug_vm.go: -------------------------------------------------------------------------------- 1 | package vm_color 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/goccy/go-json/internal/encoder" 7 | ) 8 | 9 | func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) { 10 | var code *encoder.Opcode 11 | if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 { 12 | code = codeSet.EscapeKeyCode 13 | } else { 14 | code = codeSet.NoescapeKeyCode 15 | } 16 | 17 | defer func() { 18 | if err := recover(); err != nil { 19 | w := ctx.Option.DebugOut 20 | fmt.Fprintln(w, "=============[DEBUG]===============") 21 | fmt.Fprintln(w, "* [TYPE]") 22 | fmt.Fprintln(w, codeSet.Type) 23 | fmt.Fprintf(w, "\n") 24 | fmt.Fprintln(w, "* [ALL OPCODE]") 25 | fmt.Fprintln(w, code.Dump()) 26 | fmt.Fprintf(w, "\n") 27 | fmt.Fprintln(w, "* [CONTEXT]") 28 | fmt.Fprintf(w, "%+v\n", ctx) 29 | fmt.Fprintln(w, "===================================") 30 | panic(err) 31 | } 32 | }() 33 | 34 | return Run(ctx, b, codeSet) 35 | } 36 | -------------------------------------------------------------------------------- /internal/encoder/vm_color/hack.go: -------------------------------------------------------------------------------- 1 | package vm_color 2 | 3 | import ( 4 | // HACK: compile order 5 | // `vm`, `vm_indent`, `vm_color`, `vm_color_indent` packages uses a lot of memory to compile, 6 | // so forcibly make dependencies and avoid compiling in concurrent. 7 | // dependency order: vm => vm_indent => vm_color => vm_color_indent 8 | _ "github.com/goccy/go-json/internal/encoder/vm_color_indent" 9 | ) 10 | -------------------------------------------------------------------------------- /internal/encoder/vm_color_indent/debug_vm.go: -------------------------------------------------------------------------------- 1 | package vm_color_indent 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/goccy/go-json/internal/encoder" 7 | ) 8 | 9 | func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) { 10 | var code *encoder.Opcode 11 | if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 { 12 | code = codeSet.EscapeKeyCode 13 | } else { 14 | code = codeSet.NoescapeKeyCode 15 | } 16 | 17 | defer func() { 18 | if err := recover(); err != nil { 19 | w := ctx.Option.DebugOut 20 | fmt.Fprintln(w, "=============[DEBUG]===============") 21 | fmt.Fprintln(w, "* [TYPE]") 22 | fmt.Fprintln(w, codeSet.Type) 23 | fmt.Fprintf(w, "\n") 24 | fmt.Fprintln(w, "* [ALL OPCODE]") 25 | fmt.Fprintln(w, code.Dump()) 26 | fmt.Fprintf(w, "\n") 27 | fmt.Fprintln(w, "* [CONTEXT]") 28 | fmt.Fprintf(w, "%+v\n", ctx) 29 | fmt.Fprintln(w, "===================================") 30 | panic(err) 31 | } 32 | }() 33 | 34 | return Run(ctx, b, codeSet) 35 | } 36 | -------------------------------------------------------------------------------- /internal/encoder/vm_indent/debug_vm.go: -------------------------------------------------------------------------------- 1 | package vm_indent 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/goccy/go-json/internal/encoder" 7 | ) 8 | 9 | func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) { 10 | var code *encoder.Opcode 11 | if (ctx.Option.Flag & encoder.HTMLEscapeOption) != 0 { 12 | code = codeSet.EscapeKeyCode 13 | } else { 14 | code = codeSet.NoescapeKeyCode 15 | } 16 | 17 | defer func() { 18 | if err := recover(); err != nil { 19 | w := ctx.Option.DebugOut 20 | fmt.Fprintln(w, "=============[DEBUG]===============") 21 | fmt.Fprintln(w, "* [TYPE]") 22 | fmt.Fprintln(w, codeSet.Type) 23 | fmt.Fprintf(w, "\n") 24 | fmt.Fprintln(w, "* [ALL OPCODE]") 25 | fmt.Fprintln(w, code.Dump()) 26 | fmt.Fprintf(w, "\n") 27 | fmt.Fprintln(w, "* [CONTEXT]") 28 | fmt.Fprintf(w, "%+v\n", ctx) 29 | fmt.Fprintln(w, "===================================") 30 | panic(err) 31 | } 32 | }() 33 | 34 | return Run(ctx, b, codeSet) 35 | } 36 | -------------------------------------------------------------------------------- /internal/encoder/vm_indent/hack.go: -------------------------------------------------------------------------------- 1 | package vm_indent 2 | 3 | import ( 4 | // HACK: compile order 5 | // `vm`, `vm_indent`, `vm_color`, `vm_color_indent` packages uses a lot of memory to compile, 6 | // so forcibly make dependencies and avoid compiling in concurrent. 7 | // dependency order: vm => vm_indent => vm_color => vm_color_indent 8 | _ "github.com/goccy/go-json/internal/encoder/vm_color" 9 | ) 10 | -------------------------------------------------------------------------------- /internal/encoder/vm_indent/util.go: -------------------------------------------------------------------------------- 1 | package vm_indent 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "unsafe" 7 | 8 | "github.com/goccy/go-json/internal/encoder" 9 | "github.com/goccy/go-json/internal/runtime" 10 | ) 11 | 12 | const uintptrSize = 4 << (^uintptr(0) >> 63) 13 | 14 | var ( 15 | appendInt = encoder.AppendInt 16 | appendUint = encoder.AppendUint 17 | appendFloat32 = encoder.AppendFloat32 18 | appendFloat64 = encoder.AppendFloat64 19 | appendString = encoder.AppendString 20 | appendByteSlice = encoder.AppendByteSlice 21 | appendNumber = encoder.AppendNumber 22 | appendStructEnd = encoder.AppendStructEndIndent 23 | appendIndent = encoder.AppendIndent 24 | errUnsupportedValue = encoder.ErrUnsupportedValue 25 | errUnsupportedFloat = encoder.ErrUnsupportedFloat 26 | mapiterinit = encoder.MapIterInit 27 | mapiterkey = encoder.MapIterKey 28 | mapitervalue = encoder.MapIterValue 29 | mapiternext = encoder.MapIterNext 30 | maplen = encoder.MapLen 31 | ) 32 | 33 | type emptyInterface struct { 34 | typ *runtime.Type 35 | ptr unsafe.Pointer 36 | } 37 | 38 | type nonEmptyInterface struct { 39 | itab *struct { 40 | ityp *runtime.Type // static interface type 41 | typ *runtime.Type // dynamic concrete type 42 | // unused fields... 43 | } 44 | ptr unsafe.Pointer 45 | } 46 | 47 | func errUnimplementedOp(op encoder.OpType) error { 48 | return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op) 49 | } 50 | 51 | func load(base uintptr, idx uint32) uintptr { 52 | addr := base + uintptr(idx) 53 | return **(**uintptr)(unsafe.Pointer(&addr)) 54 | } 55 | 56 | func store(base uintptr, idx uint32, p uintptr) { 57 | addr := base + uintptr(idx) 58 | **(**uintptr)(unsafe.Pointer(&addr)) = p 59 | } 60 | 61 | func loadNPtr(base uintptr, idx uint32, ptrNum uint8) uintptr { 62 | addr := base + uintptr(idx) 63 | p := **(**uintptr)(unsafe.Pointer(&addr)) 64 | for i := uint8(0); i < ptrNum; i++ { 65 | if p == 0 { 66 | return 0 67 | } 68 | p = ptrToPtr(p) 69 | } 70 | return p 71 | } 72 | 73 | func ptrToUint64(p uintptr, bitSize uint8) uint64 { 74 | switch bitSize { 75 | case 8: 76 | return (uint64)(**(**uint8)(unsafe.Pointer(&p))) 77 | case 16: 78 | return (uint64)(**(**uint16)(unsafe.Pointer(&p))) 79 | case 32: 80 | return (uint64)(**(**uint32)(unsafe.Pointer(&p))) 81 | case 64: 82 | return **(**uint64)(unsafe.Pointer(&p)) 83 | } 84 | return 0 85 | } 86 | func ptrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) } 87 | func ptrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) } 88 | func ptrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) } 89 | func ptrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) } 90 | func ptrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) } 91 | func ptrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) } 92 | func ptrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) } 93 | func ptrToPtr(p uintptr) uintptr { 94 | return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p))) 95 | } 96 | func ptrToNPtr(p uintptr, ptrNum uint8) uintptr { 97 | for i := uint8(0); i < ptrNum; i++ { 98 | if p == 0 { 99 | return 0 100 | } 101 | p = ptrToPtr(p) 102 | } 103 | return p 104 | } 105 | 106 | func ptrToUnsafePtr(p uintptr) unsafe.Pointer { 107 | return *(*unsafe.Pointer)(unsafe.Pointer(&p)) 108 | } 109 | func ptrToInterface(code *encoder.Opcode, p uintptr) interface{} { 110 | return *(*interface{})(unsafe.Pointer(&emptyInterface{ 111 | typ: code.Type, 112 | ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)), 113 | })) 114 | } 115 | 116 | func appendBool(_ *encoder.RuntimeContext, b []byte, v bool) []byte { 117 | if v { 118 | return append(b, "true"...) 119 | } 120 | return append(b, "false"...) 121 | } 122 | 123 | func appendNull(_ *encoder.RuntimeContext, b []byte) []byte { 124 | return append(b, "null"...) 125 | } 126 | 127 | func appendComma(_ *encoder.RuntimeContext, b []byte) []byte { 128 | return append(b, ',', '\n') 129 | } 130 | 131 | func appendNullComma(_ *encoder.RuntimeContext, b []byte) []byte { 132 | return append(b, "null,\n"...) 133 | } 134 | 135 | func appendColon(_ *encoder.RuntimeContext, b []byte) []byte { 136 | return append(b[:len(b)-2], ':', ' ') 137 | } 138 | 139 | func appendMapKeyValue(ctx *encoder.RuntimeContext, code *encoder.Opcode, b, key, value []byte) []byte { 140 | b = appendIndent(ctx, b, code.Indent+1) 141 | b = append(b, key...) 142 | b[len(b)-2] = ':' 143 | b[len(b)-1] = ' ' 144 | return append(b, value...) 145 | } 146 | 147 | func appendMapEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 148 | b = b[:len(b)-2] 149 | b = append(b, '\n') 150 | b = appendIndent(ctx, b, code.Indent) 151 | return append(b, '}', ',', '\n') 152 | } 153 | 154 | func appendArrayHead(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 155 | b = append(b, '[', '\n') 156 | return appendIndent(ctx, b, code.Indent+1) 157 | } 158 | 159 | func appendArrayEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 160 | b = b[:len(b)-2] 161 | b = append(b, '\n') 162 | b = appendIndent(ctx, b, code.Indent) 163 | return append(b, ']', ',', '\n') 164 | } 165 | 166 | func appendEmptyArray(_ *encoder.RuntimeContext, b []byte) []byte { 167 | return append(b, '[', ']', ',', '\n') 168 | } 169 | 170 | func appendEmptyObject(_ *encoder.RuntimeContext, b []byte) []byte { 171 | return append(b, '{', '}', ',', '\n') 172 | } 173 | 174 | func appendObjectEnd(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 175 | last := len(b) - 1 176 | // replace comma to newline 177 | b[last-1] = '\n' 178 | b = appendIndent(ctx, b[:last], code.Indent) 179 | return append(b, '}', ',', '\n') 180 | } 181 | 182 | func appendMarshalJSON(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { 183 | return encoder.AppendMarshalJSONIndent(ctx, code, b, v) 184 | } 185 | 186 | func appendMarshalText(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte, v interface{}) ([]byte, error) { 187 | return encoder.AppendMarshalTextIndent(ctx, code, b, v) 188 | } 189 | 190 | func appendStructHead(_ *encoder.RuntimeContext, b []byte) []byte { 191 | return append(b, '{', '\n') 192 | } 193 | 194 | func appendStructKey(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 195 | b = appendIndent(ctx, b, code.Indent) 196 | b = append(b, code.Key...) 197 | return append(b, ' ') 198 | } 199 | 200 | func appendStructEndSkipLast(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 201 | last := len(b) - 1 202 | if b[last-1] == '{' { 203 | b[last] = '}' 204 | } else { 205 | if b[last] == '\n' { 206 | // to remove ',' and '\n' characters 207 | b = b[:len(b)-2] 208 | } 209 | b = append(b, '\n') 210 | b = appendIndent(ctx, b, code.Indent-1) 211 | b = append(b, '}') 212 | } 213 | return appendComma(ctx, b) 214 | } 215 | 216 | func restoreIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, ctxptr uintptr) { 217 | ctx.BaseIndent = uint32(load(ctxptr, code.Length)) 218 | } 219 | 220 | func storeIndent(ctxptr uintptr, code *encoder.Opcode, indent uintptr) { 221 | store(ctxptr, code.Length, indent) 222 | } 223 | 224 | func appendArrayElemIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 225 | return appendIndent(ctx, b, code.Indent+1) 226 | } 227 | 228 | func appendMapKeyIndent(ctx *encoder.RuntimeContext, code *encoder.Opcode, b []byte) []byte { 229 | return appendIndent(ctx, b, code.Indent) 230 | } 231 | -------------------------------------------------------------------------------- /internal/errors/error.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strconv" 7 | ) 8 | 9 | type InvalidUTF8Error struct { 10 | S string // the whole string value that caused the error 11 | } 12 | 13 | func (e *InvalidUTF8Error) Error() string { 14 | return fmt.Sprintf("json: invalid UTF-8 in string: %s", strconv.Quote(e.S)) 15 | } 16 | 17 | type InvalidUnmarshalError struct { 18 | Type reflect.Type 19 | } 20 | 21 | func (e *InvalidUnmarshalError) Error() string { 22 | if e.Type == nil { 23 | return "json: Unmarshal(nil)" 24 | } 25 | 26 | if e.Type.Kind() != reflect.Ptr { 27 | return fmt.Sprintf("json: Unmarshal(non-pointer %s)", e.Type) 28 | } 29 | return fmt.Sprintf("json: Unmarshal(nil %s)", e.Type) 30 | } 31 | 32 | // A MarshalerError represents an error from calling a MarshalJSON or MarshalText method. 33 | type MarshalerError struct { 34 | Type reflect.Type 35 | Err error 36 | sourceFunc string 37 | } 38 | 39 | func (e *MarshalerError) Error() string { 40 | srcFunc := e.sourceFunc 41 | if srcFunc == "" { 42 | srcFunc = "MarshalJSON" 43 | } 44 | return fmt.Sprintf("json: error calling %s for type %s: %s", srcFunc, e.Type, e.Err.Error()) 45 | } 46 | 47 | // Unwrap returns the underlying error. 48 | func (e *MarshalerError) Unwrap() error { return e.Err } 49 | 50 | // A SyntaxError is a description of a JSON syntax error. 51 | type SyntaxError struct { 52 | msg string // description of error 53 | Offset int64 // error occurred after reading Offset bytes 54 | } 55 | 56 | func (e *SyntaxError) Error() string { return e.msg } 57 | 58 | // An UnmarshalFieldError describes a JSON object key that 59 | // led to an unexported (and therefore unwritable) struct field. 60 | // 61 | // Deprecated: No longer used; kept for compatibility. 62 | type UnmarshalFieldError struct { 63 | Key string 64 | Type reflect.Type 65 | Field reflect.StructField 66 | } 67 | 68 | func (e *UnmarshalFieldError) Error() string { 69 | return fmt.Sprintf("json: cannot unmarshal object key %s into unexported field %s of type %s", 70 | strconv.Quote(e.Key), e.Field.Name, e.Type.String(), 71 | ) 72 | } 73 | 74 | // An UnmarshalTypeError describes a JSON value that was 75 | // not appropriate for a value of a specific Go type. 76 | type UnmarshalTypeError struct { 77 | Value string // description of JSON value - "bool", "array", "number -5" 78 | Type reflect.Type // type of Go value it could not be assigned to 79 | Offset int64 // error occurred after reading Offset bytes 80 | Struct string // name of the struct type containing the field 81 | Field string // the full path from root node to the field 82 | } 83 | 84 | func (e *UnmarshalTypeError) Error() string { 85 | if e.Struct != "" || e.Field != "" { 86 | return fmt.Sprintf("json: cannot unmarshal %s into Go struct field %s.%s of type %s", 87 | e.Value, e.Struct, e.Field, e.Type, 88 | ) 89 | } 90 | return fmt.Sprintf("json: cannot unmarshal %s into Go value of type %s", e.Value, e.Type) 91 | } 92 | 93 | // An UnsupportedTypeError is returned by Marshal when attempting 94 | // to encode an unsupported value type. 95 | type UnsupportedTypeError struct { 96 | Type reflect.Type 97 | } 98 | 99 | func (e *UnsupportedTypeError) Error() string { 100 | return fmt.Sprintf("json: unsupported type: %s", e.Type) 101 | } 102 | 103 | type UnsupportedValueError struct { 104 | Value reflect.Value 105 | Str string 106 | } 107 | 108 | func (e *UnsupportedValueError) Error() string { 109 | return fmt.Sprintf("json: unsupported value: %s", e.Str) 110 | } 111 | 112 | func ErrSyntax(msg string, offset int64) *SyntaxError { 113 | return &SyntaxError{msg: msg, Offset: offset} 114 | } 115 | 116 | func ErrMarshaler(typ reflect.Type, err error, msg string) *MarshalerError { 117 | return &MarshalerError{ 118 | Type: typ, 119 | Err: err, 120 | sourceFunc: msg, 121 | } 122 | } 123 | 124 | func ErrExceededMaxDepth(c byte, cursor int64) *SyntaxError { 125 | return &SyntaxError{ 126 | msg: fmt.Sprintf(`invalid character "%c" exceeded max depth`, c), 127 | Offset: cursor, 128 | } 129 | } 130 | 131 | func ErrNotAtBeginningOfValue(cursor int64) *SyntaxError { 132 | return &SyntaxError{msg: "not at beginning of value", Offset: cursor} 133 | } 134 | 135 | func ErrUnexpectedEndOfJSON(msg string, cursor int64) *SyntaxError { 136 | return &SyntaxError{ 137 | msg: fmt.Sprintf("json: %s unexpected end of JSON input", msg), 138 | Offset: cursor, 139 | } 140 | } 141 | 142 | func ErrExpected(msg string, cursor int64) *SyntaxError { 143 | return &SyntaxError{msg: fmt.Sprintf("expected %s", msg), Offset: cursor} 144 | } 145 | 146 | func ErrInvalidCharacter(c byte, context string, cursor int64) *SyntaxError { 147 | if c == 0 { 148 | return &SyntaxError{ 149 | msg: fmt.Sprintf("json: invalid character as %s", context), 150 | Offset: cursor, 151 | } 152 | } 153 | return &SyntaxError{ 154 | msg: fmt.Sprintf("json: invalid character %c as %s", c, context), 155 | Offset: cursor, 156 | } 157 | } 158 | 159 | func ErrInvalidBeginningOfValue(c byte, cursor int64) *SyntaxError { 160 | return &SyntaxError{ 161 | msg: fmt.Sprintf("invalid character '%c' looking for beginning of value", c), 162 | Offset: cursor, 163 | } 164 | } 165 | 166 | type PathError struct { 167 | msg string 168 | } 169 | 170 | func (e *PathError) Error() string { 171 | return fmt.Sprintf("json: invalid path format: %s", e.msg) 172 | } 173 | 174 | func ErrInvalidPath(msg string, args ...interface{}) *PathError { 175 | if len(args) != 0 { 176 | return &PathError{msg: fmt.Sprintf(msg, args...)} 177 | } 178 | return &PathError{msg: msg} 179 | } 180 | 181 | func ErrEmptyPath() *PathError { 182 | return &PathError{msg: "path is empty"} 183 | } 184 | -------------------------------------------------------------------------------- /internal/runtime/rtype.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | // Type representing reflect.rtype for noescape trick 9 | type Type struct{} 10 | 11 | //go:linkname rtype_Align reflect.(*rtype).Align 12 | //go:noescape 13 | func rtype_Align(*Type) int 14 | 15 | func (t *Type) Align() int { 16 | return rtype_Align(t) 17 | } 18 | 19 | //go:linkname rtype_FieldAlign reflect.(*rtype).FieldAlign 20 | //go:noescape 21 | func rtype_FieldAlign(*Type) int 22 | 23 | func (t *Type) FieldAlign() int { 24 | return rtype_FieldAlign(t) 25 | } 26 | 27 | //go:linkname rtype_Method reflect.(*rtype).Method 28 | //go:noescape 29 | func rtype_Method(*Type, int) reflect.Method 30 | 31 | func (t *Type) Method(a0 int) reflect.Method { 32 | return rtype_Method(t, a0) 33 | } 34 | 35 | //go:linkname rtype_MethodByName reflect.(*rtype).MethodByName 36 | //go:noescape 37 | func rtype_MethodByName(*Type, string) (reflect.Method, bool) 38 | 39 | func (t *Type) MethodByName(a0 string) (reflect.Method, bool) { 40 | return rtype_MethodByName(t, a0) 41 | } 42 | 43 | //go:linkname rtype_NumMethod reflect.(*rtype).NumMethod 44 | //go:noescape 45 | func rtype_NumMethod(*Type) int 46 | 47 | func (t *Type) NumMethod() int { 48 | return rtype_NumMethod(t) 49 | } 50 | 51 | //go:linkname rtype_Name reflect.(*rtype).Name 52 | //go:noescape 53 | func rtype_Name(*Type) string 54 | 55 | func (t *Type) Name() string { 56 | return rtype_Name(t) 57 | } 58 | 59 | //go:linkname rtype_PkgPath reflect.(*rtype).PkgPath 60 | //go:noescape 61 | func rtype_PkgPath(*Type) string 62 | 63 | func (t *Type) PkgPath() string { 64 | return rtype_PkgPath(t) 65 | } 66 | 67 | //go:linkname rtype_Size reflect.(*rtype).Size 68 | //go:noescape 69 | func rtype_Size(*Type) uintptr 70 | 71 | func (t *Type) Size() uintptr { 72 | return rtype_Size(t) 73 | } 74 | 75 | //go:linkname rtype_String reflect.(*rtype).String 76 | //go:noescape 77 | func rtype_String(*Type) string 78 | 79 | func (t *Type) String() string { 80 | return rtype_String(t) 81 | } 82 | 83 | //go:linkname rtype_Kind reflect.(*rtype).Kind 84 | //go:noescape 85 | func rtype_Kind(*Type) reflect.Kind 86 | 87 | func (t *Type) Kind() reflect.Kind { 88 | return rtype_Kind(t) 89 | } 90 | 91 | //go:linkname rtype_Implements reflect.(*rtype).Implements 92 | //go:noescape 93 | func rtype_Implements(*Type, reflect.Type) bool 94 | 95 | func (t *Type) Implements(u reflect.Type) bool { 96 | return rtype_Implements(t, u) 97 | } 98 | 99 | //go:linkname rtype_AssignableTo reflect.(*rtype).AssignableTo 100 | //go:noescape 101 | func rtype_AssignableTo(*Type, reflect.Type) bool 102 | 103 | func (t *Type) AssignableTo(u reflect.Type) bool { 104 | return rtype_AssignableTo(t, u) 105 | } 106 | 107 | //go:linkname rtype_ConvertibleTo reflect.(*rtype).ConvertibleTo 108 | //go:noescape 109 | func rtype_ConvertibleTo(*Type, reflect.Type) bool 110 | 111 | func (t *Type) ConvertibleTo(u reflect.Type) bool { 112 | return rtype_ConvertibleTo(t, u) 113 | } 114 | 115 | //go:linkname rtype_Comparable reflect.(*rtype).Comparable 116 | //go:noescape 117 | func rtype_Comparable(*Type) bool 118 | 119 | func (t *Type) Comparable() bool { 120 | return rtype_Comparable(t) 121 | } 122 | 123 | //go:linkname rtype_Bits reflect.(*rtype).Bits 124 | //go:noescape 125 | func rtype_Bits(*Type) int 126 | 127 | func (t *Type) Bits() int { 128 | return rtype_Bits(t) 129 | } 130 | 131 | //go:linkname rtype_ChanDir reflect.(*rtype).ChanDir 132 | //go:noescape 133 | func rtype_ChanDir(*Type) reflect.ChanDir 134 | 135 | func (t *Type) ChanDir() reflect.ChanDir { 136 | return rtype_ChanDir(t) 137 | } 138 | 139 | //go:linkname rtype_IsVariadic reflect.(*rtype).IsVariadic 140 | //go:noescape 141 | func rtype_IsVariadic(*Type) bool 142 | 143 | func (t *Type) IsVariadic() bool { 144 | return rtype_IsVariadic(t) 145 | } 146 | 147 | //go:linkname rtype_Elem reflect.(*rtype).Elem 148 | //go:noescape 149 | func rtype_Elem(*Type) reflect.Type 150 | 151 | func (t *Type) Elem() *Type { 152 | return Type2RType(rtype_Elem(t)) 153 | } 154 | 155 | //go:linkname rtype_Field reflect.(*rtype).Field 156 | //go:noescape 157 | func rtype_Field(*Type, int) reflect.StructField 158 | 159 | func (t *Type) Field(i int) reflect.StructField { 160 | return rtype_Field(t, i) 161 | } 162 | 163 | //go:linkname rtype_FieldByIndex reflect.(*rtype).FieldByIndex 164 | //go:noescape 165 | func rtype_FieldByIndex(*Type, []int) reflect.StructField 166 | 167 | func (t *Type) FieldByIndex(index []int) reflect.StructField { 168 | return rtype_FieldByIndex(t, index) 169 | } 170 | 171 | //go:linkname rtype_FieldByName reflect.(*rtype).FieldByName 172 | //go:noescape 173 | func rtype_FieldByName(*Type, string) (reflect.StructField, bool) 174 | 175 | func (t *Type) FieldByName(name string) (reflect.StructField, bool) { 176 | return rtype_FieldByName(t, name) 177 | } 178 | 179 | //go:linkname rtype_FieldByNameFunc reflect.(*rtype).FieldByNameFunc 180 | //go:noescape 181 | func rtype_FieldByNameFunc(*Type, func(string) bool) (reflect.StructField, bool) 182 | 183 | func (t *Type) FieldByNameFunc(match func(string) bool) (reflect.StructField, bool) { 184 | return rtype_FieldByNameFunc(t, match) 185 | } 186 | 187 | //go:linkname rtype_In reflect.(*rtype).In 188 | //go:noescape 189 | func rtype_In(*Type, int) reflect.Type 190 | 191 | func (t *Type) In(i int) reflect.Type { 192 | return rtype_In(t, i) 193 | } 194 | 195 | //go:linkname rtype_Key reflect.(*rtype).Key 196 | //go:noescape 197 | func rtype_Key(*Type) reflect.Type 198 | 199 | func (t *Type) Key() *Type { 200 | return Type2RType(rtype_Key(t)) 201 | } 202 | 203 | //go:linkname rtype_Len reflect.(*rtype).Len 204 | //go:noescape 205 | func rtype_Len(*Type) int 206 | 207 | func (t *Type) Len() int { 208 | return rtype_Len(t) 209 | } 210 | 211 | //go:linkname rtype_NumField reflect.(*rtype).NumField 212 | //go:noescape 213 | func rtype_NumField(*Type) int 214 | 215 | func (t *Type) NumField() int { 216 | return rtype_NumField(t) 217 | } 218 | 219 | //go:linkname rtype_NumIn reflect.(*rtype).NumIn 220 | //go:noescape 221 | func rtype_NumIn(*Type) int 222 | 223 | func (t *Type) NumIn() int { 224 | return rtype_NumIn(t) 225 | } 226 | 227 | //go:linkname rtype_NumOut reflect.(*rtype).NumOut 228 | //go:noescape 229 | func rtype_NumOut(*Type) int 230 | 231 | func (t *Type) NumOut() int { 232 | return rtype_NumOut(t) 233 | } 234 | 235 | //go:linkname rtype_Out reflect.(*rtype).Out 236 | //go:noescape 237 | func rtype_Out(*Type, int) reflect.Type 238 | 239 | //go:linkname PtrTo reflect.(*rtype).ptrTo 240 | //go:noescape 241 | func PtrTo(*Type) *Type 242 | 243 | func (t *Type) Out(i int) reflect.Type { 244 | return rtype_Out(t, i) 245 | } 246 | 247 | //go:linkname IfaceIndir reflect.ifaceIndir 248 | //go:noescape 249 | func IfaceIndir(*Type) bool 250 | 251 | //go:linkname RType2Type reflect.toType 252 | //go:noescape 253 | func RType2Type(t *Type) reflect.Type 254 | 255 | type emptyInterface struct { 256 | _ *Type 257 | ptr unsafe.Pointer 258 | } 259 | 260 | func Type2RType(t reflect.Type) *Type { 261 | return (*Type)(((*emptyInterface)(unsafe.Pointer(&t))).ptr) 262 | } 263 | -------------------------------------------------------------------------------- /internal/runtime/struct_field.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "reflect" 5 | "strings" 6 | "unicode" 7 | ) 8 | 9 | func getTag(field reflect.StructField) string { 10 | return field.Tag.Get("json") 11 | } 12 | 13 | func IsIgnoredStructField(field reflect.StructField) bool { 14 | if field.PkgPath != "" { 15 | if field.Anonymous { 16 | t := field.Type 17 | if t.Kind() == reflect.Ptr { 18 | t = t.Elem() 19 | } 20 | if t.Kind() != reflect.Struct { 21 | return true 22 | } 23 | } else { 24 | // private field 25 | return true 26 | } 27 | } 28 | tag := getTag(field) 29 | return tag == "-" 30 | } 31 | 32 | type StructTag struct { 33 | Key string 34 | IsTaggedKey bool 35 | IsOmitEmpty bool 36 | IsString bool 37 | Field reflect.StructField 38 | } 39 | 40 | type StructTags []*StructTag 41 | 42 | func (t StructTags) ExistsKey(key string) bool { 43 | for _, tt := range t { 44 | if tt.Key == key { 45 | return true 46 | } 47 | } 48 | return false 49 | } 50 | 51 | func isValidTag(s string) bool { 52 | if s == "" { 53 | return false 54 | } 55 | for _, c := range s { 56 | switch { 57 | case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): 58 | // Backslash and quote chars are reserved, but 59 | // otherwise any punctuation chars are allowed 60 | // in a tag name. 61 | case !unicode.IsLetter(c) && !unicode.IsDigit(c): 62 | return false 63 | } 64 | } 65 | return true 66 | } 67 | 68 | func StructTagFromField(field reflect.StructField) *StructTag { 69 | keyName := field.Name 70 | tag := getTag(field) 71 | st := &StructTag{Field: field} 72 | opts := strings.Split(tag, ",") 73 | if len(opts) > 0 { 74 | if opts[0] != "" && isValidTag(opts[0]) { 75 | keyName = opts[0] 76 | st.IsTaggedKey = true 77 | } 78 | } 79 | st.Key = keyName 80 | if len(opts) > 1 { 81 | for _, opt := range opts[1:] { 82 | switch opt { 83 | case "omitempty": 84 | st.IsOmitEmpty = true 85 | case "string": 86 | st.IsString = true 87 | } 88 | } 89 | } 90 | return st 91 | } 92 | -------------------------------------------------------------------------------- /internal/runtime/type.go: -------------------------------------------------------------------------------- 1 | package runtime 2 | 3 | import ( 4 | "reflect" 5 | "sync" 6 | "unsafe" 7 | ) 8 | 9 | type SliceHeader struct { 10 | Data unsafe.Pointer 11 | Len int 12 | Cap int 13 | } 14 | 15 | const ( 16 | maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib 17 | ) 18 | 19 | type TypeAddr struct { 20 | BaseTypeAddr uintptr 21 | MaxTypeAddr uintptr 22 | AddrRange uintptr 23 | AddrShift uintptr 24 | } 25 | 26 | var ( 27 | typeAddr *TypeAddr 28 | once sync.Once 29 | ) 30 | 31 | //go:linkname typelinks reflect.typelinks 32 | func typelinks() ([]unsafe.Pointer, [][]int32) 33 | 34 | //go:linkname rtypeOff reflect.rtypeOff 35 | func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer 36 | 37 | func AnalyzeTypeAddr() *TypeAddr { 38 | once.Do(func() { 39 | sections, offsets := typelinks() 40 | if len(sections) != 1 { 41 | return 42 | } 43 | if len(offsets) != 1 { 44 | return 45 | } 46 | section := sections[0] 47 | offset := offsets[0] 48 | var ( 49 | min uintptr = uintptr(^uint(0)) 50 | max uintptr = 0 51 | isAligned64 = true 52 | isAligned32 = true 53 | ) 54 | for i := 0; i < len(offset); i++ { 55 | typ := (*Type)(rtypeOff(section, offset[i])) 56 | addr := uintptr(unsafe.Pointer(typ)) 57 | if min > addr { 58 | min = addr 59 | } 60 | if max < addr { 61 | max = addr 62 | } 63 | if typ.Kind() == reflect.Ptr { 64 | addr = uintptr(unsafe.Pointer(typ.Elem())) 65 | if min > addr { 66 | min = addr 67 | } 68 | if max < addr { 69 | max = addr 70 | } 71 | } 72 | isAligned64 = isAligned64 && (addr-min)&63 == 0 73 | isAligned32 = isAligned32 && (addr-min)&31 == 0 74 | } 75 | addrRange := max - min 76 | if addrRange == 0 { 77 | return 78 | } 79 | var addrShift uintptr 80 | if isAligned64 { 81 | addrShift = 6 82 | } else if isAligned32 { 83 | addrShift = 5 84 | } 85 | cacheSize := addrRange >> addrShift 86 | if cacheSize > maxAcceptableTypeAddrRange { 87 | return 88 | } 89 | typeAddr = &TypeAddr{ 90 | BaseTypeAddr: min, 91 | MaxTypeAddr: max, 92 | AddrRange: addrRange, 93 | AddrShift: addrShift, 94 | } 95 | }) 96 | 97 | return typeAddr 98 | } 99 | -------------------------------------------------------------------------------- /number_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "regexp" 9 | "testing" 10 | 11 | "github.com/goccy/go-json" 12 | ) 13 | 14 | func TestNumberIsValid(t *testing.T) { 15 | // From: https://stackoverflow.com/a/13340826 16 | var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 17 | 18 | validTests := []string{ 19 | "0", 20 | "-0", 21 | "1", 22 | "-1", 23 | "0.1", 24 | "-0.1", 25 | "1234", 26 | "-1234", 27 | "12.34", 28 | "-12.34", 29 | "12E0", 30 | "12E1", 31 | "12e34", 32 | "12E-0", 33 | "12e+1", 34 | "12e-34", 35 | "-12E0", 36 | "-12E1", 37 | "-12e34", 38 | "-12E-0", 39 | "-12e+1", 40 | "-12e-34", 41 | "1.2E0", 42 | "1.2E1", 43 | "1.2e34", 44 | "1.2E-0", 45 | "1.2e+1", 46 | "1.2e-34", 47 | "-1.2E0", 48 | "-1.2E1", 49 | "-1.2e34", 50 | "-1.2E-0", 51 | "-1.2e+1", 52 | "-1.2e-34", 53 | "0E0", 54 | "0E1", 55 | "0e34", 56 | "0E-0", 57 | "0e+1", 58 | "0e-34", 59 | "-0E0", 60 | "-0E1", 61 | "-0e34", 62 | "-0E-0", 63 | "-0e+1", 64 | "-0e-34", 65 | } 66 | 67 | for i, test := range validTests { 68 | if !isValidNumber(test) { 69 | t.Errorf("%d: %s should be valid", i, test) 70 | } 71 | 72 | var f float64 73 | if err := json.Unmarshal([]byte(test), &f); err != nil { 74 | t.Errorf("%d: %s should be valid but Unmarshal failed: %v", i, test, err) 75 | } 76 | 77 | if !jsonNumberRegexp.MatchString(test) { 78 | t.Errorf("%d: %s should be valid but regexp does not match", i, test) 79 | } 80 | } 81 | 82 | invalidTests := []string{ 83 | "", 84 | "invalid", 85 | "1.0.1", 86 | "1..1", 87 | "-1-2", 88 | "012a42", 89 | //"01.2", 90 | //"012", 91 | "12E12.12", 92 | "1e2e3", 93 | "1e+-2", 94 | "1e--23", 95 | "1e", 96 | "e1", 97 | "1e+", 98 | "1ea", 99 | "1a", 100 | "1.a", 101 | //"1.", 102 | //"01", 103 | //"1.e1", 104 | } 105 | 106 | for i, test := range invalidTests { 107 | if isValidNumber(test) { 108 | t.Errorf("%d: %s should be invalid", i, test) 109 | } 110 | 111 | var f float64 112 | if err := json.Unmarshal([]byte(test), &f); err == nil { 113 | t.Errorf("%d: %s should be invalid but unmarshal wrote %v", i, test, f) 114 | } 115 | 116 | if jsonNumberRegexp.MatchString(test) { 117 | t.Errorf("%d: %s should be invalid but matches regexp", i, test) 118 | } 119 | } 120 | } 121 | 122 | // isValidNumber reports whether s is a valid JSON number literal. 123 | func isValidNumber(s string) bool { 124 | // This function implements the JSON numbers grammar. 125 | // See https://tools.ietf.org/html/rfc7159#section-6 126 | // and https://www.json.org/img/number.png 127 | 128 | if s == "" { 129 | return false 130 | } 131 | 132 | // Optional - 133 | if s[0] == '-' { 134 | s = s[1:] 135 | if s == "" { 136 | return false 137 | } 138 | } 139 | 140 | // Digits 141 | switch { 142 | default: 143 | return false 144 | 145 | case s[0] == '0': 146 | s = s[1:] 147 | 148 | case '1' <= s[0] && s[0] <= '9': 149 | s = s[1:] 150 | for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 151 | s = s[1:] 152 | } 153 | } 154 | 155 | // . followed by 1 or more digits. 156 | if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' { 157 | s = s[2:] 158 | for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 159 | s = s[1:] 160 | } 161 | } 162 | 163 | // e or E followed by an optional - or + and 164 | // 1 or more digits. 165 | if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') { 166 | s = s[1:] 167 | if s[0] == '+' || s[0] == '-' { 168 | s = s[1:] 169 | if s == "" { 170 | return false 171 | } 172 | } 173 | for len(s) > 0 && '0' <= s[0] && s[0] <= '9' { 174 | s = s[1:] 175 | } 176 | } 177 | 178 | // Make sure we are at the end. 179 | return s == "" 180 | } 181 | 182 | func BenchmarkNumberIsValid(b *testing.B) { 183 | s := "-61657.61667E+61673" 184 | for i := 0; i < b.N; i++ { 185 | isValidNumber(s) 186 | } 187 | } 188 | 189 | func BenchmarkNumberIsValidRegexp(b *testing.B) { 190 | var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 191 | s := "-61657.61667E+61673" 192 | for i := 0; i < b.N; i++ { 193 | jsonNumberRegexp.MatchString(s) 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /option.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/goccy/go-json/internal/decoder" 7 | "github.com/goccy/go-json/internal/encoder" 8 | ) 9 | 10 | type EncodeOption = encoder.Option 11 | type EncodeOptionFunc func(*EncodeOption) 12 | 13 | // UnorderedMap doesn't sort when encoding map type. 14 | func UnorderedMap() EncodeOptionFunc { 15 | return func(opt *EncodeOption) { 16 | opt.Flag |= encoder.UnorderedMapOption 17 | } 18 | } 19 | 20 | // DisableHTMLEscape disables escaping of HTML characters ( '&', '<', '>' ) when encoding string. 21 | func DisableHTMLEscape() EncodeOptionFunc { 22 | return func(opt *EncodeOption) { 23 | opt.Flag &= ^encoder.HTMLEscapeOption 24 | } 25 | } 26 | 27 | // DisableNormalizeUTF8 28 | // 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. 29 | // This option disables this behaviour. You can expect faster speeds by applying this option, but be careful. 30 | // encoding/json implements here: https://github.com/golang/go/blob/6178d25fc0b28724b1b5aec2b1b74fc06d9294c7/src/encoding/json/encode.go#L1067-L1093. 31 | func DisableNormalizeUTF8() EncodeOptionFunc { 32 | return func(opt *EncodeOption) { 33 | opt.Flag &= ^encoder.NormalizeUTF8Option 34 | } 35 | } 36 | 37 | // Debug outputs debug information when panic occurs during encoding. 38 | func Debug() EncodeOptionFunc { 39 | return func(opt *EncodeOption) { 40 | opt.Flag |= encoder.DebugOption 41 | } 42 | } 43 | 44 | // DebugWith sets the destination to write debug messages. 45 | func DebugWith(w io.Writer) EncodeOptionFunc { 46 | return func(opt *EncodeOption) { 47 | opt.DebugOut = w 48 | } 49 | } 50 | 51 | // DebugDOT sets the destination to write opcodes graph. 52 | func DebugDOT(w io.WriteCloser) EncodeOptionFunc { 53 | return func(opt *EncodeOption) { 54 | opt.DebugDOTOut = w 55 | } 56 | } 57 | 58 | // Colorize add an identifier for coloring to the string of the encoded result. 59 | func Colorize(scheme *ColorScheme) EncodeOptionFunc { 60 | return func(opt *EncodeOption) { 61 | opt.Flag |= encoder.ColorizeOption 62 | opt.ColorScheme = scheme 63 | } 64 | } 65 | 66 | type DecodeOption = decoder.Option 67 | type DecodeOptionFunc func(*DecodeOption) 68 | 69 | // DecodeFieldPriorityFirstWin 70 | // in the default behavior, go-json, like encoding/json, 71 | // will reflect the result of the last evaluation when a field with the same name exists. 72 | // This option allow you to change this behavior. 73 | // this option reflects the result of the first evaluation if a field with the same name exists. 74 | // This behavior has a performance advantage as it allows the subsequent strings to be skipped if all fields have been evaluated. 75 | func DecodeFieldPriorityFirstWin() DecodeOptionFunc { 76 | return func(opt *DecodeOption) { 77 | opt.Flags |= decoder.FirstWinOption 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /path.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/goccy/go-json/internal/decoder" 7 | ) 8 | 9 | // CreatePath creates JSON Path. 10 | // 11 | // JSON Path rule 12 | // $ : root object or element. The JSON Path format must start with this operator, which refers to the outermost level of the JSON-formatted string. 13 | // . : child operator. You can identify child values using dot-notation. 14 | // .. : recursive descent. 15 | // [] : subscript operator. If the JSON object is an array, you can use brackets to specify the array index. 16 | // [*] : all objects/elements for array. 17 | // 18 | // Reserved words must be properly escaped when included in Path. 19 | // 20 | // Escape Rule 21 | // single quote style escape: e.g.) `$['a.b'].c` 22 | // double quote style escape: e.g.) `$."a.b".c` 23 | func CreatePath(p string) (*Path, error) { 24 | path, err := decoder.PathString(p).Build() 25 | if err != nil { 26 | return nil, err 27 | } 28 | return &Path{path: path}, nil 29 | } 30 | 31 | // Path represents JSON Path. 32 | type Path struct { 33 | path *decoder.Path 34 | } 35 | 36 | // RootSelectorOnly whether only the root selector ($) is used. 37 | func (p *Path) RootSelectorOnly() bool { 38 | return p.path.RootSelectorOnly 39 | } 40 | 41 | // UsedSingleQuotePathSelector whether single quote-based escaping was done when building the JSON Path. 42 | func (p *Path) UsedSingleQuotePathSelector() bool { 43 | return p.path.SingleQuotePathSelector 44 | } 45 | 46 | // UsedSingleQuotePathSelector whether double quote-based escaping was done when building the JSON Path. 47 | func (p *Path) UsedDoubleQuotePathSelector() bool { 48 | return p.path.DoubleQuotePathSelector 49 | } 50 | 51 | // Extract extracts a specific JSON string. 52 | func (p *Path) Extract(data []byte, optFuncs ...DecodeOptionFunc) ([][]byte, error) { 53 | return extractFromPath(p, data, optFuncs...) 54 | } 55 | 56 | // PathString returns original JSON Path string. 57 | func (p *Path) PathString() string { 58 | return p.path.String() 59 | } 60 | 61 | // Unmarshal extract and decode the value of the part corresponding to JSON Path from the input data. 62 | func (p *Path) Unmarshal(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) error { 63 | contents, err := extractFromPath(p, data, optFuncs...) 64 | if err != nil { 65 | return err 66 | } 67 | results := make([]interface{}, 0, len(contents)) 68 | for _, content := range contents { 69 | var result interface{} 70 | if err := Unmarshal(content, &result); err != nil { 71 | return err 72 | } 73 | results = append(results, result) 74 | } 75 | if err := decoder.AssignValue(reflect.ValueOf(results), reflect.ValueOf(v)); err != nil { 76 | return err 77 | } 78 | return nil 79 | } 80 | 81 | // Get extract and substitute the value of the part corresponding to JSON Path from the input value. 82 | func (p *Path) Get(src, dst interface{}) error { 83 | return p.path.Get(reflect.ValueOf(src), reflect.ValueOf(dst)) 84 | } 85 | -------------------------------------------------------------------------------- /path_test.go: -------------------------------------------------------------------------------- 1 | package json_test 2 | 3 | import ( 4 | "bytes" 5 | "reflect" 6 | "sort" 7 | "testing" 8 | 9 | "github.com/goccy/go-json" 10 | ) 11 | 12 | func TestExtractPath(t *testing.T) { 13 | src := []byte(`{"a":{"b":10,"c":true},"b":"text"}`) 14 | t.Run("$.a.b", func(t *testing.T) { 15 | path, err := json.CreatePath("$.a.b") 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | contents, err := path.Extract(src) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | if len(contents) != 1 { 24 | t.Fatal("failed to extract") 25 | } 26 | if !bytes.Equal(contents[0], []byte("10")) { 27 | t.Fatal("failed to extract") 28 | } 29 | }) 30 | t.Run("$.b", func(t *testing.T) { 31 | path, err := json.CreatePath("$.b") 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | contents, err := path.Extract(src) 36 | if err != nil { 37 | t.Fatal(err) 38 | } 39 | if len(contents) != 1 { 40 | t.Fatal("failed to extract") 41 | } 42 | if !bytes.Equal(contents[0], []byte(`"text"`)) { 43 | t.Fatal("failed to extract") 44 | } 45 | }) 46 | t.Run("$.a", func(t *testing.T) { 47 | path, err := json.CreatePath("$.a") 48 | if err != nil { 49 | t.Fatal(err) 50 | } 51 | contents, err := path.Extract(src) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | if len(contents) != 1 { 56 | t.Fatal("failed to extract") 57 | } 58 | if !bytes.Equal(contents[0], []byte(`{"b":10,"c":true}`)) { 59 | t.Fatal("failed to extract") 60 | } 61 | }) 62 | } 63 | 64 | func TestUnmarshalPath(t *testing.T) { 65 | t.Run("int", func(t *testing.T) { 66 | src := []byte(`{"a":{"b":10,"c":true},"b":"text"}`) 67 | t.Run("success", func(t *testing.T) { 68 | path, err := json.CreatePath("$.a.b") 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | var v int 73 | if err := path.Unmarshal(src, &v); err != nil { 74 | t.Fatal(err) 75 | } 76 | if v != 10 { 77 | t.Fatal("failed to unmarshal path") 78 | } 79 | }) 80 | t.Run("failure", func(t *testing.T) { 81 | path, err := json.CreatePath("$.a.c") 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | var v map[string]interface{} 86 | if err := path.Unmarshal(src, &v); err == nil { 87 | t.Fatal("expected error") 88 | } 89 | }) 90 | }) 91 | t.Run("bool", func(t *testing.T) { 92 | src := []byte(`{"a":{"b":10,"c":true},"b":"text"}`) 93 | t.Run("success", func(t *testing.T) { 94 | path, err := json.CreatePath("$.a.c") 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | var v bool 99 | if err := path.Unmarshal(src, &v); err != nil { 100 | t.Fatal(err) 101 | } 102 | if !v { 103 | t.Fatal("failed to unmarshal path") 104 | } 105 | }) 106 | t.Run("failure", func(t *testing.T) { 107 | path, err := json.CreatePath("$.a.b") 108 | if err != nil { 109 | t.Fatal(err) 110 | } 111 | var v bool 112 | if err := path.Unmarshal(src, &v); err == nil { 113 | t.Fatal("expected error") 114 | } 115 | }) 116 | }) 117 | t.Run("map", func(t *testing.T) { 118 | src := []byte(`{"a":{"b":10,"c":true},"b":"text"}`) 119 | t.Run("success", func(t *testing.T) { 120 | path, err := json.CreatePath("$.a") 121 | if err != nil { 122 | t.Fatal(err) 123 | } 124 | var v map[string]interface{} 125 | if err := path.Unmarshal(src, &v); err != nil { 126 | t.Fatal(err) 127 | } 128 | if len(v) != 2 { 129 | t.Fatal("failed to decode map") 130 | } 131 | }) 132 | }) 133 | 134 | t.Run("path with single quote selector", func(t *testing.T) { 135 | path, err := json.CreatePath("$['a.b'].c") 136 | if err != nil { 137 | t.Fatal(err) 138 | } 139 | 140 | var v string 141 | if err := path.Unmarshal([]byte(`{"a.b": {"c": "world"}}`), &v); err != nil { 142 | t.Fatal(err) 143 | } 144 | if v != "world" { 145 | t.Fatal("failed to unmarshal path") 146 | } 147 | }) 148 | t.Run("path with double quote selector", func(t *testing.T) { 149 | path, err := json.CreatePath(`$."a.b".c`) 150 | if err != nil { 151 | t.Fatal(err) 152 | } 153 | 154 | var v string 155 | if err := path.Unmarshal([]byte(`{"a.b": {"c": "world"}}`), &v); err != nil { 156 | t.Fatal(err) 157 | } 158 | if v != "world" { 159 | t.Fatal("failed to unmarshal path") 160 | } 161 | }) 162 | } 163 | 164 | func TestGetPath(t *testing.T) { 165 | t.Run("selector", func(t *testing.T) { 166 | var v interface{} 167 | if err := json.Unmarshal([]byte(`{"a":{"b":10,"c":true},"b":"text"}`), &v); err != nil { 168 | t.Fatal(err) 169 | } 170 | path, err := json.CreatePath("$.a.b") 171 | if err != nil { 172 | t.Fatal(err) 173 | } 174 | var b int 175 | if err := path.Get(v, &b); err != nil { 176 | t.Fatal(err) 177 | } 178 | if b != 10 { 179 | t.Fatalf("failed to decode by json.Get") 180 | } 181 | }) 182 | t.Run("index", func(t *testing.T) { 183 | var v interface{} 184 | if err := json.Unmarshal([]byte(`{"a":[{"b":10,"c":true},{"b":"text"}]}`), &v); err != nil { 185 | t.Fatal(err) 186 | } 187 | path, err := json.CreatePath("$.a[0].b") 188 | if err != nil { 189 | t.Fatal(err) 190 | } 191 | var b int 192 | if err := path.Get(v, &b); err != nil { 193 | t.Fatal(err) 194 | } 195 | if b != 10 { 196 | t.Fatalf("failed to decode by json.Get") 197 | } 198 | }) 199 | t.Run("indexAll", func(t *testing.T) { 200 | var v interface{} 201 | if err := json.Unmarshal([]byte(`{"a":[{"b":1,"c":true},{"b":2},{"b":3}]}`), &v); err != nil { 202 | t.Fatal(err) 203 | } 204 | path, err := json.CreatePath("$.a[*].b") 205 | if err != nil { 206 | t.Fatal(err) 207 | } 208 | var b []int 209 | if err := path.Get(v, &b); err != nil { 210 | t.Fatal(err) 211 | } 212 | if !reflect.DeepEqual(b, []int{1, 2, 3}) { 213 | t.Fatalf("failed to decode by json.Get") 214 | } 215 | }) 216 | t.Run("recursive", func(t *testing.T) { 217 | var v interface{} 218 | if err := json.Unmarshal([]byte(`{"a":[{"b":1,"c":true},{"b":2},{"b":3}],"a2":{"b":4}}`), &v); err != nil { 219 | t.Fatal(err) 220 | } 221 | path, err := json.CreatePath("$..b") 222 | if err != nil { 223 | t.Fatal(err) 224 | } 225 | var b []int 226 | if err := path.Get(v, &b); err != nil { 227 | t.Fatal(err) 228 | } 229 | sort.Ints(b) 230 | if !reflect.DeepEqual(b, []int{1, 2, 3, 4}) { 231 | t.Fatalf("failed to decode by json.Get") 232 | } 233 | }) 234 | } 235 | -------------------------------------------------------------------------------- /query.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "github.com/goccy/go-json/internal/encoder" 5 | ) 6 | 7 | type ( 8 | // FieldQuery you can dynamically filter the fields in the structure by creating a FieldQuery, 9 | // adding it to context.Context using SetFieldQueryToContext and then passing it to MarshalContext. 10 | // This is a type-safe operation, so it is faster than filtering using map[string]interface{}. 11 | FieldQuery = encoder.FieldQuery 12 | FieldQueryString = encoder.FieldQueryString 13 | ) 14 | 15 | var ( 16 | // FieldQueryFromContext get current FieldQuery from context.Context. 17 | FieldQueryFromContext = encoder.FieldQueryFromContext 18 | // SetFieldQueryToContext set current FieldQuery to context.Context. 19 | SetFieldQueryToContext = encoder.SetFieldQueryToContext 20 | ) 21 | 22 | // BuildFieldQuery builds FieldQuery by fieldName or sub field query. 23 | // First, specify the field name that you want to keep in structure type. 24 | // If the field you want to keep is a structure type, by creating a sub field query using BuildSubFieldQuery, 25 | // you can select the fields you want to keep in the structure. 26 | // This description can be written recursively. 27 | func BuildFieldQuery(fields ...FieldQueryString) (*FieldQuery, error) { 28 | query, err := Marshal(fields) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return FieldQueryString(query).Build() 33 | } 34 | 35 | // BuildSubFieldQuery builds sub field query. 36 | func BuildSubFieldQuery(name string) *SubFieldQuery { 37 | return &SubFieldQuery{name: name} 38 | } 39 | 40 | type SubFieldQuery struct { 41 | name string 42 | } 43 | 44 | func (q *SubFieldQuery) Fields(fields ...FieldQueryString) FieldQueryString { 45 | query, _ := Marshal(map[string][]FieldQueryString{q.name: fields}) 46 | return FieldQueryString(query) 47 | } 48 | -------------------------------------------------------------------------------- /query_test.go: -------------------------------------------------------------------------------- 1 | package json_test 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/goccy/go-json" 9 | ) 10 | 11 | type queryTestX struct { 12 | XA int 13 | XB string 14 | XC *queryTestY 15 | XD bool 16 | XE float32 17 | } 18 | 19 | type queryTestY struct { 20 | YA int 21 | YB string 22 | YC *queryTestZ 23 | YD bool 24 | YE float32 25 | } 26 | 27 | type queryTestZ struct { 28 | ZA string 29 | ZB bool 30 | ZC int 31 | } 32 | 33 | func (z *queryTestZ) MarshalJSON(ctx context.Context) ([]byte, error) { 34 | type _queryTestZ queryTestZ 35 | return json.MarshalContext(ctx, (*_queryTestZ)(z)) 36 | } 37 | 38 | func TestFieldQuery(t *testing.T) { 39 | query, err := json.BuildFieldQuery( 40 | "XA", 41 | "XB", 42 | json.BuildSubFieldQuery("XC").Fields( 43 | "YA", 44 | "YB", 45 | json.BuildSubFieldQuery("YC").Fields( 46 | "ZA", 47 | "ZB", 48 | ), 49 | ), 50 | ) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | if !reflect.DeepEqual(query, &json.FieldQuery{ 55 | Fields: []*json.FieldQuery{ 56 | { 57 | Name: "XA", 58 | }, 59 | { 60 | Name: "XB", 61 | }, 62 | { 63 | Name: "XC", 64 | Fields: []*json.FieldQuery{ 65 | { 66 | Name: "YA", 67 | }, 68 | { 69 | Name: "YB", 70 | }, 71 | { 72 | Name: "YC", 73 | Fields: []*json.FieldQuery{ 74 | { 75 | Name: "ZA", 76 | }, 77 | { 78 | Name: "ZB", 79 | }, 80 | }, 81 | }, 82 | }, 83 | }, 84 | }, 85 | }) { 86 | t.Fatal("cannot get query") 87 | } 88 | queryStr, err := query.QueryString() 89 | if err != nil { 90 | t.Fatal(err) 91 | } 92 | if queryStr != `["XA","XB",{"XC":["YA","YB",{"YC":["ZA","ZB"]}]}]` { 93 | t.Fatalf("failed to create query string. %s", queryStr) 94 | } 95 | ctx := json.SetFieldQueryToContext(context.Background(), query) 96 | b, err := json.MarshalContext(ctx, &queryTestX{ 97 | XA: 1, 98 | XB: "xb", 99 | XC: &queryTestY{ 100 | YA: 2, 101 | YB: "yb", 102 | YC: &queryTestZ{ 103 | ZA: "za", 104 | ZB: true, 105 | ZC: 3, 106 | }, 107 | YD: true, 108 | YE: 4, 109 | }, 110 | XD: true, 111 | XE: 5, 112 | }) 113 | if err != nil { 114 | t.Fatal(err) 115 | } 116 | expected := `{"XA":1,"XB":"xb","XC":{"YA":2,"YB":"yb","YC":{"ZA":"za","ZB":true}}}` 117 | got := string(b) 118 | if expected != got { 119 | t.Fatalf("failed to encode with field query: expected %q but got %q", expected, got) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /size_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "testing" 5 | "unsafe" 6 | 7 | "github.com/goccy/go-json/internal/encoder" 8 | ) 9 | 10 | func TestOpcodeSize(t *testing.T) { 11 | const uintptrSize = 4 << (^uintptr(0) >> 63) 12 | if uintptrSize == 8 { 13 | size := unsafe.Sizeof(encoder.Opcode{}) 14 | if size != 120 { 15 | t.Fatalf("unexpected opcode size: expected 112bytes but got %dbytes", size) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tagkey_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/goccy/go-json" 11 | ) 12 | 13 | type basicLatin2xTag struct { 14 | V string `json:"$%-/"` 15 | } 16 | 17 | type basicLatin3xTag struct { 18 | V string `json:"0123456789"` 19 | } 20 | 21 | type basicLatin4xTag struct { 22 | V string `json:"ABCDEFGHIJKLMO"` 23 | } 24 | 25 | type basicLatin5xTag struct { 26 | V string `json:"PQRSTUVWXYZ_"` 27 | } 28 | 29 | type basicLatin6xTag struct { 30 | V string `json:"abcdefghijklmno"` 31 | } 32 | 33 | type basicLatin7xTag struct { 34 | V string `json:"pqrstuvwxyz"` 35 | } 36 | 37 | type miscPlaneTag struct { 38 | V string `json:"色は匂へど"` 39 | } 40 | 41 | type percentSlashTag struct { 42 | V string `json:"text/html%"` // https://golang.org/issue/2718 43 | } 44 | 45 | type punctuationTag struct { 46 | V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // https://golang.org/issue/3546 47 | } 48 | 49 | type dashTag struct { 50 | V string `json:"-,"` 51 | } 52 | 53 | type emptyTag struct { 54 | W string 55 | } 56 | 57 | type misnamedTag struct { 58 | X string `jsom:"Misnamed"` 59 | } 60 | 61 | type badFormatTag struct { 62 | Y string `:"BadFormat"` 63 | } 64 | 65 | type badCodeTag struct { 66 | Z string `json:" !\"#&'()*+,."` 67 | } 68 | 69 | type spaceTag struct { 70 | Q string `json:"With space"` 71 | } 72 | 73 | type unicodeTag struct { 74 | W string `json:"Ελλάδα"` 75 | } 76 | 77 | var structTagObjectKeyTests = []struct { 78 | raw interface{} 79 | value string 80 | key string 81 | }{ 82 | {basicLatin2xTag{"2x"}, "2x", "$%-/"}, 83 | {basicLatin3xTag{"3x"}, "3x", "0123456789"}, 84 | {basicLatin4xTag{"4x"}, "4x", "ABCDEFGHIJKLMO"}, 85 | {basicLatin5xTag{"5x"}, "5x", "PQRSTUVWXYZ_"}, 86 | {basicLatin6xTag{"6x"}, "6x", "abcdefghijklmno"}, 87 | {basicLatin7xTag{"7x"}, "7x", "pqrstuvwxyz"}, 88 | {miscPlaneTag{"いろはにほへと"}, "いろはにほへと", "色は匂へど"}, 89 | {dashTag{"foo"}, "foo", "-"}, 90 | {emptyTag{"Pour Moi"}, "Pour Moi", "W"}, 91 | {misnamedTag{"Animal Kingdom"}, "Animal Kingdom", "X"}, 92 | {badFormatTag{"Orfevre"}, "Orfevre", "Y"}, 93 | {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, 94 | {percentSlashTag{"brut"}, "brut", "text/html%"}, 95 | {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, 96 | {spaceTag{"Perreddu"}, "Perreddu", "With space"}, 97 | {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, 98 | } 99 | 100 | func TestStructTagObjectKey(t *testing.T) { 101 | for _, tt := range structTagObjectKeyTests { 102 | b, err := json.Marshal(tt.raw) 103 | if err != nil { 104 | t.Fatalf("Marshal(%#q) failed: %v", tt.raw, err) 105 | } 106 | var f interface{} 107 | err = json.Unmarshal(b, &f) 108 | if err != nil { 109 | t.Fatalf("Unmarshal(%#q) failed: %v", b, err) 110 | } 111 | for i, v := range f.(map[string]interface{}) { 112 | switch i { 113 | case tt.key: 114 | if s, ok := v.(string); !ok || s != tt.value { 115 | t.Fatalf("Unexpected value: %#q, want %v", s, tt.value) 116 | } 117 | default: 118 | t.Fatalf("Unexpected key: %#q, from %#q", i, b) 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /test/cover/cover_helper_test.go: -------------------------------------------------------------------------------- 1 | package json_test 2 | 3 | import ( 4 | "bytes" 5 | stdjson "encoding/json" 6 | 7 | "github.com/goccy/go-json" 8 | ) 9 | 10 | func intptr(v int) *int { return &v } 11 | func intptr3(v int) ***int { vv := &v; vvv := &vv; return &vvv } 12 | func int8ptr(v int8) *int8 { return &v } 13 | func int8ptr3(v int8) ***int8 { vv := &v; vvv := &vv; return &vvv } 14 | func int16ptr(v int16) *int16 { return &v } 15 | func int16ptr3(v int16) ***int16 { vv := &v; vvv := &vv; return &vvv } 16 | func int32ptr(v int32) *int32 { return &v } 17 | func int32ptr3(v int32) ***int32 { vv := &v; vvv := &vv; return &vvv } 18 | func int64ptr(v int64) *int64 { return &v } 19 | func int64ptr3(v int64) ***int64 { vv := &v; vvv := &vv; return &vvv } 20 | func uptr(v uint) *uint { return &v } 21 | func uintptr3(v uint) ***uint { vv := &v; vvv := &vv; return &vvv } 22 | func uint8ptr(v uint8) *uint8 { return &v } 23 | func uint8ptr3(v uint8) ***uint8 { vv := &v; vvv := &vv; return &vvv } 24 | func uint16ptr(v uint16) *uint16 { return &v } 25 | func uint16ptr3(v uint16) ***uint16 { vv := &v; vvv := &vv; return &vvv } 26 | func uint32ptr(v uint32) *uint32 { return &v } 27 | func uint32ptr3(v uint32) ***uint32 { vv := &v; vvv := &vv; return &vvv } 28 | func uint64ptr(v uint64) *uint64 { return &v } 29 | func uint64ptr3(v uint64) ***uint64 { vv := &v; vvv := &vv; return &vvv } 30 | func float32ptr(v float32) *float32 { return &v } 31 | func float32ptr3(v float32) ***float32 { vv := &v; vvv := &vv; return &vvv } 32 | func float64ptr(v float64) *float64 { return &v } 33 | func float64ptr3(v float64) ***float64 { vv := &v; vvv := &vv; return &vvv } 34 | func stringptr(v string) *string { return &v } 35 | func stringptr3(v string) ***string { vv := &v; vvv := &vv; return &vvv } 36 | func boolptr(v bool) *bool { return &v } 37 | func boolptr3(v bool) ***bool { vv := &v; vvv := &vv; return &vvv } 38 | func bytesptr(v []byte) *[]byte { return &v } 39 | func bytesptr3(v []byte) ***[]byte { vv := &v; vvv := &vv; return &vvv } 40 | func numberptr(v json.Number) *json.Number { return &v } 41 | func numberptr3(v json.Number) ***json.Number { vv := &v; vvv := &vv; return &vvv } 42 | func sliceptr(v []int) *[]int { return &v } 43 | func arrayptr(v [2]int) *[2]int { return &v } 44 | func mapptr(v map[string]int) *map[string]int { return &v } 45 | 46 | func encodeByEncodingJSON(data interface{}, indent, escape bool) string { 47 | var buf bytes.Buffer 48 | enc := stdjson.NewEncoder(&buf) 49 | enc.SetEscapeHTML(escape) 50 | if indent { 51 | enc.SetIndent("", " ") 52 | } 53 | enc.Encode(data) 54 | return buf.String() 55 | } 56 | -------------------------------------------------------------------------------- /test/example/example_marshaling_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "strings" 11 | 12 | "github.com/goccy/go-json" 13 | ) 14 | 15 | type Animal int 16 | 17 | const ( 18 | Unknown Animal = iota 19 | Gopher 20 | Zebra 21 | ) 22 | 23 | func (a *Animal) UnmarshalJSON(b []byte) error { 24 | var s string 25 | if err := json.Unmarshal(b, &s); err != nil { 26 | return err 27 | } 28 | switch strings.ToLower(s) { 29 | default: 30 | *a = Unknown 31 | case "gopher": 32 | *a = Gopher 33 | case "zebra": 34 | *a = Zebra 35 | } 36 | 37 | return nil 38 | } 39 | 40 | func (a Animal) MarshalJSON() ([]byte, error) { 41 | var s string 42 | switch a { 43 | default: 44 | s = "unknown" 45 | case Gopher: 46 | s = "gopher" 47 | case Zebra: 48 | s = "zebra" 49 | } 50 | 51 | return json.Marshal(s) 52 | } 53 | 54 | func Example_customMarshalJSON() { 55 | blob := `["gopher","armadillo","zebra","unknown","gopher","bee","gopher","zebra"]` 56 | var zoo []Animal 57 | if err := json.Unmarshal([]byte(blob), &zoo); err != nil { 58 | log.Fatal(err) 59 | } 60 | 61 | census := make(map[Animal]int) 62 | for _, animal := range zoo { 63 | census[animal] += 1 64 | } 65 | 66 | fmt.Printf("Zoo Census:\n* Gophers: %d\n* Zebras: %d\n* Unknown: %d\n", 67 | census[Gopher], census[Zebra], census[Unknown]) 68 | 69 | // Output: 70 | // Zoo Census: 71 | // * Gophers: 3 72 | // * Zebras: 2 73 | // * Unknown: 3 74 | } 75 | -------------------------------------------------------------------------------- /test/example/example_query_test.go: -------------------------------------------------------------------------------- 1 | package json_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/goccy/go-json" 9 | ) 10 | 11 | type User struct { 12 | ID int64 13 | Name string 14 | Age int 15 | Address UserAddressResolver 16 | } 17 | 18 | type UserAddress struct { 19 | UserID int64 20 | PostCode string 21 | City string 22 | Address1 string 23 | Address2 string 24 | } 25 | 26 | type UserRepository struct { 27 | uaRepo *UserAddressRepository 28 | } 29 | 30 | func NewUserRepository() *UserRepository { 31 | return &UserRepository{ 32 | uaRepo: NewUserAddressRepository(), 33 | } 34 | } 35 | 36 | type UserAddressRepository struct{} 37 | 38 | func NewUserAddressRepository() *UserAddressRepository { 39 | return &UserAddressRepository{} 40 | } 41 | 42 | type UserAddressResolver func(context.Context) (*UserAddress, error) 43 | 44 | func (resolver UserAddressResolver) MarshalJSON(ctx context.Context) ([]byte, error) { 45 | address, err := resolver(ctx) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return json.MarshalContext(ctx, address) 50 | } 51 | 52 | func (r *UserRepository) FindByID(ctx context.Context, id int64) (*User, error) { 53 | user := &User{ID: id, Name: "Ken", Age: 20} 54 | // resolve relation from User to UserAddress 55 | user.Address = func(ctx context.Context) (*UserAddress, error) { 56 | return r.uaRepo.FindByUserID(ctx, user.ID) 57 | } 58 | return user, nil 59 | } 60 | 61 | func (*UserAddressRepository) FindByUserID(ctx context.Context, id int64) (*UserAddress, error) { 62 | return &UserAddress{ 63 | UserID: id, 64 | City: "A", 65 | Address1: "foo", 66 | Address2: "bar", 67 | }, nil 68 | } 69 | 70 | func Example_fieldQuery() { 71 | ctx := context.Background() 72 | userRepo := NewUserRepository() 73 | user, err := userRepo.FindByID(ctx, 1) 74 | if err != nil { 75 | log.Fatal(err) 76 | } 77 | query, err := json.BuildFieldQuery( 78 | "Name", 79 | "Age", 80 | json.BuildSubFieldQuery("Address").Fields( 81 | "City", 82 | ), 83 | ) 84 | if err != nil { 85 | log.Fatal(err) 86 | } 87 | ctx = json.SetFieldQueryToContext(ctx, query) 88 | b, err := json.MarshalContext(ctx, user) 89 | if err != nil { 90 | log.Fatal(err) 91 | } 92 | fmt.Println(string(b)) 93 | 94 | // Output: 95 | // {"Name":"Ken","Age":20,"Address":{"City":"A"}} 96 | } 97 | -------------------------------------------------------------------------------- /test/example/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "log" 12 | "os" 13 | "strings" 14 | 15 | "github.com/goccy/go-json" 16 | ) 17 | 18 | func ExampleMarshal() { 19 | type ColorGroup struct { 20 | ID int 21 | Name string 22 | Colors []string 23 | } 24 | group := ColorGroup{ 25 | ID: 1, 26 | Name: "Reds", 27 | Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, 28 | } 29 | b, err := json.Marshal(group) 30 | if err != nil { 31 | fmt.Println("error:", err) 32 | } 33 | os.Stdout.Write(b) 34 | // Output: 35 | // {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} 36 | } 37 | 38 | func ExampleUnmarshal() { 39 | var jsonBlob = []byte(`[ 40 | {"Name": "Platypus", "Order": "Monotremata"}, 41 | {"Name": "Quoll", "Order": "Dasyuromorphia"} 42 | ]`) 43 | type Animal struct { 44 | Name string 45 | Order string 46 | } 47 | var animals []Animal 48 | err := json.Unmarshal(jsonBlob, &animals) 49 | if err != nil { 50 | fmt.Println("error:", err) 51 | } 52 | fmt.Printf("%+v", animals) 53 | // Output: 54 | // [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}] 55 | } 56 | 57 | // This example uses a Decoder to decode a stream of distinct JSON values. 58 | func ExampleDecoder() { 59 | const jsonStream = ` 60 | {"Name": "Ed", "Text": "Knock knock."} 61 | {"Name": "Sam", "Text": "Who's there?"} 62 | {"Name": "Ed", "Text": "Go fmt."} 63 | {"Name": "Sam", "Text": "Go fmt who?"} 64 | {"Name": "Ed", "Text": "Go fmt yourself!"} 65 | ` 66 | type Message struct { 67 | Name, Text string 68 | } 69 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 70 | for { 71 | var m Message 72 | if err := dec.Decode(&m); err == io.EOF { 73 | break 74 | } else if err != nil { 75 | log.Fatal(err) 76 | } 77 | fmt.Printf("%s: %s\n", m.Name, m.Text) 78 | } 79 | // Output: 80 | // Ed: Knock knock. 81 | // Sam: Who's there? 82 | // Ed: Go fmt. 83 | // Sam: Go fmt who? 84 | // Ed: Go fmt yourself! 85 | } 86 | 87 | // This example uses a Decoder to decode a stream of distinct JSON values. 88 | func ExampleDecoder_Token() { 89 | const jsonStream = ` 90 | {"Message": "Hello", "Array": [1, 2, 3], "Null": null, "Number": 1.234} 91 | ` 92 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 93 | for { 94 | t, err := dec.Token() 95 | if err == io.EOF { 96 | break 97 | } 98 | if err != nil { 99 | log.Fatal(err) 100 | } 101 | fmt.Printf("%T: %v", t, t) 102 | if dec.More() { 103 | fmt.Printf(" (more)") 104 | } 105 | fmt.Printf("\n") 106 | } 107 | // Output: 108 | // json.Delim: { (more) 109 | // string: Message (more) 110 | // string: Hello (more) 111 | // string: Array (more) 112 | // json.Delim: [ (more) 113 | // float64: 1 (more) 114 | // float64: 2 (more) 115 | // float64: 3 116 | // json.Delim: ] (more) 117 | // string: Null (more) 118 | // : (more) 119 | // string: Number (more) 120 | // float64: 1.234 121 | // json.Delim: } 122 | } 123 | 124 | // This example uses a Decoder to decode a streaming array of JSON objects. 125 | func ExampleDecoder_Decode_stream() { 126 | const jsonStream = ` 127 | [ 128 | {"Name": "Ed", "Text": "Knock knock."}, 129 | {"Name": "Sam", "Text": "Who's there?"}, 130 | {"Name": "Ed", "Text": "Go fmt."}, 131 | {"Name": "Sam", "Text": "Go fmt who?"}, 132 | {"Name": "Ed", "Text": "Go fmt yourself!"} 133 | ] 134 | ` 135 | type Message struct { 136 | Name, Text string 137 | } 138 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 139 | 140 | // read open bracket 141 | t, err := dec.Token() 142 | if err != nil { 143 | log.Fatal(err) 144 | } 145 | fmt.Printf("%T: %v\n", t, t) 146 | 147 | // while the array contains values 148 | for dec.More() { 149 | var m Message 150 | // decode an array value (Message) 151 | err := dec.Decode(&m) 152 | if err != nil { 153 | log.Fatal(err) 154 | } 155 | 156 | fmt.Printf("%v: %v\n", m.Name, m.Text) 157 | } 158 | 159 | // read closing bracket 160 | t, err = dec.Token() 161 | if err != nil { 162 | log.Fatal(err) 163 | } 164 | fmt.Printf("%T: %v\n", t, t) 165 | 166 | // Output: 167 | // json.Delim: [ 168 | // Ed: Knock knock. 169 | // Sam: Who's there? 170 | // Ed: Go fmt. 171 | // Sam: Go fmt who? 172 | // Ed: Go fmt yourself! 173 | // json.Delim: ] 174 | } 175 | 176 | // This example uses RawMessage to delay parsing part of a JSON message. 177 | func ExampleRawMessage_unmarshal() { 178 | type Color struct { 179 | Space string 180 | Point json.RawMessage // delay parsing until we know the color space 181 | } 182 | type RGB struct { 183 | R uint8 184 | G uint8 185 | B uint8 186 | } 187 | type YCbCr struct { 188 | Y uint8 189 | Cb int8 190 | Cr int8 191 | } 192 | 193 | var j = []byte(`[ 194 | {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}}, 195 | {"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}} 196 | ]`) 197 | var colors []Color 198 | err := json.Unmarshal(j, &colors) 199 | if err != nil { 200 | log.Fatalln("error:", err) 201 | } 202 | 203 | for _, c := range colors { 204 | var dst interface{} 205 | switch c.Space { 206 | case "RGB": 207 | dst = new(RGB) 208 | case "YCbCr": 209 | dst = new(YCbCr) 210 | } 211 | err := json.Unmarshal(c.Point, dst) 212 | if err != nil { 213 | log.Fatalln("error:", err) 214 | } 215 | fmt.Println(c.Space, dst) 216 | } 217 | // Output: 218 | // YCbCr &{255 0 -10} 219 | // RGB &{98 218 255} 220 | } 221 | 222 | // This example uses RawMessage to use a precomputed JSON during marshal. 223 | func ExampleRawMessage_marshal() { 224 | h := json.RawMessage(`{"precomputed": true}`) 225 | 226 | c := struct { 227 | Header *json.RawMessage `json:"header"` 228 | Body string `json:"body"` 229 | }{Header: &h, Body: "Hello Gophers!"} 230 | 231 | b, err := json.MarshalIndent(&c, "", "\t") 232 | if err != nil { 233 | fmt.Println("error:", err) 234 | } 235 | os.Stdout.Write(b) 236 | 237 | // Output: 238 | // { 239 | // "header": { 240 | // "precomputed": true 241 | // }, 242 | // "body": "Hello Gophers!" 243 | // } 244 | } 245 | 246 | func ExampleIndent() { 247 | type Road struct { 248 | Name string 249 | Number int 250 | } 251 | roads := []Road{ 252 | {"Diamond Fork", 29}, 253 | {"Sheep Creek", 51}, 254 | } 255 | 256 | b, err := json.Marshal(roads) 257 | if err != nil { 258 | log.Fatal(err) 259 | } 260 | 261 | var out bytes.Buffer 262 | json.Indent(&out, b, "=", "\t") 263 | out.WriteTo(os.Stdout) 264 | // Output: 265 | // [ 266 | // = { 267 | // = "Name": "Diamond Fork", 268 | // = "Number": 29 269 | // = }, 270 | // = { 271 | // = "Name": "Sheep Creek", 272 | // = "Number": 51 273 | // = } 274 | // =] 275 | } 276 | 277 | /* 278 | func ExampleMarshalIndent() { 279 | data := map[string]int{ 280 | "a": 1, 281 | "b": 2, 282 | } 283 | 284 | json, err := json.MarshalIndent(data, "", "") 285 | if err != nil { 286 | log.Fatal(err) 287 | } 288 | 289 | fmt.Println(string(json)) 290 | // Output: 291 | // { 292 | // "a": 1, 293 | // "b": 2 294 | // } 295 | } 296 | */ 297 | 298 | func ExampleValid() { 299 | goodJSON := `{"example": 1}` 300 | badJSON := `{"example":2:]}}` 301 | 302 | fmt.Println(json.Valid([]byte(goodJSON)), json.Valid([]byte(badJSON))) 303 | // Output: 304 | // true false 305 | } 306 | 307 | func ExampleHTMLEscape() { 308 | var out bytes.Buffer 309 | json.HTMLEscape(&out, []byte(`{"Name":"HTML content"}`)) 310 | out.WriteTo(os.Stdout) 311 | // Output: 312 | //{"Name":"\u003cb\u003eHTML content\u003c/b\u003e"} 313 | } 314 | -------------------------------------------------------------------------------- /test/example/example_text_marshaling_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "strings" 11 | 12 | "github.com/goccy/go-json" 13 | ) 14 | 15 | type Size int 16 | 17 | const ( 18 | unrecognized Size = iota 19 | small 20 | large 21 | ) 22 | 23 | func (s *Size) UnmarshalText(text []byte) error { 24 | switch strings.ToLower(string(text)) { 25 | default: 26 | *s = unrecognized 27 | case "small": 28 | *s = small 29 | case "large": 30 | *s = large 31 | } 32 | return nil 33 | } 34 | 35 | func (s Size) MarshalText() ([]byte, error) { 36 | var name string 37 | switch s { 38 | default: 39 | name = "unrecognized" 40 | case small: 41 | name = "small" 42 | case large: 43 | name = "large" 44 | } 45 | return []byte(name), nil 46 | } 47 | 48 | func Example_textMarshalJSON() { 49 | blob := `["small","regular","large","unrecognized","small","normal","small","large"]` 50 | var inventory []Size 51 | if err := json.Unmarshal([]byte(blob), &inventory); err != nil { 52 | log.Fatal(err) 53 | } 54 | 55 | counts := make(map[Size]int) 56 | for _, size := range inventory { 57 | counts[size] += 1 58 | } 59 | 60 | fmt.Printf("Inventory Counts:\n* Small: %d\n* Large: %d\n* Unrecognized: %d\n", 61 | counts[small], counts[large], counts[unrecognized]) 62 | 63 | // Output: 64 | // Inventory Counts: 65 | // * Small: 3 66 | // * Large: 2 67 | // * Unrecognized: 3 68 | } 69 | --------------------------------------------------------------------------------