| ... | @@ -108,21 +108,18 @@ pub const ArenaAllocator = struct { | ... | @@ -108,21 +108,18 @@ pub const ArenaAllocator = struct { |
| 108 | // Thus, only the first hand full of calls to reset() will actually need to iterate the linked | 108 | // Thus, only the first hand full of calls to reset() will actually need to iterate the linked |
| 109 | // list, all future calls are just taking the first node, and only resetting the `end_index` | 109 | // list, all future calls are just taking the first node, and only resetting the `end_index` |
| 110 | // value. | 110 | // value. |
| 111 | const current_capacity = if (mode != .free_all) | 111 | const requested_capacity = switch (mode) { |
| 112 | @sizeOf(BufNode) + self.queryCapacity() // we need at least space for exactly one node + the current capacity | 112 | .retain_capacity => self.queryCapacity(), |
| 113 | else | 113 | .retain_with_limit => |limit| std.math.min(limit, self.queryCapacity()), |
| 114 | 0; | 114 | .free_all => 0, |
| 115 | if (mode == .free_all or current_capacity == 0) { | 115 | }; |
| | 116 | if (requested_capacity == 0) { |
| 116 | // just reset when we don't have anything to reallocate | 117 | // just reset when we don't have anything to reallocate |
| 117 | self.deinit(); | 118 | self.deinit(); |
| 118 | self.state = State{}; | 119 | self.state = State{}; |
| 119 | return true; | 120 | return true; |
| 120 | } | 121 | } |
| 121 | const total_size = switch (mode) { | 122 | const total_size = requested_capacity + @sizeOf(BufNode); |
| 122 | .retain_capacity => current_capacity, | | |
| 123 | .retain_with_limit => |limit| std.math.min(limit, current_capacity), | | |
| 124 | .free_all => unreachable, | | |
| 125 | }; | | |
| 126 | const align_bits = std.math.log2_int(usize, @alignOf(BufNode)); | 123 | const align_bits = std.math.log2_int(usize, @alignOf(BufNode)); |
| 127 | // Free all nodes except for the last one | 124 | // Free all nodes except for the last one |
| 128 | var it = self.state.buffer_list.first; | 125 | var it = self.state.buffer_list.first; |
| ... | @@ -139,6 +136,7 @@ pub const ArenaAllocator = struct { | ... | @@ -139,6 +136,7 @@ pub const ArenaAllocator = struct { |
| 139 | // reset the state before we try resizing the buffers, so we definitely have reset the arena to 0. | 136 | // reset the state before we try resizing the buffers, so we definitely have reset the arena to 0. |
| 140 | self.state.end_index = 0; | 137 | self.state.end_index = 0; |
| 141 | if (maybe_first_node) |first_node| { | 138 | if (maybe_first_node) |first_node| { |
| | 139 | self.state.buffer_list.first = first_node; |
| 142 | // perfect, no need to invoke the child_allocator | 140 | // perfect, no need to invoke the child_allocator |
| 143 | if (first_node.data == total_size) | 141 | if (first_node.data == total_size) |
| 144 | return true; | 142 | return true; |
| ... | @@ -270,3 +268,19 @@ test "ArenaAllocator (reset with preheating)" { | ... | @@ -270,3 +268,19 @@ test "ArenaAllocator (reset with preheating)" { |
| 270 | } | 268 | } |
| 271 | } | 269 | } |
| 272 | } | 270 | } |
| | 271 | |
| | 272 | test "ArenaAllocator (reset while retaining a buffer)" { |
| | 273 | var arena_allocator = ArenaAllocator.init(std.testing.allocator); |
| | 274 | defer arena_allocator.deinit(); |
| | 275 | const a = arena_allocator.allocator(); |
| | 276 | |
| | 277 | // Create two internal buffers |
| | 278 | _ = try a.alloc(u8, 1); |
| | 279 | _ = try a.alloc(u8, 1000); |
| | 280 | |
| | 281 | // Check that we have at least two buffers |
| | 282 | try std.testing.expect(arena_allocator.state.buffer_list.first.?.next != null); |
| | 283 | |
| | 284 | // This retains the first allocated buffer |
| | 285 | try std.testing.expect(arena_allocator.reset(.{ .retain_with_limit = 1 })); |
| | 286 | } |