Browse Source

upgrade buntdb

Resolves CVE-2021-42836, which probably didn't affect us, but we might as well
upgrade.
tags/v2.8.0-rc1
Shivaram Lingamneni 2 years ago
parent
commit
c972a92e51

+ 6
- 6
go.mod View File

@@ -17,7 +17,7 @@ require (
17 17
 	github.com/onsi/ginkgo v1.12.0 // indirect
18 18
 	github.com/onsi/gomega v1.9.0 // indirect
19 19
 	github.com/stretchr/testify v1.4.0 // indirect
20
-	github.com/tidwall/buntdb v1.2.6
20
+	github.com/tidwall/buntdb v1.2.7
21 21
 	github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208
22 22
 	github.com/xdg-go/scram v1.0.2
23 23
 	golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc
@@ -26,11 +26,11 @@ require (
26 26
 )
27 27
 
28 28
 require (
29
-	github.com/tidwall/btree v0.6.0 // indirect
30
-	github.com/tidwall/gjson v1.8.0 // indirect
31
-	github.com/tidwall/grect v0.1.2 // indirect
32
-	github.com/tidwall/match v1.0.3 // indirect
33
-	github.com/tidwall/pretty v1.1.0 // indirect
29
+	github.com/tidwall/btree v0.6.1 // indirect
30
+	github.com/tidwall/gjson v1.10.2 // indirect
31
+	github.com/tidwall/grect v0.1.3 // indirect
32
+	github.com/tidwall/match v1.1.1 // indirect
33
+	github.com/tidwall/pretty v1.2.0 // indirect
34 34
 	github.com/tidwall/rtred v0.1.2 // indirect
35 35
 	github.com/tidwall/tinyqueue v0.1.1 // indirect
36 36
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect

+ 13
- 0
go.sum View File

@@ -39,20 +39,33 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
39 39
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
40 40
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
41 41
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
42
+github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8=
42 43
 github.com/tidwall/btree v0.6.0 h1:JLYAFGV+1gjyFi3iQbO/fupBin+Ooh7dxqVV0twJ1Bo=
43 44
 github.com/tidwall/btree v0.6.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
45
+github.com/tidwall/btree v0.6.1 h1:75VVgBeviiDO+3g4U+7+BaNBNhNINxB0ULPT3fs9pMY=
46
+github.com/tidwall/btree v0.6.1/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
44 47
 github.com/tidwall/buntdb v1.2.6 h1:eS0QSmzHfCKjxxYGh8eH6wnK5VLsJ7UjyyIr29JmnEg=
45 48
 github.com/tidwall/buntdb v1.2.6/go.mod h1:zpXqlA5D2772I4cTqV3ifr2AZihDgi8FV7xAQu6edfc=
49
+github.com/tidwall/buntdb v1.2.7 h1:SIyObKAymzLyGhDeIhVk2Yc1/EwfCC75Uyu77CHlVoA=
50
+github.com/tidwall/buntdb v1.2.7/go.mod h1:b6KvZM27x/8JLI5hgRhRu60pa3q0Tz9c50TyD46OHUM=
46 51
 github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ=
47 52
 github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
53
+github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
54
+github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
48 55
 github.com/tidwall/grect v0.1.2 h1:wKVeQVZhjaFCKTTlpkDe3Ex4ko3cMGW3MRKawRe8uQ4=
49 56
 github.com/tidwall/grect v0.1.2/go.mod h1:v+n4ewstPGduVJebcp5Eh2WXBJBumNzyhK8GZt4gHNw=
57
+github.com/tidwall/grect v0.1.3 h1:z9YwQAMUxVSBde3b7Sl8Da37rffgNfZ6Fq6h9t6KdXE=
58
+github.com/tidwall/grect v0.1.3/go.mod h1:8GMjwh3gPZVpLBI/jDz9uslCe0dpxRpWDdtN0lWAS/E=
50 59
 github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8=
51 60
 github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8=
52 61
 github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
53 62
 github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
63
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
64
+github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
54 65
 github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
55 66
 github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
67
+github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
68
+github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
56 69
 github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8=
57 70
 github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ=
58 71
 github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=

+ 35
- 4
vendor/github.com/tidwall/btree/btree.go View File

@@ -296,7 +296,38 @@ func (n *node) scan(iter func(item interface{}) bool) bool {
296 296
 
297 297
 // Get a value for key
298 298
 func (tr *BTree) Get(key interface{}) interface{} {
299
-	return tr.GetHint(key, nil)
299
+	// This operation is basically the same as calling:
300
+	//     return tr.GetHint(key, nil)
301
+	// But here we inline the bsearch to avoid the hint logic and extra
302
+	// function call.
303
+	if tr.rlock() {
304
+		defer tr.runlock()
305
+	}
306
+	if tr.root == nil || key == nil {
307
+		return nil
308
+	}
309
+	depth := 0
310
+	n := tr.root
311
+	for {
312
+		low := int16(0)
313
+		high := n.numItems - 1
314
+		for low <= high {
315
+			mid := low + ((high+1)-low)/2
316
+			if !tr.less(key, n.items[mid]) {
317
+				low = mid + 1
318
+			} else {
319
+				high = mid - 1
320
+			}
321
+		}
322
+		if low > 0 && !tr.less(n.items[low-1], key) {
323
+			return n.items[low-1]
324
+		}
325
+		if n.leaf {
326
+			return nil
327
+		}
328
+		n = n.children[low]
329
+		depth++
330
+	}
300 331
 }
301 332
 
302 333
 // GetHint gets a value for key using a path hint
@@ -310,14 +341,14 @@ func (tr *BTree) GetHint(key interface{}, hint *PathHint) interface{} {
310 341
 	depth := 0
311 342
 	n := tr.root
312 343
 	for {
313
-		i, found := n.find(key, tr.less, hint, depth)
344
+		index, found := n.find(key, tr.less, hint, depth)
314 345
 		if found {
315
-			return n.items[i]
346
+			return n.items[index]
316 347
 		}
317 348
 		if n.leaf {
318 349
 			return nil
319 350
 		}
320
-		n = n.children[i]
351
+		n = n.children[index]
321 352
 		depth++
322 353
 	}
323 354
 }

+ 11
- 5
vendor/github.com/tidwall/buntdb/buntdb.go View File

@@ -1263,12 +1263,15 @@ type dbItem struct {
1263 1263
 	keyless  bool        // keyless item for scanning
1264 1264
 }
1265 1265
 
1266
+// estIntSize returns the string representions size.
1267
+// Has the same result as len(strconv.Itoa(x)).
1266 1268
 func estIntSize(x int) int {
1267
-	if x == 0 {
1268
-		return 1
1269
+	n := 1
1270
+	if x < 0 {
1271
+		n++
1272
+		x *= -1
1269 1273
 	}
1270
-	var n int
1271
-	for x > 0 {
1274
+	for x >= 10 {
1272 1275
 		n++
1273 1276
 		x /= 10
1274 1277
 	}
@@ -1283,7 +1286,10 @@ func estBulkStringSize(s string) int {
1283 1286
 	return 1 + estIntSize(len(s)) + 2 + len(s) + 2
1284 1287
 }
1285 1288
 
1286
-func (dbi *dbItem) estAOFSetSize() (n int) {
1289
+// estAOFSetSize returns an estimated number of bytes that this item will use
1290
+// when stored in the aof file.
1291
+func (dbi *dbItem) estAOFSetSize() int {
1292
+	var n int
1287 1293
 	if dbi.opts != nil && dbi.opts.ex {
1288 1294
 		n += estArraySize(5)
1289 1295
 		n += estBulkStringSize("set")

+ 18
- 14
vendor/github.com/tidwall/gjson/README.md View File

@@ -123,11 +123,12 @@ nil, for JSON null
123 123
 To directly access the value:
124 124
 
125 125
 ```go
126
-result.Type    // can be String, Number, True, False, Null, or JSON
127
-result.Str     // holds the string
128
-result.Num     // holds the float64 number
129
-result.Raw     // holds the raw json
130
-result.Index   // index of raw value in original json, zero means index unknown
126
+result.Type           // can be String, Number, True, False, Null, or JSON
127
+result.Str            // holds the string
128
+result.Num            // holds the float64 number
129
+result.Raw            // holds the raw json
130
+result.Index          // index of raw value in original json, zero means index unknown
131
+result.Indexes        // indexes of all the elements that match on a path containing the '#' query character.
131 132
 ```
132 133
 
133 134
 There are a variety of handy functions that work on a result:
@@ -199,6 +200,8 @@ There are currently the following built-in modifiers:
199 200
 - `@valid`: Ensure the json document is valid.
200 201
 - `@flatten`: Flattens an array.
201 202
 - `@join`: Joins multiple objects into a single object.
203
+- `@keys`: Returns an array of keys for an object.
204
+- `@values`: Returns an array of values for an object.
202 205
 
203 206
 ### Modifier arguments
204 207
 
@@ -433,14 +436,15 @@ Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/js
433 436
 and [json-iterator](https://github.com/json-iterator/go)
434 437
 
435 438
 ```
436
-BenchmarkGJSONGet-8                  3000000        372 ns/op          0 B/op         0 allocs/op
437
-BenchmarkGJSONUnmarshalMap-8          900000       4154 ns/op       1920 B/op        26 allocs/op
438
-BenchmarkJSONUnmarshalMap-8           600000       9019 ns/op       3048 B/op        69 allocs/op
439
-BenchmarkJSONDecoder-8                300000      14120 ns/op       4224 B/op       184 allocs/op
440
-BenchmarkFFJSONLexer-8               1500000       3111 ns/op        896 B/op         8 allocs/op
441
-BenchmarkEasyJSONLexer-8             3000000        887 ns/op        613 B/op         6 allocs/op
442
-BenchmarkJSONParserGet-8             3000000        499 ns/op         21 B/op         0 allocs/op
443
-BenchmarkJSONIterator-8              3000000        812 ns/op        544 B/op         9 allocs/op
439
+BenchmarkGJSONGet-16                11644512       311 ns/op       0 B/op	       0 allocs/op
440
+BenchmarkGJSONUnmarshalMap-16        1122678      3094 ns/op    1920 B/op	      26 allocs/op
441
+BenchmarkJSONUnmarshalMap-16          516681      6810 ns/op    2944 B/op	      69 allocs/op
442
+BenchmarkJSONUnmarshalStruct-16       697053      5400 ns/op     928 B/op	      13 allocs/op
443
+BenchmarkJSONDecoder-16               330450     10217 ns/op    3845 B/op	     160 allocs/op
444
+BenchmarkFFJSONLexer-16              1424979      2585 ns/op     880 B/op	       8 allocs/op
445
+BenchmarkEasyJSONLexer-16            3000000       729 ns/op     501 B/op	       5 allocs/op
446
+BenchmarkJSONParserGet-16            3000000       366 ns/op      21 B/op	       0 allocs/op
447
+BenchmarkJSONIterator-16             3000000       869 ns/op     693 B/op	      14 allocs/op
444 448
 ```
445 449
 
446 450
 JSON document used:
@@ -481,4 +485,4 @@ widget.image.hOffset
481 485
 widget.text.onMouseUp
482 486
 ```
483 487
 
484
-*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).*
488
+*These benchmarks were run on a MacBook Pro 16" 2.4 GHz Intel Core i9 using Go 1.17 and can be found [here](https://github.com/tidwall/gjson-benchmarks).*

+ 35
- 2
vendor/github.com/tidwall/gjson/SYNTAX.md View File

@@ -135,6 +135,37 @@ changed in v1.3.0 as to avoid confusion with the new [multipath](#multipaths)
135 135
 syntax. For backwards compatibility, `#[...]` will continue to work until the
136 136
 next major release.*
137 137
 
138
+The `~` (tilde) operator will convert a value to a boolean before comparison.
139
+
140
+For example, using the following JSON:
141
+
142
+```json
143
+{
144
+  "vals": [
145
+    { "a": 1, "b": true },
146
+    { "a": 2, "b": true },
147
+    { "a": 3, "b": false },
148
+    { "a": 4, "b": "0" },
149
+    { "a": 5, "b": 0 },
150
+    { "a": 6, "b": "1" },
151
+    { "a": 7, "b": 1 },
152
+    { "a": 8, "b": "true" },
153
+    { "a": 9, "b": false },
154
+    { "a": 10, "b": null },
155
+    { "a": 11 }
156
+  ]
157
+}
158
+```
159
+
160
+You can now query for all true(ish) or false(ish) values:
161
+
162
+```
163
+vals.#(b==~true)#.a    >> [1,2,6,7,8]
164
+vals.#(b==~false)#.a   >> [3,4,5,9,10,11]
165
+```
166
+
167
+The last value which was non-existent is treated as `false`
168
+
138 169
 ### Dot vs Pipe
139 170
 
140 171
 The `.` is standard separator, but it's also possible to use a `|`. 
@@ -205,6 +236,8 @@ There are currently the following built-in modifiers:
205 236
 - `@valid`: Ensure the json document is valid.
206 237
 - `@flatten`: Flattens an array.
207 238
 - `@join`: Joins multiple objects into a single object.
239
+- `@keys`: Returns an array of keys for an object.
240
+- `@values`: Returns an array of values for an object.
208 241
 
209 242
 #### Modifier arguments
210 243
 
@@ -260,8 +293,8 @@ gjson.AddModifier("case", func(json, arg string) string {
260 293
 ### Multipaths
261 294
 
262 295
 Starting with v1.3.0, GJSON added the ability to join multiple paths together
263
-to form new documents. Wrapping comma-separated paths between `{...}` or 
264
-`[...]` will result in a new array or object, respectively.
296
+to form new documents. Wrapping comma-separated paths between `[...]` or
297
+`{...}` will result in a new array or object, respectively.
265 298
 
266 299
 For example, using the given multipath 
267 300
 

+ 168
- 49
vendor/github.com/tidwall/gjson/gjson.go View File

@@ -64,6 +64,9 @@ type Result struct {
64 64
 	Num float64
65 65
 	// Index of raw value in original json, zero means index unknown
66 66
 	Index int
67
+	// Indexes of all the elements that match on a path containing the '#'
68
+	// query character.
69
+	Indexes []int
67 70
 }
68 71
 
69 72
 // String returns a string representation of the value.
@@ -186,14 +189,15 @@ func (t Result) Time() time.Time {
186 189
 }
187 190
 
188 191
 // Array returns back an array of values.
189
-// If the result represents a non-existent value, then an empty array will be
190
-// returned. If the result is not a JSON array, the return value will be an
192
+// If the result represents a null value or is non-existent, then an empty
193
+// array will be returned.
194
+// If the result is not a JSON array, the return value will be an
191 195
 // array containing one result.
192 196
 func (t Result) Array() []Result {
193 197
 	if t.Type == Null {
194 198
 		return []Result{}
195 199
 	}
196
-	if t.Type != JSON {
200
+	if !t.IsArray() {
197 201
 		return []Result{t}
198 202
 	}
199 203
 	r := t.arrayOrMap('[', false)
@@ -281,7 +285,8 @@ func (t Result) ForEach(iterator func(key, value Result) bool) {
281 285
 	}
282 286
 }
283 287
 
284
-// Map returns back an map of values. The result should be a JSON array.
288
+// Map returns back a map of values. The result should be a JSON object.
289
+// If the result is not a JSON object, the return value will be an empty map.
285 290
 func (t Result) Map() map[string]Result {
286 291
 	if t.Type != JSON {
287 292
 		return map[string]Result{}
@@ -584,7 +589,7 @@ func tostr(json string) (raw string, str string) {
584 589
 							continue
585 590
 						}
586 591
 					}
587
-					break
592
+					return json[:i+1], unescape(json[1:i])
588 593
 				}
589 594
 			}
590 595
 			var ret string
@@ -756,7 +761,7 @@ func parseArrayPath(path string) (r arrayPathResult) {
756 761
 						// bad query, end now
757 762
 						break
758 763
 					}
759
-					if len(value) > 2 && value[0] == '"' &&
764
+					if len(value) >= 2 && value[0] == '"' &&
760 765
 						value[len(value)-1] == '"' {
761 766
 						value = value[1 : len(value)-1]
762 767
 						if vesc {
@@ -1085,9 +1090,9 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
1085 1090
 		}
1086 1091
 		if rp.wild {
1087 1092
 			if kesc {
1088
-				pmatch = match.Match(unescape(key), rp.part)
1093
+				pmatch = matchLimit(unescape(key), rp.part)
1089 1094
 			} else {
1090
-				pmatch = match.Match(key, rp.part)
1095
+				pmatch = matchLimit(key, rp.part)
1091 1096
 			}
1092 1097
 		} else {
1093 1098
 			if kesc {
@@ -1098,6 +1103,7 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
1098 1103
 		}
1099 1104
 		hit = pmatch && !rp.more
1100 1105
 		for ; i < len(c.json); i++ {
1106
+			var num bool
1101 1107
 			switch c.json[i] {
1102 1108
 			default:
1103 1109
 				continue
@@ -1145,15 +1151,13 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
1145 1151
 						return i, true
1146 1152
 					}
1147 1153
 				}
1148
-			case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
1149
-				i, val = parseNumber(c.json, i)
1150
-				if hit {
1151
-					c.value.Raw = val
1152
-					c.value.Type = Number
1153
-					c.value.Num, _ = strconv.ParseFloat(val, 64)
1154
-					return i, true
1154
+			case 'n':
1155
+				if i+1 < len(c.json) && c.json[i+1] != 'u' {
1156
+					num = true
1157
+					break
1155 1158
 				}
1156
-			case 't', 'f', 'n':
1159
+				fallthrough
1160
+			case 't', 'f':
1157 1161
 				vc := c.json[i]
1158 1162
 				i, val = parseLiteral(c.json, i)
1159 1163
 				if hit {
@@ -1166,12 +1170,33 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
1166 1170
 					}
1167 1171
 					return i, true
1168 1172
 				}
1173
+			case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1174
+				'i', 'I', 'N':
1175
+				num = true
1176
+			}
1177
+			if num {
1178
+				i, val = parseNumber(c.json, i)
1179
+				if hit {
1180
+					c.value.Raw = val
1181
+					c.value.Type = Number
1182
+					c.value.Num, _ = strconv.ParseFloat(val, 64)
1183
+					return i, true
1184
+				}
1169 1185
 			}
1170 1186
 			break
1171 1187
 		}
1172 1188
 	}
1173 1189
 	return i, false
1174 1190
 }
1191
+
1192
+// matchLimit will limit the complexity of the match operation to avoid ReDos
1193
+// attacks from arbritary inputs.
1194
+// See the github.com/tidwall/match.MatchLimit function for more information.
1195
+func matchLimit(str, pattern string) bool {
1196
+	matched, _ := match.MatchLimit(str, pattern, 10000)
1197
+	return matched
1198
+}
1199
+
1175 1200
 func queryMatches(rp *arrayPathResult, value Result) bool {
1176 1201
 	rpv := rp.query.value
1177 1202
 	if len(rpv) > 0 && rpv[0] == '~' {
@@ -1209,9 +1234,9 @@ func queryMatches(rp *arrayPathResult, value Result) bool {
1209 1234
 		case ">=":
1210 1235
 			return value.Str >= rpv
1211 1236
 		case "%":
1212
-			return match.Match(value.Str, rpv)
1237
+			return matchLimit(value.Str, rpv)
1213 1238
 		case "!%":
1214
-			return !match.Match(value.Str, rpv)
1239
+			return !matchLimit(value.Str, rpv)
1215 1240
 		}
1216 1241
 	case Number:
1217 1242
 		rpvn, _ := strconv.ParseFloat(rpv, 64)
@@ -1261,6 +1286,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1261 1286
 	var alog []int
1262 1287
 	var partidx int
1263 1288
 	var multires []byte
1289
+	var queryIndexes []int
1264 1290
 	rp := parseArrayPath(path)
1265 1291
 	if !rp.arrch {
1266 1292
 		n, ok := parseUint(rp.part)
@@ -1281,6 +1307,10 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1281 1307
 				multires = append(multires, '[')
1282 1308
 			}
1283 1309
 		}
1310
+		var tmp parseContext
1311
+		tmp.value = qval
1312
+		fillIndex(c.json, &tmp)
1313
+		parentIndex := tmp.value.Index
1284 1314
 		var res Result
1285 1315
 		if qval.Type == JSON {
1286 1316
 			res = qval.Get(rp.query.path)
@@ -1312,6 +1342,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1312 1342
 						multires = append(multires, ',')
1313 1343
 					}
1314 1344
 					multires = append(multires, raw...)
1345
+					queryIndexes = append(queryIndexes, res.Index+parentIndex)
1315 1346
 				}
1316 1347
 			} else {
1317 1348
 				c.value = res
@@ -1338,6 +1369,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1338 1369
 			} else {
1339 1370
 				ch = c.json[i]
1340 1371
 			}
1372
+			var num bool
1341 1373
 			switch ch {
1342 1374
 			default:
1343 1375
 				continue
@@ -1420,26 +1452,13 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1420 1452
 						return i, true
1421 1453
 					}
1422 1454
 				}
1423
-			case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
1424
-				i, val = parseNumber(c.json, i)
1425
-				if rp.query.on {
1426
-					var qval Result
1427
-					qval.Raw = val
1428
-					qval.Type = Number
1429
-					qval.Num, _ = strconv.ParseFloat(val, 64)
1430
-					if procQuery(qval) {
1431
-						return i, true
1432
-					}
1433
-				} else if hit {
1434
-					if rp.alogok {
1435
-						break
1436
-					}
1437
-					c.value.Raw = val
1438
-					c.value.Type = Number
1439
-					c.value.Num, _ = strconv.ParseFloat(val, 64)
1440
-					return i, true
1455
+			case 'n':
1456
+				if i+1 < len(c.json) && c.json[i+1] != 'u' {
1457
+					num = true
1458
+					break
1441 1459
 				}
1442
-			case 't', 'f', 'n':
1460
+				fallthrough
1461
+			case 't', 'f':
1443 1462
 				vc := c.json[i]
1444 1463
 				i, val = parseLiteral(c.json, i)
1445 1464
 				if rp.query.on {
@@ -1467,6 +1486,9 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1467 1486
 					}
1468 1487
 					return i, true
1469 1488
 				}
1489
+			case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1490
+				'i', 'I', 'N':
1491
+				num = true
1470 1492
 			case ']':
1471 1493
 				if rp.arrch && rp.part == "#" {
1472 1494
 					if rp.alogok {
@@ -1476,6 +1498,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1476 1498
 							c.pipe = right
1477 1499
 							c.piped = true
1478 1500
 						}
1501
+						var indexes = make([]int, 0, 64)
1479 1502
 						var jsons = make([]byte, 0, 64)
1480 1503
 						jsons = append(jsons, '[')
1481 1504
 						for j, k := 0, 0; j < len(alog); j++ {
@@ -1490,6 +1513,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1490 1513
 							}
1491 1514
 							if idx < len(c.json) && c.json[idx] != ']' {
1492 1515
 								_, res, ok := parseAny(c.json, idx, true)
1516
+								parentIndex := res.Index
1493 1517
 								if ok {
1494 1518
 									res := res.Get(rp.alogkey)
1495 1519
 									if res.Exists() {
@@ -1501,6 +1525,8 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1501 1525
 											raw = res.String()
1502 1526
 										}
1503 1527
 										jsons = append(jsons, []byte(raw)...)
1528
+										indexes = append(indexes,
1529
+											res.Index+parentIndex)
1504 1530
 										k++
1505 1531
 									}
1506 1532
 								}
@@ -1509,6 +1535,7 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1509 1535
 						jsons = append(jsons, ']')
1510 1536
 						c.value.Type = JSON
1511 1537
 						c.value.Raw = string(jsons)
1538
+						c.value.Indexes = indexes
1512 1539
 						return i + 1, true
1513 1540
 					}
1514 1541
 					if rp.alogok {
@@ -1524,8 +1551,9 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1524 1551
 				if !c.value.Exists() {
1525 1552
 					if len(multires) > 0 {
1526 1553
 						c.value = Result{
1527
-							Raw:  string(append(multires, ']')),
1528
-							Type: JSON,
1554
+							Raw:     string(append(multires, ']')),
1555
+							Type:    JSON,
1556
+							Indexes: queryIndexes,
1529 1557
 						}
1530 1558
 					} else if rp.query.all {
1531 1559
 						c.value = Result{
@@ -1536,6 +1564,26 @@ func parseArray(c *parseContext, i int, path string) (int, bool) {
1536 1564
 				}
1537 1565
 				return i + 1, false
1538 1566
 			}
1567
+			if num {
1568
+				i, val = parseNumber(c.json, i)
1569
+				if rp.query.on {
1570
+					var qval Result
1571
+					qval.Raw = val
1572
+					qval.Type = Number
1573
+					qval.Num, _ = strconv.ParseFloat(val, 64)
1574
+					if procQuery(qval) {
1575
+						return i, true
1576
+					}
1577
+				} else if hit {
1578
+					if rp.alogok {
1579
+						break
1580
+					}
1581
+					c.value.Raw = val
1582
+					c.value.Type = Number
1583
+					c.value.Num, _ = strconv.ParseFloat(val, 64)
1584
+					return i, true
1585
+				}
1586
+			}
1539 1587
 			break
1540 1588
 		}
1541 1589
 	}
@@ -1806,6 +1854,7 @@ func Get(json, path string) Result {
1806 1854
 					if len(path) > 0 && (path[0] == '|' || path[0] == '.') {
1807 1855
 						res := Get(rjson, path[1:])
1808 1856
 						res.Index = 0
1857
+						res.Indexes = nil
1809 1858
 						return res
1810 1859
 					}
1811 1860
 					return Parse(rjson)
@@ -2046,11 +2095,15 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) {
2046 2095
 				res.Raw = val
2047 2096
 				res.Type = JSON
2048 2097
 			}
2049
-			return i, res, true
2098
+			var tmp parseContext
2099
+			tmp.value = res
2100
+			fillIndex(json, &tmp)
2101
+			return i, tmp.value, true
2050 2102
 		}
2051 2103
 		if json[i] <= ' ' {
2052 2104
 			continue
2053 2105
 		}
2106
+		var num bool
2054 2107
 		switch json[i] {
2055 2108
 		case '"':
2056 2109
 			i++
@@ -2070,15 +2123,13 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) {
2070 2123
 				}
2071 2124
 			}
2072 2125
 			return i, res, true
2073
-		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
2074
-			i, val = parseNumber(json, i)
2075
-			if hit {
2076
-				res.Raw = val
2077
-				res.Type = Number
2078
-				res.Num, _ = strconv.ParseFloat(val, 64)
2126
+		case 'n':
2127
+			if i+1 < len(json) && json[i+1] != 'u' {
2128
+				num = true
2129
+				break
2079 2130
 			}
2080
-			return i, res, true
2081
-		case 't', 'f', 'n':
2131
+			fallthrough
2132
+		case 't', 'f':
2082 2133
 			vc := json[i]
2083 2134
 			i, val = parseLiteral(json, i)
2084 2135
 			if hit {
@@ -2091,7 +2142,20 @@ func parseAny(json string, i int, hit bool) (int, Result, bool) {
2091 2142
 				}
2092 2143
 				return i, res, true
2093 2144
 			}
2145
+		case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
2146
+			'i', 'I', 'N':
2147
+			num = true
2094 2148
 		}
2149
+		if num {
2150
+			i, val = parseNumber(json, i)
2151
+			if hit {
2152
+				res.Raw = val
2153
+				res.Type = Number
2154
+				res.Num, _ = strconv.ParseFloat(val, 64)
2155
+			}
2156
+			return i, res, true
2157
+		}
2158
+
2095 2159
 	}
2096 2160
 	return i, res, false
2097 2161
 }
@@ -2455,7 +2519,8 @@ func parseInt(s string) (n int64, ok bool) {
2455 2519
 // safeInt validates a given JSON number
2456 2520
 // ensures it lies within the minimum and maximum representable JSON numbers
2457 2521
 func safeInt(f float64) (n int64, ok bool) {
2458
-	//  https://tc39.es/ecma262/#sec-number.min_safe_integer || https://tc39.es/ecma262/#sec-number.max_safe_integer
2522
+	// https://tc39.es/ecma262/#sec-number.min_safe_integer
2523
+	// https://tc39.es/ecma262/#sec-number.max_safe_integer
2459 2524
 	if f < -9007199254740991 || f > 9007199254740991 {
2460 2525
 		return 0, false
2461 2526
 	}
@@ -2534,6 +2599,8 @@ var modifiers = map[string]func(json, arg string) string{
2534 2599
 	"flatten": modFlatten,
2535 2600
 	"join":    modJoin,
2536 2601
 	"valid":   modValid,
2602
+	"keys":    modKeys,
2603
+	"values":  modValues,
2537 2604
 }
2538 2605
 
2539 2606
 // AddModifier binds a custom modifier command to the GJSON syntax.
@@ -2690,6 +2757,58 @@ func modFlatten(json, arg string) string {
2690 2757
 	return bytesString(out)
2691 2758
 }
2692 2759
 
2760
+// @keys extracts the keys from an object.
2761
+//  {"first":"Tom","last":"Smith"} -> ["first","last"]
2762
+func modKeys(json, arg string) string {
2763
+	v := Parse(json)
2764
+	if !v.Exists() {
2765
+		return "[]"
2766
+	}
2767
+	obj := v.IsObject()
2768
+	var out strings.Builder
2769
+	out.WriteByte('[')
2770
+	var i int
2771
+	v.ForEach(func(key, _ Result) bool {
2772
+		if i > 0 {
2773
+			out.WriteByte(',')
2774
+		}
2775
+		if obj {
2776
+			out.WriteString(key.Raw)
2777
+		} else {
2778
+			out.WriteString("null")
2779
+		}
2780
+		i++
2781
+		return true
2782
+	})
2783
+	out.WriteByte(']')
2784
+	return out.String()
2785
+}
2786
+
2787
+// @values extracts the values from an object.
2788
+//   {"first":"Tom","last":"Smith"} -> ["Tom","Smith"]
2789
+func modValues(json, arg string) string {
2790
+	v := Parse(json)
2791
+	if !v.Exists() {
2792
+		return "[]"
2793
+	}
2794
+	if v.IsArray() {
2795
+		return json
2796
+	}
2797
+	var out strings.Builder
2798
+	out.WriteByte('[')
2799
+	var i int
2800
+	v.ForEach(func(_, value Result) bool {
2801
+		if i > 0 {
2802
+			out.WriteByte(',')
2803
+		}
2804
+		out.WriteString(value.Raw)
2805
+		i++
2806
+		return true
2807
+	})
2808
+	out.WriteByte(']')
2809
+	return out.String()
2810
+}
2811
+
2693 2812
 // @join multiple objects into a single object.
2694 2813
 //   [{"first":"Tom"},{"last":"Smith"}] -> {"first","Tom","last":"Smith"}
2695 2814
 // The arg can be "true" to specify that duplicate keys should be preserved.

+ 144
- 99
vendor/github.com/tidwall/match/match.go View File

@@ -1,7 +1,9 @@
1 1
 // Package match provides a simple pattern matcher with unicode support.
2 2
 package match
3 3
 
4
-import "unicode/utf8"
4
+import (
5
+	"unicode/utf8"
6
+)
5 7
 
6 8
 // Match returns true if str matches pattern. This is a very
7 9
 // simple wildcard match where '*' matches on any number characters
@@ -16,127 +18,170 @@ import "unicode/utf8"
16 18
 // 	'\\' c      matches character c
17 19
 //
18 20
 func Match(str, pattern string) bool {
19
-	return deepMatch(str, pattern)
20
-}
21
-
22
-func deepMatch(str, pattern string) bool {
23 21
 	if pattern == "*" {
24 22
 		return true
25 23
 	}
26
-	for len(pattern) > 1 && pattern[0] == '*' && pattern[1] == '*' {
27
-		pattern = pattern[1:]
28
-	}
29
-	for len(pattern) > 0 {
30
-		if pattern[0] > 0x7f {
31
-			return deepMatchRune(str, pattern)
32
-		}
33
-		switch pattern[0] {
34
-		default:
35
-			if len(str) == 0 {
36
-				return false
37
-			}
38
-			if str[0] > 0x7f {
39
-				return deepMatchRune(str, pattern)
40
-			}
41
-			if str[0] != pattern[0] {
42
-				return false
43
-			}
44
-		case '?':
45
-			if len(str) == 0 {
46
-				return false
47
-			}
48
-		case '*':
49
-			return deepMatch(str, pattern[1:]) ||
50
-				(len(str) > 0 && deepMatch(str[1:], pattern))
51
-		}
52
-		str = str[1:]
53
-		pattern = pattern[1:]
54
-	}
55
-	return len(str) == 0 && len(pattern) == 0
24
+	return match(str, pattern, 0, nil, -1) == rMatch
56 25
 }
57 26
 
58
-func deepMatchRune(str, pattern string) bool {
27
+// MatchLimit is the same as Match but will limit the complexity of the match
28
+// operation. This is to avoid long running matches, specifically to avoid ReDos
29
+// attacks from arbritary inputs.
30
+//
31
+// How it works:
32
+// The underlying match routine is recursive and may call itself when it
33
+// encounters a sandwiched wildcard pattern, such as: `user:*:name`.
34
+// Everytime it calls itself a counter is incremented.
35
+// The operation is stopped when counter > maxcomp*len(str).
36
+func MatchLimit(str, pattern string, maxcomp int) (matched, stopped bool) {
59 37
 	if pattern == "*" {
60
-		return true
38
+		return true, false
61 39
 	}
62
-	for len(pattern) > 1 && pattern[0] == '*' && pattern[1] == '*' {
63
-		pattern = pattern[1:]
40
+	counter := 0
41
+	r := match(str, pattern, len(str), &counter, maxcomp)
42
+	if r == rStop {
43
+		return false, true
64 44
 	}
45
+	return r == rMatch, false
46
+}
65 47
 
66
-	var sr, pr rune
67
-	var srsz, prsz int
48
+type result int
68 49
 
69
-	// read the first rune ahead of time
70
-	if len(str) > 0 {
71
-		if str[0] > 0x7f {
72
-			sr, srsz = utf8.DecodeRuneInString(str)
73
-		} else {
74
-			sr, srsz = rune(str[0]), 1
50
+const (
51
+	rNoMatch result = iota
52
+	rMatch
53
+	rStop
54
+)
55
+
56
+func match(str, pat string, slen int, counter *int, maxcomp int) result {
57
+	// check complexity limit
58
+	if maxcomp > -1 {
59
+		if *counter > slen*maxcomp {
60
+			return rStop
75 61
 		}
76
-	} else {
77
-		sr, srsz = utf8.RuneError, 0
62
+		*counter++
78 63
 	}
79
-	if len(pattern) > 0 {
80
-		if pattern[0] > 0x7f {
81
-			pr, prsz = utf8.DecodeRuneInString(pattern)
82
-		} else {
83
-			pr, prsz = rune(pattern[0]), 1
64
+
65
+	for len(pat) > 0 {
66
+		var wild bool
67
+		pc, ps := rune(pat[0]), 1
68
+		if pc > 0x7f {
69
+			pc, ps = utf8.DecodeRuneInString(pat)
84 70
 		}
85
-	} else {
86
-		pr, prsz = utf8.RuneError, 0
87
-	}
88
-	// done reading
89
-	for pr != utf8.RuneError {
90
-		switch pr {
91
-		default:
92
-			if srsz == utf8.RuneError {
93
-				return false
94
-			}
95
-			if sr != pr {
96
-				return false
71
+		var sc rune
72
+		var ss int
73
+		if len(str) > 0 {
74
+			sc, ss = rune(str[0]), 1
75
+			if sc > 0x7f {
76
+				sc, ss = utf8.DecodeRuneInString(str)
97 77
 			}
78
+		}
79
+		switch pc {
98 80
 		case '?':
99
-			if srsz == utf8.RuneError {
100
-				return false
81
+			if ss == 0 {
82
+				return rNoMatch
101 83
 			}
102 84
 		case '*':
103
-			return deepMatchRune(str, pattern[prsz:]) ||
104
-				(srsz > 0 && deepMatchRune(str[srsz:], pattern))
105
-		}
106
-		str = str[srsz:]
107
-		pattern = pattern[prsz:]
108
-		// read the next runes
109
-		if len(str) > 0 {
110
-			if str[0] > 0x7f {
111
-				sr, srsz = utf8.DecodeRuneInString(str)
112
-			} else {
113
-				sr, srsz = rune(str[0]), 1
85
+			// Ignore repeating stars.
86
+			for len(pat) > 1 && pat[1] == '*' {
87
+				pat = pat[1:]
114 88
 			}
115
-		} else {
116
-			sr, srsz = utf8.RuneError, 0
117
-		}
118
-		if len(pattern) > 0 {
119
-			if pattern[0] > 0x7f {
120
-				pr, prsz = utf8.DecodeRuneInString(pattern)
121
-			} else {
122
-				pr, prsz = rune(pattern[0]), 1
89
+
90
+			// If this star is the last character then it must be a match.
91
+			if len(pat) == 1 {
92
+				return rMatch
123 93
 			}
124
-		} else {
125
-			pr, prsz = utf8.RuneError, 0
94
+
95
+			// Match and trim any non-wildcard suffix characters.
96
+			var ok bool
97
+			str, pat, ok = matchTrimSuffix(str, pat)
98
+			if !ok {
99
+				return rNoMatch
100
+			}
101
+
102
+			// Check for single star again.
103
+			if len(pat) == 1 {
104
+				return rMatch
105
+			}
106
+
107
+			// Perform recursive wildcard search.
108
+			r := match(str, pat[1:], slen, counter, maxcomp)
109
+			if r != rNoMatch {
110
+				return r
111
+			}
112
+			if len(str) == 0 {
113
+				return rNoMatch
114
+			}
115
+			wild = true
116
+		default:
117
+			if ss == 0 {
118
+				return rNoMatch
119
+			}
120
+			if pc == '\\' {
121
+				pat = pat[ps:]
122
+				pc, ps = utf8.DecodeRuneInString(pat)
123
+				if ps == 0 {
124
+					return rNoMatch
125
+				}
126
+			}
127
+			if sc != pc {
128
+				return rNoMatch
129
+			}
130
+		}
131
+		str = str[ss:]
132
+		if !wild {
133
+			pat = pat[ps:]
126 134
 		}
127
-		// done reading
128 135
 	}
129
-
130
-	return srsz == 0 && prsz == 0
136
+	if len(str) == 0 {
137
+		return rMatch
138
+	}
139
+	return rNoMatch
131 140
 }
132 141
 
133
-var maxRuneBytes = func() []byte {
134
-	b := make([]byte, 4)
135
-	if utf8.EncodeRune(b, '\U0010FFFF') != 4 {
136
-		panic("invalid rune encoding")
142
+// matchTrimSuffix matches and trims any non-wildcard suffix characters.
143
+// Returns the trimed string and pattern.
144
+//
145
+// This is called because the pattern contains extra data after the wildcard
146
+// star. Here we compare any suffix characters in the pattern to the suffix of
147
+// the target string. Basically a reverse match that stops when a wildcard
148
+// character is reached. This is a little trickier than a forward match because
149
+// we need to evaluate an escaped character in reverse.
150
+//
151
+// Any matched characters will be trimmed from both the target
152
+// string and the pattern.
153
+func matchTrimSuffix(str, pat string) (string, string, bool) {
154
+	// It's expected that the pattern has at least two bytes and the first byte
155
+	// is a wildcard star '*'
156
+	match := true
157
+	for len(str) > 0 && len(pat) > 1 {
158
+		pc, ps := utf8.DecodeLastRuneInString(pat)
159
+		var esc bool
160
+		for i := 0; ; i++ {
161
+			if pat[len(pat)-ps-i-1] != '\\' {
162
+				if i&1 == 1 {
163
+					esc = true
164
+					ps++
165
+				}
166
+				break
167
+			}
168
+		}
169
+		if pc == '*' && !esc {
170
+			match = true
171
+			break
172
+		}
173
+		sc, ss := utf8.DecodeLastRuneInString(str)
174
+		if !((pc == '?' && !esc) || pc == sc) {
175
+			match = false
176
+			break
177
+		}
178
+		str = str[:len(str)-ss]
179
+		pat = pat[:len(pat)-ps]
137 180
 	}
138
-	return b
139
-}()
181
+	return str, pat, match
182
+}
183
+
184
+var maxRuneBytes = [...]byte{244, 143, 191, 191}
140 185
 
141 186
 // Allowable parses the pattern and determines the minimum and maximum allowable
142 187
 // values that the pattern can represent.
@@ -157,7 +202,7 @@ func Allowable(pattern string) (min, max string) {
157 202
 		}
158 203
 		if pattern[i] == '?' {
159 204
 			minb = append(minb, 0)
160
-			maxb = append(maxb, maxRuneBytes...)
205
+			maxb = append(maxb, maxRuneBytes[:]...)
161 206
 		} else {
162 207
 			minb = append(minb, pattern[i])
163 208
 			maxb = append(maxb, pattern[i])

+ 7
- 46
vendor/github.com/tidwall/pretty/README.md View File

@@ -79,46 +79,6 @@ Will format the json to:
79 79
 {"name":{"first":"Tom","last":"Anderson"},"age":37,"children":["Sara","Alex","Jack"],"fav.movie":"Deer Hunter","friends":[{"first":"Janet","last":"Murphy","age":44}]}```
80 80
 ```
81 81
 
82
-## Spec
83
-
84
-Spec cleans comments and trailing commas from input JSON, converting it to
85
-valid JSON per the official spec: https://tools.ietf.org/html/rfc8259
86
-
87
-The resulting JSON will always be the same length as the input and it will
88
-include all of the same line breaks at matching offsets. This is to ensure
89
-the result can be later processed by a external parser and that that
90
-parser will report messages or errors with the correct offsets.
91
-
92
-The following example uses a JSON document that has comments and trailing
93
-commas and converts it prior to unmarshalling to using the standard Go
94
-JSON library.
95
-
96
-```go
97
-
98
-data := `
99
-{
100
-  /* Dev Machine */
101
-  "dbInfo": {
102
-    "host": "localhost",
103
-    "port": 5432,          // use full email address
104
-    "username": "josh",
105
-    "password": "pass123", // use a hashed password
106
-  }
107
-  /* Only SMTP Allowed */
108
-  "emailInfo": {
109
-    "email": "josh@example.com",
110
-    "password": "pass123",
111
-    "smtp": "smpt.example.com",
112
-  }
113
-}
114
-`
115
-
116
-err := json.Unmarshal(pretty.Spec(data), &config)
117
-
118
-```
119
-
120
-
121
-
122 82
 ## Customized output
123 83
 
124 84
 There's a `PrettyOptions(json, opts)` function which allows for customizing the output with the following options:
@@ -143,14 +103,15 @@ type Options struct {
143 103
 
144 104
 Benchmarks of Pretty alongside the builtin `encoding/json` Indent/Compact methods.
145 105
 ```
146
-BenchmarkPretty-8            1000000     1283 ns/op      720 B/op      2 allocs/op
147
-BenchmarkUgly-8              3000000      426 ns/op      240 B/op      1 allocs/op
148
-BenchmarkUglyInPlace-8       5000000      340 ns/op        0 B/op      0 allocs/op
149
-BenchmarkJSONIndent-8         300000     4628 ns/op     1069 B/op      4 allocs/op
150
-BenchmarkJSONCompact-8       1000000     2469 ns/op      758 B/op      4 allocs/op
106
+BenchmarkPretty-16           1000000    1034 ns/op    720 B/op     2 allocs/op
107
+BenchmarkPrettySortKeys-16    586797    1983 ns/op   2848 B/op    14 allocs/op
108
+BenchmarkUgly-16             4652365     254 ns/op    240 B/op     1 allocs/op
109
+BenchmarkUglyInPlace-16      6481233     183 ns/op      0 B/op     0 allocs/op
110
+BenchmarkJSONIndent-16        450654    2687 ns/op   1221 B/op     0 allocs/op
111
+BenchmarkJSONCompact-16       685111    1699 ns/op    442 B/op     0 allocs/op
151 112
 ```
152 113
 
153
-*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7.*
114
+*These benchmarks were run on a MacBook Pro 2.4 GHz 8-Core Intel Core i9.*
154 115
 
155 116
 ## Contact
156 117
 Josh Baker [@tidwall](http://twitter.com/tidwall)

+ 110
- 8
vendor/github.com/tidwall/pretty/pretty.go View File

@@ -1,7 +1,10 @@
1 1
 package pretty
2 2
 
3 3
 import (
4
+	"bytes"
5
+	"encoding/json"
4 6
 	"sort"
7
+	"strconv"
5 8
 )
6 9
 
7 10
 // Options is Pretty options
@@ -84,6 +87,14 @@ func ugly(dst, src []byte) []byte {
84 87
 	return dst
85 88
 }
86 89
 
90
+func isNaNOrInf(src []byte) bool {
91
+	return src[0] == 'i' || //Inf
92
+		src[0] == 'I' || // inf
93
+		src[0] == '+' || // +Inf
94
+		src[0] == 'N' || // Nan
95
+		(src[0] == 'n' && len(src) > 1 && src[1] != 'u') // nan
96
+}
97
+
87 98
 func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, indent string, sortkeys bool, tabs, nl, max int) ([]byte, int, int, bool) {
88 99
 	for ; i < len(json); i++ {
89 100
 		if json[i] <= ' ' {
@@ -92,7 +103,8 @@ func appendPrettyAny(buf, json []byte, i int, pretty bool, width int, prefix, in
92 103
 		if json[i] == '"' {
93 104
 			return appendPrettyString(buf, json, i, nl)
94 105
 		}
95
-		if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' {
106
+
107
+		if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' || isNaNOrInf(json[i:]) {
96 108
 			return appendPrettyNumber(buf, json, i, nl)
97 109
 		}
98 110
 		if json[i] == '{' {
@@ -121,6 +133,7 @@ type pair struct {
121 133
 type byKeyVal struct {
122 134
 	sorted bool
123 135
 	json   []byte
136
+	buf    []byte
124 137
 	pairs  []pair
125 138
 }
126 139
 
@@ -128,21 +141,110 @@ func (arr *byKeyVal) Len() int {
128 141
 	return len(arr.pairs)
129 142
 }
130 143
 func (arr *byKeyVal) Less(i, j int) bool {
131
-	key1 := arr.json[arr.pairs[i].kstart+1 : arr.pairs[i].kend-1]
132
-	key2 := arr.json[arr.pairs[j].kstart+1 : arr.pairs[j].kend-1]
133
-	if string(key1) < string(key2) {
144
+	if arr.isLess(i, j, byKey) {
134 145
 		return true
135 146
 	}
136
-	if string(key1) > string(key2) {
147
+	if arr.isLess(j, i, byKey) {
137 148
 		return false
138 149
 	}
139
-	return arr.pairs[i].vstart < arr.pairs[j].vstart
150
+	return arr.isLess(i, j, byVal)
140 151
 }
141 152
 func (arr *byKeyVal) Swap(i, j int) {
142 153
 	arr.pairs[i], arr.pairs[j] = arr.pairs[j], arr.pairs[i]
143 154
 	arr.sorted = true
144 155
 }
145 156
 
157
+type byKind int
158
+
159
+const (
160
+	byKey byKind = 0
161
+	byVal byKind = 1
162
+)
163
+
164
+type jtype int
165
+
166
+const (
167
+	jnull jtype = iota
168
+	jfalse
169
+	jnumber
170
+	jstring
171
+	jtrue
172
+	jjson
173
+)
174
+
175
+func getjtype(v []byte) jtype {
176
+	if len(v) == 0 {
177
+		return jnull
178
+	}
179
+	switch v[0] {
180
+	case '"':
181
+		return jstring
182
+	case 'f':
183
+		return jfalse
184
+	case 't':
185
+		return jtrue
186
+	case 'n':
187
+		return jnull
188
+	case '[', '{':
189
+		return jjson
190
+	default:
191
+		return jnumber
192
+	}
193
+}
194
+
195
+func (arr *byKeyVal) isLess(i, j int, kind byKind) bool {
196
+	k1 := arr.json[arr.pairs[i].kstart:arr.pairs[i].kend]
197
+	k2 := arr.json[arr.pairs[j].kstart:arr.pairs[j].kend]
198
+	var v1, v2 []byte
199
+	if kind == byKey {
200
+		v1 = k1
201
+		v2 = k2
202
+	} else {
203
+		v1 = bytes.TrimSpace(arr.buf[arr.pairs[i].vstart:arr.pairs[i].vend])
204
+		v2 = bytes.TrimSpace(arr.buf[arr.pairs[j].vstart:arr.pairs[j].vend])
205
+		if len(v1) >= len(k1)+1 {
206
+			v1 = bytes.TrimSpace(v1[len(k1)+1:])
207
+		}
208
+		if len(v2) >= len(k2)+1 {
209
+			v2 = bytes.TrimSpace(v2[len(k2)+1:])
210
+		}
211
+	}
212
+	t1 := getjtype(v1)
213
+	t2 := getjtype(v2)
214
+	if t1 < t2 {
215
+		return true
216
+	}
217
+	if t1 > t2 {
218
+		return false
219
+	}
220
+	if t1 == jstring {
221
+		s1 := parsestr(v1)
222
+		s2 := parsestr(v2)
223
+		return string(s1) < string(s2)
224
+	}
225
+	if t1 == jnumber {
226
+		n1, _ := strconv.ParseFloat(string(v1), 64)
227
+		n2, _ := strconv.ParseFloat(string(v2), 64)
228
+		return n1 < n2
229
+	}
230
+	return string(v1) < string(v2)
231
+
232
+}
233
+
234
+func parsestr(s []byte) []byte {
235
+	for i := 1; i < len(s); i++ {
236
+		if s[i] == '\\' {
237
+			var str string
238
+			json.Unmarshal(s, &str)
239
+			return []byte(str)
240
+		}
241
+		if s[i] == '"' {
242
+			return s[1:i]
243
+		}
244
+	}
245
+	return nil
246
+}
247
+
146 248
 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) {
147 249
 	var ok bool
148 250
 	if width > 0 {
@@ -249,7 +351,7 @@ func sortPairs(json, buf []byte, pairs []pair) []byte {
249 351
 	}
250 352
 	vstart := pairs[0].vstart
251 353
 	vend := pairs[len(pairs)-1].vend
252
-	arr := byKeyVal{false, json, pairs}
354
+	arr := byKeyVal{false, json, buf, pairs}
253 355
 	sort.Stable(&arr)
254 356
 	if !arr.sorted {
255 357
 		return buf
@@ -446,7 +548,7 @@ func Color(src []byte, style *Style) []byte {
446 548
 			dst = apnd(dst, src[i])
447 549
 		} else {
448 550
 			var kind byte
449
-			if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' {
551
+			if (src[i] >= '0' && src[i] <= '9') || src[i] == '-' || isNaNOrInf(src[i:]) {
450 552
 				kind = '0'
451 553
 				dst = append(dst, style.Number[0]...)
452 554
 			} else if src[i] == 't' {

+ 7
- 7
vendor/modules.txt View File

@@ -42,23 +42,23 @@ github.com/okzk/sdnotify
42 42
 ## explicit
43 43
 # github.com/stretchr/testify v1.4.0
44 44
 ## explicit
45
-# github.com/tidwall/btree v0.6.0
45
+# github.com/tidwall/btree v0.6.1
46 46
 ## explicit; go 1.16
47 47
 github.com/tidwall/btree
48
-# github.com/tidwall/buntdb v1.2.6
48
+# github.com/tidwall/buntdb v1.2.7
49 49
 ## explicit; go 1.16
50 50
 github.com/tidwall/buntdb
51
-# github.com/tidwall/gjson v1.8.0
51
+# github.com/tidwall/gjson v1.10.2
52 52
 ## explicit; go 1.12
53 53
 github.com/tidwall/gjson
54
-# github.com/tidwall/grect v0.1.2
54
+# github.com/tidwall/grect v0.1.3
55 55
 ## explicit; go 1.15
56 56
 github.com/tidwall/grect
57
-# github.com/tidwall/match v1.0.3
57
+# github.com/tidwall/match v1.1.1
58 58
 ## explicit; go 1.15
59 59
 github.com/tidwall/match
60
-# github.com/tidwall/pretty v1.1.0
61
-## explicit
60
+# github.com/tidwall/pretty v1.2.0
61
+## explicit; go 1.16
62 62
 github.com/tidwall/pretty
63 63
 # github.com/tidwall/rtred v0.1.2
64 64
 ## explicit; go 1.15

Loading…
Cancel
Save