diff --git a/Makefile b/Makefile index 0b988399..b0a2a0b8 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ clean: rm -f ./tippecanoe ./tippecanoe-* ./tile-join ./unit *.o *.d */*.o */*.d tests/**/*.mbtiles tests/**/*.check indent: - clang-format -i -style="{BasedOnStyle: Google, IndentWidth: 8, UseTab: Always, AllowShortIfStatementsOnASingleLine: false, ColumnLimit: 0, ContinuationIndentWidth: 8, SpaceAfterCStyleCast: true, IndentCaseLabels: false, AllowShortBlocksOnASingleLine: false, AllowShortFunctionsOnASingleLine: false, SortIncludes: false}" $(filter-out flatgeobuf.cpp,$(C)) $(H) jsonpull/*.[ch] + clang-format -i -style="{BasedOnStyle: Google, IndentWidth: 8, UseTab: Always, AllowShortIfStatementsOnASingleLine: false, ColumnLimit: 0, ContinuationIndentWidth: 8, SpaceAfterCStyleCast: true, IndentCaseLabels: false, AllowShortBlocksOnASingleLine: false, AllowShortFunctionsOnASingleLine: false, SortIncludes: false}" $(filter-out flatgeobuf.cpp,$(C)) $(H) jsonpull/jsonpull.h jsonpull/jsonpull.cpp TESTS = $(wildcard tests/*/out/*.json) SPACE = $(NULL) $(NULL) diff --git a/attribute.cpp b/attribute.cpp index 53c9dae5..bf53d399 100644 --- a/attribute.cpp +++ b/attribute.cpp @@ -42,10 +42,10 @@ void set_attribute_accum(std::unordered_map &attribut void set_attribute_accum(std::unordered_map &attribute_accum, const char *arg, char **argv) { if (*arg == '{') { - json_pull *jp = json_begin_string(arg); - json_object *o = json_read_tree(jp); + json_pull_ptr jp = json_begin_string(arg); + json_object_ptr o = json_read_tree(jp); - if (o == NULL) { + if (o == nullptr) { fprintf(stderr, "%s: -E%s: %s\n", *argv, arg, jp->error); exit(EXIT_JSON); } @@ -55,9 +55,9 @@ void set_attribute_accum(std::unordered_map &attribut exit(EXIT_JSON); } - for (size_t i = 0; i < o->value.object.length; i++) { - json_object *k = o->value.object.keys[i]; - json_object *v = o->value.object.values[i]; + for (size_t i = 0; i < o->value.object.keys.size(); i++) { + json_object_ptr k = o->value.object.keys[i]; + json_object_ptr v = o->value.object.values[i]; if (k->type != JSON_STRING) { fprintf(stderr, "%s: -E%s: key %zu not a string\n", *argv, arg, i); @@ -68,11 +68,9 @@ void set_attribute_accum(std::unordered_map &attribut exit(EXIT_JSON); } - set_attribute_accum(attribute_accum, k->value.string.string, v->value.string.string); + set_attribute_accum(attribute_accum, k->value.string.string.c_str(), v->value.string.string.c_str()); } - json_free(o); - json_end(jp); return; } diff --git a/clip.cpp b/clip.cpp index 72a1fb3a..a50a7eda 100644 --- a/clip.cpp +++ b/clip.cpp @@ -1221,7 +1221,7 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int n std::vector const &exclude_prefix, bool do_compress, std::vector> *next_overzoomed_tiles, - bool demultiply, json_object *filter, bool preserve_input_order, + bool demultiply, json_object_ptr filter, bool preserve_input_order, std::unordered_map const &attribute_accum, std::vector const &unidecode_data, double simplification, double tiny_polygon_size, @@ -1457,7 +1457,7 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int std::vector const &exclude_prefix, bool do_compress, std::vector> *next_overzoomed_tiles, - bool demultiply, json_object *filter, bool preserve_input_order, + bool demultiply, json_object_ptr filter, bool preserve_input_order, std::unordered_map const &attribute_accum, std::vector const &unidecode_data, double simplification, double tiny_polygon_size, diff --git a/dirtiles.cpp b/dirtiles.cpp index 98138bd5..da7c82af 100644 --- a/dirtiles.cpp +++ b/dirtiles.cpp @@ -248,9 +248,9 @@ sqlite3 *dirmeta2tmp(const char *fname) { if (f == NULL) { perror(name.c_str()); } else { - json_pull *jp = json_begin_file(f); - json_object *o = json_read_tree(jp); - if (o == NULL) { + json_pull_ptr jp = json_begin_file(f); + json_object_ptr o = json_read_tree(jp); + if (o == nullptr) { fprintf(stderr, "%s: metadata parsing error: %s\n", name.c_str(), jp->error); exit(EXIT_JSON); } @@ -260,19 +260,18 @@ sqlite3 *dirmeta2tmp(const char *fname) { exit(EXIT_JSON); } - for (size_t i = 0; i < o->value.object.length; i++) { + for (size_t i = 0; i < o->value.object.keys.size(); i++) { if (o->value.object.keys[i]->type != JSON_STRING || o->value.object.values[i]->type != JSON_STRING) { fprintf(stderr, "%s: non-string in metadata\n", name.c_str()); } - char *sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES (%Q, %Q);", o->value.object.keys[i]->value.string.string, o->value.object.values[i]->value.string.string); + char *sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES (%Q, %Q);", o->value.object.keys[i]->value.string.string.c_str(), o->value.object.values[i]->value.string.string.c_str()); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { - fprintf(stderr, "set %s in metadata: %s\n", o->value.object.keys[i]->value.string.string, err); + fprintf(stderr, "set %s in metadata: %s\n", o->value.object.keys[i]->value.string.string.c_str(), err); } sqlite3_free(sql); } - json_end(jp); fclose(f); } diff --git a/evaluator.cpp b/evaluator.cpp index 38566ae2..0d99409c 100644 --- a/evaluator.cpp +++ b/evaluator.cpp @@ -9,7 +9,7 @@ #include "milo/dtoa_milo.h" #include "text.hpp" -int compare(mvt_value const &one, json_object *two, bool &fail) { +int compare(mvt_value const &one, json_object_ptr two, bool &fail) { switch (one.type) { case mvt_string: if (two->type != JSON_STRING) { @@ -17,7 +17,7 @@ int compare(mvt_value const &one, json_object *two, bool &fail) { return false; // string vs non-string } - return strcmp(one.c_str(), two->value.string.string); + return strcmp(one.c_str(), two->value.string.string.c_str()); case mvt_double: case mvt_float: @@ -91,8 +91,8 @@ int compare(mvt_value const &one, json_object *two, bool &fail) { // 0: false // 1: true // -1: incomparable (sql null), treated as false in final output -static int eval(std::function feature, json_object *f, std::set &exclude_attributes, std::vector const &unidecode_data) { - if (f != NULL) { +static int eval(std::function feature, json_object_ptr f, std::set &exclude_attributes, std::vector const &unidecode_data) { + if (f != nullptr) { if (f->type == JSON_TRUE) { return 1; } else if (f->type == JSON_FALSE) { @@ -110,7 +110,7 @@ static int eval(std::function feature, json_obje } if (f->type == JSON_STRING) { - if (f->value.string.string[0] == '\0') { + if (f->value.string.string.empty()) { return 0; } else { return 1; @@ -118,70 +118,70 @@ static int eval(std::function feature, json_obje } } - if (f == NULL || f->type != JSON_ARRAY) { - fprintf(stderr, "Filter is not an array: %s\n", json_stringify(f)); + if (f == nullptr || f->type != JSON_ARRAY) { + fprintf(stderr, "Filter is not an array: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - if (f->value.array.length < 1) { - fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f)); + if (f->value.array.array.size() < 1) { + fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } if (f->value.array.array[0]->type != JSON_STRING) { - fprintf(stderr, "Filter operation is not a string: %s\n", json_stringify(f)); + fprintf(stderr, "Filter operation is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - if (strcmp(f->value.array.array[0]->value.string.string, "has") == 0 || - strcmp(f->value.array.array[0]->value.string.string, "!has") == 0) { - if (f->value.array.length != 2) { - fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f)); + const std::string &op = f->value.array.array[0]->value.string.string; + + if (op == "has" || + op == "!has") { + if (f->value.array.array.size() != 2) { + fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - if (strcmp(f->value.array.array[0]->value.string.string, "has") == 0) { + if (op == "has") { if (f->value.array.array[1]->type != JSON_STRING) { - fprintf(stderr, "\"has\" key is not a string: %s\n", json_stringify(f)); + fprintf(stderr, "\"has\" key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - return feature(std::string(f->value.array.array[1]->value.string.string)).type != mvt_no_such_key; + return feature(f->value.array.array[1]->value.string.string).type != mvt_no_such_key; } - if (strcmp(f->value.array.array[0]->value.string.string, "!has") == 0) { + if (op == "!has") { if (f->value.array.array[1]->type != JSON_STRING) { - fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f)); + fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - return feature(std::string(f->value.array.array[1]->value.string.string)).type == mvt_no_such_key; + return feature(f->value.array.array[1]->value.string.string).type == mvt_no_such_key; } } - if (strcmp(f->value.array.array[0]->value.string.string, "==") == 0 || - strcmp(f->value.array.array[0]->value.string.string, "!=") == 0 || - strcmp(f->value.array.array[0]->value.string.string, ">") == 0 || - strcmp(f->value.array.array[0]->value.string.string, ">=") == 0 || - strcmp(f->value.array.array[0]->value.string.string, "<") == 0 || - strcmp(f->value.array.array[0]->value.string.string, "<=") == 0) { - if (f->value.array.length != 3) { - fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f)); + if (op == "==" || + op == "!=" || + op == ">" || + op == ">=" || + op == "<" || + op == "<=") { + if (f->value.array.array.size() != 3) { + fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } if (f->value.array.array[1]->type != JSON_STRING) { - fprintf(stderr, "comparison key is not a string: %s\n", json_stringify(f)); + fprintf(stderr, "comparison key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - mvt_value ff = feature(std::string(f->value.array.array[1]->value.string.string)); + mvt_value ff = feature(f->value.array.array[1]->value.string.string); if (ff.type == mvt_no_such_key) { static bool warned = false; if (!warned) { - const char *s = json_stringify(f); - fprintf(stderr, "Warning: attribute not found for comparison: %s\n", s); - free((void *) s); + fprintf(stderr, "Warning: attribute not found for comparison: %s\n", json_stringify(f).c_str()); warned = true; } - if (strcmp(f->value.array.array[0]->value.string.string, "!=") == 0) { + if (op == "!=") { return true; // attributes that aren't found are not equal } return false; // not found: comparison is false @@ -193,56 +193,54 @@ static int eval(std::function feature, json_obje if (fail) { static bool warned = false; if (!warned) { - const char *s = json_stringify(f); - fprintf(stderr, "Warning: mismatched type in comparison: %s\n", s); - free((void *) s); + fprintf(stderr, "Warning: mismatched type in comparison: %s\n", json_stringify(f).c_str()); warned = true; } - if (strcmp(f->value.array.array[0]->value.string.string, "!=") == 0) { + if (op == "!=") { return true; // mismatched types are not equal } return false; } - if (strcmp(f->value.array.array[0]->value.string.string, "==") == 0) { + if (op == "==") { return cmp == 0; } - if (strcmp(f->value.array.array[0]->value.string.string, "!=") == 0) { + if (op == "!=") { return cmp != 0; } - if (strcmp(f->value.array.array[0]->value.string.string, ">") == 0) { + if (op == ">") { return cmp > 0; } - if (strcmp(f->value.array.array[0]->value.string.string, ">=") == 0) { + if (op == ">=") { return cmp >= 0; } - if (strcmp(f->value.array.array[0]->value.string.string, "<") == 0) { + if (op == "<") { return cmp < 0; } - if (strcmp(f->value.array.array[0]->value.string.string, "<=") == 0) { + if (op == "<=") { return cmp <= 0; } - fprintf(stderr, "Internal error: can't happen: %s\n", json_stringify(f)); + fprintf(stderr, "Internal error: can't happen: %s\n", json_stringify(f).c_str()); exit(EXIT_IMPOSSIBLE); } - if (strcmp(f->value.array.array[0]->value.string.string, "all") == 0 || - strcmp(f->value.array.array[0]->value.string.string, "any") == 0 || - strcmp(f->value.array.array[0]->value.string.string, "none") == 0) { + if (op == "all" || + op == "any" || + op == "none") { bool v; - if (strcmp(f->value.array.array[0]->value.string.string, "all") == 0) { + if (op == "all") { v = true; } else { v = false; } - for (size_t i = 1; i < f->value.array.length; i++) { + for (size_t i = 1; i < f->value.array.array.size(); i++) { int out = eval(feature, f->value.array.array[i], exclude_attributes, unidecode_data); if (out >= 0) { // nulls are ignored in boolean and/or expressions - if (strcmp(f->value.array.array[0]->value.string.string, "all") == 0) { + if (op == "all") { v = v && out; if (!v) { break; @@ -256,51 +254,47 @@ static int eval(std::function feature, json_obje } } - if (strcmp(f->value.array.array[0]->value.string.string, "none") == 0) { + if (op == "none") { return !v; } else { return v; } } - if (strcmp(f->value.array.array[0]->value.string.string, "in") == 0 || - strcmp(f->value.array.array[0]->value.string.string, "!in") == 0) { - if (f->value.array.length < 2) { - fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f)); + if (op == "in" || + op == "!in") { + if (f->value.array.array.size() < 2) { + fprintf(stderr, "Array too small in filter: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } if (f->value.array.array[1]->type != JSON_STRING) { - fprintf(stderr, "\"!in\" key is not a string: %s\n", json_stringify(f)); + fprintf(stderr, "\"!in\" key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - mvt_value ff = feature(std::string(f->value.array.array[1]->value.string.string)); + mvt_value ff = feature(f->value.array.array[1]->value.string.string); if (ff.type == mvt_no_such_key) { static bool warned = false; if (!warned) { - const char *s = json_stringify(f); - fprintf(stderr, "Warning: attribute not found for comparison: %s\n", s); - free((void *) s); + fprintf(stderr, "Warning: attribute not found for comparison: %s\n", json_stringify(f).c_str()); warned = true; } - if (strcmp(f->value.array.array[0]->value.string.string, "!in") == 0) { + if (op == "!in") { return true; // attributes that aren't found are not in } return false; // not found: comparison is false } bool found = false; - for (size_t i = 2; i < f->value.array.length; i++) { + for (size_t i = 2; i < f->value.array.array.size(); i++) { bool fail = false; int cmp = compare(ff, f->value.array.array[i], fail); if (fail) { static bool warned = false; if (!warned) { - const char *s = json_stringify(f); - fprintf(stderr, "Warning: mismatched type in comparison: %s\n", s); - free((void *) s); + fprintf(stderr, "Warning: mismatched type in comparison: %s\n", json_stringify(f).c_str()); warned = true; } cmp = 1; @@ -312,21 +306,21 @@ static int eval(std::function feature, json_obje } } - if (strcmp(f->value.array.array[0]->value.string.string, "in") == 0) { + if (op == "in") { return found; } else { return !found; } } - if (strcmp(f->value.array.array[0]->value.string.string, "attribute-filter") == 0) { - if (f->value.array.length != 3) { - fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f)); + if (op == "attribute-filter") { + if (f->value.array.array.size() != 3) { + fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } if (f->value.array.array[1]->type != JSON_STRING) { - fprintf(stderr, "\"attribute-filter\" key is not a string: %s\n", json_stringify(f)); + fprintf(stderr, "\"attribute-filter\" key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } @@ -338,65 +332,63 @@ static int eval(std::function feature, json_obje return true; } - fprintf(stderr, "Unknown filter %s\n", json_stringify(f)); + fprintf(stderr, "Unknown filter %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } -bool evaluate(std::function feature, std::string const &layer, json_object *filter, std::set &exclude_attributes, std::vector const &unidecode_data) { - if (filter == NULL || filter->type != JSON_HASH) { - fprintf(stderr, "Error: filter is not a hash: %s\n", json_stringify(filter)); +bool evaluate(std::function feature, std::string const &layer, json_object_ptr filter, std::set &exclude_attributes, std::vector const &unidecode_data) { + if (filter == nullptr || filter->type != JSON_HASH) { + fprintf(stderr, "Error: filter is not a hash: %s\n", json_stringify(filter).c_str()); exit(EXIT_JSON); } bool ok = true; - json_object *f; + json_object_ptr f; f = json_hash_get(filter, layer.c_str()); - if (ok && f != NULL) { + if (ok && f != nullptr) { ok = eval(feature, f, exclude_attributes, unidecode_data) > 0; } f = json_hash_get(filter, "*"); - if (ok && f != NULL) { + if (ok && f != nullptr) { ok = eval(feature, f, exclude_attributes, unidecode_data) > 0; } return ok; } -json_object *read_filter(const char *fname) { +json_object_ptr read_filter(const char *fname) { FILE *fp = fopen(fname, "r"); if (fp == NULL) { perror(fname); exit(EXIT_OPEN); } - json_pull *jp = json_begin_file(fp); - json_object *filter = json_read_tree(jp); - if (filter == NULL) { + json_pull_ptr jp = json_begin_file(fp); + json_object_ptr filter = json_read_tree(jp); + if (filter == nullptr) { fprintf(stderr, "%s: %s\n", fname, jp->error); exit(EXIT_JSON); } json_disconnect(filter); - json_end(jp); fclose(fp); return filter; } -json_object *parse_filter(const char *s) { - json_pull *jp = json_begin_string(s); - json_object *filter = json_read_tree(jp); - if (filter == NULL) { +json_object_ptr parse_filter(const char *s) { + json_pull_ptr jp = json_begin_string(s); + json_object_ptr filter = json_read_tree(jp); + if (filter == nullptr) { fprintf(stderr, "Could not parse filter %s\n", s); fprintf(stderr, "%s\n", jp->error); exit(EXIT_JSON); } json_disconnect(filter); - json_end(jp); return filter; } -bool evaluate(std::unordered_map const &feature, std::string const &layer, json_object *filter, std::set &exclude_attributes, std::vector const &unidecode_data) { +bool evaluate(std::unordered_map const &feature, std::string const &layer, json_object_ptr filter, std::set &exclude_attributes, std::vector const &unidecode_data) { std::function getter = [&](std::string const &key) { auto f = feature.find(key); if (f != feature.end()) { @@ -412,7 +404,7 @@ bool evaluate(std::unordered_map const &feature, std::st return evaluate(getter, layer, filter, exclude_attributes, unidecode_data); } -bool evaluate(mvt_feature const &feat, mvt_layer const &layer, json_object *filter, std::set &exclude_attributes, int z, std::vector const &unidecode_data) { +bool evaluate(mvt_feature const &feat, mvt_layer const &layer, json_object_ptr filter, std::set &exclude_attributes, int z, std::vector const &unidecode_data) { std::function getter = [&](std::string const &key) { const static std::string dollar_id = "$id"; if (key == dollar_id && feat.has_id) { diff --git a/evaluator.hpp b/evaluator.hpp index bc6be00c..99a91fb7 100644 --- a/evaluator.hpp +++ b/evaluator.hpp @@ -7,10 +7,10 @@ #include "jsonpull/jsonpull.h" #include "mvt.hpp" -bool evaluate(std::unordered_map const &feature, std::string const &layer, json_object *filter, std::set &exclude_attributes, std::vector const &unidecode_data); -json_object *parse_filter(const char *s); -json_object *read_filter(const char *fname); +bool evaluate(std::unordered_map const &feature, std::string const &layer, json_object_ptr filter, std::set &exclude_attributes, std::vector const &unidecode_data); +json_object_ptr parse_filter(const char *s); +json_object_ptr read_filter(const char *fname); -bool evaluate(mvt_feature const &feat, mvt_layer const &layer, json_object *filter, std::set &exclude_attributes, int z, std::vector const &unidecode_data); +bool evaluate(mvt_feature const &feat, mvt_layer const &layer, json_object_ptr filter, std::set &exclude_attributes, int z, std::vector const &unidecode_data); #endif diff --git a/geobuf.cpp b/geobuf.cpp index 02d8ded3..c06b73c9 100644 --- a/geobuf.cpp +++ b/geobuf.cpp @@ -394,28 +394,25 @@ void readFeature(protozero::pbf_reader &pbf, size_t dim, double e, std::vectorsecond.s.c_str()); - json_object *o = json_read_tree(jp); + json_pull_ptr jp = json_begin_string(tip->second.s.c_str()); + json_object_ptr o = json_read_tree(jp); - if (o != NULL) { - json_object *min = json_hash_get(o, "minzoom"); - if (min != NULL && (min->type == JSON_NUMBER)) { + if (o != nullptr) { + json_object_ptr min = json_hash_get(o, "minzoom"); + if (min != nullptr && (min->type == JSON_NUMBER)) { sf.tippecanoe_minzoom = integer_zoom(sst->fname, milo::dtoa_milo(min->value.number.number)); } - json_object *max = json_hash_get(o, "maxzoom"); - if (max != NULL && (max->type == JSON_NUMBER)) { + json_object_ptr max = json_hash_get(o, "maxzoom"); + if (max != nullptr && (max->type == JSON_NUMBER)) { sf.tippecanoe_maxzoom = integer_zoom(sst->fname, milo::dtoa_milo(max->value.number.number)); } - json_object *tlayer = json_hash_get(o, "layer"); - if (tlayer != NULL && (tlayer->type == JSON_STRING)) { + json_object_ptr tlayer = json_hash_get(o, "layer"); + if (tlayer != nullptr && (tlayer->type == JSON_STRING)) { layername = tlayer->value.string.string; } } - - json_free(o); - json_end(jp); } serialize_feature(sst, sf, layername); diff --git a/geojson-loop.cpp b/geojson-loop.cpp index 199c6531..adc0b0a1 100644 --- a/geojson-loop.cpp +++ b/geojson-loop.cpp @@ -25,35 +25,35 @@ static const char *geometry_names[GEOM_TYPES] = { }; // XXX duplicated -static void json_context(json_object *j) { - char *s = json_stringify(j); +static void json_context(json_object_ptr j) { + std::string s = json_stringify(j); - if (strlen(s) >= 500) { - snprintf(s + 497, strlen(s) + 1 - 497, "..."); + if (s.size() >= 500) { + s.resize(497); + s.append("..."); } - fprintf(stderr, "in JSON object %s\n", s); - free(s); // stringify + fprintf(stderr, "in JSON object %s\n", s.c_str()); } -void parse_json(json_feature_action *jfa, json_pull *jp) { +void parse_json(json_feature_action *jfa, json_pull_ptr jp) { long long found_hashes = 0; long long found_features = 0; long long found_geometries = 0; while (1) { - json_object *j = json_read(jp); - if (j == NULL) { - if (jp->error != NULL) { + json_object_ptr j = json_read(jp); + if (j == nullptr) { + if (jp->error != nullptr) { fprintf(stderr, "%s:%d: %s: ", jfa->fname.c_str(), jp->line, jp->error); - if (jp->root != NULL) { + if (jp->root != nullptr) { json_context(jp->root); } else { fprintf(stderr, "\n"); } } - json_free(jp->root); + jp->root.reset(); break; } @@ -65,8 +65,8 @@ void parse_json(json_feature_action *jfa, json_pull *jp) { } } - json_object *type = json_hash_get(j, "type"); - if (type == NULL || type->type != JSON_STRING) { + json_object_ptr type = json_hash_get(j, "type"); + if (type == nullptr || type->type != JSON_STRING) { continue; } @@ -74,25 +74,25 @@ void parse_json(json_feature_action *jfa, json_pull *jp) { int i; int is_geometry = 0; for (i = 0; i < GEOM_TYPES; i++) { - if (strcmp(type->value.string.string, geometry_names[i]) == 0) { + if (type->value.string.string == geometry_names[i]) { is_geometry = 1; break; } } if (is_geometry) { - if (j->parent != NULL) { - if (j->parent->type == JSON_ARRAY && j->parent->parent != NULL) { + if (j->parent != nullptr) { + if (j->parent->type == JSON_ARRAY && j->parent->parent != nullptr) { if (j->parent->parent->type == JSON_HASH) { - json_object *geometries = json_hash_get(j->parent->parent, "geometries"); - if (geometries != NULL) { + json_object_ptr geometries = json_hash_get(j->parent->parent->shared_from_this(), "geometries"); + if (geometries != nullptr) { // Parent of Parent must be a GeometryCollection is_geometry = 0; } } } else if (j->parent->type == JSON_HASH) { - json_object *geometry = json_hash_get(j->parent, "geometry"); - if (geometry != NULL) { + json_object_ptr geometry = json_hash_get(j->parent->shared_from_this(), "geometry"); + if (geometry != nullptr) { // Parent must be a Feature is_geometry = 0; } @@ -101,10 +101,10 @@ void parse_json(json_feature_action *jfa, json_pull *jp) { } if (is_geometry) { - json_object *jo = j; - while (jo != NULL) { - if (jo->parent != NULL && jo->parent->type == JSON_HASH) { - if (json_hash_get(jo->parent, "properties") == jo) { + json_object *jo = j.get(); + while (jo != nullptr) { + if (jo->parent != nullptr && jo->parent->type == JSON_HASH) { + if (json_hash_get(jo->parent->shared_from_this(), "properties").get() == jo) { // Ancestor is the value corresponding to a properties key is_geometry = 0; break; @@ -120,14 +120,14 @@ void parse_json(json_feature_action *jfa, json_pull *jp) { } found_geometries++; - jfa->add_feature(j, false, NULL, NULL, NULL, j); + jfa->add_feature(j, false, nullptr, nullptr, nullptr, j); json_free(j); continue; } } - if (strcmp(type->value.string.string, "Feature") != 0) { - if (strcmp(type->value.string.string, "FeatureCollection") == 0) { + if (type->value.string.string != "Feature") { + if (type->value.string.string == "FeatureCollection") { jfa->check_crs(j); json_free(j); } @@ -140,16 +140,16 @@ void parse_json(json_feature_action *jfa, json_pull *jp) { } found_features++; - json_object *geometry = json_hash_get(j, "geometry"); - if (geometry == NULL) { + json_object_ptr geometry = json_hash_get(j, "geometry"); + if (geometry == nullptr) { fprintf(stderr, "%s:%d: feature with no geometry: ", jfa->fname.c_str(), jp->line); json_context(j); json_free(j); continue; } - json_object *properties = json_hash_get(j, "properties"); - if (properties == NULL || (properties->type != JSON_HASH && properties->type != JSON_NULL)) { + json_object_ptr properties = json_hash_get(j, "properties"); + if (properties == nullptr || (properties->type != JSON_HASH && properties->type != JSON_NULL)) { fprintf(stderr, "%s:%d: feature without properties hash: ", jfa->fname.c_str(), jp->line); json_context(j); json_free(j); @@ -158,10 +158,10 @@ void parse_json(json_feature_action *jfa, json_pull *jp) { bool is_feature = true; { - json_object *jo = j; - while (jo != NULL) { - if (jo->parent != NULL && jo->parent->type == JSON_HASH) { - if (json_hash_get(jo->parent, "properties") == jo) { + json_object *jo = j.get(); + while (jo != nullptr) { + if (jo->parent != nullptr && jo->parent->type == JSON_HASH) { + if (json_hash_get(jo->parent->shared_from_this(), "properties").get() == jo) { // Ancestor is the value corresponding to a properties key is_feature = false; break; @@ -174,11 +174,11 @@ void parse_json(json_feature_action *jfa, json_pull *jp) { continue; } - json_object *tippecanoe = json_hash_get(j, "tippecanoe"); - json_object *id = json_hash_get(j, "id"); + json_object_ptr tippecanoe = json_hash_get(j, "tippecanoe"); + json_object_ptr id = json_hash_get(j, "id"); - json_object *geometries = json_hash_get(geometry, "geometries"); - if (geometries != NULL && geometries->type == JSON_ARRAY) { + json_object_ptr geometries = json_hash_get(geometry, "geometries"); + if (geometries != nullptr && geometries->type == JSON_ARRAY) { jfa->add_feature(geometries, true, properties, id, tippecanoe, j); } else { jfa->add_feature(geometry, false, properties, id, tippecanoe, j); diff --git a/geojson-loop.hpp b/geojson-loop.hpp index 3d82be8e..f1b26584 100644 --- a/geojson-loop.hpp +++ b/geojson-loop.hpp @@ -4,8 +4,8 @@ struct json_feature_action { std::string fname; - virtual int add_feature(json_object *geometry, bool geometrycollection, json_object *properties, json_object *id, json_object *tippecanoe, json_object *feature) = 0; - virtual void check_crs(json_object *j) = 0; + virtual int add_feature(json_object_ptr geometry, bool geometrycollection, json_object_ptr properties, json_object_ptr id, json_object_ptr tippecanoe, json_object_ptr feature) = 0; + virtual void check_crs(json_object_ptr j) = 0; }; -void parse_json(json_feature_action *action, json_pull *jp); +void parse_json(json_feature_action *action, json_pull_ptr jp); diff --git a/geojson.cpp b/geojson.cpp index 3798d14b..6e287d73 100644 --- a/geojson.cpp +++ b/geojson.cpp @@ -40,9 +40,9 @@ #include "milo/dtoa_milo.h" #include "errors.hpp" -int serialize_geojson_feature(struct serialization_state *sst, json_object *geometry, json_object *properties, json_object *id, int layer, json_object *tippecanoe, json_object *feature, std::string const &layername) { - json_object *geometry_type = json_hash_get(geometry, "type"); - if (geometry_type == NULL) { +int serialize_geojson_feature(struct serialization_state *sst, json_object_ptr geometry, json_object_ptr properties, json_object_ptr id, int layer, json_object_ptr tippecanoe, json_object_ptr feature, std::string const &layername) { + json_object_ptr geometry_type = json_hash_get(geometry, "type"); + if (geometry_type == nullptr) { static int warned = 0; if (!warned) { fprintf(stderr, "%s:%d: null geometry (additional not reported): ", sst->fname, sst->line); @@ -59,8 +59,8 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom return 0; } - json_object *coordinates = json_hash_get(geometry, "coordinates"); - if (coordinates == NULL || coordinates->type != JSON_ARRAY) { + json_object_ptr coordinates = json_hash_get(geometry, "coordinates"); + if (coordinates == nullptr || coordinates->type != JSON_ARRAY) { fprintf(stderr, "%s:%d: feature without coordinates array: ", sst->fname, sst->line); json_context(feature); return 0; @@ -68,12 +68,12 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom int t; for (t = 0; t < GEOM_TYPES; t++) { - if (strcmp(geometry_type->value.string.string, geometry_names[t]) == 0) { + if (geometry_type->value.string.string == geometry_names[t]) { break; } } if (t >= GEOM_TYPES) { - fprintf(stderr, "%s:%d: Can't handle geometry type %s: ", sst->fname, sst->line, geometry_type->value.string.string); + fprintf(stderr, "%s:%d: Can't handle geometry type %s: ", sst->fname, sst->line, geometry_type->value.string.string.c_str()); json_context(feature); return 0; } @@ -82,26 +82,26 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom int tippecanoe_maxzoom = -1; std::string tippecanoe_layername = layername; - if (tippecanoe != NULL) { - json_object *min = json_hash_get(tippecanoe, "minzoom"); - if (min != NULL && (min->type == JSON_NUMBER)) { + if (tippecanoe != nullptr) { + json_object_ptr min = json_hash_get(tippecanoe, "minzoom"); + if (min != nullptr && (min->type == JSON_NUMBER)) { tippecanoe_minzoom = integer_zoom(sst->fname, milo::dtoa_milo(min->value.number.number)); } - json_object *max = json_hash_get(tippecanoe, "maxzoom"); - if (max != NULL && (max->type == JSON_NUMBER)) { + json_object_ptr max = json_hash_get(tippecanoe, "maxzoom"); + if (max != nullptr && (max->type == JSON_NUMBER)) { tippecanoe_maxzoom = integer_zoom(sst->fname, milo::dtoa_milo(max->value.number.number)); } - json_object *ln = json_hash_get(tippecanoe, "layer"); - if (ln != NULL && (ln->type == JSON_STRING)) { - tippecanoe_layername = std::string(ln->value.string.string); + json_object_ptr ln = json_hash_get(tippecanoe, "layer"); + if (ln != nullptr && (ln->type == JSON_STRING)) { + tippecanoe_layername = ln->value.string.string; } } bool has_id = false; unsigned long long id_value = 0; - if (id != NULL) { + if (id != nullptr) { if (id->type == JSON_NUMBER) { if (id->value.number.number >= 0) { char *err = NULL; @@ -142,20 +142,20 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom if (additional[A_CONVERT_NUMERIC_IDS] && id->type == JSON_STRING) { char *err = NULL; - id_value = strtoull(id->value.string.string, &err, 10); + id_value = strtoull(id->value.string.string.c_str(), &err, 10); if (err != NULL && *err != '\0') { static bool warned_frac = false; if (!warned_frac) { - fprintf(stderr, "Warning: Can't represent non-integer feature ID %s\n", id->value.string.string); + fprintf(stderr, "Warning: Can't represent non-integer feature ID %s\n", id->value.string.string.c_str()); warned_frac = true; } } else if (std::to_string(id_value) != id->value.string.string) { static bool warned = false; if (!warned) { - fprintf(stderr, "Warning: Can't represent too-large feature ID %s\n", id->value.string.string); + fprintf(stderr, "Warning: Can't represent too-large feature ID %s\n", id->value.string.string.c_str()); warned = true; } } else { @@ -168,9 +168,7 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom static bool warned_nan = false; if (!warned_nan) { - char *s = json_stringify(id); - fprintf(stderr, "Warning: Can't represent non-numeric feature ID %s\n", s); - free(s); // stringify + fprintf(stderr, "Warning: Can't represent non-numeric feature ID %s\n", json_stringify(id).c_str()); warned_nan = true; } } @@ -178,8 +176,8 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom } size_t nprop = 0; - if (properties != NULL && properties->type == JSON_HASH) { - nprop = properties->value.object.length; + if (properties != nullptr && properties->type == JSON_HASH) { + nprop = properties->value.object.keys.size(); } std::vector> full_keys; @@ -193,7 +191,7 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom if (properties->value.object.keys[i]->type == JSON_STRING) { serial_val sv = stringify_value(properties->value.object.values[i], sst->fname, sst->line, feature); - full_keys.emplace_back(key_pool.pool(properties->value.object.keys[i]->value.string.string)); + full_keys.emplace_back(key_pool.pool(properties->value.object.keys[i]->value.string.string.c_str())); values.push_back(std::move(sv)); } } @@ -218,16 +216,16 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object *geom return serialize_feature(sst, sf, tippecanoe_layername); } -void check_crs(json_object *j, const char *reading) { - json_object *crs = json_hash_get(j, "crs"); - if (crs != NULL) { - json_object *properties = json_hash_get(crs, "properties"); - if (properties != NULL) { - json_object *name = json_hash_get(properties, "name"); - if (name != NULL && name->type == JSON_STRING) { - if (strcmp(name->value.string.string, projection->alias) != 0) { +void check_crs(json_object_ptr j, const char *reading) { + json_object_ptr crs = json_hash_get(j, "crs"); + if (crs != nullptr) { + json_object_ptr properties = json_hash_get(crs, "properties"); + if (properties != nullptr) { + json_object_ptr name = json_hash_get(properties, "name"); + if (name != nullptr && name->type == JSON_STRING) { + if (name->value.string.string != projection->alias) { if (!quiet) { - fprintf(stderr, "%s: Warning: GeoJSON specified projection \"%s\", not the expected \"%s\".\n", reading, name->value.string.string, projection->alias); + fprintf(stderr, "%s: Warning: GeoJSON specified projection \"%s\", not the expected \"%s\".\n", reading, name->value.string.string.c_str(), projection->alias); fprintf(stderr, "%s: If \"%s\" is not the expected projection, use -s to specify the right one.\n", reading, projection->alias); } } @@ -241,11 +239,11 @@ struct json_serialize_action : json_feature_action { int layer; std::string layername; - int add_feature(json_object *geometry, bool geometrycollection, json_object *properties, json_object *id, json_object *tippecanoe, json_object *feature) { + int add_feature(json_object_ptr geometry, bool geometrycollection, json_object_ptr properties, json_object_ptr id, json_object_ptr tippecanoe, json_object_ptr feature) { sst->line = geometry->parser->line; if (geometrycollection) { int ret = 1; - for (size_t g = 0; g < geometry->value.array.length; g++) { + for (size_t g = 0; g < geometry->value.array.array.size(); g++) { ret &= serialize_geojson_feature(sst, geometry->value.array.array[g], properties, id, layer, tippecanoe, feature, layername); } return ret; @@ -254,12 +252,12 @@ struct json_serialize_action : json_feature_action { } } - void check_crs(json_object *j) { + void check_crs(json_object_ptr j) { ::check_crs(j, fname.c_str()); } }; -void parse_json(struct serialization_state *sst, json_pull *jp, int layer, std::string layername) { +void parse_json(struct serialization_state *sst, json_pull_ptr jp, int layer, std::string layername) { json_serialize_action jsa; jsa.fname = sst->fname; jsa.sst = sst; @@ -296,7 +294,7 @@ ssize_t json_map_read(struct json_pull *jp, char *buffer, size_t n) { return n; } -struct json_pull *json_begin_map(char *map, long long len) { +json_pull_ptr json_begin_map(char *map, long long len) { struct jsonmap *jm = new jsonmap; if (jm == NULL) { perror("Out of memory"); @@ -310,7 +308,7 @@ struct json_pull *json_begin_map(char *map, long long len) { return json_begin(json_map_read, jm); } -void json_end_map(struct json_pull *jp) { +void json_end_map(json_pull_ptr jp) { delete (struct jsonmap *) jp->source; json_end(jp); } diff --git a/geojson.hpp b/geojson.hpp index 664ea2e8..aca48008 100644 --- a/geojson.hpp +++ b/geojson.hpp @@ -10,21 +10,21 @@ #include "serial.hpp" struct parse_json_args { - json_pull *jp; + json_pull_ptr jp; int layer; std::string *layername; struct serialization_state *sst; - parse_json_args(json_pull *jp1, int layer1, std::string *layername1, struct serialization_state *sst1) + parse_json_args(json_pull_ptr jp1, int layer1, std::string *layername1, struct serialization_state *sst1) : jp(jp1), layer(layer1), layername(layername1), sst(sst1) { } }; -struct json_pull *json_begin_map(char *map, long long len); -void json_end_map(struct json_pull *jp); +json_pull_ptr json_begin_map(char *map, long long len); +void json_end_map(json_pull_ptr jp); -void parse_json(struct serialization_state *sst, json_pull *jp, int layer, std::string layername); +void parse_json(struct serialization_state *sst, json_pull_ptr jp, int layer, std::string layername); void *run_parse_json(void *v); #endif diff --git a/geometry.hpp b/geometry.hpp index 454cea61..3aab6c18 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -141,7 +141,7 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int std::vector const &exclude_prefix, bool do_compress, std::vector> *next_overzoomed_tiles, - bool demultiply, json_object *filter, bool preserve_input_order, + bool demultiply, json_object_ptr filter, bool preserve_input_order, std::unordered_map const &attribute_accum, std::vector const &unidecode_data, double simplification, double tiny_polygon_size, @@ -157,7 +157,7 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int n std::vector const &exclude_prefix, bool do_compress, std::vector> *next_overzoomed_tiles, - bool demultiply, json_object *filter, bool preserve_input_order, + bool demultiply, json_object_ptr filter, bool preserve_input_order, std::unordered_map const &attribute_accum, std::vector const &unidecode_data, double simplification, double tiny_polygon_size, diff --git a/jsonpull/jsonpull.cpp b/jsonpull/jsonpull.cpp index e69de29b..9c061d57 100644 --- a/jsonpull/jsonpull.cpp +++ b/jsonpull/jsonpull.cpp @@ -0,0 +1,819 @@ +#define _GNU_SOURCE // for asprintf() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "jsonpull.h" +#include "../milo/milo.h" + +#define BUFFER 10000 + +json_pull_ptr json_begin(ssize_t (*read)(struct json_pull *, char *buffer, size_t n), void *source) { + auto j = std::make_shared(); + j->read = read; + j->source = source; + j->buffer.resize(BUFFER); + return j; +} + +static inline int peek(json_pull *j) { + if (j->buffer_head < j->buffer_tail) { + return (unsigned char) j->buffer[j->buffer_head]; + } else { + j->buffer_head = 0; + j->buffer_tail = j->read(j, j->buffer.data(), BUFFER); + if (j->buffer_head >= j->buffer_tail) { + return EOF; + } + return (unsigned char) j->buffer[j->buffer_head]; + } +} + +static inline int next(json_pull *j) { + if (j->buffer_head < j->buffer_tail) { + return (unsigned char) j->buffer[j->buffer_head++]; + } else { + j->buffer_head = 0; + j->buffer_tail = j->read(j, j->buffer.data(), BUFFER); + if (j->buffer_head >= j->buffer_tail) { + return EOF; + } + return (unsigned char) j->buffer[j->buffer_head++]; + } +} + +static ssize_t read_file(json_pull *j, char *buffer, size_t n) { + return fread(buffer, 1, n, (FILE *) j->source); +} + +json_pull_ptr json_begin_file(FILE *f) { + return json_begin(read_file, f); +} + +static ssize_t read_string(json_pull *j, char *buffer, size_t n) { + const char *cp = (const char *) j->source; + size_t out = 0; + + while (out < n && cp[out] != '\0') { + buffer[out] = cp[out]; + out++; + } + + j->source = (void *) (cp + out); + return out; +} + +json_pull_ptr json_begin_string(const char *s) { + return json_begin(read_string, (void *) s); +} + +void json_end(json_pull_ptr &p) { + p.reset(); +} + +static inline int read_wrap(json_pull *j) { + int c = next(j); + + if (c == '\n') { + j->line++; + } + + return c; +} + +static json_object_ptr fabricate_object(json_pull *jp, json_object *parent, json_type type) { + auto o = std::make_shared(); + o->type = type; + o->parent = parent; + o->parser = jp; + return o; +} + +static json_object_ptr add_object(json_pull *j, json_type type) { + json_object *c = j->container.get(); + json_object_ptr o = fabricate_object(j, c, type); + + if (c != nullptr) { + if (c->type == JSON_ARRAY) { + if (c->expect == JSON_ITEM) { + c->value.array.array.push_back(o); + c->expect = JSON_COMMA; + } else { + j->error = "Expected a comma, not a list item"; + return nullptr; + } + } else if (c->type == JSON_HASH) { + if (c->expect == JSON_VALUE) { + c->value.object.values.back() = o; + c->expect = JSON_COMMA; + } else if (c->expect == JSON_KEY) { + if (type != JSON_STRING) { + j->error = "Hash key is not a string"; + return nullptr; + } + + c->value.object.keys.push_back(o); + c->value.object.values.push_back(nullptr); + c->expect = JSON_COLON; + } else { + j->error = "Expected a comma or colon"; + return nullptr; + } + } + } else { + // Drop the previous top-level value; replacing the parser's root + // shared_ptr will free it if no one else holds a reference. + j->root = o; + } + + return o; +} + +json_object_ptr json_hash_get(json_object_ptr o, const char *s) { + if (o == nullptr || o->type != JSON_HASH) { + return nullptr; + } + + for (size_t i = 0; i < o->value.object.keys.size(); i++) { + const auto &key = o->value.object.keys[i]; + if (key != nullptr && key->type == JSON_STRING) { + if (key->value.string.string == s) { + return o->value.object.values[i]; + } + } + } + + return nullptr; +} + +json_object_ptr json_read_separators(json_pull_ptr jp, json_separator_callback cb, void *state) { + int c; + json_pull *j = jp.get(); + + // In case there is an error at the top level + if (j->container == nullptr) { + j->root.reset(); + } + +again: + c = read_wrap(j); + if (c == EOF) { + if (j->container != nullptr) { + j->error = "Reached EOF without all containers being closed"; + } + + return nullptr; + } + + switch (c) { + /////////////////////////// Byte order mark + + case 0xEF: { + int c2 = peek(j); + if (c2 == 0xBB) { + c2 = read_wrap(j); + c2 = peek(j); + if (c2 == 0xBF) { + c2 = read_wrap(j); + c = ' '; + goto again; + } + } + j->error = "Corrupt byte-order mark found"; + return nullptr; + } + + /////////////////////////// Whitespace + + case ' ': + case '\t': + case '\r': + case '\n': + case 0x1E: + goto again; + + /////////////////////////// Arrays + + case '[': { + json_object_ptr o = add_object(j, JSON_ARRAY); + if (o == nullptr) { + return nullptr; + } + j->container = o; + j->container->expect = JSON_ITEM; + + if (cb != nullptr) { + cb(JSON_ARRAY, j, state); + } + + goto again; + } + + case ']': { + if (j->container == nullptr) { + j->error = "Found ] at top level"; + return nullptr; + } + + if (j->container->type != JSON_ARRAY) { + j->error = "Found ] not in an array"; + return nullptr; + } + + if (j->container->expect != JSON_COMMA) { + if (!(j->container->expect == JSON_ITEM && j->container->value.array.array.size() == 0)) { + j->error = "Found ] without final element"; + return nullptr; + } + } + + json_object_ptr ret = j->container; + // Walk up to the parent container. The parent (if any) still owns + // `ret` via its own array vector, so the raw `parent` pointer is + // still valid and we can resurrect a shared_ptr to it. + if (ret->parent != nullptr) { + j->container = ret->parent->shared_from_this(); + } else { + j->container.reset(); + } + return ret; + } + + /////////////////////////// Hashes + + case '{': { + json_object_ptr o = add_object(j, JSON_HASH); + if (o == nullptr) { + return nullptr; + } + j->container = o; + j->container->expect = JSON_KEY; + + if (cb != nullptr) { + cb(JSON_HASH, j, state); + } + + goto again; + } + + case '}': { + if (j->container == nullptr) { + j->error = "Found } at top level"; + return nullptr; + } + + if (j->container->type != JSON_HASH) { + j->error = "Found } not in a hash"; + return nullptr; + } + + if (j->container->expect != JSON_COMMA) { + if (!(j->container->expect == JSON_KEY && j->container->value.object.keys.size() == 0)) { + j->error = "Found } without final element"; + return nullptr; + } + } + + json_object_ptr ret = j->container; + if (ret->parent != nullptr) { + j->container = ret->parent->shared_from_this(); + } else { + j->container.reset(); + } + return ret; + } + + /////////////////////////// Null + + case 'n': { + if (read_wrap(j) != 'u' || read_wrap(j) != 'l' || read_wrap(j) != 'l') { + j->error = "Found misspelling of null"; + return nullptr; + } + + return add_object(j, JSON_NULL); + } + + /////////////////////////// NaN + + case 'N': { + if (read_wrap(j) != 'a' || read_wrap(j) != 'N') { + j->error = "Found misspelling of NaN"; + return nullptr; + } + + j->error = "JSON does not allow NaN"; + return nullptr; + } + + /////////////////////////// Infinity + + case 'I': { + if (read_wrap(j) != 'n' || read_wrap(j) != 'f' || read_wrap(j) != 'i' || + read_wrap(j) != 'n' || read_wrap(j) != 'i' || read_wrap(j) != 't' || + read_wrap(j) != 'y') { + j->error = "Found misspelling of Infinity"; + return nullptr; + } + + j->error = "JSON does not allow Infinity"; + return nullptr; + } + + /////////////////////////// True + + case 't': { + if (read_wrap(j) != 'r' || read_wrap(j) != 'u' || read_wrap(j) != 'e') { + j->error = "Found misspelling of true"; + return nullptr; + } + + return add_object(j, JSON_TRUE); + } + + /////////////////////////// False + + case 'f': { + if (read_wrap(j) != 'a' || read_wrap(j) != 'l' || read_wrap(j) != 's' || read_wrap(j) != 'e') { + j->error = "Found misspelling of false"; + return nullptr; + } + + return add_object(j, JSON_FALSE); + } + + /////////////////////////// Comma + + case ',': { + if (j->container != nullptr) { + if (j->container->expect != JSON_COMMA) { + j->error = "Found unexpected comma"; + return nullptr; + } + + if (j->container->type == JSON_HASH) { + j->container->expect = JSON_KEY; + } else { + j->container->expect = JSON_ITEM; + } + } + + if (cb != nullptr) { + cb(JSON_COMMA, j, state); + } + + goto again; + } + + /////////////////////////// Colon + + case ':': { + if (j->container == nullptr) { + j->error = "Found colon at top level"; + return nullptr; + } + + if (j->container->expect != JSON_COLON) { + j->error = "Found unexpected colon"; + return nullptr; + } + + j->container->expect = JSON_VALUE; + + if (cb != nullptr) { + cb(JSON_COLON, j, state); + } + + goto again; + } + + /////////////////////////// Numbers + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + j->number_buffer.clear(); + int decimal = 0; + + if (c == '-') { + j->number_buffer.push_back(c); + c = read_wrap(j); + } + + if (c == '0') { + j->number_buffer.push_back(c); + } else if (c >= '1' && c <= '9') { + j->number_buffer.push_back(c); + c = peek(j); + + while (c >= '0' && c <= '9') { + j->number_buffer.push_back(read_wrap(j)); + c = peek(j); + } + } + + if (peek(j) == '.') { + j->number_buffer.push_back(read_wrap(j)); + decimal = 1; + + c = peek(j); + if (c < '0' || c > '9') { + j->error = "Decimal point without digits"; + return nullptr; + } + while (c >= '0' && c <= '9') { + j->number_buffer.push_back(read_wrap(j)); + c = peek(j); + } + } + + c = peek(j); + if (c == 'e' || c == 'E') { + j->number_buffer.push_back(read_wrap(j)); + decimal = 1; + + c = peek(j); + if (c == '+' || c == '-') { + j->number_buffer.push_back(read_wrap(j)); + } + + c = peek(j); + if (c < '0' || c > '9') { + j->error = "Exponent without digits"; + return nullptr; + } + while (c >= '0' && c <= '9') { + j->number_buffer.push_back(read_wrap(j)); + c = peek(j); + } + } + + json_object_ptr n = add_object(j, JSON_NUMBER); + if (n != nullptr) { + n->value.number.number = atof(j->number_buffer.c_str()); + n->value.number.large_signed = 0; + n->value.number.large_unsigned = 0; + +#define MAX_SAFE_INTEGER 9007199254740991.0 +#define MIN_SAFE_INTEGER -9007199254740991.0 + + if (!decimal && n->value.number.number > MAX_SAFE_INTEGER) { + errno = 0; + char *err = nullptr; + unsigned long long ull = strtoull(j->number_buffer.c_str(), &err, 10); + if (errno == 0 && (err == nullptr || *err == '\0')) { + n->value.number.large_unsigned = ull; + } + } + if (!decimal && n->value.number.number < MIN_SAFE_INTEGER) { + errno = 0; + char *err = nullptr; + long long ll = strtoll(j->number_buffer.c_str(), &err, 10); + if (errno == 0 && (err == nullptr || *err == '\0')) { + n->value.number.large_signed = ll; + } + } + } + return n; + } + + /////////////////////////// Strings + + case '"': { + std::string val; + + int surrogate = -1; + while ((c = read_wrap(j)) != EOF) { + if (c == '"') { + if (surrogate >= 0) { + val.push_back(0xE0 | (surrogate >> 12)); + val.push_back(0x80 | ((surrogate >> 6) & 0x3F)); + val.push_back(0x80 | (surrogate & 0x3F)); + surrogate = -1; + } + + break; + } else if (c == '\\') { + c = read_wrap(j); + + if (c == 'u') { + char hex[5] = "aaaa"; + int i; + for (i = 0; i < 4; i++) { + hex[i] = read_wrap(j); + if (hex[i] < '0' || (hex[i] > '9' && hex[i] < 'A') || (hex[i] > 'F' && hex[i] < 'a') || hex[i] > 'f') { + j->error = "Invalid \\u hex character"; + return nullptr; + } + } + + unsigned long ch = strtoul(hex, nullptr, 16); + if (ch >= 0xd800 && ch <= 0xdbff) { + if (surrogate < 0) { + surrogate = ch; + } else { + // Impossible surrogate, so output the first half, + // keep what might be a legitimate new first half. + val.push_back(0xE0 | (surrogate >> 12)); + val.push_back(0x80 | ((surrogate >> 6) & 0x3F)); + val.push_back(0x80 | (surrogate & 0x3F)); + surrogate = ch; + } + continue; + } else if (ch >= 0xdc00 && c <= 0xdfff) { + if (surrogate >= 0) { + long c1 = surrogate - 0xd800; + long c2 = ch - 0xdc00; + ch = ((c1 << 10) | c2) + 0x010000; + surrogate = -1; + } + } + + if (surrogate >= 0) { + val.push_back(0xE0 | (surrogate >> 12)); + val.push_back(0x80 | ((surrogate >> 6) & 0x3F)); + val.push_back(0x80 | (surrogate & 0x3F)); + surrogate = -1; + } + + if (ch <= 0x7F) { + val.push_back(ch); + } else if (ch <= 0x7FF) { + val.push_back(0xC0 | (ch >> 6)); + val.push_back(0x80 | (ch & 0x3F)); + } else if (ch < 0xFFFF) { + val.push_back(0xE0 | (ch >> 12)); + val.push_back(0x80 | ((ch >> 6) & 0x3F)); + val.push_back(0x80 | (ch & 0x3F)); + } else { + val.push_back(0xF0 | (ch >> 18)); + val.push_back(0x80 | ((ch >> 12) & 0x3F)); + val.push_back(0x80 | ((ch >> 6) & 0x3F)); + val.push_back(0x80 | (ch & 0x3F)); + } + } else { + if (surrogate >= 0) { + val.push_back(0xE0 | (surrogate >> 12)); + val.push_back(0x80 | ((surrogate >> 6) & 0x3F)); + val.push_back(0x80 | (surrogate & 0x3F)); + surrogate = -1; + } + + if (c == '"') { + val.push_back('"'); + } else if (c == '\\') { + val.push_back('\\'); + } else if (c == '/') { + val.push_back('/'); + } else if (c == 'b') { + val.push_back('\b'); + } else if (c == 'f') { + val.push_back('\f'); + } else if (c == 'n') { + val.push_back('\n'); + } else if (c == 'r') { + val.push_back('\r'); + } else if (c == 't') { + val.push_back('\t'); + } else { + j->error = "Found backslash followed by unknown character"; + return nullptr; + } + } + } else if (c < ' ') { + j->error = "Found control character in string"; + return nullptr; + } else { + if (surrogate >= 0) { + val.push_back(0xE0 | (surrogate >> 12)); + val.push_back(0x80 | ((surrogate >> 6) & 0x3F)); + val.push_back(0x80 | (surrogate & 0x3F)); + surrogate = -1; + } + + val.push_back(c); + } + } + if (c == EOF) { + j->error = "String without closing quote mark"; + return nullptr; + } + + json_object_ptr s = add_object(j, JSON_STRING); + if (s != nullptr) { + s->value.string.string = std::move(val); + s->value.string.refcon = nullptr; + } + return s; + } + } + + j->error = "Found unexpected character"; + return nullptr; +} + +json_object_ptr json_read(json_pull_ptr j) { + return json_read_separators(j, nullptr, nullptr); +} + +json_object_ptr json_read_tree(json_pull_ptr p) { + json_object_ptr j; + + while ((j = json_read(p)) != nullptr) { + if (j->parent == nullptr) { + return j; + } + } + + return nullptr; +} + +void json_free(json_object_ptr &o) { + o.reset(); +} + +// Walk the subtree clearing parent/parser back-pointers so the detached +// subtree can outlive the original parser. +static void clear_back_pointers(json_object *o) { + if (o == nullptr) { + return; + } + + if (o->type == JSON_HASH) { + for (size_t i = 0; i < o->value.object.keys.size(); i++) { + clear_back_pointers(o->value.object.keys[i].get()); + clear_back_pointers(o->value.object.values[i].get()); + } + } else if (o->type == JSON_ARRAY) { + for (size_t i = 0; i < o->value.array.array.size(); i++) { + clear_back_pointers(o->value.array.array[i].get()); + } + } + + o->parent = nullptr; + o->parser = nullptr; +} + +void json_disconnect(json_object_ptr o) { + if (o == nullptr) { + return; + } + + // Splice o out of its parent's array or object. The parent's vector + // holds the shared_ptr to this child; erasing it removes one reference, + // but the caller still holds `o`, so the subtree stays alive. + + json_object *parent = o->parent; + if (parent != nullptr) { + if (parent->type == JSON_ARRAY) { + auto &arr = parent->value.array.array; + for (size_t i = 0; i < arr.size(); i++) { + if (arr[i].get() == o.get()) { + arr.erase(arr.begin() + i); + break; + } + } + } else if (parent->type == JSON_HASH) { + auto &keys = parent->value.object.keys; + auto &vals = parent->value.object.values; + + for (size_t i = 0; i < keys.size(); i++) { + if (keys[i].get() == o.get()) { + // Leave a NULL placeholder in the key slot so the + // surrounding value isn't shifted; if the corresponding + // value is also detached the pair is removed below. + keys[i] = fabricate_object(parent->parser, parent, JSON_NULL); + + if (vals[i] != nullptr && vals[i]->type == JSON_NULL && keys[i]->type == JSON_NULL) { + keys.erase(keys.begin() + i); + vals.erase(vals.begin() + i); + } + break; + } + if (vals[i].get() == o.get()) { + vals[i] = fabricate_object(parent->parser, parent, JSON_NULL); + + if (keys[i] != nullptr && keys[i]->type == JSON_NULL && vals[i]->type == JSON_NULL) { + keys.erase(keys.begin() + i); + vals.erase(vals.begin() + i); + } + break; + } + } + } + } + + // Drop the parser's reference to this subtree if it was the root. + json_pull *parser = o->parser; + if (parser != nullptr && parser->root.get() == o.get()) { + parser->root.reset(); + } + + clear_back_pointers(o.get()); +} + +static void string_append_c(std::string &val, char c) { + val.push_back(c); +} + +static void string_append(std::string &val, const char *add) { + val.append(add); +} + +static void json_print_one(std::string &val, json_object *o) { + if (o == nullptr) { + string_append(val, "..."); + } else if (o->type == JSON_STRING) { + string_append_c(val, '\"'); + + for (const char *cp = o->value.string.string.c_str(); *cp != '\0'; cp++) { + if (*cp == '\\' || *cp == '"') { + string_append_c(val, '\\'); + string_append_c(val, *cp); + } else if (*cp >= 0 && *cp < ' ') { + char *s; + if (asprintf(&s, "\\u%04x", *cp) >= 0) { + string_append(val, s); + free(s); + } + } else { + string_append_c(val, *cp); + } + } + + string_append_c(val, '\"'); + } else if (o->type == JSON_NUMBER) { + if (o->value.number.large_signed != 0) { + char s[65]; + snprintf(s, sizeof(s), "%lld", o->value.number.large_signed); + string_append(val, s); + } else if (o->value.number.large_unsigned != 0) { + char s[65]; + snprintf(s, sizeof(s), "%llu", o->value.number.large_unsigned); + string_append(val, s); + } else { + char *s = dtoa_milo(o->value.number.number); + string_append(val, s); + free(s); + } + } else if (o->type == JSON_NULL) { + string_append(val, "null"); + } else if (o->type == JSON_TRUE) { + string_append(val, "true"); + } else if (o->type == JSON_FALSE) { + string_append(val, "false"); + } else if (o->type == JSON_HASH) { + string_append_c(val, '}'); + } else if (o->type == JSON_ARRAY) { + string_append_c(val, ']'); + } +} + +static void json_print(std::string &val, json_object *o) { + if (o == nullptr) { + // Hash value in incompletely read hash + string_append(val, "..."); + } else if (o->type == JSON_HASH) { + string_append_c(val, '{'); + + for (size_t i = 0; i < o->value.object.keys.size(); i++) { + json_print(val, o->value.object.keys[i].get()); + string_append_c(val, ':'); + json_print(val, o->value.object.values[i].get()); + if (i + 1 < o->value.object.keys.size()) { + string_append_c(val, ','); + } + } + string_append_c(val, '}'); + } else if (o->type == JSON_ARRAY) { + string_append_c(val, '['); + for (size_t i = 0; i < o->value.array.array.size(); i++) { + json_print(val, o->value.array.array[i].get()); + if (i + 1 < o->value.array.array.size()) { + string_append_c(val, ','); + } + } + string_append_c(val, ']'); + } else { + json_print_one(val, o); + } +} + +std::string json_stringify(json_object_ptr o) { + std::string val; + json_print(val, o.get()); + return val; +} diff --git a/jsonpull/jsonpull.h b/jsonpull/jsonpull.h index b19e0e8e..3ccb0fae 100644 --- a/jsonpull/jsonpull.h +++ b/jsonpull/jsonpull.h @@ -1,9 +1,11 @@ #ifndef JSONPULL_H #define JSONPULL_H -#ifdef __cplusplus -extern "C" { -#endif +#include +#include +#include +#include +#include typedef enum json_type { // These types can be returned by json_read() @@ -25,74 +27,98 @@ typedef enum json_type { JSON_VALUE, } json_type; -typedef struct json_object { - struct json_object *parent; - struct json_pull *parser; +struct json_object; +struct json_pull; - union { +typedef std::shared_ptr json_object_ptr; +typedef std::shared_ptr json_pull_ptr; + +// json_object owns its descendants via std::shared_ptr in std::vector<>s, +// and keeps raw back-pointers to its parent and to the parser. The back-pointers +// remain valid as long as the node is attached to the tree (the parent is kept +// alive by holding a shared_ptr to this child, and the parser is kept alive by +// the caller's json_pull_ptr). json_disconnect() splices a node out of its +// parent and clears those back-pointers in the detached subtree, so the +// detached subtree can outlive the original parser. + +struct json_object : public std::enable_shared_from_this { + json_object *parent = nullptr; + json_pull *parser = nullptr; + + json_type type = JSON_NULL; + int expect = 0; + + // Members named to match the previous C union layout so that existing + // access paths like `o->value.string.string` and `o->value.array.array[i]` + // continue to work. This is no longer a union because std::string and + // std::vector have non-trivial destructors. + struct value_t { struct { - double number; - unsigned long long large_unsigned; - long long large_signed; + double number = 0; + unsigned long long large_unsigned = 0; + long long large_signed = 0; } number; struct { - char *string; - void *refcon; // reference constant for caller's use + std::string string; + void *refcon = nullptr; // reference constant for caller's use } string; struct { - struct json_object **array; - size_t length; + std::vector array; } array; struct { - struct json_object **keys; - struct json_object **values; - size_t length; + std::vector keys; + std::vector values; } object; } value; +}; - json_type type; - int expect; -} json_object; +struct json_pull { + const char *error = nullptr; // points at a string literal; no allocation + int line = 1; -typedef struct json_pull { - char *error; - int line; + ssize_t (*read)(struct json_pull *, char *buf, size_t n) = nullptr; + void *source = nullptr; + std::vector buffer; + ssize_t buffer_tail = 0; + ssize_t buffer_head = 0; - ssize_t (*read)(struct json_pull *, char *buf, size_t n); - void *source; - char *buffer; - ssize_t buffer_tail; - ssize_t buffer_head; + json_object_ptr container; + json_object_ptr root; - json_object *container; - json_object *root; + std::string number_buffer; +}; - struct string *number_buffer; -} json_pull; +json_pull_ptr json_begin_file(FILE *f); +json_pull_ptr json_begin_string(const char *s); -json_pull *json_begin_file(FILE *f); -json_pull *json_begin_string(const char *s); +json_pull_ptr json_begin(ssize_t (*read)(struct json_pull *, char *buffer, size_t n), void *source); -json_pull *json_begin(ssize_t (*read)(struct json_pull *, char *buffer, size_t n), void *source); -void json_end(json_pull *p); +// json_end is now a thin convenience that resets the caller's json_pull_ptr. +// The parser (and any tree it still owns) is freed when the last shared_ptr +// to it is dropped, so calling json_end is optional if the json_pull_ptr will +// go out of scope on its own. +void json_end(json_pull_ptr &p); typedef void (*json_separator_callback)(json_type type, json_pull *j, void *state); -json_object *json_read_tree(json_pull *j); -json_object *json_read(json_pull *j); -json_object *json_read_separators(json_pull *j, json_separator_callback cb, void *state); -void json_free(json_object *j); -void json_disconnect(json_object *j); +json_object_ptr json_read_tree(json_pull_ptr j); +json_object_ptr json_read(json_pull_ptr j); +json_object_ptr json_read_separators(json_pull_ptr j, json_separator_callback cb, void *state); -json_object *json_hash_get(json_object *o, const char *s); +// json_free now just resets the caller's json_object_ptr. The subtree is +// destroyed when the last shared_ptr to it is dropped (typically by also +// being removed from its parent or parser). +void json_free(json_object_ptr &j); -char *json_stringify(json_object *o); +// Splice o out of its parent's array/object and clear parent/parser back-pointers +// throughout the detached subtree so it can outlive the original parser. +void json_disconnect(json_object_ptr j); -#ifdef __cplusplus -} -#endif +json_object_ptr json_hash_get(json_object_ptr o, const char *s); + +std::string json_stringify(json_object_ptr o); #endif diff --git a/jsontool.cpp b/jsontool.cpp index bb5341b8..5c35206f 100644 --- a/jsontool.cpp +++ b/jsontool.cpp @@ -140,23 +140,18 @@ std::string sort_quote(const char *s) { return ret; } -void out(std::string const &s, int type, json_object *properties) { +void out(std::string const &s, int type, json_object_ptr properties) { if (extract != NULL) { std::string extracted = sort_quote("null"); bool found = false; - json_object *o = json_hash_get(properties, extract); - if (o != NULL) { + json_object_ptr o = json_hash_get(properties, extract); + if (o != nullptr) { found = true; if (o->type == JSON_STRING || o->type == JSON_NUMBER) { - extracted = sort_quote(o->value.string.string); + extracted = sort_quote(o->value.string.string.c_str()); } else { - // Don't really know what to do about sort quoting - // for arbitrary objects - - const char *out = json_stringify(o); - extracted = sort_quote(out); - free((void *) out); + extracted = sort_quote(json_stringify(o).c_str()); } } @@ -205,7 +200,7 @@ void out(std::string const &s, int type, json_object *properties) { std::string prev_joinkey; -void join_csv(json_object *j) { +void join_csv(json_object_ptr j) { if (header.size() == 0) { std::string s = csv_getline(csvfile); if (s.size() == 0) { @@ -231,14 +226,14 @@ void join_csv(json_object *j) { } } - json_object *properties = json_hash_get(j, "properties"); - json_object *key = NULL; + json_object_ptr properties = json_hash_get(j, "properties"); + json_object_ptr key; - if (properties != NULL) { + if (properties != nullptr) { key = json_hash_get(properties, header[0].c_str()); } - if (key == NULL) { + if (key == nullptr) { static bool warned = false; if (!warned) { fprintf(stderr, "Warning: couldn't find CSV key \"%s\" in JSON\n", header[0].c_str()); @@ -253,9 +248,7 @@ void join_csv(json_object *j) { } else if (key->type == JSON_NUMBER) { joinkey = milo::dtoa_milo(key->value.number.number); } else { - const char *s = json_stringify(key); - joinkey = s; - free((void *) s); + joinkey = json_stringify(key); } if (joinkey < prev_joinkey) { @@ -305,14 +298,8 @@ void join_csv(json_object *j) { } if (fields.size() > 0 && joinkey == fields[0]) { - // This knows more about the structure of JSON objects than it ought to - // The 8 is to round up at least as much as SIZE_FOR in json_pull.c - properties->value.object.keys = (json_object **) realloc((void *) properties->value.object.keys, (properties->value.object.length + 8 + fields.size()) * sizeof(json_object *)); - properties->value.object.values = (json_object **) realloc((void *) properties->value.object.values, (properties->value.object.length + 8 + fields.size()) * sizeof(json_object *)); - if (properties->value.object.keys == NULL || properties->value.object.values == NULL) { - perror("realloc"); - exit(EXIT_MEMORY); - } + properties->value.object.keys.reserve(properties->value.object.keys.size() + fields.size()); + properties->value.object.values.reserve(properties->value.object.values.size() + fields.size()); for (size_t i = 1; i < fields.size(); i++) { std::string k = header[i]; @@ -330,35 +317,20 @@ void join_csv(json_object *j) { } if (attr_type != JSON_NULL) { - // This knows more about the structure of JSON objects than it ought to - - json_object *ko = (json_object *) malloc(sizeof(json_object)); - json_object *vo = (json_object *) malloc(sizeof(json_object)); - if (ko == NULL || vo == NULL) { - perror("malloc"); - exit(EXIT_MEMORY); - } + auto ko = std::make_shared(); + auto vo = std::make_shared(); ko->type = JSON_STRING; - ko->parent = properties; + ko->parent = properties.get(); ko->parser = properties->parser; - - ko->value.string.string = strdup(k.c_str()); - if (ko->value.string.string == NULL) { - perror("strdup"); - exit(EXIT_MEMORY); - } + ko->value.string.string = k; vo->type = attr_type; - vo->parent = properties; + vo->parent = properties.get(); vo->parser = properties->parser; if (attr_type == JSON_STRING) { - vo->value.string.string = strdup(v.c_str()); - if (vo->value.string.string == NULL) { - perror("strdup"); - exit(EXIT_MEMORY); - } + vo->value.string.string = v; } else if (attr_type == JSON_NUMBER) { vo->value.number.number = atof(v.c_str()); vo->value.number.large_unsigned = 0; @@ -367,44 +339,38 @@ void join_csv(json_object *j) { abort(); } - properties->value.object.keys[properties->value.object.length] = ko; - properties->value.object.values[properties->value.object.length] = vo; - properties->value.object.length++; + properties->value.object.keys.push_back(ko); + properties->value.object.values.push_back(vo); } } } } struct json_join_action : json_feature_action { - int add_feature(json_object *geometry, bool, json_object *, json_object *, json_object *, json_object *feature) { + int add_feature(json_object_ptr geometry, bool, json_object_ptr, json_object_ptr, json_object_ptr, json_object_ptr feature) { if (feature != geometry) { // a real feature, not a bare geometry if (csvfile != NULL) { join_csv(feature); } - char *s = json_stringify(feature); - out(s, 1, json_hash_get(feature, "properties")); - free(s); + out(json_stringify(feature), 1, json_hash_get(feature, "properties")); } else { - char *s = json_stringify(geometry); - out(s, 2, NULL); - free(s); + out(json_stringify(geometry), 2, nullptr); } return 1; } - void check_crs(json_object *) { + void check_crs(json_object_ptr) { } }; void process(FILE *fp, const char *fname) { - json_pull *jp = json_begin_file(fp); + json_pull_ptr jp = json_begin_file(fp); json_join_action jja; jja.fname = fname; parse_json(&jja, jp); - json_end(jp); } int main(int argc, char **argv) { diff --git a/main.cpp b/main.cpp index 1f8b8f83..1fc7d497 100644 --- a/main.cpp +++ b/main.cpp @@ -586,7 +586,7 @@ struct STREAM { } } - json_pull *json_begin() { + json_pull_ptr json_begin() { return ::json_begin(read_stream, this); } }; @@ -1215,7 +1215,7 @@ double round_droprate(double r) { return std::round(r * 100000.0) / 100000.0; } -std::pair read_input(std::vector &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set *exclude, std::set *include, int exclude_all, json_object *filter, double droprate, int buffer, const char *tmpdir, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, long long *file_bbox1, long long *file_bbox2, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, bool guess_cluster_maxzoom, std::unordered_map const *attribute_types, const char *pgm, std::unordered_map const *attribute_accum, std::map const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) { +std::pair read_input(std::vector &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set *exclude, std::set *include, int exclude_all, json_object_ptr filter, double droprate, int buffer, const char *tmpdir, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, long long *file_bbox1, long long *file_bbox2, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, bool guess_cluster_maxzoom, std::unordered_map const *attribute_types, const char *pgm, std::unordered_map const *attribute_accum, std::map const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) { int ret = EXIT_SUCCESS; std::vector readers; @@ -1818,7 +1818,7 @@ std::pair read_input(std::vector &sources, char *fname, i // Plain serial reading std::atomic layer_seq(overall_offset); - json_pull *jp = fp->json_begin(); + json_pull_ptr jp = fp->json_begin(); struct serialization_state sst; sst.fname = reading.c_str(); @@ -1845,7 +1845,6 @@ std::pair read_input(std::vector &sources, char *fname, i sst.attribute_types = attribute_types; parse_json(&sst, jp, layer, sources[layer].layer); - json_end(jp); overall_offset = layer_seq; checkdisk(&readers); } @@ -2873,10 +2872,10 @@ void set_attribute_type(std::unordered_map &attribute_types, c void set_attribute_value(const char *arg) { if (*arg == '{') { - json_pull *jp = json_begin_string(arg); - json_object *o = json_read_tree(jp); + json_pull_ptr jp = json_begin_string(arg); + json_object_ptr o = json_read_tree(jp); - if (o == NULL) { + if (o == nullptr) { fprintf(stderr, "%s: --set-attribute %s: %s\n", *av, arg, jp->error); exit(EXIT_JSON); } @@ -2886,9 +2885,9 @@ void set_attribute_value(const char *arg) { exit(EXIT_JSON); } - for (size_t i = 0; i < o->value.object.length; i++) { - json_object *k = o->value.object.keys[i]; - json_object *v = o->value.object.values[i]; + for (size_t i = 0; i < o->value.object.keys.size(); i++) { + json_object_ptr k = o->value.object.keys[i]; + json_object_ptr v = o->value.object.values[i]; if (k->type != JSON_STRING) { fprintf(stderr, "%s: --set-attribute %s: key %zu not a string\n", *av, arg, i); @@ -2899,8 +2898,6 @@ void set_attribute_value(const char *arg) { set_attributes.emplace(k->value.string.string, val); } - json_free(o); - json_end(jp); return; } @@ -2925,10 +2922,10 @@ void set_attribute_value(const char *arg) { } void parse_json_source(const char *arg, struct source &src) { - json_pull *jp = json_begin_string(arg); - json_object *o = json_read_tree(jp); + json_pull_ptr jp = json_begin_string(arg); + json_object_ptr o = json_read_tree(jp); - if (o == NULL) { + if (o == nullptr) { fprintf(stderr, "%s: -L%s: %s\n", *av, arg, jp->error); exit(EXIT_JSON); } @@ -2938,31 +2935,28 @@ void parse_json_source(const char *arg, struct source &src) { exit(EXIT_JSON); } - json_object *fname = json_hash_get(o, "file"); - if (fname == NULL || fname->type != JSON_STRING) { + json_object_ptr fname = json_hash_get(o, "file"); + if (fname == nullptr || fname->type != JSON_STRING) { fprintf(stderr, "%s: -L%s: requires \"file\": filename\n", *av, arg); exit(EXIT_JSON); } - src.file = std::string(fname->value.string.string); + src.file = fname->value.string.string; - json_object *layer = json_hash_get(o, "layer"); - if (layer != NULL && layer->type == JSON_STRING) { - src.layer = std::string(layer->value.string.string); + json_object_ptr layer = json_hash_get(o, "layer"); + if (layer != nullptr && layer->type == JSON_STRING) { + src.layer = layer->value.string.string; } - json_object *description = json_hash_get(o, "description"); - if (description != NULL && description->type == JSON_STRING) { - src.description = std::string(description->value.string.string); + json_object_ptr description = json_hash_get(o, "description"); + if (description != nullptr && description->type == JSON_STRING) { + src.description = description->value.string.string; } - json_object *format = json_hash_get(o, "format"); - if (format != NULL && format->type == JSON_STRING) { - src.format = std::string(format->value.string.string); + json_object_ptr format = json_hash_get(o, "format"); + if (format != nullptr && format->type == JSON_STRING) { + src.format = format->value.string.string; } - - json_free(o); - json_end(jp); } int main(int argc, char **argv) { @@ -3008,7 +3002,7 @@ int main(int argc, char **argv) { int exclude_all = 0; int read_parallel = 0; int files_open_at_start; - json_object *filter = NULL; + json_object_ptr filter; memsize = calc_memsize(); @@ -3876,9 +3870,7 @@ int main(int argc, char **argv) { exit(EXIT_IMPOSSIBLE); } - if (filter != NULL) { - json_free(filter); - } + filter.reset(); return ret; } diff --git a/overzoom.cpp b/overzoom.cpp index cdef1dd8..b0b16910 100644 --- a/overzoom.cpp +++ b/overzoom.cpp @@ -238,7 +238,7 @@ int main(int argc, char **argv) { std::string out; { - json_object *json_filter = NULL; + json_object_ptr json_filter; if (filter.size() > 0) { json_filter = parse_filter(filter.c_str()); } diff --git a/plugin.cpp b/plugin.cpp index cfe06593..b90fbfe7 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -27,9 +27,7 @@ #include "errors.hpp" #include "thread.hpp" -extern "C" { #include "jsonpull/jsonpull.h" -} #include "plugin.hpp" #include "write_json.hpp" @@ -145,15 +143,15 @@ std::vector parse_layers(int fd, int z, unsigned x, unsigned y, std:: } // Reads from the prefilter -serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std::vector> *layermaps, size_t tiling_seg, std::vector> *layer_unmaps, bool postfilter, key_pool &key_pool) { +serial_feature parse_feature(json_pull_ptr jp, int z, unsigned x, unsigned y, std::vector> *layermaps, size_t tiling_seg, std::vector> *layer_unmaps, bool postfilter, key_pool &key_pool) { serial_feature sf; while (1) { - json_object *j = json_read(jp); - if (j == NULL) { - if (jp->error != NULL) { + json_object_ptr j = json_read(jp); + if (j == nullptr) { + if (jp->error != nullptr) { fprintf(stderr, "Filter output:%d: %s: ", jp->line, jp->error); - if (jp->root != NULL) { + if (jp->root != nullptr) { json_context(jp->root); } else { fprintf(stderr, "\n"); @@ -161,37 +159,35 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std:: exit(EXIT_JSON); } - json_free(jp->root); + jp->root.reset(); sf.t = -1; return sf; } - json_object *type = json_hash_get(j, "type"); - if (type == NULL || type->type != JSON_STRING) { + json_object_ptr type = json_hash_get(j, "type"); + if (type == nullptr || type->type != JSON_STRING) { continue; } - if (strcmp(type->value.string.string, "Feature") != 0) { + if (type->value.string.string != "Feature") { continue; } - json_object *geometry = json_hash_get(j, "geometry"); - if (geometry == NULL) { + json_object_ptr geometry = json_hash_get(j, "geometry"); + if (geometry == nullptr) { fprintf(stderr, "Filter output:%d: filtered feature with no geometry: ", jp->line); json_context(j); - json_free(j); exit(EXIT_JSON); } - json_object *properties = json_hash_get(j, "properties"); - if (properties == NULL || (properties->type != JSON_HASH && properties->type != JSON_NULL)) { + json_object_ptr properties = json_hash_get(j, "properties"); + if (properties == nullptr || (properties->type != JSON_HASH && properties->type != JSON_NULL)) { fprintf(stderr, "Filter output:%d: feature without properties hash: ", jp->line); json_context(j); - json_free(j); exit(EXIT_JSON); } - json_object *geometry_type = json_hash_get(geometry, "type"); - if (geometry_type == NULL) { + json_object_ptr geometry_type = json_hash_get(geometry, "type"); + if (geometry_type == nullptr) { fprintf(stderr, "Filter output:%d: null geometry (additional not reported): ", jp->line); json_context(j); exit(EXIT_JSON); @@ -203,8 +199,8 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std:: exit(EXIT_JSON); } - json_object *coordinates = json_hash_get(geometry, "coordinates"); - if (coordinates == NULL || coordinates->type != JSON_ARRAY) { + json_object_ptr coordinates = json_hash_get(geometry, "coordinates"); + if (coordinates == nullptr || coordinates->type != JSON_ARRAY) { fprintf(stderr, "Filter output:%d: feature without coordinates array: ", jp->line); json_context(j); exit(EXIT_JSON); @@ -212,12 +208,12 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std:: int t; for (t = 0; t < GEOM_TYPES; t++) { - if (strcmp(geometry_type->value.string.string, geometry_names[t]) == 0) { + if (geometry_type->value.string.string == geometry_names[t]) { break; } } if (t >= GEOM_TYPES) { - fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->value.string.string); + fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->value.string.string.c_str()); json_context(j); exit(EXIT_JSON); } @@ -252,30 +248,30 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std:: sf.has_id = false; std::string layername = "unknown"; - json_object *tippecanoe = json_hash_get(j, "tippecanoe"); - if (tippecanoe != NULL) { - json_object *layer = json_hash_get(tippecanoe, "layer"); - if (layer != NULL && layer->type == JSON_STRING) { - layername = std::string(layer->value.string.string); + json_object_ptr tippecanoe = json_hash_get(j, "tippecanoe"); + if (tippecanoe != nullptr) { + json_object_ptr layer = json_hash_get(tippecanoe, "layer"); + if (layer != nullptr && layer->type == JSON_STRING) { + layername = layer->value.string.string; } - json_object *index = json_hash_get(tippecanoe, "index"); - if (index != NULL && index->type == JSON_NUMBER) { + json_object_ptr index = json_hash_get(tippecanoe, "index"); + if (index != nullptr && index->type == JSON_NUMBER) { sf.index = index->value.number.number; } - json_object *sequence = json_hash_get(tippecanoe, "sequence"); - if (sequence != NULL && sequence->type == JSON_NUMBER) { + json_object_ptr sequence = json_hash_get(tippecanoe, "sequence"); + if (sequence != nullptr && sequence->type == JSON_NUMBER) { sf.seq = sequence->value.number.number; } - json_object *extent = json_hash_get(tippecanoe, "extent"); - if (extent != NULL && extent->type == JSON_NUMBER) { + json_object_ptr extent = json_hash_get(tippecanoe, "extent"); + if (extent != nullptr && extent->type == JSON_NUMBER) { sf.extent = extent->value.number.number; } - json_object *dropped = json_hash_get(tippecanoe, "dropped"); - if (dropped != NULL && dropped->type == JSON_TRUE) { + json_object_ptr dropped = json_hash_get(tippecanoe, "dropped"); + if (dropped != nullptr && dropped->type == JSON_TRUE) { sf.dropped = FEATURE_DROPPED; // dropped } else { sf.dropped = FEATURE_KEPT; // kept @@ -299,8 +295,8 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std:: } } - json_object *id = json_hash_get(j, "id"); - if (id != NULL && id->type == JSON_NUMBER) { + json_object_ptr id = json_hash_get(j, "id"); + if (id != nullptr && id->type == JSON_NUMBER) { sf.id = id->value.number.number; if (id->value.number.large_unsigned > 0) { sf.id = id->value.number.large_unsigned; @@ -347,27 +343,24 @@ serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std:: } } - for (size_t i = 0; i < properties->value.object.length; i++) { + for (size_t i = 0; i < properties->value.object.keys.size(); i++) { serial_val v = stringify_value(properties->value.object.values[i], "Filter output", jp->line, j); // Nulls can be excluded here because the expression evaluation filter // would have already run before prefiltering if (v.type != mvt_null) { - sf.full_keys.push_back(key_pool.pool(std::string(properties->value.object.keys[i]->value.string.string))); + sf.full_keys.push_back(key_pool.pool(properties->value.object.keys[i]->value.string.string)); sf.full_values.push_back(v); if (!postfilter) { - add_to_tilestats(ts->second.tilestats, std::string(properties->value.object.keys[i]->value.string.string), v); + add_to_tilestats(ts->second.tilestats, properties->value.object.keys[i]->value.string.string, v); } } } - json_free(j); return sf; } - - json_free(j); } } diff --git a/plugin.hpp b/plugin.hpp index fc901daf..2a1feab6 100644 --- a/plugin.hpp +++ b/plugin.hpp @@ -1,4 +1,4 @@ struct key_pool; std::vector filter_layers(const char *filter, std::vector &layer, unsigned z, unsigned x, unsigned y, std::vector> *layermaps, size_t tiling_seg, std::vector> *layer_unmaps, int extent); void setup_filter(const char *filter, int *write_to, int *read_from, pid_t *pid, unsigned z, unsigned x, unsigned y); -serial_feature parse_feature(json_pull *jp, int z, unsigned x, unsigned y, std::vector> *layermaps, size_t tiling_seg, std::vector> *layer_unmaps, bool filters, key_pool &key_pool); +serial_feature parse_feature(json_pull_ptr jp, int z, unsigned x, unsigned y, std::vector> *layermaps, size_t tiling_seg, std::vector> *layer_unmaps, bool filters, key_pool &key_pool); diff --git a/pmtiles_file.cpp b/pmtiles_file.cpp index e86e538b..499d6cee 100644 --- a/pmtiles_file.cpp +++ b/pmtiles_file.cpp @@ -397,9 +397,9 @@ sqlite3 *pmtilesmeta2tmp(const char *fname, const char *pmtiles_map) { exit(EXIT_OPEN); } - json_pull *jp = json_begin_string(decompressed_json.c_str()); - json_object *o = json_read_tree(jp); - if (o == NULL) { + json_pull_ptr jp = json_begin_string(decompressed_json.c_str()); + json_object_ptr o = json_read_tree(jp); + if (o == nullptr) { fprintf(stderr, "%s: metadata parsing error: %s\n", fname, jp->error); exit(EXIT_JSON); } @@ -415,45 +415,44 @@ sqlite3 *pmtilesmeta2tmp(const char *fname, const char *pmtiles_map) { state.nospace = true; state.json_write_hash(); - for (size_t i = 0; i < o->value.object.length; i++) { - const char *key = o->value.object.keys[i]->value.string.string; - if (strcmp(key, "vector_layers") == 0 && o->value.object.values[i]->type == JSON_ARRAY) { + for (size_t i = 0; i < o->value.object.keys.size(); i++) { + const std::string &key = o->value.object.keys[i]->value.string.string; + if (key == "vector_layers" && o->value.object.values[i]->type == JSON_ARRAY) { has_json = true; state.nospace = true; state.json_write_string("vector_layers"); state.nospace = true; state.json_write_json(json_stringify(o->value.object.values[i])); - } else if (strcmp(key, "tilestats") == 0 && o->value.object.values[i]->type == JSON_HASH) { + } else if (key == "tilestats" && o->value.object.values[i]->type == JSON_HASH) { has_json = true; state.nospace = true; state.json_write_string("tilestats"); state.nospace = true; state.json_write_json(json_stringify(o->value.object.values[i])); - } else if (strcmp(key, "strategies") == 0 && o->value.object.values[i]->type == JSON_ARRAY) { - sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('strategies', %Q);", json_stringify(o->value.object.values[i])); + } else if (key == "strategies" && o->value.object.values[i]->type == JSON_ARRAY) { + sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('strategies', %Q);", json_stringify(o->value.object.values[i]).c_str()); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { - fprintf(stderr, "set %s in metadata: %s\n", key, err); + fprintf(stderr, "set %s in metadata: %s\n", key.c_str(), err); } sqlite3_free(sql); - } else if (strcmp(key, "tippecanoe_decisions") == 0 && o->value.object.values[i]->type == JSON_HASH) { - sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('tippecanoe_decisions', %Q);", json_stringify(o->value.object.values[i])); + } else if (key == "tippecanoe_decisions" && o->value.object.values[i]->type == JSON_HASH) { + sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('tippecanoe_decisions', %Q);", json_stringify(o->value.object.values[i]).c_str()); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { - fprintf(stderr, "set %s in metadata: %s\n", key, err); + fprintf(stderr, "set %s in metadata: %s\n", key.c_str(), err); } sqlite3_free(sql); } else if (o->value.object.keys[i]->type != JSON_STRING || o->value.object.values[i]->type != JSON_STRING) { - fprintf(stderr, "%s\n", key); + fprintf(stderr, "%s\n", key.c_str()); fprintf(stderr, "%s: non-string in metadata\n", fname); } else { - sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES (%Q, %Q);", key, o->value.object.values[i]->value.string.string); + sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES (%Q, %Q);", key.c_str(), o->value.object.values[i]->value.string.string.c_str()); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { - fprintf(stderr, "set %s in metadata: %s\n", key, err); + fprintf(stderr, "set %s in metadata: %s\n", key.c_str(), err); } sqlite3_free(sql); } } - json_end(jp); state.nospace = true; state.json_end_hash(); diff --git a/read_json.cpp b/read_json.cpp index 63329acb..08ff2f14 100644 --- a/read_json.cpp +++ b/read_json.cpp @@ -42,19 +42,19 @@ int mb_geometry[GEOM_TYPES] = { VT_POLYGON, }; -void json_context(json_object *j) { - char *s = json_stringify(j); +void json_context(json_object_ptr j) { + std::string s = json_stringify(j); - if (strlen(s) >= 500) { - snprintf(s + 497, strlen(s) + 1 - 497, "..."); + if (s.size() >= 500) { + s.resize(497); + s.append("..."); } - fprintf(stderr, "in JSON object %s\n", s); - free(s); // stringify + fprintf(stderr, "in JSON object %s\n", s.c_str()); } -void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char *fname, int line, json_object *feature) { - if (j == NULL || j->type != JSON_ARRAY) { +void parse_coordinates(int t, json_object_ptr j, drawvec &out, int op, const char *fname, int line, json_object_ptr feature) { + if (j == nullptr || j->type != JSON_ARRAY) { fprintf(stderr, "%s:%d: expected array for geometry type %d: ", fname, line, t); json_context(feature); return; @@ -63,7 +63,7 @@ void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char * int within = geometry_within[t]; if (within >= 0) { size_t i; - for (i = 0; i < j->value.array.length; i++) { + for (i = 0; i < j->value.array.array.size(); i++) { if (within == GEOM_POINT) { if (i == 0 || mb_geometry[t] == VT_POINT) { op = VT_MOVETO; @@ -75,13 +75,13 @@ void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char * parse_coordinates(within, j->value.array.array[i], out, op, fname, line, feature); } } else { - if (j->value.array.length >= 2 && j->value.array.array[0]->type == JSON_NUMBER && j->value.array.array[1]->type == JSON_NUMBER) { + if (j->value.array.array.size() >= 2 && j->value.array.array[0]->type == JSON_NUMBER && j->value.array.array[1]->type == JSON_NUMBER) { long long x, y; double lon = j->value.array.array[0]->value.number.number; double lat = j->value.array.array[1]->value.number.number; projection->project(lon, lat, 32, &x, &y); - if (j->value.array.length > 2) { + if (j->value.array.array.size() > 2) { static int warned = 0; if (!warned) { @@ -121,10 +121,10 @@ void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char * // type and stringified value. All numeric values, even if they are integers, // even integers that are too large to fit in a double but will still be // stringified with their original precision, are recorded here as mvt_double. -serial_val stringify_value(json_object *value, const char *reading, int line, json_object *feature) { +serial_val stringify_value(json_object_ptr value, const char *reading, int line, json_object_ptr feature) { serial_val sv; - if (value != NULL) { + if (value != nullptr) { int vt = value->type; if (vt == JSON_STRING) { @@ -158,9 +158,7 @@ serial_val stringify_value(json_object *value, const char *reading, int line, js sv.s = "null"; } else { sv.type = mvt_string; - const char *v = json_stringify(value); - sv.s = std::string(v); - free((void *) v); // stringify + sv.s = json_stringify(value); } } @@ -178,10 +176,10 @@ static std::vector to_feature(drawvec &geom) { return out; } -std::pair parse_geometry(json_object *geometry, json_pull *jp, json_object *j, +std::pair parse_geometry(json_object_ptr geometry, json_pull_ptr jp, json_object_ptr j, int z, int x, int y, long long extent, bool fix_longitudes, bool mvt_style) { - json_object *geometry_type = json_hash_get(geometry, "type"); - if (geometry_type == NULL) { + json_object_ptr geometry_type = json_hash_get(geometry, "type"); + if (geometry_type == nullptr) { fprintf(stderr, "Filter output:%d: null geometry (additional not reported): ", jp->line); json_context(j); exit(EXIT_JSON); @@ -193,8 +191,8 @@ std::pair parse_geometry(json_object *geometry, json_pull *jp, jso exit(EXIT_JSON); } - json_object *coordinates = json_hash_get(geometry, "coordinates"); - if (coordinates == NULL || coordinates->type != JSON_ARRAY) { + json_object_ptr coordinates = json_hash_get(geometry, "coordinates"); + if (coordinates == nullptr || coordinates->type != JSON_ARRAY) { fprintf(stderr, "Filter output:%d: geometry without coordinates array: ", jp->line); json_context(j); exit(EXIT_JSON); @@ -202,12 +200,12 @@ std::pair parse_geometry(json_object *geometry, json_pull *jp, jso int t; for (t = 0; t < GEOM_TYPES; t++) { - if (strcmp(geometry_type->value.string.string, geometry_names[t]) == 0) { + if (geometry_type->value.string.string == geometry_names[t]) { break; } } if (t >= GEOM_TYPES) { - fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->value.string.string); + fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->value.string.string.c_str()); json_context(j); exit(EXIT_JSON); } @@ -305,13 +303,13 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int std::map ret; std::shared_ptr tile_stringpool = std::make_shared(); - json_pull *jp = json_begin_file(fp); + json_pull_ptr jp = json_begin_file(fp); while (1) { - json_object *j = json_read(jp); - if (j == NULL) { - if (jp->error != NULL) { + json_object_ptr j = json_read(jp); + if (j == nullptr) { + if (jp->error != nullptr) { fprintf(stderr, "Filter output:%d: %s: ", jp->line, jp->error); - if (jp->root != NULL) { + if (jp->root != nullptr) { json_context(jp->root); } else { fprintf(stderr, "\n"); @@ -319,33 +317,32 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int exit(EXIT_JSON); } - json_free(jp->root); + jp->root.reset(); break; } - json_object *type = json_hash_get(j, "type"); - if (type == NULL || type->type != JSON_STRING) { + json_object_ptr type = json_hash_get(j, "type"); + if (type == nullptr || type->type != JSON_STRING) { continue; } - if (strcmp(type->value.string.string, "Feature") != 0) { + if (type->value.string.string != "Feature") { continue; } - json_object *properties = json_hash_get(j, "properties"); - if (properties == NULL || (properties->type != JSON_HASH && properties->type != JSON_NULL)) { + json_object_ptr properties = json_hash_get(j, "properties"); + if (properties == nullptr || (properties->type != JSON_HASH && properties->type != JSON_NULL)) { fprintf(stderr, "Filter output:%d: feature without properties hash: ", jp->line); json_context(j); - json_free(j); exit(EXIT_JSON); } std::string layername = "unknown"; - json_object *tippecanoe = json_hash_get(j, "tippecanoe"); - json_object *layer = NULL; - if (tippecanoe != NULL) { + json_object_ptr tippecanoe = json_hash_get(j, "tippecanoe"); + json_object_ptr layer; + if (tippecanoe != nullptr) { layer = json_hash_get(tippecanoe, "layer"); - if (layer != NULL && layer->type == JSON_STRING) { - layername = std::string(layer->value.string.string); + if (layer != nullptr && layer->type == JSON_STRING) { + layername = layer->value.string.string; } } @@ -359,11 +356,10 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int } auto l = ret.find(layername); - json_object *geometry = json_hash_get(j, "geometry"); - if (geometry == NULL) { + json_object_ptr geometry = json_hash_get(j, "geometry"); + if (geometry == nullptr) { fprintf(stderr, "Filter output:%d: filtered feature with no geometry: ", jp->line); json_context(j); - json_free(j); exit(EXIT_JSON); } @@ -377,8 +373,8 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int feature.type = mb_geometry[t]; feature.geometry = to_feature(dv); - json_object *id = json_hash_get(j, "id"); - if (id != NULL && id->type == JSON_NUMBER) { + json_object_ptr id = json_hash_get(j, "id"); + if (id != nullptr && id->type == JSON_NUMBER) { feature.id = id->value.number.number; if (id->value.number.large_unsigned > 0) { feature.id = id->value.number.large_unsigned; @@ -386,7 +382,7 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int feature.has_id = true; } - for (size_t i = 0; i < properties->value.object.length; i++) { + for (size_t i = 0; i < properties->value.object.keys.size(); i++) { serial_val sv = stringify_value(properties->value.object.values[i], "Filter output", jp->line, j); // Nulls can be excluded here because this is the postfilter @@ -394,18 +390,14 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int if (sv.type != mvt_null) { mvt_value v = stringified_to_mvt_value(sv.type, sv.s.c_str(), tile_stringpool); - l->second.tag(feature, std::string(properties->value.object.keys[i]->value.string.string), v); + l->second.tag(feature, properties->value.object.keys[i]->value.string.string, v); } } l->second.features.push_back(feature); } - - json_free(j); } - json_end(jp); - std::vector final; for (auto a : ret) { final.push_back(a.second); diff --git a/read_json.hpp b/read_json.hpp index a5d5d7b2..4b8f9fb8 100644 --- a/read_json.hpp +++ b/read_json.hpp @@ -10,10 +10,10 @@ extern const char *geometry_names[GEOM_TYPES]; extern int geometry_within[GEOM_TYPES]; extern int mb_geometry[GEOM_TYPES]; -void json_context(json_object *j); -void parse_coordinates(int t, json_object *j, drawvec &out, int op, const char *fname, int line, json_object *feature); -std::pair parse_geometry(json_object *geometry, json_pull *jp, json_object *j, +void json_context(json_object_ptr j); +void parse_coordinates(int t, json_object_ptr j, drawvec &out, int op, const char *fname, int line, json_object_ptr feature); +std::pair parse_geometry(json_object_ptr geometry, json_pull_ptr jp, json_object_ptr j, int z, int x, int y, long long extent, bool fix_longitudes, bool mvt_style); std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int extent, bool fix_longitudes); -serial_val stringify_value(json_object *value, const char *reading, int line, json_object *feature); +serial_val stringify_value(json_object_ptr value, const char *reading, int line, json_object_ptr feature); diff --git a/tile-join.cpp b/tile-join.cpp index 1b057577..24d96167 100644 --- a/tile-join.cpp +++ b/tile-join.cpp @@ -89,7 +89,7 @@ struct arg { std::set *keep_layers = NULL; std::set *remove_layers = NULL; int ifmatched = 0; - json_object *filter = NULL; + json_object_ptr filter; struct tileset_reader *readers = NULL; double minlat, minlon; @@ -97,7 +97,7 @@ struct arg { double minlon2, maxlon2; }; -void append_tile(std::string message, int z, unsigned x, unsigned y, std::map &layermap, std::vector &header, std::map> &mapping, sqlite3 * /* db */, std::set &exclude, std::set &include, std::set &keep_layers, std::set &remove_layers, int ifmatched, mvt_tile &outtile, json_object *filter, struct arg *a) { +void append_tile(std::string message, int z, unsigned x, unsigned y, std::map &layermap, std::vector &header, std::map> &mapping, sqlite3 * /* db */, std::set &exclude, std::set &include, std::set &keep_layers, std::set &remove_layers, int ifmatched, mvt_tile &outtile, json_object_ptr filter, struct arg *a) { mvt_tile tile; int features_added = 0; bool was_compressed; @@ -891,7 +891,7 @@ void *join_worker(void *v) { return NULL; } -void dispatch_tasks(std::map> &tasks, std::vector> &layermaps, sqlite3 *outdb, const char *outdir, std::vector &header, std::map> &mapping, sqlite3 *db, std::set &exclude, std::set &include, int ifmatched, std::set &keep_layers, std::set &remove_layers, json_object *filter, struct tileset_reader *readers, double *minlat, double *minlon, double *maxlat, double *maxlon, double *minlon2, double *maxlon2) { +void dispatch_tasks(std::map> &tasks, std::vector> &layermaps, sqlite3 *outdb, const char *outdir, std::vector &header, std::map> &mapping, sqlite3 *db, std::set &exclude, std::set &include, int ifmatched, std::set &keep_layers, std::set &remove_layers, json_object_ptr filter, struct tileset_reader *readers, double *minlat, double *minlon, double *maxlat, double *maxlon, double *minlon2, double *maxlon2) { pthread_t pthreads[CPUS]; std::vector args; @@ -965,16 +965,16 @@ void dispatch_tasks(std::map> &tasks, std::vector< } void handle_strategies(const unsigned char *s, std::vector *st) { - json_pull *jp = json_begin_string((const char *) s); - json_object *o = json_read_tree(jp); + json_pull_ptr jp = json_begin_string((const char *) s); + json_object_ptr o = json_read_tree(jp); - if (o != NULL && o->type == JSON_ARRAY) { - for (size_t i = 0; i < o->value.array.length; i++) { - json_object *h = o->value.array.array[i]; + if (o != nullptr && o->type == JSON_ARRAY) { + for (size_t i = 0; i < o->value.array.array.size(); i++) { + json_object_ptr h = o->value.array.array[i]; if (h->type == JSON_HASH) { - for (size_t j = 0; j < h->value.object.length; j++) { - json_object *k = h->value.object.keys[j]; - json_object *v = h->value.object.values[j]; + for (size_t j = 0; j < h->value.object.keys.size(); j++) { + json_object_ptr k = h->value.object.keys[j]; + json_object_ptr v = h->value.object.values[j]; if (k->type != JSON_STRING) { fprintf(stderr, "Key %zu of %zu is not a string: %s\n", j, i, s); @@ -985,23 +985,24 @@ void handle_strategies(const unsigned char *s, std::vector *st) { st->resize(i + 1); } - if (strcmp(k->value.string.string, "dropped_by_rate") == 0) { + const std::string &key = k->value.string.string; + if (key == "dropped_by_rate") { (*st)[i].dropped_by_rate += v->value.number.number; - } else if (strcmp(k->value.string.string, "dropped_by_gamma") == 0) { + } else if (key == "dropped_by_gamma") { (*st)[i].dropped_by_gamma += v->value.number.number; - } else if (strcmp(k->value.string.string, "dropped_as_needed") == 0) { + } else if (key == "dropped_as_needed") { (*st)[i].dropped_as_needed += v->value.number.number; - } else if (strcmp(k->value.string.string, "coalesced_as_needed") == 0) { + } else if (key == "coalesced_as_needed") { (*st)[i].coalesced_as_needed += v->value.number.number; - } else if (strcmp(k->value.string.string, "truncated_zooms") == 0) { + } else if (key == "truncated_zooms") { (*st)[i].truncated_zooms += v->value.number.number; - } else if (strcmp(k->value.string.string, "detail_reduced") == 0) { + } else if (key == "detail_reduced") { (*st)[i].detail_reduced += v->value.number.number; - } else if (strcmp(k->value.string.string, "tiny_polygons") == 0) { + } else if (key == "tiny_polygons") { (*st)[i].tiny_polygons += v->value.number.number; - } else if (strcmp(k->value.string.string, "tile_size_desired") == 0) { + } else if (key == "tile_size_desired") { (*st)[i].tile_size += v->value.number.number; - } else if (strcmp(k->value.string.string, "feature_count_desired") == 0) { + } else if (key == "feature_count_desired") { (*st)[i].feature_count += v->value.number.number; } } @@ -1010,22 +1011,19 @@ void handle_strategies(const unsigned char *s, std::vector *st) { fprintf(stderr, "Element %zu is not a hash: %s\n", i, s); } } - json_free(o); } - - json_end(jp); } -void handle_vector_layers(json_object *vector_layers, std::map &layermap, std::map &attribute_descriptions) { - if (vector_layers != NULL && vector_layers->type == JSON_ARRAY) { - for (size_t i = 0; i < vector_layers->value.array.length; i++) { +void handle_vector_layers(json_object_ptr vector_layers, std::map &layermap, std::map &attribute_descriptions) { + if (vector_layers != nullptr && vector_layers->type == JSON_ARRAY) { + for (size_t i = 0; i < vector_layers->value.array.array.size(); i++) { if (vector_layers->value.array.array[i]->type == JSON_HASH) { - json_object *id = json_hash_get(vector_layers->value.array.array[i], "id"); - json_object *desc = json_hash_get(vector_layers->value.array.array[i], "description"); + json_object_ptr id = json_hash_get(vector_layers->value.array.array[i], "id"); + json_object_ptr desc = json_hash_get(vector_layers->value.array.array[i], "description"); - if (id != NULL && desc != NULL && id->type == JSON_STRING && desc->type == JSON_STRING) { - std::string sid = id->value.string.string; - std::string sdesc = desc->value.string.string; + if (id != nullptr && desc != nullptr && id->type == JSON_STRING && desc->type == JSON_STRING) { + const std::string &sid = id->value.string.string; + const std::string &sdesc = desc->value.string.string; if (sdesc.size() != 0) { auto f = layermap.find(sid); @@ -1035,16 +1033,16 @@ void handle_vector_layers(json_object *vector_layers, std::mapvalue.array.array[i], "fields"); - if (fields != NULL && fields->type == JSON_HASH) { - for (size_t j = 0; j < fields->value.object.length; j++) { + json_object_ptr fields = json_hash_get(vector_layers->value.array.array[i], "fields"); + if (fields != nullptr && fields->type == JSON_HASH) { + for (size_t j = 0; j < fields->value.object.keys.size(); j++) { if (fields->value.object.keys[j]->type == JSON_STRING && fields->value.object.values[j]->type) { - const char *desc2 = fields->value.object.values[j]->value.string.string; + const std::string &desc2 = fields->value.object.values[j]->value.string.string; - if (strcmp(desc2, "Number") != 0 && - strcmp(desc2, "String") != 0 && - strcmp(desc2, "Boolean") != 0 && - strcmp(desc2, "Mixed") != 0) { + if (desc2 != "Number" && + desc2 != "String" && + desc2 != "Boolean" && + desc2 != "Mixed") { attribute_descriptions.insert(std::pair(fields->value.object.keys[j]->value.string.string, desc2)); } } @@ -1055,7 +1053,7 @@ void handle_vector_layers(json_object *vector_layers, std::map &layermap, sqlite3 *outdb, const char *outdir, struct stats *st, std::vector &header, std::map> &mapping, sqlite3 *db, std::set &exclude, std::set &include, int ifmatched, std::string &attribution, std::string &description, std::set &keep_layers, std::set &remove_layers, std::string &name, json_object *filter, std::map &attribute_descriptions, std::string &generator_options, std::vector *strategies) { +void decode(struct tileset_reader *readers, std::map &layermap, sqlite3 *outdb, const char *outdir, struct stats *st, std::vector &header, std::map> &mapping, sqlite3 *db, std::set &exclude, std::set &include, int ifmatched, std::string &attribution, std::string &description, std::set &keep_layers, std::set &remove_layers, std::string &name, json_object_ptr filter, std::map &attribute_descriptions, std::string &generator_options, std::vector *strategies) { std::vector> layermaps; for (size_t i = 0; i < CPUS; i++) { layermaps.push_back(std::map()); @@ -1205,17 +1203,14 @@ void decode(struct tileset_reader *readers, std::maptype == JSON_HASH) { - json_object *vector_layers = json_hash_get(o, "vector_layers"); + if (o != nullptr && o->type == JSON_HASH) { + json_object_ptr vector_layers = json_hash_get(o, "vector_layers"); handle_vector_layers(vector_layers, layermap, attribute_descriptions); - json_free(o); } - - json_end(jp); } } @@ -1266,7 +1261,7 @@ int main(int argc, char **argv) { int force = 0; int ifmatched = 0; int filearg = 0; - json_object *filter = NULL; + json_object_ptr filter; std::string join_sqlite_fname; @@ -1648,9 +1643,7 @@ int main(int argc, char **argv) { mbtiles_close(outdb, argv[0]); } - if (filter != NULL) { - json_free(filter); - } + filter.reset(); if (pmtiles_has_suffix(out_mbtiles)) { mbtiles_map_image_to_pmtiles(out_mbtiles, m, !pC, quiet, false); diff --git a/tile.cpp b/tile.cpp index a4f12c75..309d2e92 100644 --- a/tile.cpp +++ b/tile.cpp @@ -941,7 +941,7 @@ struct write_tile_args { bool still_dropping = false; int wrote_zoom = 0; size_t tiling_seg = 0; - json_object *filter = NULL; + json_object_ptr filter; std::vector const *unidecode_data; std::atomic *dropped_count = NULL; atomic_strategy *strategy = NULL; @@ -1102,7 +1102,7 @@ struct next_feature_state { // This function is called repeatedly from write_tile() to retrieve the next feature // from the input stream. If the stream is at an end, it returns a feature with the // geometry type set to -2. -static serial_feature next_feature(decompressor *geoms, std::atomic *geompos_in, int z, unsigned tx, unsigned ty, unsigned *initial_x, unsigned *initial_y, long long *original_features, long long *unclipped_features, int nextzoom, int maxzoom, int minzoom, int max_zoom_increment, size_t pass, std::atomic *along, long long alongminus, int buffer, std::atomic *within, compressor **geomfile, std::atomic *geompos, long long start_geompos[], std::atomic *oprogress, double todo, const char *fname, int child_shards, json_object *filter, const char *global_stringpool, long long *pool_off, std::vector> *layer_unmaps, bool first_time, bool compressed, multiplier_state *multiplier_state, std::shared_ptr &tile_stringpool, std::vector const &unidecode_data, next_feature_state &next_feature_state, double droprate) { +static serial_feature next_feature(decompressor *geoms, std::atomic *geompos_in, int z, unsigned tx, unsigned ty, unsigned *initial_x, unsigned *initial_y, long long *original_features, long long *unclipped_features, int nextzoom, int maxzoom, int minzoom, int max_zoom_increment, size_t pass, std::atomic *along, long long alongminus, int buffer, std::atomic *within, compressor **geomfile, std::atomic *geompos, long long start_geompos[], std::atomic *oprogress, double todo, const char *fname, int child_shards, json_object_ptr filter, const char *global_stringpool, long long *pool_off, std::vector> *layer_unmaps, bool first_time, bool compressed, multiplier_state *multiplier_state, std::shared_ptr &tile_stringpool, std::vector const &unidecode_data, next_feature_state &next_feature_state, double droprate) { double extra_multiplier_zooms = log(retain_points_multiplier) / log(droprate); while (1) { @@ -1350,7 +1350,7 @@ struct run_prefilter_args { char *global_stringpool = NULL; long long *pool_off = NULL; FILE *prefilter_fp = NULL; - json_object *filter = NULL; + json_object_ptr filter; std::vector const *unidecode_data; bool first_time = false; bool compressed = false; @@ -1641,7 +1641,7 @@ void skip_tile(decompressor *geoms, std::atomic *geompos_in, bool com } } -long long write_tile(decompressor *geoms, std::atomic *geompos_in, char *global_stringpool, int z, const unsigned tx, const unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, compressor **geomfile, std::atomic *geompos, int minzoom, int maxzoom, double todo, std::atomic *along, long long alongminus, double gamma, int child_shards, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t tiling_seg, size_t pass, unsigned long long mingap, long long minextent, unsigned long long mindrop_sequence, double minattribute, const char *prefilter, const char *postfilter, json_object *filter, write_tile_args *arg, atomic_strategy *strategy_out, bool compressed_input, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, std::vector const &unidecode_data, long long estimated_complexity, std::set &skip_children_out) { +long long write_tile(decompressor *geoms, std::atomic *geompos_in, char *global_stringpool, int z, const unsigned tx, const unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, compressor **geomfile, std::atomic *geompos, int minzoom, int maxzoom, double todo, std::atomic *along, long long alongminus, double gamma, int child_shards, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t tiling_seg, size_t pass, unsigned long long mingap, long long minextent, unsigned long long mindrop_sequence, double minattribute, const char *prefilter, const char *postfilter, json_object_ptr filter, write_tile_args *arg, atomic_strategy *strategy_out, bool compressed_input, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, std::vector const &unidecode_data, long long estimated_complexity, std::set &skip_children_out) { double merge_fraction = 1; double mingap_fraction = 1; double minextent_fraction = 1; @@ -1775,7 +1775,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch pthread_t prefilter_writer; run_prefilter_args rpa; // here so it stays in scope until joined FILE *prefilter_read_fp = NULL; - json_pull *prefilter_jp = NULL; + json_pull_ptr prefilter_jp; if (z < minzoom) { prefilter = NULL; @@ -3213,7 +3213,7 @@ exit(EXIT_IMPOSSIBLE); return err_or_null; } -int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector> &layermaps, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, json_object *filter, std::vector &strategies, int iz, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data, std::string const *drop_by_attribute_as_needed_attribute, bool drop_by_attribute_descending) { +int traverse_zooms(int *geomfd, off_t *geom_size, char *global_stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector> &layermaps, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, json_object_ptr filter, std::vector &strategies, int iz, node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data, std::string const *drop_by_attribute_as_needed_attribute, bool drop_by_attribute_descending) { last_progress = 0; // The existing layermaps are one table per input thread. diff --git a/tile.hpp b/tile.hpp index 8f266c0b..e9c7d42a 100644 --- a/tile.hpp +++ b/tile.hpp @@ -62,7 +62,7 @@ struct strategy { // long long write_tile(char **geom, char *stringpool, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int min_detail, int basezoom, sqlite3 *outdb, const char *outdir, double droprate, int buffer, const char *fname, FILE **geomfile, int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along, double gamma, int nlayers, std::atomic *strategy); -int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector > &layermap, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz, struct node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data, std::string const *drop_by_attribute_as_needed_attribute, bool drop_by_attribute_descending); +int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector > &layermap, const char *prefilter, const char *postfilter, std::unordered_map const *attribute_accum, json_object_ptr filter, std::vector &strategies, int iz, struct node *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom, int basezoom, double droprate, std::vector const &unidecode_data, std::string const *drop_by_attribute_as_needed_attribute, bool drop_by_attribute_descending); int manage_gap(unsigned long long index, unsigned long long *previndex, double scale, double gamma, double *gap);