added stack frame. faster

This commit is contained in:
Josh Baker 2016-08-11 10:39:38 -07:00
parent f4afb106da
commit a4005bcf0f
2 changed files with 51 additions and 51 deletions

View File

@ -103,7 +103,7 @@ result.Raw // holds the raw json
Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/), [ffjson](https://github.com/pquerna/ffjson), and [EasyJSON](https://github.com/mailru/easyjson). Benchmarks of GJSON alongside [encoding/json](https://golang.org/pkg/encoding/json/), [ffjson](https://github.com/pquerna/ffjson), and [EasyJSON](https://github.com/mailru/easyjson).
``` ```
BenchmarkGJSONGet-8 3000000 477 ns/op 0 B/op 0 allocs/op BenchmarkGJSONGet-8 3000000 440 ns/op 0 B/op 0 allocs/op
BenchmarkJSONUnmarshalMap-8 600000 10738 ns/op 3176 B/op 69 allocs/op BenchmarkJSONUnmarshalMap-8 600000 10738 ns/op 3176 B/op 69 allocs/op
BenchmarkJSONUnmarshalStruct-8 600000 11635 ns/op 1960 B/op 69 allocs/op BenchmarkJSONUnmarshalStruct-8 600000 11635 ns/op 1960 B/op 69 allocs/op
BenchmarkJSONDecoder-8 300000 17193 ns/op 4864 B/op 184 allocs/op BenchmarkJSONDecoder-8 300000 17193 ns/op 4864 B/op 184 allocs/op

100
gjson.go
View File

@ -76,6 +76,17 @@ func (t Result) Value() interface{} {
} }
} }
type part struct {
wild bool
key string
}
type frame struct {
key string
count int
stype byte
}
// Get searches json for the specified path. // Get searches json for the specified path.
// A path is in dot syntax, such as "name.last" or "age". // 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. // This function expects that the json is well-formed, and does not validate.
@ -99,33 +110,44 @@ func (t Result) Value() interface{} {
// "c?ildren.0" >> "Sara" // "c?ildren.0" >> "Sara"
// //
func Get(json string, path string) Result { func Get(json string, path string) Result {
var i, s, depth int var s int
var squashed string
var key string
var stype byte
var count int
var wild bool var wild bool
var matched bool var parts = make([]part, 0, 4)
var parts = make([]string, 0, 4)
var wilds = make([]bool, 0, 4)
var keys = make([]string, 0, 4)
var stypes = make([]byte, 0, 4)
var counts = make([]int, 0, 4)
// do nothing when no path specified // do nothing when no path specified
if len(path) == 0 { if len(path) == 0 {
return Result{} // nothing return Result{} // nothing
} }
// parse the path. just split on the dot
for i := 0; i < len(path); i++ {
if path[i] == '.' {
parts = append(parts, part{wild: wild, key: path[s:i]})
if wild {
wild = false
}
s = i + 1
} else if path[i] == '*' || path[i] == '?' {
wild = true
}
}
parts = append(parts, part{wild: wild, key: path[s:]})
var i, depth int
var squashed string
var f frame
var matched bool
var stack = make([]frame, 0, 4)
depth = 1 depth = 1
// look for first delimiter // look for first delimiter
for ; i < len(json); i++ { for ; i < len(json); i++ {
if json[i] > ' ' { if json[i] > ' ' {
if json[i] == '{' { if json[i] == '{' {
stype = '{' f.stype = '{'
} else if json[i] == '[' { } else if json[i] == '[' {
stype = '[' f.stype = '['
} else { } else {
// not a valid type // not a valid type
return Result{} return Result{}
@ -135,30 +157,13 @@ func Get(json string, path string) Result {
} }
} }
stypes = append(stypes, stype) stack = append(stack, f)
counts = append(counts, count)
// parse the path. just split on the dot
for i := 0; i < len(path); i++ {
if path[i] == '.' {
parts = append(parts, path[s:i])
wilds = append(wilds, wild)
if wild {
wild = false
}
s = i + 1
} else if path[i] == '*' || path[i] == '?' {
wild = true
}
}
parts = append(parts, path[s:])
wilds = append(wilds, wild)
// search for key // search for key
read_key: read_key:
if stype == '[' { if f.stype == '[' {
key = strconv.FormatInt(int64(count), 10) f.key = strconv.FormatInt(int64(f.count), 10)
count++ f.count++
} else { } else {
for ; i < len(json); i++ { for ; i < len(json); i++ {
if json[i] == '"' { if json[i] == '"' {
@ -169,7 +174,7 @@ read_key:
s = i s = i
for ; i < len(json); i++ { for ; i < len(json); i++ {
if json[i] == '"' { if json[i] == '"' {
key = json[s:i] f.key = json[s:i]
i++ i++
break break
} }
@ -193,7 +198,7 @@ read_key:
break break
} }
} }
key = unescape(json[s:i]) f.key = unescape(json[s:i])
i++ i++
break break
} }
@ -206,11 +211,11 @@ read_key:
// we have a brand new key. // we have a brand new key.
// is it the key that we are looking for? // is it the key that we are looking for?
if wilds[depth-1] { if parts[depth-1].wild {
// it's a wildcard path element // it's a wildcard path element
matched = wildcardMatch(key, parts[depth-1]) matched = wildcardMatch(f.key, parts[depth-1].key)
} else { } else {
matched = parts[depth-1] == key matched = parts[depth-1].key == f.key
} }
// read to the value token // read to the value token
@ -377,10 +382,8 @@ proc_val:
// can only deep search objects // can only deep search objects
// return Result{} // return Result{}
} else { } else {
stype = vc f.stype = vc
keys = append(keys, key) stack = append(stack, f)
stypes = append(stypes, stype)
counts = append(counts, count)
depth++ depth++
goto read_key goto read_key
} }
@ -416,19 +419,16 @@ proc_val:
for ; i < len(json); i++ { for ; i < len(json); i++ {
switch json[i] { switch json[i] {
case '}', ']': case '}', ']':
if parts[depth-1] == "#" { if parts[depth-1].key == "#" {
return Result{Type: Number, Num: float64(count)} return Result{Type: Number, Num: float64(f.count)}
} }
// step the stack back // step the stack back
depth-- depth--
if depth == 0 { if depth == 0 {
return Result{} return Result{}
} }
keys = keys[:len(keys)-1] stack = stack[:len(stack)-1]
stypes = stypes[:len(stypes)-1] f = stack[len(stack)-1]
counts = counts[:len(counts)-1]
stype = stypes[len(stypes)-1]
count = counts[len(counts)-1]
case ',': case ',':
i++ i++
goto read_key goto read_key