| ... | ... | @@ -108,21 +108,18 @@ pub const ArenaAllocator = struct { |
| 108 | 108 | // Thus, only the first hand full of calls to reset() will actually need to iterate the linked |
| 109 | 109 | // list, all future calls are just taking the first node, and only resetting the `end_index` |
| 110 | 110 | // value. |
| 111 | | const current_capacity = if (mode != .free_all) |
| 112 | | @sizeOf(BufNode) + self.queryCapacity() // we need at least space for exactly one node + the current capacity |
| 113 | | else |
| 114 | | 0; |
| 115 | | if (mode == .free_all or current_capacity == 0) { |
| 111 | const requested_capacity = switch (mode) { |
| 112 | .retain_capacity => self.queryCapacity(), |
| 113 | .retain_with_limit => |limit| std.math.min(limit, self.queryCapacity()), |
| 114 | .free_all => 0, |
| 115 | }; |
| 116 | if (requested_capacity == 0) { |
| 116 | 117 | // just reset when we don't have anything to reallocate |
| 117 | 118 | self.deinit(); |
| 118 | 119 | self.state = State{}; |
| 119 | 120 | return true; |
| 120 | 121 | } |
| 121 | | const total_size = switch (mode) { |
| 122 | | .retain_capacity => current_capacity, |
| 123 | | .retain_with_limit => |limit| std.math.min(limit, current_capacity), |
| 124 | | .free_all => unreachable, |
| 125 | | }; |
| 122 | const total_size = requested_capacity + @sizeOf(BufNode); |
| 126 | 123 | const align_bits = std.math.log2_int(usize, @alignOf(BufNode)); |
| 127 | 124 | // Free all nodes except for the last one |
| 128 | 125 | var it = self.state.buffer_list.first; |
| ... | ... | @@ -139,6 +136,7 @@ pub const ArenaAllocator = struct { |
| 139 | 136 | // reset the state before we try resizing the buffers, so we definitely have reset the arena to 0. |
| 140 | 137 | self.state.end_index = 0; |
| 141 | 138 | if (maybe_first_node) |first_node| { |
| 139 | self.state.buffer_list.first = first_node; |
| 142 | 140 | // perfect, no need to invoke the child_allocator |
| 143 | 141 | if (first_node.data == total_size) |
| 144 | 142 | return true; |
| ... | ... | @@ -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 | } |