╭ changed ────╮ │src/event.zig│ ╰─────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │617 const parent_child = try DB.SortedSet(.read_only).init(kv_pair.value_cursor); │ │618 const baseline_child = try DB.SortedSet(.read_only).init(baseline_value_cursor); │ │619 try mergeChangedSetEntries(DB, allocator, target_child, parent_child, baseline_child); │ │620 + } else if (tag == .sorted_map) { │ │621 + const target_existing_cursor = try target.getCursor(kv_pair.hash) orelse return error.MergeConflict;│ │622 + if (target_existing_cursor.slot().tag != .sorted_map) { │ │623 + return error.UnexpectedTag; │ │624 + } │ │625 + │ │626 + const target_child_cursor = try target.putCursor(kv_pair.hash); │ │627 + const target_child = try DB.SortedMap(.read_write).init(target_child_cursor); │ │628 + const parent_child = try DB.SortedMap(.read_only).init(kv_pair.value_cursor); │ │629 + const baseline_child = try DB.SortedMap(.read_only).init(baseline_value_cursor); │ │630 + try mergeChangedSortedMapEntries(DB, allocator, target_child, parent_child, baseline_child); │ │631 } else if (is_top_level) { │ │632 // the moment-index is a top-level key in the haxy moment │ │633 // and is not a map. we don't want to do any merge of this. │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │642 } │ │643 │ │644 try target.put(kv_pair.hash, .{ .slot = kv_pair.value_cursor.slot() }); │ │645 + } │ │646 + } │ │647 +} │ │648 + │ │649 +// three-way merge of a sorted map keyed by byte strings, like │ │650 +// mergeChangedMapEntries but recursing into sorted-set values. │ │651 +fn mergeChangedSortedMapEntries( │ │652 + comptime DB: type, │ │653 + allocator: std.mem.Allocator, │ │654 + target: DB.SortedMap(.read_write), │ │655 + parent: DB.SortedMap(.read_only), │ │656 + baseline: DB.SortedMap(.read_only), │ │657 +) !void { │ │658 + var parent_iter = try parent.iterator(); │ │659 + while (try parent_iter.next()) |kv_pair_cursor| { │ │660 + const kv_pair = try kv_pair_cursor.readKeyValuePair(); │ │661 + const key = try kv_pair.key_cursor.readBytesAlloc(allocator, null); │ │662 + defer allocator.free(key); │ │663 + │ │664 + const baseline_value_cursor = try baseline.getCursor(key) orelse { │ │665 + if (try target.getCursor(key)) |target_existing_cursor| { │ │666 + // both parents created this key. sets union (they hold │ │667 + // independently added members); anything else conflicts. │ │668 + const parent_tag = kv_pair.value_cursor.slot().tag; │ │669 + if (target_existing_cursor.slot().tag != parent_tag) { │ │670 + return error.UnexpectedTag; │ │671 + } │ │672 + if (parent_tag != .sorted_set) { │ │673 + return error.MergeConflict; │ │674 + } │ │675 + │ │676 + const target_child_cursor = try target.putCursor(key); │ │677 + const target_child = try DB.SortedSet(.read_write).init(target_child_cursor); │ │678 + const parent_child = try DB.SortedSet(.read_only).init(kv_pair.value_cursor); │ │679 + var set_iter = try parent_child.iterator(); │ │680 + while (try set_iter.next()) |set_kv_cursor| { │ │681 + const set_kv = try set_kv_cursor.readKeyValuePair(); │ │682 + const set_key = try set_kv.key_cursor.readBytesAlloc(allocator, null); │ │683 + defer allocator.free(set_key); │ │684 + try target_child.put(set_key); │ │685 + } │ │686 + continue; │ │687 + } │ │688 + │ │689 + try target.put(key, .{ .slot = kv_pair.value_cursor.slot() }); │ │690 + continue; │ │691 + }; │ │692 + │ │693 + const tag = kv_pair.value_cursor.slot().tag; │ │694 + │ │695 + if (tag != baseline_value_cursor.slot().tag) { │ │696 + return error.UnexpectedTag; │ │697 } │ │698 + │ │699 + if (kv_pair.value_cursor.slot().value == baseline_value_cursor.slot().value) { │ │700 + continue; │ │701 + } │ │702 + │ │703 + if (tag == .sorted_set) { │ │704 + const target_existing_cursor = try target.getCursor(key) orelse return error.MergeConflict;│ │705 + if (target_existing_cursor.slot().tag != .sorted_set) { │ │706 + return error.UnexpectedTag; │ │707 + } │ │708 + │ │709 + const target_child_cursor = try target.putCursor(key); │ │710 + const target_child = try DB.SortedSet(.read_write).init(target_child_cursor); │ │711 + const parent_child = try DB.SortedSet(.read_only).init(kv_pair.value_cursor); │ │712 + const baseline_child = try DB.SortedSet(.read_only).init(baseline_value_cursor); │ │713 + try mergeChangedSetEntries(DB, allocator, target_child, parent_child, baseline_child); │ │714 + } else { │ │715 + const target_value_cursor = try target.getCursor(key) orelse return error.MergeConflict; │ │716 + if (target_value_cursor.slot().tag != baseline_value_cursor.slot().tag) { │ │717 + return error.UnexpectedTag; │ │718 + } │ │719 + if (target_value_cursor.slot().value != baseline_value_cursor.slot().value) { │ │720 + return error.MergeConflict; │ │721 + } │ │722 + │ │723 + try target.put(key, .{ .slot = kv_pair.value_cursor.slot() }); │ │724 + } │ │725 } │ │726 } │ │727 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯