mirror of https://github.com/tidwall/tile38.git
Merge pull request #501 from JordanArmstrong/strict-jset-numbers
Strictly check if values passed to JSET are numbers
This commit is contained in:
commit
90c2474e3d
|
@ -40,6 +40,83 @@ func jsonString(s string) string {
|
||||||
b[len(b)-1] = '"'
|
b[len(b)-1] = '"'
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isJsonNumber(data string) bool {
|
||||||
|
// Returns true if the given string can be encoded as a JSON number value.
|
||||||
|
// See:
|
||||||
|
// https://json.org
|
||||||
|
// http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||||
|
if data == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
// sign
|
||||||
|
if data[i] == '-' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == len(data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// int
|
||||||
|
if data[i] == '0' {
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if data[i] >= '0' && data[i] <= '9' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// frac
|
||||||
|
if i == len(data) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if data[i] == '.' {
|
||||||
|
i++
|
||||||
|
if i == len(data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if data[i] < '0' || data[i] > '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if data[i] >= '0' && data[i] <= '9' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// exp
|
||||||
|
if i == len(data) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if data[i] == 'e' || data[i] == 'E' {
|
||||||
|
i++
|
||||||
|
if i == len(data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if data[i] == '+' || data[i] == '-' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i == len(data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if data[i] < '0' || data[i] > '9' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
for ; i < len(data); i++ {
|
||||||
|
if data[i] >= '0' && data[i] <= '9' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i == len(data)
|
||||||
|
}
|
||||||
|
|
||||||
func appendJSONSimpleBounds(dst []byte, o geojson.Object) []byte {
|
func appendJSONSimpleBounds(dst []byte, o geojson.Object) []byte {
|
||||||
bbox := o.Rect()
|
bbox := o.Rect()
|
||||||
dst = append(dst, `{"sw":{"lat":`...)
|
dst = append(dst, `{"sw":{"lat":`...)
|
||||||
|
@ -183,13 +260,7 @@ func (c *Server) cmdJset(msg *Message) (res resp.Value, d commandDetails, err er
|
||||||
if !str && !raw {
|
if !str && !raw {
|
||||||
switch val {
|
switch val {
|
||||||
default:
|
default:
|
||||||
if len(val) > 0 {
|
raw = isJsonNumber(val)
|
||||||
if (val[0] >= '0' && val[0] <= '9') || val[0] == '-' {
|
|
||||||
if _, err := strconv.ParseFloat(val, 64); err == nil {
|
|
||||||
raw = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "true", "false", "null":
|
case "true", "false", "null":
|
||||||
raw = true
|
raw = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,3 +18,29 @@ func BenchmarkJSONMarshal(t *testing.B) {
|
||||||
json.Marshal(s)
|
json.Marshal(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsJsonNumber(t *testing.T) {
|
||||||
|
test := func(expected bool, val string) {
|
||||||
|
actual := isJsonNumber(val)
|
||||||
|
if expected != actual {
|
||||||
|
t.Fatalf("Expected %t == isJsonNumber(\"%s\") but was %t", expected, val, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test(false, "")
|
||||||
|
test(false, "-")
|
||||||
|
test(false, "foo")
|
||||||
|
test(false, "0123")
|
||||||
|
test(false, "1.")
|
||||||
|
test(false, "1.0e")
|
||||||
|
test(false, "1.0e-")
|
||||||
|
test(false, "1.0E10NaN")
|
||||||
|
test(false, "1.0ENaN")
|
||||||
|
test(true, "-1")
|
||||||
|
test(true, "0")
|
||||||
|
test(true, "0.0")
|
||||||
|
test(true, "42")
|
||||||
|
test(true, "1.0E10")
|
||||||
|
test(true, "1.0e10")
|
||||||
|
test(true, "1E+5")
|
||||||
|
test(true, "1E-10")
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ import "testing"
|
||||||
func subTestJSON(t *testing.T, mc *mockServer) {
|
func subTestJSON(t *testing.T, mc *mockServer) {
|
||||||
runStep(t, mc, "basic", json_JSET_basic_test)
|
runStep(t, mc, "basic", json_JSET_basic_test)
|
||||||
runStep(t, mc, "geojson", json_JSET_geojson_test)
|
runStep(t, mc, "geojson", json_JSET_geojson_test)
|
||||||
|
runStep(t, mc, "number", json_JSET_number_test)
|
||||||
|
|
||||||
}
|
}
|
||||||
func json_JSET_basic_test(mc *mockServer) error {
|
func json_JSET_basic_test(mc *mockServer) error {
|
||||||
return mc.DoBatch([][]interface{}{
|
return mc.DoBatch([][]interface{}{
|
||||||
|
@ -39,3 +41,16 @@ func json_JSET_geojson_test(mc *mockServer) error {
|
||||||
{"JDEL", "mykey", "myid1", "type"}, {"ERR missing type"},
|
{"JDEL", "mykey", "myid1", "type"}, {"ERR missing type"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func json_JSET_number_test(mc *mockServer) error {
|
||||||
|
return mc.DoBatch([][]interface{}{
|
||||||
|
{"JSET", "mykey", "myid1", "hello", "0"}, {"OK"},
|
||||||
|
{"JGET", "mykey", "myid1"}, {`{"hello":0}`},
|
||||||
|
{"JSET", "mykey", "myid1", "hello", "0123"}, {"OK"},
|
||||||
|
{"JGET", "mykey", "myid1"}, {`{"hello":"0123"}`},
|
||||||
|
{"JSET", "mykey", "myid1", "hello", "3.14"}, {"OK"},
|
||||||
|
{"JGET", "mykey", "myid1"}, {`{"hello":3.14}`},
|
||||||
|
{"JSET", "mykey", "myid1", "hello", "1.0e10"}, {"OK"},
|
||||||
|
{"JGET", "mykey", "myid1"}, {`{"hello":1.0e10}`},
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue