summaryrefslogtreecommitdiff
path: root/cmd/ponzu/vendor/github.com/tidwall
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/ponzu/vendor/github.com/tidwall')
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/gjson/LICENSE20
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/gjson/README.md371
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/gjson/gjson.go1942
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/gjson/logo.pngbin15936 -> 0 bytes
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/sjson/.travis.yml1
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/sjson/LICENSE21
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/sjson/README.md278
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/sjson/logo.pngbin16874 -> 0 bytes
-rw-r--r--cmd/ponzu/vendor/github.com/tidwall/sjson/sjson.go653
9 files changed, 0 insertions, 3286 deletions
diff --git a/cmd/ponzu/vendor/github.com/tidwall/gjson/LICENSE b/cmd/ponzu/vendor/github.com/tidwall/gjson/LICENSE
deleted file mode 100644
index 58f5819..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/gjson/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 Josh Baker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/cmd/ponzu/vendor/github.com/tidwall/gjson/README.md b/cmd/ponzu/vendor/github.com/tidwall/gjson/README.md
deleted file mode 100644
index bc26c3c..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/gjson/README.md
+++ /dev/null
@@ -1,371 +0,0 @@
-<p align="center">
-<img
- src="logo.png"
- width="240" height="78" border="0" alt="GJSON">
-<br>
-<a href="https://travis-ci.org/tidwall/gjson"><img src="https://img.shields.io/travis/tidwall/gjson.svg?style=flat-square" alt="Build Status"></a><!--
-<a href="http://gocover.io/github.com/tidwall/gjson"><img src="https://img.shields.io/badge/coverage-97%25-brightgreen.svg?style=flat-square" alt="Code Coverage"></a>
--->
-<a href="https://godoc.org/github.com/tidwall/gjson"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a>
-</p>
-
-<p align="center">get a json value quickly</a></p>
-
-GJSON is a Go package that provides a [very fast](#performance) and simple way to get a value from a json document. The purpose for this library it to give efficient json indexing for the [BuntDB](https://github.com/tidwall/buntdb) project.
-
-For a command line interface check out [JSONed](https://github.com/tidwall/jsoned).
-
-Getting Started
-===============
-
-## Installing
-
-To start using GJSON, install Go and run `go get`:
-
-```sh
-$ go get -u github.com/tidwall/gjson
-```
-
-This will retrieve the library.
-
-## Get a value
-Get searches json for the specified path. A path is in dot syntax, such as "name.last" or "age". This function expects that the json is well-formed and validates. Invalid json will not panic, but it may return back unexpected results. When the value is found it's returned immediately.
-
-```go
-package main
-
-import "github.com/tidwall/gjson"
-
-const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
-
-func main() {
- value := gjson.Get(json, "name.last")
- println(value.String())
-}
-```
-
-This will print:
-
-```
-Prichard
-```
-*There's also the [GetMany](#get-multiple-values-at-once) function to get multiple values at once, and [GetBytes](#working-with-bytes) for working with JSON byte slices.*
-
-## Path Syntax
-
-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 the '#' character.
-The dot and wildcard characters can be escaped with '\'.
-
-```json
-{
- "name": {"first": "Tom", "last": "Anderson"},
- "age":37,
- "children": ["Sara","Alex","Jack"],
- "fav.movie": "Deer Hunter",
- "friends": [
- {"first": "Dale", "last": "Murphy", "age": 44},
- {"first": "Roger", "last": "Craig", "age": 68},
- {"first": "Jane", "last": "Murphy", "age": 47}
- ]
-}
-```
-```
-"name.last" >> "Anderson"
-"age" >> 37
-"children" >> ["Sara","Alex","Jack"]
-"children.#" >> 3
-"children.1" >> "Alex"
-"child*.2" >> "Jack"
-"c?ildren.0" >> "Sara"
-"fav\.movie" >> "Deer Hunter"
-"friends.#.first" >> ["Dale","Roger","Jane"]
-"friends.1.last" >> "Craig"
-```
-
-You can also query an array for the first match by using `#[...]`, or find all matches with `#[...]#`.
-Queries support the `==`, `!=`, `<`, `<=`, `>`, `>=` comparison operators and the simple pattern matching `%` operator.
-
-```
-friends.#[last=="Murphy"].first >> "Dale"
-friends.#[last=="Murphy"]#.first >> ["Dale","Jane"]
-friends.#[age>45]#.last >> ["Craig","Murphy"]
-friends.#[first%"D*"].last >> "Murphy"
-```
-
-## Result Type
-
-GJSON supports the json types `string`, `number`, `bool`, and `null`.
-Arrays and Objects are returned as their raw json types.
-
-The `Result` type holds one of these:
-
-```
-bool, for JSON booleans
-float64, for JSON numbers
-string, for JSON string literals
-nil, for JSON null
-```
-
-To directly access the value:
-
-```go
-result.Type // can be String, Number, True, False, Null, or JSON
-result.Str // holds the string
-result.Num // holds the float64 number
-result.Raw // holds the raw json
-result.Index // index of raw value in original json, zero means index unknown
-```
-
-There are a variety of handy functions that work on a result:
-
-```go
-result.Value() interface{}
-result.Int() int64
-result.Uint() uint64
-result.Float() float64
-result.String() string
-result.Bool() bool
-result.Array() []gjson.Result
-result.Map() map[string]gjson.Result
-result.Get(path string) Result
-result.ForEach(iterator func(key, value Result) bool)
-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
-string >> string
-null >> nil
-array >> []interface{}
-object >> map[string]interface{}
-```
-
-## Get nested array values
-
-Suppose you want all the last names from the following json:
-
-```json
-{
- "programmers": [
- {
- "firstName": "Janet",
- "lastName": "McLaughlin",
- }, {
- "firstName": "Elliotte",
- "lastName": "Hunter",
- }, {
- "firstName": "Jason",
- "lastName": "Harold",
- }
- ]
-}`
-```
-
-You would use the path "programmers.#.lastName" like such:
-
-```go
-result := gjson.Get(json, "programmers.#.lastName")
-for _,name := range result.Array() {
- println(name.String())
-}
-```
-
-You can also query an object inside an array:
-
-```go
-name := gjson.Get(json, `programmers.#[lastName="Hunter"].firstName`)
-println(name.String()) // prints "Elliotte"
-```
-
-## Iterate through an object or array
-
-The `ForEach` function allows for quickly iterating through an object or array.
-The key and value are passed to the iterator function for objects.
-Only the value is passed for arrays.
-Returning `false` from an iterator will stop iteration.
-
-```go
-result := gjson.Get(json, "programmers")
-result.ForEach(func(key, value gjson.Result) bool{
- println(value.String())
- return true // keep iterating
-})
-```
-
-## Simple Parse and Get
-
-There's a `Parse(json)` function that will do a simple parse, and `result.Get(path)` that will search a result.
-
-For example, all of these will return the same result:
-
-```go
-gjson.Parse(json).Get("name").Get("last")
-gjson.Get(json, "name").Get("last")
-gjson.Get(json, "name.last")
-```
-
-## Check for the existence of a value
-
-Sometimes you just want to know if a value exists.
-
-```go
-value := gjson.Get(json, "name.last")
-if !value.Exists() {
- println("no last name")
-} else {
- println(value.String())
-}
-
-// Or as one step
-if gjson.Get(json, "name.last").Exists(){
- println("has a last name")
-}
-```
-
-## Unmarshal to a map
-
-To unmarshal to a `map[string]interface{}`:
-
-```go
-m, ok := gjson.Parse(json).Value().(map[string]interface{})
-if !ok{
- // not a map
-}
-```
-
-## Working with Bytes
-
-If your JSON is contained in a `[]byte` slice, there's the [GetBytes](https://godoc.org/github.com/tidwall/gjson#GetBytes) function. This is preferred over `Get(string(data), path)`.
-
-```go
-var json []byte = ...
-result := gjson.GetBytes(json, path)
-```
-
-If you are using the `gjson.GetBytes(json, path)` function and you want to avoid converting `result.Raw` to a `[]byte`, then you can use this pattern:
-
-```go
-var json []byte = ...
-result := gjson.GetBytes(json, path)
-var raw []byte
-if result.Index > 0 {
- raw = json[result.Index:result.Index+len(result.Raw)]
-} else {
- raw = []byte(result.Raw)
-}
-```
-
-This is a best-effort no allocation sub slice of the original json. This method utilizes the `result.Index` field, which is the position of the raw data in the original json. It's possible that the value of `result.Index` equals zero, in which case the `result.Raw` is converted to a `[]byte`.
-
-## Get multiple values at once
-
-The `GetMany` function can be used to get multiple values at the same time, and is optimized to scan over a JSON payload once.
-
-```go
-results := gjson.GetMany(json, "name.first", "name.last", "age")
-```
-
-The return value is a `[]Result`, which will always contain exactly the same number of items as the input paths.
-
-## Performance
-
-Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
-[ffjson](https://github.com/pquerna/ffjson),
-[EasyJSON](https://github.com/mailru/easyjson),
-and [jsonparser](https://github.com/buger/jsonparser)
-
-```
-BenchmarkGJSONGet-8 15000000 333 ns/op 0 B/op 0 allocs/op
-BenchmarkGJSONUnmarshalMap-8 900000 4188 ns/op 1920 B/op 26 allocs/op
-BenchmarkJSONUnmarshalMap-8 600000 8908 ns/op 3048 B/op 69 allocs/op
-BenchmarkJSONUnmarshalStruct-8 600000 9026 ns/op 1832 B/op 69 allocs/op
-BenchmarkJSONDecoder-8 300000 14339 ns/op 4224 B/op 184 allocs/op
-BenchmarkFFJSONLexer-8 1500000 3156 ns/op 896 B/op 8 allocs/op
-BenchmarkEasyJSONLexer-8 3000000 938 ns/op 613 B/op 6 allocs/op
-BenchmarkJSONParserGet-8 3000000 442 ns/op 21 B/op 0 allocs/op
-```
-
-Benchmarks for the `GetMany` function:
-
-```
-BenchmarkGJSONGetMany4Paths-8 4000000 319 ns/op 112 B/op 0 allocs/op
-BenchmarkGJSONGetMany8Paths-8 8000000 218 ns/op 56 B/op 0 allocs/op
-BenchmarkGJSONGetMany16Paths-8 16000000 160 ns/op 56 B/op 0 allocs/op
-BenchmarkGJSONGetMany32Paths-8 32000000 130 ns/op 64 B/op 0 allocs/op
-BenchmarkGJSONGetMany64Paths-8 64000000 117 ns/op 64 B/op 0 allocs/op
-BenchmarkGJSONGetMany128Paths-8 128000000 109 ns/op 64 B/op 0 allocs/op
-```
-
-JSON document used:
-
-```json
-{
- "widget": {
- "debug": "on",
- "window": {
- "title": "Sample Konfabulator Widget",
- "name": "main_window",
- "width": 500,
- "height": 500
- },
- "image": {
- "src": "Images/Sun.png",
- "hOffset": 250,
- "vOffset": 250,
- "alignment": "center"
- },
- "text": {
- "data": "Click Here",
- "size": 36,
- "style": "bold",
- "vOffset": 100,
- "alignment": "center",
- "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
- }
- }
-}
-```
-
-Each operation was rotated though one of the following search paths:
-
-```
-widget.window.name
-widget.image.hOffset
-widget.text.onMouseUp
-```
-
-For the `GetMany` benchmarks these paths are used:
-
-```
-widget.window.name
-widget.image.hOffset
-widget.text.onMouseUp
-widget.window.title
-widget.image.alignment
-widget.text.style
-widget.window.height
-widget.image.src
-widget.text.data
-widget.text.size
-```
-
-*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7.*
-
-## Contact
-Josh Baker [@tidwall](http://twitter.com/tidwall)
-
-## License
-
-GJSON source code is available under the MIT [License](/LICENSE).
diff --git a/cmd/ponzu/vendor/github.com/tidwall/gjson/gjson.go b/cmd/ponzu/vendor/github.com/tidwall/gjson/gjson.go
deleted file mode 100644
index 1ee26c9..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/gjson/gjson.go
+++ /dev/null
@@ -1,1942 +0,0 @@
-// Package gjson provides searching for json strings.
-package gjson
-
-import (
- "reflect"
- "strconv"
- "unsafe"
-
- "github.com/tidwall/match"
-)
-
-// Type is Result type
-type Type int
-
-const (
- // Null is a null json value
- Null Type = iota
- // False is a json false boolean
- False
- // Number is json number
- Number
- // String is a json string
- String
- // True is a json true boolean
- True
- // JSON is a raw block of JSON
- JSON
-)
-
-// String returns a string representation of the type.
-func (t Type) String() string {
- switch t {
- default:
- return ""
- case Null:
- return "Null"
- case False:
- return "False"
- case Number:
- return "Number"
- case String:
- return "String"
- case True:
- return "True"
- case JSON:
- return "JSON"
- }
-}
-
-// Result represents a json value that is returned from Get().
-type Result struct {
- // Type is the json type
- Type Type
- // Raw is the raw json
- Raw string
- // Str is the json string
- Str string
- // Num is the json number
- Num float64
- // Index of raw value in original json, zero means index unknown
- Index int
-}
-
-// String returns a string representation of the value.
-func (t Result) String() string {
- switch t.Type {
- default:
- return "null"
- case False:
- return "false"
- case Number:
- return strconv.FormatFloat(t.Num, 'f', -1, 64)
- case String:
- return t.Str
- case JSON:
- return t.Raw
- case True:
- return "true"
- }
-}
-
-// Bool returns an boolean representation.
-func (t Result) Bool() bool {
- switch t.Type {
- default:
- return false
- case True:
- return true
- case String:
- return t.Str != "" && t.Str != "0"
- case Number:
- return t.Num != 0
- }
-}
-
-// Int returns an integer representation.
-func (t Result) Int() int64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := strconv.ParseInt(t.Str, 10, 64)
- return n
- case Number:
- return int64(t.Num)
- }
-}
-
-// Uint returns an unsigned integer representation.
-func (t Result) Uint() uint64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := strconv.ParseUint(t.Str, 10, 64)
- return n
- case Number:
- return uint64(t.Num)
- }
-}
-
-// Float returns an float64 representation.
-func (t Result) Float() float64 {
- switch t.Type {
- default:
- return 0
- case True:
- return 1
- case String:
- n, _ := strconv.ParseFloat(t.Str, 64)
- return n
- case Number:
- return t.Num
- }
-}
-
-// Array 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.
-func (t Result) Array() []Result {
- if !t.Exists() {
- return nil
- }
- if t.Type != JSON {
- return []Result{t}
- }
- r := t.arrayOrMap('[', false)
- return r.a
-}
-
-// ForEach iterates through values.
-// If the result represents a non-existent value, then no values will be iterated.
-// If the result is an Object, the iterator will pass the key and value of each item.
-// If the result is an Array, the iterator will only pass the value of each item.
-// If the result is not a JSON array or object, the iterator will pass back one value equal to the result.
-func (t Result) ForEach(iterator func(key, value Result) bool) {
- if !t.Exists() {
- return
- }
- if t.Type != JSON {
- iterator(Result{}, t)
- return
- }
- json := t.Raw
- var keys bool
- var i int
- var key, value Result
- for ; i < len(json); i++ {
- if json[i] == '{' {
- i++
- key.Type = String
- keys = true
- break
- } else if json[i] == '[' {
- i++
- break
- }
- if json[i] > ' ' {
- return
- }
- }
- var str string
- var vesc bool
- var ok bool
- for ; i < len(json); i++ {
- if keys {
- if json[i] != '"' {
- continue
- }
- s := i
- i, str, vesc, ok = parseString(json, i+1)
- if !ok {
- return
- }
- if vesc {
- key.Str = unescape(str[1 : len(str)-1])
- } else {
- key.Str = str[1 : len(str)-1]
- }
- key.Raw = str
- key.Index = s
- }
- for ; i < len(json); i++ {
- if json[i] <= ' ' || json[i] == ',' || json[i] == ':' {
- continue
- }
- break
- }
- s := i
- i, value, ok = parseAny(json, i, true)
- if !ok {
- return
- }
- value.Index = s
- if !iterator(key, value) {
- return
- }
- }
-}
-
-// Map returns back an map of values. The result should be a JSON array.
-func (t Result) Map() map[string]Result {
- if t.Type != JSON {
- return map[string]Result{}
- }
- r := t.arrayOrMap('{', false)
- return r.o
-}
-
-// Get searches result for the specified path.
-// The result should be a JSON array or object.
-func (t Result) Get(path string) Result {
- return Get(t.Raw, path)
-}
-
-type arrayOrMapResult struct {
- a []Result
- ai []interface{}
- o map[string]Result
- oi map[string]interface{}
- vc byte
-}
-
-func (t Result) arrayOrMap(vc byte, valueize bool) (r arrayOrMapResult) {
- var json = t.Raw
- var i int
- var value Result
- var count int
- var key Result
- if vc == 0 {
- for ; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- r.vc = json[i]
- i++
- break
- }
- if json[i] > ' ' {
- goto end
- }
- }
- } else {
- for ; i < len(json); i++ {
- if json[i] == vc {
- i++
- break
- }
- if json[i] > ' ' {
- goto end
- }
- }
- r.vc = vc
- }
- if r.vc == '{' {
- if valueize {
- r.oi = make(map[string]interface{})
- } else {
- r.o = make(map[string]Result)
- }
- } else {
- if valueize {
- r.ai = make([]interface{}, 0)
- } else {
- r.a = make([]Result, 0)
- }
- }
- for ; i < len(json); i++ {
- if json[i] <= ' ' {
- continue
- }
- // get next value
- if json[i] == ']' || json[i] == '}' {
- break
- }
- switch json[i] {
- default:
- if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- } else {
- continue
- }
- case '{', '[':
- value.Type = JSON
- value.Raw = squash(json[i:])
- case 'n':
- value.Type = Null
- value.Raw = tolit(json[i:])
- case 't':
- value.Type = True
- value.Raw = tolit(json[i:])
- case 'f':
- value.Type = False
- value.Raw = tolit(json[i:])
- case '"':
- value.Type = String
- value.Raw, value.Str = tostr(json[i:])
- }
- i += len(value.Raw) - 1
-
- if r.vc == '{' {
- if count%2 == 0 {
- key = value
- } else {
- if valueize {
- r.oi[key.Str] = value.Value()
- } else {
- r.o[key.Str] = value
- }
- }
- count++
- } else {
- if valueize {
- r.ai = append(r.ai, value.Value())
- } else {
- r.a = append(r.a, value)
- }
- }
- }
-end:
- return
-}
-
-// Parse parses the json and returns a result.
-func Parse(json string) Result {
- var value Result
- for i := 0; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- value.Type = JSON
- value.Raw = json[i:] // just take the entire raw
- break
- }
- if json[i] <= ' ' {
- continue
- }
- switch json[i] {
- default:
- if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {
- value.Type = Number
- value.Raw, value.Num = tonum(json[i:])
- } else {
- return Result{}
- }
- case 'n':
- value.Type = Null
- value.Raw = tolit(json[i:])
- case 't':
- value.Type = True
- value.Raw = tolit(json[i:])
- case 'f':
- value.Type = False
- value.Raw = tolit(json[i:])
- case '"':
- value.Type = String
- value.Raw, value.Str = tostr(json[i:])
- }
- break
- }
- return value
-}
-
-// ParseBytes parses the json and returns a result.
-// If working with bytes, this method preferred over Parse(string(data))
-func ParseBytes(json []byte) Result {
- return Parse(string(json))
-}
-
-func squash(json string) string {
- // expects that the lead character is a '[' or '{'
- // squash the value, ignoring all nested arrays and objects.
- // the first '[' or '{' has already been read
- depth := 1
- for i := 1; i < len(json); i++ {
- if json[i] >= '"' && json[i] <= '}' {
- switch json[i] {
- case '"':
- i++
- s2 := i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > s2-1; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- case '{', '[':
- depth++
- case '}', ']':
- depth--
- if depth == 0 {
- return json[:i+1]
- }
- }
- }
- }
- return json
-}
-
-func tonum(json string) (raw string, num float64) {
- for i := 1; i < len(json); i++ {
- // less than dash might have valid characters
- if json[i] <= '-' {
- if json[i] <= ' ' || json[i] == ',' {
- // break on whitespace and comma
- raw = json[:i]
- num, _ = strconv.ParseFloat(raw, 64)
- return
- }
- // could be a '+' or '-'. let's assume so.
- continue
- }
- if json[i] < ']' {
- // probably a valid number
- continue
- }
- if json[i] == 'e' || json[i] == 'E' {
- // allow for exponential numbers
- continue
- }
- // likely a ']' or '}'
- raw = json[:i]
- num, _ = strconv.ParseFloat(raw, 64)
- return
- }
- raw = json
- num, _ = strconv.ParseFloat(raw, 64)
- return
-}
-
-func tolit(json string) (raw string) {
- for i := 1; i < len(json); i++ {
- if json[i] <= 'a' || json[i] >= 'z' {
- return json[:i]
- }
- }
- return json
-}
-
-func tostr(json string) (raw string, str string) {
- // expects that the lead character is a '"'
- for i := 1; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- return json[:i+1], json[1:i]
- }
- if json[i] == '\\' {
- i++
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- var ret string
- if i+1 < len(json) {
- ret = json[:i+1]
- } else {
- ret = json[:i]
- }
- return ret, unescape(json[1:i])
- }
- }
- return json, json[1:]
-}
-
-// Exists returns true if value exists.
-//
-// if gjson.Get(json, "name.last").Exists(){
-// println("value exists")
-// }
-func (t Result) Exists() bool {
- return t.Type != Null || len(t.Raw) != 0
-}
-
-// Value returns one of these types:
-//
-// bool, for JSON booleans
-// float64, for JSON numbers
-// Number, for JSON numbers
-// string, for JSON string literals
-// nil, for JSON null
-//
-func (t Result) Value() interface{} {
- if t.Type == String {
- return t.Str
- }
- switch t.Type {
- default:
- return nil
- case False:
- return false
- case Number:
- return t.Num
- case JSON:
- r := t.arrayOrMap(0, true)
- if r.vc == '{' {
- return r.oi
- } else if r.vc == '[' {
- return r.ai
- }
- return nil
- case True:
- return true
- }
-}
-
-func parseString(json string, i int) (int, string, bool, bool) {
- var s = i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- return i + 1, json[s-1 : i+1], false, true
- }
- if json[i] == '\\' {
- i++
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- return i + 1, json[s-1 : i+1], true, true
- }
- }
- break
- }
- }
- return i, json[s-1:], false, false
-}
-
-func parseNumber(json string, i int) (int, string) {
- var s = i
- i++
- for ; i < len(json); i++ {
- if json[i] <= ' ' || json[i] == ',' || json[i] == ']' || json[i] == '}' {
- return i, json[s:i]
- }
- }
- return i, json[s:]
-}
-
-func parseLiteral(json string, i int) (int, string) {
- var s = i
- i++
- for ; i < len(json); i++ {
- if json[i] < 'a' || json[i] > 'z' {
- return i, json[s:i]
- }
- }
- return i, json[s:]
-}
-
-type arrayPathResult struct {
- part string
- path string
- more bool
- alogok bool
- arrch bool
- alogkey string
- query struct {
- on bool
- path string
- op string
- value string
- all bool
- }
-}
-
-func parseArrayPath(path string) (r arrayPathResult) {
- for i := 0; i < len(path); i++ {
- if path[i] == '.' {
- r.part = path[:i]
- r.path = path[i+1:]
- r.more = true
- return
- }
- if path[i] == '#' {
- r.arrch = true
- if i == 0 && len(path) > 1 {
- if path[1] == '.' {
- r.alogok = true
- r.alogkey = path[2:]
- r.path = path[:1]
- } else if path[1] == '[' {
- r.query.on = true
- // query
- 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] == ']' {
- 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] == '=' {
- 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] == ']' {
- 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
- }
- }
- }
- continue
- }
- }
- r.part = path
- r.path = ""
- return
-}
-
-type objectPathResult struct {
- part string
- path string
- wild bool
- more bool
-}
-
-func parseObjectPath(path string) (r objectPathResult) {
- for i := 0; i < len(path); i++ {
- if path[i] == '.' {
- r.part = path[:i]
- r.path = path[i+1:]
- r.more = true
- return
- }
- if path[i] == '*' || path[i] == '?' {
- r.wild = true
- continue
- }
- if path[i] == '\\' {
- // go into escape mode. this is a slower path that
- // strips off the escape character from the part.
- epart := []byte(path[:i])
- i++
- if i < len(path) {
- epart = append(epart, path[i])
- i++
- for ; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- if i < len(path) {
- epart = append(epart, path[i])
- }
- continue
- } else if path[i] == '.' {
- r.part = string(epart)
- r.path = path[i+1:]
- r.more = true
- return
- } else if path[i] == '*' || path[i] == '?' {
- r.wild = true
- }
- epart = append(epart, path[i])
- }
- }
- // append the last part
- r.part = string(epart)
- return
- }
- }
- r.part = path
- return
-}
-
-func parseSquash(json string, i int) (int, string) {
- // expects that the lead character is a '[' or '{'
- // squash the value, ignoring all nested arrays and objects.
- // the first '[' or '{' has already been read
- s := i
- i++
- depth := 1
- for ; i < len(json); i++ {
- if json[i] >= '"' && json[i] <= '}' {
- switch json[i] {
- case '"':
- i++
- s2 := i
- for ; i < len(json); i++ {
- if json[i] > '\\' {
- continue
- }
- if json[i] == '"' {
- // look for an escaped slash
- if json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > s2-1; j-- {
- if json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- break
- }
- }
- case '{', '[':
- depth++
- case '}', ']':
- depth--
- if depth == 0 {
- i++
- return i, json[s:i]
- }
- }
- }
- }
- return i, json[s:]
-}
-
-func parseObject(c *parseContext, i int, path string) (int, bool) {
- var pmatch, kesc, vesc, ok, hit bool
- var key, val string
- rp := parseObjectPath(path)
- for i < len(c.json) {
- for ; i < len(c.json); i++ {
- if c.json[i] == '"' {
- // parse_key_string
- // this is slightly different from getting s string value
- // because we don't need the outer quotes.
- i++
- var s = i
- for ; i < len(c.json); i++ {
- if c.json[i] > '\\' {
- continue
- }
- if c.json[i] == '"' {
- i, key, kesc, ok = i+1, c.json[s:i], false, true
- goto parse_key_string_done
- }
- if c.json[i] == '\\' {
- i++
- for ; i < len(c.json); i++ {
- if c.json[i] > '\\' {
- continue
- }
- if c.json[i] == '"' {
- // look for an escaped slash
- if c.json[i-1] == '\\' {
- n := 0
- for j := i - 2; j > 0; j-- {
- if c.json[j] != '\\' {
- break
- }
- n++
- }
- if n%2 == 0 {
- continue
- }
- }
- i, key, kesc, ok = i+1, c.json[s:i], true, true
- goto parse_key_string_done
- }
- }
- break
- }
- }
- i, key, kesc, ok = i, c.json[s:], false, false
- parse_key_string_done:
- break
- }
- if c.json[i] == '}' {
- return i + 1, false
- }
- }
- if !ok {
- return i, false
- }
- if rp.wild {
- if kesc {
- pmatch = match.Match(unescape(key), rp.part)
- } else {
- pmatch = match.Match(key, rp.part)
- }
- } else {
- if kesc {
- pmatch = rp.part == unescape(key)
- } else {
- pmatch = rp.part == key
- }
- }
- hit = pmatch && !rp.more
- for ; i < len(c.json); i++ {
- switch c.json[i] {
- default:
- continue
- case '"':
- i++
- i, val, vesc, ok = parseString(c.json, i)
- if !ok {
- return i, false
- }
- if hit {
- if vesc {
- c.value.Str = unescape(val[1 : len(val)-1])
- } else {
- c.value.Str = val[1 : len(val)-1]
- }
- c.value.Raw = val
- c.value.Type = String
- return i, true
- }
- case '{':
- if pmatch && !hit {
- i, hit = parseObject(c, i+1, rp.path)
- if hit {
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '[':
- if pmatch && !hit {
- i, hit = parseArray(c, i+1, rp.path)
- if hit {
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- i, val = parseNumber(c.json, i)
- if hit {
- c.value.Raw = val
- c.value.Type = Number
- c.value.Num, _ = strconv.ParseFloat(val, 64)
- return i, true
- }
- case 't', 'f', 'n':
- vc := c.json[i]
- i, val = parseLiteral(c.json, i)
- if hit {
- c.value.Raw = val
- switch vc {
- case 't':
- c.value.Type = True
- case 'f':
- c.value.Type = False
- }
- return i, true
- }
- }
- break
- }
- }
- return i, false
-}
-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]
- }
- switch value.Type {
- case String:
- switch rp.query.op {
- case "=":
- return value.Str == rpv
- case "!=":
- return value.Str != rpv
- case "<":
- return value.Str < rpv
- case "<=":
- return value.Str <= rpv
- case ">":
- return value.Str > rpv
- case ">=":
- return value.Str >= rpv
- case "%":
- return match.Match(value.Str, rpv)
- }
- case Number:
- rpvn, _ := strconv.ParseFloat(rpv, 64)
- switch rp.query.op {
- case "=":
- return value.Num == rpvn
- case "!=":
- return value.Num == rpvn
- case "<":
- return value.Num < rpvn
- case "<=":
- return value.Num <= rpvn
- case ">":
- return value.Num > rpvn
- case ">=":
- return value.Num >= rpvn
- }
- case True:
- switch rp.query.op {
- case "=":
- return rpv == "true"
- case "!=":
- return rpv != "true"
- case ">":
- return rpv == "false"
- case ">=":
- return true
- }
- case False:
- switch rp.query.op {
- case "=":
- return rpv == "false"
- case "!=":
- return rpv != "false"
- case "<":
- return rpv == "true"
- case "<=":
- return true
- }
- }
- return false
-}
-func parseArray(c *parseContext, i int, path string) (int, bool) {
- var pmatch, vesc, ok, hit bool
- var val string
- var h int
- var alog []int
- var partidx int
- var multires []byte
- rp := parseArrayPath(path)
- if !rp.arrch {
- n, err := strconv.ParseUint(rp.part, 10, 64)
- if err != nil {
- partidx = -1
- } else {
- partidx = int(n)
- }
- }
- for i < len(c.json) {
- if !rp.arrch {
- pmatch = partidx == h
- hit = pmatch && !rp.more
- }
- h++
- if rp.alogok {
- alog = append(alog, i)
- }
- for ; i < len(c.json); i++ {
- switch c.json[i] {
- default:
- continue
- case '"':
- i++
- i, val, vesc, ok = parseString(c.json, i)
- if !ok {
- return i, false
- }
- if hit {
- if rp.alogok {
- break
- }
- if vesc {
- c.value.Str = unescape(val[1 : len(val)-1])
- } else {
- c.value.Str = val[1 : len(val)-1]
- }
- c.value.Raw = val
- c.value.Type = String
- return i, true
- }
- case '{':
- if pmatch && !hit {
- i, hit = parseObject(c, i+1, rp.path)
- if hit {
- if rp.alogok {
- break
- }
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if rp.query.on {
- res := Get(val, rp.query.path)
- if queryMatches(&rp, res) {
- if rp.more {
- res = Get(val, rp.path)
- } else {
- res = Result{Raw: val, Type: JSON}
- }
- if rp.query.all {
- if len(multires) == 0 {
- multires = append(multires, '[')
- } else {
- multires = append(multires, ',')
- }
- multires = append(multires, res.Raw...)
- } else {
- c.value = res
- return i, true
- }
- }
- } else if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '[':
- if pmatch && !hit {
- i, hit = parseArray(c, i+1, rp.path)
- if hit {
- if rp.alogok {
- break
- }
- return i, true
- }
- } else {
- i, val = parseSquash(c.json, i)
- if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = JSON
- return i, true
- }
- }
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- i, val = parseNumber(c.json, i)
- if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = Number
- c.value.Num, _ = strconv.ParseFloat(val, 64)
- return i, true
- }
- case 't', 'f', 'n':
- vc := c.json[i]
- i, val = parseLiteral(c.json, i)
- if hit {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- switch vc {
- case 't':
- c.value.Type = True
- case 'f':
- c.value.Type = False
- }
- return i, true
- }
- case ']':
- if rp.arrch && rp.part == "#" {
- if rp.alogok {
- var jsons = make([]byte, 0, 64)
- jsons = append(jsons, '[')
- for j, k := 0, 0; j < len(alog); j++ {
- res := Get(c.json[alog[j]:], rp.alogkey)
- if res.Exists() {
- if k > 0 {
- jsons = append(jsons, ',')
- }
- jsons = append(jsons, []byte(res.Raw)...)
- k++
- }
- }
- jsons = append(jsons, ']')
- c.value.Type = JSON
- c.value.Raw = string(jsons)
- return i + 1, true
- } else {
- if rp.alogok {
- break
- }
- c.value.Raw = val
- c.value.Type = Number
- c.value.Num = float64(h - 1)
- c.calcd = true
- return i + 1, true
- }
- }
- if len(multires) > 0 && !c.value.Exists() {
- c.value = Result{
- Raw: string(append(multires, ']')),
- Type: JSON,
- }
- }
- return i + 1, false
- }
- break
- }
- }
- return i, false
-}
-
-type parseContext struct {
- json string
- value Result
- calcd bool
-}
-
-// Get searches json for the specified path.
-// A path is in dot syntax, such as "name.last" or "age".
-// This function expects that the json is well-formed, and does not validate.
-// Invalid json will not panic, but it may return back unexpected results.
-// When the value is found it's returned immediately.
-//
-// A path is a series of keys searated 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 the '#' character.
-// The dot and wildcard character can be escaped with '\'.
-//
-// {
-// "name": {"first": "Tom", "last": "Anderson"},
-// "age":37,
-// "children": ["Sara","Alex","Jack"],
-// "friends": [
-// {"first": "James", "last": "Murphy"},
-// {"first": "Roger", "last": "Craig"}
-// ]
-// }
-// "name.last" >> "Anderson"
-// "age" >> 37
-// "children" >> ["Sara","Alex","Jack"]
-// "children.#" >> 3
-// "children.1" >> "Alex"
-// "child*.2" >> "Jack"
-// "c?ildren.0" >> "Sara"
-// "friends.#.first" >> ["James","Roger"]
-//
-func Get(json, path string) Result {
- var i int
- var c = &parseContext{json: json}
- for ; i < len(c.json); i++ {
- if c.json[i] == '{' {
- i++
- parseObject(c, i, path)
- break
- }
- if c.json[i] == '[' {
- i++
- parseArray(c, i, path)
- break
- }
- }
- 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)
- if c.value.Index < 0 || c.value.Index >= len(json) {
- c.value.Index = 0
- }
- }
- return c.value
-}
-func fromBytesGet(result Result) Result {
- // safely get the string headers
- rawhi := *(*reflect.StringHeader)(unsafe.Pointer(&result.Raw))
- strhi := *(*reflect.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 {
- // str is nil
- if rawh.Data == 0 {
- // raw is nil
- result.Raw = ""
- } else {
- // raw has data, safely copy the slice header to a string
- result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
- }
- result.Str = ""
- } else if rawh.Data == 0 {
- // 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 {
- // Str is a substring of Raw.
- start := int(strh.Data - 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]
- } else {
- // safely copy both the raw and str slice headers to strings
- result.Raw = string(*(*[]byte)(unsafe.Pointer(&rawh)))
- result.Str = string(*(*[]byte)(unsafe.Pointer(&strh)))
- }
- return result
-}
-
-// GetBytes searches json for the specified path.
-// If working with bytes, this method preferred over Get(string(data), path)
-func GetBytes(json []byte, path string) Result {
- var result Result
- if json != nil {
- // unsafe cast to string
- result = Get(*(*string)(unsafe.Pointer(&json)), path)
- result = fromBytesGet(result)
- }
- return result
-}
-
-// unescape unescapes a string
-func unescape(json string) string { //, error) {
- var str = make([]byte, 0, len(json))
- for i := 0; i < len(json); i++ {
- switch {
- default:
- str = append(str, json[i])
- case json[i] < ' ':
- return "" //, errors.New("invalid character in string")
- case json[i] == '\\':
- i++
- if i >= len(json) {
- return "" //, errors.New("invalid escape sequence")
- }
- switch json[i] {
- default:
- return "" //, errors.New("invalid escape sequence")
- case '\\':
- str = append(str, '\\')
- case '/':
- str = append(str, '/')
- case 'b':
- str = append(str, '\b')
- case 'f':
- str = append(str, '\f')
- case 'n':
- str = append(str, '\n')
- case 'r':
- str = append(str, '\r')
- case 't':
- str = append(str, '\t')
- case '"':
- str = append(str, '"')
- case 'u':
- if i+5 > len(json) {
- return "" //, errors.New("invalid escape sequence")
- }
- i++
- // extract the codepoint
- var code int
- for j := i; j < i+4; j++ {
- switch {
- default:
- return "" //, errors.New("invalid escape sequence")
- case json[j] >= '0' && json[j] <= '9':
- code += (int(json[j]) - '0') << uint(12-(j-i)*4)
- case json[j] >= 'a' && json[j] <= 'f':
- code += (int(json[j]) - 'a' + 10) << uint(12-(j-i)*4)
- case json[j] >= 'a' && json[j] <= 'f':
- code += (int(json[j]) - 'a' + 10) << uint(12-(j-i)*4)
- }
- }
- str = append(str, []byte(string(code))...)
- i += 3 // only 3 because we will increment on the for-loop
- }
- }
- }
- return string(str) //, nil
-}
-
-// Less return true if a token is less than another token.
-// The caseSensitive paramater is used when the tokens are Strings.
-// The order when comparing two different type is:
-//
-// Null < False < Number < String < True < JSON
-//
-func (t Result) Less(token Result, caseSensitive bool) bool {
- if t.Type < token.Type {
- return true
- }
- if t.Type > token.Type {
- return false
- }
- if t.Type == String {
- if caseSensitive {
- return t.Str < token.Str
- }
- return stringLessInsensitive(t.Str, token.Str)
- }
- if t.Type == Number {
- return t.Num < token.Num
- }
- return t.Raw < token.Raw
-}
-
-func stringLessInsensitive(a, b string) bool {
- for i := 0; i < len(a) && i < len(b); i++ {
- if a[i] >= 'A' && a[i] <= 'Z' {
- if b[i] >= 'A' && b[i] <= 'Z' {
- // both are uppercase, do nothing
- if a[i] < b[i] {
- return true
- } else if a[i] > b[i] {
- return false
- }
- } else {
- // a is uppercase, convert a to lowercase
- if a[i]+32 < b[i] {
- return true
- } else if a[i]+32 > b[i] {
- return false
- }
- }
- } else if b[i] >= 'A' && b[i] <= 'Z' {
- // b is uppercase, convert b to lowercase
- if a[i] < b[i]+32 {
- return true
- } else if a[i] > b[i]+32 {
- return false
- }
- } else {
- // neither are uppercase
- if a[i] < b[i] {
- return true
- } else if a[i] > b[i] {
- return false
- }
- }
- }
- return len(a) < len(b)
-}
-
-// parseAny parses the next value from a json string.
-// A Result is returned when the hit param is set.
-// The return values are (i int, res Result, ok bool)
-func parseAny(json string, i int, hit bool) (int, Result, bool) {
- var res Result
- var val string
- for ; i < len(json); i++ {
- if json[i] == '{' || json[i] == '[' {
- i, val = parseSquash(json, i)
- if hit {
- res.Raw = val
- res.Type = JSON
- }
- return i, res, true
- }
- if json[i] <= ' ' {
- continue
- }
- switch json[i] {
- case '"':
- i++
- var vesc bool
- var ok bool
- i, val, vesc, ok = parseString(json, i)
- if !ok {
- return i, res, false
- }
- if hit {
- res.Type = String
- res.Raw = val
- if vesc {
- res.Str = unescape(val[1 : len(val)-1])
- } else {
- res.Str = val[1 : len(val)-1]
- }
- }
- return i, res, true
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- i, val = parseNumber(json, i)
- if hit {
- res.Raw = val
- res.Type = Number
- res.Num, _ = strconv.ParseFloat(val, 64)
- }
- return i, res, true
- case 't', 'f', 'n':
- vc := json[i]
- i, val = parseLiteral(json, i)
- if hit {
- res.Raw = val
- switch vc {
- case 't':
- res.Type = True
- case 'f':
- res.Type = False
- }
- return i, res, true
- }
- }
- }
- return i, res, false
-}
-
-var ( // used for testing
- testWatchForFallback bool
- testLastWasFallback bool
-)
-
-// areSimplePaths returns true if all the paths are simple enough
-// to parse quickly for GetMany(). Allows alpha-numeric, dots,
-// underscores, and the dollar sign. It does not allow non-alnum,
-// escape characters, or keys which start with a numbers.
-// For example:
-// "name.last" == OK
-// "user.id0" == OK
-// "user.ID" == OK
-// "user.first_name" == OK
-// "user.firstName" == OK
-// "user.0item" == BAD
-// "user.#id" == BAD
-// "user\.name" == BAD
-func areSimplePaths(paths []string) bool {
- for _, path := range paths {
- var fi int // first key index, for keys with numeric prefix
- for i := 0; i < len(path); i++ {
- if path[i] >= 'a' && path[i] <= 'z' {
- // a-z is likely to be the highest frequency charater.
- continue
- }
- if path[i] == '.' {
- fi = i + 1
- continue
- }
- if path[i] >= 'A' && path[i] <= 'Z' {
- continue
- }
- if path[i] == '_' || path[i] == '$' {
- continue
- }
- if i > fi && path[i] >= '0' && path[i] <= '9' {
- continue
- }
- return false
- }
- }
- return true
-}
-
-// 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.
-func GetMany(json string, paths ...string) []Result {
- if len(paths) < 4 {
- if testWatchForFallback {
- testLastWasFallback = false
- }
- switch len(paths) {
- case 0:
- // return nil when no paths are specified.
- return nil
- case 1:
- return []Result{Get(json, paths[0])}
- case 2:
- return []Result{Get(json, paths[0]), Get(json, paths[1])}
- case 3:
- return []Result{Get(json, paths[0]), Get(json, paths[1]), Get(json, paths[2])}
- }
- }
- var results []Result
- var ok bool
- var i int
- if len(paths) > 512 {
- // we can only support up to 512 paths. Is that too many?
- goto fallback
- }
- if !areSimplePaths(paths) {
- // If there is even one path that is not considered "simple" then
- // we need to use the fallback method.
- goto fallback
- }
- // locate the object token.
- for ; i < len(json); i++ {
- if json[i] == '{' {
- i++
- break
- }
- if json[i] <= ' ' {
- continue
- }
- goto fallback
- }
- // use the call function table.
- if len(paths) <= 8 {
- results, ok = getMany8(json, i, paths)
- } else if len(paths) <= 16 {
- results, ok = getMany16(json, i, paths)
- } else if len(paths) <= 32 {
- results, ok = getMany32(json, i, paths)
- } else if len(paths) <= 64 {
- results, ok = getMany64(json, i, paths)
- } else if len(paths) <= 128 {
- results, ok = getMany128(json, i, paths)
- } else if len(paths) <= 256 {
- results, ok = getMany256(json, i, paths)
- } else if len(paths) <= 512 {
- results, ok = getMany512(json, i, paths)
- }
- if !ok {
- // there was some fault while parsing. we should try the
- // fallback method. This could result in performance
- // degregation in some cases.
- goto fallback
- }
- if testWatchForFallback {
- testLastWasFallback = false
- }
- return results
-fallback:
- results = results[:0]
- for i := 0; i < len(paths); i++ {
- results = append(results, Get(json, paths[i]))
- }
- if testWatchForFallback {
- testLastWasFallback = true
- }
- return results
-}
-
-// GetManyBytes searches json for the specified path.
-// If working with bytes, this method preferred over
-// GetMany(string(data), paths...)
-func GetManyBytes(json []byte, paths ...string) []Result {
- if json == nil {
- return GetMany("", paths...)
- }
- results := GetMany(*(*string)(unsafe.Pointer(&json)), paths...)
- for i := range results {
- results[i] = fromBytesGet(results[i])
- }
- return results
-}
-
-// parseGetMany parses a json object for keys that match against the callers
-// paths. It's a best-effort attempt and quickly locating and assigning the
-// values to the []Result array. If there are failures such as bad json, or
-// invalid input paths, or too much recursion, the function will exit with a
-// return value of 'false'.
-func parseGetMany(
- json string, i int,
- level uint, kplen int,
- paths []string, completed []bool, matches []uint64, results []Result,
-) (int, bool) {
- if level > 62 {
- // The recursion level is limited because the matches []uint64
- // array cannot handle more the 64-bits.
- return i, false
- }
- // At this point the last character read was a '{'.
- // Read all object keys and try to match against the paths.
- var key string
- var val string
- var vesc, ok bool
-next_key:
- for ; i < len(json); i++ {
- if json[i] == '"' {
- // read the key
- i, val, vesc, ok = parseString(json, i+1)
- if !ok {
- return i, false
- }
- if vesc {
- // the value is escaped
- key = unescape(val[1 : len(val)-1])
- } else {
- // just a plain old ascii key
- key = val[1 : len(val)-1]
- }
- var hasMatch bool
- var parsedVal bool
- var valOrgIndex int
- var valPathIndex int
- for j := 0; j < len(key); j++ {
- if key[j] == '.' {
- // we need to look for keys with dot and ignore them.
- if i, _, ok = parseAny(json, i, false); !ok {
- return i, false
- }
- continue next_key
- }
- }
- var usedPaths int
- // loop through paths and look for matches
- for j := 0; j < len(paths); j++ {
- if completed[j] {
- usedPaths++
- // ignore completed paths
- continue
- }
- if level > 0 && (matches[j]>>(level-1))&1 == 0 {
- // ignore unmatched paths
- usedPaths++
- continue
- }
-
- // try to match the key to the path
- // this is spaghetti code but the idea is to minimize
- // calls and variable assignments when comparing the
- // key to paths
- if len(paths[j])-kplen >= len(key) {
- i, k := kplen, 0
- for ; k < len(key); k, i = k+1, i+1 {
- if key[k] != paths[j][i] {
- // no match
- goto nomatch
- }
- }
- if i < len(paths[j]) {
- if paths[j][i] == '.' {
- // matched, but there still more keys in the path
- goto match_not_atend
- }
- }
- // matched and at the end of the path
- goto match_atend
- }
- // no match, jump to the nomatch label
- goto nomatch
- match_atend:
- // found a match
- // at the end of the path. we must take the value.
- usedPaths++
- if !parsedVal {
- // the value has not been parsed yet. let's do so.
- valOrgIndex = i // keep track of the current position.
- i, results[j], ok = parseAny(json, i, true)
- if !ok {
- return i, false
- }
- parsedVal = true
- valPathIndex = j
- } else {
- results[j] = results[valPathIndex]
- }
- // mark as complete
- completed[j] = true
- // jump over the match_not_atend label
- goto nomatch
- match_not_atend:
- // found a match
- // still in the middle of the path.
- usedPaths++
- // mark the path as matched
- matches[j] |= 1 << level
- if !hasMatch {
- hasMatch = true
- }
- nomatch: // noop label
- }
-
- if !parsedVal {
- if hasMatch {
- // we found a match and the value has not been parsed yet.
- // let's find out if the next value type is an object.
- for ; i < len(json); i++ {
- if json[i] <= ' ' || json[i] == ':' {
- continue
- }
- break
- }
- if i < len(json) {
- if json[i] == '{' {
- // it's an object. let's go deeper
- i, ok = parseGetMany(json, i+1, level+1, kplen+len(key)+1, paths, completed, matches, results)
- if !ok {
- return i, false
- }
- } else {
- // not an object. just parse and ignore.
- if i, _, ok = parseAny(json, i, false); !ok {
- return i, false
- }
- }
- }
- } else {
- // Since there was no matches we can just parse the value and
- // ignore the result.
- if i, _, ok = parseAny(json, i, false); !ok {
- return i, false
- }
- }
- } else if hasMatch && len(results[valPathIndex].Raw) > 0 && results[valPathIndex].Raw[0] == '{' {
- // The value was already parsed and the value type is an object.
- // Rewind the json index and let's parse deeper.
- i = valOrgIndex
- for ; i < len(json); i++ {
- if json[i] == '{' {
- break
- }
- }
- i, ok = parseGetMany(json, i+1, level+1, kplen+len(key)+1, paths, completed, matches, results)
- if !ok {
- return i, false
- }
- }
- if usedPaths == len(paths) {
- // all paths have been used, either completed or matched.
- // we should stop parsing this object to save CPU cycles.
- if level > 0 && i < len(json) {
- i, _ = parseSquash(json, i)
- }
- return i, true
- }
- } else if json[i] == '}' {
- // reached the end of the object. end it here.
- return i + 1, true
- }
- }
- return i, true
-}
-
-// Call table for GetMany. Using an isolated function allows for allocating
-// arrays with know capacities on the stack, as opposed to dynamically
-// allocating on the heap. This can provide a tremendous performance boost
-// by avoiding the GC.
-func getMany8(json string, i int, paths []string) ([]Result, bool) {
- const max = 8
- var completed = make([]bool, 0, max)
- var matches = make([]uint64, 0, max)
- var results = make([]Result, 0, max)
- completed = completed[0:len(paths):max]
- matches = matches[0:len(paths):max]
- results = results[0:len(paths):max]
- _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
- return results, ok
-}
-func getMany16(json string, i int, paths []string) ([]Result, bool) {
- const max = 16
- var completed = make([]bool, 0, max)
- var matches = make([]uint64, 0, max)
- var results = make([]Result, 0, max)
- completed = completed[0:len(paths):max]
- matches = matches[0:len(paths):max]
- results = results[0:len(paths):max]
- _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
- return results, ok
-}
-func getMany32(json string, i int, paths []string) ([]Result, bool) {
- const max = 32
- var completed = make([]bool, 0, max)
- var matches = make([]uint64, 0, max)
- var results = make([]Result, 0, max)
- completed = completed[0:len(paths):max]
- matches = matches[0:len(paths):max]
- results = results[0:len(paths):max]
- _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
- return results, ok
-}
-func getMany64(json string, i int, paths []string) ([]Result, bool) {
- const max = 64
- var completed = make([]bool, 0, max)
- var matches = make([]uint64, 0, max)
- var results = make([]Result, 0, max)
- completed = completed[0:len(paths):max]
- matches = matches[0:len(paths):max]
- results = results[0:len(paths):max]
- _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
- return results, ok
-}
-func getMany128(json string, i int, paths []string) ([]Result, bool) {
- const max = 128
- var completed = make([]bool, 0, max)
- var matches = make([]uint64, 0, max)
- var results = make([]Result, 0, max)
- completed = completed[0:len(paths):max]
- matches = matches[0:len(paths):max]
- results = results[0:len(paths):max]
- _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
- return results, ok
-}
-func getMany256(json string, i int, paths []string) ([]Result, bool) {
- const max = 256
- var completed = make([]bool, 0, max)
- var matches = make([]uint64, 0, max)
- var results = make([]Result, 0, max)
- completed = completed[0:len(paths):max]
- matches = matches[0:len(paths):max]
- results = results[0:len(paths):max]
- _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
- return results, ok
-}
-func getMany512(json string, i int, paths []string) ([]Result, bool) {
- const max = 512
- var completed = make([]bool, 0, max)
- var matches = make([]uint64, 0, max)
- var results = make([]Result, 0, max)
- completed = completed[0:len(paths):max]
- matches = matches[0:len(paths):max]
- results = results[0:len(paths):max]
- _, ok := parseGetMany(json, i, 0, 0, paths, completed, matches, results)
- return results, ok
-}
diff --git a/cmd/ponzu/vendor/github.com/tidwall/gjson/logo.png b/cmd/ponzu/vendor/github.com/tidwall/gjson/logo.png
deleted file mode 100644
index 17a8bbe..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/gjson/logo.png
+++ /dev/null
Binary files differ
diff --git a/cmd/ponzu/vendor/github.com/tidwall/sjson/.travis.yml b/cmd/ponzu/vendor/github.com/tidwall/sjson/.travis.yml
deleted file mode 100644
index 4f2ee4d..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/sjson/.travis.yml
+++ /dev/null
@@ -1 +0,0 @@
-language: go
diff --git a/cmd/ponzu/vendor/github.com/tidwall/sjson/LICENSE b/cmd/ponzu/vendor/github.com/tidwall/sjson/LICENSE
deleted file mode 100644
index 89593c7..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/sjson/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 Josh Baker
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
diff --git a/cmd/ponzu/vendor/github.com/tidwall/sjson/README.md b/cmd/ponzu/vendor/github.com/tidwall/sjson/README.md
deleted file mode 100644
index 1a7c5c4..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/sjson/README.md
+++ /dev/null
@@ -1,278 +0,0 @@
-<p align="center">
-<img
- src="logo.png"
- width="240" height="78" border="0" alt="SJSON">
-<br>
-<a href="https://travis-ci.org/tidwall/sjson"><img src="https://img.shields.io/travis/tidwall/sjson.svg?style=flat-square" alt="Build Status"></a>
-<a href="https://godoc.org/github.com/tidwall/sjson"><img src="https://img.shields.io/badge/api-reference-blue.svg?style=flat-square" alt="GoDoc"></a>
-</p>
-
-<p align="center">set a json value quickly</a></p>
-
-SJSON is a Go package that provides a [very fast](#performance) and simple way to set a value in a json document. The purpose for this library is to provide efficient json updating for the [SummitDB](https://github.com/tidwall/summitdb) project.
-For quickly retrieving json values check out [GJSON](https://github.com/tidwall/gjson).
-
-For a command line interface check out [JSONed](https://github.com/tidwall/jsoned).
-
-Getting Started
-===============
-
-Installing
-----------
-
-To start using SJSON, install Go and run `go get`:
-
-```sh
-$ go get -u github.com/tidwall/sjson
-```
-
-This will retrieve the library.
-
-Set a value
------------
-Set sets the value for the specified path.
-A path is in dot syntax, such as "name.last" or "age".
-This function expects that the json is well-formed and validated.
-Invalid json will not panic, but it may return back unexpected results.
-Invalid paths may return an error.
-
-```go
-package main
-
-import "github.com/tidwall/sjson"
-
-const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
-
-func main() {
- value, _ := sjson.Set(json, "name.last", "Anderson")
- println(value)
-}
-```
-
-This will print:
-
-```json
-{"name":{"first":"Janet","last":"Anderson"},"age":47}
-```
-
-Path syntax
------------
-
-A path is a series of keys separated by a dot.
-The dot and colon characters can be escaped with '\'.
-
-```json
-{
- "name": {"first": "Tom", "last": "Anderson"},
- "age":37,
- "children": ["Sara","Alex","Jack"],
- "fav.movie": "Deer Hunter",
- "friends": [
- {"first": "James", "last": "Murphy"},
- {"first": "Roger", "last": "Craig"}
- ]
-}
-```
-```
-"name.last" >> "Anderson"
-"age" >> 37
-"children.1" >> "Alex"
-"friends.1.last" >> "Craig"
-```
-
-The `-1` key can be used to append a value to an existing array:
-
-```
-"children.-1" >> appends a new value to the end of the children array
-```
-
-Normally number keys are used to modify arrays, but it's possible to force a numeric object key by using the colon character:
-
-```json
-{
- "users":{
- "2313":{"name":"Sara"},
- "7839":{"name":"Andy"}
- }
-}
-```
-
-A colon path would look like:
-
-```
-"users.:2313.name" >> "Sara"
-```
-
-Supported types
----------------
-
-Pretty much any type is supported:
-
-```go
-sjson.Set(`{"key":true}`, "key", nil)
-sjson.Set(`{"key":true}`, "key", false)
-sjson.Set(`{"key":true}`, "key", 1)
-sjson.Set(`{"key":true}`, "key", 10.5)
-sjson.Set(`{"key":true}`, "key", "hello")
-sjson.Set(`{"key":true}`, "key", map[string]interface{}{"hello":"world"})
-```
-
-When a type is not recognized, SJSON will fallback to the `encoding/json` Marshaller.
-
-
-Examples
---------
-
-Set a value from empty document:
-```go
-value, _ := sjson.Set("", "name", "Tom")
-println(value)
-
-// Output:
-// {"name":"Tom"}
-```
-
-Set a nested value from empty document:
-```go
-value, _ := sjson.Set("", "name.last", "Anderson")
-println(value)
-
-// Output:
-// {"name":{"last":"Anderson"}}
-```
-
-Set a new value:
-```go
-value, _ := sjson.Set(`{"name":{"last":"Anderson"}}`, "name.first", "Sara")
-println(value)
-
-// Output:
-// {"name":{"first":"Sara","last":"Anderson"}}
-```
-
-Update an existing value:
-```go
-value, _ := sjson.Set(`{"name":{"last":"Anderson"}}`, "name.last", "Smith")
-println(value)
-
-// Output:
-// {"name":{"last":"Smith"}}
-```
-
-Set a new array value:
-```go
-value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.2", "Sara")
-println(value)
-
-// Output:
-// {"friends":["Andy","Carol","Sara"]
-```
-
-Append an array value by using the `-1` key in a path:
-```go
-value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.-1", "Sara")
-println(value)
-
-// Output:
-// {"friends":["Andy","Carol","Sara"]
-```
-
-Append an array value that is past the end:
-```go
-value, _ := sjson.Set(`{"friends":["Andy","Carol"]}`, "friends.4", "Sara")
-println(value)
-
-// Output:
-// {"friends":["Andy","Carol",null,null,"Sara"]
-```
-
-Delete a value:
-```go
-value, _ := sjson.Delete(`{"name":{"first":"Sara","last":"Anderson"}}`, "name.first")
-println(value)
-
-// Output:
-// {"name":{"last":"Anderson"}}
-```
-
-Delete an array value:
-```go
-value, _ := sjson.Delete(`{"friends":["Andy","Carol"]}`, "friends.1")
-println(value)
-
-// Output:
-// {"friends":["Andy"]}
-```
-
-Delete the last array value:
-```go
-value, _ := sjson.Delete(`{"friends":["Andy","Carol"]}`, "friends.-1")
-println(value)
-
-// Output:
-// {"friends":["Andy"]}
-```
-
-## Performance
-
-Benchmarks of SJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/),
-[ffjson](https://github.com/pquerna/ffjson),
-[EasyJSON](https://github.com/mailru/easyjson),
-and [Gabs](https://github.com/Jeffail/gabs)
-
-```
-Benchmark_SJSON-8 3000000 805 ns/op 1077 B/op 3 allocs/op
-Benchmark_SJSON_ReplaceInPlace-8 3000000 449 ns/op 0 B/op 0 allocs/op
-Benchmark_JSON_Map-8 300000 21236 ns/op 6392 B/op 150 allocs/op
-Benchmark_JSON_Struct-8 300000 14691 ns/op 1789 B/op 24 allocs/op
-Benchmark_Gabs-8 300000 21311 ns/op 6752 B/op 150 allocs/op
-Benchmark_FFJSON-8 300000 17673 ns/op 3589 B/op 47 allocs/op
-Benchmark_EasyJSON-8 1500000 3119 ns/op 1061 B/op 13 allocs/op
-```
-
-JSON document used:
-
-```json
-{
- "widget": {
- "debug": "on",
- "window": {
- "title": "Sample Konfabulator Widget",
- "name": "main_window",
- "width": 500,
- "height": 500
- },
- "image": {
- "src": "Images/Sun.png",
- "hOffset": 250,
- "vOffset": 250,
- "alignment": "center"
- },
- "text": {
- "data": "Click Here",
- "size": 36,
- "style": "bold",
- "vOffset": 100,
- "alignment": "center",
- "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
- }
- }
-}
-```
-
-Each operation was rotated though one of the following search paths:
-
-```
-widget.window.name
-widget.image.hOffset
-widget.text.onMouseUp
-```
-
-*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7.*
-
-## Contact
-Josh Baker [@tidwall](http://twitter.com/tidwall)
-
-## License
-
-SJSON source code is available under the MIT [License](/LICENSE).
diff --git a/cmd/ponzu/vendor/github.com/tidwall/sjson/logo.png b/cmd/ponzu/vendor/github.com/tidwall/sjson/logo.png
deleted file mode 100644
index b5aa257..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/sjson/logo.png
+++ /dev/null
Binary files differ
diff --git a/cmd/ponzu/vendor/github.com/tidwall/sjson/sjson.go b/cmd/ponzu/vendor/github.com/tidwall/sjson/sjson.go
deleted file mode 100644
index 7f1d358..0000000
--- a/cmd/ponzu/vendor/github.com/tidwall/sjson/sjson.go
+++ /dev/null
@@ -1,653 +0,0 @@
-// Package sjson provides setting json values.
-package sjson
-
-import (
- jsongo "encoding/json"
- "reflect"
- "strconv"
- "unsafe"
-
- "github.com/tidwall/gjson"
-)
-
-type errorType struct {
- msg string
-}
-
-func (err *errorType) Error() string {
- return err.msg
-}
-
-// Options represents additional options for the Set and Delete functions.
-type Options struct {
- // Optimistic is a hint that the value likely exists which
- // allows for the sjson to perform a fast-track search and replace.
- Optimistic bool
- // ReplaceInPlace is a hint to replace the input json rather than
- // allocate a new json byte slice. When this field is specified
- // the input json will not longer be valid and it should not be used
- // In the case when the destination slice doesn't have enough free
- // bytes to replace the data in place, a new bytes slice will be
- // created under the hood.
- // The Optimistic flag must be set to true and the input must be a
- // byte slice in order to use this field.
- ReplaceInPlace bool
-}
-
-type pathResult struct {
- part string // current key part
- path string // remaining path
- force bool // force a string key
- more bool // there is more path to parse
-}
-
-func parsePath(path string) (pathResult, error) {
- var r pathResult
- if len(path) > 0 && path[0] == ':' {
- r.force = true
- path = path[1:]
- }
- for i := 0; i < len(path); i++ {
- if path[i] == '.' {
- r.part = path[:i]
- r.path = path[i+1:]
- r.more = true
- return r, nil
- }
- if path[i] == '*' || path[i] == '?' {
- return r, &errorType{"wildcard characters not allowed in path"}
- } else if path[i] == '#' {
- return r, &errorType{"array access character not allowed in path"}
- }
- if path[i] == '\\' {
- // go into escape mode. this is a slower path that
- // strips off the escape character from the part.
- epart := []byte(path[:i])
- i++
- if i < len(path) {
- epart = append(epart, path[i])
- i++
- for ; i < len(path); i++ {
- if path[i] == '\\' {
- i++
- if i < len(path) {
- epart = append(epart, path[i])
- }
- continue
- } else if path[i] == '.' {
- r.part = string(epart)
- r.path = path[i+1:]
- r.more = true
- return r, nil
- } else if path[i] == '*' || path[i] == '?' {
- return r, &errorType{
- "wildcard characters not allowed in path"}
- } else if path[i] == '#' {
- return r, &errorType{
- "array access character not allowed in path"}
- }
- epart = append(epart, path[i])
- }
- }
- // append the last part
- r.part = string(epart)
- return r, nil
- }
- }
- r.part = path
- return r, nil
-}
-
-func mustMarshalString(s string) bool {
- for i := 0; i < len(s); i++ {
- if s[i] < ' ' || s[i] > 0x7f || s[i] == '"' {
- return true
- }
- }
- return false
-}
-
-// appendStringify makes a json string and appends to buf.
-func appendStringify(buf []byte, s string) []byte {
- if mustMarshalString(s) {
- b, _ := jsongo.Marshal(s)
- return append(buf, b...)
- }
- buf = append(buf, '"')
- buf = append(buf, s...)
- buf = append(buf, '"')
- return buf
-}
-
-// appendBuild builds a json block from a json path.
-func appendBuild(buf []byte, array bool, paths []pathResult, raw string,
- stringify bool) []byte {
- if !array {
- buf = appendStringify(buf, paths[0].part)
- buf = append(buf, ':')
- }
- if len(paths) > 1 {
- n, numeric := atoui(paths[1])
- if numeric || (!paths[1].force && paths[1].part == "-1") {
- buf = append(buf, '[')
- buf = appendRepeat(buf, "null,", n)
- buf = appendBuild(buf, true, paths[1:], raw, stringify)
- buf = append(buf, ']')
- } else {
- buf = append(buf, '{')
- buf = appendBuild(buf, false, paths[1:], raw, stringify)
- buf = append(buf, '}')
- }
- } else {
- if stringify {
- buf = appendStringify(buf, raw)
- } else {
- buf = append(buf, raw...)
- }
- }
- return buf
-}
-
-// atoui does a rip conversion of string -> unigned int.
-func atoui(r pathResult) (n int, ok bool) {
- if r.force {
- return 0, false
- }
- for i := 0; i < len(r.part); i++ {
- if r.part[i] < '0' || r.part[i] > '9' {
- return 0, false
- }
- n = n*10 + int(r.part[i]-'0')
- }
- return n, true
-}
-
-// appendRepeat repeats string "n" times and appends to buf.
-func appendRepeat(buf []byte, s string, n int) []byte {
- for i := 0; i < n; i++ {
- buf = append(buf, s...)
- }
- return buf
-}
-
-// trim does a rip trim
-func trim(s string) string {
- for len(s) > 0 {
- if s[0] <= ' ' {
- s = s[1:]
- continue
- }
- break
- }
- for len(s) > 0 {
- if s[len(s)-1] <= ' ' {
- s = s[:len(s)-1]
- continue
- }
- break
- }
- return s
-}
-
-// deleteTailItem deletes the previous key or comma.
-func deleteTailItem(buf []byte) ([]byte, bool) {
-loop:
- for i := len(buf) - 1; i >= 0; i-- {
- // look for either a ',',':','['
- switch buf[i] {
- case '[':
- return buf, true
- case ',':
- return buf[:i], false
- case ':':
- // delete tail string
- i--
- for ; i >= 0; i-- {
- if buf[i] == '"' {
- i--
- for ; i >= 0; i-- {
- if buf[i] == '"' {
- i--
- if i >= 0 && i == '\\' {
- i--
- continue
- }
- for ; i >= 0; i-- {
- // look for either a ',','{'
- switch buf[i] {
- case '{':
- return buf[:i+1], true
- case ',':
- return buf[:i], false
- }
- }
- }
- }
- break
- }
- }
- break loop
- }
- }
- return buf, false
-}
-
-var errNoChange = &errorType{"no change"}
-
-func appendRawPaths(buf []byte, jstr string, paths []pathResult, raw string,
- stringify, del bool) ([]byte, error) {
- var err error
- var res gjson.Result
- var found bool
- if del {
- if paths[0].part == "-1" && !paths[0].force {
- res = gjson.Get(jstr, "#")
- if res.Int() > 0 {
- res = gjson.Get(jstr, strconv.FormatInt(int64(res.Int()-1), 10))
- found = true
- }
- }
- }
- if !found {
- res = gjson.Get(jstr, paths[0].part)
- }
- if res.Index > 0 {
- if len(paths) > 1 {
- buf = append(buf, jstr[:res.Index]...)
- buf, err = appendRawPaths(buf, res.Raw, paths[1:], raw,
- stringify, del)
- if err != nil {
- return nil, err
- }
- buf = append(buf, jstr[res.Index+len(res.Raw):]...)
- return buf, nil
- }
- buf = append(buf, jstr[:res.Index]...)
- var exidx int // additional forward stripping
- if del {
- var delNextComma bool
- buf, delNextComma = deleteTailItem(buf)
- if delNextComma {
- i, j := res.Index+len(res.Raw), 0
- for ; i < len(jstr); i, j = i+1, j+1 {
- if jstr[i] <= ' ' {
- continue
- }
- if jstr[i] == ',' {
- exidx = j + 1
- }
- break
- }
- }
- } else {
- if stringify {
- buf = appendStringify(buf, raw)
- } else {
- buf = append(buf, raw...)
- }
- }
- buf = append(buf, jstr[res.Index+len(res.Raw)+exidx:]...)
- return buf, nil
- }
- if del {
- return nil, errNoChange
- }
- n, numeric := atoui(paths[0])
- isempty := true
- for i := 0; i < len(jstr); i++ {
- if jstr[i] > ' ' {
- isempty = false
- break
- }
- }
- if isempty {
- if numeric {
- jstr = "[]"
- } else {
- jstr = "{}"
- }
- }
- jsres := gjson.Parse(jstr)
- if jsres.Type != gjson.JSON {
- if numeric {
- jstr = "[]"
- } else {
- jstr = "{}"
- }
- jsres = gjson.Parse(jstr)
- }
- var comma bool
- for i := 1; i < len(jsres.Raw); i++ {
- if jsres.Raw[i] <= ' ' {
- continue
- }
- if jsres.Raw[i] == '}' || jsres.Raw[i] == ']' {
- break
- }
- comma = true
- break
- }
- switch jsres.Raw[0] {
- default:
- return nil, &errorType{"json must be an object or array"}
- case '{':
- buf = append(buf, '{')
- buf = appendBuild(buf, false, paths, raw, stringify)
- if comma {
- buf = append(buf, ',')
- }
- buf = append(buf, jsres.Raw[1:]...)
- return buf, nil
- case '[':
- var appendit bool
- if !numeric {
- if paths[0].part == "-1" && !paths[0].force {
- appendit = true
- } else {
- return nil, &errorType{
- "cannot set array element for non-numeric key '" +
- paths[0].part + "'"}
- }
- }
- if appendit {
- njson := trim(jsres.Raw)
- if njson[len(njson)-1] == ']' {
- njson = njson[:len(njson)-1]
- }
- buf = append(buf, njson...)
- if comma {
- buf = append(buf, ',')
- }
-
- buf = appendBuild(buf, true, paths, raw, stringify)
- buf = append(buf, ']')
- return buf, nil
- }
- buf = append(buf, '[')
- ress := jsres.Array()
- for i := 0; i < len(ress); i++ {
- if i > 0 {
- buf = append(buf, ',')
- }
- buf = append(buf, ress[i].Raw...)
- }
- if len(ress) == 0 {
- buf = appendRepeat(buf, "null,", n-len(ress))
- } else {
- buf = appendRepeat(buf, ",null", n-len(ress))
- if comma {
- buf = append(buf, ',')
- }
- }
- buf = appendBuild(buf, true, paths, raw, stringify)
- buf = append(buf, ']')
- return buf, nil
- }
-}
-
-func isOptimisticPath(path string) bool {
- for i := 0; i < len(path); i++ {
- if path[i] < '.' || path[i] > 'z' {
- return false
- }
- if path[i] > '9' && path[i] < 'A' {
- return false
- }
- if path[i] > 'z' {
- return false
- }
- }
- return true
-}
-
-func set(jstr, path, raw string,
- stringify, del, optimistic, inplace bool) ([]byte, error) {
- if path == "" {
- return nil, &errorType{"path cannot be empty"}
- }
- if !del && optimistic && isOptimisticPath(path) {
- res := gjson.Get(jstr, path)
- if res.Exists() && res.Index > 0 {
- sz := len(jstr) - len(res.Raw) + len(raw)
- if stringify {
- sz += 2
- }
- if inplace && sz <= len(jstr) {
- if !stringify || !mustMarshalString(raw) {
- jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&jstr))
- jsonbh := reflect.SliceHeader{
- Data: jsonh.Data, Len: jsonh.Len, Cap: jsonh.Len}
- jbytes := *(*[]byte)(unsafe.Pointer(&jsonbh))
- if stringify {
- jbytes[res.Index] = '"'
- copy(jbytes[res.Index+1:], []byte(raw))
- jbytes[res.Index+1+len(raw)] = '"'
- copy(jbytes[res.Index+1+len(raw)+1:],
- jbytes[res.Index+len(res.Raw):])
- } else {
- copy(jbytes[res.Index:], []byte(raw))
- copy(jbytes[res.Index+len(raw):],
- jbytes[res.Index+len(res.Raw):])
- }
- return jbytes[:sz], nil
- }
- return nil, nil
- }
- buf := make([]byte, 0, sz)
- buf = append(buf, jstr[:res.Index]...)
- if stringify {
- buf = appendStringify(buf, raw)
- } else {
- buf = append(buf, raw...)
- }
- buf = append(buf, jstr[res.Index+len(res.Raw):]...)
- return buf, nil
- }
- }
- // parse the path, make sure that it does not contain invalid characters
- // such as '#', '?', '*'
- paths := make([]pathResult, 0, 4)
- r, err := parsePath(path)
- if err != nil {
- return nil, err
- }
- paths = append(paths, r)
- for r.more {
- if r, err = parsePath(r.path); err != nil {
- return nil, err
- }
- paths = append(paths, r)
- }
-
- njson, err := appendRawPaths(nil, jstr, paths, raw, stringify, del)
- if err != nil {
- return nil, err
- }
- return njson, nil
-}
-
-// Set sets a json value for the specified path.
-// A path is in dot syntax, such as "name.last" or "age".
-// This function expects that the json is well-formed, and does not validate.
-// Invalid json will not panic, but it may return back unexpected results.
-// An error is returned if the path is not valid.
-//
-// A path is a series of keys separated by a dot.
-//
-// {
-// "name": {"first": "Tom", "last": "Anderson"},
-// "age":37,
-// "children": ["Sara","Alex","Jack"],
-// "friends": [
-// {"first": "James", "last": "Murphy"},
-// {"first": "Roger", "last": "Craig"}
-// ]
-// }
-// "name.last" >> "Anderson"
-// "age" >> 37
-// "children.1" >> "Alex"
-//
-func Set(json, path string, value interface{}) (string, error) {
- return SetOptions(json, path, value, nil)
-}
-
-// SetOptions sets a json value for the specified path with options.
-// A path is in dot syntax, such as "name.last" or "age".
-// This function expects that the json is well-formed, and does not validate.
-// Invalid json will not panic, but it may return back unexpected results.
-// An error is returned if the path is not valid.
-func SetOptions(json, path string, value interface{},
- opts *Options) (string, error) {
- if opts != nil {
- if opts.ReplaceInPlace {
- // it's not safe to replace bytes in-place for strings
- // copy the Options and set options.ReplaceInPlace to false.
- nopts := *opts
- opts = &nopts
- opts.ReplaceInPlace = false
- }
- }
- jsonh := *(*reflect.StringHeader)(unsafe.Pointer(&json))
- jsonbh := reflect.SliceHeader{Data: jsonh.Data, Len: jsonh.Len}
- jsonb := *(*[]byte)(unsafe.Pointer(&jsonbh))
- res, err := SetBytesOptions(jsonb, path, value, opts)
- return string(res), err
-}
-
-// SetBytes sets a json value for the specified path.
-// If working with bytes, this method preferred over
-// Set(string(data), path, value)
-func SetBytes(json []byte, path string, value interface{}) ([]byte, error) {
- return SetBytesOptions(json, path, value, nil)
-}
-
-// SetBytesOptions sets a json value for the specified path with options.
-// If working with bytes, this method preferred over
-// SetOptions(string(data), path, value)
-func SetBytesOptions(json []byte, path string, value interface{},
- opts *Options) ([]byte, error) {
- var optimistic, inplace bool
- if opts != nil {
- optimistic = opts.Optimistic
- inplace = opts.ReplaceInPlace
- }
- jstr := *(*string)(unsafe.Pointer(&json))
- var res []byte
- var err error
- switch v := value.(type) {
- default:
- b, err := jsongo.Marshal(value)
- if err != nil {
- return nil, err
- }
- raw := *(*string)(unsafe.Pointer(&b))
- res, err = set(jstr, path, raw, false, false, optimistic, inplace)
- case dtype:
- res, err = set(jstr, path, "", false, true, optimistic, inplace)
- case string:
- res, err = set(jstr, path, v, true, false, optimistic, inplace)
- case []byte:
- raw := *(*string)(unsafe.Pointer(&v))
- res, err = set(jstr, path, raw, true, false, optimistic, inplace)
- case bool:
- if v {
- res, err = set(jstr, path, "true", false, false, optimistic, inplace)
- } else {
- res, err = set(jstr, path, "false", false, false, optimistic, inplace)
- }
- case int8:
- res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
- false, false, optimistic, inplace)
- case int16:
- res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
- false, false, optimistic, inplace)
- case int32:
- res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
- false, false, optimistic, inplace)
- case int64:
- res, err = set(jstr, path, strconv.FormatInt(int64(v), 10),
- false, false, optimistic, inplace)
- case uint8:
- res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
- false, false, optimistic, inplace)
- case uint16:
- res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
- false, false, optimistic, inplace)
- case uint32:
- res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
- false, false, optimistic, inplace)
- case uint64:
- res, err = set(jstr, path, strconv.FormatUint(uint64(v), 10),
- false, false, optimistic, inplace)
- case float32:
- res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
- false, false, optimistic, inplace)
- case float64:
- res, err = set(jstr, path, strconv.FormatFloat(float64(v), 'f', -1, 64),
- false, false, optimistic, inplace)
- }
- if err == errNoChange {
- return json, nil
- }
- return res, err
-}
-
-// SetRaw sets a raw json value for the specified path.
-// This function works the same as Set except that the value is set as a
-// raw block of json. This allows for setting premarshalled json objects.
-func SetRaw(json, path, value string) (string, error) {
- return SetRawOptions(json, path, value, nil)
-}
-
-// SetRawOptions sets a raw json value for the specified path with options.
-// This furnction works the same as SetOptions except that the value is set
-// as a raw block of json. This allows for setting premarshalled json objects.
-func SetRawOptions(json, path, value string, opts *Options) (string, error) {
- var optimistic bool
- if opts != nil {
- optimistic = opts.Optimistic
- }
- res, err := set(json, path, value, false, false, optimistic, false)
- if err == errNoChange {
- return json, nil
- }
- return string(res), err
-}
-
-// SetRawBytes sets a raw json value for the specified path.
-// If working with bytes, this method preferred over
-// SetRaw(string(data), path, value)
-func SetRawBytes(json []byte, path string, value []byte) ([]byte, error) {
- return SetRawBytesOptions(json, path, value, nil)
-}
-
-// SetRawBytesOptions sets a raw json value for the specified path with options.
-// If working with bytes, this method preferred over
-// SetRawOptions(string(data), path, value, opts)
-func SetRawBytesOptions(json []byte, path string, value []byte,
- opts *Options) ([]byte, error) {
- jstr := *(*string)(unsafe.Pointer(&json))
- vstr := *(*string)(unsafe.Pointer(&value))
- var optimistic, inplace bool
- if opts != nil {
- optimistic = opts.Optimistic
- inplace = opts.ReplaceInPlace
- }
- res, err := set(jstr, path, vstr, false, false, optimistic, inplace)
- if err == errNoChange {
- return json, nil
- }
- return res, err
-}
-
-type dtype struct{}
-
-// Delete deletes a value from json for the specified path.
-func Delete(json, path string) (string, error) {
- return Set(json, path, dtype{})
-}
-
-// DeleteBytes deletes a value from json for the specified path.
-func DeleteBytes(json []byte, path string) ([]byte, error) {
- return SetBytes(json, path, dtype{})
-}