Skip to content

Commit

Permalink
Generally faster parsing
Browse files Browse the repository at this point in the history
This commit includes an optimization that increases overall
performance.

The gains are roughly between 20% to 300% depending on the size
of the JSON document. Larger documents will see the greates gains,
particularly when searching for keys that are deeply embedded, or
near the end of the document.
  • Loading branch information
tidwall committed Oct 2, 2024
1 parent 92dff34 commit 133f42c
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 34 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/js
and [json-iterator](https://github.com/json-iterator/go)

```
BenchmarkGJSONGet-10 14919366 240.9 ns/op 0 B/op 0 allocs/op
BenchmarkGJSONGet-10 17893731 202.1 ns/op 0 B/op 0 allocs/op
BenchmarkGJSONUnmarshalMap-10 1663548 2157 ns/op 1920 B/op 26 allocs/op
BenchmarkJSONUnmarshalMap-10 832236 4279 ns/op 2920 B/op 68 allocs/op
BenchmarkJSONUnmarshalStruct-10 1076475 3219 ns/op 920 B/op 12 allocs/op
Expand Down Expand Up @@ -489,6 +489,4 @@ widget.text.onMouseUp

**

Last run: Oct 1, 2024

*These benchmarks were run on a MacBook Pro M1 Max using Go 1.22 and can be found [here](https://github.com/tidwall/gjson-benchmarks).*
160 changes: 129 additions & 31 deletions gjson.go
Original file line number Diff line number Diff line change
Expand Up @@ -1040,50 +1040,148 @@ func parseObjectPath(path string) (r objectPathResult) {
return
}

var vchars = [256]byte{
'"': 2, '{': 3, '(': 3, '[': 3, '}': 1, ')': 1, ']': 1,
}

func parseSquash(json string, i int) (int, string) {
// expects that the lead character is a '[' or '{' or '('
// squash the value, ignoring all nested arrays and objects.
// the first '[' or '{' or '(' has already been read
s := i
i++
depth := 1
for ; i < len(json); i++ {
if json[i] >= '"' && json[i] <= '}' {
switch json[i] {
case '"':
var c byte
for i < len(json) {
for i < len(json)-8 {
jslice := json[i : i+8]
c = vchars[jslice[0]]
if c != 0 {
i += 0
goto token
}
c = vchars[jslice[1]]
if c != 0 {
i += 1
goto token
}
c = vchars[jslice[2]]
if c != 0 {
i += 2
goto token
}
c = vchars[jslice[3]]
if c != 0 {
i += 3
goto token
}
c = vchars[jslice[4]]
if c != 0 {
i += 4
goto token
}
c = vchars[jslice[5]]
if c != 0 {
i += 5
goto token
}
c = vchars[jslice[6]]
if c != 0 {
i += 6
goto token
}
c = vchars[jslice[7]]
if c != 0 {
i += 7
goto token
}
i += 8
}
c = vchars[json[i]]
if c == 0 {
i++
continue
}
token:
if c == 2 {
// '"' string
i++
s2 := i
nextquote:
for i < len(json)-8 {
jslice := json[i : i+8]
if jslice[0] == '"' {
i += 0
goto strchkesc
}
if jslice[1] == '"' {
i += 1
goto strchkesc
}
if jslice[2] == '"' {
i += 2
goto strchkesc
}
if jslice[3] == '"' {
i += 3
goto strchkesc
}
if jslice[4] == '"' {
i += 4
goto strchkesc
}
if jslice[5] == '"' {
i += 5
goto strchkesc
}
if jslice[6] == '"' {
i += 6
goto strchkesc
}
if jslice[7] == '"' {
i += 7
goto strchkesc
}
i += 8
}
goto strchkstd
strchkesc:
if json[i-1] != '\\' {
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
}
continue
}
strchkstd:
for i < len(json) {
if json[i] > '\\' || json[i] != '"' {
i++
continue
}
// look for an escaped slash
if json[i-1] == '\\' {
n := 0
for j := i - 2; j > s2-1; j-- {
if json[j] != '\\' {
break
}
break
n++
}
if n%2 == 0 {
i++
goto nextquote
}
}
case '{', '[', '(':
depth++
case '}', ']', ')':
depth--
if depth == 0 {
i++
return i, json[s:i]
}
break
}
} else {
// '{', '[', '(', '}', ']', ')'
// open close tokens
depth += int(c) - 2
if depth == 0 {
i++
return i, json[s:i]
}
}
i++
}
return i, json[s:]
}
Expand Down

0 comments on commit 133f42c

Please sign in to comment.