- geojson.cpp and attribute.cpp passed key_pool::pool() and
set_attribute_accum() a c_str() from a std::string, forcing a needless
reconstruction (and truncating at an embedded NUL). Both overloads take
std::string, so pass it directly. The geojson.cpp one is the hottest
loop in the program.
- Replace the hand-maintained counters beside range-for loops in
attribute.cpp, main.cpp and tile-join.cpp with indexed loops, since the
index is only wanted for error messages.
- parse_json_args took json_pull_ptr by value and then copied it, costing
two refcount bumps per construction. Move it.
- Assert that the parser is still attached where geojson.cpp reads
geometry->parser->line. Only json_read results reach it today, but
json_read_tree and json_disconnect now clear every parser pointer, so a
detached tree would null-deref there instead of tripping an assert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017KNxyHKasyWrWcvre2yK4r
Replaces the shared_ptr-based json_object_ptr with a unique_ptr that
has a stateless custom deleter dispatching on json_object::type before
calling the right subclass destructor. Eliminates per-node atomic
reference-counting and the control-block allocation that shared_ptr
required for every node in the tree.
API now distinguishes owning and borrowing pointers explicitly:
- json_read / json_read_separators / json_hash_get return raw
json_object * (borrowed from the parser-owned tree).
- json_read_tree / json_disconnect return json_object_ptr (caller
takes ownership; back-pointers are cleared so the subtree can
outlive the parser).
- json_free / json_context / json_stringify take raw pointers.
- The parser's container_stack holds raw pointers; jp->root keeps
unique_ptr ownership of the most recent top-level value.
Internally, take_from_owner moves the unique_ptr out of whichever
parent vector / hash entry / parser root owned it, which both
json_free and json_disconnect rely on.
In the streaming parsers (parse_feature, parse_layers, the
geojson-loop callback), we are careful to free `j` only after we
have processed a complete Feature: json_read returns each token
as the tree is being built up, and freeing an intermediate node
would splice it out of the surrounding hash and corrupt the
in-progress feature.
Benchmark (tl_2022_us_county.json, -z0 --extend-zooms-if-still-dropping,
median of 5 runs on macOS arm64): 8.5s, vs 10.6s with shared_ptr
and 8.7s on the pre-refactor C baseline.
Co-authored-by: Cursor <cursoragent@cursor.com>
- jsontool.cpp `out()`: route JSON_NUMBER (and anything else non-string)
through `json_stringify` instead of `o->string()`, which now asserts
on a non-string type and would crash `--extract` on numeric attributes.
- geojson.{hpp,cpp} `json_end_map`: take `json_pull_ptr` by reference so
the caller's shared_ptr is released, null-guard before touching
`jp->source`, and clear `jp->source` after delete to avoid a dangling
pointer.
- jsonpull/jsonpull.cpp: low-surrogate range check was comparing the
outer-loop byte `c` instead of the parsed code unit `ch`, breaking
surrogate-pair decoding for some \\uXXXX escapes. Pre-existing bug
preserved across the port.
- tile-join.cpp `handle_vector_layers`: require the field value to have
type JSON_STRING (and the key to be non-null) before calling
`string()`; the previous truthy `type` check would assert on a
non-string value.
Co-authored-by: Cursor <cursoragent@cursor.com>
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>