Skip to content

refactor: drop ElementId; identity = position in tree#15

Merged
vieiralucas merged 3 commits into
mainfrom
drop-id
Jun 4, 2026
Merged

refactor: drop ElementId; identity = position in tree#15
vieiralucas merged 3 commits into
mainfrom
drop-id

Conversation

@vieiralucas

@vieiralucas vieiralucas commented Jun 3, 2026

Copy link
Copy Markdown
Member

What

  • Drops ElementId field on Counter / Register / Map. Identity for recursive merge = (key, kind) in the containing Map.
  • Removes elementid.c/h, test_elementid.c, and test_map_abort.c (death test for the now-defunct cross-arena composite abort).
  • *_create no longer take ElementId; counter_id / register_id / map_id accessors gone.
  • map_merge recursive guard simplified to (both alive, same kind, kind != SCALAR).
  • map_merge LWW composite path now element_clones the winner into the dst arena instead of host_aborting.
  • map_set tolerates type-flip at a key via LWW (composite displaces scalar, scalar displaces composite, different-kind composite displaces).
  • New get-or-create helpers: map_counter / map_register / map_map. Same kind in slot → return existing; otherwise install fresh if stamp wins LWW. Stamp loses → return detached composite (Yjs-style: caller always gets a usable handle).
  • Deep-copy clones for all primitives: scalar_clone (renamed from scalar_dup), register_clone, counter_clone, map_clone, and element_clone dispatcher.
  • map_clone skips element_clone on tombstones — stale value field would otherwise read undefined. Regression test probes via arena_used.
  • map_clone now checks hashtable_insert return.
  • counter_inc / counter_dec factored through counter_entry_for helper. ~50 lines of dup removed.
  • scalar_eq's string branch uses memcmp.

Why

ElementId (PR #14) was a redundant restatement of position in the tree. Every place that consumed it could derive the same answer from (parent path, key). Eliminating it simplifies the merge recursive guard, removes the cross-arena composite abort, unlocks type-flip at a key via LWW + clone, and makes detached-composite construction trivial — counter_create(arena); ...; map_set(m, k, klen, element_counter(c), stamp) just works.

273 tests across 10 suites (was 251).


Summary by cubic

Remove ElementId; identity is now the (key, kind) position in the parent Map. This simplifies merges, enables safe LWW type flips, supports cross-arena composites via deep cloning, and avoids wasted clones when a source loses LWW.

  • Refactors

    • Removed ElementId across the codebase; deleted elementid.*, dropped *_id accessors, updated *_create(Arena *) APIs.
    • Simplified map_merge recursion to “both alive, same kind”; LWW path clones the winner into dst and now skips clone+set when src would lose.
    • Added deep-clone APIs: element_clone, scalar_clone (renamed from scalar_dup), register_clone, counter_clone, map_clone; map_clone skips tombstones and checks hashtable_insert.
    • Cleanups: deduped counter updates via counter_entry_for; scalar_eq uses memcmp with a zero-length guard.
    • Refreshed header comments for positional identity, LWW semantics, ownership, and lifetimes.
    • Removed abort/death tests and Makefile targets tied to ElementId; expanded tests (273 total, up from 251).
  • New Features

    • Type flips at a key via LWW (scalar ↔ composite; composite kind changes).
    • Cross-arena composite LWW now works by cloning into the destination arena.
    • Get-or-create helpers: map_counter / map_register / map_map (return existing if kind matches; otherwise install if stamp wins; losing stamp returns a detached composite handle).

Written for commit 92ea0e9. Summary will update on new commits.

Review in cubic

Eliminates the ElementId field on Counter / Register / Map. Identity for
recursive merge comes from the slot's (key, kind) tuple in the containing
Map. Removes the elementid module entirely and the dedicated death test
binary that exercised the cross-arena composite abort.

- map_merge's recursive guard simplifies to (same kind, both alive) — no
  id comparison.
- map_merge's LWW composite path now element_clones the winner into the
  dst arena instead of host_abort'ing. Cross-arena composite displacement
  is supported and observable.
- map_set tolerates type-flip at a key via LWW (composite displaces
  scalar, scalar displaces composite, different-kind composite displaces).
- New get-or-create helpers: map_counter / map_register / map_map. Each
  derives the composite at the given key (same kind → return existing
  pointer; different kind / empty / tombstone → install fresh if stamp
  wins LWW; stamp loses → return detached composite).
- Deep-copy clones land for all primitives: scalar_clone (renamed from
  scalar_dup), register_clone, counter_clone, map_clone, and the
  element_clone dispatcher.
- map_clone skips element_clone on tombstones — stale value field would
  otherwise read as undefined; covered by a new regression that probes
  arena_used after cloning a deleted big-subtree slot.
- map_clone now checks hashtable_insert; was unchecked.
- counter_inc / counter_dec factored through a counter_entry_for helper
  removing ~50 lines of duplication.
- scalar_eq's string branch uses memcmp instead of a hand-rolled loop.

Drops: elementid.c/h, test_elementid.c, test_map_abort.c, counter_id /
register_id / map_id accessors, ElementId arg from all *_create.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 21 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread map.c
Comment thread scalar.c
…rings

- map_merge: skip element_clone + map_set when src would lose LWW against
  an existing dst entry. The clone is a deep recursive copy into dst's
  arena, and map_set would discard the write anyway — so the work was
  guaranteed-unreachable when src lost.
- scalar_eq: short-circuit zero-length string comparison before memcmp.
  memcmp on NULL pointers is UB pre-C2x even with size 0; the original
  byte loop was safe here, my prior refactor was not.
@vieiralucas vieiralucas merged commit b466d5d into main Jun 4, 2026
2 checks passed
@vieiralucas vieiralucas deleted the drop-id branch June 4, 2026 19:40
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.

1 participant