| ... | ... | @@ -305,12 +305,20 @@ pub const Node = extern struct { |
| 305 | 305 | } |
| 306 | 306 | } |
| 307 | 307 | |
| 308 | | pub fn realign(ni: Node.Index, mf: *MappedFile, new_alignment: std.mem.Alignment) void { |
| 309 | | ni.get(mf).flags.alignment = new_alignment; |
| 310 | | |
| 311 | | const old_offset, const old_size = ni.location(mf).resolve(mf); |
| 312 | | if (!new_alignment.check(@intCast(old_offset)) or !new_alignment.check(@intCast(old_size))) { |
| 313 | | @panic("TODO MappedFile.realign"); |
| 308 | /// Moves and expands a node such that its offset and size are aligned to `new_alignment`. |
| 309 | /// |
| 310 | /// Asserts that `ni` is not `Node.Index.root`. |
| 311 | pub fn realign( |
| 312 | ni: Node.Index, |
| 313 | mf: *MappedFile, |
| 314 | gpa: std.mem.Allocator, |
| 315 | new_alignment: std.mem.Alignment, |
| 316 | ) !void { |
| 317 | try mf.realignNode(gpa, ni, new_alignment); |
| 318 | var writers_it = mf.writers.first; |
| 319 | while (writers_it) |writer_node| : (writers_it = writer_node.next) { |
| 320 | const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node); |
| 321 | w.interface.buffer = w.ni.slice(mf); |
| 314 | 322 | } |
| 315 | 323 | } |
| 316 | 324 | |
| ... | ... | @@ -852,6 +860,82 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested |
| 852 | 860 | } |
| 853 | 861 | } |
| 854 | 862 | |
| 863 | fn realignNode( |
| 864 | mf: *MappedFile, |
| 865 | gpa: std.mem.Allocator, |
| 866 | ni: Node.Index, |
| 867 | new_alignment: std.mem.Alignment, |
| 868 | ) !void { |
| 869 | assert(ni != Node.Index.root); // currently unsupported |
| 870 | |
| 871 | const node = ni.get(mf); |
| 872 | const old_offset, const size = node.location().resolve(mf); |
| 873 | |
| 874 | assert(new_alignment.compare(.gt, node.flags.alignment)); |
| 875 | |
| 876 | defer if (std.debug.runtime_safety) mf.verify(); |
| 877 | |
| 878 | node.flags.alignment = new_alignment; |
| 879 | |
| 880 | const new_size = node.flags.alignment.forward(@intCast(size)); |
| 881 | if (new_alignment.check(@intCast(old_offset))) { |
| 882 | if (new_size > size) try mf.resizeNode(gpa, ni, new_size); |
| 883 | return; |
| 884 | } |
| 885 | |
| 886 | _, const parent_size = node.parent.location(mf).resolve(mf); |
| 887 | const trailing_end = trailing_end: switch (node.next) { |
| 888 | .none => parent_size, |
| 889 | else => |next_ni| { |
| 890 | const next_offset, _ = next_ni.location(mf).resolve(mf); |
| 891 | break :trailing_end next_offset; |
| 892 | }, |
| 893 | }; |
| 894 | |
| 895 | const forward_offset = new_alignment.forward(@intCast(old_offset)); |
| 896 | if (forward_offset + new_size <= trailing_end) { |
| 897 | // Shift into the free space if possible |
| 898 | try mf.ensureCapacityForSetLocation(gpa); |
| 899 | if (node.flags.has_content) { |
| 900 | const old_file_offset = ni.fileLocation(mf, false).offset; |
| 901 | const new_file_offset = (old_file_offset - old_offset) + forward_offset; |
| 902 | if (new_file_offset < old_file_offset + size) { |
| 903 | @memmove( |
| 904 | mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)], |
| 905 | mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], |
| 906 | ); |
| 907 | } else try mf.moveRange(old_file_offset, new_file_offset, size); |
| 908 | @memset(mf.memory_map.memory[@intCast(new_file_offset + size)..][0..@intCast(new_size - size)], 0); |
| 909 | } |
| 910 | |
| 911 | ni.setLocationAssumeCapacity(mf, forward_offset, new_size); |
| 912 | } else { |
| 913 | const temp_size = node.flags.alignment.forward(@intCast(new_size + 1)); |
| 914 | try mf.resizeNode(gpa, ni, temp_size); |
| 915 | const new_offset, _ = ni.location(mf).resolve(mf); |
| 916 | |
| 917 | try mf.ensureCapacityForSetLocation(gpa); |
| 918 | |
| 919 | // Non-fixed nodes may now be aligned if the resize moved them |
| 920 | const new_forward_offset = new_alignment.forward(@intCast(new_offset)); |
| 921 | const final_offset = if (new_forward_offset != new_offset) final_offset: { |
| 922 | if (node.flags.has_content) { |
| 923 | const old_file_offset = ni.fileLocation(mf, false).offset; |
| 924 | const new_file_offset = (old_file_offset - new_offset) + new_forward_offset; |
| 925 | @memmove( |
| 926 | mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)], |
| 927 | mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], |
| 928 | ); |
| 929 | @memset(mf.memory_map.memory[@intCast(old_file_offset)..@intCast(new_file_offset)], 0); |
| 930 | } |
| 931 | |
| 932 | break :final_offset new_forward_offset; |
| 933 | } else new_offset; |
| 934 | |
| 935 | ni.setLocationAssumeCapacity(mf, final_offset, new_size); |
| 936 | } |
| 937 | } |
| 938 | |
| 855 | 939 | fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void { |
| 856 | 940 | // make a copy of this node at the new location |
| 857 | 941 | try mf.copyRange(old_file_offset, new_file_offset, size); |