| ... | @@ -375,6 +375,25 @@ pub const Node = extern struct { | ... | @@ -375,6 +375,25 @@ pub const Node = extern struct { |
| 375 | } | 375 | } |
| 376 | } | 376 | } |
| 377 | | 377 | |
| | 378 | /// Shrink a node to `size`, exactly. |
| | 379 | /// If the new size can't contain all the children, returns error.ShrinkImpossible. |
| | 380 | /// If `shift_next` is set, then the following node is shifted backwards into |
| | 381 | /// the free space as much as alignment allows. |
| | 382 | pub fn shrink( |
| | 383 | ni: Node.Index, |
| | 384 | mf: *MappedFile, |
| | 385 | gpa: std.mem.Allocator, |
| | 386 | size: u64, |
| | 387 | shift_next: bool, |
| | 388 | ) !void { |
| | 389 | try mf.shrinkNode(gpa, ni, size, shift_next); |
| | 390 | var writers_it = mf.writers.first; |
| | 391 | while (writers_it) |writer_node| : (writers_it = writer_node.next) { |
| | 392 | const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node); |
| | 393 | w.interface.buffer = w.ni.slice(mf); |
| | 394 | } |
| | 395 | } |
| | 396 | |
| 378 | pub fn writer(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, w: *Writer) void { | 397 | pub fn writer(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, w: *Writer) void { |
| 379 | w.* = .{ | 398 | w.* = .{ |
| 380 | .gpa = gpa, | 399 | .gpa = gpa, |
| ... | @@ -582,7 +601,9 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct { | ... | @@ -582,7 +601,9 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct { |
| 582 | free_node.flags.resized = false; | 601 | free_node.flags.resized = false; |
| 583 | } | 602 | } |
| 584 | _, const parent_size = opts.parent.location(mf).resolve(mf); | 603 | _, const parent_size = opts.parent.location(mf).resolve(mf); |
| 585 | if (offset > parent_size) try opts.parent.resize(mf, gpa, offset); | 604 | const required_parent_size = offset + opts.add_node.size; |
| | 605 | if (required_parent_size > parent_size) |
| | 606 | try opts.parent.resize(mf, gpa, required_parent_size); |
| 586 | try free_ni.resize(mf, gpa, opts.add_node.size); | 607 | try free_ni.resize(mf, gpa, opts.add_node.size); |
| 587 | } | 608 | } |
| 588 | if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf); | 609 | if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf); |
| ... | @@ -670,11 +691,55 @@ pub fn addNodeAfter( | ... | @@ -670,11 +691,55 @@ pub fn addNodeAfter( |
| 670 | }); | 691 | }); |
| 671 | } | 692 | } |
| 672 | | 693 | |
| | 694 | fn shrinkNode( |
| | 695 | mf: *MappedFile, |
| | 696 | gpa: std.mem.Allocator, |
| | 697 | ni: Node.Index, |
| | 698 | size: u64, |
| | 699 | shrink_next: bool, |
| | 700 | ) !void { |
| | 701 | const node = ni.get(mf); |
| | 702 | const old_offset, _ = node.location().resolve(mf); |
| | 703 | |
| | 704 | // This would require unmapping first |
| | 705 | if (ni == Node.Index.root) return error.Unimplemented; |
| | 706 | |
| | 707 | if (node.last != .none) { |
| | 708 | const last = node.last.get(mf); |
| | 709 | const last_offset, const last_size = last.location().resolve(mf); |
| | 710 | if (last_offset + last_size > size) return error.ShrinkImpossible; |
| | 711 | } |
| | 712 | |
| | 713 | try mf.large.ensureUnusedCapacity(gpa, 4); |
| | 714 | try mf.updates.ensureUnusedCapacity(gpa, 2); |
| | 715 | |
| | 716 | ni.setLocationAssumeCapacity(mf, old_offset, size); |
| | 717 | if (!shrink_next or node.next == .none) return; |
| | 718 | |
| | 719 | const next = node.next.get(mf); |
| | 720 | const old_next_offset, const next_size = next.location().resolve(mf); |
| | 721 | const padding = old_next_offset - (old_offset + size); |
| | 722 | const new_next_offset = next.flags.alignment.forward(@intCast(old_next_offset - padding)); |
| | 723 | |
| | 724 | if (next.flags.has_content and new_next_offset < old_next_offset) { |
| | 725 | const old_file_offset = node.next.fileLocation(mf, false).offset; |
| | 726 | const new_file_offset = (old_file_offset - old_next_offset) + new_next_offset; |
| | 727 | @memmove( |
| | 728 | mf.memory_map.memory[new_file_offset..][0..next_size], |
| | 729 | mf.memory_map.memory[old_file_offset..][0..next_size], |
| | 730 | ); |
| | 731 | } |
| | 732 | |
| | 733 | node.next.setLocationAssumeCapacity(mf, new_next_offset, next_size); |
| | 734 | } |
| | 735 | |
| 673 | fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) (Allocator.Error || Io.Cancelable || IoError)!void { | 736 | fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested_size: u64) (Allocator.Error || Io.Cancelable || IoError)!void { |
| 674 | const io = mf.io; | 737 | const io = mf.io; |
| 675 | const node = ni.get(mf); | 738 | const node = ni.get(mf); |
| 676 | const old_offset, const old_size = node.location().resolve(mf); | 739 | const old_offset, const old_size = node.location().resolve(mf); |
| 677 | const new_size = node.flags.alignment.forward(@intCast(requested_size)); | 740 | const new_size = node.flags.alignment.forward(@intCast(requested_size)); |
| | 741 | if (new_size <= old_size) return; |
| | 742 | |
| 678 | // Resize the entire file | 743 | // Resize the entire file |
| 679 | if (ni == Node.Index.root) { | 744 | if (ni == Node.Index.root) { |
| 680 | try mf.ensureCapacityForSetLocation(gpa); | 745 | try mf.ensureCapacityForSetLocation(gpa); |