mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-04 17:35:42 +02:00
Keep the parent links inside a detached jsonpull subtree
json_read_tree() and json_disconnect() cleared both back-pointers on every node of the subtree they handed out. Clearing `parser` throughout is necessary -- the json_pull can be destroyed while the subtree lives on, so a surviving `parser` would dangle -- but clearing `parent` throughout cost more than it bought. `parent` is a non-owning raw pointer, so keeping it cannot form a reference cycle or keep anything alive; there is nothing to leak. And within a detached subtree it refers to nodes the caller now owns as a single unit, so it stays valid for exactly as long as the subtree itself. Clearing it only made the tree unwalkable upwards, and made json_free() and json_disconnect() silently no-ops on interior nodes of a detached tree, since both find a node's owner through o->parent. So clear `parser` everywhere and clear `parent` on the detached root alone, which is the one that pointed out of the subtree at a node the parser still owns. Split the old clear_back_pointers() into clear_parser_pointers() plus a detach_subtree() wrapper that adds the root's `parent`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017KNxyHKasyWrWcvre2yK4r
This commit is contained in:
+21
-13
@@ -78,10 +78,14 @@ struct json_entry {
|
|||||||
//
|
//
|
||||||
// Children are owned by their parent (via std::vector<json_object_ptr>
|
// Children are owned by their parent (via std::vector<json_object_ptr>
|
||||||
// inside json_array / json_hash); the raw `parent` and `parser`
|
// inside json_array / json_hash); the raw `parent` and `parser`
|
||||||
// back-pointers stay valid as long as the node is attached to the tree.
|
// back-pointers own nothing. json_disconnect() splices a node out of its
|
||||||
// json_disconnect() splices a node out of its parent and walks the
|
// parent and walks the detached subtree clearing every `parser` pointer,
|
||||||
// detached subtree clearing those back-pointers so the subtree can
|
// so the subtree can outlive the original parser. The `parent` pointers
|
||||||
// outlive the original parser.
|
// within the subtree survive -- they refer to nodes the caller now owns
|
||||||
|
// as one unit -- so a detached tree can still be walked upwards, and
|
||||||
|
// json_free() / json_disconnect() still work on its interior nodes. Only
|
||||||
|
// the detached root's `parent`, which pointed out of the subtree, is
|
||||||
|
// cleared.
|
||||||
//
|
//
|
||||||
// json_object intentionally has no virtual functions and no virtual
|
// json_object intentionally has no virtual functions and no virtual
|
||||||
// destructor; the json_object_ptr deleter (see below in this header)
|
// destructor; the json_object_ptr deleter (see below in this header)
|
||||||
@@ -364,10 +368,11 @@ json_object *json_read_separators(json_pull_ptr &j, json_separator_callback cb,
|
|||||||
|
|
||||||
// json_read_tree drains the next top-level value out of the parser
|
// json_read_tree drains the next top-level value out of the parser
|
||||||
// and hands ownership to the caller. After it returns, jp->root is
|
// and hands ownership to the caller. After it returns, jp->root is
|
||||||
// empty, the parent/parser back-pointers throughout the subtree have
|
// empty, every `parser` back-pointer in the subtree has been cleared
|
||||||
// been cleared, and the caller's json_object_ptr is the only thing
|
// (as has the root's `parent`), and the caller's json_object_ptr is the
|
||||||
// keeping the tree alive. The returned tree can outlive the
|
// only thing keeping the tree alive. The returned tree can outlive the
|
||||||
// json_pull it was parsed from.
|
// json_pull it was parsed from, and stays internally navigable: the
|
||||||
|
// `parent` pointers between its nodes are left intact.
|
||||||
json_object_ptr json_read_tree(json_pull_ptr &j);
|
json_object_ptr json_read_tree(json_pull_ptr &j);
|
||||||
|
|
||||||
// json_free splices `o` out of its parent (if any), or clears the
|
// json_free splices `o` out of its parent (if any), or clears the
|
||||||
@@ -377,11 +382,14 @@ json_object_ptr json_read_tree(json_pull_ptr &j);
|
|||||||
void json_free(json_object *o);
|
void json_free(json_object *o);
|
||||||
|
|
||||||
// Splice `o` out of its parent's array/object (or out of the parser's
|
// Splice `o` out of its parent's array/object (or out of the parser's
|
||||||
// root), walk the detached subtree clearing parent/parser back-pointers,
|
// root), walk the detached subtree clearing every `parser` back-pointer
|
||||||
// and return ownership of the subtree to the caller as a
|
// (and the root's `parent`, which pointed out of the subtree), and return
|
||||||
// json_object_ptr. After this returns, the parser no longer references
|
// ownership of the subtree to the caller as a json_object_ptr. After this
|
||||||
// any node in the subtree, and the subtree can outlive the original
|
// returns, the parser no longer references any node in the subtree, and
|
||||||
// parser.
|
// the subtree can outlive the original parser. The `parent` pointers
|
||||||
|
// among the subtree's own nodes are preserved, so the detached tree can
|
||||||
|
// still be walked upwards and json_free() / json_disconnect() still work
|
||||||
|
// on its interior nodes.
|
||||||
json_object_ptr json_disconnect(json_object *o);
|
json_object_ptr json_disconnect(json_object *o);
|
||||||
|
|
||||||
// Look up `s` in the hash `o`. Returns a borrowed pointer; ownership
|
// Look up `s` in the hash `o`. Returns a borrowed pointer; ownership
|
||||||
|
|||||||
Reference in New Issue
Block a user