Skip to content

Make tree deserialisation exception-safe - #80

Merged
Amaury Chamayou (achamayou) merged 1 commit into
mainfrom
achamayou-tree-deserialise-ownership
Aug 25, 2026
Merged

Make tree deserialisation exception-safe#80
Amaury Chamayou (achamayou) merged 1 commit into
mainfrom
achamayou-tree-deserialise-ownership

Conversation

@achamayou

@achamayou Amaury Chamayou (achamayou) commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

  • reconstruct deserialised leaves and intermediate levels under std::unique_ptr ownership instead of assembling a raw-pointer forest
  • keep the non-owning leaf-pointer index local until reconstruction succeeds
  • transfer child ownership only after parent allocation succeeds, then release the completed root and publish leaf_nodes and num_flushed as the final commit step

Rationale

The tree stores its completed structure as raw pointers owned recursively by the root. During deserialisation, however, there is no root owner until reconstruction finishes. The previous implementation placed newly allocated leaves and intermediate nodes directly in raw-pointer containers, so an exception during reconstruction could strand every node built so far.

The lower bounds-checking PR now rejects known malformed encodings before allocation, but reconstruction still contains throwing operations, including node allocation and vector growth, and must remain safe if later reconstruction work introduces another exception. This PR gives the in-progress forest explicit RAII ownership without changing the successful tree representation.

Ownership and cleanup guarantees

  • each leaf is wrapped in a std::unique_ptr immediately after allocation; the temporary leaf index contains non-owning aliases only
  • when combining two children, both remain independently owned until Node::make(left, right) succeeds; ownership is released only after the new parent has taken responsibility for recursively deleting them
  • unpaired nodes move to the next level without losing ownership
  • if allocation, insertion, or level rebuilding throws, the owning vectors destroy every complete or partial subtree automatically
  • member leaf_nodes and num_flushed are not published until the root is complete, so a failed deserialisation leaves no partially published tree state
  • on success, exactly one completed root is released into _root; its existing recursive destructor owns all descendants, while leaf_nodes remains the tree's non-owning leaf index

Malformed input coverage

The lower PR validates platform-size limits and the full serialized hash count before reconstruction, including truncated resident leaves and missing flushed-edge hashes. This PR retains a regression with one resident leaf, num_flushed == 3, and only one of the two required flushed-edge hashes. It must fail with not enough bytes; combined with sanitizer execution, this covers the stacked malformed-input path while the ownership changes protect any exception that occurs after allocation begins.

Testing

Clang 21.1.8 Debug build with AddressSanitizer, LeakSanitizer (detect_leaks=1), and UndefinedBehaviorSanitizer:

  • TreeT rejects invalid serialised leaf data
  • TreeT deserialises flushed counts beyond signed shift width
  • Empty tree
  • One-node tree
  • Three-node tree

Result: 5 test cases passed, 65 assertions passed.

@achamayou
Amaury Chamayou (achamayou) force-pushed the achamayou-tree-deserialise-ownership branch 2 times, most recently from 2a49f78 to 5778593 Compare August 24, 2026 20:31
@achamayou
Amaury Chamayou (achamayou) marked this pull request as ready for review August 25, 2026 12:12
@achamayou
Amaury Chamayou (achamayou) requested a review from a team as a code owner August 25, 2026 12:12
Copilot AI balanced review requested due to automatic review settings August 25, 2026 12:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Makes tree deserialization exception-safe by using RAII during reconstruction.

Changes:

  • Owns temporary nodes with std::unique_ptr.
  • Publishes root and leaf metadata only after successful reconstruction.
  • Adds truncated flushed-hash regression coverage.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
merklecpp.h Adds exception-safe ownership and final state commit.
test/unit_tests.cpp Tests truncated flushed-edge hashes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread merklecpp.h
@achamayou
Amaury Chamayou (achamayou) force-pushed the achamayou-tree-deserialise-ownership branch 2 times, most recently from 4a25c5b to e3801e3 Compare August 25, 2026 12:54
Base automatically changed from achamayou-tree-bounds-checks to main August 25, 2026 15:09
Own partially reconstructed nodes until the complete tree has been built, then transfer the final root and leaf metadata. Add a malformed flushed-tree regression that exercises cleanup after partial reconstruction.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@achamayou
Amaury Chamayou (achamayou) force-pushed the achamayou-tree-deserialise-ownership branch from e3801e3 to 048536d Compare August 25, 2026 15:09
@achamayou
Amaury Chamayou (achamayou) merged commit c588d8b into main Aug 25, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants