Ensure Parse handles NaN/Inf and returns correct Index

This commit is contained in:
tidwall 2021-10-28 09:09:00 -07:00
parent 0b52f9a361
commit 7cadbb5756
2 changed files with 73 additions and 24 deletions

View File

@ -426,7 +426,8 @@ end:
// use the Valid function first. // use the Valid function first.
func Parse(json string) Result { func Parse(json string) Result {
var value Result var value Result
for i := 0; i < len(json); i++ { i := 0
for ; i < len(json); i++ {
if json[i] == '{' || json[i] == '[' { if json[i] == '{' || json[i] == '[' {
value.Type = JSON value.Type = JSON
value.Raw = json[i:] // just take the entire raw value.Raw = json[i:] // just take the entire raw
@ -436,16 +437,20 @@ func Parse(json string) Result {
continue continue
} }
switch json[i] { switch json[i] {
default: case '+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
if (json[i] >= '0' && json[i] <= '9') || json[i] == '-' { 'i', 'I', 'N':
value.Type = Number
value.Raw, value.Num = tonum(json[i:])
case 'n':
if i+1 < len(json) && json[i+1] != 'u' {
// nan
value.Type = Number value.Type = Number
value.Raw, value.Num = tonum(json[i:]) value.Raw, value.Num = tonum(json[i:])
} else { } else {
return Result{} // null
}
case 'n':
value.Type = Null value.Type = Null
value.Raw = tolit(json[i:]) value.Raw = tolit(json[i:])
}
case 't': case 't':
value.Type = True value.Type = True
value.Raw = tolit(json[i:]) value.Raw = tolit(json[i:])
@ -455,9 +460,14 @@ func Parse(json string) Result {
case '"': case '"':
value.Type = String value.Type = String
value.Raw, value.Str = tostr(json[i:]) value.Raw, value.Str = tostr(json[i:])
default:
return Result{}
} }
break break
} }
if value.Exists() {
value.Index = i
}
return value return value
} }
@ -531,21 +541,13 @@ func tonum(json string) (raw string, num float64) {
return return
} }
// could be a '+' or '-'. let's assume so. // could be a '+' or '-'. let's assume so.
continue } else if json[i] == ']' || json[i] == '}' {
} // break on ']' or '}'
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] raw = json[:i]
num, _ = strconv.ParseFloat(raw, 64) num, _ = strconv.ParseFloat(raw, 64)
return return
} }
}
raw = json raw = json
num, _ = strconv.ParseFloat(raw, 64) num, _ = strconv.ParseFloat(raw, 64)
return return
@ -2971,3 +2973,31 @@ func stringBytes(s string) []byte {
func bytesString(b []byte) string { func bytesString(b []byte) string {
return *(*string)(unsafe.Pointer(&b)) return *(*string)(unsafe.Pointer(&b))
} }
func GetPath(r Result, json string) (string, bool) {
if len(r.Raw) == 0 || len(json) == 0 {
return "", false
}
p := uintptr((*(*stringHeader)(unsafe.Pointer(&(r.Raw)))).data)
s := uintptr((*(*stringHeader)(unsafe.Pointer(&(json)))).data)
e := s + uintptr(len(json))
if p < s || p >= e {
return "", false
}
i := int(p - s)
_ = i
// for ; i >= 0; i-- {
// if json[i] <= ' ' {
// } else if json[i] == ':' {
// // inside of an object, read the key string
// } else if json[i] == ',' {
// // array-value
// } else if json[i] == '[' {
// // array-value (end or array)
// } else {
// }
// }
// return "", false
return "", false
}

View File

@ -717,10 +717,6 @@ var exampleJSON = `{
} }
}` }`
func TestNewParse(t *testing.T) {
//fmt.Printf("%v\n", parse2(exampleJSON, "widget").String())
}
func TestUnmarshalMap(t *testing.T) { func TestUnmarshalMap(t *testing.T) {
var m1 = Parse(exampleJSON).Value().(map[string]interface{}) var m1 = Parse(exampleJSON).Value().(map[string]interface{})
var m2 map[string]interface{} var m2 map[string]interface{}
@ -2250,16 +2246,28 @@ func TestNaNInf(t *testing.T) {
r := Get(json, fmt.Sprintf("%d", i)) r := Get(json, fmt.Sprintf("%d", i))
assert(t, r.Raw == raws[i]) assert(t, r.Raw == raws[i])
assert(t, r.Num == nums[i] || (math.IsNaN(r.Num) && math.IsNaN(nums[i]))) assert(t, r.Num == nums[i] || (math.IsNaN(r.Num) && math.IsNaN(nums[i])))
assert(t, r.Type == Number)
} }
var i int var i int
Parse(json).ForEach(func(_, r Result) bool { Parse(json).ForEach(func(_, r Result) bool {
assert(t, r.Raw == raws[i]) assert(t, r.Raw == raws[i])
assert(t, r.Num == nums[i] || (math.IsNaN(r.Num) && math.IsNaN(nums[i]))) assert(t, r.Num == nums[i] || (math.IsNaN(r.Num) && math.IsNaN(nums[i])))
assert(t, r.Type == Number)
i++ i++
return true return true
}) })
// Parse should also return valid numbers
assert(t, math.IsNaN(Parse("nan").Float()))
assert(t, math.IsNaN(Parse("NaN").Float()))
assert(t, math.IsNaN(Parse(" NaN").Float()))
assert(t, math.IsInf(Parse("+inf").Float(), +1))
assert(t, math.IsInf(Parse("-inf").Float(), -1))
assert(t, math.IsInf(Parse("+INF").Float(), +1))
assert(t, math.IsInf(Parse("-INF").Float(), -1))
assert(t, math.IsInf(Parse(" +INF").Float(), +1))
assert(t, math.IsInf(Parse(" -INF").Float(), -1))
} }
func TestEmptyValueQuery(t *testing.T) { func TestEmptyValueQuery(t *testing.T) {
@ -2273,3 +2281,14 @@ func TestEmptyValueQuery(t *testing.T) {
`#(!=)#`).Raw == `#(!=)#`).Raw ==
`["ig","tw","fb","tw","ig","tw"]`) `["ig","tw","fb","tw","ig","tw"]`)
} }
func TestParseIndex(t *testing.T) {
assert(t, Parse(`{}`).Index == 0)
assert(t, Parse(` {}`).Index == 1)
assert(t, Parse(` []`).Index == 1)
assert(t, Parse(` true`).Index == 1)
assert(t, Parse(` false`).Index == 1)
assert(t, Parse(` null`).Index == 1)
assert(t, Parse(` +inf`).Index == 1)
assert(t, Parse(` -inf`).Index == 1)
}