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:
Erica Fischer
2026-05-30 17:48:32 -07:00
co-authored by Cursor
parent 9381158165
commit 3da03c6075
25 changed files with 1295 additions and 531 deletions
+26 -60
View File
@@ -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<json_object>();
auto vo = std::make_shared<json_object>();
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) {