From f366b2c4aa95882e55be16f8ddb766cb016857c2 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Sat, 30 May 2026 09:47:22 -0700 Subject: [PATCH] Subclass json_object so primitives shrink from 168 to 24 bytes The previous "every member in a struct" layout cost 168 bytes per json_object, even for JSON_NULL / JSON_TRUE / JSON_FALSE nodes that have no payload. Splitting json_object into a small base class plus json_number / json_string / json_array / json_hash subclasses brings each instance down to just the size of its actual contents: json_object (base, TRUE / FALSE / NULL) 24 bytes json_number 48 bytes json_string 48 bytes json_array (empty) 48 bytes json_hash (empty) 72 bytes Other size wins along the way: * Drop enable_shared_from_this (its embedded weak_ptr was 16 bytes per node). json_pull now keeps an explicit container_stack and the parser no longer needs to resurrect a shared_ptr from a raw `parent` walk. * Remove the unused `refcon` slot from the string variant. * No virtual destructor: shared_ptr keeps the deleter from the original std::make_shared call, so destroying a shared_ptr still runs the right subclass dtor. The base class exposes type-tagged accessors (o->string(), o->number(), o->array(), o->keys(), o->values(), o->large_signed(), o->large_unsigned()) that assert the type matches and downcast to the appropriate subclass storage. All call sites were swept from the old `o->value.X.Y` field paths to these accessors. A raw-pointer overload of json_hash_get() replaces the few external uses of shared_from_this() that survived in geojson-loop.cpp. Co-authored-by: Cursor --- attribute.cpp | 8 +- dirtiles.cpp | 8 +- evaluator.cpp | 56 ++++++------ geobuf.cpp | 6 +- geojson-loop.cpp | 14 +-- geojson.cpp | 50 +++++------ jsonpull/jsonpull.cpp | 195 ++++++++++++++++++++++++------------------ jsonpull/jsonpull.h | 176 +++++++++++++++++++++++++++++++------- jsontool.cpp | 38 ++++---- main.cpp | 16 ++-- plugin.cpp | 28 +++--- pmtiles_file.cpp | 24 +++--- read_json.cpp | 44 +++++----- tile-join.cpp | 52 +++++------ 14 files changed, 424 insertions(+), 291 deletions(-) diff --git a/attribute.cpp b/attribute.cpp index bf53d399..2794e5b7 100644 --- a/attribute.cpp +++ b/attribute.cpp @@ -55,9 +55,9 @@ void set_attribute_accum(std::unordered_map &attribut exit(EXIT_JSON); } - 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]; + for (size_t i = 0; i < o->keys().size(); i++) { + json_object_ptr k = o->keys()[i]; + json_object_ptr v = o->values()[i]; if (k->type != JSON_STRING) { fprintf(stderr, "%s: -E%s: key %zu not a string\n", *argv, arg, i); @@ -68,7 +68,7 @@ void set_attribute_accum(std::unordered_map &attribut exit(EXIT_JSON); } - set_attribute_accum(attribute_accum, k->value.string.string.c_str(), v->value.string.string.c_str()); + set_attribute_accum(attribute_accum, k->string().c_str(), v->string().c_str()); } return; diff --git a/dirtiles.cpp b/dirtiles.cpp index da7c82af..de3f86d4 100644 --- a/dirtiles.cpp +++ b/dirtiles.cpp @@ -260,14 +260,14 @@ sqlite3 *dirmeta2tmp(const char *fname) { exit(EXIT_JSON); } - 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) { + for (size_t i = 0; i < o->keys().size(); i++) { + if (o->keys()[i]->type != JSON_STRING || o->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.c_str(), o->value.object.values[i]->value.string.string.c_str()); + char *sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES (%Q, %Q);", o->keys()[i]->string().c_str(), o->values()[i]->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.c_str(), err); + fprintf(stderr, "set %s in metadata: %s\n", o->keys()[i]->string().c_str(), err); } sqlite3_free(sql); } diff --git a/evaluator.cpp b/evaluator.cpp index 0d99409c..efd27746 100644 --- a/evaluator.cpp +++ b/evaluator.cpp @@ -17,7 +17,7 @@ int compare(mvt_value const &one, json_object_ptr two, bool &fail) { return false; // string vs non-string } - return strcmp(one.c_str(), two->value.string.string.c_str()); + return strcmp(one.c_str(), two->string().c_str()); case mvt_double: case mvt_float: @@ -52,9 +52,9 @@ int compare(mvt_value const &one, json_object_ptr two, bool &fail) { exit(EXIT_IMPOSSIBLE); } - if (v < two->value.number.number) { + if (v < two->number()) { return -1; - } else if (v > two->value.number.number) { + } else if (v > two->number()) { return 1; } else { return 0; @@ -102,7 +102,7 @@ static int eval(std::function feature, json_obje } if (f->type == JSON_NUMBER) { - if (f->value.number.number == 0) { + if (f->number() == 0) { return 0; } else { return 1; @@ -110,7 +110,7 @@ static int eval(std::function feature, json_obje } if (f->type == JSON_STRING) { - if (f->value.string.string.empty()) { + if (f->string().empty()) { return 0; } else { return 1; @@ -123,39 +123,39 @@ static int eval(std::function feature, json_obje exit(EXIT_FILTER); } - if (f->value.array.array.size() < 1) { + if (f->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) { + if (f->array()[0]->type != JSON_STRING) { fprintf(stderr, "Filter operation is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - const std::string &op = f->value.array.array[0]->value.string.string; + const std::string &op = f->array()[0]->string(); if (op == "has" || op == "!has") { - if (f->value.array.array.size() != 2) { + if (f->array().size() != 2) { fprintf(stderr, "Wrong number of array elements in filter: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } if (op == "has") { - if (f->value.array.array[1]->type != JSON_STRING) { + if (f->array()[1]->type != JSON_STRING) { fprintf(stderr, "\"has\" key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - return feature(f->value.array.array[1]->value.string.string).type != mvt_no_such_key; + return feature(f->array()[1]->string()).type != mvt_no_such_key; } if (op == "!has") { - if (f->value.array.array[1]->type != JSON_STRING) { + if (f->array()[1]->type != JSON_STRING) { fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - return feature(f->value.array.array[1]->value.string.string).type == mvt_no_such_key; + return feature(f->array()[1]->string()).type == mvt_no_such_key; } } @@ -165,16 +165,16 @@ static int eval(std::function feature, json_obje op == ">=" || op == "<" || op == "<=") { - if (f->value.array.array.size() != 3) { + if (f->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) { + if (f->array()[1]->type != JSON_STRING) { fprintf(stderr, "comparison key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - mvt_value ff = feature(f->value.array.array[1]->value.string.string); + mvt_value ff = feature(f->array()[1]->string()); if (ff.type == mvt_no_such_key) { static bool warned = false; if (!warned) { @@ -188,7 +188,7 @@ static int eval(std::function feature, json_obje } bool fail = false; - int cmp = compare(ff, f->value.array.array[2], fail); + int cmp = compare(ff, f->array()[2], fail); if (fail) { static bool warned = false; @@ -236,8 +236,8 @@ static int eval(std::function feature, json_obje v = false; } - 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); + for (size_t i = 1; i < f->array().size(); i++) { + int out = eval(feature, f->array()[i], exclude_attributes, unidecode_data); if (out >= 0) { // nulls are ignored in boolean and/or expressions if (op == "all") { @@ -263,17 +263,17 @@ static int eval(std::function feature, json_obje if (op == "in" || op == "!in") { - if (f->value.array.array.size() < 2) { + if (f->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) { + if (f->array()[1]->type != JSON_STRING) { fprintf(stderr, "\"!in\" key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - mvt_value ff = feature(f->value.array.array[1]->value.string.string); + mvt_value ff = feature(f->array()[1]->string()); if (ff.type == mvt_no_such_key) { static bool warned = false; if (!warned) { @@ -287,9 +287,9 @@ static int eval(std::function feature, json_obje } bool found = false; - for (size_t i = 2; i < f->value.array.array.size(); i++) { + for (size_t i = 2; i < f->array().size(); i++) { bool fail = false; - int cmp = compare(ff, f->value.array.array[i], fail); + int cmp = compare(ff, f->array()[i], fail); if (fail) { static bool warned = false; @@ -314,19 +314,19 @@ static int eval(std::function feature, json_obje } if (op == "attribute-filter") { - if (f->value.array.array.size() != 3) { + if (f->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) { + if (f->array()[1]->type != JSON_STRING) { fprintf(stderr, "\"attribute-filter\" key is not a string: %s\n", json_stringify(f).c_str()); exit(EXIT_FILTER); } - bool ok = eval(feature, f->value.array.array[2], exclude_attributes, unidecode_data) > 0; + bool ok = eval(feature, f->array()[2], exclude_attributes, unidecode_data) > 0; if (!ok) { - exclude_attributes.insert(f->value.array.array[1]->value.string.string); + exclude_attributes.insert(f->array()[1]->string()); } return true; diff --git a/geobuf.cpp b/geobuf.cpp index c06b73c9..721c675c 100644 --- a/geobuf.cpp +++ b/geobuf.cpp @@ -400,17 +400,17 @@ void readFeature(protozero::pbf_reader &pbf, size_t dim, double e, std::vectortype == JSON_NUMBER)) { - sf.tippecanoe_minzoom = integer_zoom(sst->fname, milo::dtoa_milo(min->value.number.number)); + sf.tippecanoe_minzoom = integer_zoom(sst->fname, milo::dtoa_milo(min->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)); + sf.tippecanoe_maxzoom = integer_zoom(sst->fname, milo::dtoa_milo(max->number())); } json_object_ptr tlayer = json_hash_get(o, "layer"); if (tlayer != nullptr && (tlayer->type == JSON_STRING)) { - layername = tlayer->value.string.string; + layername = tlayer->string(); } } } diff --git a/geojson-loop.cpp b/geojson-loop.cpp index adc0b0a1..75149e94 100644 --- a/geojson-loop.cpp +++ b/geojson-loop.cpp @@ -74,7 +74,7 @@ void parse_json(json_feature_action *jfa, json_pull_ptr jp) { int i; int is_geometry = 0; for (i = 0; i < GEOM_TYPES; i++) { - if (type->value.string.string == geometry_names[i]) { + if (type->string() == geometry_names[i]) { is_geometry = 1; break; } @@ -84,14 +84,14 @@ void parse_json(json_feature_action *jfa, json_pull_ptr jp) { if (j->parent != nullptr) { if (j->parent->type == JSON_ARRAY && j->parent->parent != nullptr) { if (j->parent->parent->type == JSON_HASH) { - json_object_ptr geometries = json_hash_get(j->parent->parent->shared_from_this(), "geometries"); + json_object_ptr geometries = json_hash_get(j->parent->parent, "geometries"); if (geometries != nullptr) { // Parent of Parent must be a GeometryCollection is_geometry = 0; } } } else if (j->parent->type == JSON_HASH) { - json_object_ptr geometry = json_hash_get(j->parent->shared_from_this(), "geometry"); + json_object_ptr geometry = json_hash_get(j->parent, "geometry"); if (geometry != nullptr) { // Parent must be a Feature is_geometry = 0; @@ -104,7 +104,7 @@ void parse_json(json_feature_action *jfa, json_pull_ptr jp) { 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) { + if (json_hash_get(jo->parent, "properties").get() == jo) { // Ancestor is the value corresponding to a properties key is_geometry = 0; break; @@ -126,8 +126,8 @@ void parse_json(json_feature_action *jfa, json_pull_ptr jp) { } } - if (type->value.string.string != "Feature") { - if (type->value.string.string == "FeatureCollection") { + if (type->string() != "Feature") { + if (type->string() == "FeatureCollection") { jfa->check_crs(j); json_free(j); } @@ -161,7 +161,7 @@ void parse_json(json_feature_action *jfa, json_pull_ptr jp) { 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) { + if (json_hash_get(jo->parent, "properties").get() == jo) { // Ancestor is the value corresponding to a properties key is_feature = false; break; diff --git a/geojson.cpp b/geojson.cpp index 6e287d73..4d16088f 100644 --- a/geojson.cpp +++ b/geojson.cpp @@ -68,12 +68,12 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object_ptr g int t; for (t = 0; t < GEOM_TYPES; t++) { - if (geometry_type->value.string.string == geometry_names[t]) { + if (geometry_type->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.c_str()); + fprintf(stderr, "%s:%d: Can't handle geometry type %s: ", sst->fname, sst->line, geometry_type->string().c_str()); json_context(feature); return 0; } @@ -85,17 +85,17 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object_ptr g 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)); + tippecanoe_minzoom = integer_zoom(sst->fname, milo::dtoa_milo(min->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)); + tippecanoe_maxzoom = integer_zoom(sst->fname, milo::dtoa_milo(max->number())); } json_object_ptr ln = json_hash_get(tippecanoe, "layer"); if (ln != nullptr && (ln->type == JSON_STRING)) { - tippecanoe_layername = ln->value.string.string; + tippecanoe_layername = ln->string(); } } @@ -103,27 +103,27 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object_ptr g unsigned long long id_value = 0; if (id != nullptr) { if (id->type == JSON_NUMBER) { - if (id->value.number.number >= 0) { + if (id->number() >= 0) { char *err = NULL; - std::string id_number = milo::dtoa_milo(id->value.number.number); + std::string id_number = milo::dtoa_milo(id->number()); id_value = strtoull(id_number.c_str(), &err, 10); - if (id->value.number.large_unsigned != 0) { - id_value = id->value.number.large_unsigned; + if (id->large_unsigned() != 0) { + id_value = id->large_unsigned(); } 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", milo::dtoa_milo(id->value.number.number).c_str()); + fprintf(stderr, "Warning: Can't represent non-integer feature ID %s\n", milo::dtoa_milo(id->number()).c_str()); warned_frac = true; } - } else if (id->value.number.large_unsigned == 0 && std::to_string(id_value) != milo::dtoa_milo(id->value.number.number)) { + } else if (id->large_unsigned() == 0 && std::to_string(id_value) != milo::dtoa_milo(id->number())) { static bool warned = false; if (!warned) { - fprintf(stderr, "Warning: Can't represent too-large feature ID %s\n", milo::dtoa_milo(id->value.number.number).c_str()); + fprintf(stderr, "Warning: Can't represent too-large feature ID %s\n", milo::dtoa_milo(id->number()).c_str()); warned = true; } } else { @@ -133,7 +133,7 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object_ptr g static bool warned_neg = false; if (!warned_neg) { - fprintf(stderr, "Warning: Can't represent negative feature ID %s\n", milo::dtoa_milo(id->value.number.number).c_str()); + fprintf(stderr, "Warning: Can't represent negative feature ID %s\n", milo::dtoa_milo(id->number()).c_str()); warned_neg = true; } } @@ -142,20 +142,20 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object_ptr g if (additional[A_CONVERT_NUMERIC_IDS] && id->type == JSON_STRING) { char *err = NULL; - id_value = strtoull(id->value.string.string.c_str(), &err, 10); + id_value = strtoull(id->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.c_str()); + fprintf(stderr, "Warning: Can't represent non-integer feature ID %s\n", id->string().c_str()); warned_frac = true; } - } else if (std::to_string(id_value) != id->value.string.string) { + } else if (std::to_string(id_value) != id->string()) { static bool warned = false; if (!warned) { - fprintf(stderr, "Warning: Can't represent too-large feature ID %s\n", id->value.string.string.c_str()); + fprintf(stderr, "Warning: Can't represent too-large feature ID %s\n", id->string().c_str()); warned = true; } } else { @@ -177,7 +177,7 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object_ptr g size_t nprop = 0; if (properties != nullptr && properties->type == JSON_HASH) { - nprop = properties->value.object.keys.size(); + nprop = properties->keys().size(); } std::vector> full_keys; @@ -188,10 +188,10 @@ int serialize_geojson_feature(struct serialization_state *sst, json_object_ptr g key_pool key_pool; for (size_t i = 0; i < nprop; i++) { - if (properties->value.object.keys[i]->type == JSON_STRING) { - serial_val sv = stringify_value(properties->value.object.values[i], sst->fname, sst->line, feature); + if (properties->keys()[i]->type == JSON_STRING) { + serial_val sv = stringify_value(properties->values()[i], sst->fname, sst->line, feature); - full_keys.emplace_back(key_pool.pool(properties->value.object.keys[i]->value.string.string.c_str())); + full_keys.emplace_back(key_pool.pool(properties->keys()[i]->string().c_str())); values.push_back(std::move(sv)); } } @@ -223,9 +223,9 @@ void check_crs(json_object_ptr j, const char *reading) { 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 (name->string() != projection->alias) { if (!quiet) { - 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: Warning: GeoJSON specified projection \"%s\", not the expected \"%s\".\n", reading, name->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); } } @@ -243,8 +243,8 @@ struct json_serialize_action : json_feature_action { sst->line = geometry->parser->line; if (geometrycollection) { int ret = 1; - 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); + for (size_t g = 0; g < geometry->array().size(); g++) { + ret &= serialize_geojson_feature(sst, geometry->array()[g], properties, id, layer, tippecanoe, feature, layername); } return ret; } else { diff --git a/jsonpull/jsonpull.cpp b/jsonpull/jsonpull.cpp index 9c061d57..d35bf8d3 100644 --- a/jsonpull/jsonpull.cpp +++ b/jsonpull/jsonpull.cpp @@ -86,22 +86,47 @@ static inline int read_wrap(json_pull *j) { 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; +// Construct an instance of the right subclass for the given type. +// JSON_TRUE / JSON_FALSE / JSON_NULL and the parse-token types are bare +// json_objects; the value-bearing types each get their own subclass. +static json_object_ptr make_object(json_type type, json_object *parent, json_pull *jp) { + json_object_ptr o; + switch (type) { + case JSON_NUMBER: + o = std::make_shared(parent, jp); + break; + case JSON_STRING: + o = std::make_shared(parent, jp); + break; + case JSON_ARRAY: + o = std::make_shared(parent, jp); + break; + case JSON_HASH: + o = std::make_shared(parent, jp); + break; + default: + o = std::make_shared(type, parent, jp); + break; + } return o; } +static json_object_ptr fabricate_object(json_pull *jp, json_object *parent, json_type type) { + return make_object(type, parent, jp); +} + +static inline json_object *current_container(json_pull *j) { + return j->container_stack.empty() ? nullptr : j->container_stack.back().get(); +} + 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); + json_object *c = current_container(j); + json_object_ptr o = make_object(type, c, j); if (c != nullptr) { if (c->type == JSON_ARRAY) { if (c->expect == JSON_ITEM) { - c->value.array.array.push_back(o); + c->array().push_back(o); c->expect = JSON_COMMA; } else { j->error = "Expected a comma, not a list item"; @@ -109,7 +134,7 @@ static json_object_ptr add_object(json_pull *j, json_type type) { } } else if (c->type == JSON_HASH) { if (c->expect == JSON_VALUE) { - c->value.object.values.back() = o; + c->values().back() = o; c->expect = JSON_COMMA; } else if (c->expect == JSON_KEY) { if (type != JSON_STRING) { @@ -117,8 +142,8 @@ static json_object_ptr add_object(json_pull *j, json_type type) { return nullptr; } - c->value.object.keys.push_back(o); - c->value.object.values.push_back(nullptr); + c->keys().push_back(o); + c->values().push_back(nullptr); c->expect = JSON_COLON; } else { j->error = "Expected a comma or colon"; @@ -134,16 +159,18 @@ static json_object_ptr add_object(json_pull *j, json_type type) { return o; } -json_object_ptr json_hash_get(json_object_ptr o, const char *s) { +json_object_ptr json_hash_get(json_object *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]; + const auto &keys = o->keys(); + const auto &vals = o->values(); + for (size_t i = 0; i < keys.size(); i++) { + const auto &key = keys[i]; if (key != nullptr && key->type == JSON_STRING) { - if (key->value.string.string == s) { - return o->value.object.values[i]; + if (key->string() == s) { + return vals[i]; } } } @@ -151,19 +178,23 @@ json_object_ptr json_hash_get(json_object_ptr o, const char *s) { return nullptr; } +json_object_ptr json_hash_get(json_object_ptr o, const char *s) { + return json_hash_get(o.get(), s); +} + 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) { + if (j->container_stack.empty()) { j->root.reset(); } again: c = read_wrap(j); if (c == EOF) { - if (j->container != nullptr) { + if (!j->container_stack.empty()) { j->error = "Reached EOF without all containers being closed"; } @@ -204,8 +235,8 @@ again: if (o == nullptr) { return nullptr; } - j->container = o; - j->container->expect = JSON_ITEM; + o->expect = JSON_ITEM; + j->container_stack.push_back(o); if (cb != nullptr) { cb(JSON_ARRAY, j, state); @@ -215,32 +246,26 @@ again: } case ']': { - if (j->container == nullptr) { + json_object *cc = current_container(j); + if (cc == nullptr) { j->error = "Found ] at top level"; return nullptr; } - if (j->container->type != JSON_ARRAY) { + if (cc->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)) { + if (cc->expect != JSON_COMMA) { + if (!(cc->expect == JSON_ITEM && cc->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(); - } + json_object_ptr ret = j->container_stack.back(); + j->container_stack.pop_back(); return ret; } @@ -251,8 +276,8 @@ again: if (o == nullptr) { return nullptr; } - j->container = o; - j->container->expect = JSON_KEY; + o->expect = JSON_KEY; + j->container_stack.push_back(o); if (cb != nullptr) { cb(JSON_HASH, j, state); @@ -262,29 +287,26 @@ again: } case '}': { - if (j->container == nullptr) { + json_object *cc = current_container(j); + if (cc == nullptr) { j->error = "Found } at top level"; return nullptr; } - if (j->container->type != JSON_HASH) { + if (cc->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)) { + if (cc->expect != JSON_COMMA) { + if (!(cc->expect == JSON_KEY && cc->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(); - } + json_object_ptr ret = j->container_stack.back(); + j->container_stack.pop_back(); return ret; } @@ -350,16 +372,17 @@ again: /////////////////////////// Comma case ',': { - if (j->container != nullptr) { - if (j->container->expect != JSON_COMMA) { + json_object *cc = current_container(j); + if (cc != nullptr) { + if (cc->expect != JSON_COMMA) { j->error = "Found unexpected comma"; return nullptr; } - if (j->container->type == JSON_HASH) { - j->container->expect = JSON_KEY; + if (cc->type == JSON_HASH) { + cc->expect = JSON_KEY; } else { - j->container->expect = JSON_ITEM; + cc->expect = JSON_ITEM; } } @@ -373,17 +396,18 @@ again: /////////////////////////// Colon case ':': { - if (j->container == nullptr) { + json_object *cc = current_container(j); + if (cc == nullptr) { j->error = "Found colon at top level"; return nullptr; } - if (j->container->expect != JSON_COLON) { + if (cc->expect != JSON_COLON) { j->error = "Found unexpected colon"; return nullptr; } - j->container->expect = JSON_VALUE; + cc->expect = JSON_VALUE; if (cb != nullptr) { cb(JSON_COLON, j, state); @@ -463,27 +487,27 @@ again: 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; + n->number() = atof(j->number_buffer.c_str()); + n->large_signed() = 0; + n->large_unsigned() = 0; #define MAX_SAFE_INTEGER 9007199254740991.0 #define MIN_SAFE_INTEGER -9007199254740991.0 - if (!decimal && n->value.number.number > MAX_SAFE_INTEGER) { + if (!decimal && n->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; + n->large_unsigned() = ull; } } - if (!decimal && n->value.number.number < MIN_SAFE_INTEGER) { + if (!decimal && n->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; + n->large_signed() = ll; } } } @@ -614,8 +638,7 @@ again: json_object_ptr s = add_object(j, JSON_STRING); if (s != nullptr) { - s->value.string.string = std::move(val); - s->value.string.refcon = nullptr; + s->string() = std::move(val); } return s; } @@ -653,13 +676,16 @@ static void clear_back_pointers(json_object *o) { } 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()); + const auto &keys = o->keys(); + const auto &vals = o->values(); + for (size_t i = 0; i < keys.size(); i++) { + clear_back_pointers(keys[i].get()); + clear_back_pointers(vals[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()); + const auto &arr = o->array(); + for (size_t i = 0; i < arr.size(); i++) { + clear_back_pointers(arr[i].get()); } } @@ -679,7 +705,7 @@ void json_disconnect(json_object_ptr o) { json_object *parent = o->parent; if (parent != nullptr) { if (parent->type == JSON_ARRAY) { - auto &arr = parent->value.array.array; + auto &arr = parent->array(); for (size_t i = 0; i < arr.size(); i++) { if (arr[i].get() == o.get()) { arr.erase(arr.begin() + i); @@ -687,8 +713,8 @@ void json_disconnect(json_object_ptr o) { } } } else if (parent->type == JSON_HASH) { - auto &keys = parent->value.object.keys; - auto &vals = parent->value.object.values; + auto &keys = parent->keys(); + auto &vals = parent->values(); for (size_t i = 0; i < keys.size(); i++) { if (keys[i].get() == o.get()) { @@ -739,7 +765,7 @@ static void json_print_one(std::string &val, json_object *o) { } else if (o->type == JSON_STRING) { string_append_c(val, '\"'); - for (const char *cp = o->value.string.string.c_str(); *cp != '\0'; cp++) { + for (const char *cp = o->string().c_str(); *cp != '\0'; cp++) { if (*cp == '\\' || *cp == '"') { string_append_c(val, '\\'); string_append_c(val, *cp); @@ -756,16 +782,16 @@ static void json_print_one(std::string &val, json_object *o) { string_append_c(val, '\"'); } else if (o->type == JSON_NUMBER) { - if (o->value.number.large_signed != 0) { + if (o->large_signed() != 0) { char s[65]; - snprintf(s, sizeof(s), "%lld", o->value.number.large_signed); + snprintf(s, sizeof(s), "%lld", o->large_signed()); string_append(val, s); - } else if (o->value.number.large_unsigned != 0) { + } else if (o->large_unsigned() != 0) { char s[65]; - snprintf(s, sizeof(s), "%llu", o->value.number.large_unsigned); + snprintf(s, sizeof(s), "%llu", o->large_unsigned()); string_append(val, s); } else { - char *s = dtoa_milo(o->value.number.number); + char *s = dtoa_milo(o->number()); string_append(val, s); free(s); } @@ -789,20 +815,23 @@ static void json_print(std::string &val, json_object *o) { } 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()); + const auto &keys = o->keys(); + const auto &vals = o->values(); + for (size_t i = 0; i < keys.size(); i++) { + json_print(val, keys[i].get()); string_append_c(val, ':'); - json_print(val, o->value.object.values[i].get()); - if (i + 1 < o->value.object.keys.size()) { + json_print(val, vals[i].get()); + if (i + 1 < 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()) { + const auto &arr = o->array(); + for (size_t i = 0; i < arr.size(); i++) { + json_print(val, arr[i].get()); + if (i + 1 < arr.size()) { string_append_c(val, ','); } } diff --git a/jsonpull/jsonpull.h b/jsonpull/jsonpull.h index 3ccb0fae..fa9b265f 100644 --- a/jsonpull/jsonpull.h +++ b/jsonpull/jsonpull.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -33,48 +34,152 @@ struct json_pull; 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. +// json_object is a small base type that just records the JSON type and +// the back-pointers to its parent and parser. The actual value payload +// lives in a type-specific subclass (json_number, json_string, json_array, +// json_hash), so that JSON_TRUE / JSON_FALSE / JSON_NULL nodes pay only +// the base-class cost and a JSON_HASH does not also drag along a string +// or a number field. Type-tagged accessor methods on the base class +// downcast and return references to the underlying subclass storage. +// +// Children are owned by their parent (via std::vector +// inside json_array / json_hash); the raw `parent` and `parser` +// back-pointers stay valid as long as the node is attached to the tree. +// json_disconnect() splices a node out of its parent and walks the +// detached subtree clearing those back-pointers so the subtree can +// outlive the original parser. +// +// json_object intentionally has no virtual functions and no virtual +// destructor: subclasses are constructed via std::make_shared(), +// and std::shared_ptr remembers the deleter from the original type, so +// destroying a shared_ptr that actually points at a +// json_string still runs ~json_string(). Dispatch on `type` is what the +// rest of the code already does. The accessor methods assert at debug +// time that the type matches before downcasting. -struct json_object : public std::enable_shared_from_this { +struct json_object { json_object *parent = nullptr; json_pull *parser = nullptr; - json_type type = JSON_NULL; - int expect = 0; + json_type type; + int expect = 0; // used by the parser on JSON_ARRAY / JSON_HASH nodes - // 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 = 0; - unsigned long long large_unsigned = 0; - long long large_signed = 0; - } number; + json_object(json_type t) : type(t) {} + json_object(json_type t, json_object *p, json_pull *pl) : parent(p), parser(pl), type(t) {} - struct { - std::string string; - void *refcon = nullptr; // reference constant for caller's use - } string; + // Type-tagged accessors. Each one asserts that the receiver is of + // the right kind, then downcasts to the storage in the appropriate + // subclass. Inline so the assert and cast disappear at -O. + inline std::string &string(); + inline const std::string &string() const; - struct { - std::vector array; - } array; + inline double &number(); + inline double number() const; + inline unsigned long long &large_unsigned(); + inline unsigned long long large_unsigned() const; + inline long long &large_signed(); + inline long long large_signed() const; - struct { - std::vector keys; - std::vector values; - } object; - } value; + inline std::vector &array(); + inline const std::vector &array() const; + + inline std::vector &keys(); + inline const std::vector &keys() const; + inline std::vector &values(); + inline const std::vector &values() const; }; +struct json_number : json_object { + double number_value = 0; + unsigned long long large_unsigned_value = 0; + long long large_signed_value = 0; + + json_number() : json_object(JSON_NUMBER) {} + json_number(json_object *p, json_pull *pl) : json_object(JSON_NUMBER, p, pl) {} +}; + +struct json_string : json_object { + std::string string_value; + + json_string() : json_object(JSON_STRING) {} + json_string(json_object *p, json_pull *pl) : json_object(JSON_STRING, p, pl) {} +}; + +struct json_array : json_object { + std::vector array_value; + + json_array() : json_object(JSON_ARRAY) {} + json_array(json_object *p, json_pull *pl) : json_object(JSON_ARRAY, p, pl) {} +}; + +struct json_hash : json_object { + std::vector keys_value; + std::vector values_value; + + json_hash() : json_object(JSON_HASH) {} + json_hash(json_object *p, json_pull *pl) : json_object(JSON_HASH, p, pl) {} +}; + +inline std::string &json_object::string() { + assert(type == JSON_STRING); + return static_cast(this)->string_value; +} +inline const std::string &json_object::string() const { + assert(type == JSON_STRING); + return static_cast(this)->string_value; +} + +inline double &json_object::number() { + assert(type == JSON_NUMBER); + return static_cast(this)->number_value; +} +inline double json_object::number() const { + assert(type == JSON_NUMBER); + return static_cast(this)->number_value; +} +inline unsigned long long &json_object::large_unsigned() { + assert(type == JSON_NUMBER); + return static_cast(this)->large_unsigned_value; +} +inline unsigned long long json_object::large_unsigned() const { + assert(type == JSON_NUMBER); + return static_cast(this)->large_unsigned_value; +} +inline long long &json_object::large_signed() { + assert(type == JSON_NUMBER); + return static_cast(this)->large_signed_value; +} +inline long long json_object::large_signed() const { + assert(type == JSON_NUMBER); + return static_cast(this)->large_signed_value; +} + +inline std::vector &json_object::array() { + assert(type == JSON_ARRAY); + return static_cast(this)->array_value; +} +inline const std::vector &json_object::array() const { + assert(type == JSON_ARRAY); + return static_cast(this)->array_value; +} + +inline std::vector &json_object::keys() { + assert(type == JSON_HASH); + return static_cast(this)->keys_value; +} +inline const std::vector &json_object::keys() const { + assert(type == JSON_HASH); + return static_cast(this)->keys_value; +} +inline std::vector &json_object::values() { + assert(type == JSON_HASH); + return static_cast(this)->values_value; +} +inline const std::vector &json_object::values() const { + assert(type == JSON_HASH); + return static_cast(this)->values_value; +} + struct json_pull { const char *error = nullptr; // points at a string literal; no allocation int line = 1; @@ -85,7 +190,11 @@ struct json_pull { ssize_t buffer_tail = 0; ssize_t buffer_head = 0; - json_object_ptr container; + // Stack of currently-open containers; the top is the innermost container + // being parsed. Replaces the previous single `container` pointer / parent + // walk, which previously required enable_shared_from_this + // on every json_object instance (16 extra bytes per node). + std::vector container_stack; json_object_ptr root; std::string number_buffer; @@ -118,6 +227,7 @@ void json_free(json_object_ptr &j); void json_disconnect(json_object_ptr j); json_object_ptr json_hash_get(json_object_ptr o, const char *s); +json_object_ptr json_hash_get(json_object *o, const char *s); std::string json_stringify(json_object_ptr o); diff --git a/jsontool.cpp b/jsontool.cpp index 5c35206f..e83ab5a8 100644 --- a/jsontool.cpp +++ b/jsontool.cpp @@ -149,7 +149,7 @@ void out(std::string const &s, int type, json_object_ptr properties) { if (o != nullptr) { found = true; if (o->type == JSON_STRING || o->type == JSON_NUMBER) { - extracted = sort_quote(o->value.string.string.c_str()); + extracted = sort_quote(o->string().c_str()); } else { extracted = sort_quote(json_stringify(o).c_str()); } @@ -244,9 +244,9 @@ void join_csv(json_object_ptr j) { std::string joinkey; if (key->type == JSON_STRING) { - joinkey = key->value.string.string; + joinkey = key->string(); } else if (key->type == JSON_NUMBER) { - joinkey = milo::dtoa_milo(key->value.number.number); + joinkey = milo::dtoa_milo(key->number()); } else { joinkey = json_stringify(key); } @@ -298,8 +298,8 @@ void join_csv(json_object_ptr j) { } if (fields.size() > 0 && joinkey == fields[0]) { - properties->value.object.keys.reserve(properties->value.object.keys.size() + fields.size()); - properties->value.object.values.reserve(properties->value.object.values.size() + fields.size()); + properties->keys().reserve(properties->keys().size() + fields.size()); + properties->values().reserve(properties->values().size() + fields.size()); for (size_t i = 1; i < fields.size(); i++) { std::string k = header[i]; @@ -317,30 +317,24 @@ void join_csv(json_object_ptr j) { } if (attr_type != JSON_NULL) { - auto ko = std::make_shared(); - auto vo = std::make_shared(); - - ko->type = JSON_STRING; - ko->parent = properties.get(); - ko->parser = properties->parser; - ko->value.string.string = k; - - vo->type = attr_type; - vo->parent = properties.get(); - vo->parser = properties->parser; + auto ko = std::make_shared(properties.get(), properties->parser); + ko->string_value = k; + json_object_ptr vo; if (attr_type == JSON_STRING) { - vo->value.string.string = v; + auto s = std::make_shared(properties.get(), properties->parser); + s->string_value = v; + vo = s; } else if (attr_type == JSON_NUMBER) { - vo->value.number.number = atof(v.c_str()); - vo->value.number.large_unsigned = 0; - vo->value.number.large_signed = 0; + auto n = std::make_shared(properties.get(), properties->parser); + n->number_value = atof(v.c_str()); + vo = n; } else { abort(); } - properties->value.object.keys.push_back(ko); - properties->value.object.values.push_back(vo); + properties->keys().push_back(ko); + properties->values().push_back(vo); } } } diff --git a/main.cpp b/main.cpp index 1fc7d497..fae5b50e 100644 --- a/main.cpp +++ b/main.cpp @@ -2885,9 +2885,9 @@ void set_attribute_value(const char *arg) { exit(EXIT_JSON); } - 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]; + for (size_t i = 0; i < o->keys().size(); i++) { + json_object_ptr k = o->keys()[i]; + json_object_ptr v = o->values()[i]; if (k->type != JSON_STRING) { fprintf(stderr, "%s: --set-attribute %s: key %zu not a string\n", *av, arg, i); @@ -2895,7 +2895,7 @@ void set_attribute_value(const char *arg) { } serial_val val = stringify_value(v, "json", 1, o); - set_attributes.emplace(k->value.string.string, val); + set_attributes.emplace(k->string(), val); } return; @@ -2941,21 +2941,21 @@ void parse_json_source(const char *arg, struct source &src) { exit(EXIT_JSON); } - src.file = fname->value.string.string; + src.file = fname->string(); json_object_ptr layer = json_hash_get(o, "layer"); if (layer != nullptr && layer->type == JSON_STRING) { - src.layer = layer->value.string.string; + src.layer = layer->string(); } json_object_ptr description = json_hash_get(o, "description"); if (description != nullptr && description->type == JSON_STRING) { - src.description = description->value.string.string; + src.description = description->string(); } json_object_ptr format = json_hash_get(o, "format"); if (format != nullptr && format->type == JSON_STRING) { - src.format = format->value.string.string; + src.format = format->string(); } } diff --git a/plugin.cpp b/plugin.cpp index b90fbfe7..10e8d9c3 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -168,7 +168,7 @@ serial_feature parse_feature(json_pull_ptr jp, int z, unsigned x, unsigned y, st if (type == nullptr || type->type != JSON_STRING) { continue; } - if (type->value.string.string != "Feature") { + if (type->string() != "Feature") { continue; } @@ -208,12 +208,12 @@ serial_feature parse_feature(json_pull_ptr jp, int z, unsigned x, unsigned y, st int t; for (t = 0; t < GEOM_TYPES; t++) { - if (geometry_type->value.string.string == geometry_names[t]) { + if (geometry_type->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.c_str()); + fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->string().c_str()); json_context(j); exit(EXIT_JSON); } @@ -252,22 +252,22 @@ serial_feature parse_feature(json_pull_ptr jp, int z, unsigned x, unsigned y, st if (tippecanoe != nullptr) { json_object_ptr layer = json_hash_get(tippecanoe, "layer"); if (layer != nullptr && layer->type == JSON_STRING) { - layername = layer->value.string.string; + layername = layer->string(); } json_object_ptr index = json_hash_get(tippecanoe, "index"); if (index != nullptr && index->type == JSON_NUMBER) { - sf.index = index->value.number.number; + sf.index = index->number(); } json_object_ptr sequence = json_hash_get(tippecanoe, "sequence"); if (sequence != nullptr && sequence->type == JSON_NUMBER) { - sf.seq = sequence->value.number.number; + sf.seq = sequence->number(); } json_object_ptr extent = json_hash_get(tippecanoe, "extent"); if (extent != nullptr && extent->type == JSON_NUMBER) { - sf.extent = extent->value.number.number; + sf.extent = extent->number(); } json_object_ptr dropped = json_hash_get(tippecanoe, "dropped"); @@ -297,9 +297,9 @@ serial_feature parse_feature(json_pull_ptr jp, int z, unsigned x, unsigned y, st 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; + sf.id = id->number(); + if (id->large_unsigned() > 0) { + sf.id = id->large_unsigned(); } sf.has_id = true; } @@ -343,18 +343,18 @@ serial_feature parse_feature(json_pull_ptr jp, int z, unsigned x, unsigned y, st } } - 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); + for (size_t i = 0; i < properties->keys().size(); i++) { + serial_val v = stringify_value(properties->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(properties->value.object.keys[i]->value.string.string)); + sf.full_keys.push_back(key_pool.pool(properties->keys()[i]->string())); sf.full_values.push_back(v); if (!postfilter) { - add_to_tilestats(ts->second.tilestats, properties->value.object.keys[i]->value.string.string, v); + add_to_tilestats(ts->second.tilestats, properties->keys()[i]->string(), v); } } } diff --git a/pmtiles_file.cpp b/pmtiles_file.cpp index 499d6cee..39ed4f44 100644 --- a/pmtiles_file.cpp +++ b/pmtiles_file.cpp @@ -415,37 +415,37 @@ 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.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) { + for (size_t i = 0; i < o->keys().size(); i++) { + const std::string &key = o->keys()[i]->string(); + if (key == "vector_layers" && o->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 (key == "tilestats" && o->value.object.values[i]->type == JSON_HASH) { + state.json_write_json(json_stringify(o->values()[i])); + } else if (key == "tilestats" && o->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 (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()); + state.json_write_json(json_stringify(o->values()[i])); + } else if (key == "strategies" && o->values()[i]->type == JSON_ARRAY) { + sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('strategies', %Q);", json_stringify(o->values()[i]).c_str()); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { fprintf(stderr, "set %s in metadata: %s\n", key.c_str(), err); } sqlite3_free(sql); - } 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()); + } else if (key == "tippecanoe_decisions" && o->values()[i]->type == JSON_HASH) { + sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES ('tippecanoe_decisions', %Q);", json_stringify(o->values()[i]).c_str()); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { 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) { + } else if (o->keys()[i]->type != JSON_STRING || o->values()[i]->type != JSON_STRING) { 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.c_str(), o->value.object.values[i]->value.string.string.c_str()); + sql = sqlite3_mprintf("INSERT INTO metadata (name, value) VALUES (%Q, %Q);", key.c_str(), o->values()[i]->string().c_str()); if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { fprintf(stderr, "set %s in metadata: %s\n", key.c_str(), err); } diff --git a/read_json.cpp b/read_json.cpp index 08ff2f14..61304153 100644 --- a/read_json.cpp +++ b/read_json.cpp @@ -63,7 +63,7 @@ void parse_coordinates(int t, json_object_ptr j, drawvec &out, int op, const cha int within = geometry_within[t]; if (within >= 0) { size_t i; - for (i = 0; i < j->value.array.array.size(); i++) { + for (i = 0; i < j->array().size(); i++) { if (within == GEOM_POINT) { if (i == 0 || mb_geometry[t] == VT_POINT) { op = VT_MOVETO; @@ -72,16 +72,16 @@ void parse_coordinates(int t, json_object_ptr j, drawvec &out, int op, const cha } } - parse_coordinates(within, j->value.array.array[i], out, op, fname, line, feature); + parse_coordinates(within, j->array()[i], out, op, fname, line, feature); } } else { - if (j->value.array.array.size() >= 2 && j->value.array.array[0]->type == JSON_NUMBER && j->value.array.array[1]->type == JSON_NUMBER) { + if (j->array().size() >= 2 && j->array()[0]->type == JSON_NUMBER && j->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; + double lon = j->array()[0]->number(); + double lat = j->array()[1]->number(); projection->project(lon, lat, 32, &x, &y); - if (j->value.array.array.size() > 2) { + if (j->array().size() > 2) { static int warned = 0; if (!warned) { @@ -129,7 +129,7 @@ serial_val stringify_value(json_object_ptr value, const char *reading, int line, if (vt == JSON_STRING) { sv.type = mvt_string; - sv.s = value->value.string.string; + sv.s = value->string(); std::string err = check_utf8(sv.s); if (err.size() > 0) { @@ -140,12 +140,12 @@ serial_val stringify_value(json_object_ptr value, const char *reading, int line, } else if (vt == JSON_NUMBER) { sv.type = mvt_double; - if (value->value.number.large_unsigned != 0) { - sv.s = std::to_string(value->value.number.large_unsigned); - } else if (value->value.number.large_signed != 0) { - sv.s = std::to_string(value->value.number.large_signed); + if (value->large_unsigned() != 0) { + sv.s = std::to_string(value->large_unsigned()); + } else if (value->large_signed() != 0) { + sv.s = std::to_string(value->large_signed()); } else { - sv.s = milo::dtoa_milo(value->value.number.number); + sv.s = milo::dtoa_milo(value->number()); } } else if (vt == JSON_TRUE) { sv.type = mvt_bool; @@ -200,12 +200,12 @@ std::pair parse_geometry(json_object_ptr geometry, json_pull_ptr j int t; for (t = 0; t < GEOM_TYPES; t++) { - if (geometry_type->value.string.string == geometry_names[t]) { + if (geometry_type->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.c_str()); + fprintf(stderr, "Filter output:%d: Can't handle geometry type %s: ", jp->line, geometry_type->string().c_str()); json_context(j); exit(EXIT_JSON); } @@ -325,7 +325,7 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int if (type == nullptr || type->type != JSON_STRING) { continue; } - if (type->value.string.string != "Feature") { + if (type->string() != "Feature") { continue; } @@ -342,7 +342,7 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int if (tippecanoe != nullptr) { layer = json_hash_get(tippecanoe, "layer"); if (layer != nullptr && layer->type == JSON_STRING) { - layername = layer->value.string.string; + layername = layer->string(); } } @@ -375,22 +375,22 @@ std::vector parse_layers(FILE *fp, int z, unsigned x, unsigned y, int 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; + feature.id = id->number(); + if (id->large_unsigned() > 0) { + feature.id = id->large_unsigned(); } feature.has_id = true; } - 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); + for (size_t i = 0; i < properties->keys().size(); i++) { + serial_val sv = stringify_value(properties->values()[i], "Filter output", jp->line, j); // Nulls can be excluded here because this is the postfilter // and it is nearly time to create the vector representation if (sv.type != mvt_null) { mvt_value v = stringified_to_mvt_value(sv.type, sv.s.c_str(), tile_stringpool); - l->second.tag(feature, properties->value.object.keys[i]->value.string.string, v); + l->second.tag(feature, properties->keys()[i]->string(), v); } } diff --git a/tile-join.cpp b/tile-join.cpp index 24d96167..cf4eb833 100644 --- a/tile-join.cpp +++ b/tile-join.cpp @@ -969,12 +969,12 @@ void handle_strategies(const unsigned char *s, std::vector *st) { json_object_ptr o = json_read_tree(jp); 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]; + for (size_t i = 0; i < o->array().size(); i++) { + json_object_ptr h = o->array()[i]; if (h->type == JSON_HASH) { - 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]; + for (size_t j = 0; j < h->keys().size(); j++) { + json_object_ptr k = h->keys()[j]; + json_object_ptr v = h->values()[j]; if (k->type != JSON_STRING) { fprintf(stderr, "Key %zu of %zu is not a string: %s\n", j, i, s); @@ -985,25 +985,25 @@ void handle_strategies(const unsigned char *s, std::vector *st) { st->resize(i + 1); } - const std::string &key = k->value.string.string; + const std::string &key = k->string(); if (key == "dropped_by_rate") { - (*st)[i].dropped_by_rate += v->value.number.number; + (*st)[i].dropped_by_rate += v->number(); } else if (key == "dropped_by_gamma") { - (*st)[i].dropped_by_gamma += v->value.number.number; + (*st)[i].dropped_by_gamma += v->number(); } else if (key == "dropped_as_needed") { - (*st)[i].dropped_as_needed += v->value.number.number; + (*st)[i].dropped_as_needed += v->number(); } else if (key == "coalesced_as_needed") { - (*st)[i].coalesced_as_needed += v->value.number.number; + (*st)[i].coalesced_as_needed += v->number(); } else if (key == "truncated_zooms") { - (*st)[i].truncated_zooms += v->value.number.number; + (*st)[i].truncated_zooms += v->number(); } else if (key == "detail_reduced") { - (*st)[i].detail_reduced += v->value.number.number; + (*st)[i].detail_reduced += v->number(); } else if (key == "tiny_polygons") { - (*st)[i].tiny_polygons += v->value.number.number; + (*st)[i].tiny_polygons += v->number(); } else if (key == "tile_size_desired") { - (*st)[i].tile_size += v->value.number.number; + (*st)[i].tile_size += v->number(); } else if (key == "feature_count_desired") { - (*st)[i].feature_count += v->value.number.number; + (*st)[i].feature_count += v->number(); } } } @@ -1016,14 +1016,14 @@ void handle_strategies(const unsigned char *s, std::vector *st) { 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_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"); + for (size_t i = 0; i < vector_layers->array().size(); i++) { + if (vector_layers->array()[i]->type == JSON_HASH) { + json_object_ptr id = json_hash_get(vector_layers->array()[i], "id"); + json_object_ptr desc = json_hash_get(vector_layers->array()[i], "description"); 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; + const std::string &sid = id->string(); + const std::string &sdesc = desc->string(); if (sdesc.size() != 0) { auto f = layermap.find(sid); @@ -1033,17 +1033,17 @@ void handle_vector_layers(json_object_ptr vector_layers, std::mapvalue.array.array[i], "fields"); + json_object_ptr fields = json_hash_get(vector_layers->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 std::string &desc2 = fields->value.object.values[j]->value.string.string; + for (size_t j = 0; j < fields->keys().size(); j++) { + if (fields->keys()[j]->type == JSON_STRING && fields->values()[j]->type) { + const std::string &desc2 = fields->values()[j]->string(); if (desc2 != "Number" && desc2 != "String" && desc2 != "Boolean" && desc2 != "Mixed") { - attribute_descriptions.insert(std::pair(fields->value.object.keys[j]->value.string.string, desc2)); + attribute_descriptions.insert(std::pair(fields->keys()[j]->string(), desc2)); } } }