diff --git a/go.mod b/go.mod index 3148b721c..2eba078d6 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a github.com/jmoiron/sqlx v1.2.0 github.com/json-iterator/go v1.1.9 - github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 // indirect github.com/mattn/go-sqlite3 v1.14.6 github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 github.com/remeh/sizedwaitgroup v1.0.0 @@ -32,7 +31,8 @@ require ( github.com/spf13/pflag v1.0.3 github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.5.1 - github.com/tidwall/gjson v1.6.0 + github.com/tidwall/gjson v1.8.1 + github.com/tidwall/pretty v1.2.0 // indirect github.com/vektah/gqlparser/v2 v2.0.1 github.com/vektra/mockery/v2 v2.2.1 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 diff --git a/go.sum b/go.sum index 6c673e170..f70dae912 100644 --- a/go.sum +++ b/go.sum @@ -75,14 +75,9 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g= -github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c h1:qM1xzKK8kc93zKPkxK4iqtjksqDDrU6g9wGnr30jyLo= -github.com/chromedp/cdproto v0.0.0-20200608134039-8a80cdaf865c/go.mod h1:E6LPWRdIJc11h/di5p0rwvRmUYbhGpBEH7ZbPfzDIOE= github.com/chromedp/cdproto v0.0.0-20210526005521-9e51b9051fd0/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= github.com/chromedp/cdproto v0.0.0-20210622022015-fe1827b46b84 h1:Xxl4imt7LA3SbkrlIH5mm+mbzsv0tpHomLASTPINlvQ= github.com/chromedp/cdproto v0.0.0-20210622022015-fe1827b46b84/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= -github.com/chromedp/chromedp v0.5.3 h1:F9LafxmYpsQhWQBdCs+6Sret1zzeeFyHS5LkRF//Ffg= -github.com/chromedp/chromedp v0.5.3/go.mod h1:YLdPtndaHQ4rCpSpBG+IPpy9JvX0VD+7aaLxYgYj28w= github.com/chromedp/chromedp v0.7.3 h1:FvgJICfjvXtDX+miuMUY0NHuY8zQvjS/TcEQEG6Ldzs= github.com/chromedp/chromedp v0.7.3/go.mod h1:9gC521Yzgrk078Ulv6KIgG7hJ2x9aWrxMBBobTFk30A= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= @@ -373,16 +368,10 @@ github.com/gobuffalo/uuid v2.0.5+incompatible/go.mod h1:ErhIzkRhm0FtRuiE/PeORqcw github.com/gobuffalo/validate v2.0.3+incompatible/go.mod h1:N+EtDe0J8252BgfzQUChBgfd6L93m9weay53EWFVsMM= github.com/gobuffalo/x v0.0.0-20181003152136-452098b06085/go.mod h1:WevpGD+5YOreDJznWevcn8NTmQEW5STSBgIkpkjzqXc= github.com/gobuffalo/x v0.0.0-20181007152206-913e47c59ca7/go.mod h1:9rDPXaB3kXdKWzMc4odGQQdG2e2DIEmANy5aSJ9yesY= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gobwas/ws v1.1.0-rc.5 h1:QOAag7FoBaBYYHRqzqkhhd8fq5RTubvI4v3Ft/gDVVQ= github.com/gobwas/ws v1.1.0-rc.5/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= @@ -431,6 +420,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -516,8 +506,6 @@ github.com/karrick/godirwalk v1.7.8/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46s github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08 h1:V0an7KRw92wmJysvFvtqtKMAPmvS5O0jtB0nYo6t+gs= -github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0= github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -538,9 +526,6 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8= -github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/deplist v1.0.4/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM= @@ -770,13 +755,15 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= -github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= -github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= -github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= +github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU= +github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk= +github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= diff --git a/vendor/github.com/tidwall/gjson/.travis.yml b/vendor/github.com/tidwall/gjson/.travis.yml deleted file mode 100644 index 4f2ee4d97..000000000 --- a/vendor/github.com/tidwall/gjson/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: go diff --git a/vendor/github.com/tidwall/gjson/README.md b/vendor/github.com/tidwall/gjson/README.md index 4108deb37..059570f9c 100644 --- a/vendor/github.com/tidwall/gjson/README.md +++ b/vendor/github.com/tidwall/gjson/README.md @@ -3,13 +3,10 @@ src="logo.png" width="240" height="78" border="0" alt="GJSON">
-Build Status GoDoc GJSON Playground

- -

get json values quickly

GJSON is a Go package that provides a [fast](#performance) and [simple](#get-a-value) way to get values from a json document. @@ -153,10 +150,6 @@ result.Less(token Result, caseSensitive bool) bool The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types: -The `result.Array()` function returns back an array of values. -If the result represents a non-existent value, then an empty array will be returned. -If the result is not a JSON array, the return value will be an array containing one result. - ```go boolean >> bool number >> float64 @@ -166,6 +159,10 @@ array >> []interface{} object >> map[string]interface{} ``` +The `result.Array()` function returns back an array of values. +If the result represents a non-existent value, then an empty array will be returned. +If the result is not a JSON array, the return value will be an array containing one result. + ### 64-bit integers The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers. @@ -476,7 +473,7 @@ JSON document used: } ``` -Each operation was rotated though one of the following search paths: +Each operation was rotated through one of the following search paths: ``` widget.window.name @@ -484,12 +481,4 @@ widget.image.hOffset widget.text.onMouseUp ``` -*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8 and can be be found [here](https://github.com/tidwall/gjson-benchmarks).* - - -## Contact -Josh Baker [@tidwall](http://twitter.com/tidwall) - -## License - -GJSON source code is available under the MIT [License](/LICENSE). +*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8 and can be found [here](https://github.com/tidwall/gjson-benchmarks).* diff --git a/vendor/github.com/tidwall/gjson/SYNTAX.md b/vendor/github.com/tidwall/gjson/SYNTAX.md index 5ea0407f5..bb6e92d54 100644 --- a/vendor/github.com/tidwall/gjson/SYNTAX.md +++ b/vendor/github.com/tidwall/gjson/SYNTAX.md @@ -77,14 +77,21 @@ Special purpose characters, such as `.`, `*`, and `?` can be escaped with `\`. fav\.movie "Deer Hunter" ``` -You'll also need to make sure that the `\` character is correctly escaped when hardcoding a path in source code. +You'll also need to make sure that the `\` character is correctly escaped when hardcoding a path in you source code. ```go -res := gjson.Get(json, "fav\\.movie") // must escape the slash -res := gjson.Get(json, `fav\.movie`) // no need to escape the slash - +// Go +val := gjson.Get(json, "fav\\.movie") // must escape the slash +val := gjson.Get(json, `fav\.movie`) // no need to escape the slash ``` +```rust +// Rust +let val = gjson::get(json, "fav\\.movie") // must escape the slash +let val = gjson::get(json, r#"fav\.movie"#) // no need to escape the slash +``` + + ### Arrays The `#` character allows for digging into JSON Arrays. @@ -128,6 +135,37 @@ changed in v1.3.0 as to avoid confusion with the new [multipath](#multipaths) syntax. For backwards compatibility, `#[...]` will continue to work until the next major release.* +The `~` (tilde) operator will convert a value to a boolean before comparison. + +For example, using the following JSON: + +```json +{ + "vals": [ + { "a": 1, "b": true }, + { "a": 2, "b": true }, + { "a": 3, "b": false }, + { "a": 4, "b": "0" }, + { "a": 5, "b": 0 }, + { "a": 6, "b": "1" }, + { "a": 7, "b": 1 }, + { "a": 8, "b": "true" }, + { "a": 9, "b": false }, + { "a": 10, "b": null }, + { "a": 11 } + ] +} +``` + +You can now query for all true(ish) or false(ish) values: + +``` +vals.#(b==~true)#.a >> [1,2,6,7,8] +vals.#(b==~false)#.a >> [3,4,5,9,10,11] +``` + +The last value which was non-existent is treated as `false` + ### Dot vs Pipe The `.` is standard separator, but it's also possible to use a `|`. @@ -248,6 +286,8 @@ gjson.AddModifier("case", func(json, arg string) string { "children.@case:lower.@reverse" ["jack","alex","sara"] ``` +*Note: Custom modifiers are not yet available in the Rust version* + ### Multipaths Starting with v1.3.0, GJSON added the ability to join multiple paths together diff --git a/vendor/github.com/tidwall/gjson/gjson.go b/vendor/github.com/tidwall/gjson/gjson.go index 0b6dcb098..8e6c0990c 100644 --- a/vendor/github.com/tidwall/gjson/gjson.go +++ b/vendor/github.com/tidwall/gjson/gjson.go @@ -3,7 +3,6 @@ package gjson import ( "encoding/json" - "reflect" "strconv" "strings" "time" @@ -106,7 +105,8 @@ func (t Result) Bool() bool { case True: return true case String: - return t.Str != "" && t.Str != "0" && t.Str != "false" + b, _ := strconv.ParseBool(strings.ToLower(t.Str)) + return b case Number: return t.Num != 0 } @@ -124,16 +124,17 @@ func (t Result) Int() int64 { return n case Number: // try to directly convert the float64 to int64 - n, ok := floatToInt(t.Num) - if !ok { - // now try to parse the raw string - n, ok = parseInt(t.Raw) - if !ok { - // fallback to a standard conversion - return int64(t.Num) - } + i, ok := safeInt(t.Num) + if ok { + return i } - return n + // now try to parse the raw string + i, ok = parseInt(t.Raw) + if ok { + return i + } + // fallback to a standard conversion + return int64(t.Num) } } @@ -149,16 +150,17 @@ func (t Result) Uint() uint64 { return n case Number: // try to directly convert the float64 to uint64 - n, ok := floatToUint(t.Num) - if !ok { - // now try to parse the raw string - n, ok = parseUint(t.Raw) - if !ok { - // fallback to a standard conversion - return uint64(t.Num) - } + i, ok := safeInt(t.Num) + if ok && i >= 0 { + return uint64(i) } - return n + // now try to parse the raw string + u, ok := parseUint(t.Raw) + if ok { + return u + } + // fallback to a standard conversion + return uint64(t.Num) } } @@ -495,6 +497,9 @@ func squash(json string) string { } } if depth == 0 { + if i >= len(json) { + return json + } return json[:i+1] } case '{', '[', '(': @@ -579,7 +584,7 @@ func tostr(json string) (raw string, str string) { continue } } - break + return json[:i+1], unescape(json[1:i]) } } var ret string @@ -709,10 +714,10 @@ type arrayPathResult struct { alogkey string query struct { on bool + all bool path string op string value string - all bool } } @@ -726,8 +731,13 @@ func parseArrayPath(path string) (r arrayPathResult) { } if path[i] == '.' { r.part = path[:i] - r.path = path[i+1:] - r.more = true + if !r.arrch && i < len(path)-1 && isDotPiperChar(path[i+1]) { + r.pipe = path[i+1:] + r.piped = true + } else { + r.path = path[i+1:] + r.more = true + } return } if path[i] == '#' { @@ -740,120 +750,27 @@ func parseArrayPath(path string) (r arrayPathResult) { } else if path[1] == '[' || path[1] == '(' { // query r.query.on = true - if true { - qpath, op, value, _, fi, ok := parseQuery(path[i:]) - if !ok { - // bad query, end now - break - } - r.query.path = qpath - r.query.op = op - r.query.value = value - i = fi - 1 - if i+1 < len(path) && path[i+1] == '#' { - r.query.all = true - } - } else { - var end byte - if path[1] == '[' { - end = ']' - } else { - end = ')' - } - i += 2 - // whitespace - for ; i < len(path); i++ { - if path[i] > ' ' { - break - } - } - s := i - for ; i < len(path); i++ { - if path[i] <= ' ' || - path[i] == '!' || - path[i] == '=' || - path[i] == '<' || - path[i] == '>' || - path[i] == '%' || - path[i] == end { - break - } - } - r.query.path = path[s:i] - // whitespace - for ; i < len(path); i++ { - if path[i] > ' ' { - break - } - } - if i < len(path) { - s = i - if path[i] == '!' { - if i < len(path)-1 && (path[i+1] == '=' || - path[i+1] == '%') { - i++ - } - } else if path[i] == '<' || path[i] == '>' { - if i < len(path)-1 && path[i+1] == '=' { - i++ - } - } else if path[i] == '=' { - if i < len(path)-1 && path[i+1] == '=' { - s++ - i++ - } - } - i++ - r.query.op = path[s:i] - // whitespace - for ; i < len(path); i++ { - if path[i] > ' ' { - break - } - } - s = i - for ; i < len(path); i++ { - if path[i] == '"' { - i++ - s2 := i - for ; i < len(path); i++ { - if path[i] > '\\' { - continue - } - if path[i] == '"' { - // look for an escaped slash - if path[i-1] == '\\' { - n := 0 - for j := i - 2; j > s2-1; j-- { - if path[j] != '\\' { - break - } - n++ - } - if n%2 == 0 { - continue - } - } - break - } - } - } else if path[i] == end { - if i+1 < len(path) && path[i+1] == '#' { - r.query.all = true - } - break - } - } - if i > len(path) { - i = len(path) - } - v := path[s:i] - for len(v) > 0 && v[len(v)-1] <= ' ' { - v = v[:len(v)-1] - } - r.query.value = v + qpath, op, value, _, fi, vesc, ok := + parseQuery(path[i:]) + if !ok { + // bad query, end now + break + } + if len(value) > 2 && value[0] == '"' && + value[len(value)-1] == '"' { + value = value[1 : len(value)-1] + if vesc { + value = unescape(value) } } + r.query.path = qpath + r.query.op = op + r.query.value = value + + i = fi - 1 + if i+1 < len(path) && path[i+1] == '#' { + r.query.all = true + } } } continue @@ -879,11 +796,11 @@ func parseArrayPath(path string) (r arrayPathResult) { // # middle // .cap # right func parseQuery(query string) ( - path, op, value, remain string, i int, ok bool, + path, op, value, remain string, i int, vesc, ok bool, ) { if len(query) < 2 || query[0] != '#' || (query[1] != '(' && query[1] != '[') { - return "", "", "", "", i, false + return "", "", "", "", i, false, false } i = 2 j := 0 // start of value part @@ -911,6 +828,7 @@ func parseQuery(query string) ( i++ for ; i < len(query); i++ { if query[i] == '\\' { + vesc = true i++ } else if query[i] == '"' { break @@ -919,7 +837,7 @@ func parseQuery(query string) ( } } if depth > 0 { - return "", "", "", "", i, false + return "", "", "", "", i, false, false } if j > 0 { path = trim(query[2:j]) @@ -956,7 +874,7 @@ func parseQuery(query string) ( path = trim(query[2:i]) remain = query[i+1:] } - return path, op, value, remain, i + 1, true + return path, op, value, remain, i + 1, vesc, true } func trim(s string) string { @@ -973,6 +891,11 @@ right: return s } +// peek at the next byte and see if it's a '@', '[', or '{'. +func isDotPiperChar(c byte) bool { + return !DisableModifiers && (c == '@' || c == '[' || c == '{') +} + type objectPathResult struct { part string path string @@ -991,12 +914,8 @@ func parseObjectPath(path string) (r objectPathResult) { return } if path[i] == '.' { - // peek at the next byte and see if it's a '@', '[', or '{'. r.part = path[:i] - if !DisableModifiers && - i < len(path)-1 && - (path[i+1] == '@' || - path[i+1] == '[' || path[i+1] == '{') { + if i < len(path)-1 && isDotPiperChar(path[i+1]) { r.pipe = path[i+1:] r.piped = true } else { @@ -1026,14 +945,11 @@ func parseObjectPath(path string) (r objectPathResult) { continue } else if path[i] == '.' { r.part = string(epart) - // peek at the next byte and see if it's a '@' modifier - if !DisableModifiers && - i < len(path)-1 && path[i+1] == '@' { + if i < len(path)-1 && isDotPiperChar(path[i+1]) { r.pipe = path[i+1:] r.piped = true } else { r.path = path[i+1:] - r.more = true } r.more = true return @@ -1258,8 +1174,14 @@ func parseObject(c *parseContext, i int, path string) (int, bool) { } func queryMatches(rp *arrayPathResult, value Result) bool { rpv := rp.query.value - if len(rpv) > 2 && rpv[0] == '"' && rpv[len(rpv)-1] == '"' { - rpv = rpv[1 : len(rpv)-1] + if len(rpv) > 0 && rpv[0] == '~' { + // convert to bool + rpv = rpv[1:] + if value.Bool() { + value = Result{Type: True} + } else { + value = Result{Type: False} + } } if !value.Exists() { return false @@ -1398,7 +1320,6 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { } return false } - for i < len(c.json)+1 { if !rp.arrch { pmatch = partidx == h @@ -1600,10 +1521,17 @@ func parseArray(c *parseContext, i int, path string) (int, bool) { c.calcd = true return i + 1, true } - if len(multires) > 0 && !c.value.Exists() { - c.value = Result{ - Raw: string(append(multires, ']')), - Type: JSON, + if !c.value.Exists() { + if len(multires) > 0 { + c.value = Result{ + Raw: string(append(multires, ']')), + Type: JSON, + } + } else if rp.query.all { + c.value = Result{ + Raw: "[]", + Type: JSON, + } } } return i + 1, false @@ -1835,7 +1763,7 @@ type parseContext struct { // A path is in dot syntax, such as "name.last" or "age". // When the value is found it's returned immediately. // -// A path is a series of keys searated by a dot. +// A path is a series of keys separated by a dot. // A key may contain special wildcard characters '*' and '?'. // To access an array value use the index as the key. // To get the number of elements in an array or to access a child path, use @@ -1944,7 +1872,6 @@ func Get(json, path string) Result { } } } - var i int var c = &parseContext{json: json} if len(path) >= 2 && path[0] == '.' && path[1] == '.' { @@ -2169,11 +2096,6 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) { return i, res, false } -var ( // used for testing - testWatchForFallback bool - testLastWasFallback bool -) - // GetMany searches json for the multiple paths. // The return value is a Result array where the number of items // will be equal to the number of input paths. @@ -2374,6 +2296,12 @@ func validnumber(data []byte, i int) (outi int, ok bool) { // sign if data[i] == '-' { i++ + if i == len(data) { + return i, false + } + if data[i] < '0' || data[i] > '9' { + return i, false + } } // int if i == len(data) { @@ -2524,25 +2452,14 @@ func parseInt(s string) (n int64, ok bool) { return n, true } -const minUint53 = 0 -const maxUint53 = 4503599627370495 -const minInt53 = -2251799813685248 -const maxInt53 = 2251799813685247 - -func floatToUint(f float64) (n uint64, ok bool) { - n = uint64(f) - if float64(n) == f && n >= minUint53 && n <= maxUint53 { - return n, true +// safeInt validates a given JSON number +// ensures it lies within the minimum and maximum representable JSON numbers +func safeInt(f float64) (n int64, ok bool) { + // https://tc39.es/ecma262/#sec-number.min_safe_integer || https://tc39.es/ecma262/#sec-number.max_safe_integer + if f < -9007199254740991 || f > 9007199254740991 { + return 0, false } - return 0, false -} - -func floatToInt(f float64) (n int64, ok bool) { - n = int64(f) - if float64(n) == f && n >= minInt53 && n <= maxInt53 { - return n, true - } - return 0, false + return int64(f), true } // execModifier parses the path to find a matching modifier function. @@ -2600,7 +2517,7 @@ func execModifier(json, path string) (pathOut, res string, ok bool) { // unwrap removes the '[]' or '{}' characters around json func unwrap(json string) string { json = trim(json) - if len(json) >= 2 && json[0] == '[' || json[0] == '{' { + if len(json) >= 2 && (json[0] == '[' || json[0] == '{') { json = json[1 : len(json)-1] } return json @@ -2632,6 +2549,26 @@ func ModifierExists(name string, fn func(json, arg string) string) bool { return ok } +// cleanWS remove any non-whitespace from string +func cleanWS(s string) string { + for i := 0; i < len(s); i++ { + switch s[i] { + case ' ', '\t', '\n', '\r': + continue + default: + var s2 []byte + for i := 0; i < len(s); i++ { + switch s[i] { + case ' ', '\t', '\n', '\r': + s2 = append(s2, s[i]) + } + } + return string(s2) + } + } + return s +} + // @pretty modifier makes the json look nice. func modPretty(json, arg string) string { if len(arg) > 0 { @@ -2641,9 +2578,9 @@ func modPretty(json, arg string) string { case "sortKeys": opts.SortKeys = value.Bool() case "indent": - opts.Indent = value.String() + opts.Indent = cleanWS(value.String()) case "prefix": - opts.Prefix = value.String() + opts.Prefix = cleanWS(value.String()) case "width": opts.Width = int(value.Int()) } @@ -2729,19 +2666,24 @@ func modFlatten(json, arg string) string { out = append(out, '[') var idx int res.ForEach(func(_, value Result) bool { - if idx > 0 { - out = append(out, ',') - } + var raw string if value.IsArray() { if deep { - out = append(out, unwrap(modFlatten(value.Raw, arg))...) + raw = unwrap(modFlatten(value.Raw, arg)) } else { - out = append(out, unwrap(value.Raw)...) + raw = unwrap(value.Raw) } } else { - out = append(out, value.Raw...) + raw = value.Raw + } + raw = strings.TrimSpace(raw) + if len(raw) > 0 { + if idx > 0 { + out = append(out, ',') + } + out = append(out, raw...) + idx++ } - idx++ return true }) out = append(out, ']') @@ -2825,6 +2767,19 @@ func modValid(json, arg string) string { return json } +// stringHeader instead of reflect.StringHeader +type stringHeader struct { + data unsafe.Pointer + len int +} + +// sliceHeader instead of reflect.SliceHeader +type sliceHeader struct { + data unsafe.Pointer + len int + cap int +} + // getBytes casts the input json bytes to a string and safely returns the // results as uniquely allocated data. This operation is intended to minimize // copies and allocations for the large json string->[]byte. @@ -2834,14 +2789,14 @@ func getBytes(json []byte, path string) Result { // unsafe cast to string result = Get(*(*string)(unsafe.Pointer(&json)), path) // safely get the string headers - rawhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Raw)) - strhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Str)) + rawhi := *(*stringHeader)(unsafe.Pointer(&result.Raw)) + strhi := *(*stringHeader)(unsafe.Pointer(&result.Str)) // create byte slice headers - rawh := reflect.SliceHeader{Data: rawhi.Data, Len: rawhi.Len} - strh := reflect.SliceHeader{Data: strhi.Data, Len: strhi.Len} - if strh.Data == 0 { + rawh := sliceHeader{data: rawhi.data, len: rawhi.len, cap: rawhi.len} + strh := sliceHeader{data: strhi.data, len: strhi.len, cap: rawhi.len} + if strh.data == nil { // str is nil - if rawh.Data == 0 { + if rawh.data == nil { // raw is nil result.Raw = "" } else { @@ -2849,19 +2804,20 @@ func getBytes(json []byte, path string) Result { result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) } result.Str = "" - } else if rawh.Data == 0 { + } else if rawh.data == nil { // raw is nil result.Raw = "" // str has data, safely copy the slice header to a string result.Str = string(*(*[]byte)(unsafe.Pointer(&strh))) - } else if strh.Data >= rawh.Data && - int(strh.Data)+strh.Len <= int(rawh.Data)+rawh.Len { + } else if uintptr(strh.data) >= uintptr(rawh.data) && + uintptr(strh.data)+uintptr(strh.len) <= + uintptr(rawh.data)+uintptr(rawh.len) { // Str is a substring of Raw. - start := int(strh.Data - rawh.Data) + start := uintptr(strh.data) - uintptr(rawh.data) // safely copy the raw slice header result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) // substring the raw - result.Str = result.Raw[start : start+strh.Len] + result.Str = result.Raw[start : start+uintptr(strh.len)] } else { // safely copy both the raw and str slice headers to strings result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh))) @@ -2876,9 +2832,9 @@ func getBytes(json []byte, path string) Result { // used instead. func fillIndex(json string, c *parseContext) { if len(c.value.Raw) > 0 && !c.calcd { - jhdr := *(*reflect.StringHeader)(unsafe.Pointer(&json)) - rhdr := *(*reflect.StringHeader)(unsafe.Pointer(&(c.value.Raw))) - c.value.Index = int(rhdr.Data - jhdr.Data) + jhdr := *(*stringHeader)(unsafe.Pointer(&json)) + rhdr := *(*stringHeader)(unsafe.Pointer(&(c.value.Raw))) + c.value.Index = int(uintptr(rhdr.data) - uintptr(jhdr.data)) if c.value.Index < 0 || c.value.Index >= len(json) { c.value.Index = 0 } @@ -2886,10 +2842,10 @@ func fillIndex(json string, c *parseContext) { } func stringBytes(s string) []byte { - return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Data: (*reflect.StringHeader)(unsafe.Pointer(&s)).Data, - Len: len(s), - Cap: len(s), + return *(*[]byte)(unsafe.Pointer(&sliceHeader{ + data: (*stringHeader)(unsafe.Pointer(&s)).data, + len: len(s), + cap: len(s), })) } diff --git a/vendor/github.com/tidwall/gjson/go.mod b/vendor/github.com/tidwall/gjson/go.mod index d851688cc..9853a3096 100644 --- a/vendor/github.com/tidwall/gjson/go.mod +++ b/vendor/github.com/tidwall/gjson/go.mod @@ -3,6 +3,6 @@ module github.com/tidwall/gjson go 1.12 require ( - github.com/tidwall/match v1.0.1 - github.com/tidwall/pretty v1.0.0 + github.com/tidwall/match v1.0.3 + github.com/tidwall/pretty v1.1.0 ) diff --git a/vendor/github.com/tidwall/gjson/go.sum b/vendor/github.com/tidwall/gjson/go.sum index a4a2d872c..739d13276 100644 --- a/vendor/github.com/tidwall/gjson/go.sum +++ b/vendor/github.com/tidwall/gjson/go.sum @@ -1,4 +1,4 @@ -github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= -github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= +github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= diff --git a/vendor/github.com/tidwall/match/.travis.yml b/vendor/github.com/tidwall/match/.travis.yml deleted file mode 100644 index 4f2ee4d97..000000000 --- a/vendor/github.com/tidwall/match/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: go diff --git a/vendor/github.com/tidwall/match/README.md b/vendor/github.com/tidwall/match/README.md index 2aa5bc38b..5fdd4cf63 100644 --- a/vendor/github.com/tidwall/match/README.md +++ b/vendor/github.com/tidwall/match/README.md @@ -1,20 +1,17 @@ -Match -===== -Build Status -GoDoc +# Match + +[![GoDoc](https://godoc.org/github.com/tidwall/match?status.svg)](https://godoc.org/github.com/tidwall/match) Match is a very simple pattern matcher where '*' matches on any number characters and '?' matches on any one character. -Installing ----------- +## Installing ``` go get -u github.com/tidwall/match ``` -Example -------- +## Example ```go match.Match("hello", "*llo") @@ -23,10 +20,10 @@ match.Match("hello", "h*o") ``` -Contact -------- +## Contact + Josh Baker [@tidwall](http://twitter.com/tidwall) -License -------- +## License + Redcon source code is available under the MIT [License](/LICENSE). diff --git a/vendor/github.com/tidwall/match/go.mod b/vendor/github.com/tidwall/match/go.mod new file mode 100644 index 000000000..df19b5f77 --- /dev/null +++ b/vendor/github.com/tidwall/match/go.mod @@ -0,0 +1,3 @@ +module github.com/tidwall/match + +go 1.15 diff --git a/vendor/github.com/tidwall/match/match.go b/vendor/github.com/tidwall/match/match.go index fcfe998b5..57aabf424 100644 --- a/vendor/github.com/tidwall/match/match.go +++ b/vendor/github.com/tidwall/match/match.go @@ -1,4 +1,4 @@ -// Match provides a simple pattern matcher with unicode support. +// Package match provides a simple pattern matcher with unicode support. package match import "unicode/utf8" @@ -6,7 +6,7 @@ import "unicode/utf8" // Match returns true if str matches pattern. This is a very // simple wildcard match where '*' matches on any number characters // and '?' matches on any one character. - +// // pattern: // { term } // term: @@ -16,12 +16,16 @@ import "unicode/utf8" // '\\' c matches character c // func Match(str, pattern string) bool { + return deepMatch(str, pattern) +} + +func deepMatch(str, pattern string) bool { if pattern == "*" { return true } - return deepMatch(str, pattern) -} -func deepMatch(str, pattern string) bool { + for len(pattern) > 1 && pattern[0] == '*' && pattern[1] == '*' { + pattern = pattern[1:] + } for len(pattern) > 0 { if pattern[0] > 0x7f { return deepMatchRune(str, pattern) @@ -52,6 +56,13 @@ func deepMatch(str, pattern string) bool { } func deepMatchRune(str, pattern string) bool { + if pattern == "*" { + return true + } + for len(pattern) > 1 && pattern[0] == '*' && pattern[1] == '*' { + pattern = pattern[1:] + } + var sr, pr rune var srsz, prsz int diff --git a/vendor/github.com/tidwall/pretty/.travis.yml b/vendor/github.com/tidwall/pretty/.travis.yml deleted file mode 100644 index 4f2ee4d97..000000000 --- a/vendor/github.com/tidwall/pretty/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: go diff --git a/vendor/github.com/tidwall/pretty/README.md b/vendor/github.com/tidwall/pretty/README.md index d2b8864d5..d3be5e54e 100644 --- a/vendor/github.com/tidwall/pretty/README.md +++ b/vendor/github.com/tidwall/pretty/README.md @@ -1,8 +1,6 @@ # Pretty -[![Build Status](https://img.shields.io/travis/tidwall/pretty.svg?style=flat-square)](https://travis-ci.org/tidwall/prettty) -[![Coverage Status](https://img.shields.io/badge/coverage-100%25-brightgreen.svg?style=flat-square)](http://gocover.io/github.com/tidwall/pretty) -[![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/tidwall/pretty) +[![GoDoc](https://img.shields.io/badge/api-reference-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/tidwall/pretty) Pretty is a Go package that provides [fast](#performance) methods for formatting JSON for human readability, or to compact JSON for smaller payloads. @@ -81,7 +79,6 @@ Will format the json to: {"name":{"first":"Tom","last":"Anderson"},"age":37,"children":["Sara","Alex","Jack"],"fav.movie":"Deer Hunter","friends":[{"first":"Janet","last":"Murphy","age":44}]}``` ``` - ## Customized output There's a `PrettyOptions(json, opts)` function which allows for customizing the output with the following options: @@ -106,14 +103,15 @@ type Options struct { Benchmarks of Pretty alongside the builtin `encoding/json` Indent/Compact methods. ``` -BenchmarkPretty-8 1000000 1283 ns/op 720 B/op 2 allocs/op -BenchmarkUgly-8 3000000 426 ns/op 240 B/op 1 allocs/op -BenchmarkUglyInPlace-8 5000000 340 ns/op 0 B/op 0 allocs/op -BenchmarkJSONIndent-8 300000 4628 ns/op 1069 B/op 4 allocs/op -BenchmarkJSONCompact-8 1000000 2469 ns/op 758 B/op 4 allocs/op +BenchmarkPretty-16 1000000 1034 ns/op 720 B/op 2 allocs/op +BenchmarkPrettySortKeys-16 586797 1983 ns/op 2848 B/op 14 allocs/op +BenchmarkUgly-16 4652365 254 ns/op 240 B/op 1 allocs/op +BenchmarkUglyInPlace-16 6481233 183 ns/op 0 B/op 0 allocs/op +BenchmarkJSONIndent-16 450654 2687 ns/op 1221 B/op 0 allocs/op +BenchmarkJSONCompact-16 685111 1699 ns/op 442 B/op 0 allocs/op ``` -*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7.* +*These benchmarks were run on a MacBook Pro 2.4 GHz 8-Core Intel Core i9.* ## Contact Josh Baker [@tidwall](http://twitter.com/tidwall) diff --git a/vendor/github.com/tidwall/pretty/go.mod b/vendor/github.com/tidwall/pretty/go.mod new file mode 100644 index 000000000..6106735a3 --- /dev/null +++ b/vendor/github.com/tidwall/pretty/go.mod @@ -0,0 +1,3 @@ +module github.com/tidwall/pretty + +go 1.16 diff --git a/vendor/github.com/tidwall/pretty/pretty.go b/vendor/github.com/tidwall/pretty/pretty.go index 0a922d039..f3f756aad 100644 --- a/vendor/github.com/tidwall/pretty/pretty.go +++ b/vendor/github.com/tidwall/pretty/pretty.go @@ -1,7 +1,10 @@ package pretty import ( + "bytes" + "encoding/json" "sort" + "strconv" ) // Options is Pretty options @@ -84,6 +87,14 @@ func ugly(dst, src []byte) []byte { return dst } +func isNaNOrInf(src []byte) bool { + return src[0] == 'i' || //Inf + src[0] == 'I' || // inf + src[0] == '+' || // +Inf + src[0] == 'N' || // Nan + (src[0] == 'n' && len(src) > 1 && src[1] != 'u') // nan +} + func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) { for ; i < len(json); i++ { if json[i] <= ' ' { @@ -92,7 +103,8 @@ func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, in if json[i] == '"' { return appendPrettyString(buf, json, i, nl) } - if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { + + if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' || isNaNOrInf(json[i:]) { return appendPrettyNumber(buf, json, i, nl) } if json[i] == '{' { @@ -118,25 +130,121 @@ type pair struct { vstart, vend int } -type byKey struct { +type byKeyVal struct { sorted bool json []byte + buf []byte pairs []pair } -func (arr *byKey) Len() int { +func (arr *byKeyVal) Len() int { return len(arr.pairs) } -func (arr *byKey) Less(i, j int) bool { - key1 := arr.json[arr.pairs[i].kstart+1 : arr.pairs[i].kend-1] - key2 := arr.json[arr.pairs[j].kstart+1 : arr.pairs[j].kend-1] - return string(key1) < string(key2) +func (arr *byKeyVal) Less(i, j int) bool { + if arr.isLess(i, j, byKey) { + return true + } + if arr.isLess(j, i, byKey) { + return false + } + return arr.isLess(i, j, byVal) } -func (arr *byKey) Swap(i, j int) { +func (arr *byKeyVal) Swap(i, j int) { arr.pairs[i], arr.pairs[j] = arr.pairs[j], arr.pairs[i] arr.sorted = true } +type byKind int + +const ( + byKey byKind = 0 + byVal byKind = 1 +) + +type jtype int + +const ( + jnull jtype = iota + jfalse + jnumber + jstring + jtrue + jjson +) + +func getjtype(v []byte) jtype { + if len(v) == 0 { + return jnull + } + switch v[0] { + case '"': + return jstring + case 'f': + return jfalse + case 't': + return jtrue + case 'n': + return jnull + case '[', '{': + return jjson + default: + return jnumber + } +} + +func (arr *byKeyVal) isLess(i, j int, kind byKind) bool { + k1 := arr.json[arr.pairs[i].kstart:arr.pairs[i].kend] + k2 := arr.json[arr.pairs[j].kstart:arr.pairs[j].kend] + var v1, v2 []byte + if kind == byKey { + v1 = k1 + v2 = k2 + } else { + v1 = bytes.TrimSpace(arr.buf[arr.pairs[i].vstart:arr.pairs[i].vend]) + v2 = bytes.TrimSpace(arr.buf[arr.pairs[j].vstart:arr.pairs[j].vend]) + if len(v1) >= len(k1)+1 { + v1 = bytes.TrimSpace(v1[len(k1)+1:]) + } + if len(v2) >= len(k2)+1 { + v2 = bytes.TrimSpace(v2[len(k2)+1:]) + } + } + t1 := getjtype(v1) + t2 := getjtype(v2) + if t1 < t2 { + return true + } + if t1 > t2 { + return false + } + if t1 == jstring { + s1 := parsestr(v1) + s2 := parsestr(v2) + return string(s1) < string(s2) + } + if t1 == jnumber { + n1, _ := strconv.ParseFloat(string(v1), 64) + n2, _ := strconv.ParseFloat(string(v2), 64) + return n1 < n2 + } + return string(v1) < string(v2) + +} + +func parsestr(s []byte) []byte { + for i := 1; i < len(s); i++ { + if s[i] == '\\' { + var str string + json.Unmarshal(s, &str) + return []byte(str) + } + if s[i] == '"' { + return s[1:i] + } + } + return nil +} + func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) { var ok bool if width > 0 { @@ -174,7 +282,11 @@ func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, } if n > 0 { nl = len(buf) - buf = append(buf, '\n') + if buf[nl-1] == ' ' { + buf[nl-1] = '\n' + } else { + buf = append(buf, '\n') + } } if buf[len(buf)-1] != open { buf = appendTabs(buf, prefix, indent, tabs) @@ -193,7 +305,11 @@ func appendPrettyObject(buf, json []byte, i int, open, close byte, pretty bool, var p pair if pretty { nl = len(buf) - buf = append(buf, '\n') + if buf[nl-1] == ' ' { + buf[nl-1] = '\n' + } else { + buf = append(buf, '\n') + } if open == '{' && sortkeys { p.kstart = i p.vstart = len(buf) @@ -235,8 +351,8 @@ func sortPairs(json, buf []byte, pairs []pair) []byte { } vstart := pairs[0].vstart vend := pairs[len(pairs)-1].vend - arr := byKey{false, json, pairs} - sort.Sort(&arr) + arr := byKeyVal{false, json, buf, pairs} + sort.Stable(&arr) if !arr.sorted { return buf } @@ -305,6 +421,7 @@ func appendTabs(buf []byte, prefix, indent string, tabs int) []byte { type Style struct { Key, String, Number [2]string True, False, Null [2]string + Escape [2]string Append func(dst []byte, c byte) []byte } @@ -318,21 +435,26 @@ func hexp(p byte) byte { } // TerminalStyle is for terminals -var TerminalStyle = &Style{ - Key: [2]string{"\x1B[94m", "\x1B[0m"}, - String: [2]string{"\x1B[92m", "\x1B[0m"}, - Number: [2]string{"\x1B[93m", "\x1B[0m"}, - True: [2]string{"\x1B[96m", "\x1B[0m"}, - False: [2]string{"\x1B[96m", "\x1B[0m"}, - Null: [2]string{"\x1B[91m", "\x1B[0m"}, - Append: func(dst []byte, c byte) []byte { - if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') { - dst = append(dst, "\\u00"...) - dst = append(dst, hexp((c>>4)&0xF)) - return append(dst, hexp((c)&0xF)) - } - return append(dst, c) - }, +var TerminalStyle *Style + +func init() { + TerminalStyle = &Style{ + Key: [2]string{"\x1B[94m", "\x1B[0m"}, + String: [2]string{"\x1B[92m", "\x1B[0m"}, + Number: [2]string{"\x1B[93m", "\x1B[0m"}, + True: [2]string{"\x1B[96m", "\x1B[0m"}, + False: [2]string{"\x1B[96m", "\x1B[0m"}, + Null: [2]string{"\x1B[91m", "\x1B[0m"}, + Escape: [2]string{"\x1B[35m", "\x1B[0m"}, + Append: func(dst []byte, c byte) []byte { + if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') { + dst = append(dst, "\\u00"...) + dst = append(dst, hexp((c>>4)&0xF)) + return append(dst, hexp((c)&0xF)) + } + return append(dst, c) + }, + } } // Color will colorize the json. The style parma is used for customizing @@ -363,8 +485,39 @@ func Color(src []byte, style *Style) []byte { dst = append(dst, style.String[0]...) } dst = apnd(dst, '"') + esc := false + uesc := 0 for i = i + 1; i < len(src); i++ { - dst = apnd(dst, src[i]) + if src[i] == '\\' { + if key { + dst = append(dst, style.Key[1]...) + } else { + dst = append(dst, style.String[1]...) + } + dst = append(dst, style.Escape[0]...) + dst = apnd(dst, src[i]) + esc = true + if i+1 < len(src) && src[i+1] == 'u' { + uesc = 5 + } else { + uesc = 1 + } + } else if esc { + dst = apnd(dst, src[i]) + if uesc == 1 { + esc = false + dst = append(dst, style.Escape[1]...) + if key { + dst = append(dst, style.Key[0]...) + } else { + dst = append(dst, style.String[0]...) + } + } else { + uesc-- + } + } else { + dst = apnd(dst, src[i]) + } if src[i] == '"' { j := i - 1 for ; ; j-- { @@ -377,7 +530,9 @@ func Color(src []byte, style *Style) []byte { } } } - if key { + if esc { + dst = append(dst, style.Escape[1]...) + } else if key { dst = append(dst, style.Key[1]...) } else { dst = append(dst, style.String[1]...) @@ -393,7 +548,7 @@ func Color(src []byte, style *Style) []byte { dst = apnd(dst, src[i]) } else { var kind byte - if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' { + if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' || isNaNOrInf(src[i:]) { kind = '0' dst = append(dst, style.Number[0]...) } else if src[i] == 't' { @@ -430,3 +585,90 @@ func Color(src []byte, style *Style) []byte { } return dst } + +// Spec strips out comments and trailing commas and convert the input to a +// valid JSON per the official spec: https://tools.ietf.org/html/rfc8259 +// +// The resulting JSON will always be the same length as the input and it will +// include all of the same line breaks at matching offsets. This is to ensure +// the result can be later processed by a external parser and that that +// parser will report messages or errors with the correct offsets. +func Spec(src []byte) []byte { + return spec(src, nil) +} + +// SpecInPlace is the same as Spec, but this method reuses the input json +// buffer to avoid allocations. Do not use the original bytes slice upon return. +func SpecInPlace(src []byte) []byte { + return spec(src, src) +} + +func spec(src, dst []byte) []byte { + dst = dst[:0] + for i := 0; i < len(src); i++ { + if src[i] == '/' { + if i < len(src)-1 { + if src[i+1] == '/' { + dst = append(dst, ' ', ' ') + i += 2 + for ; i < len(src); i++ { + if src[i] == '\n' { + dst = append(dst, '\n') + break + } else if src[i] == '\t' || src[i] == '\r' { + dst = append(dst, src[i]) + } else { + dst = append(dst, ' ') + } + } + continue + } + if src[i+1] == '*' { + dst = append(dst, ' ', ' ') + i += 2 + for ; i < len(src)-1; i++ { + if src[i] == '*' && src[i+1] == '/' { + dst = append(dst, ' ', ' ') + i++ + break + } else if src[i] == '\n' || src[i] == '\t' || + src[i] == '\r' { + dst = append(dst, src[i]) + } else { + dst = append(dst, ' ') + } + } + continue + } + } + } + dst = append(dst, src[i]) + if src[i] == '"' { + for i = i + 1; i < len(src); i++ { + dst = append(dst, src[i]) + if src[i] == '"' { + j := i - 1 + for ; ; j-- { + if src[j] != '\\' { + break + } + } + if (j-i)%2 != 0 { + break + } + } + } + } else if src[i] == '}' || src[i] == ']' { + for j := len(dst) - 2; j >= 0; j-- { + if dst[j] <= ' ' { + continue + } + if dst[j] == ',' { + dst[j] = ' ' + } + break + } + } + } + return dst +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 2dac88301..cde17c49c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -300,11 +300,11 @@ github.com/stretchr/testify/assert github.com/stretchr/testify/mock # github.com/subosito/gotenv v1.2.0 github.com/subosito/gotenv -# github.com/tidwall/gjson v1.6.0 +# github.com/tidwall/gjson v1.8.1 github.com/tidwall/gjson -# github.com/tidwall/match v1.0.1 +# github.com/tidwall/match v1.0.3 github.com/tidwall/match -# github.com/tidwall/pretty v1.0.0 +# github.com/tidwall/pretty v1.2.0 github.com/tidwall/pretty # github.com/urfave/cli/v2 v2.1.1 github.com/urfave/cli/v2