mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 16:35:40 +02:00
Convert jsonpull to C++ with shared_ptr and std::vector/std::string
Replace the manual malloc/realloc/free memory management in jsonpull with std::shared_ptr ownership. Each json_object now owns its children through std::vector<json_object_ptr>; raw back-pointers to parent and parser remain valid by structural invariant and are cleared on json_disconnect so detached subtrees can outlive their parser. Strings become std::string, child arrays become std::vector, and the old union becomes a struct so non-trivial members can coexist while preserving the existing o->value.xxx access paths. The old jsonpull.c is replaced by jsonpull.cpp, json_stringify now returns std::string, and all callers across tippecanoe, tile-join, tippecanoe-decode, tippecanoe-json-tool, tippecanoe-overzoom and the unit tests are updated to use json_object_ptr / json_pull_ptr. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+43
-51
@@ -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<mvt_geometry> to_feature(drawvec &geom) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::pair<int, drawvec> parse_geometry(json_object *geometry, json_pull *jp, json_object *j,
|
||||
std::pair<int, drawvec> 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<int, drawvec> 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<int, drawvec> 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<mvt_layer> parse_layers(FILE *fp, int z, unsigned x, unsigned y, int
|
||||
std::map<std::string, mvt_layer> ret;
|
||||
std::shared_ptr<std::string> tile_stringpool = std::make_shared<std::string>();
|
||||
|
||||
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<mvt_layer> 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<mvt_layer> 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<mvt_layer> 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<mvt_layer> 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<mvt_layer> 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<mvt_layer> final;
|
||||
for (auto a : ret) {
|
||||
final.push_back(a.second);
|
||||
|
||||
Reference in New Issue
Block a user