| ... | ... | @@ -207,6 +207,7 @@ pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancel |
| 207 | 207 | .resized = false, |
| 208 | 208 | .next_moved = false, |
| 209 | 209 | .has_content = false, |
| 210 | .updated = false, |
| 210 | 211 | }, |
| 211 | 212 | .location_payload = switch (root_location) { |
| 212 | 213 | .small => |small| .{ .small = small }, |
| ... | ... | @@ -287,7 +288,14 @@ pub const Node = extern struct { |
| 287 | 288 | next_moved: bool, |
| 288 | 289 | /// Whether this node might contain initialized bytes. |
| 289 | 290 | has_content: bool, |
| 290 | | unused: u17 = 0, |
| 291 | |
| 292 | /// Whether this node is currently in `updates`. Usually this is just equal to whether any |
| 293 | /// of `moved`, `resized`, or `next_moved` is set, but when a node is deleted and later |
| 294 | /// reused, it may still be in `updates`, so preserving this flag through the deletion |
| 295 | /// prevents us from adding it to `updates` a second time. |
| 296 | updated: bool, |
| 297 | |
| 298 | unused: u16 = 0, |
| 291 | 299 | }; |
| 292 | 300 | |
| 293 | 301 | pub const Location = union(enum(u1)) { |
| ... | ... | @@ -494,6 +502,11 @@ pub const Node = extern struct { |
| 494 | 502 | } |
| 495 | 503 | } |
| 496 | 504 | |
| 505 | pub fn cleanUpdated(ni: Node.Index, mf: *MappedFile) void { |
| 506 | assert(ni.get(mf).flags.updated); |
| 507 | ni.get(mf).flags.updated = false; |
| 508 | } |
| 509 | |
| 497 | 510 | pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool { |
| 498 | 511 | var cur_ni = ni; |
| 499 | 512 | while (cur_ni != .root) { |
| ... | ... | @@ -518,9 +531,11 @@ pub const Node = extern struct { |
| 518 | 531 | if (node.prev.unwrap()) |prev_ni| prev_ni.nextMovedAssumeCapacity(mf); |
| 519 | 532 | if (ni.hasMoved(mf)) return; |
| 520 | 533 | node.flags.moved = true; |
| 521 | | if (node.flags.resized or node.flags.next_moved) return; |
| 522 | | mf.updates.appendAssumeCapacity(ni); |
| 523 | | mf.update_prog_node.increaseEstimatedTotalItems(1); |
| 534 | if (!node.flags.updated) { |
| 535 | node.flags.updated = true; |
| 536 | mf.updates.appendAssumeCapacity(ni); |
| 537 | mf.update_prog_node.increaseEstimatedTotalItems(1); |
| 538 | } |
| 524 | 539 | } |
| 525 | 540 | |
| 526 | 541 | pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool { |
| ... | ... | @@ -539,9 +554,11 @@ pub const Node = extern struct { |
| 539 | 554 | const node = ni.get(mf); |
| 540 | 555 | if (node.flags.resized) return; |
| 541 | 556 | node.flags.resized = true; |
| 542 | | if (node.flags.moved or node.flags.next_moved) return; |
| 543 | | mf.updates.appendAssumeCapacity(ni); |
| 544 | | mf.update_prog_node.increaseEstimatedTotalItems(1); |
| 557 | if (!node.flags.updated) { |
| 558 | node.flags.updated = true; |
| 559 | mf.updates.appendAssumeCapacity(ni); |
| 560 | mf.update_prog_node.increaseEstimatedTotalItems(1); |
| 561 | } |
| 545 | 562 | } |
| 546 | 563 | |
| 547 | 564 | pub fn hasNextMoved(ni: Node.Index, mf: *const MappedFile) bool { |
| ... | ... | @@ -560,9 +577,11 @@ pub const Node = extern struct { |
| 560 | 577 | const node = ni.get(mf); |
| 561 | 578 | if (!node.flags.enable_next_moved or node.flags.next_moved) return; |
| 562 | 579 | node.flags.next_moved = true; |
| 563 | | if (node.flags.moved or node.flags.resized) return; |
| 564 | | mf.updates.appendAssumeCapacity(ni); |
| 565 | | mf.update_prog_node.increaseEstimatedTotalItems(1); |
| 580 | if (!node.flags.updated) { |
| 581 | node.flags.updated = true; |
| 582 | mf.updates.appendAssumeCapacity(ni); |
| 583 | mf.update_prog_node.increaseEstimatedTotalItems(1); |
| 584 | } |
| 566 | 585 | } |
| 567 | 586 | |
| 568 | 587 | pub fn alignment(ni: Node.Index, mf: *const MappedFile) Alignment { |
| ... | ... | @@ -647,19 +666,30 @@ pub const Node = extern struct { |
| 647 | 666 | pub fn delete(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void { |
| 648 | 667 | const node = ni.get(mf); |
| 649 | 668 | assert(node.first == .none and node.last == .none); // has children |
| 650 | | mf.removeNodesFromChildList(gpa, ni, ni); |
| 651 | | const updated = node.flags.moved or node.flags.resized or node.flags.next_moved; |
| 652 | | node.* = undefined; |
| 653 | | node.next = ni.toOptional(); |
| 654 | | if (!updated) assert(ni.pendingDelete(mf)); |
| 655 | | } |
| 656 | | |
| 657 | | pub fn pendingDelete(ni: Node.Index, mf: *MappedFile) bool { |
| 658 | | const node = ni.get(mf); |
| 659 | | if (node.next != ni.toOptional()) return false; |
| 660 | | node.next = mf.free_ni; |
| 669 | try mf.removeNodesFromChildList(gpa, ni, ni); |
| 670 | node.* = .{ |
| 671 | .parent = undefined, |
| 672 | .prev = undefined, |
| 673 | .next = mf.free_ni, |
| 674 | .first = undefined, |
| 675 | .last = undefined, |
| 676 | .flags = .{ |
| 677 | .position = .floating, |
| 678 | .alignment = .@"1", |
| 679 | .bubbles_moved = false, |
| 680 | .enable_next_moved = false, |
| 681 | .location_tag = .small, |
| 682 | .moved = false, |
| 683 | .resized = false, |
| 684 | .next_moved = false, |
| 685 | .has_content = false, |
| 686 | // It is important that we preserve this flag! When the node gets reused, this |
| 687 | // prevents it being redundantly added to `mf.updates` if it is already there. |
| 688 | .updated = node.flags.updated, |
| 689 | }, |
| 690 | .location_payload = undefined, |
| 691 | }; |
| 661 | 692 | mf.free_ni = ni.toOptional(); |
| 662 | | return true; |
| 663 | 693 | } |
| 664 | 694 | |
| 665 | 695 | /// Ensures that the size of `ni` is at least `min_size`. Valid for any node. |
| ... | ... | @@ -946,6 +976,7 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct { |
| 946 | 976 | .resized = false, |
| 947 | 977 | .next_moved = false, |
| 948 | 978 | .has_content = false, |
| 979 | .updated = false, |
| 949 | 980 | }, |
| 950 | 981 | .location_payload = switch (location) { |
| 951 | 982 | .small => |small| .{ .small = small }, |