aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/neilotoole/jsoncolor
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-02-05 00:37:32 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-02-05 00:37:32 +0100
commit6960aecc25400320adee1b8802a86839326e15b6 (patch)
tree334f7ca9491080a5e6f9a9747da77281c4958ba2 /vendor/github.com/neilotoole/jsoncolor
downloadhepi-6960aecc25400320adee1b8802a86839326e15b6.tar.gz
Engage!
Diffstat (limited to 'vendor/github.com/neilotoole/jsoncolor')
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/.gitignore21
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/.golangci.yml334
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/LICENSE21
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/README.md241
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/SECURITY.md14
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/SEGMENTIO_README.md76
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/ascii.go124
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/codec.go1183
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/decode.go1195
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/encode.go1054
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/json.go459
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/jsoncolor.go141
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/parse.go735
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/reflect.go20
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/reflect_optimize.go30
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/splash.pngbin0 -> 99137 bytes
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/terminal.go42
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/terminal_windows.go53
-rw-r--r--vendor/github.com/neilotoole/jsoncolor/token.go286
19 files changed, 6029 insertions, 0 deletions
diff --git a/vendor/github.com/neilotoole/jsoncolor/.gitignore b/vendor/github.com/neilotoole/jsoncolor/.gitignore
new file mode 100644
index 0000000..97fe1d6
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/.gitignore
@@ -0,0 +1,21 @@
1# Binaries for programs and plugins
2*.exe
3*.exe~
4*.dll
5*.so
6*.dylib
7
8# Test binary, built with `go test -c`
9*.test
10
11# Output of the go coverage tool, specifically when used with LiteIDE
12*.out
13
14# Dependency directories (remove the comment below to include it)
15# vendor/
16
17*.iml
18.idea
19TODO.md
20**/.DS_Store
21/scratch/
diff --git a/vendor/github.com/neilotoole/jsoncolor/.golangci.yml b/vendor/github.com/neilotoole/jsoncolor/.golangci.yml
new file mode 100644
index 0000000..a53fb78
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/.golangci.yml
@@ -0,0 +1,334 @@
1# This code is licensed under the terms of the MIT license.
2
3## Golden config for golangci-lint v1.54
4#
5# This is the best config for golangci-lint based on my experience and opinion.
6# It is very strict, but not extremely strict.
7# Feel free to adopt and change it for your needs.
8#
9# @neilotoole: ^^ Well, it's less strict now!
10# Based on: https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322
11
12run:
13 # Timeout for analysis, e.g. 30s, 5m.
14 # Default: 1m
15 timeout: 5m
16
17 tests: false
18
19 skip-dirs:
20 - scratch
21
22
23
24
25
26output:
27 sort-results: true
28
29# This file contains only configs which differ from defaults.
30# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml
31linters-settings:
32 cyclop:
33 # The maximal code complexity to report.
34 # Default: 10
35 max-complexity: 50
36 # The maximal average package complexity.
37 # If it's higher than 0.0 (float) the check is enabled
38 # Default: 0.0
39 package-average: 10.0
40
41 errcheck:
42 # Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
43 # Such cases aren't reported by default.
44 # Default: false
45 check-type-assertions: true
46
47 exhaustive:
48 # Program elements to check for exhaustiveness.
49 # Default: [ switch ]
50 check:
51 - switch
52 - map
53
54 funlen:
55 # Checks the number of lines in a function.
56 # If lower than 0, disable the check.
57 # Default: 60
58 lines: 150
59 # Checks the number of statements in a function.
60 # If lower than 0, disable the check.
61 # Default: 40
62 statements: 100
63
64 gocognit:
65 # Minimal code complexity to report
66 # Default: 30 (but we recommend 10-20)
67 min-complexity: 50
68
69 gocritic:
70 # Settings passed to gocritic.
71 # The settings key is the name of a supported gocritic checker.
72 # The list of supported checkers can be find in https://go-critic.github.io/overview.
73 settings:
74 captLocal:
75 # Whether to restrict checker to params only.
76 # Default: true
77 paramsOnly: false
78 underef:
79 # Whether to skip (*x).method() calls where x is a pointer receiver.
80 # Default: true
81 skipRecvDeref: false
82
83 gocyclo:
84 # Minimal code complexity to report.
85 # Default: 30 (but we recommend 10-20)
86 min-complexity: 50
87
88 gofumpt:
89 # Module path which contains the source code being formatted.
90 # Default: ""
91 module-path: github.com/neilotoole/jsoncolor
92 # Choose whether to use the extra rules.
93 # Default: false
94 extra-rules: true
95
96 gomnd:
97 # List of function patterns to exclude from analysis.
98 # Values always ignored: `time.Date`,
99 # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`,
100 # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`.
101 # Default: []
102 ignored-functions:
103 - make
104 - os.Chmod
105 - os.Mkdir
106 - os.MkdirAll
107 - os.OpenFile
108 - os.WriteFile
109 - prometheus.ExponentialBuckets
110 - prometheus.ExponentialBucketsRange
111 - prometheus.LinearBuckets
112 ignored-numbers:
113 - "2"
114 - "3"
115
116 gomodguard:
117 blocked:
118 # List of blocked modules.
119 # Default: []
120 modules:
121 - github.com/golang/protobuf:
122 recommendations:
123 - google.golang.org/protobuf
124 reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules"
125 - github.com/satori/go.uuid:
126 recommendations:
127 - github.com/google/uuid
128 reason: "satori's package is not maintained"
129 - github.com/gofrs/uuid:
130 recommendations:
131 - github.com/google/uuid
132 reason: "gofrs' package is not go module"
133
134 govet:
135 # Enable all analyzers.
136 # Default: false
137 enable-all: true
138 # Disable analyzers by name.
139 # Run `go tool vet help` to see all analyzers.
140 # Default: []
141 disable:
142 - fieldalignment # too strict
143 # Settings per analyzer.
144 settings:
145 shadow:
146 # Whether to be strict about shadowing; can be noisy.
147 # Default: false
148 strict: false
149
150 lll:
151 # Max line length, lines longer will be reported.
152 # '\t' is counted as 1 character by default, and can be changed with the tab-width option.
153 # Default: 120.
154 line-length: 120
155 # Tab width in spaces.
156 # Default: 1
157 tab-width: 1
158
159 nakedret:
160 # Make an issue if func has more lines of code than this setting, and it has naked returns.
161 # Default: 30
162 max-func-lines: 0
163
164 nestif:
165 # Minimal complexity of if statements to report.
166 # Default: 5
167 min-complexity: 20
168
169 nolintlint:
170 # Exclude following linters from requiring an explanation.
171 # Default: []
172 allow-no-explanation: [ funlen, gocognit, lll ]
173 # Enable to require an explanation of nonzero length after each nolint directive.
174 # Default: false
175 require-explanation: false
176 # Enable to require nolint directives to mention the specific linter being suppressed.
177 # Default: false
178 require-specific: true
179
180 rowserrcheck:
181 # database/sql is always checked
182 # Default: []
183 packages:
184# - github.com/jmoiron/sqlx
185
186 tenv:
187 # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
188 # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
189 # Default: false
190 all: true
191
192
193linters:
194 disable-all: true
195
196 enable:
197 ## enabled by default
198 - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases
199 - gosimple # specializes in simplifying a code
200 - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
201 - ineffassign # detects when assignments to existing variables are not used
202 - staticcheck # is a go vet on steroids, applying a ton of static analysis checks
203 - typecheck # like the front-end of a Go compiler, parses and type-checks Go code
204 - unused # checks for unused constants, variables, functions and types
205
206
207 # ## disabled by default
208 - asasalint # checks for pass []any as any in variadic func(...any)
209 - asciicheck # checks that your code does not contain non-ASCII identifiers
210 - bidichk # checks for dangerous unicode character sequences
211 - bodyclose # checks whether HTTP response body is closed successfully
212 - cyclop # checks function and package cyclomatic complexity
213 - dupl # tool for code clone detection
214 - durationcheck # checks for two durations multiplied together
215 - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error
216 - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13
217 - execinquery # checks query string in Query function which reads your Go src files and warning it finds
218 - exhaustive # checks exhaustiveness of enum switch statements
219 - exportloopref # checks for pointers to enclosing loop variables
220 - forbidigo # forbids identifiers
221 - funlen # tool for detection of long functions
222 - gochecknoinits # checks that no init functions are present in Go code
223 - gocognit # computes and checks the cognitive complexity of functions
224 - goconst # finds repeated strings that could be replaced by a constant
225 - gocritic # provides diagnostics that check for bugs, performance and style issues
226 - gocyclo # computes and checks the cyclomatic complexity of functions
227 - godot # checks if comments end in a period
228 - gofumpt
229 - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt
230 # - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod
231 - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations
232 - goprintffuncname # checks that printf-like functions are named with f at the end
233 - gosec # inspects source code for security problems
234 - lll # reports long lines
235 - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap)
236 - makezero # finds slice declarations with non-zero initial length
237 - nakedret # finds naked returns in functions greater than a specified function length
238 - nestif # reports deeply nested if statements
239 - nilerr # finds the code that returns nil even if it checks that the error is not nil
240 - nilnil # checks that there is no simultaneous return of nil error and an invalid value
241 - noctx # finds sending http request without context.Context
242 - nolintlint # reports ill-formed or insufficient nolint directives
243 - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL
244 - predeclared # finds code that shadows one of Go's predeclared identifiers
245 - promlinter # checks Prometheus metrics naming via promlint
246 - reassign # checks that package variables are not reassigned
247 - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint
248 - stylecheck # is a replacement for golint
249 - tenv # detects using os.Setenv instead of t.Setenv since Go1.17
250 - testableexamples # checks if examples are testable (have an expected output)
251 - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes
252 - unconvert # removes unnecessary type conversions
253 - unparam # reports unused function parameters
254 - usestdlibvars # detects the possibility to use variables/constants from the Go standard library
255 - whitespace # detects leading and trailing whitespace
256
257 ## These three linters are disabled for now due to generics: https://github.com/golangci/golangci-lint/issues/2649
258 #- rowserrcheck # checks whether Err of rows is checked successfully # Disabled because: https://github.com/golangci/golangci-lint/issues/2649
259 #- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
260 #- wastedassign # finds wasted assignment statements
261
262
263 ## you may want to enable
264 #- decorder # checks declaration order and count of types, constants, variables and functions
265 #- exhaustruct # checks if all structure fields are initialized
266 #- gochecknoglobals # checks that no global variables exist
267 #- godox # detects FIXME, TODO and other comment keywords
268 #- goheader # checks is file header matches to pattern
269 #- gomnd # detects magic numbers
270 #- interfacebloat # checks the number of methods inside an interface
271 #- ireturn # accept interfaces, return concrete types
272 #- prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated
273 #- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope
274 #- wrapcheck # checks that errors returned from external packages are wrapped
275
276 ## disabled
277 #- containedctx # detects struct contained context.Context field
278 #- contextcheck # [too many false positives] checks the function whether use a non-inherited context
279 #- depguard # [replaced by gomodguard] checks if package imports are in a list of acceptable packages
280 #- dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
281 #- dupword # [useless without config] checks for duplicate words in the source code
282 #- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted
283 #- forcetypeassert # [replaced by errcheck] finds forced type assertions
284 #- goerr113 # [too strict] checks the errors handling expressions
285 #- gofmt # [replaced by goimports] checks whether code was gofmt-ed
286 #- gofumpt # [replaced by goimports, gofumports is not available yet] checks whether code was gofumpt-ed
287 #- grouper # analyzes expression groups
288 #- importas # enforces consistent import aliases
289 #- maintidx # measures the maintainability index of each function
290 #- misspell # [useless] finds commonly misspelled English words in comments
291 #- nlreturn # [too strict and mostly code is not more readable] checks for a new line before return and branch statements to increase code clarity
292 #- paralleltest # [too many false positives] detects missing usage of t.Parallel() method in your Go test
293 #- tagliatelle # checks the struct tags
294 #- thelper # detects golang test helpers without t.Helper() call and checks the consistency of test helpers
295 #- wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines
296
297 ## deprecated
298 #- deadcode # [deprecated, replaced by unused] finds unused code
299 #- exhaustivestruct # [deprecated, replaced by exhaustruct] checks if all struct's fields are initialized
300 #- golint # [deprecated, replaced by revive] golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
301 #- ifshort # [deprecated] checks that your code uses short syntax for if-statements whenever possible
302 #- interfacer # [deprecated] suggests narrower interface types
303 #- maligned # [deprecated, replaced by govet fieldalignment] detects Go structs that would take less memory if their fields were sorted
304 #- nosnakecase # [deprecated, replaced by revive var-naming] detects snake case of variable naming and function name
305 #- scopelint # [deprecated, replaced by exportloopref] checks for unpinned variables in go programs
306 #- structcheck # [deprecated, replaced by unused] finds unused struct fields
307 #- varcheck # [deprecated, replaced by unused] finds unused global variables and constants
308
309
310issues:
311 # Maximum count of issues with the same text.
312 # Set to 0 to disable.
313 # Default: 3
314 max-same-issues: 3
315
316 exclude-rules:
317 - source: "^//\\s*go:generate\\s"
318 linters: [ lll ]
319 - source: "(noinspection|TODO)"
320 linters: [ godot ]
321 - source: "//noinspection"
322 linters: [ gocritic ]
323 - source: "^\\s+if _, ok := err\\.\\([^.]+\\.InternalError\\); ok {"
324 linters: [ errorlint ]
325 - path: "_test\\.go"
326 linters:
327 - bodyclose
328 - dupl
329 - funlen
330 - goconst
331 - gosec
332 - noctx
333 - wrapcheck
334
diff --git a/vendor/github.com/neilotoole/jsoncolor/LICENSE b/vendor/github.com/neilotoole/jsoncolor/LICENSE
new file mode 100644
index 0000000..76e1f9c
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/LICENSE
@@ -0,0 +1,21 @@
1MIT License
2
3Copyright (c) 2023 Neil O'Toole
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
diff --git a/vendor/github.com/neilotoole/jsoncolor/README.md b/vendor/github.com/neilotoole/jsoncolor/README.md
new file mode 100644
index 0000000..a8da54c
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/README.md
@@ -0,0 +1,241 @@
1[![Actions Status](https://github.com/neilotoole/jsoncolor/workflows/Go/badge.svg)](https://github.com/neilotoole/jsoncolor/actions?query=workflow%3AGo)
2[![Go Report Card](https://goreportcard.com/badge/neilotoole/jsoncolor)](https://goreportcard.com/report/neilotoole/jsoncolor)
3[![release](https://img.shields.io/badge/release-v0.7.0-green.svg)](https://github.com/neilotoole/jsoncolor/releases/tag/v0.7.0)
4[![Go Reference](https://pkg.go.dev/badge/github.com/neilotoole/jsoncolor.svg)](https://pkg.go.dev/github.com/neilotoole/jsoncolor)
5[![license](https://img.shields.io/github/license/neilotoole/jsoncolor)](./LICENSE)
6
7# jsoncolor
8
9Package `neilotoole/jsoncolor` is a drop-in replacement for stdlib
10[`encoding/json`](https://pkg.go.dev/encoding/json) that outputs colorized JSON.
11
12Why? Well, [`jq`](https://jqlang.github.io/jq/) colorizes its output by default, and color output
13is desirable for many Go CLIs. This package performs colorization (and indentation) inline
14in the encoder, and is significantly faster than stdlib at indentation.
15
16From the example [`jc`](./cmd/jc/main.go) app:
17
18![jsoncolor-output](./splash.png)
19
20## Usage
21
22Get the package per the normal mechanism (requires Go 1.16+):
23
24```shell
25go get -u github.com/neilotoole/jsoncolor
26```
27
28Then:
29
30```go
31package main
32
33import (
34 "fmt"
35 "github.com/mattn/go-colorable"
36 json "github.com/neilotoole/jsoncolor"
37 "os"
38)
39
40func main() {
41 var enc *json.Encoder
42
43 // Note: this check will fail if running inside Goland (and
44 // other IDEs?) as IsColorTerminal will return false.
45 if json.IsColorTerminal(os.Stdout) {
46 // Safe to use color
47 out := colorable.NewColorable(os.Stdout) // needed for Windows
48 enc = json.NewEncoder(out)
49
50 // DefaultColors are similar to jq
51 clrs := json.DefaultColors()
52
53 // Change some values, just for fun
54 clrs.Bool = json.Color("\x1b[36m") // Change the bool color
55 clrs.String = json.Color{} // Disable the string color
56
57 enc.SetColors(clrs)
58 } else {
59 // Can't use color; but the encoder will still work
60 enc = json.NewEncoder(os.Stdout)
61 }
62
63 m := map[string]interface{}{
64 "a": 1,
65 "b": true,
66 "c": "hello",
67 }
68
69 if err := enc.Encode(m); err != nil {
70 fmt.Fprintln(os.Stderr, err)
71 os.Exit(1)
72 }
73}
74```
75
76### Configuration
77
78To enable colorization, invoke [`enc.SetColors`](https://pkg.go.dev/github.com/neilotoole/jsoncolor#Encoder.SetColors).
79
80The [`Colors`](https://pkg.go.dev/github.com/neilotoole/jsoncolor#Colors) struct
81holds color config. The zero value and `nil` are both safe for use (resulting in no colorization).
82
83The [`DefaultColors`](https://pkg.go.dev/github.com/neilotoole/jsoncolor#DefaultColors) func
84returns a `Colors` struct that produces results similar to `jq`:
85
86```go
87// DefaultColors returns the default Colors configuration.
88// These colors largely follow jq's default colorization,
89// with some deviation.
90func DefaultColors() *Colors {
91 return &Colors{
92 Null: Color("\x1b[2m"),
93 Bool: Color("\x1b[1m"),
94 Number: Color("\x1b[36m"),
95 String: Color("\x1b[32m"),
96 Key: Color("\x1b[34;1m"),
97 Bytes: Color("\x1b[2m"),
98 Time: Color("\x1b[32;2m"),
99 Punc: Color{}, // No colorization
100 }
101}
102```
103
104As seen above, use the `Color` zero value (`Color{}`) to
105disable colorization for that JSON element.
106
107### Helper for `fatih/color`
108
109It can be inconvenient to use terminal codes, e.g. `json.Color("\x1b[36m")`.
110A helper package provides an adapter for [`fatih/color`](https://github.com/fatih/color).
111
112```go
113 // import "github.com/neilotoole/jsoncolor/helper/fatihcolor"
114 // import "github.com/fatih/color"
115 // import "github.com/mattn/go-colorable"
116
117 out := colorable.NewColorable(os.Stdout) // needed for Windows
118 enc = json.NewEncoder(out)
119
120 fclrs := fatihcolor.DefaultColors()
121 // Change some values, just for fun
122 fclrs.Number = color.New(color.FgBlue)
123 fclrs.String = color.New(color.FgCyan)
124
125 clrs := fatihcolor.ToCoreColors(fclrs)
126 enc.SetColors(clrs)
127```
128
129### Drop-in for `encoding/json`
130
131This package is a full drop-in for stdlib [`encoding/json`](https://pkg.go.dev/encoding/json)
132(thanks to the ancestral [`segmentio/encoding/json`](https://pkg.go.dev/github.com/segmentio/encoding/json)
133pkg being a full drop-in).
134
135To drop-in, just use an import alias:
136
137```go
138 import json "github.com/neilotoole/jsoncolor"
139```
140
141## Example app: `jc`
142
143See [`cmd/jc`](cmd/jc/main.go) for a trivial CLI implementation that can accept JSON input,
144and output that JSON in color.
145
146```shell
147# From project root
148$ go install ./cmd/jc
149$ cat ./testdata/sakila_actor.json | jc
150```
151
152## Benchmarks
153
154Note that this package contains [`golang_bench_test.go`](./golang_bench_test.go), which
155is inherited from `segmentj`. But here we're interested in [`benchmark_test.go:BenchmarkEncode`](./benchmark_test.go),
156which benchmarks encoding performance versus other JSON encoder packages.
157The results below benchmark the following:
158
159- Stdlib [`encoding/json`](https://pkg.go.dev/encoding/json) (`go1.17.1`).
160- [`segmentj`](https://github.com/segmentio/encoding): `v0.1.14`, which was when `jsoncolor` was forked. The newer `segmentj` code performs even better.
161- `neilotoole/jsoncolor`: (this package) `v0.6.0`.
162- [`nwidger/jsoncolor`](https://github.com/nwidger/jsoncolor): `v0.3.0`, latest at time of benchmarks.
163
164Note that two other Go JSON colorization packages ([`hokaccha/go-prettyjson`](https://github.com/hokaccha/go-prettyjson) and
165[`TylerBrock/colorjson`](https://github.com/TylerBrock/colorjson)) are excluded from
166these benchmarks because they do not provide a stdlib-compatible `Encoder` impl.
167
168```
169$ go test -bench=BenchmarkEncode -benchtime="5s"
170goarch: amd64
171pkg: github.com/neilotoole/jsoncolor
172cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
173BenchmarkEncode/stdlib_NoIndent-16 181 33047390 ns/op 8870685 B/op 120022 allocs/op
174BenchmarkEncode/stdlib_Indent-16 124 48093178 ns/op 10470366 B/op 120033 allocs/op
175BenchmarkEncode/segmentj_NoIndent-16 415 14658699 ns/op 3788911 B/op 10020 allocs/op
176BenchmarkEncode/segmentj_Indent-16 195 30628798 ns/op 5404492 B/op 10025 allocs/op
177BenchmarkEncode/neilotoole_NoIndent_NoColor-16 362 16522399 ns/op 3789034 B/op 10020 allocs/op
178BenchmarkEncode/neilotoole_Indent_NoColor-16 303 20146856 ns/op 5460753 B/op 10021 allocs/op
179BenchmarkEncode/neilotoole_NoIndent_Color-16 295 19989420 ns/op 10326019 B/op 10029 allocs/op
180BenchmarkEncode/neilotoole_Indent_Color-16 246 24714163 ns/op 11996890 B/op 10030 allocs/op
181BenchmarkEncode/nwidger_NoIndent_NoColor-16 10 541107983 ns/op 92934231 B/op 4490210 allocs/op
182BenchmarkEncode/nwidger_Indent_NoColor-16 7 798088086 ns/op 117258321 B/op 6290213 allocs/op
183BenchmarkEncode/nwidger_indent_NoIndent_Colo-16 10 542002051 ns/op 92935639 B/op 4490224 allocs/op
184BenchmarkEncode/nwidger_indent_Indent_Color-16 7 799928353 ns/op 117259195 B/op 6290220 allocs/op
185```
186
187As always, take benchmarks with a large grain of salt, as they're based on a (small) synthetic benchmark.
188More benchmarks would give a better picture (and note as well that the benchmarked `segmentj` is an older version, `v0.1.14`).
189
190All that having been said, what can we surmise from these particular results?
191
192- `segmentj` performs better than `stdlib` at all encoding tasks.
193- `jsoncolor` performs better than `segmentj` for indentation (which makes sense, as indentation is performed inline).
194- `jsoncolor` performs better than `stdlib` at all encoding tasks.
195
196Again, trust these benchmarks at your peril. Create your own benchmarks for your own workload.
197
198## Notes
199
200- The [`.golangci.yml`](./.golangci.yml) linter settings have been fiddled with to hush some
201 linting issues inherited from the `segmentio` codebase at the time of forking. Thus, the linter report
202 may not be of great use. In an ideal world, the `jsoncolor` functionality would be [ported](https://github.com/neilotoole/jsoncolor/issues/15) to a
203 more recent (and better-linted) version of the `segementio` codebase.
204- The `segmentio` encoder (at least as of `v0.1.14`) encodes `time.Duration` as string, while `stdlib` outputs as `int64`.
205 This package follows `stdlib`.
206- The [`Colors.Punc`](https://pkg.go.dev/github.com/neilotoole/jsoncolor#Colors) field controls all
207 punctuation colorization, i.e. `[]{},:"`. It is probably worthwhile to [separate](https://github.com/neilotoole/jsoncolor/issues/16)
208 these out into individually-configurable elements.
209
210<a name="history"></a>
211## CHANGELOG
212
213History: this package is an extract of [`sq`](https://github.com/neilotoole/sq)'s JSON encoding package, which itself is a fork of the
214[`segmentio/encoding`](https://github.com/segmentio/encoding) JSON encoding package. Note that the
215original `sq` JSON encoder was forked from Segment's codebase at `v0.1.14`, so
216the codebases have drifted significantly by now.
217
218### [v0.7.1](https://github.com/neilotoole/jsoncolor/releases/tag/v0.7.1)
219
220- [#27](https://github.com/neilotoole/jsoncolor/pull/27): Improved Windows terminal color support checking.
221
222### [v0.7.0](https://github.com/neilotoole/jsoncolor/releases/tag/v0.7.0)
223
224- [#21](https://github.com/neilotoole/jsoncolor/pull/21): Support for [`encoding.TextMarshaler`](https://pkg.go.dev/encoding#TextMarshaler).
225- [#22](https://github.com/neilotoole/jsoncolor/pull/22): Removed redundant dependencies.
226- [#26](https://github.com/neilotoole/jsoncolor/pull/26): Updated dependencies.
227
228## Acknowledgments
229
230- [`jq`](https://stedolan.github.io/jq/): sine qua non.
231- [`segmentio/encoding`](https://github.com/segmentio/encoding): `jsoncolor` is layered into Segment's JSON encoder. They did the hard work. Much gratitude to that team.
232- [`sq`](https://github.com/neilotoole/sq): `jsoncolor` is effectively an extract of code created specifically for `sq`.
233- [`mattn/go-colorable`](https://github.com/mattn/go-colorable): no project is complete without `mattn` having played a role.
234- [`fatih/color`](https://github.com/fatih/color): the color library.
235- [`@hermannm`](https://github.com/hermannm): for several PRs.
236
237### Related
238
239- [`nwidger/jsoncolor`](https://github.com/nwidger/jsoncolor)
240- [`hokaccha/go-prettyjson`](https://github.com/hokaccha/go-prettyjson)
241- [`TylerBrock/colorjson`](https://github.com/TylerBrock/colorjson)
diff --git a/vendor/github.com/neilotoole/jsoncolor/SECURITY.md b/vendor/github.com/neilotoole/jsoncolor/SECURITY.md
new file mode 100644
index 0000000..af83493
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/SECURITY.md
@@ -0,0 +1,14 @@
1# Security Policy
2
3## Supported Versions
4
5
6| Version | Supported |
7| ------- | ------------------ |
8| v0.7.0 | :white_check_mark: |
9| v0.6.0 | :x: |
10
11
12## Reporting a Vulnerability
13
14Open an [issue](https://github.com/neilotoole/jsoncolor/issues/new).
diff --git a/vendor/github.com/neilotoole/jsoncolor/SEGMENTIO_README.md b/vendor/github.com/neilotoole/jsoncolor/SEGMENTIO_README.md
new file mode 100644
index 0000000..c5ed94b
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/SEGMENTIO_README.md
@@ -0,0 +1,76 @@
1# encoding/json [![GoDoc](https://godoc.org/github.com/segmentio/encoding/json?status.svg)](https://godoc.org/github.com/segmentio/encoding/json)
2
3Go package offering a replacement implementation of the standard library's
4[`encoding/json`](https://golang.org/pkg/encoding/json/) package, with much
5better performance.
6
7## Usage
8
9The exported API of this package mirrors the standard library's
10[`encoding/json`](https://golang.org/pkg/encoding/json/) package, the only
11change needed to take advantage of the performance improvements is the import
12path of the `json` package, from:
13```go
14import (
15 "encoding/json"
16)
17```
18to
19```go
20import (
21 "github.com/segmentio/encoding/json"
22)
23```
24
25One way to gain higher encoding throughput is to disable HTML escaping.
26It allows the string encoding to use a much more efficient code path which
27does not require parsing UTF-8 runes most of the time.
28
29## Performance Improvements
30
31The internal implementation uses a fair amount of unsafe operations (untyped
32code, pointer arithmetic, etc...) to avoid using reflection as much as possible,
33which is often the reason why serialization code has a large CPU and memory
34footprint.
35
36The package aims for zero unnecessary dynamic memory allocations and hot code
37paths that are mostly free from calls into the reflect package.
38
39## Compatibility with encoding/json
40
41This package aims to be a drop-in replacement, therefore it is tested to behave
42exactly like the standard library's package. However, there are still a few
43missing features that have not been ported yet:
44
45- Streaming decoder, currently the `Decoder` implementation offered by the
46package does not support progressively reading values from a JSON array (unlike
47the standard library). In our experience this is a very rare use-case, if you
48need it you're better off sticking to the standard library, or spend a bit of
49time implementing it in here ;)
50
51Note that none of those features should result in performance degradations if
52they were implemented in the package, and we welcome contributions!
53
54## Trade-offs
55
56As one would expect, we had to make a couple of trade-offs to achieve greater
57performance than the standard library, but there were also features that we
58did not want to give away.
59
60Other open-source packages offering a reduced CPU and memory footprint usually
61do so by designing a different API, or require code generation (therefore adding
62complexity to the build process). These were not acceptable conditions for us,
63as we were not willing to trade off developer productivity for better runtime
64performance. To achieve this, we chose to exactly replicate the standard
65library interfaces and behavior, which meant the package implementation was the
66only area that we were able to work with. The internals of this package make
67heavy use of unsafe pointer arithmetics and other performance optimizations,
68and therefore are not as approachable as typical Go programs. Basically, we put
69a bigger burden on maintainers to achieve better runtime cost without
70sacrificing developer productivity.
71
72For these reasons, we also don't believe that this code should be ported upstream
73to the standard `encoding/json` package. The standard library has to remain
74readable and approachable to maximize stability and maintainability, and make
75projects like this one possible because a high quality reference implementation
76already exists.
diff --git a/vendor/github.com/neilotoole/jsoncolor/ascii.go b/vendor/github.com/neilotoole/jsoncolor/ascii.go
new file mode 100644
index 0000000..6c66eb5
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/ascii.go
@@ -0,0 +1,124 @@
1package jsoncolor
2
3import "unsafe"
4
5// asciiValid returns true if b contains only ASCII characters.
6//
7// From https://github.com/segmentio/encoding/blob/v0.1.14/ascii/valid.go#L28
8//
9//go:nosplit
10func asciiValid(b []byte) bool {
11 s, n := unsafe.Pointer(&b), uintptr(len(b))
12
13 i := uintptr(0)
14 p := *(*unsafe.Pointer)(s)
15
16 for n >= 8 {
17 if ((*(*uint64)(unsafe.Pointer(uintptr(p) + i))) & 0x8080808080808080) != 0 {
18 return false
19 }
20 i += 8
21 n -= 8
22 }
23
24 if n >= 4 {
25 if ((*(*uint32)(unsafe.Pointer(uintptr(p) + i))) & 0x80808080) != 0 {
26 return false
27 }
28 i += 4
29 n -= 4
30 }
31
32 var x uint32
33 switch n {
34 case 3:
35 x = uint32(*(*uint8)(unsafe.Pointer(uintptr(p) + i))) | uint32(*(*uint16)(unsafe.Pointer(uintptr(p) + i + 1)))<<8
36 case 2:
37 x = uint32(*(*uint16)(unsafe.Pointer(uintptr(p) + i)))
38 case 1:
39 x = uint32(*(*uint8)(unsafe.Pointer(uintptr(p) + i)))
40 default:
41 return true
42 }
43 return (x & 0x80808080) == 0
44}
45
46// asciiValidPrint returns true if b contains only printable ASCII characters.
47//
48// From https://github.com/segmentio/encoding/blob/v0.1.14/ascii/valid.go#L83
49//
50//go:nosplit
51func asciiValidPrint(b []byte) bool {
52 s, n := unsafe.Pointer(&b), uintptr(len(b))
53
54 if n == 0 {
55 return true
56 }
57
58 i := uintptr(0)
59 p := *(*unsafe.Pointer)(s)
60
61 for (n - i) >= 8 {
62 x := *(*uint64)(unsafe.Pointer(uintptr(p) + i))
63 if hasLess64(x, 0x20) || hasMore64(x, 0x7e) {
64 return false
65 }
66 i += 8
67 }
68
69 if (n - i) >= 4 {
70 x := *(*uint32)(unsafe.Pointer(uintptr(p) + i))
71 if hasLess32(x, 0x20) || hasMore32(x, 0x7e) {
72 return false
73 }
74 i += 4
75 }
76
77 var x uint32
78 switch n - i {
79 case 3:
80 x = 0x20000000 | uint32(*(*uint8)(unsafe.Pointer(uintptr(p) + i))) | uint32(*(*uint16)(unsafe.Pointer(uintptr(p) + i + 1)))<<8
81 case 2:
82 x = 0x20200000 | uint32(*(*uint16)(unsafe.Pointer(uintptr(p) + i)))
83 case 1:
84 x = 0x20202000 | uint32(*(*uint8)(unsafe.Pointer(uintptr(p) + i)))
85 default:
86 return true
87 }
88 return !(hasLess32(x, 0x20) || hasMore32(x, 0x7e))
89}
90
91// https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
92const (
93 hasLessConstL64 = (^uint64(0)) / 255
94 hasLessConstR64 = hasLessConstL64 * 128
95
96 hasLessConstL32 = (^uint32(0)) / 255
97 hasLessConstR32 = hasLessConstL32 * 128
98
99 hasMoreConstL64 = (^uint64(0)) / 255
100 hasMoreConstR64 = hasMoreConstL64 * 128
101
102 hasMoreConstL32 = (^uint32(0)) / 255
103 hasMoreConstR32 = hasMoreConstL32 * 128
104)
105
106//go:nosplit
107func hasLess64(x, n uint64) bool {
108 return ((x - (hasLessConstL64 * n)) & ^x & hasLessConstR64) != 0
109}
110
111//go:nosplit
112func hasLess32(x, n uint32) bool {
113 return ((x - (hasLessConstL32 * n)) & ^x & hasLessConstR32) != 0
114}
115
116//go:nosplit
117func hasMore64(x, n uint64) bool {
118 return (((x + (hasMoreConstL64 * (127 - n))) | x) & hasMoreConstR64) != 0
119}
120
121//go:nosplit
122func hasMore32(x, n uint32) bool {
123 return (((x + (hasMoreConstL32 * (127 - n))) | x) & hasMoreConstR32) != 0
124}
diff --git a/vendor/github.com/neilotoole/jsoncolor/codec.go b/vendor/github.com/neilotoole/jsoncolor/codec.go
new file mode 100644
index 0000000..b3b42ef
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/codec.go
@@ -0,0 +1,1183 @@
1package jsoncolor
2
3import (
4 "encoding"
5 "encoding/json"
6 "fmt"
7 "reflect"
8 "sort"
9 "strconv"
10 "strings"
11 "sync/atomic"
12 "time"
13 "unicode"
14 "unsafe"
15)
16
17type codec struct {
18 encode encodeFunc
19 decode decodeFunc
20}
21
22type encoder struct {
23 flags AppendFlags
24 clrs *Colors
25 indentr *indenter
26}
27type decoder struct{ flags ParseFlags }
28
29type encodeFunc func(encoder, []byte, unsafe.Pointer) ([]byte, error)
30type decodeFunc func(decoder, []byte, unsafe.Pointer) ([]byte, error)
31
32type emptyFunc func(unsafe.Pointer) bool
33type sortFunc func([]reflect.Value)
34
35var (
36 // Eventually consistent cache mapping go types to dynamically generated
37 // codecs.
38 //
39 // Note: using a uintptr as key instead of reflect.Type shaved ~15ns off of
40 // the ~30ns Marhsal/Unmarshal functions which were dominated by the map
41 // lookup time for simple types like bool, int, etc..
42 cache unsafe.Pointer // map[unsafe.Pointer]codec
43)
44
45func cacheLoad() map[unsafe.Pointer]codec {
46 p := atomic.LoadPointer(&cache)
47 return *(*map[unsafe.Pointer]codec)(unsafe.Pointer(&p))
48}
49
50func cacheStore(typ reflect.Type, cod codec, oldCodecs map[unsafe.Pointer]codec) {
51 newCodecs := make(map[unsafe.Pointer]codec, len(oldCodecs)+1)
52 newCodecs[typeid(typ)] = cod
53
54 for t, c := range oldCodecs {
55 newCodecs[t] = c
56 }
57
58 atomic.StorePointer(&cache, *(*unsafe.Pointer)(unsafe.Pointer(&newCodecs)))
59}
60
61func typeid(t reflect.Type) unsafe.Pointer {
62 return (*iface)(unsafe.Pointer(&t)).ptr
63}
64
65func constructCachedCodec(t reflect.Type, cache map[unsafe.Pointer]codec) codec {
66 c := constructCodec(t, map[reflect.Type]*structType{}, t.Kind() == reflect.Ptr)
67
68 if inlined(t) {
69 c.encode = constructInlineValueEncodeFunc(c.encode)
70 }
71
72 cacheStore(t, c, cache)
73 return c
74}
75
76func constructCodec(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) (c codec) {
77 switch t {
78 case nullType, nil:
79 c = codec{encode: encoder.encodeNull, decode: decoder.decodeNull}
80
81 case numberType:
82 c = codec{encode: encoder.encodeNumber, decode: decoder.decodeNumber}
83
84 case bytesType:
85 c = codec{encode: encoder.encodeBytes, decode: decoder.decodeBytes}
86
87 case durationType:
88 c = codec{encode: encoder.encodeDuration, decode: decoder.decodeDuration}
89
90 case timeType:
91 c = codec{encode: encoder.encodeTime, decode: decoder.decodeTime}
92
93 case interfaceType:
94 c = codec{encode: encoder.encodeInterface, decode: decoder.decodeInterface}
95
96 case rawMessageType:
97 c = codec{encode: encoder.encodeRawMessage, decode: decoder.decodeRawMessage}
98
99 case numberPtrType:
100 c = constructPointerCodec(numberPtrType, nil)
101
102 case durationPtrType:
103 c = constructPointerCodec(durationPtrType, nil)
104
105 case timePtrType:
106 c = constructPointerCodec(timePtrType, nil)
107
108 case rawMessagePtrType:
109 c = constructPointerCodec(rawMessagePtrType, nil)
110 }
111
112 if c.encode != nil {
113 return
114 }
115
116 switch t.Kind() {
117 case reflect.Bool:
118 c = codec{encode: encoder.encodeBool, decode: decoder.decodeBool}
119
120 case reflect.Int:
121 c = codec{encode: encoder.encodeInt, decode: decoder.decodeInt}
122
123 case reflect.Int8:
124 c = codec{encode: encoder.encodeInt8, decode: decoder.decodeInt8}
125
126 case reflect.Int16:
127 c = codec{encode: encoder.encodeInt16, decode: decoder.decodeInt16}
128
129 case reflect.Int32:
130 c = codec{encode: encoder.encodeInt32, decode: decoder.decodeInt32}
131
132 case reflect.Int64:
133 c = codec{encode: encoder.encodeInt64, decode: decoder.decodeInt64}
134
135 case reflect.Uint:
136 c = codec{encode: encoder.encodeUint, decode: decoder.decodeUint}
137
138 case reflect.Uintptr:
139 c = codec{encode: encoder.encodeUintptr, decode: decoder.decodeUintptr}
140
141 case reflect.Uint8:
142 c = codec{encode: encoder.encodeUint8, decode: decoder.decodeUint8}
143
144 case reflect.Uint16:
145 c = codec{encode: encoder.encodeUint16, decode: decoder.decodeUint16}
146
147 case reflect.Uint32:
148 c = codec{encode: encoder.encodeUint32, decode: decoder.decodeUint32}
149
150 case reflect.Uint64:
151 c = codec{encode: encoder.encodeUint64, decode: decoder.decodeUint64}
152
153 case reflect.Float32:
154 c = codec{encode: encoder.encodeFloat32, decode: decoder.decodeFloat32}
155
156 case reflect.Float64:
157 c = codec{encode: encoder.encodeFloat64, decode: decoder.decodeFloat64}
158
159 case reflect.String:
160 c = codec{encode: encoder.encodeString, decode: decoder.decodeString}
161
162 case reflect.Interface:
163 c = constructInterfaceCodec(t)
164
165 case reflect.Array:
166 c = constructArrayCodec(t, seen, canAddr)
167
168 case reflect.Slice:
169 c = constructSliceCodec(t, seen)
170
171 case reflect.Map:
172 c = constructMapCodec(t, seen)
173
174 case reflect.Struct:
175 c = constructStructCodec(t, seen, canAddr)
176
177 case reflect.Ptr:
178 c = constructPointerCodec(t, seen)
179
180 default:
181 c = constructUnsupportedTypeCodec(t)
182 }
183
184 p := reflect.PtrTo(t)
185
186 if canAddr {
187 switch {
188 case p.Implements(jsonMarshalerType):
189 c.encode = constructJSONMarshalerEncodeFunc(t, true)
190 case p.Implements(textMarshalerType):
191 c.encode = constructTextMarshalerEncodeFunc(t, true)
192 }
193 }
194
195 switch {
196 case t.Implements(jsonMarshalerType):
197 c.encode = constructJSONMarshalerEncodeFunc(t, false)
198 case t.Implements(textMarshalerType):
199 c.encode = constructTextMarshalerEncodeFunc(t, false)
200 }
201
202 switch {
203 case p.Implements(jsonUnmarshalerType):
204 c.decode = constructJSONUnmarshalerDecodeFunc(t, true)
205 case p.Implements(textUnmarshalerType):
206 c.decode = constructTextUnmarshalerDecodeFunc(t, true)
207 }
208
209 return
210}
211
212func constructStringCodec(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) codec {
213 c := constructCodec(t, seen, canAddr)
214 return codec{
215 encode: constructStringEncodeFunc(c.encode),
216 decode: constructStringDecodeFunc(c.decode),
217 }
218}
219
220func constructStringEncodeFunc(encode encodeFunc) encodeFunc {
221 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
222 return e.encodeToString(b, p, encode)
223 }
224}
225
226func constructStringDecodeFunc(decode decodeFunc) decodeFunc {
227 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
228 return d.decodeFromString(b, p, decode)
229 }
230}
231
232func constructStringToIntDecodeFunc(t reflect.Type, decode decodeFunc) decodeFunc {
233 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
234 return d.decodeFromStringToInt(b, p, t, decode)
235 }
236}
237
238func constructArrayCodec(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) codec {
239 e := t.Elem()
240 c := constructCodec(e, seen, canAddr)
241 s := alignedSize(e)
242 return codec{
243 encode: constructArrayEncodeFunc(s, t, c.encode),
244 decode: constructArrayDecodeFunc(s, t, c.decode),
245 }
246}
247
248func constructArrayEncodeFunc(size uintptr, t reflect.Type, encode encodeFunc) encodeFunc {
249 n := t.Len()
250 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
251 return e.encodeArray(b, p, n, size, t, encode)
252 }
253}
254
255func constructArrayDecodeFunc(size uintptr, t reflect.Type, decode decodeFunc) decodeFunc {
256 n := t.Len()
257 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
258 return d.decodeArray(b, p, n, size, t, decode)
259 }
260}
261
262func constructSliceCodec(t reflect.Type, seen map[reflect.Type]*structType) codec {
263 e := t.Elem()
264 s := alignedSize(e)
265
266 if e.Kind() == reflect.Uint8 {
267 // Go 1.7+ behavior: slices of byte types (and aliases) may override the
268 // default encoding and decoding behaviors by implementing marshaler and
269 // unmarshaler interfaces.
270 p := reflect.PtrTo(e)
271 c := codec{}
272
273 switch {
274 case e.Implements(jsonMarshalerType):
275 c.encode = constructJSONMarshalerEncodeFunc(e, false)
276 case e.Implements(textMarshalerType):
277 c.encode = constructTextMarshalerEncodeFunc(e, false)
278 case p.Implements(jsonMarshalerType):
279 c.encode = constructJSONMarshalerEncodeFunc(e, true)
280 case p.Implements(textMarshalerType):
281 c.encode = constructTextMarshalerEncodeFunc(e, true)
282 }
283
284 switch {
285 case e.Implements(jsonUnmarshalerType):
286 c.decode = constructJSONUnmarshalerDecodeFunc(e, false)
287 case e.Implements(textUnmarshalerType):
288 c.decode = constructTextUnmarshalerDecodeFunc(e, false)
289 case p.Implements(jsonUnmarshalerType):
290 c.decode = constructJSONUnmarshalerDecodeFunc(e, true)
291 case p.Implements(textUnmarshalerType):
292 c.decode = constructTextUnmarshalerDecodeFunc(e, true)
293 }
294
295 if c.encode != nil {
296 c.encode = constructSliceEncodeFunc(s, t, c.encode)
297 } else {
298 c.encode = encoder.encodeBytes
299 }
300
301 if c.decode != nil {
302 c.decode = constructSliceDecodeFunc(s, t, c.decode)
303 } else {
304 c.decode = decoder.decodeBytes
305 }
306
307 return c
308 }
309
310 c := constructCodec(e, seen, true)
311 return codec{
312 encode: constructSliceEncodeFunc(s, t, c.encode),
313 decode: constructSliceDecodeFunc(s, t, c.decode),
314 }
315}
316
317func constructSliceEncodeFunc(size uintptr, t reflect.Type, encode encodeFunc) encodeFunc {
318 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
319 return e.encodeSlice(b, p, size, t, encode)
320 }
321}
322
323func constructSliceDecodeFunc(size uintptr, t reflect.Type, decode decodeFunc) decodeFunc {
324 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
325 return d.decodeSlice(b, p, size, t, decode)
326 }
327}
328
329func constructMapCodec(t reflect.Type, seen map[reflect.Type]*structType) codec {
330 var sortKeys sortFunc
331 k := t.Key()
332 v := t.Elem()
333
334 // Faster implementations for some common cases.
335 switch {
336 case k == stringType && v == interfaceType:
337 return codec{
338 encode: encoder.encodeMapStringInterface,
339 decode: decoder.decodeMapStringInterface,
340 }
341
342 case k == stringType && v == rawMessageType:
343 return codec{
344 encode: encoder.encodeMapStringRawMessage,
345 decode: decoder.decodeMapStringRawMessage,
346 }
347 }
348
349 kc := codec{}
350 vc := constructCodec(v, seen, false)
351
352 if k.Implements(textMarshalerType) || reflect.PtrTo(k).Implements(textUnmarshalerType) {
353 kc.encode = constructTextMarshalerEncodeFunc(k, false)
354 kc.decode = constructTextUnmarshalerDecodeFunc(k, true)
355
356 sortKeys = func(keys []reflect.Value) {
357 sort.Slice(keys, func(i, j int) bool {
358 // This is a performance abomination but the use case is rare
359 // enough that it shouldn't be a problem in practice.
360 k1, _ := keys[i].Interface().(encoding.TextMarshaler).MarshalText()
361 k2, _ := keys[j].Interface().(encoding.TextMarshaler).MarshalText()
362 return string(k1) < string(k2)
363 })
364 }
365 } else {
366 switch k.Kind() {
367 case reflect.String:
368 kc.encode = encoder.encodeKey
369 kc.decode = decoder.decodeString
370
371 sortKeys = func(keys []reflect.Value) {
372 sort.Slice(keys, func(i, j int) bool { return keys[i].String() < keys[j].String() })
373 }
374
375 case reflect.Int,
376 reflect.Int8,
377 reflect.Int16,
378 reflect.Int32,
379 reflect.Int64:
380 kc = constructStringCodec(k, seen, false)
381
382 sortKeys = func(keys []reflect.Value) {
383 sort.Slice(keys, func(i, j int) bool { return intStringsAreSorted(keys[i].Int(), keys[j].Int()) })
384 }
385
386 case reflect.Uint,
387 reflect.Uintptr,
388 reflect.Uint8,
389 reflect.Uint16,
390 reflect.Uint32,
391 reflect.Uint64:
392 kc = constructStringCodec(k, seen, false)
393
394 sortKeys = func(keys []reflect.Value) {
395 sort.Slice(keys, func(i, j int) bool { return uintStringsAreSorted(keys[i].Uint(), keys[j].Uint()) })
396 }
397
398 default:
399 return constructUnsupportedTypeCodec(t)
400 }
401 }
402
403 if inlined(v) {
404 vc.encode = constructInlineValueEncodeFunc(vc.encode)
405 }
406
407 return codec{
408 encode: constructMapEncodeFunc(t, kc.encode, vc.encode, sortKeys),
409 decode: constructMapDecodeFunc(t, kc.decode, vc.decode),
410 }
411}
412
413func constructMapEncodeFunc(t reflect.Type, encodeKey, encodeValue encodeFunc, sortKeys sortFunc) encodeFunc {
414 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
415 return e.encodeMap(b, p, t, encodeKey, encodeValue, sortKeys)
416 }
417}
418
419func constructMapDecodeFunc(t reflect.Type, decodeKey, decodeValue decodeFunc) decodeFunc {
420 kt := t.Key()
421 vt := t.Elem()
422 kz := reflect.Zero(kt)
423 vz := reflect.Zero(vt)
424 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
425 return d.decodeMap(b, p, t, kt, vt, kz, vz, decodeKey, decodeValue)
426 }
427}
428
429func constructStructCodec(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) codec {
430 st := constructStructType(t, seen, canAddr)
431 return codec{
432 encode: constructStructEncodeFunc(st),
433 decode: constructStructDecodeFunc(st),
434 }
435}
436
437func constructStructType(t reflect.Type, seen map[reflect.Type]*structType, canAddr bool) *structType {
438 // Used for preventing infinite recursion on types that have pointers to
439 // themselves.
440 st := seen[t]
441
442 if st == nil {
443 st = &structType{
444 fields: make([]structField, 0, t.NumField()),
445 fieldsIndex: make(map[string]*structField),
446 ficaseIndex: make(map[string]*structField),
447 typ: t,
448 }
449
450 seen[t] = st
451 st.fields = appendStructFields(st.fields, t, 0, seen, canAddr)
452
453 for i := range st.fields {
454 f := &st.fields[i]
455 s := strings.ToLower(f.name)
456 st.fieldsIndex[f.name] = f
457 // When there is ambiguity because multiple fields have the same
458 // case-insensitive representation, the first field must win.
459 if _, exists := st.ficaseIndex[s]; !exists {
460 st.ficaseIndex[s] = f
461 }
462 }
463 }
464
465 return st
466}
467
468func constructStructEncodeFunc(st *structType) encodeFunc {
469 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
470 return e.encodeStruct(b, p, st)
471 }
472}
473
474func constructStructDecodeFunc(st *structType) decodeFunc {
475 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
476 return d.decodeStruct(b, p, st)
477 }
478}
479
480func constructEmbeddedStructPointerCodec(t reflect.Type, unexported bool, offset uintptr, field codec) codec {
481 return codec{
482 encode: constructEmbeddedStructPointerEncodeFunc(t, unexported, offset, field.encode),
483 decode: constructEmbeddedStructPointerDecodeFunc(t, unexported, offset, field.decode),
484 }
485}
486
487func constructEmbeddedStructPointerEncodeFunc(t reflect.Type, unexported bool, offset uintptr, encode encodeFunc) encodeFunc {
488 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
489 return e.encodeEmbeddedStructPointer(b, p, t, unexported, offset, encode)
490 }
491}
492
493func constructEmbeddedStructPointerDecodeFunc(t reflect.Type, unexported bool, offset uintptr, decode decodeFunc) decodeFunc {
494 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
495 return d.decodeEmbeddedStructPointer(b, p, t, unexported, offset, decode)
496 }
497}
498
499func appendStructFields(fields []structField, t reflect.Type, offset uintptr, seen map[reflect.Type]*structType, canAddr bool) []structField {
500 type embeddedField struct {
501 index int
502 offset uintptr
503 pointer bool
504 unexported bool
505 subtype *structType
506 subfield *structField
507 }
508
509 names := make(map[string]struct{})
510 embedded := make([]embeddedField, 0, 10)
511
512 for i, n := 0, t.NumField(); i < n; i++ {
513 f := t.Field(i)
514
515 var (
516 name = f.Name
517 anonymous = f.Anonymous
518 tag = false
519 omitempty = false
520 stringify = false
521 unexported = len(f.PkgPath) != 0
522 )
523
524 if unexported && !anonymous { // unexported
525 continue
526 }
527
528 if parts := strings.Split(f.Tag.Get("json"), ","); len(parts) != 0 {
529 if len(parts[0]) != 0 {
530 name, tag = parts[0], true
531 }
532
533 if name == "-" && len(parts) == 1 { // ignored
534 continue
535 }
536
537 if !isValidTag(name) {
538 name = f.Name
539 }
540
541 for _, tag := range parts[1:] {
542 switch tag {
543 case "omitempty":
544 omitempty = true
545 case "string":
546 stringify = true
547 }
548 }
549 }
550
551 if anonymous && !tag { // embedded
552 typ := f.Type
553 ptr := f.Type.Kind() == reflect.Ptr
554
555 if ptr {
556 typ = f.Type.Elem()
557 }
558
559 if typ.Kind() == reflect.Struct {
560 // When the embedded fields is inlined the fields can be looked
561 // up by offset from the address of the wrapping object, so we
562 // simply add the embedded struct fields to the list of fields
563 // of the current struct type.
564 subtype := constructStructType(typ, seen, canAddr)
565
566 for j := range subtype.fields {
567 embedded = append(embedded, embeddedField{
568 index: i<<32 | j,
569 offset: offset + f.Offset,
570 pointer: ptr,
571 unexported: unexported,
572 subtype: subtype,
573 subfield: &subtype.fields[j],
574 })
575 }
576
577 continue
578 }
579
580 if unexported { // ignore unexported non-struct types
581 continue
582 }
583 }
584
585 codec := constructCodec(f.Type, seen, canAddr)
586
587 if stringify {
588 // https://golang.org/pkg/encoding/json/#Marshal
589 //
590 // The "string" option signals that a field is stored as JSON inside
591 // a JSON-encoded string. It applies only to fields of string,
592 // floating point, integer, or boolean types. This extra level of
593 // encoding is sometimes used when communicating with JavaScript
594 // programs:
595 typ := f.Type
596
597 if typ.Kind() == reflect.Ptr {
598 typ = typ.Elem()
599 }
600
601 switch typ.Kind() {
602 case reflect.Int,
603 reflect.Int8,
604 reflect.Int16,
605 reflect.Int32,
606 reflect.Int64,
607 reflect.Uint,
608 reflect.Uintptr,
609 reflect.Uint8,
610 reflect.Uint16,
611 reflect.Uint32,
612 reflect.Uint64:
613 codec.encode = constructStringEncodeFunc(codec.encode)
614 codec.decode = constructStringToIntDecodeFunc(typ, codec.decode)
615 case reflect.Bool,
616 reflect.Float32,
617 reflect.Float64,
618 reflect.String:
619 codec.encode = constructStringEncodeFunc(codec.encode)
620 codec.decode = constructStringDecodeFunc(codec.decode)
621 }
622 }
623
624 fields = append(fields, structField{
625 codec: codec,
626 offset: offset + f.Offset,
627 empty: emptyFuncOf(f.Type),
628 tag: tag,
629 omitempty: omitempty,
630 name: name,
631 index: i << 32,
632 typ: f.Type,
633 zero: reflect.Zero(f.Type),
634 })
635
636 names[name] = struct{}{}
637 }
638
639 // Only unambiguous embedded fields must be serialized.
640 ambiguousNames := make(map[string]int)
641 ambiguousTags := make(map[string]int)
642
643 // Embedded types can never override a field that was already present at
644 // the top-level.
645 for name := range names {
646 ambiguousNames[name]++
647 ambiguousTags[name]++
648 }
649
650 for _, embfield := range embedded {
651 ambiguousNames[embfield.subfield.name]++
652 if embfield.subfield.tag {
653 ambiguousTags[embfield.subfield.name]++
654 }
655 }
656
657 for _, embfield := range embedded {
658 subfield := *embfield.subfield
659
660 if ambiguousNames[subfield.name] > 1 && !(subfield.tag && ambiguousTags[subfield.name] == 1) {
661 continue // ambiguous embedded field
662 }
663
664 if embfield.pointer {
665 subfield.codec = constructEmbeddedStructPointerCodec(embfield.subtype.typ, embfield.unexported, subfield.offset, subfield.codec)
666 subfield.offset = embfield.offset
667 } else {
668 subfield.offset += embfield.offset
669 }
670
671 // To prevent dominant flags more than one level below the embedded one.
672 subfield.tag = false
673
674 // To ensure the order of the fields in the output is the same is in the
675 // struct type.
676 subfield.index = embfield.index
677
678 fields = append(fields, subfield)
679 }
680
681 for i := range fields {
682 fields[i].json = encodeString(fields[i].name, 0)
683 fields[i].html = encodeString(fields[i].name, EscapeHTML)
684 }
685
686 sort.Slice(fields, func(i, j int) bool { return fields[i].index < fields[j].index })
687 return fields
688}
689
690func encodeString(s string, flags AppendFlags) string {
691 b := make([]byte, 0, len(s)+2)
692 e := encoder{flags: flags}
693 b, _ = e.doEncodeString(b, unsafe.Pointer(&s))
694 return *(*string)(unsafe.Pointer(&b))
695}
696
697func constructPointerCodec(t reflect.Type, seen map[reflect.Type]*structType) codec {
698 e := t.Elem()
699 c := constructCodec(e, seen, true)
700 return codec{
701 encode: constructPointerEncodeFunc(e, c.encode),
702 decode: constructPointerDecodeFunc(e, c.decode),
703 }
704}
705
706func constructPointerEncodeFunc(t reflect.Type, encode encodeFunc) encodeFunc {
707 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
708 return e.encodePointer(b, p, t, encode)
709 }
710}
711
712func constructPointerDecodeFunc(t reflect.Type, decode decodeFunc) decodeFunc {
713 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
714 return d.decodePointer(b, p, t, decode)
715 }
716}
717
718func constructInterfaceCodec(t reflect.Type) codec {
719 return codec{
720 encode: constructMaybeEmptyInterfaceEncoderFunc(t),
721 decode: constructMaybeEmptyInterfaceDecoderFunc(t),
722 }
723}
724
725func constructMaybeEmptyInterfaceEncoderFunc(t reflect.Type) encodeFunc {
726 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
727 return e.encodeMaybeEmptyInterface(b, p, t)
728 }
729}
730
731func constructMaybeEmptyInterfaceDecoderFunc(t reflect.Type) decodeFunc {
732 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
733 return d.decodeMaybeEmptyInterface(b, p, t)
734 }
735}
736
737func constructUnsupportedTypeCodec(t reflect.Type) codec {
738 return codec{
739 encode: constructUnsupportedTypeEncodeFunc(t),
740 decode: constructUnsupportedTypeDecodeFunc(t),
741 }
742}
743
744func constructUnsupportedTypeEncodeFunc(t reflect.Type) encodeFunc {
745 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
746 return e.encodeUnsupportedTypeError(b, p, t)
747 }
748}
749
750func constructUnsupportedTypeDecodeFunc(t reflect.Type) decodeFunc {
751 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
752 return d.decodeUnmarshalTypeError(b, p, t)
753 }
754}
755
756func constructJSONMarshalerEncodeFunc(t reflect.Type, pointer bool) encodeFunc {
757 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
758 return e.encodeJSONMarshaler(b, p, t, pointer)
759 }
760}
761
762func constructJSONUnmarshalerDecodeFunc(t reflect.Type, pointer bool) decodeFunc {
763 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
764 return d.decodeJSONUnmarshaler(b, p, t, pointer)
765 }
766}
767
768func constructTextMarshalerEncodeFunc(t reflect.Type, pointer bool) encodeFunc {
769 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
770 return e.encodeTextMarshaler(b, p, t, pointer)
771 }
772}
773
774func constructTextUnmarshalerDecodeFunc(t reflect.Type, pointer bool) decodeFunc {
775 return func(d decoder, b []byte, p unsafe.Pointer) ([]byte, error) {
776 return d.decodeTextUnmarshaler(b, p, t, pointer)
777 }
778}
779
780func constructInlineValueEncodeFunc(encode encodeFunc) encodeFunc {
781 return func(e encoder, b []byte, p unsafe.Pointer) ([]byte, error) {
782 return encode(e, b, noescape(unsafe.Pointer(&p)))
783 }
784}
785
786// noescape hides a pointer from escape analysis. noescape is
787// the identity function but escape analysis doesn't think the
788// output depends on the input. noescape is inlined and currently
789// compiles down to zero instructions.
790// USE CAREFULLY!
791// This was copied from the runtime; see issues 23382 and 7921.
792//go:nosplit
793func noescape(p unsafe.Pointer) unsafe.Pointer {
794 x := uintptr(p)
795 return unsafe.Pointer(x ^ 0)
796}
797
798func alignedSize(t reflect.Type) uintptr {
799 a := t.Align()
800 s := t.Size()
801 return align(uintptr(a), uintptr(s))
802}
803
804func align(align, size uintptr) uintptr {
805 if align != 0 && (size%align) != 0 {
806 size = ((size / align) + 1) * align
807 }
808 return size
809}
810
811func inlined(t reflect.Type) bool {
812 switch t.Kind() {
813 case reflect.Ptr:
814 return true
815 case reflect.Map:
816 return true
817 case reflect.Struct:
818 return t.NumField() == 1 && inlined(t.Field(0).Type)
819 default:
820 return false
821 }
822}
823
824func isValidTag(s string) bool {
825 if s == "" {
826 return false
827 }
828 for _, c := range s {
829 switch {
830 case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
831 // Backslash and quote chars are reserved, but
832 // otherwise any punctuation chars are allowed
833 // in a tag name.
834 default:
835 if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
836 return false
837 }
838 }
839 }
840 return true
841}
842
843func emptyFuncOf(t reflect.Type) emptyFunc {
844 switch t {
845 case bytesType, rawMessageType:
846 return func(p unsafe.Pointer) bool { return (*slice)(p).len == 0 }
847 }
848
849 switch t.Kind() {
850 case reflect.Array:
851 if t.Len() == 0 {
852 return func(unsafe.Pointer) bool { return true }
853 }
854
855 case reflect.Map:
856 return func(p unsafe.Pointer) bool { return reflect.NewAt(t, p).Elem().Len() == 0 }
857
858 case reflect.Slice:
859 return func(p unsafe.Pointer) bool { return (*slice)(p).len == 0 }
860
861 case reflect.String:
862 return func(p unsafe.Pointer) bool { return len(*(*string)(p)) == 0 }
863
864 case reflect.Bool:
865 return func(p unsafe.Pointer) bool { return !*(*bool)(p) }
866
867 case reflect.Int, reflect.Uint:
868 return func(p unsafe.Pointer) bool { return *(*uint)(p) == 0 }
869
870 case reflect.Uintptr:
871 return func(p unsafe.Pointer) bool { return *(*uintptr)(p) == 0 }
872
873 case reflect.Int8, reflect.Uint8:
874 return func(p unsafe.Pointer) bool { return *(*uint8)(p) == 0 }
875
876 case reflect.Int16, reflect.Uint16:
877 return func(p unsafe.Pointer) bool { return *(*uint16)(p) == 0 }
878
879 case reflect.Int32, reflect.Uint32:
880 return func(p unsafe.Pointer) bool { return *(*uint32)(p) == 0 }
881
882 case reflect.Int64, reflect.Uint64:
883 return func(p unsafe.Pointer) bool { return *(*uint64)(p) == 0 }
884
885 case reflect.Float32:
886 return func(p unsafe.Pointer) bool { return *(*float32)(p) == 0 }
887
888 case reflect.Float64:
889 return func(p unsafe.Pointer) bool { return *(*float64)(p) == 0 }
890
891 case reflect.Ptr:
892 return func(p unsafe.Pointer) bool { return *(*unsafe.Pointer)(p) == nil }
893
894 case reflect.Interface:
895 return func(p unsafe.Pointer) bool { return (*iface)(p).ptr == nil }
896 }
897
898 return func(unsafe.Pointer) bool { return false }
899}
900
901type iface struct {
902 typ unsafe.Pointer
903 ptr unsafe.Pointer
904}
905
906type slice struct {
907 data unsafe.Pointer
908 len int
909 cap int
910}
911
912type structType struct {
913 fields []structField
914 fieldsIndex map[string]*structField
915 ficaseIndex map[string]*structField
916 typ reflect.Type
917 inlined bool
918}
919
920type structField struct {
921 codec codec
922 offset uintptr
923 empty emptyFunc
924 tag bool
925 omitempty bool
926 json string
927 html string
928 name string
929 typ reflect.Type
930 zero reflect.Value
931 index int
932}
933
934func unmarshalTypeError(b []byte, t reflect.Type) error {
935 return &UnmarshalTypeError{Value: strconv.Quote(prefix(b)), Type: t}
936}
937
938func unmarshalOverflow(b []byte, t reflect.Type) error {
939 return &UnmarshalTypeError{Value: "number " + prefix(b) + " overflows", Type: t}
940}
941
942func unexpectedEOF(b []byte) error {
943 return syntaxError(b, "unexpected end of JSON input")
944}
945
946var syntaxErrorMsgOffset = ^uintptr(0)
947
948func init() {
949 t := reflect.TypeOf(SyntaxError{})
950 for i, n := 0, t.NumField(); i < n; i++ {
951 if f := t.Field(i); f.Type.Kind() == reflect.String {
952 syntaxErrorMsgOffset = f.Offset
953 }
954 }
955}
956
957func syntaxError(b []byte, msg string, args ...interface{}) error {
958 e := new(SyntaxError)
959 i := syntaxErrorMsgOffset
960 if i != ^uintptr(0) {
961 s := "json: " + fmt.Sprintf(msg, args...) + ": " + prefix(b)
962 p := unsafe.Pointer(e)
963 // Hack to set the unexported `msg` field.
964 *(*string)(unsafe.Pointer(uintptr(p) + i)) = s
965 }
966 return e
967}
968
969func inputError(b []byte, t reflect.Type) ([]byte, error) {
970 if len(b) == 0 {
971 return nil, unexpectedEOF(b)
972 }
973 _, r, err := parseValue(b)
974 if err != nil {
975 return r, err
976 }
977 return skipSpaces(r), unmarshalTypeError(b, t)
978}
979
980func objectKeyError(b []byte, err error) ([]byte, error) {
981 if len(b) == 0 {
982 return nil, unexpectedEOF(b)
983 }
984 switch err.(type) {
985 case *UnmarshalTypeError:
986 err = syntaxError(b, "invalid character '%c' looking for beginning of object key", b[0])
987 }
988 return b, err
989}
990
991func prefix(b []byte) string {
992 if len(b) < 32 {
993 return string(b)
994 }
995 return string(b[:32]) + "..."
996}
997
998func intStringsAreSorted(i0, i1 int64) bool {
999 var b0, b1 [32]byte
1000 return string(strconv.AppendInt(b0[:0], i0, 10)) < string(strconv.AppendInt(b1[:0], i1, 10))
1001}
1002
1003func uintStringsAreSorted(u0, u1 uint64) bool {
1004 var b0, b1 [32]byte
1005 return string(strconv.AppendUint(b0[:0], u0, 10)) < string(strconv.AppendUint(b1[:0], u1, 10))
1006}
1007
1008//go:nosplit
1009func stringToBytes(s string) []byte {
1010 return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ // nolint:govet // from segment's code
1011 Data: ((*reflect.StringHeader)(unsafe.Pointer(&s))).Data,
1012 Len: len(s),
1013 Cap: len(s),
1014 }))
1015}
1016
1017var (
1018 nullType = reflect.TypeOf(nil)
1019 boolType = reflect.TypeOf(false)
1020
1021 intType = reflect.TypeOf(int(0))
1022 int8Type = reflect.TypeOf(int8(0))
1023 int16Type = reflect.TypeOf(int16(0))
1024 int32Type = reflect.TypeOf(int32(0))
1025 int64Type = reflect.TypeOf(int64(0))
1026
1027 uintType = reflect.TypeOf(uint(0))
1028 uint8Type = reflect.TypeOf(uint8(0))
1029 uint16Type = reflect.TypeOf(uint16(0))
1030 uint32Type = reflect.TypeOf(uint32(0))
1031 uint64Type = reflect.TypeOf(uint64(0))
1032 uintptrType = reflect.TypeOf(uintptr(0))
1033
1034 float32Type = reflect.TypeOf(float32(0))
1035 float64Type = reflect.TypeOf(float64(0))
1036
1037 numberType = reflect.TypeOf(json.Number(""))
1038 stringType = reflect.TypeOf("")
1039 bytesType = reflect.TypeOf(([]byte)(nil))
1040 durationType = reflect.TypeOf(time.Duration(0))
1041 timeType = reflect.TypeOf(time.Time{})
1042 rawMessageType = reflect.TypeOf(RawMessage(nil))
1043
1044 numberPtrType = reflect.PtrTo(numberType)
1045 durationPtrType = reflect.PtrTo(durationType)
1046 timePtrType = reflect.PtrTo(timeType)
1047 rawMessagePtrType = reflect.PtrTo(rawMessageType)
1048
1049 sliceInterfaceType = reflect.TypeOf(([]interface{})(nil))
1050 mapStringInterfaceType = reflect.TypeOf((map[string]interface{})(nil))
1051 mapStringRawMessageType = reflect.TypeOf((map[string]RawMessage)(nil))
1052
1053 interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
1054 jsonMarshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
1055 jsonUnmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
1056 textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
1057 textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
1058)
1059
1060// =============================================================================
1061// Copyright 2009 The Go Authors. All rights reserved.
1062// Use of this source code is governed by a BSD-style
1063// license that can be found in the LICENSE file.
1064
1065// appendDuration appends a human-readable representation of d to b.
1066//
1067// The function copies the implementation of time.Duration.String but prevents
1068// Go from making a dynamic memory allocation on the returned value.
1069func appendDuration(b []byte, d time.Duration) []byte {
1070 // Largest time is 2540400h10m10.000000000s
1071 var buf [32]byte
1072 w := len(buf)
1073
1074 u := uint64(d)
1075 neg := d < 0
1076 if neg {
1077 u = -u
1078 }
1079
1080 if u < uint64(time.Second) {
1081 // Special case: if duration is smaller than a second,
1082 // use smaller units, like 1.2ms
1083 var prec int
1084 w--
1085 buf[w] = 's'
1086 w--
1087 switch {
1088 case u == 0:
1089 return append(b, '0', 's')
1090 case u < uint64(time.Microsecond):
1091 // print nanoseconds
1092 prec = 0
1093 buf[w] = 'n'
1094 case u < uint64(time.Millisecond):
1095 // print microseconds
1096 prec = 3
1097 // U+00B5 'µ' micro sign == 0xC2 0xB5
1098 w-- // Need room for two bytes.
1099 copy(buf[w:], "µ")
1100 default:
1101 // print milliseconds
1102 prec = 6
1103 buf[w] = 'm'
1104 }
1105 w, u = fmtFrac(buf[:w], u, prec)
1106 w = fmtInt(buf[:w], u)
1107 } else {
1108 w--
1109 buf[w] = 's'
1110
1111 w, u = fmtFrac(buf[:w], u, 9)
1112
1113 // u is now integer seconds
1114 w = fmtInt(buf[:w], u%60)
1115 u /= 60
1116
1117 // u is now integer minutes
1118 if u > 0 {
1119 w--
1120 buf[w] = 'm'
1121 w = fmtInt(buf[:w], u%60)
1122 u /= 60
1123
1124 // u is now integer hours
1125 // Stop at hours because days can be different lengths.
1126 if u > 0 {
1127 w--
1128 buf[w] = 'h'
1129 w = fmtInt(buf[:w], u)
1130 }
1131 }
1132 }
1133
1134 if neg {
1135 w--
1136 buf[w] = '-'
1137 }
1138
1139 return append(b, buf[w:]...)
1140}
1141
1142// fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the
1143// tail of buf, omitting trailing zeros. it omits the decimal
1144// point too when the fraction is 0. It returns the index where the
1145// output bytes begin and the value v/10**prec.
1146func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) {
1147 // Omit trailing zeros up to and including decimal point.
1148 w := len(buf)
1149 print := false
1150 for i := 0; i < prec; i++ {
1151 digit := v % 10
1152 print = print || digit != 0
1153 if print {
1154 w--
1155 buf[w] = byte(digit) + '0'
1156 }
1157 v /= 10
1158 }
1159 if print {
1160 w--
1161 buf[w] = '.'
1162 }
1163 return w, v
1164}
1165
1166// fmtInt formats v into the tail of buf.
1167// It returns the index where the output begins.
1168func fmtInt(buf []byte, v uint64) int {
1169 w := len(buf)
1170 if v == 0 {
1171 w--
1172 buf[w] = '0'
1173 } else {
1174 for v > 0 {
1175 w--
1176 buf[w] = byte(v%10) + '0'
1177 v /= 10
1178 }
1179 }
1180 return w
1181}
1182
1183// =============================================================================
diff --git a/vendor/github.com/neilotoole/jsoncolor/decode.go b/vendor/github.com/neilotoole/jsoncolor/decode.go
new file mode 100644
index 0000000..b30a38f
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/decode.go
@@ -0,0 +1,1195 @@
1package jsoncolor
2
3import (
4 "bytes"
5 "encoding"
6 "encoding/base64"
7 "encoding/json"
8 "fmt"
9 "math"
10 "reflect"
11 "strconv"
12 "time"
13 "unsafe"
14)
15
16func (d decoder) decodeNull(b []byte, p unsafe.Pointer) ([]byte, error) {
17 if hasNullPrefix(b) {
18 return b[4:], nil
19 }
20 return inputError(b, nullType)
21}
22
23func (d decoder) decodeBool(b []byte, p unsafe.Pointer) ([]byte, error) {
24 switch {
25 case hasTruePrefix(b):
26 *(*bool)(p) = true
27 return b[4:], nil
28
29 case hasFalsePrefix(b):
30 *(*bool)(p) = false
31 return b[5:], nil
32
33 case hasNullPrefix(b):
34 return b[4:], nil
35
36 default:
37 return inputError(b, boolType)
38 }
39}
40
41func (d decoder) decodeInt(b []byte, p unsafe.Pointer) ([]byte, error) {
42 if hasNullPrefix(b) {
43 return b[4:], nil
44 }
45
46 v, r, err := parseInt(b, intType)
47 if err != nil {
48 return r, err
49 }
50
51 *(*int)(p) = int(v)
52 return r, nil
53}
54
55func (d decoder) decodeInt8(b []byte, p unsafe.Pointer) ([]byte, error) {
56 if hasNullPrefix(b) {
57 return b[4:], nil
58 }
59
60 v, r, err := parseInt(b, int8Type)
61 if err != nil {
62 return r, err
63 }
64
65 if v < math.MinInt8 || v > math.MaxInt8 {
66 return r, unmarshalOverflow(b[:len(b)-len(r)], int8Type)
67 }
68
69 *(*int8)(p) = int8(v)
70 return r, nil
71}
72
73func (d decoder) decodeInt16(b []byte, p unsafe.Pointer) ([]byte, error) {
74 if hasNullPrefix(b) {
75 return b[4:], nil
76 }
77
78 v, r, err := parseInt(b, int16Type)
79 if err != nil {
80 return r, err
81 }
82
83 if v < math.MinInt16 || v > math.MaxInt16 {
84 return r, unmarshalOverflow(b[:len(b)-len(r)], int16Type)
85 }
86
87 *(*int16)(p) = int16(v)
88 return r, nil
89}
90
91func (d decoder) decodeInt32(b []byte, p unsafe.Pointer) ([]byte, error) {
92 if hasNullPrefix(b) {
93 return b[4:], nil
94 }
95
96 v, r, err := parseInt(b, int32Type)
97 if err != nil {
98 return r, err
99 }
100
101 if v < math.MinInt32 || v > math.MaxInt32 {
102 return r, unmarshalOverflow(b[:len(b)-len(r)], int32Type)
103 }
104
105 *(*int32)(p) = int32(v)
106 return r, nil
107}
108
109func (d decoder) decodeInt64(b []byte, p unsafe.Pointer) ([]byte, error) {
110 if hasNullPrefix(b) {
111 return b[4:], nil
112 }
113
114 v, r, err := parseInt(b, int64Type)
115 if err != nil {
116 return r, err
117 }
118
119 *(*int64)(p) = v
120 return r, nil
121}
122
123func (d decoder) decodeUint(b []byte, p unsafe.Pointer) ([]byte, error) {
124 if hasNullPrefix(b) {
125 return b[4:], nil
126 }
127
128 v, r, err := parseUint(b, uintType)
129 if err != nil {
130 return r, err
131 }
132
133 *(*uint)(p) = uint(v)
134 return r, nil
135}
136
137func (d decoder) decodeUintptr(b []byte, p unsafe.Pointer) ([]byte, error) {
138 if hasNullPrefix(b) {
139 return b[4:], nil
140 }
141
142 v, r, err := parseUint(b, uintptrType)
143 if err != nil {
144 return r, err
145 }
146
147 *(*uintptr)(p) = uintptr(v)
148 return r, nil
149}
150
151func (d decoder) decodeUint8(b []byte, p unsafe.Pointer) ([]byte, error) {
152 if hasNullPrefix(b) {
153 return b[4:], nil
154 }
155
156 v, r, err := parseUint(b, uint8Type)
157 if err != nil {
158 return r, err
159 }
160
161 if v > math.MaxUint8 {
162 return r, unmarshalOverflow(b[:len(b)-len(r)], uint8Type)
163 }
164
165 *(*uint8)(p) = uint8(v)
166 return r, nil
167}
168
169func (d decoder) decodeUint16(b []byte, p unsafe.Pointer) ([]byte, error) {
170 if hasNullPrefix(b) {
171 return b[4:], nil
172 }
173
174 v, r, err := parseUint(b, uint16Type)
175 if err != nil {
176 return r, err
177 }
178
179 if v > math.MaxUint16 {
180 return r, unmarshalOverflow(b[:len(b)-len(r)], uint16Type)
181 }
182
183 *(*uint16)(p) = uint16(v)
184 return r, nil
185}
186
187func (d decoder) decodeUint32(b []byte, p unsafe.Pointer) ([]byte, error) {
188 if hasNullPrefix(b) {
189 return b[4:], nil
190 }
191
192 v, r, err := parseUint(b, uint32Type)
193 if err != nil {
194 return r, err
195 }
196
197 if v > math.MaxUint32 {
198 return r, unmarshalOverflow(b[:len(b)-len(r)], uint32Type)
199 }
200
201 *(*uint32)(p) = uint32(v)
202 return r, nil
203}
204
205func (d decoder) decodeUint64(b []byte, p unsafe.Pointer) ([]byte, error) {
206 if hasNullPrefix(b) {
207 return b[4:], nil
208 }
209
210 v, r, err := parseUint(b, uint64Type)
211 if err != nil {
212 return r, err
213 }
214
215 *(*uint64)(p) = v
216 return r, nil
217}
218
219func (d decoder) decodeFloat32(b []byte, p unsafe.Pointer) ([]byte, error) {
220 if hasNullPrefix(b) {
221 return b[4:], nil
222 }
223
224 v, r, err := parseNumber(b)
225 if err != nil {
226 return inputError(b, float32Type)
227 }
228
229 f, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&v)), 32)
230 if err != nil {
231 return inputError(b, float32Type)
232 }
233
234 *(*float32)(p) = float32(f)
235 return r, nil
236}
237
238func (d decoder) decodeFloat64(b []byte, p unsafe.Pointer) ([]byte, error) {
239 if hasNullPrefix(b) {
240 return b[4:], nil
241 }
242
243 v, r, err := parseNumber(b)
244 if err != nil {
245 return inputError(b, float64Type)
246 }
247
248 f, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&v)), 64)
249 if err != nil {
250 return inputError(b, float64Type)
251 }
252
253 *(*float64)(p) = f
254 return r, nil
255}
256
257func (d decoder) decodeNumber(b []byte, p unsafe.Pointer) ([]byte, error) {
258 if hasNullPrefix(b) {
259 return b[4:], nil
260 }
261
262 v, r, err := parseNumber(b)
263 if err != nil {
264 return inputError(b, numberType)
265 }
266
267 if (d.flags & DontCopyNumber) != 0 {
268 *(*Number)(p) = *(*Number)(unsafe.Pointer(&v))
269 } else {
270 *(*Number)(p) = Number(v)
271 }
272
273 return r, nil
274}
275
276func (d decoder) decodeString(b []byte, p unsafe.Pointer) ([]byte, error) {
277 if hasNullPrefix(b) {
278 return b[4:], nil
279 }
280
281 s, r, new, err := parseStringUnquote(b, nil)
282 if err != nil {
283 if len(b) == 0 || b[0] != '"' {
284 return inputError(b, stringType)
285 }
286 return r, err
287 }
288
289 if new || (d.flags&DontCopyString) != 0 {
290 *(*string)(p) = *(*string)(unsafe.Pointer(&s))
291 } else {
292 *(*string)(p) = string(s)
293 }
294
295 return r, nil
296}
297
298func (d decoder) decodeFromString(b []byte, p unsafe.Pointer, decode decodeFunc) ([]byte, error) {
299 if hasNullPrefix(b) {
300 return decode(d, b, p)
301 }
302
303 v, b, _, err := parseStringUnquote(b, nil)
304 if err != nil {
305 return inputError(v, stringType)
306 }
307
308 if v, err = decode(d, v, p); err != nil {
309 return b, err
310 }
311
312 if v = skipSpaces(v); len(v) != 0 {
313 return b, syntaxError(v, "unexpected trailing tokens after string value")
314 }
315
316 return b, nil
317}
318
319func (d decoder) decodeFromStringToInt(b []byte, p unsafe.Pointer, t reflect.Type, decode decodeFunc) ([]byte, error) {
320 if hasPrefix(b, "null") {
321 return decode(d, b, p)
322 }
323
324 if len(b) > 0 && b[0] != '"' {
325 v, r, err := parseNumber(b)
326 if err == nil {
327 // The encoding/json package will return a *json.UnmarshalTypeError if
328 // the input was a floating point number representation, even tho a
329 // string is expected here.
330 isFloat := true
331 switch {
332 case bytes.IndexByte(v, '.') >= 0:
333 case bytes.IndexByte(v, 'e') >= 0:
334 case bytes.IndexByte(v, 'E') >= 0:
335 default:
336 isFloat = false
337 }
338 if isFloat {
339 _, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&v)), 64)
340 if err != nil {
341 return r, unmarshalTypeError(v, t)
342 }
343 }
344 }
345 return r, fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into int")
346 }
347
348 if len(b) > 1 && b[0] == '"' && b[1] == '"' {
349 return b, fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal \"\" into int")
350 }
351
352 v, b, _, err := parseStringUnquote(b, nil)
353 if err != nil {
354 return inputError(v, t)
355 }
356
357 if hasLeadingZeroes(v) {
358 // In this context the encoding/json package accepts leading zeroes because
359 // it is not constrained by the JSON syntax, remove them so the parsing
360 // functions don't return syntax errors.
361 u := make([]byte, 0, len(v))
362 i := 0
363
364 if i < len(v) && v[i] == '-' || v[i] == '+' {
365 u = append(u, v[i])
366 i++
367 }
368
369 for (i+1) < len(v) && v[i] == '0' && '0' <= v[i+1] && v[i+1] <= '9' {
370 i++
371 }
372
373 v = append(u, v[i:]...)
374 }
375
376 if r, err := decode(d, v, p); err != nil {
377 if _, isSyntaxError := err.(*SyntaxError); isSyntaxError {
378 if hasPrefix(v, "-") {
379 // The standard library interprets sequences of '-' characters
380 // as numbers but still returns type errors in this case...
381 return b, unmarshalTypeError(v, t)
382 }
383 return b, fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into int", prefix(v))
384 }
385 // When the input value was a valid number representation we retain the
386 // error returned by the decoder.
387 if _, _, err := parseNumber(v); err != nil {
388 // When the input value valid JSON we mirror the behavior of the
389 // encoding/json package and return a generic error.
390 if _, _, err := parseValue(v); err == nil {
391 return b, fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into int", prefix(v))
392 }
393 }
394 return b, err
395 } else if len(r) != 0 {
396 return r, unmarshalTypeError(v, t)
397 }
398
399 return b, nil
400}
401
402func (d decoder) decodeBytes(b []byte, p unsafe.Pointer) ([]byte, error) {
403 if hasNullPrefix(b) {
404 *(*[]byte)(p) = nil
405 return b[4:], nil
406 }
407
408 if len(b) < 2 {
409 return inputError(b, bytesType)
410 }
411
412 if b[0] != '"' {
413 // Go 1.7- behavior: bytes slices may be decoded from array of integers.
414 if len(b) > 0 && b[0] == '[' {
415 return d.decodeSlice(b, p, 1, bytesType, decoder.decodeUint8)
416 }
417 return inputError(b, bytesType)
418 }
419
420 // The input string contains escaped sequences, we need to parse it before
421 // decoding it to match the encoding/json package behvaior.
422 src, r, _, err := parseStringUnquote(b, nil)
423 if err != nil {
424 return inputError(b, bytesType)
425 }
426
427 dst := make([]byte, base64.StdEncoding.DecodedLen(len(src)))
428
429 n, err := base64.StdEncoding.Decode(dst, src)
430 if err != nil {
431 return r, err
432 }
433
434 *(*[]byte)(p) = dst[:n]
435 return r, nil
436}
437
438func (d decoder) decodeDuration(b []byte, p unsafe.Pointer) ([]byte, error) {
439 if hasNullPrefix(b) {
440 return b[4:], nil
441 }
442
443 // in order to inter-operate with the stdlib, we must be able to interpret
444 // durations passed as integer values. there's some discussion about being
445 // flexible on how durations are formatted, but for the time being, it's
446 // been punted to go2 at the earliest: https://github.com/golang/go/issues/4712
447 if len(b) > 0 && b[0] != '"' {
448 v, r, err := parseInt(b, durationType)
449 if err != nil {
450 return inputError(b, int32Type)
451 }
452
453 if v < math.MinInt64 || v > math.MaxInt64 {
454 return r, unmarshalOverflow(b[:len(b)-len(r)], int32Type)
455 }
456
457 *(*time.Duration)(p) = time.Duration(v)
458 return r, nil
459 }
460
461 if len(b) < 2 || b[0] != '"' {
462 return inputError(b, durationType)
463 }
464
465 i := bytes.IndexByte(b[1:], '"') + 1
466 if i <= 0 {
467 return inputError(b, durationType)
468 }
469
470 s := b[1:i] // trim quotes
471
472 v, err := time.ParseDuration(*(*string)(unsafe.Pointer(&s)))
473 if err != nil {
474 return inputError(b, durationType)
475 }
476
477 *(*time.Duration)(p) = v
478 return b[i+1:], nil
479}
480
481func (d decoder) decodeTime(b []byte, p unsafe.Pointer) ([]byte, error) {
482 if hasNullPrefix(b) {
483 return b[4:], nil
484 }
485
486 if len(b) < 2 || b[0] != '"' {
487 return inputError(b, timeType)
488 }
489
490 i := bytes.IndexByte(b[1:], '"') + 1
491 if i <= 0 {
492 return inputError(b, timeType)
493 }
494
495 s := b[1:i] // trim quotes
496
497 v, err := time.Parse(time.RFC3339Nano, *(*string)(unsafe.Pointer(&s)))
498 if err != nil {
499 return inputError(b, timeType)
500 }
501
502 *(*time.Time)(p) = v
503 return b[i+1:], nil
504}
505
506func (d decoder) decodeArray(b []byte, p unsafe.Pointer, n int, size uintptr, t reflect.Type, decode decodeFunc) ([]byte, error) {
507 if hasNullPrefix(b) {
508 return b[4:], nil
509 }
510
511 if len(b) < 2 || b[0] != '[' {
512 return inputError(b, t)
513 }
514 b = b[1:]
515
516 var err error
517 for i := 0; i < n; i++ {
518 b = skipSpaces(b)
519
520 if i != 0 {
521 if len(b) == 0 {
522 return b, syntaxError(b, "unexpected EOF after array element")
523 }
524 switch b[0] {
525 case ',':
526 b = skipSpaces(b[1:])
527 case ']':
528 return b[1:], nil
529 default:
530 return b, syntaxError(b, "expected ',' after array element but found '%c'", b[0])
531 }
532 }
533
534 b, err = decode(d, b, unsafe.Pointer(uintptr(p)+(uintptr(i)*size)))
535 if err != nil {
536 if e, ok := err.(*UnmarshalTypeError); ok {
537 e.Struct = t.String() + e.Struct
538 e.Field = strconv.Itoa(i) + "." + e.Field
539 }
540 return b, err
541 }
542 }
543
544 // The encoding/json package ignores extra elements found when decoding into
545 // array types (which have a fixed size).
546 for {
547 b = skipSpaces(b)
548
549 if len(b) == 0 {
550 return b, syntaxError(b, "missing closing ']' in array value")
551 }
552
553 switch b[0] {
554 case ',':
555 b = skipSpaces(b[1:])
556 case ']':
557 return b[1:], nil
558 }
559
560 _, b, err = parseValue(b)
561 if err != nil {
562 return b, err
563 }
564 }
565}
566
567var (
568 // This is a placeholder used to consturct non-nil empty slices.
569 empty struct{}
570)
571
572func (d decoder) decodeSlice(b []byte, p unsafe.Pointer, size uintptr, t reflect.Type, decode decodeFunc) ([]byte, error) {
573 if hasNullPrefix(b) {
574 *(*slice)(p) = slice{}
575 return b[4:], nil
576 }
577
578 if len(b) < 2 {
579 return inputError(b, t)
580 }
581
582 if b[0] != '[' {
583 // Go 1.7- behavior: fallback to decoding as a []byte if the element
584 // type is byte; allow conversions from JSON strings even tho the
585 // underlying type implemented unmarshaler interfaces.
586 if t.Elem().Kind() == reflect.Uint8 {
587 return d.decodeBytes(b, p)
588 }
589 return inputError(b, t)
590 }
591
592 input := b
593 b = b[1:]
594
595 s := (*slice)(p)
596 s.len = 0
597
598 var err error
599 for {
600 b = skipSpaces(b)
601
602 if len(b) != 0 && b[0] == ']' {
603 if s.data == nil {
604 s.data = unsafe.Pointer(&empty)
605 }
606 return b[1:], nil
607 }
608
609 if s.len != 0 {
610 if len(b) == 0 {
611 return b, syntaxError(b, "unexpected EOF after array element")
612 }
613 if b[0] != ',' {
614 return b, syntaxError(b, "expected ',' after array element but found '%c'", b[0])
615 }
616 b = skipSpaces(b[1:])
617 }
618
619 if s.len == s.cap {
620 c := s.cap
621
622 if c == 0 {
623 c = 10
624 } else {
625 c *= 2
626 }
627
628 *s = extendSlice(t, s, c)
629 }
630
631 b, err = decode(d, b, unsafe.Pointer(uintptr(s.data)+(uintptr(s.len)*size)))
632 if err != nil {
633 if _, r, err := parseValue(input); err != nil {
634 return r, err
635 } else {
636 b = r
637 }
638 if e, ok := err.(*UnmarshalTypeError); ok {
639 e.Struct = t.String() + e.Struct
640 e.Field = strconv.Itoa(s.len) + "." + e.Field
641 }
642 return b, err
643 }
644
645 s.len++
646 }
647}
648
649func (d decoder) decodeMap(b []byte, p unsafe.Pointer, t, kt, vt reflect.Type, kz, vz reflect.Value, decodeKey, decodeValue decodeFunc) ([]byte, error) {
650 if hasNullPrefix(b) {
651 *(*unsafe.Pointer)(p) = nil
652 return b[4:], nil
653 }
654
655 if len(b) < 2 || b[0] != '{' {
656 return inputError(b, t)
657 }
658 i := 0
659 m := reflect.NewAt(t, p).Elem()
660
661 k := reflect.New(kt).Elem()
662 v := reflect.New(vt).Elem()
663
664 kptr := (*iface)(unsafe.Pointer(&k)).ptr
665 vptr := (*iface)(unsafe.Pointer(&v)).ptr
666 input := b
667
668 if m.IsNil() {
669 m = reflect.MakeMap(t)
670 }
671
672 var err error
673 b = b[1:]
674 for {
675 k.Set(kz)
676 v.Set(vz)
677 b = skipSpaces(b)
678
679 if len(b) != 0 && b[0] == '}' {
680 *(*unsafe.Pointer)(p) = unsafe.Pointer(m.Pointer())
681 return b[1:], nil
682 }
683
684 if i != 0 {
685 if len(b) == 0 {
686 return b, syntaxError(b, "unexpected end of JONS input after object field value")
687 }
688 if b[0] != ',' {
689 return b, syntaxError(b, "expected ',' after object field value but found '%c'", b[0])
690 }
691 b = skipSpaces(b[1:])
692 }
693
694 if hasPrefix(b, "null") {
695 return b, syntaxError(b, "cannot decode object key string from 'null' value")
696 }
697
698 if b, err = decodeKey(d, b, kptr); err != nil {
699 return objectKeyError(b, err)
700 }
701 b = skipSpaces(b)
702
703 if len(b) == 0 {
704 return b, syntaxError(b, "unexpected end of JSON input after object field key")
705 }
706 if b[0] != ':' {
707 return b, syntaxError(b, "expected ':' after object field key but found '%c'", b[0])
708 }
709 b = skipSpaces(b[1:])
710
711 if b, err = decodeValue(d, b, vptr); err != nil {
712 if _, r, err := parseValue(input); err != nil {
713 return r, err
714 } else {
715 b = r
716 }
717 if e, ok := err.(*UnmarshalTypeError); ok {
718 e.Struct = "map[" + kt.String() + "]" + vt.String() + "{" + e.Struct + "}"
719 e.Field = fmt.Sprint(k.Interface()) + "." + e.Field
720 }
721 return b, err
722 }
723
724 m.SetMapIndex(k, v)
725 i++
726 }
727}
728
729func (d decoder) decodeMapStringInterface(b []byte, p unsafe.Pointer) ([]byte, error) {
730 if hasNullPrefix(b) {
731 *(*unsafe.Pointer)(p) = nil
732 return b[4:], nil
733 }
734
735 if len(b) < 2 || b[0] != '{' {
736 return inputError(b, mapStringInterfaceType)
737 }
738
739 i := 0
740 m := *(*map[string]interface{})(p)
741
742 if m == nil {
743 m = make(map[string]interface{}, 64)
744 }
745
746 var err error
747 var key string
748 var val interface{}
749 var input = b
750
751 b = b[1:]
752 for {
753 key = ""
754 val = nil
755
756 b = skipSpaces(b)
757
758 if len(b) != 0 && b[0] == '}' {
759 *(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(unsafe.Pointer(&m))
760 return b[1:], nil
761 }
762
763 if i != 0 {
764 if len(b) == 0 {
765 return b, syntaxError(b, "unexpected end of JSON input after object field value")
766 }
767 if b[0] != ',' {
768 return b, syntaxError(b, "expected ',' after object field value but found '%c'", b[0])
769 }
770 b = skipSpaces(b[1:])
771 }
772
773 if hasPrefix(b, "null") {
774 return b, syntaxError(b, "cannot decode object key string from 'null' value")
775 }
776
777 b, err = d.decodeString(b, unsafe.Pointer(&key))
778 if err != nil {
779 return objectKeyError(b, err)
780 }
781 b = skipSpaces(b)
782
783 if len(b) == 0 {
784 return b, syntaxError(b, "unexpected end of JSON input after object field key")
785 }
786 if b[0] != ':' {
787 return b, syntaxError(b, "expected ':' after object field key but found '%c'", b[0])
788 }
789 b = skipSpaces(b[1:])
790
791 b, err = d.decodeInterface(b, unsafe.Pointer(&val))
792 if err != nil {
793 if _, r, err := parseValue(input); err != nil {
794 return r, err
795 } else {
796 b = r
797 }
798 if e, ok := err.(*UnmarshalTypeError); ok {
799 e.Struct = mapStringInterfaceType.String() + e.Struct
800 e.Field = key + "." + e.Field
801 }
802 return b, err
803 }
804
805 m[key] = val
806 i++
807 }
808}
809
810func (d decoder) decodeMapStringRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
811 if hasNullPrefix(b) {
812 *(*unsafe.Pointer)(p) = nil
813 return b[4:], nil
814 }
815
816 if len(b) < 2 || b[0] != '{' {
817 return inputError(b, mapStringRawMessageType)
818 }
819
820 i := 0
821 m := *(*map[string]RawMessage)(p)
822
823 if m == nil {
824 m = make(map[string]RawMessage, 64)
825 }
826
827 var err error
828 var key string
829 var val RawMessage
830 var input = b
831
832 b = b[1:]
833 for {
834 key = ""
835 val = nil
836
837 b = skipSpaces(b)
838
839 if len(b) != 0 && b[0] == '}' {
840 *(*unsafe.Pointer)(p) = *(*unsafe.Pointer)(unsafe.Pointer(&m))
841 return b[1:], nil
842 }
843
844 if i != 0 {
845 if len(b) == 0 {
846 return b, syntaxError(b, "unexpected end of JSON input after object field value")
847 }
848 if b[0] != ',' {
849 return b, syntaxError(b, "expected ',' after object field value but found '%c'", b[0])
850 }
851 b = skipSpaces(b[1:])
852 }
853
854 if hasPrefix(b, "null") {
855 return b, syntaxError(b, "cannot decode object key string from 'null' value")
856 }
857
858 b, err = d.decodeString(b, unsafe.Pointer(&key))
859 if err != nil {
860 return objectKeyError(b, err)
861 }
862 b = skipSpaces(b)
863
864 if len(b) == 0 {
865 return b, syntaxError(b, "unexpected end of JSON input after object field key")
866 }
867 if b[0] != ':' {
868 return b, syntaxError(b, "expected ':' after object field key but found '%c'", b[0])
869 }
870 b = skipSpaces(b[1:])
871
872 b, err = d.decodeRawMessage(b, unsafe.Pointer(&val))
873 if err != nil {
874 if _, r, err := parseValue(input); err != nil {
875 return r, err
876 } else {
877 b = r
878 }
879 if e, ok := err.(*UnmarshalTypeError); ok {
880 e.Struct = mapStringRawMessageType.String() + e.Struct
881 e.Field = key + "." + e.Field
882 }
883 return b, err
884 }
885
886 m[key] = val
887 i++
888 }
889}
890
891func (d decoder) decodeStruct(b []byte, p unsafe.Pointer, st *structType) ([]byte, error) {
892 if hasNullPrefix(b) {
893 return b[4:], nil
894 }
895
896 if len(b) < 2 || b[0] != '{' {
897 return inputError(b, st.typ)
898 }
899
900 var err error
901 var k []byte
902 var i int
903
904 // memory buffer used to convert short field names to lowercase
905 var buf [64]byte
906 var key []byte
907 var input = b
908
909 b = b[1:]
910 for {
911 b = skipSpaces(b)
912
913 if len(b) != 0 && b[0] == '}' {
914 return b[1:], nil
915 }
916
917 if i != 0 {
918 if len(b) == 0 {
919 return b, syntaxError(b, "unexpected end of JSON input after object field value")
920 }
921 if b[0] != ',' {
922 return b, syntaxError(b, "expected ',' after object field value but found '%c'", b[0])
923 }
924 b = skipSpaces(b[1:])
925 }
926 i++
927
928 if hasPrefix(b, "null") {
929 return b, syntaxError(b, "cannot decode object key string from 'null' value")
930 }
931
932 k, b, _, err = parseStringUnquote(b, nil)
933 if err != nil {
934 return objectKeyError(b, err)
935 }
936 b = skipSpaces(b)
937
938 if len(b) == 0 {
939 return b, syntaxError(b, "unexpected end of JSON input after object field key")
940 }
941 if b[0] != ':' {
942 return b, syntaxError(b, "expected ':' after object field key but found '%c'", b[0])
943 }
944 b = skipSpaces(b[1:])
945
946 f := st.fieldsIndex[string(k)]
947
948 if f == nil && (d.flags&DontMatchCaseInsensitiveStructFields) == 0 {
949 key = appendToLower(buf[:0], k)
950 f = st.ficaseIndex[string(key)]
951 }
952
953 if f == nil {
954 if (d.flags & DisallowUnknownFields) != 0 {
955 return b, fmt.Errorf("json: unknown field %q", k)
956 }
957 if _, b, err = parseValue(b); err != nil {
958 return b, err
959 }
960 continue
961 }
962
963 if b, err = f.codec.decode(d, b, unsafe.Pointer(uintptr(p)+f.offset)); err != nil {
964 if _, r, err := parseValue(input); err != nil {
965 return r, err
966 } else {
967 b = r
968 }
969 if e, ok := err.(*UnmarshalTypeError); ok {
970 e.Struct = st.typ.String() + e.Struct
971 e.Field = string(k) + "." + e.Field
972 }
973 return b, err
974 }
975 }
976}
977
978func (d decoder) decodeEmbeddedStructPointer(b []byte, p unsafe.Pointer, t reflect.Type, unexported bool, offset uintptr, decode decodeFunc) ([]byte, error) {
979 v := *(*unsafe.Pointer)(p)
980
981 if v == nil {
982 if unexported {
983 return nil, fmt.Errorf("json: cannot set embedded pointer to unexported struct: %s", t)
984 }
985 v = unsafe.Pointer(reflect.New(t).Pointer())
986 *(*unsafe.Pointer)(p) = v
987 }
988
989 return decode(d, b, unsafe.Pointer(uintptr(v)+offset))
990}
991
992func (d decoder) decodePointer(b []byte, p unsafe.Pointer, t reflect.Type, decode decodeFunc) ([]byte, error) {
993 if hasNullPrefix(b) {
994 pp := *(*unsafe.Pointer)(p)
995 if pp != nil && t.Kind() == reflect.Ptr {
996 return decode(d, b, pp)
997 }
998 *(*unsafe.Pointer)(p) = nil
999 return b[4:], nil
1000 }
1001
1002 v := *(*unsafe.Pointer)(p)
1003 if v == nil {
1004 v = unsafe.Pointer(reflect.New(t).Pointer())
1005 *(*unsafe.Pointer)(p) = v
1006 }
1007
1008 return decode(d, b, v)
1009}
1010
1011func (d decoder) decodeInterface(b []byte, p unsafe.Pointer) ([]byte, error) {
1012 val := *(*interface{})(p)
1013 *(*interface{})(p) = nil
1014
1015 if t := reflect.TypeOf(val); t != nil && t.Kind() == reflect.Ptr {
1016 if v := reflect.ValueOf(val); v.IsNil() || t.Elem().Kind() != reflect.Ptr {
1017 // If the destination is nil the only value that is OK to decode is
1018 // `null`, and the encoding/json package always nils the destination
1019 // interface value in this case.
1020 if hasNullPrefix(b) {
1021 *(*interface{})(p) = nil
1022 return b[4:], nil
1023 }
1024 }
1025
1026 b, err := Parse(b, val, d.flags)
1027 if err == nil {
1028 *(*interface{})(p) = val
1029 }
1030 return b, err
1031 }
1032
1033 v, b, err := parseValue(b)
1034 if err != nil {
1035 return b, err
1036 }
1037
1038 switch v[0] {
1039 case '{':
1040 m := make(map[string]interface{})
1041 v, err = d.decodeMapStringInterface(v, unsafe.Pointer(&m))
1042 val = m
1043
1044 case '[':
1045 a := make([]interface{}, 0, 10)
1046 v, err = d.decodeSlice(v, unsafe.Pointer(&a), unsafe.Sizeof(a[0]), sliceInterfaceType, decoder.decodeInterface)
1047 val = a
1048
1049 case '"':
1050 s := ""
1051 v, err = d.decodeString(v, unsafe.Pointer(&s))
1052 val = s
1053
1054 case 'n':
1055 v, err = d.decodeNull(v, nil)
1056 val = nil
1057
1058 case 't', 'f':
1059 x := false
1060 v, err = d.decodeBool(v, unsafe.Pointer(&x))
1061 val = x
1062
1063 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
1064 if (d.flags & UseNumber) != 0 {
1065 n := Number("")
1066 v, err = d.decodeNumber(v, unsafe.Pointer(&n))
1067 val = n
1068 } else {
1069 f := 0.0
1070 v, err = d.decodeFloat64(v, unsafe.Pointer(&f))
1071 val = f
1072 }
1073
1074 default:
1075 return b, syntaxError(v, "expected token but found '%c'", v[0])
1076 }
1077
1078 if err != nil {
1079 return b, err
1080 }
1081
1082 if v = skipSpaces(v); len(v) != 0 {
1083 return b, syntaxError(v, "unexpected trailing trailing tokens after json value")
1084 }
1085
1086 *(*interface{})(p) = val
1087 return b, nil
1088}
1089
1090func (d decoder) decodeMaybeEmptyInterface(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {
1091 if hasNullPrefix(b) {
1092 *(*interface{})(p) = nil
1093 return b[4:], nil
1094 }
1095
1096 if x := reflect.NewAt(t, p).Elem(); !x.IsNil() {
1097 if e := x.Elem(); e.Kind() == reflect.Ptr {
1098 return Parse(b, e.Interface(), d.flags)
1099 }
1100 } else if t.NumMethod() == 0 { // empty interface
1101 return Parse(b, (*interface{})(p), d.flags)
1102 }
1103
1104 return d.decodeUnmarshalTypeError(b, p, t)
1105}
1106
1107func (d decoder) decodeUnmarshalTypeError(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {
1108 v, b, err := parseValue(b)
1109 if err != nil {
1110 return b, err
1111 }
1112 return b, &UnmarshalTypeError{
1113 Value: string(v),
1114 Type: t,
1115 }
1116}
1117
1118func (d decoder) decodeRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
1119 v, r, err := parseValue(b)
1120 if err != nil {
1121 return inputError(b, rawMessageType)
1122 }
1123
1124 if (d.flags & DontCopyRawMessage) == 0 {
1125 v = append(make([]byte, 0, len(v)), v...)
1126 }
1127
1128 *(*RawMessage)(p) = json.RawMessage(v)
1129 return r, err
1130}
1131
1132func (d decoder) decodeJSONUnmarshaler(b []byte, p unsafe.Pointer, t reflect.Type, pointer bool) ([]byte, error) {
1133 v, b, err := parseValue(b)
1134 if err != nil {
1135 return b, err
1136 }
1137
1138 if len(v) != 0 && v[0] == 'n' { // null
1139 return b, nil
1140 }
1141
1142 u := reflect.NewAt(t, p)
1143 if !pointer {
1144 u = u.Elem()
1145 t = t.Elem()
1146 }
1147 if u.IsNil() {
1148 u.Set(reflect.New(t))
1149 }
1150 return b, u.Interface().(Unmarshaler).UnmarshalJSON(v)
1151}
1152
1153func (d decoder) decodeTextUnmarshaler(b []byte, p unsafe.Pointer, t reflect.Type, pointer bool) ([]byte, error) {
1154 var value string
1155
1156 v, b, err := parseValue(b)
1157 if err != nil {
1158 return b, err
1159 }
1160 if len(v) == 0 {
1161 return inputError(v, t)
1162 }
1163
1164 switch v[0] {
1165 case 'n':
1166 _, _, err := parseNull(v)
1167 return b, err
1168 case '"':
1169 s, _, _, err := parseStringUnquote(v, nil)
1170 if err != nil {
1171 return b, err
1172 }
1173 u := reflect.NewAt(t, p)
1174 if !pointer {
1175 u = u.Elem()
1176 t = t.Elem()
1177 }
1178 if u.IsNil() {
1179 u.Set(reflect.New(t))
1180 }
1181 return b, u.Interface().(encoding.TextUnmarshaler).UnmarshalText(s)
1182 case '{':
1183 value = "object"
1184 case '[':
1185 value = "array"
1186 case 't':
1187 value = "true"
1188 case 'f':
1189 value = "false"
1190 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
1191 value = "number"
1192 }
1193
1194 return b, &UnmarshalTypeError{Value: value, Type: reflect.PtrTo(t)}
1195}
diff --git a/vendor/github.com/neilotoole/jsoncolor/encode.go b/vendor/github.com/neilotoole/jsoncolor/encode.go
new file mode 100644
index 0000000..4259352
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/encode.go
@@ -0,0 +1,1054 @@
1package jsoncolor
2
3import (
4 "bytes"
5 "encoding"
6 "encoding/base64"
7 "math"
8 "reflect"
9 "sort"
10 "strconv"
11 "sync"
12 "time"
13 "unicode/utf8"
14 "unsafe"
15)
16
17const hex = "0123456789abcdef"
18
19func (e encoder) encodeNull(b []byte, p unsafe.Pointer) ([]byte, error) {
20 return e.clrs.appendNull(b), nil
21}
22
23func (e encoder) encodeBool(b []byte, p unsafe.Pointer) ([]byte, error) {
24 return e.clrs.appendBool(b, *(*bool)(p)), nil
25}
26
27func (e encoder) encodeInt(b []byte, p unsafe.Pointer) ([]byte, error) {
28 return e.clrs.appendInt64(b, int64(*(*int)(p))), nil
29}
30
31func (e encoder) encodeInt8(b []byte, p unsafe.Pointer) ([]byte, error) {
32 return e.clrs.appendInt64(b, int64(*(*int8)(p))), nil
33}
34
35func (e encoder) encodeInt16(b []byte, p unsafe.Pointer) ([]byte, error) {
36 return e.clrs.appendInt64(b, int64(*(*int16)(p))), nil
37}
38
39func (e encoder) encodeInt32(b []byte, p unsafe.Pointer) ([]byte, error) {
40 return e.clrs.appendInt64(b, int64(*(*int32)(p))), nil
41}
42
43func (e encoder) encodeInt64(b []byte, p unsafe.Pointer) ([]byte, error) {
44 return e.clrs.appendInt64(b, *(*int64)(p)), nil
45}
46
47func (e encoder) encodeUint(b []byte, p unsafe.Pointer) ([]byte, error) {
48 return e.clrs.appendUint64(b, uint64(*(*uint)(p))), nil
49}
50
51func (e encoder) encodeUintptr(b []byte, p unsafe.Pointer) ([]byte, error) {
52 return e.clrs.appendUint64(b, uint64(*(*uintptr)(p))), nil
53}
54
55func (e encoder) encodeUint8(b []byte, p unsafe.Pointer) ([]byte, error) {
56 return e.clrs.appendUint64(b, uint64(*(*uint8)(p))), nil
57}
58
59func (e encoder) encodeUint16(b []byte, p unsafe.Pointer) ([]byte, error) {
60 return e.clrs.appendUint64(b, uint64(*(*uint16)(p))), nil
61}
62
63func (e encoder) encodeUint32(b []byte, p unsafe.Pointer) ([]byte, error) {
64 return e.clrs.appendUint64(b, uint64(*(*uint32)(p))), nil
65}
66
67func (e encoder) encodeUint64(b []byte, p unsafe.Pointer) ([]byte, error) {
68 return e.clrs.appendUint64(b, *(*uint64)(p)), nil
69}
70
71func (e encoder) encodeFloat32(b []byte, p unsafe.Pointer) ([]byte, error) {
72 if e.clrs == nil {
73 return e.encodeFloat(b, float64(*(*float32)(p)), 32)
74 }
75
76 b = append(b, e.clrs.Number...)
77 var err error
78 b, err = e.encodeFloat(b, float64(*(*float32)(p)), 32)
79 b = append(b, ansiReset...)
80 return b, err
81}
82
83func (e encoder) encodeFloat64(b []byte, p unsafe.Pointer) ([]byte, error) {
84 if e.clrs == nil {
85 return e.encodeFloat(b, *(*float64)(p), 64)
86 }
87
88 b = append(b, e.clrs.Number...)
89 var err error
90 b, err = e.encodeFloat(b, *(*float64)(p), 64)
91 b = append(b, ansiReset...)
92 return b, err
93}
94
95func (e encoder) encodeFloat(b []byte, f float64, bits int) ([]byte, error) {
96 switch {
97 case math.IsNaN(f):
98 return b, &UnsupportedValueError{Value: reflect.ValueOf(f), Str: "NaN"}
99 case math.IsInf(f, 0):
100 return b, &UnsupportedValueError{Value: reflect.ValueOf(f), Str: "inf"}
101 }
102
103 // Convert as if by ES6 number to string conversion.
104 // This matches most other JSON generators.
105 // See golang.org/issue/6384 and golang.org/issue/14135.
106 // Like fmt %g, but the exponent cutoffs are different
107 // and exponents themselves are not padded to two digits.
108 abs := math.Abs(f)
109 fmt := byte('f')
110 // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
111 if abs != 0 {
112 if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
113 fmt = 'e'
114 }
115 }
116
117 b = strconv.AppendFloat(b, f, fmt, -1, int(bits))
118
119 if fmt == 'e' {
120 // clean up e-09 to e-9
121 n := len(b)
122 if n >= 4 && b[n-4] == 'e' && b[n-3] == '-' && b[n-2] == '0' {
123 b[n-2] = b[n-1]
124 b = b[:n-1]
125 }
126 }
127
128 return b, nil
129}
130
131func (e encoder) encodeNumber(b []byte, p unsafe.Pointer) ([]byte, error) {
132 n := *(*Number)(p)
133 if n == "" {
134 n = "0"
135 }
136
137 _, _, err := parseNumber(stringToBytes(string(n)))
138 if err != nil {
139 return b, err
140 }
141
142 if e.clrs == nil {
143 return append(b, n...), nil
144 }
145
146 b = append(b, e.clrs.Number...)
147 b = append(b, n...)
148 b = append(b, ansiReset...)
149 return b, nil
150}
151
152func (e encoder) encodeKey(b []byte, p unsafe.Pointer) ([]byte, error) {
153 if e.clrs == nil {
154 return e.doEncodeString(b, p)
155 }
156
157 b = append(b, e.clrs.Key...)
158 var err error
159 b, err = e.doEncodeString(b, p)
160 b = append(b, ansiReset...)
161 return b, err
162}
163
164func (e encoder) encodeString(b []byte, p unsafe.Pointer) ([]byte, error) {
165 if e.clrs == nil {
166 return e.doEncodeString(b, p)
167 }
168
169 b = append(b, e.clrs.String...)
170 var err error
171 b, err = e.doEncodeString(b, p)
172 b = append(b, ansiReset...)
173 return b, err
174}
175
176func (e encoder) doEncodeString(b []byte, p unsafe.Pointer) ([]byte, error) {
177 s := *(*string)(p)
178 i := 0
179 j := 0
180 escapeHTML := (e.flags & EscapeHTML) != 0
181
182 b = append(b, '"')
183
184 for j < len(s) {
185 c := s[j]
186
187 if c >= 0x20 && c <= 0x7f && c != '\\' && c != '"' && (!escapeHTML || (c != '<' && c != '>' && c != '&')) {
188 // fast path: most of the time, printable ascii characters are used
189 j++
190 continue
191 }
192
193 switch c {
194 case '\\', '"':
195 b = append(b, s[i:j]...)
196 b = append(b, '\\', c)
197 i = j + 1
198 j = j + 1
199 continue
200
201 case '\n':
202 b = append(b, s[i:j]...)
203 b = append(b, '\\', 'n')
204 i = j + 1
205 j = j + 1
206 continue
207
208 case '\r':
209 b = append(b, s[i:j]...)
210 b = append(b, '\\', 'r')
211 i = j + 1
212 j = j + 1
213 continue
214
215 case '\t':
216 b = append(b, s[i:j]...)
217 b = append(b, '\\', 't')
218 i = j + 1
219 j = j + 1
220 continue
221
222 case '<', '>', '&':
223 b = append(b, s[i:j]...)
224 b = append(b, `\u00`...)
225 b = append(b, hex[c>>4], hex[c&0xF])
226 i = j + 1
227 j = j + 1
228 continue
229 }
230
231 // This encodes bytes < 0x20 except for \t, \n and \r.
232 if c < 0x20 {
233 b = append(b, s[i:j]...)
234 b = append(b, `\u00`...)
235 b = append(b, hex[c>>4], hex[c&0xF])
236 i = j + 1
237 j = j + 1
238 continue
239 }
240
241 r, size := utf8.DecodeRuneInString(s[j:])
242
243 if r == utf8.RuneError && size == 1 {
244 b = append(b, s[i:j]...)
245 b = append(b, `\ufffd`...)
246 i = j + size
247 j = j + size
248 continue
249 }
250
251 switch r {
252 case '\u2028', '\u2029':
253 // U+2028 is LINE SEPARATOR.
254 // U+2029 is PARAGRAPH SEPARATOR.
255 // They are both technically valid characters in JSON strings,
256 // but don't work in JSONP, which has to be evaluated as JavaScript,
257 // and can lead to security holes there. It is valid JSON to
258 // escape them, so we do so unconditionally.
259 // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
260 b = append(b, s[i:j]...)
261 b = append(b, `\u202`...)
262 b = append(b, hex[r&0xF])
263 i = j + size
264 j = j + size
265 continue
266 }
267
268 j += size
269 }
270
271 b = append(b, s[i:]...)
272 b = append(b, '"')
273 return b, nil
274}
275
276func (e encoder) encodeToString(b []byte, p unsafe.Pointer, encode encodeFunc) ([]byte, error) {
277 i := len(b)
278
279 b, err := encode(e, b, p)
280 if err != nil {
281 return b, err
282 }
283
284 j := len(b)
285 s := b[i:]
286
287 if b, err = e.doEncodeString(b, unsafe.Pointer(&s)); err != nil {
288 return b, err
289 }
290
291 n := copy(b[i:], b[j:])
292 return b[:i+n], nil
293}
294
295func (e encoder) encodeBytes(b []byte, p unsafe.Pointer) ([]byte, error) {
296 if e.clrs == nil {
297 return e.doEncodeBytes(b, p)
298 }
299
300 b = append(b, e.clrs.Bytes...)
301 var err error
302 b, err = e.doEncodeBytes(b, p)
303 return append(b, ansiReset...), err
304}
305
306func (e encoder) doEncodeBytes(b []byte, p unsafe.Pointer) ([]byte, error) {
307 v := *(*[]byte)(p)
308 if v == nil {
309 return e.clrs.appendNull(b), nil
310 }
311
312 n := base64.StdEncoding.EncodedLen(len(v)) + 2
313
314 if avail := cap(b) - len(b); avail < n {
315 newB := make([]byte, cap(b)+(n-avail))
316 copy(newB, b)
317 b = newB[:len(b)]
318 }
319
320 i := len(b)
321 j := len(b) + n
322
323 b = b[:j]
324 b[i] = '"'
325 base64.StdEncoding.Encode(b[i+1:j-1], v)
326 b[j-1] = '"'
327 return b, nil
328}
329
330func (e encoder) encodeDuration(b []byte, p unsafe.Pointer) ([]byte, error) {
331 // NOTE: The segmentj encoder does special handling for time.Duration (converts to string).
332 // The stdlib encoder does not. It just outputs the int64 value.
333 // We choose to follow the stdlib pattern, for fuller compatibility.
334
335 b = e.clrs.appendInt64(b, int64(*(*time.Duration)(p)))
336 return b, nil
337
338 // NOTE: if we were to follow the segmentj pattern, we'd execute the code below.
339 //if e.clrs == nil {
340 // b = append(b, '"')
341 //
342 // b = appendDuration(b, *(*time.Duration)(p))
343 // b = append(b, '"')
344 // return b, nil
345 //}
346 //
347 //b = append(b, e.clrs.Time...)
348 //b = append(b, '"')
349 //b = appendDuration(b, *(*time.Duration)(p))
350 //b = append(b, '"')
351 //b = append(b, ansiReset...)
352 //return b, nil
353}
354
355func (e encoder) encodeTime(b []byte, p unsafe.Pointer) ([]byte, error) {
356 if e.clrs == nil {
357 t := *(*time.Time)(p)
358 b = append(b, '"')
359 b = t.AppendFormat(b, time.RFC3339Nano)
360 b = append(b, '"')
361 return b, nil
362 }
363
364 t := *(*time.Time)(p)
365 b = append(b, e.clrs.Time...)
366 b = append(b, '"')
367 b = t.AppendFormat(b, time.RFC3339Nano)
368 b = append(b, '"')
369 b = append(b, ansiReset...)
370 return b, nil
371}
372
373func (e encoder) encodeArray(b []byte, p unsafe.Pointer, n int, size uintptr, t reflect.Type, encode encodeFunc) ([]byte, error) {
374 var start = len(b)
375 var err error
376
377 b = e.clrs.appendPunc(b, '[')
378
379 if n > 0 {
380 e.indentr.push()
381 for i := 0; i < n; i++ {
382 if i != 0 {
383 b = e.clrs.appendPunc(b, ',')
384 }
385
386 b = e.indentr.appendByte(b, '\n')
387 b = e.indentr.appendIndent(b)
388
389 if b, err = encode(e, b, unsafe.Pointer(uintptr(p)+(uintptr(i)*size))); err != nil {
390 return b[:start], err
391 }
392 }
393 e.indentr.pop()
394 b = e.indentr.appendByte(b, '\n')
395 b = e.indentr.appendIndent(b)
396 }
397
398 b = e.clrs.appendPunc(b, ']')
399
400 return b, nil
401}
402
403func (e encoder) encodeSlice(b []byte, p unsafe.Pointer, size uintptr, t reflect.Type, encode encodeFunc) ([]byte, error) {
404 s := (*slice)(p)
405
406 if s.data == nil && s.len == 0 && s.cap == 0 {
407 return e.clrs.appendNull(b), nil
408 }
409
410 return e.encodeArray(b, s.data, s.len, size, t, encode)
411}
412
413func (e encoder) encodeMap(b []byte, p unsafe.Pointer, t reflect.Type, encodeKey, encodeValue encodeFunc, sortKeys sortFunc) ([]byte, error) {
414 m := reflect.NewAt(t, p).Elem()
415 if m.IsNil() {
416 return e.clrs.appendNull(b), nil
417 }
418
419 keys := m.MapKeys()
420 if sortKeys != nil && (e.flags&SortMapKeys) != 0 {
421 sortKeys(keys)
422 }
423
424 var start = len(b)
425 var err error
426 b = e.clrs.appendPunc(b, '{')
427
428 if len(keys) != 0 {
429 b = e.indentr.appendByte(b, '\n')
430
431 e.indentr.push()
432 for i, k := range keys {
433 v := m.MapIndex(k)
434
435 if i != 0 {
436 b = e.clrs.appendPunc(b, ',')
437 b = e.indentr.appendByte(b, '\n')
438 }
439
440 b = e.indentr.appendIndent(b)
441 if b, err = encodeKey(e, b, (*iface)(unsafe.Pointer(&k)).ptr); err != nil {
442 return b[:start], err
443 }
444
445 b = e.clrs.appendPunc(b, ':')
446 b = e.indentr.appendByte(b, ' ')
447
448 if b, err = encodeValue(e, b, (*iface)(unsafe.Pointer(&v)).ptr); err != nil {
449 return b[:start], err
450 }
451 }
452 b = e.indentr.appendByte(b, '\n')
453 e.indentr.pop()
454 b = e.indentr.appendIndent(b)
455 }
456
457 b = e.clrs.appendPunc(b, '}')
458 return b, nil
459}
460
461type element struct {
462 key string
463 val interface{}
464 raw RawMessage
465}
466
467type mapslice struct {
468 elements []element
469}
470
471func (m *mapslice) Len() int { return len(m.elements) }
472func (m *mapslice) Less(i, j int) bool { return m.elements[i].key < m.elements[j].key }
473func (m *mapslice) Swap(i, j int) { m.elements[i], m.elements[j] = m.elements[j], m.elements[i] }
474
475var mapslicePool = sync.Pool{
476 New: func() interface{} { return new(mapslice) },
477}
478
479func (e encoder) encodeMapStringInterface(b []byte, p unsafe.Pointer) ([]byte, error) {
480 m := *(*map[string]interface{})(p)
481 if m == nil {
482 return e.clrs.appendNull(b), nil
483 }
484
485 if (e.flags & SortMapKeys) == 0 {
486 // Optimized code path when the program does not need the map keys to be
487 // sorted.
488 b = e.clrs.appendPunc(b, '{')
489
490 if len(m) != 0 {
491 b = e.indentr.appendByte(b, '\n')
492
493 var err error
494 var i = 0
495
496 e.indentr.push()
497 for k, v := range m {
498 if i != 0 {
499 b = e.clrs.appendPunc(b, ',')
500 b = e.indentr.appendByte(b, '\n')
501 }
502
503 b = e.indentr.appendIndent(b)
504
505 b, err = e.encodeKey(b, unsafe.Pointer(&k))
506 if err != nil {
507 return b, err
508 }
509
510 b = e.clrs.appendPunc(b, ':')
511 b = e.indentr.appendByte(b, ' ')
512
513 b, err = Append(b, v, e.flags, e.clrs, e.indentr)
514 if err != nil {
515 return b, err
516 }
517
518 i++
519 }
520 b = e.indentr.appendByte(b, '\n')
521 e.indentr.pop()
522 b = e.indentr.appendIndent(b)
523 }
524
525 b = e.clrs.appendPunc(b, '}')
526 return b, nil
527 }
528
529 s := mapslicePool.Get().(*mapslice)
530 if cap(s.elements) < len(m) {
531 s.elements = make([]element, 0, align(10, uintptr(len(m))))
532 }
533 for key, val := range m {
534 s.elements = append(s.elements, element{key: key, val: val})
535 }
536 sort.Sort(s)
537
538 var start = len(b)
539 var err error
540 b = e.clrs.appendPunc(b, '{')
541
542 if len(s.elements) > 0 {
543 b = e.indentr.appendByte(b, '\n')
544
545 e.indentr.push()
546 for i, elem := range s.elements {
547 if i != 0 {
548 b = e.clrs.appendPunc(b, ',')
549 b = e.indentr.appendByte(b, '\n')
550 }
551
552 b = e.indentr.appendIndent(b)
553
554 b, _ = e.encodeKey(b, unsafe.Pointer(&elem.key))
555 b = e.clrs.appendPunc(b, ':')
556 b = e.indentr.appendByte(b, ' ')
557
558 b, err = Append(b, elem.val, e.flags, e.clrs, e.indentr)
559 if err != nil {
560 break
561 }
562 }
563 b = e.indentr.appendByte(b, '\n')
564 e.indentr.pop()
565 b = e.indentr.appendIndent(b)
566 }
567
568 for i := range s.elements {
569 s.elements[i] = element{}
570 }
571
572 s.elements = s.elements[:0]
573 mapslicePool.Put(s)
574
575 if err != nil {
576 return b[:start], err
577 }
578
579 b = e.clrs.appendPunc(b, '}')
580 return b, nil
581}
582
583func (e encoder) encodeMapStringRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
584 m := *(*map[string]RawMessage)(p)
585 if m == nil {
586 return e.clrs.appendNull(b), nil
587 }
588
589 if (e.flags & SortMapKeys) == 0 {
590 // Optimized code path when the program does not need the map keys to be
591 // sorted.
592 b = e.clrs.appendPunc(b, '{')
593
594 if len(m) != 0 {
595 b = e.indentr.appendByte(b, '\n')
596
597 var err error
598 var i = 0
599
600 e.indentr.push()
601 for k, v := range m {
602 if i != 0 {
603 b = e.clrs.appendPunc(b, ',')
604 b = e.indentr.appendByte(b, '\n')
605 }
606
607 b = e.indentr.appendIndent(b)
608
609 b, _ = e.encodeKey(b, unsafe.Pointer(&k))
610
611 b = e.clrs.appendPunc(b, ':')
612 b = e.indentr.appendByte(b, ' ')
613
614 b, err = e.encodeRawMessage(b, unsafe.Pointer(&v))
615 if err != nil {
616 break
617 }
618
619 i++
620 }
621 b = e.indentr.appendByte(b, '\n')
622 e.indentr.pop()
623 b = e.indentr.appendIndent(b)
624 }
625
626 b = e.clrs.appendPunc(b, '}')
627 return b, nil
628 }
629
630 s := mapslicePool.Get().(*mapslice)
631 if cap(s.elements) < len(m) {
632 s.elements = make([]element, 0, align(10, uintptr(len(m))))
633 }
634 for key, raw := range m {
635 s.elements = append(s.elements, element{key: key, raw: raw})
636 }
637 sort.Sort(s)
638
639 var start = len(b)
640 var err error
641 b = e.clrs.appendPunc(b, '{')
642
643 if len(s.elements) > 0 {
644 b = e.indentr.appendByte(b, '\n')
645
646 e.indentr.push()
647
648 for i, elem := range s.elements {
649 if i != 0 {
650 b = e.clrs.appendPunc(b, ',')
651 b = e.indentr.appendByte(b, '\n')
652 }
653
654 b = e.indentr.appendIndent(b)
655
656 b, _ = e.encodeKey(b, unsafe.Pointer(&elem.key))
657 b = e.clrs.appendPunc(b, ':')
658 b = e.indentr.appendByte(b, ' ')
659
660 b, err = e.encodeRawMessage(b, unsafe.Pointer(&elem.raw))
661 if err != nil {
662 break
663 }
664 }
665 b = e.indentr.appendByte(b, '\n')
666 e.indentr.pop()
667 b = e.indentr.appendIndent(b)
668 }
669
670 for i := range s.elements {
671 s.elements[i] = element{}
672 }
673
674 s.elements = s.elements[:0]
675 mapslicePool.Put(s)
676
677 if err != nil {
678 return b[:start], err
679 }
680
681 b = e.clrs.appendPunc(b, '}')
682 return b, nil
683}
684
685func (e encoder) encodeStruct(b []byte, p unsafe.Pointer, st *structType) ([]byte, error) {
686 var start = len(b)
687 var err error
688 var k string
689 var n int
690
691 b = e.clrs.appendPunc(b, '{')
692
693 if len(st.fields) > 0 {
694 b = e.indentr.appendByte(b, '\n')
695 }
696
697 e.indentr.push()
698
699 for i := range st.fields {
700 f := &st.fields[i]
701 v := unsafe.Pointer(uintptr(p) + f.offset)
702
703 if f.omitempty && f.empty(v) {
704 continue
705 }
706
707 if n != 0 {
708 b = e.clrs.appendPunc(b, ',')
709 b = e.indentr.appendByte(b, '\n')
710 }
711
712 if (e.flags & EscapeHTML) != 0 {
713 k = f.html
714 } else {
715 k = f.json
716 }
717
718 lengthBeforeKey := len(b)
719 b = e.indentr.appendIndent(b)
720
721 if e.clrs == nil {
722 b = append(b, k...)
723 } else {
724 b = append(b, e.clrs.Key...)
725 b = append(b, k...)
726 b = append(b, ansiReset...)
727 }
728
729 b = e.clrs.appendPunc(b, ':')
730
731 b = e.indentr.appendByte(b, ' ')
732
733 if b, err = f.codec.encode(e, b, v); err != nil {
734 if err == (rollback{}) {
735 b = b[:lengthBeforeKey]
736 continue
737 }
738 return b[:start], err
739 }
740
741 n++
742 }
743
744 if n > 0 {
745 b = e.indentr.appendByte(b, '\n')
746 }
747
748 e.indentr.pop()
749 b = e.indentr.appendIndent(b)
750
751 b = e.clrs.appendPunc(b, '}')
752 return b, nil
753}
754
755type rollback struct{}
756
757func (rollback) Error() string { return "rollback" }
758
759func (e encoder) encodeEmbeddedStructPointer(b []byte, p unsafe.Pointer, t reflect.Type, unexported bool, offset uintptr, encode encodeFunc) ([]byte, error) {
760 p = *(*unsafe.Pointer)(p)
761 if p == nil {
762 return b, rollback{}
763 }
764 return encode(e, b, unsafe.Pointer(uintptr(p)+offset))
765}
766
767func (e encoder) encodePointer(b []byte, p unsafe.Pointer, t reflect.Type, encode encodeFunc) ([]byte, error) {
768 if p = *(*unsafe.Pointer)(p); p != nil {
769 return encode(e, b, p)
770 }
771 return e.encodeNull(b, nil)
772}
773
774func (e encoder) encodeInterface(b []byte, p unsafe.Pointer) ([]byte, error) {
775 return Append(b, *(*interface{})(p), e.flags, e.clrs, e.indentr)
776}
777
778func (e encoder) encodeMaybeEmptyInterface(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {
779 return Append(b, reflect.NewAt(t, p).Elem().Interface(), e.flags, e.clrs, e.indentr)
780}
781
782func (e encoder) encodeUnsupportedTypeError(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {
783 return b, &UnsupportedTypeError{Type: t}
784}
785
786// encodeRawMessage encodes a RawMessage to bytes. Unfortunately, this
787// implementation has a deficiency: it uses Unmarshal to build an
788// object from the RawMessage, which in the case of a struct, results
789// in a map being constructed, and thus the order of the keys is not
790// guaranteed to be maintained. A superior implementation would decode and
791// then re-encode (with color/indentation) the basic JSON tokens on the fly.
792// Note also that if TrustRawMessage is set, and the RawMessage is
793// invalid JSON (cannot be parsed by Unmarshal), then this function
794// falls back to encodeRawMessageNoParseTrusted, which seems to exhibit the
795// correct behavior. It's a bit of a mess, but seems to do the trick.
796func (e encoder) encodeRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
797 v := *(*RawMessage)(p)
798
799 if v == nil {
800 return e.clrs.appendNull(b), nil
801 }
802
803 var s []byte
804
805 if (e.flags & TrustRawMessage) != 0 {
806 s = v
807 } else {
808 var err error
809 s, _, err = parseValue(v)
810 if err != nil {
811 return b, &UnsupportedValueError{Value: reflect.ValueOf(v), Str: err.Error()}
812 }
813 }
814
815 var x interface{}
816 if err := Unmarshal(s, &x); err != nil {
817 return e.encodeRawMessageNoParseTrusted(b, p)
818 }
819
820 return Append(b, x, e.flags, e.clrs, e.indentr)
821}
822
823// encodeRawMessageNoParseTrusted is a fallback method that is
824// used by encodeRawMessage if it fails to parse a trusted RawMessage.
825// The (invalid) JSON produced by this method is not colorized.
826// This method may have wonky logic or even bugs in it; little effort
827// has been expended on it because it's a rarely visited edge case.
828func (e encoder) encodeRawMessageNoParseTrusted(b []byte, p unsafe.Pointer) ([]byte, error) {
829 v := *(*RawMessage)(p)
830
831 if v == nil {
832 return e.clrs.appendNull(b), nil
833 }
834
835 var s []byte
836
837 if (e.flags & TrustRawMessage) != 0 {
838 s = v
839 } else {
840 var err error
841 s, _, err = parseValue(v)
842 if err != nil {
843 return b, &UnsupportedValueError{Value: reflect.ValueOf(v), Str: err.Error()}
844 }
845 }
846
847 if e.indentr == nil {
848 if (e.flags & EscapeHTML) != 0 {
849 return appendCompactEscapeHTML(b, s), nil
850 }
851
852 return append(b, s...), nil
853 }
854
855 // In order to get the tests inherited from the original segmentio
856 // encoder to work, we need to support indentation.
857
858 // This below is sloppy, but seems to work.
859 if (e.flags & EscapeHTML) != 0 {
860 s = appendCompactEscapeHTML(nil, s)
861 }
862
863 // The "prefix" arg to Indent is the current indentation.
864 pre := e.indentr.appendIndent(nil)
865
866 buf := &bytes.Buffer{}
867 // And now we just make use of the existing Indent function.
868 err := Indent(buf, s, string(pre), e.indentr.indent)
869 if err != nil {
870 return b, err
871 }
872
873 s = buf.Bytes()
874
875 return append(b, s...), nil
876}
877
878
879// encodeJSONMarshaler suffers from the same defect as encodeRawMessage; it
880// can result in keys being reordered.
881func (e encoder) encodeJSONMarshaler(b []byte, p unsafe.Pointer, t reflect.Type, pointer bool) ([]byte, error) {
882 v := reflect.NewAt(t, p)
883
884 if !pointer {
885 v = v.Elem()
886 }
887
888 switch v.Kind() {
889 case reflect.Ptr, reflect.Interface:
890 if v.IsNil() {
891 return e.clrs.appendNull(b), nil
892 }
893 }
894
895 j, err := v.Interface().(Marshaler).MarshalJSON()
896 if err != nil {
897 return b, err
898 }
899
900 // We effectively delegate to the encodeRawMessage method.
901 return Append(b, RawMessage(j), e.flags, e.clrs, e.indentr)
902}
903
904func (e encoder) encodeTextMarshaler(b []byte, p unsafe.Pointer, t reflect.Type, pointer bool) ([]byte, error) {
905 v := reflect.NewAt(t, p)
906
907 if !pointer {
908 v = v.Elem()
909 }
910
911 switch v.Kind() {
912 case reflect.Ptr, reflect.Interface:
913 if v.IsNil() {
914 return e.clrs.appendNull(b), nil
915 }
916 }
917
918 s, err := v.Interface().(encoding.TextMarshaler).MarshalText()
919 if err != nil {
920 return b, err
921 }
922
923 if e.clrs == nil {
924 return e.doEncodeString(b, unsafe.Pointer(&s))
925 }
926
927 b = append(b, e.clrs.TextMarshaler...)
928 b, err = e.doEncodeString(b, unsafe.Pointer(&s))
929 b = append(b, ansiReset...)
930 return b, err
931}
932
933func appendCompactEscapeHTML(dst []byte, src []byte) []byte {
934 start := 0
935 escape := false
936 inString := false
937
938 for i, c := range src {
939 if !inString {
940 switch c {
941 case '"': // enter string
942 inString = true
943 case ' ', '\n', '\r', '\t': // skip space
944 if start < i {
945 dst = append(dst, src[start:i]...)
946 }
947 start = i + 1
948 }
949 continue
950 }
951
952 if escape {
953 escape = false
954 continue
955 }
956
957 if c == '\\' {
958 escape = true
959 continue
960 }
961
962 if c == '"' {
963 inString = false
964 continue
965 }
966
967 if c == '<' || c == '>' || c == '&' {
968 if start < i {
969 dst = append(dst, src[start:i]...)
970 }
971 dst = append(dst, `\u00`...)
972 dst = append(dst, hex[c>>4], hex[c&0xF])
973 start = i + 1
974 continue
975 }
976
977 // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
978 if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
979 if start < i {
980 dst = append(dst, src[start:i]...)
981 }
982 dst = append(dst, `\u202`...)
983 dst = append(dst, hex[src[i+2]&0xF])
984 start = i + 3
985 continue
986 }
987 }
988
989 if start < len(src) {
990 dst = append(dst, src[start:]...)
991 }
992
993 return dst
994}
995
996// indenter is used to indent JSON. The push and pop methods
997// change indentation level. The appendIndent method appends the
998// computed indentation. The appendByte method appends a byte. All
999// methods are safe to use with a nil receiver.
1000type indenter struct {
1001 disabled bool
1002 prefix string
1003 indent string
1004 depth int
1005}
1006
1007// newIndenter returns a new indenter instance. If prefix and
1008// indent are both empty, the indenter is effectively disabled,
1009// and the appendIndent and appendByte methods are no-op.
1010func newIndenter(prefix, indent string) *indenter {
1011 return &indenter{
1012 disabled: prefix == "" && indent == "",
1013 prefix: prefix,
1014 indent: indent,
1015 }
1016}
1017
1018// push increases the indentation level.
1019func (in *indenter) push() {
1020 if in != nil {
1021 in.depth++
1022 }
1023}
1024
1025// pop decreases the indentation level.
1026func (in *indenter) pop() {
1027 if in != nil {
1028 in.depth--
1029 }
1030}
1031
1032// appendByte appends a to b if the indenter is non-nil and enabled.
1033// Otherwise b is returned unmodified.
1034func (in *indenter) appendByte(b []byte, a byte) []byte {
1035 if in == nil || in.disabled {
1036 return b
1037 }
1038
1039 return append(b, a)
1040}
1041
1042// appendIndent writes indentation to b, returning the resulting slice.
1043// If the indenter is nil or disabled b is returned unchanged.
1044func (in *indenter) appendIndent(b []byte) []byte {
1045 if in == nil || in.disabled {
1046 return b
1047 }
1048
1049 b = append(b, in.prefix...)
1050 for i := 0; i < in.depth; i++ {
1051 b = append(b, in.indent...)
1052 }
1053 return b
1054}
diff --git a/vendor/github.com/neilotoole/jsoncolor/json.go b/vendor/github.com/neilotoole/jsoncolor/json.go
new file mode 100644
index 0000000..3dc5b46
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/json.go
@@ -0,0 +1,459 @@
1package jsoncolor
2
3import (
4 "bytes"
5 "encoding/json"
6 "io"
7 "reflect"
8 "runtime"
9 "sync"
10 "unsafe"
11)
12
13// Delim is documented at https://golang.org/pkg/encoding/json/#Delim
14type Delim = json.Delim
15
16// InvalidUTF8Error is documented at https://golang.org/pkg/encoding/json/#InvalidUTF8Error
17type InvalidUTF8Error = json.InvalidUTF8Error
18
19// InvalidUnmarshalError is documented at https://golang.org/pkg/encoding/json/#InvalidUnmarshalError
20type InvalidUnmarshalError = json.InvalidUnmarshalError
21
22// Marshaler is documented at https://golang.org/pkg/encoding/json/#Marshaler
23type Marshaler = json.Marshaler
24
25// MarshalerError is documented at https://golang.org/pkg/encoding/json/#MarshalerError
26type MarshalerError = json.MarshalerError
27
28// Number is documented at https://golang.org/pkg/encoding/json/#Number
29type Number = json.Number
30
31// RawMessage is documented at https://golang.org/pkg/encoding/json/#RawMessage
32type RawMessage = json.RawMessage
33
34// A SyntaxError is a description of a JSON syntax error.
35type SyntaxError = json.SyntaxError
36
37// Token is documented at https://golang.org/pkg/encoding/json/#Token
38type Token = json.Token
39
40// UnmarshalFieldError is documented at https://golang.org/pkg/encoding/json/#UnmarshalFieldError
41type UnmarshalFieldError = json.UnmarshalFieldError
42
43// UnmarshalTypeError is documented at https://golang.org/pkg/encoding/json/#UnmarshalTypeError
44type UnmarshalTypeError = json.UnmarshalTypeError
45
46// Unmarshaler is documented at https://golang.org/pkg/encoding/json/#Unmarshaler
47type Unmarshaler = json.Unmarshaler
48
49// UnsupportedTypeError is documented at https://golang.org/pkg/encoding/json/#UnsupportedTypeError
50type UnsupportedTypeError = json.UnsupportedTypeError
51
52// UnsupportedValueError is documented at https://golang.org/pkg/encoding/json/#UnsupportedValueError
53type UnsupportedValueError = json.UnsupportedValueError
54
55// AppendFlags is a type used to represent configuration options that can be
56// applied when formatting json output.
57type AppendFlags int
58
59const (
60 // EscapeHTML is a formatting flag used to to escape HTML in json strings.
61 EscapeHTML AppendFlags = 1 << iota
62
63 // SortMapKeys is formatting flag used to enable sorting of map keys when
64 // encoding JSON (this matches the behavior of the standard encoding/json
65 // package).
66 SortMapKeys
67
68 // TrustRawMessage is a performance optimization flag to skip value
69 // checking of raw messages. It should only be used if the values are
70 // known to be valid json (e.g., they were created by json.Unmarshal).
71 TrustRawMessage
72)
73
74// ParseFlags is a type used to represent configuration options that can be
75// applied when parsing json input.
76type ParseFlags int
77
78const (
79 // DisallowUnknownFields is a parsing flag used to prevent decoding of
80 // objects to Go struct values when a field of the input does not match
81 // with any of the struct fields.
82 DisallowUnknownFields ParseFlags = 1 << iota
83
84 // UseNumber is a parsing flag used to load numeric values as Number
85 // instead of float64.
86 UseNumber
87
88 // DontCopyString is a parsing flag used to provide zero-copy support when
89 // loading string values from a json payload. It is not always possible to
90 // avoid dynamic memory allocations, for example when a string is escaped in
91 // the json data a new buffer has to be allocated, but when the `wire` value
92 // can be used as content of a Go value the decoder will simply point into
93 // the input buffer.
94 DontCopyString
95
96 // DontCopyNumber is a parsing flag used to provide zero-copy support when
97 // loading Number values (see DontCopyString and DontCopyRawMessage).
98 DontCopyNumber
99
100 // DontCopyRawMessage is a parsing flag used to provide zero-copy support
101 // when loading RawMessage values from a json payload. When used, the
102 // RawMessage values will not be allocated into new memory buffers and
103 // will instead point directly to the area of the input buffer where the
104 // value was found.
105 DontCopyRawMessage
106
107 // DontMatchCaseInsensitiveStructFields is a parsing flag used to prevent
108 // matching fields in a case-insensitive way. This can prevent degrading
109 // performance on case conversions, and can also act as a stricter decoding
110 // mode.
111 DontMatchCaseInsensitiveStructFields
112
113 // ZeroCopy is a parsing flag that combines all the copy optimizations
114 // available in the package.
115 //
116 // The zero-copy optimizations are better used in request-handler style
117 // code where none of the values are retained after the handler returns.
118 ZeroCopy = DontCopyString | DontCopyNumber | DontCopyRawMessage
119)
120
121// Append acts like Marshal but appends the json representation to b instead of
122// always reallocating a new slice.
123func Append(b []byte, x interface{}, flags AppendFlags, clrs *Colors, indentr *indenter) ([]byte, error) {
124 if x == nil {
125 // Special case for nil values because it makes the rest of the code
126 // simpler to assume that it won't be seeing nil pointers.
127 return clrs.appendNull(b), nil
128 }
129
130 t := reflect.TypeOf(x)
131 p := (*iface)(unsafe.Pointer(&x)).ptr
132
133 cache := cacheLoad()
134 c, found := cache[typeid(t)]
135
136 if !found {
137 c = constructCachedCodec(t, cache)
138 }
139
140 b, err := c.encode(encoder{flags: flags, clrs: clrs, indentr: indentr}, b, p)
141 runtime.KeepAlive(x)
142 return b, err
143}
144
145// Compact is documented at https://golang.org/pkg/encoding/json/#Compact
146func Compact(dst *bytes.Buffer, src []byte) error {
147 return json.Compact(dst, src)
148}
149
150// HTMLEscape is documented at https://golang.org/pkg/encoding/json/#HTMLEscape
151func HTMLEscape(dst *bytes.Buffer, src []byte) {
152 json.HTMLEscape(dst, src)
153}
154
155// Indent is documented at https://golang.org/pkg/encoding/json/#Indent
156func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
157 return json.Indent(dst, src, prefix, indent)
158}
159
160// Marshal is documented at https://golang.org/pkg/encoding/json/#Marshal
161func Marshal(x interface{}) ([]byte, error) {
162 var err error
163 var buf = encoderBufferPool.Get().(*encoderBuffer)
164
165 if buf.data, err = Append(buf.data[:0], x, EscapeHTML|SortMapKeys, nil, nil); err != nil {
166 return nil, err
167 }
168
169 b := make([]byte, len(buf.data))
170 copy(b, buf.data)
171 encoderBufferPool.Put(buf)
172 return b, nil
173}
174
175// MarshalIndent is documented at https://golang.org/pkg/encoding/json/#MarshalIndent
176func MarshalIndent(x interface{}, prefix, indent string) ([]byte, error) {
177 b, err := Marshal(x)
178
179 if err == nil {
180 tmp := &bytes.Buffer{}
181 tmp.Grow(2 * len(b))
182
183 if err = Indent(tmp, b, prefix, indent); err != nil {
184 return b, err
185 }
186
187 b = tmp.Bytes()
188 }
189
190 return b, err
191}
192
193// Unmarshal is documented at https://golang.org/pkg/encoding/json/#Unmarshal
194func Unmarshal(b []byte, x interface{}) error {
195 r, err := Parse(b, x, 0)
196 if len(r) != 0 {
197 if _, ok := err.(*SyntaxError); !ok {
198 // The encoding/json package prioritizes reporting errors caused by
199 // unexpected trailing bytes over other issues; here we emulate this
200 // behavior by overriding the error.
201 err = syntaxError(r, "invalid character '%c' after top-level value", r[0])
202 }
203 }
204 return err
205}
206
207// Parse behaves like Unmarshal but the caller can pass a set of flags to
208// configure the parsing behavior.
209func Parse(b []byte, x interface{}, flags ParseFlags) ([]byte, error) {
210 t := reflect.TypeOf(x)
211 p := (*iface)(unsafe.Pointer(&x)).ptr
212
213 if t == nil || p == nil || t.Kind() != reflect.Ptr {
214 _, r, err := parseValue(skipSpaces(b))
215 r = skipSpaces(r)
216 if err != nil {
217 return r, err
218 }
219 return r, &InvalidUnmarshalError{Type: t}
220 }
221 t = t.Elem()
222
223 cache := cacheLoad()
224 c, found := cache[typeid(t)]
225
226 if !found {
227 c = constructCachedCodec(t, cache)
228 }
229
230 r, err := c.decode(decoder{flags: flags}, skipSpaces(b), p)
231 return skipSpaces(r), err
232}
233
234// Valid is documented at https://golang.org/pkg/encoding/json/#Valid
235func Valid(data []byte) bool {
236 _, data, err := parseValue(skipSpaces(data))
237 if err != nil {
238 return false
239 }
240 return len(skipSpaces(data)) == 0
241}
242
243// Decoder is documented at https://golang.org/pkg/encoding/json/#Decoder
244type Decoder struct {
245 reader io.Reader
246 buffer []byte
247 remain []byte
248 inputOffset int64
249 err error
250 flags ParseFlags
251}
252
253// NewDecoder is documented at https://golang.org/pkg/encoding/json/#NewDecoder
254func NewDecoder(r io.Reader) *Decoder { return &Decoder{reader: r} }
255
256// Buffered is documented at https://golang.org/pkg/encoding/json/#Decoder.Buffered
257func (dec *Decoder) Buffered() io.Reader {
258 return bytes.NewReader(dec.remain)
259}
260
261// Decode is documented at https://golang.org/pkg/encoding/json/#Decoder.Decode
262func (dec *Decoder) Decode(v interface{}) error {
263 raw, err := dec.readValue()
264 if err != nil {
265 return err
266 }
267 _, err = Parse(raw, v, dec.flags)
268 return err
269}
270
271const (
272 minBufferSize = 32768
273 minReadSize = 4096
274)
275
276// readValue reads one JSON value from the buffer and returns its raw bytes. It
277// is optimized for the "one JSON value per line" case.
278func (dec *Decoder) readValue() (v []byte, err error) {
279 var n int
280 var r []byte
281
282 for {
283 if len(dec.remain) != 0 {
284 v, r, err = parseValue(dec.remain)
285 if err == nil {
286 dec.remain, n = skipSpacesN(r)
287 dec.inputOffset += int64(len(v) + n)
288 return
289 }
290 if len(r) != 0 {
291 // Parsing of the next JSON value stopped at a position other
292 // than the end of the input buffer, which indicaates that a
293 // syntax error was encountered.
294 return
295 }
296 }
297
298 if err = dec.err; err != nil {
299 if len(dec.remain) != 0 && err == io.EOF {
300 err = io.ErrUnexpectedEOF
301 }
302 return
303 }
304
305 if dec.buffer == nil {
306 dec.buffer = make([]byte, 0, minBufferSize)
307 } else {
308 dec.buffer = dec.buffer[:copy(dec.buffer[:cap(dec.buffer)], dec.remain)]
309 dec.remain = nil
310 }
311
312 if (cap(dec.buffer) - len(dec.buffer)) < minReadSize {
313 buf := make([]byte, len(dec.buffer), 2*cap(dec.buffer))
314 copy(buf, dec.buffer)
315 dec.buffer = buf
316 }
317
318 n, err = io.ReadFull(dec.reader, dec.buffer[len(dec.buffer):cap(dec.buffer)])
319 if n > 0 {
320 dec.buffer = dec.buffer[:len(dec.buffer)+n]
321 if err != nil {
322 err = nil
323 }
324 } else if err == io.ErrUnexpectedEOF {
325 err = io.EOF
326 }
327 dec.remain, n = skipSpacesN(dec.buffer)
328 dec.inputOffset += int64(n)
329 dec.err = err
330 }
331}
332
333// DisallowUnknownFields is documented at https://golang.org/pkg/encoding/json/#Decoder.DisallowUnknownFields
334func (dec *Decoder) DisallowUnknownFields() { dec.flags |= DisallowUnknownFields }
335
336// UseNumber is documented at https://golang.org/pkg/encoding/json/#Decoder.UseNumber
337func (dec *Decoder) UseNumber() { dec.flags |= UseNumber }
338
339// DontCopyString is an extension to the standard encoding/json package
340// which instructs the decoder to not copy strings loaded from the json
341// payloads when possible.
342func (dec *Decoder) DontCopyString() { dec.flags |= DontCopyString }
343
344// DontCopyNumber is an extension to the standard encoding/json package
345// which instructs the decoder to not copy numbers loaded from the json
346// payloads.
347func (dec *Decoder) DontCopyNumber() { dec.flags |= DontCopyNumber }
348
349// DontCopyRawMessage is an extension to the standard encoding/json package
350// which instructs the decoder to not allocate RawMessage values in separate
351// memory buffers (see the documentation of the DontcopyRawMessage flag for
352// more detais).
353func (dec *Decoder) DontCopyRawMessage() { dec.flags |= DontCopyRawMessage }
354
355// DontMatchCaseInsensitiveStructFields is an extension to the standard
356// encoding/json package which instructs the decoder to not match object fields
357// against struct fields in a case-insensitive way, the field names have to
358// match exactly to be decoded into the struct field values.
359func (dec *Decoder) DontMatchCaseInsensitiveStructFields() {
360 dec.flags |= DontMatchCaseInsensitiveStructFields
361}
362
363// ZeroCopy is an extension to the standard encoding/json package which enables
364// all the copy optimizations of the decoder.
365func (dec *Decoder) ZeroCopy() { dec.flags |= ZeroCopy }
366
367// InputOffset returns the input stream byte offset of the current decoder position.
368// The offset gives the location of the end of the most recently returned token
369// and the beginning of the next token.
370func (dec *Decoder) InputOffset() int64 {
371 return dec.inputOffset
372}
373
374// Encoder is documented at https://golang.org/pkg/encoding/json/#Encoder
375type Encoder struct {
376 writer io.Writer
377 buffer *bytes.Buffer
378 err error
379 flags AppendFlags
380 clrs *Colors
381 indentr *indenter
382}
383
384// NewEncoder is documented at https://golang.org/pkg/encoding/json/#NewEncoder
385func NewEncoder(w io.Writer) *Encoder { return &Encoder{writer: w, flags: EscapeHTML | SortMapKeys} }
386
387// SetColors sets the colors for the encoder to use.
388func (enc *Encoder) SetColors(c *Colors) {
389 enc.clrs = c
390}
391
392// Encode is documented at https://golang.org/pkg/encoding/json/#Encoder.Encode
393func (enc *Encoder) Encode(v interface{}) error {
394 if enc.err != nil {
395 return enc.err
396 }
397
398 var err error
399 var buf = encoderBufferPool.Get().(*encoderBuffer)
400
401 // Note: unlike the original segmentio encoder, indentation is
402 // performed via the Append function.
403 buf.data, err = Append(buf.data[:0], v, enc.flags, enc.clrs, enc.indentr)
404 if err != nil {
405 encoderBufferPool.Put(buf)
406 return err
407 }
408
409 buf.data = append(buf.data, '\n')
410 b := buf.data
411
412 if _, err := enc.writer.Write(b); err != nil {
413 enc.err = err
414 }
415
416 encoderBufferPool.Put(buf)
417 return err
418}
419
420// SetEscapeHTML is documented at https://golang.org/pkg/encoding/json/#Encoder.SetEscapeHTML
421func (enc *Encoder) SetEscapeHTML(on bool) {
422 if on {
423 enc.flags |= EscapeHTML
424 } else {
425 enc.flags &= ^EscapeHTML
426 }
427}
428
429// SetIndent is documented at https://golang.org/pkg/encoding/json/#Encoder.SetIndent
430func (enc *Encoder) SetIndent(prefix, indent string) {
431 enc.indentr = newIndenter(prefix, indent)
432}
433
434// SetSortMapKeys is an extension to the standard encoding/json package which
435// allows the program to toggle sorting of map keys on and off.
436func (enc *Encoder) SetSortMapKeys(on bool) {
437 if on {
438 enc.flags |= SortMapKeys
439 } else {
440 enc.flags &= ^SortMapKeys
441 }
442}
443
444// SetTrustRawMessage skips value checking when encoding a raw json message. It should only
445// be used if the values are known to be valid json, e.g. because they were originally created
446// by json.Unmarshal.
447func (enc *Encoder) SetTrustRawMessage(on bool) {
448 if on {
449 enc.flags |= TrustRawMessage
450 } else {
451 enc.flags &= ^TrustRawMessage
452 }
453}
454
455var encoderBufferPool = sync.Pool{
456 New: func() interface{} { return &encoderBuffer{data: make([]byte, 0, 4096)} },
457}
458
459type encoderBuffer struct{ data []byte }
diff --git a/vendor/github.com/neilotoole/jsoncolor/jsoncolor.go b/vendor/github.com/neilotoole/jsoncolor/jsoncolor.go
new file mode 100644
index 0000000..4c8835c
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/jsoncolor.go
@@ -0,0 +1,141 @@
1package jsoncolor
2
3import (
4 "strconv"
5)
6
7// Colors specifies colorization of JSON output. Each field
8// is a Color, which is simply the bytes of the terminal color code.
9type Colors struct {
10 // Null is the color for JSON nil.
11 Null Color
12
13 // Bool is the color for boolean values.
14 Bool Color
15
16 // Number is the color for number values.
17 Number Color
18
19 // String is the color for string values.
20 String Color
21
22 // Key is the color for JSON keys.
23 Key Color
24
25 // Bytes is the color for byte data.
26 Bytes Color
27
28 // Time is the color for datetime values.
29 Time Color
30
31 // Punc is the color for JSON punctuation: []{},: etc.
32 Punc Color
33
34 // TextMarshaler is the color for values implementing encoding.TextMarshaler.
35 TextMarshaler Color
36}
37
38// appendNull appends a colorized "null" to b.
39func (c *Colors) appendNull(b []byte) []byte {
40 if c == nil {
41 return append(b, "null"...)
42 }
43
44 b = append(b, c.Null...)
45 b = append(b, "null"...)
46 return append(b, ansiReset...)
47}
48
49// appendBool appends the colorized bool v to b.
50func (c *Colors) appendBool(b []byte, v bool) []byte {
51 if c == nil {
52 if v {
53 return append(b, "true"...)
54 }
55
56 return append(b, "false"...)
57 }
58
59 b = append(b, c.Bool...)
60 if v {
61 b = append(b, "true"...)
62 } else {
63 b = append(b, "false"...)
64 }
65
66 return append(b, ansiReset...)
67}
68
69// appendKey appends the colorized key v to b.
70func (c *Colors) appendKey(b []byte, v []byte) []byte {
71 if c == nil {
72 return append(b, v...)
73 }
74
75 b = append(b, c.Key...)
76 b = append(b, v...)
77 return append(b, ansiReset...)
78}
79
80// appendInt64 appends the colorized int64 v to b.
81func (c *Colors) appendInt64(b []byte, v int64) []byte {
82 if c == nil {
83 return strconv.AppendInt(b, v, 10)
84 }
85
86 b = append(b, c.Number...)
87 b = strconv.AppendInt(b, v, 10)
88 return append(b, ansiReset...)
89}
90
91// appendUint64 appends the colorized uint64 v to b.
92func (c *Colors) appendUint64(b []byte, v uint64) []byte {
93 if c == nil {
94 return strconv.AppendUint(b, v, 10)
95 }
96
97 b = append(b, c.Number...)
98 b = strconv.AppendUint(b, v, 10)
99 return append(b, ansiReset...)
100}
101
102// appendPunc appends the colorized punctuation mark v to b.
103func (c *Colors) appendPunc(b []byte, v byte) []byte {
104 if c == nil {
105 return append(b, v)
106 }
107
108 b = append(b, c.Punc...)
109 b = append(b, v)
110 return append(b, ansiReset...)
111}
112
113// Color is used to render terminal colors. In effect, Color is
114// the bytes of the ANSI prefix code. The zero value is valid (results in
115// no colorization). When Color is non-zero, the encoder writes the prefix,
116// then the actual value, then the ANSI reset code.
117//
118// Example value:
119//
120// number := Color("\x1b[36m")
121type Color []byte
122
123// ansiReset is the ANSI ansiReset escape code.
124const ansiReset = "\x1b[0m"
125
126// DefaultColors returns the default Colors configuration.
127// These colors largely follow jq's default colorization,
128// with some deviation.
129func DefaultColors() *Colors {
130 return &Colors{
131 Null: Color("\x1b[2m"),
132 Bool: Color("\x1b[1m"),
133 Number: Color("\x1b[36m"),
134 String: Color("\x1b[32m"),
135 Key: Color("\x1b[34;1m"),
136 Bytes: Color("\x1b[2m"),
137 Time: Color("\x1b[32;2m"),
138 Punc: Color{}, // No colorization
139 TextMarshaler: Color("\x1b[32m"), // Same as String
140 }
141}
diff --git a/vendor/github.com/neilotoole/jsoncolor/parse.go b/vendor/github.com/neilotoole/jsoncolor/parse.go
new file mode 100644
index 0000000..0f43b4c
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/parse.go
@@ -0,0 +1,735 @@
1package jsoncolor
2
3import (
4 "bytes"
5 "math"
6 "reflect"
7 "unicode"
8 "unicode/utf16"
9 "unicode/utf8"
10)
11
12// All spaces characters defined in the json specification.
13const (
14 sp = ' '
15 ht = '\t'
16 nl = '\n'
17 cr = '\r'
18)
19
20const (
21 escape = '\\'
22 quote = '"' //nolint:varcheck // from original code
23)
24
25func skipSpaces(b []byte) []byte {
26 b, _ = skipSpacesN(b)
27 return b
28}
29
30func skipSpacesN(b []byte) ([]byte, int) {
31 for i := range b {
32 switch b[i] {
33 case sp, ht, nl, cr:
34 default:
35 return b[i:], i
36 }
37 }
38 return nil, 0
39}
40
41// parseInt parses a decimanl representation of an int64 from b.
42//
43// The function is equivalent to calling strconv.ParseInt(string(b), 10, 64) but
44// it prevents Go from making a memory allocation for converting a byte slice to
45// a string (escape analysis fails due to the error returned by strconv.ParseInt).
46//
47// Because it only works with base 10 the function is also significantly faster
48// than strconv.ParseInt.
49func parseInt(b []byte, t reflect.Type) (int64, []byte, error) {
50 var value int64
51 var count int
52
53 if len(b) == 0 {
54 return 0, b, syntaxError(b, "cannot decode integer from an empty input")
55 }
56
57 if b[0] == '-' {
58 const max = math.MinInt64
59 const lim = max / 10
60
61 if len(b) == 1 {
62 return 0, b, syntaxError(b, "cannot decode integer from '-'")
63 }
64
65 if len(b) > 2 && b[1] == '0' && '0' <= b[2] && b[2] <= '9' {
66 return 0, b, syntaxError(b, "invalid leading character '0' in integer")
67 }
68
69 for _, d := range b[1:] {
70 if !(d >= '0' && d <= '9') {
71 if count == 0 {
72 b, err := inputError(b, t)
73 return 0, b, err
74 }
75 break
76 }
77
78 if value < lim {
79 return 0, b, unmarshalOverflow(b, t)
80 }
81
82 value *= 10
83 x := int64(d - '0')
84
85 if value < (max + x) {
86 return 0, b, unmarshalOverflow(b, t)
87 }
88
89 value -= x
90 count++
91 }
92
93 count++
94 } else {
95 const max = math.MaxInt64
96 const lim = max / 10
97
98 if len(b) > 1 && b[0] == '0' && '0' <= b[1] && b[1] <= '9' {
99 return 0, b, syntaxError(b, "invalid leading character '0' in integer")
100 }
101
102 for _, d := range b {
103 if !(d >= '0' && d <= '9') {
104 if count == 0 {
105 b, err := inputError(b, t)
106 return 0, b, err
107 }
108 break
109 }
110 x := int64(d - '0')
111
112 if value > lim {
113 return 0, b, unmarshalOverflow(b, t)
114 }
115
116 if value *= 10; value > (max - x) {
117 return 0, b, unmarshalOverflow(b, t)
118 }
119
120 value += x
121 count++
122 }
123 }
124
125 if count < len(b) {
126 switch b[count] {
127 case '.', 'e', 'E': // was this actually a float?
128 v, r, err := parseNumber(b)
129 if err != nil {
130 v, r = b[:count+1], b[count+1:]
131 }
132 return 0, r, unmarshalTypeError(v, t)
133 }
134 }
135
136 return value, b[count:], nil
137}
138
139// parseUint is like parseInt but for unsigned integers.
140func parseUint(b []byte, t reflect.Type) (uint64, []byte, error) {
141 const max = math.MaxUint64
142 const lim = max / 10
143
144 var value uint64
145 var count int
146
147 if len(b) == 0 {
148 return 0, b, syntaxError(b, "cannot decode integer value from an empty input")
149 }
150
151 if len(b) > 1 && b[0] == '0' && '0' <= b[1] && b[1] <= '9' {
152 return 0, b, syntaxError(b, "invalid leading character '0' in integer")
153 }
154
155 for _, d := range b {
156 if !(d >= '0' && d <= '9') {
157 if count == 0 {
158 b, err := inputError(b, t)
159 return 0, b, err
160 }
161 break
162 }
163 x := uint64(d - '0')
164
165 if value > lim {
166 return 0, b, unmarshalOverflow(b, t)
167 }
168
169 if value *= 10; value > (max - x) {
170 return 0, b, unmarshalOverflow(b, t)
171 }
172
173 value += x
174 count++
175 }
176
177 if count < len(b) {
178 switch b[count] {
179 case '.', 'e', 'E': // was this actually a float?
180 v, r, err := parseNumber(b)
181 if err != nil {
182 v, r = b[:count+1], b[count+1:]
183 }
184 return 0, r, unmarshalTypeError(v, t)
185 }
186 }
187
188 return value, b[count:], nil
189}
190
191// parseUintHex parses a hexadecimanl representation of a uint64 from b.
192//
193// The function is equivalent to calling strconv.ParseUint(string(b), 16, 64) but
194// it prevents Go from making a memory allocation for converting a byte slice to
195// a string (escape analysis fails due to the error returned by strconv.ParseUint).
196//
197// Because it only works with base 16 the function is also significantly faster
198// than strconv.ParseUint.
199func parseUintHex(b []byte) (uint64, []byte, error) {
200 const max = math.MaxUint64
201 const lim = max / 0x10
202
203 var value uint64
204 var count int
205
206 if len(b) == 0 {
207 return 0, b, syntaxError(b, "cannot decode hexadecimal value from an empty input")
208 }
209
210parseLoop:
211 for i, d := range b {
212 var x uint64
213
214 switch {
215 case d >= '0' && d <= '9':
216 x = uint64(d - '0')
217
218 case d >= 'A' && d <= 'F':
219 x = uint64(d-'A') + 0xA
220
221 case d >= 'a' && d <= 'f':
222 x = uint64(d-'a') + 0xA
223
224 default:
225 if i == 0 {
226 return 0, b, syntaxError(b, "expected hexadecimal digit but found '%c'", d)
227 }
228 break parseLoop
229 }
230
231 if value > lim {
232 return 0, b, syntaxError(b, "hexadecimal value out of range")
233 }
234
235 if value *= 0x10; value > (max - x) {
236 return 0, b, syntaxError(b, "hexadecimal value out of range")
237 }
238
239 value += x
240 count++
241 }
242
243 return value, b[count:], nil
244}
245
246func parseNull(b []byte) ([]byte, []byte, error) {
247 if hasNullPrefix(b) {
248 return b[:4], b[4:], nil
249 }
250 if len(b) < 4 {
251 return nil, b[len(b):], unexpectedEOF(b)
252 }
253 return nil, b, syntaxError(b, "expected 'null' but found invalid token")
254}
255
256func parseTrue(b []byte) ([]byte, []byte, error) {
257 if hasTruePrefix(b) {
258 return b[:4], b[4:], nil
259 }
260 if len(b) < 4 {
261 return nil, b[len(b):], unexpectedEOF(b)
262 }
263 return nil, b, syntaxError(b, "expected 'true' but found invalid token")
264}
265
266func parseFalse(b []byte) ([]byte, []byte, error) {
267 if hasFalsePrefix(b) {
268 return b[:5], b[5:], nil
269 }
270 if len(b) < 5 {
271 return nil, b[len(b):], unexpectedEOF(b)
272 }
273 return nil, b, syntaxError(b, "expected 'false' but found invalid token")
274}
275
276func parseNumber(b []byte) (v, r []byte, err error) {
277 if len(b) == 0 {
278 r, err = b, unexpectedEOF(b)
279 return
280 }
281
282 i := 0
283 // sign
284 if b[i] == '-' {
285 i++
286 }
287
288 if i == len(b) {
289 r, err = b[i:], syntaxError(b, "missing number value after sign")
290 return
291 }
292
293 if b[i] < '0' || b[i] > '9' {
294 r, err = b[i:], syntaxError(b, "expected digit but got '%c'", b[i])
295 return
296 }
297
298 // integer part
299 if b[i] == '0' {
300 i++
301 if i == len(b) || (b[i] != '.' && b[i] != 'e' && b[i] != 'E') {
302 v, r = b[:i], b[i:]
303 return
304 }
305 if '0' <= b[i] && b[i] <= '9' {
306 r, err = b[i:], syntaxError(b, "cannot decode number with leading '0' character")
307 return
308 }
309 }
310
311 for i < len(b) && '0' <= b[i] && b[i] <= '9' {
312 i++
313 }
314
315 // decimal part
316 if i < len(b) && b[i] == '.' {
317 i++
318 decimalStart := i
319
320 for i < len(b) {
321 if c := b[i]; !('0' <= c && c <= '9') {
322 if i == decimalStart {
323 r, err = b[i:], syntaxError(b, "expected digit but found '%c'", c)
324 return
325 }
326 break
327 }
328 i++
329 }
330
331 if i == decimalStart {
332 r, err = b[i:], syntaxError(b, "expected decimal part after '.'")
333 return
334 }
335 }
336
337 // exponent part
338 if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
339 i++
340
341 if i < len(b) {
342 if c := b[i]; c == '+' || c == '-' {
343 i++
344 }
345 }
346
347 if i == len(b) {
348 r, err = b[i:], syntaxError(b, "missing exponent in number")
349 return
350 }
351
352 exponentStart := i
353
354 for i < len(b) {
355 if c := b[i]; !('0' <= c && c <= '9') {
356 if i == exponentStart {
357 err = syntaxError(b, "expected digit but found '%c'", c)
358 return
359 }
360 break
361 }
362 i++
363 }
364 }
365
366 v, r = b[:i], b[i:]
367 return
368}
369
370func parseUnicode(b []byte) (rune, int, error) {
371 if len(b) < 4 {
372 return 0, 0, syntaxError(b, "unicode code point must have at least 4 characters")
373 }
374
375 u, r, err := parseUintHex(b[:4])
376 if err != nil {
377 return 0, 0, syntaxError(b, "parsing unicode code point: %s", err)
378 }
379
380 if len(r) != 0 {
381 return 0, 0, syntaxError(b, "invalid unicode code point")
382 }
383
384 return rune(u), 4, nil
385}
386
387func parseStringFast(b []byte) ([]byte, []byte, bool, error) {
388 if len(b) < 2 {
389 return nil, b[len(b):], false, unexpectedEOF(b)
390 }
391 if b[0] != '"' {
392 return nil, b, false, syntaxError(b, "expected '\"' at the beginning of a string value")
393 }
394
395 n := bytes.IndexByte(b[1:], '"') + 2
396 if n <= 1 {
397 return nil, b[len(b):], false, syntaxError(b, "missing '\"' at the end of a string value")
398 }
399 if bytes.IndexByte(b[1:n], '\\') < 0 && asciiValidPrint(b[1:n]) {
400 return b[:n], b[n:], false, nil
401 }
402
403 for i := 1; i < len(b); i++ {
404 switch b[i] {
405 case '\\':
406 if i++; i < len(b) {
407 switch b[i] {
408 case '"', '\\', '/', 'n', 'r', 't', 'f', 'b':
409 case 'u':
410 _, n, err := parseUnicode(b[i+1:])
411 if err != nil {
412 return nil, b, false, err
413 }
414 i += n
415 default:
416 return nil, b, false, syntaxError(b, "invalid character '%c' in string escape code", b[i])
417 }
418 }
419
420 case '"':
421 return b[:i+1], b[i+1:], true, nil
422
423 default:
424 if b[i] < 0x20 {
425 return nil, b, false, syntaxError(b, "invalid character '%c' in string escape code", b[i])
426 }
427 }
428 }
429
430 return nil, b[len(b):], false, syntaxError(b, "missing '\"' at the end of a string value")
431}
432
433func parseString(b []byte) ([]byte, []byte, error) {
434 s, b, _, err := parseStringFast(b)
435 return s, b, err
436}
437
438func parseStringUnquote(b []byte, r []byte) ([]byte, []byte, bool, error) {
439 s, b, escaped, err := parseStringFast(b)
440 if err != nil {
441 return s, b, false, err
442 }
443
444 s = s[1 : len(s)-1] // trim the quotes
445
446 if !escaped {
447 return s, b, false, nil
448 }
449
450 if r == nil {
451 r = make([]byte, 0, len(s))
452 }
453
454 for len(s) != 0 {
455 i := bytes.IndexByte(s, '\\')
456
457 if i < 0 {
458 r = appendCoerceInvalidUTF8(r, s)
459 break
460 }
461
462 r = appendCoerceInvalidUTF8(r, s[:i])
463 s = s[i+1:]
464
465 c := s[0]
466 switch c {
467 case '"', '\\', '/':
468 // simple escaped character
469 case 'n':
470 c = '\n'
471
472 case 'r':
473 c = '\r'
474
475 case 't':
476 c = '\t'
477
478 case 'b':
479 c = '\b'
480
481 case 'f':
482 c = '\f'
483
484 case 'u':
485 s = s[1:]
486
487 r1, n1, err := parseUnicode(s)
488 if err != nil {
489 return r, b, true, err
490 }
491 s = s[n1:]
492
493 if utf16.IsSurrogate(r1) {
494 if !hasPrefix(s, `\u`) {
495 r1 = unicode.ReplacementChar
496 } else {
497 r2, n2, err := parseUnicode(s[2:])
498 if err != nil {
499 return r, b, true, err
500 }
501 if r1 = utf16.DecodeRune(r1, r2); r1 != unicode.ReplacementChar {
502 s = s[2+n2:]
503 }
504 }
505 }
506
507 r = appendRune(r, r1)
508 continue
509
510 default: // not sure what this escape sequence is
511 return r, b, false, syntaxError(s, "invalid character '%c' in string escape code", c)
512 }
513
514 r = append(r, c)
515 s = s[1:]
516 }
517
518 return r, b, true, nil
519}
520
521func appendRune(b []byte, r rune) []byte {
522 n := len(b)
523 b = append(b, 0, 0, 0, 0)
524 return b[:n+utf8.EncodeRune(b[n:], r)]
525}
526
527func appendCoerceInvalidUTF8(b []byte, s []byte) []byte {
528 c := [4]byte{}
529
530 for _, r := range string(s) {
531 b = append(b, c[:utf8.EncodeRune(c[:], r)]...)
532 }
533
534 return b
535}
536
537func parseObject(b []byte) ([]byte, []byte, error) {
538 if len(b) < 2 {
539 return nil, b[len(b):], unexpectedEOF(b)
540 }
541
542 if b[0] != '{' {
543 return nil, b, syntaxError(b, "expected '{' at the beginning of an object value")
544 }
545
546 var err error
547 var a = b
548 var n = len(b)
549 var i = 0
550
551 b = b[1:]
552 for {
553 b = skipSpaces(b)
554
555 if len(b) == 0 {
556 return nil, b, syntaxError(b, "cannot decode object from empty input")
557 }
558
559 if b[0] == '}' {
560 j := (n - len(b)) + 1
561 return a[:j], a[j:], nil
562 }
563
564 if i != 0 {
565 if len(b) == 0 {
566 return nil, b, syntaxError(b, "unexpected EOF after object field value")
567 }
568 if b[0] != ',' {
569 return nil, b, syntaxError(b, "expected ',' after object field value but found '%c'", b[0])
570 }
571 b = skipSpaces(b[1:])
572 if len(b) == 0 {
573 return nil, b, unexpectedEOF(b)
574 }
575 if b[0] == '}' {
576 return nil, b, syntaxError(b, "unexpected trailing comma after object field")
577 }
578 }
579
580 _, b, err = parseString(b)
581 if err != nil {
582 return nil, b, err
583 }
584 b = skipSpaces(b)
585
586 if len(b) == 0 {
587 return nil, b, syntaxError(b, "unexpected EOF after object field key")
588 }
589 if b[0] != ':' {
590 return nil, b, syntaxError(b, "expected ':' after object field key but found '%c'", b[0])
591 }
592 b = skipSpaces(b[1:])
593
594 _, b, err = parseValue(b)
595 if err != nil {
596 return nil, b, err
597 }
598
599 i++
600 }
601}
602
603func parseArray(b []byte) ([]byte, []byte, error) {
604 if len(b) < 2 {
605 return nil, b[len(b):], unexpectedEOF(b)
606 }
607
608 if b[0] != '[' {
609 return nil, b, syntaxError(b, "expected '[' at the beginning of array value")
610 }
611
612 var err error
613 var a = b
614 var n = len(b)
615 var i = 0
616
617 b = b[1:]
618 for {
619 b = skipSpaces(b)
620
621 if len(b) == 0 {
622 return nil, b, syntaxError(b, "missing closing ']' after array value")
623 }
624
625 if b[0] == ']' {
626 j := (n - len(b)) + 1
627 return a[:j], a[j:], nil
628 }
629
630 if i != 0 {
631 if len(b) == 0 {
632 return nil, b, syntaxError(b, "unexpected EOF after array element")
633 }
634 if b[0] != ',' {
635 return nil, b, syntaxError(b, "expected ',' after array element but found '%c'", b[0])
636 }
637 b = skipSpaces(b[1:])
638 if len(b) == 0 {
639 return nil, b, unexpectedEOF(b)
640 }
641 if b[0] == ']' {
642 return nil, b, syntaxError(b, "unexpected trailing comma after object field")
643 }
644 }
645
646 _, b, err = parseValue(b)
647 if err != nil {
648 return nil, b, err
649 }
650
651 i++
652 }
653}
654
655func parseValue(b []byte) ([]byte, []byte, error) {
656 if len(b) != 0 {
657 switch b[0] {
658 case '{':
659 return parseObject(b)
660 case '[':
661 return parseArray(b)
662 case '"':
663 return parseString(b)
664 case 'n':
665 return parseNull(b)
666 case 't':
667 return parseTrue(b)
668 case 'f':
669 return parseFalse(b)
670 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
671 return parseNumber(b)
672 default:
673 return nil, b, syntaxError(b, "invalid character '%c' looking for beginning of value", b[0])
674 }
675 }
676 return nil, b, syntaxError(b, "unexpected end of JSON input")
677}
678
679func hasNullPrefix(b []byte) bool {
680 return len(b) >= 4 && string(b[:4]) == "null"
681}
682
683func hasTruePrefix(b []byte) bool {
684 return len(b) >= 4 && string(b[:4]) == "true"
685}
686
687func hasFalsePrefix(b []byte) bool {
688 return len(b) >= 5 && string(b[:5]) == "false"
689}
690
691func hasPrefix(b []byte, s string) bool {
692 return len(b) >= len(s) && s == string(b[:len(s)])
693}
694
695func hasLeadingSign(b []byte) bool {
696 return len(b) > 0 && (b[0] == '+' || b[0] == '-')
697}
698
699func hasLeadingZeroes(b []byte) bool {
700 if hasLeadingSign(b) {
701 b = b[1:]
702 }
703 return len(b) > 1 && b[0] == '0' && '0' <= b[1] && b[1] <= '9'
704}
705
706func appendToLower(b, s []byte) []byte {
707 if asciiValid(s) { // fast path for ascii strings
708 i := 0
709
710 for j := range s {
711 c := s[j]
712
713 if 'A' <= c && c <= 'Z' {
714 b = append(b, s[i:j]...)
715 b = append(b, c+('a'-'A'))
716 i = j + 1
717 }
718 }
719
720 return append(b, s[i:]...)
721 }
722
723 for _, r := range string(s) {
724 b = appendRune(b, foldRune(r))
725 }
726
727 return b
728}
729
730func foldRune(r rune) rune {
731 if r = unicode.SimpleFold(r); 'A' <= r && r <= 'Z' {
732 r = r + ('a' - 'A')
733 }
734 return r
735}
diff --git a/vendor/github.com/neilotoole/jsoncolor/reflect.go b/vendor/github.com/neilotoole/jsoncolor/reflect.go
new file mode 100644
index 0000000..0dcf174
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/reflect.go
@@ -0,0 +1,20 @@
1//go:build go1.15
2// +build go1.15
3
4package jsoncolor
5
6import (
7 "reflect"
8 "unsafe"
9)
10
11func extendSlice(t reflect.Type, s *slice, n int) slice {
12 arrayType := reflect.ArrayOf(n, t.Elem())
13 arrayData := reflect.New(arrayType)
14 reflect.Copy(arrayData.Elem(), reflect.NewAt(t, unsafe.Pointer(s)).Elem())
15 return slice{
16 data: unsafe.Pointer(arrayData.Pointer()),
17 len: s.len,
18 cap: n,
19 }
20}
diff --git a/vendor/github.com/neilotoole/jsoncolor/reflect_optimize.go b/vendor/github.com/neilotoole/jsoncolor/reflect_optimize.go
new file mode 100644
index 0000000..03fc849
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/reflect_optimize.go
@@ -0,0 +1,30 @@
1//go:build !go1.15
2// +build !go1.15
3
4package jsoncolor
5
6import (
7 "reflect"
8 "unsafe"
9)
10
11//go:linkname unsafe_NewArray reflect.unsafe_NewArray
12func unsafe_NewArray(rtype unsafe.Pointer, length int) unsafe.Pointer
13
14//go:linkname typedslicecopy reflect.typedslicecopy
15//go:noescape
16func typedslicecopy(elemType unsafe.Pointer, dst, src slice) int
17
18func extendSlice(t reflect.Type, s *slice, n int) slice {
19 elemTypeRef := t.Elem()
20 elemTypePtr := ((*iface)(unsafe.Pointer(&elemTypeRef))).ptr
21
22 d := slice{
23 data: unsafe_NewArray(elemTypePtr, n),
24 len: s.len,
25 cap: n,
26 }
27
28 typedslicecopy(elemTypePtr, d, *s)
29 return d
30}
diff --git a/vendor/github.com/neilotoole/jsoncolor/splash.png b/vendor/github.com/neilotoole/jsoncolor/splash.png
new file mode 100644
index 0000000..f962327
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/splash.png
Binary files differ
diff --git a/vendor/github.com/neilotoole/jsoncolor/terminal.go b/vendor/github.com/neilotoole/jsoncolor/terminal.go
new file mode 100644
index 0000000..e9398f9
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/terminal.go
@@ -0,0 +1,42 @@
1//go:build !windows
2
3package jsoncolor
4
5import (
6 "io"
7 "os"
8
9 "golang.org/x/term"
10)
11
12// IsColorTerminal returns true if w is a colorable terminal.
13// It respects [NO_COLOR], [FORCE_COLOR] and TERM=dumb environment variables.
14//
15// [NO_COLOR]: https://no-color.org/
16// [FORCE_COLOR]: https://force-color.org/
17func IsColorTerminal(w io.Writer) bool {
18 if os.Getenv("NO_COLOR") != "" {
19 return false
20 }
21 if os.Getenv("FORCE_COLOR") != "" {
22 return true
23 }
24 if os.Getenv("TERM") == "dumb" {
25 return false
26 }
27
28 if w == nil {
29 return false
30 }
31
32 f, ok := w.(*os.File)
33 if !ok {
34 return false
35 }
36
37 if !term.IsTerminal(int(f.Fd())) {
38 return false
39 }
40
41 return true
42}
diff --git a/vendor/github.com/neilotoole/jsoncolor/terminal_windows.go b/vendor/github.com/neilotoole/jsoncolor/terminal_windows.go
new file mode 100644
index 0000000..38259dc
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/terminal_windows.go
@@ -0,0 +1,53 @@
1package jsoncolor
2
3import (
4 "io"
5 "os"
6
7 "golang.org/x/sys/windows"
8)
9
10// IsColorTerminal returns true if w is a colorable terminal.
11// It respects [NO_COLOR], [FORCE_COLOR] and TERM=dumb environment variables.
12//
13// [NO_COLOR]: https://no-color.org/
14// [FORCE_COLOR]: https://force-color.org/
15func IsColorTerminal(w io.Writer) bool {
16 if os.Getenv("NO_COLOR") != "" {
17 return false
18 }
19 if os.Getenv("FORCE_COLOR") != "" {
20 return true
21 }
22 if os.Getenv("TERM") == "dumb" {
23 return false
24 }
25
26 if w == nil {
27 return false
28 }
29
30 f, ok := w.(*os.File)
31 if !ok {
32 return false
33 }
34 fd := f.Fd()
35
36 console := windows.Handle(fd)
37 var mode uint32
38 if err := windows.GetConsoleMode(console, &mode); err != nil {
39 return false
40 }
41
42 var want uint32 = windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
43 if (mode & want) == want {
44 return true
45 }
46
47 mode |= want
48 if err := windows.SetConsoleMode(console, mode); err != nil {
49 return false
50 }
51
52 return true
53}
diff --git a/vendor/github.com/neilotoole/jsoncolor/token.go b/vendor/github.com/neilotoole/jsoncolor/token.go
new file mode 100644
index 0000000..ba3277e
--- /dev/null
+++ b/vendor/github.com/neilotoole/jsoncolor/token.go
@@ -0,0 +1,286 @@
1package jsoncolor
2
3// Tokenizer is an iterator-style type which can be used to progressively parse
4// through a json input.
5//
6// Tokenizing json is useful to build highly efficient parsing operations, for
7// example when doing tranformations on-the-fly where as the program reads the
8// input and produces the transformed json to an output buffer.
9//
10// Here is a common pattern to use a tokenizer:
11//
12// for t := json.NewTokenizer(b); t.Next(); {
13// switch t.Delim {
14// case '{':
15// ...
16// case '}':
17// ...
18// case '[':
19// ...
20// case ']':
21// ...
22// case ':':
23// ...
24// case ',':
25// ...
26// }
27//
28// switch {
29// case t.Value.String():
30// ...
31// case t.Value.Null():
32// ...
33// case t.Value.True():
34// ...
35// case t.Value.False():
36// ...
37// case t.Value.Number():
38// ...
39// }
40// }
41//
42type Tokenizer struct {
43 // When the tokenizer is positioned on a json delimiter this field is not
44 // zero. In this case the possible values are '{', '}', '[', ']', ':', and
45 // ','.
46 Delim Delim
47
48 // This field contains the raw json token that the tokenizer is pointing at.
49 // When Delim is not zero, this field is a single-element byte slice
50 // continaing the delimiter value. Otherwise, this field holds values like
51 // null, true, false, numbers, or quoted strings.
52 Value RawValue
53
54 // When the tokenizer has encountered invalid content this field is not nil.
55 Err error
56
57 // When the value is in an array or an object, this field contains the depth
58 // at which it was found.
59 Depth int
60
61 // When the value is in an array or an object, this field contains the
62 // position at which it was found.
63 Index int
64
65 // This field is true when the value is the key of an object.
66 IsKey bool
67
68 // Tells whether the next value read from the tokenizer is a key.
69 isKey bool
70
71 // json input for the tokenizer, pointing at data right after the last token
72 // that was parsed.
73 json []byte
74
75 // Stack used to track entering and leaving arrays, objects, and keys. The
76 // buffer is used as a AppendPre-allocated space to
77 stack []state
78 buffer [8]state
79}
80
81type state struct {
82 typ scope
83 len int
84}
85
86type scope int
87
88const (
89 inArray scope = iota
90 inObject
91)
92
93// NewTokenizer constructs a new Tokenizer which reads its json input from b.
94func NewTokenizer(b []byte) *Tokenizer { return &Tokenizer{json: b} }
95
96// Reset erases the state of t and re-initializes it with the json input from b.
97func (t *Tokenizer) Reset(b []byte) {
98 // This code is similar to:
99 //
100 // *t = Tokenizer{json: b}
101 //
102 // However, it does not compile down to an invocation of duff-copy, which
103 // ends up being slower and prevents the code from being inlined.
104 t.Delim = 0
105 t.Value = nil
106 t.Err = nil
107 t.Depth = 0
108 t.Index = 0
109 t.IsKey = false
110 t.isKey = false
111 t.json = b
112 t.stack = nil
113}
114
115// Next returns a new tokenizer pointing at the next token, or the zero-value of
116// Tokenizer if the end of the json input has been reached.
117//
118// If the tokenizer encounters malformed json while reading the input the method
119// sets t.Err to an error describing the issue, and returns false. Once an error
120// has been encountered, the tokenizer will always fail until its input is
121// cleared by a call to its Reset method.
122func (t *Tokenizer) Next() bool {
123 if t.Err != nil {
124 return false
125 }
126
127 // Inlined code of the skipSpaces function, this give a ~15% speed boost.
128 i := 0
129skipLoop:
130 for _, c := range t.json {
131 switch c {
132 case sp, ht, nl, cr:
133 i++
134 default:
135 break skipLoop
136 }
137 }
138
139 if t.json = t.json[i:]; len(t.json) == 0 {
140 t.Reset(nil)
141 return false
142 }
143
144 var d Delim
145 var v []byte
146 var b []byte
147 var err error
148
149 switch t.json[0] {
150 case '"':
151 v, b, err = parseString(t.json)
152 case 'n':
153 v, b, err = parseNull(t.json)
154 case 't':
155 v, b, err = parseTrue(t.json)
156 case 'f':
157 v, b, err = parseFalse(t.json)
158 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
159 v, b, err = parseNumber(t.json)
160 case '{', '}', '[', ']', ':', ',':
161 d, v, b = Delim(t.json[0]), t.json[:1], t.json[1:]
162 default:
163 v, b, err = t.json[:1], t.json[1:], syntaxError(t.json, "expected token but found '%c'", t.json[0])
164 }
165
166 t.Delim = d
167 t.Value = RawValue(v)
168 t.Err = err
169 t.Depth = t.depth()
170 t.Index = t.index()
171 t.IsKey = d == 0 && t.isKey
172 t.json = b
173
174 if d != 0 {
175 switch d {
176 case '{':
177 t.isKey = true
178 t.push(inObject)
179 case '[':
180 t.push(inArray)
181 case '}':
182 err = t.pop(inObject)
183 t.Depth--
184 t.Index = t.index()
185 case ']':
186 err = t.pop(inArray)
187 t.Depth--
188 t.Index = t.index()
189 case ':':
190 t.isKey = false
191 case ',':
192 if t.is(inObject) {
193 t.isKey = true
194 }
195 t.stack[len(t.stack)-1].len++
196 }
197 }
198
199 return (d != 0 || len(v) != 0) && err == nil
200}
201
202func (t *Tokenizer) push(typ scope) {
203 if t.stack == nil {
204 t.stack = t.buffer[:0]
205 }
206 t.stack = append(t.stack, state{typ: typ, len: 1})
207}
208
209func (t *Tokenizer) pop(expect scope) error {
210 i := len(t.stack) - 1
211
212 if i < 0 {
213 return syntaxError(t.json, "found unexpected character while tokenizing json input")
214 }
215
216 if found := t.stack[i]; expect != found.typ {
217 return syntaxError(t.json, "found unexpected character while tokenizing json input")
218 }
219
220 t.stack = t.stack[:i]
221 return nil
222}
223
224func (t *Tokenizer) is(typ scope) bool {
225 return len(t.stack) != 0 && t.stack[len(t.stack)-1].typ == typ
226}
227
228func (t *Tokenizer) depth() int {
229 return len(t.stack)
230}
231
232func (t *Tokenizer) index() int {
233 if len(t.stack) == 0 {
234 return 0
235 }
236 return t.stack[len(t.stack)-1].len - 1
237}
238
239// RawValue represents a raw json value, it is intended to carry null, true,
240// false, number, and string values only.
241type RawValue []byte
242
243// String returns true if v contains a string value.
244func (v RawValue) String() bool { return len(v) != 0 && v[0] == '"' }
245
246// Null returns true if v contains a null value.
247func (v RawValue) Null() bool { return len(v) != 0 && v[0] == 'n' }
248
249// True returns true if v contains a true value.
250func (v RawValue) True() bool { return len(v) != 0 && v[0] == 't' }
251
252// False returns true if v contains a false value.
253func (v RawValue) False() bool { return len(v) != 0 && v[0] == 'f' }
254
255// Number returns true if v contains a number value.
256func (v RawValue) Number() bool {
257 if len(v) != 0 {
258 switch v[0] {
259 case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
260 return true
261 }
262 }
263 return false
264}
265
266// AppendUnquote writes the unquoted version of the string value in v into b.
267func (v RawValue) AppendUnquote(b []byte) []byte {
268 s, r, new, err := parseStringUnquote([]byte(v), b)
269 if err != nil {
270 panic(err)
271 }
272 if len(r) != 0 {
273 panic(syntaxError(r, "unexpected trailing tokens after json value"))
274 }
275 if new {
276 b = s
277 } else {
278 b = append(b, s...)
279 }
280 return b
281}
282
283// Unquote returns the unquoted version of the string value in v.
284func (v RawValue) Unquote() []byte {
285 return v.AppendUnquote(nil)
286}