From aa7191b1eeb2de1f25a7a4730e43a089df7f3beb Mon Sep 17 00:00:00 2001 From: Eric Fischer <enf@pobox.com> Date: Thu, 9 Nov 2017 12:49:09 -0800 Subject: [PATCH] Also test large integers. Work around an apparent bug in strtoull. --- mvt.cpp | 44 +++++++++++++++++++++++++++++++++++-- mvt.hpp | 3 +++ read_json.cpp | 24 ++++++++++++++++++-- tests/overflow/in.json | 12 ++++++++++ tests/overflow/out/-z0.json | 26 +++++++++++++++++++++- 5 files changed, 104 insertions(+), 5 deletions(-) diff --git a/mvt.cpp b/mvt.cpp index 27ee21a..d1d7452 100644 --- a/mvt.cpp +++ b/mvt.cpp @@ -472,7 +472,7 @@ void mvt_layer::tag(mvt_feature &feature, std::string key, mvt_value value) { feature.tags.push_back(vo); } -static int is_integer(const char *s, long long *v) { +bool is_integer(const char *s, long long *v) { errno = 0; char *endptr; @@ -480,7 +480,47 @@ static int is_integer(const char *s, long long *v) { if (*v == 0 && errno != 0) { return 0; } - if ((*v == LLONG_MIN || *v == LLONG_MAX) && (errno == ERANGE)) { + if ((*v == LLONG_MIN || *v == LLONG_MAX) && (errno == ERANGE || errno == EINVAL)) { + return 0; + } + if (*endptr != '\0') { + // Special case: If it is an integer followed by .0000 or similar, + // it is still an integer + + if (*endptr != '.') { + return 0; + } + endptr++; + for (; *endptr != '\0'; endptr++) { + if (*endptr != '0') { + return 0; + } + } + + return 1; + } + + return 1; +} + +bool is_unsigned_integer(const char *s, unsigned long long *v) { + errno = 0; + char *endptr; + + // Special check because MacOS stroull() returns 1 + // for -18446744073709551615 + while (isspace(*s)) { + s++; + } + if (*s == '-') { + return 0; + } + + *v = strtoull(s, &endptr, 0); + if (*v == 0 && errno != 0) { + return 0; + } + if ((*v == ULLONG_MAX) && (errno == ERANGE || errno == EINVAL)) { return 0; } if (*endptr != '\0') { diff --git a/mvt.hpp b/mvt.hpp index 32ef55f..78539a9 100644 --- a/mvt.hpp +++ b/mvt.hpp @@ -111,4 +111,7 @@ int compress(std::string const &input, std::string &output); int dezig(unsigned n); mvt_value stringified_to_mvt_value(int type, const char *s); + +bool is_integer(const char *s, long long *v); +bool is_unsigned_integer(const char *s, unsigned long long *v); #endif diff --git a/read_json.cpp b/read_json.cpp index bf70e78..5cc63a9 100644 --- a/read_json.cpp +++ b/read_json.cpp @@ -105,7 +105,17 @@ void parse_geometry(int t, json_object *j, drawvec &out, int op, const char *fna void canonicalize(json_object *o) { if (o->type == JSON_NUMBER) { - std::string s = milo::dtoa_milo(o->number); + std::string s; + long long v; + unsigned long long uv; + + if (is_integer(o->string, &v)) { + s = std::to_string(v); + } else if (is_unsigned_integer(o->string, &uv)) { + s = std::to_string(uv); + } else { + s = milo::dtoa_milo(o->number); + } free(o->string); o->string = strdup(s.c_str()); } else if (o->type == JSON_HASH) { @@ -150,7 +160,17 @@ void stringify_value(json_object *value, int &type, std::string &stringified, co } } else if (vt == JSON_NUMBER) { type = mvt_double; - stringified = milo::dtoa_milo(value->number); + + long long v; + unsigned long long uv; + + if (is_integer(value->string, &v)) { + stringified = std::to_string(v); + } else if (is_unsigned_integer(value->string, &uv)) { + stringified = std::to_string(uv); + } else { + stringified = milo::dtoa_milo(value->number); + } } else if (vt == JSON_TRUE || vt == JSON_FALSE) { type = mvt_bool; stringified = val; diff --git a/tests/overflow/in.json b/tests/overflow/in.json index cbf7d63..9aeffd2 100644 --- a/tests/overflow/in.json +++ b/tests/overflow/in.json @@ -1,3 +1,15 @@ { "type": "Feature", "properties": { "excess": 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } { "type": "Feature", "properties": { "excess": 22e291 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } { "type": "Feature", "properties": { "excess": 2.5 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": 2147483648 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": -2147483648 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": 2147483647 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": -2147483647 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": 18446744073709551616 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": -18446744073709551616 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": 18446744073709551615 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": -18446744073709551615 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": 9223372036854775808 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": -9223372036854775808 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": 9223372036854775807 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } +{ "type": "Feature", "properties": { "excess": -9223372036854775807 }, "geometry": { "type": "Point", "coordinates": [ 0,0 ] } } diff --git a/tests/overflow/out/-z0.json b/tests/overflow/out/-z0.json index 94df132..a6f6cdc 100644 --- a/tests/overflow/out/-z0.json +++ b/tests/overflow/out/-z0.json @@ -3,7 +3,7 @@ "center": "0.000000,0.000000,0", "description": "tests/overflow/out/-z0.json.check.mbtiles", "format": "pbf", -"json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {\"excess\": \"Number\"} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"in\",\"count\": 3,\"geometry\": \"Point\",\"attributeCount\": 1,\"attributes\": [{\"attribute\": \"excess\",\"count\": 3,\"type\": \"number\",\"values\": [2.2222222222222223e+291,2.2e+292,2.5],\"min\": 2.5,\"max\": 2.2e+292}]}]}}", +"json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {\"excess\": \"Number\"} } ],\"tilestats\": {\"layerCount\": 1,\"layers\": [{\"layer\": \"in\",\"count\": 15,\"geometry\": \"Point\",\"attributeCount\": 1,\"attributes\": [{\"attribute\": \"excess\",\"count\": 14,\"type\": \"number\",\"values\": [-18446744073709553000,-2147483647,-2147483648,-9223372036854775807,-9223372036854775808,18446744073709551615,18446744073709553000,2.2222222222222223e+291,2.2e+292,2.5,2147483647,2147483648,9223372036854775807,9223372036854775808],\"min\": -18446744073709553000,\"max\": 2.2e+292}]}]}}", "maxzoom": "0", "minzoom": "0", "name": "tests/overflow/out/-z0.json.check.mbtiles", @@ -17,6 +17,30 @@ { "type": "Feature", "properties": { "excess": 2.2e+292 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } , { "type": "Feature", "properties": { "excess": 2.5 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": 2147483648 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": -2147483648 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": 2147483647 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": -2147483647 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": 18446744073709553000 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": -18446744073709553000 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": 18446744073709553000 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": -18446744073709553000 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": 9223372036854776000 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": -9223372036854775808 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": 9223372036854775807 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } +, +{ "type": "Feature", "properties": { "excess": -9223372036854775807 }, "geometry": { "type": "Point", "coordinates": [ 0.000000, 0.000000 ] } } ] } ] } ] }