| ... | ... | @@ -78,7 +78,7 @@ fn countListCapacity(first_node: ?*Node) usize { |
| 78 | 78 | while (it) |node| : (it = node.next) { |
| 79 | 79 | // Compute the actually allocated size excluding the |
| 80 | 80 | // linked list node. |
| 81 | | capacity += node.size - @sizeOf(Node); |
| 81 | capacity += node.size.toInt() - @sizeOf(Node); |
| 82 | 82 | } |
| 83 | 83 | return capacity; |
| 84 | 84 | } |
| ... | ... | @@ -164,7 +164,10 @@ pub fn reset(arena: *ArenaAllocator, mode: ResetMode) bool { |
| 164 | 164 | }; |
| 165 | 165 | const allocated_slice = node.allocatedSliceUnsafe(); |
| 166 | 166 | |
| 167 | | if (new_capacity == 0) { |
| 167 | // Align backwards to always stay below limit. |
| 168 | const new_size = mem.alignBackward(usize, @sizeOf(Node) + new_capacity, 2); |
| 169 | |
| 170 | if (new_size == @sizeOf(Node)) { |
| 168 | 171 | arena.child_allocator.rawFree(allocated_slice, .of(Node), @returnAddress()); |
| 169 | 172 | first_node_ptr.* = null; |
| 170 | 173 | continue; |
| ... | ... | @@ -173,19 +176,17 @@ pub fn reset(arena: *ArenaAllocator, mode: ResetMode) bool { |
| 173 | 176 | node.end_index = 0; |
| 174 | 177 | first_node_ptr.* = node; |
| 175 | 178 | |
| 176 | | const adjusted_capacity: usize = mem.alignForward(usize, new_capacity, 2); |
| 177 | | |
| 178 | | if (allocated_slice.len - @sizeOf(Node) == adjusted_capacity) { |
| 179 | if (allocated_slice.len == new_size) { |
| 179 | 180 | // perfect, no need to invoke the child_allocator |
| 180 | 181 | continue; |
| 181 | 182 | } |
| 182 | 183 | |
| 183 | | if (arena.child_allocator.rawResize(allocated_slice, .of(Node), adjusted_capacity, @returnAddress())) { |
| 184 | if (arena.child_allocator.rawResize(allocated_slice, .of(Node), new_size, @returnAddress())) { |
| 184 | 185 | // successful resize |
| 185 | | node.size = adjusted_capacity; |
| 186 | node.size = .fromInt(new_size); |
| 186 | 187 | } else { |
| 187 | 188 | // manual realloc |
| 188 | | const new_ptr = arena.child_allocator.rawAlloc(adjusted_capacity, .of(Node), @returnAddress()) orelse { |
| 189 | const new_ptr = arena.child_allocator.rawAlloc(new_size, .of(Node), @returnAddress()) orelse { |
| 189 | 190 | // we failed to preheat the arena properly, signal this to the user. |
| 190 | 191 | ok = false; |
| 191 | 192 | continue; |
| ... | ... | @@ -193,7 +194,7 @@ pub fn reset(arena: *ArenaAllocator, mode: ResetMode) bool { |
| 193 | 194 | arena.child_allocator.rawFree(allocated_slice, .of(Node), @returnAddress()); |
| 194 | 195 | const new_first_node: *Node = @ptrCast(@alignCast(new_ptr)); |
| 195 | 196 | new_first_node.* = .{ |
| 196 | | .size = adjusted_capacity, |
| 197 | .size = .fromInt(new_size), |
| 197 | 198 | .end_index = 0, |
| 198 | 199 | .next = null, |
| 199 | 200 | }; |
| ... | ... | @@ -224,30 +225,49 @@ const Node = struct { |
| 224 | 225 | /// accessing it. |
| 225 | 226 | next: ?*Node, |
| 226 | 227 | |
| 227 | | const resize_bit: usize = 1; |
| 228 | const Size = packed struct(usize) { |
| 229 | resizing: bool, |
| 230 | _: @Int(.unsigned, @bitSizeOf(usize) - 1) = 0, |
| 231 | |
| 232 | fn fromInt(int: usize) Size { |
| 233 | assert(int >= @sizeOf(Node)); |
| 234 | const size: Size = @bitCast(int); |
| 235 | assert(!size.resizing); |
| 236 | return size; |
| 237 | } |
| 238 | |
| 239 | fn toInt(size: Size) usize { |
| 240 | var int = size; |
| 241 | int.resizing = false; |
| 242 | return @bitCast(int); |
| 243 | } |
| 244 | |
| 245 | comptime { |
| 246 | assert(Size{ .resizing = true } == @as(Size, @bitCast(@as(usize, 1)))); |
| 247 | } |
| 248 | }; |
| 228 | 249 | |
| 229 | 250 | fn loadBuf(node: *Node) []u8 { |
| 230 | 251 | // monotonic is fine since `size` can only ever grow, so the buffer returned |
| 231 | 252 | // by this function is always valid memory. |
| 232 | | const size = @atomicLoad(usize, &node.size, .monotonic); |
| 233 | | return @as([*]u8, @ptrCast(node))[0 .. size & ~resize_bit][@sizeOf(Node)..]; |
| 253 | const size = @atomicLoad(Size, &node.size, .monotonic); |
| 254 | return @as([*]u8, @ptrCast(node))[0..size.toInt()][@sizeOf(Node)..]; |
| 234 | 255 | } |
| 235 | 256 | |
| 236 | 257 | /// Returns allocated slice or `null` if node is already (being) resized. |
| 237 | 258 | fn beginResize(node: *Node) ?[]u8 { |
| 238 | | const size = @atomicRmw(usize, &node.size, .Or, resize_bit, .acquire); // syncs with release in `endResize` |
| 239 | | if (size & resize_bit != 0) return null; |
| 240 | | return @as([*]u8, @ptrCast(node))[0..size]; |
| 259 | const size = @atomicRmw(Size, &node.size, .Or, .{ .resizing = true }, .acquire); // syncs with release in `endResize` |
| 260 | if (size.resizing) return null; |
| 261 | return @as([*]u8, @ptrCast(node))[0..size.toInt()]; |
| 241 | 262 | } |
| 242 | 263 | |
| 243 | 264 | fn endResize(node: *Node, size: usize) void { |
| 244 | | assert(size & resize_bit == 0); |
| 245 | | return @atomicStore(usize, &node.size, size, .release); // syncs with acquire in `beginResize` |
| 265 | return @atomicStore(Size, &node.size, .fromInt(size), .release); // syncs with acquire in `beginResize` |
| 246 | 266 | } |
| 247 | 267 | |
| 248 | 268 | /// Not threadsafe. |
| 249 | 269 | fn allocatedSliceUnsafe(node: *Node) []u8 { |
| 250 | | return @as([*]u8, @ptrCast(node))[0 .. node.size & ~resize_bit]; |
| 270 | return @as([*]u8, @ptrCast(node))[0..node.size.toInt()]; |
| 251 | 271 | } |
| 252 | 272 | }; |
| 253 | 273 | |
| ... | ... | @@ -408,7 +428,7 @@ fn alloc(ctx: *anyopaque, n: usize, alignment: Alignment, ret_addr: usize) ?[*]u |
| 408 | 428 | it = node.next; |
| 409 | 429 | }) { |
| 410 | 430 | last_free = node; |
| 411 | | assert(node.size & Node.resize_bit == 0); |
| 431 | assert(!node.size.resizing); |
| 412 | 432 | const buf = node.allocatedSliceUnsafe()[@sizeOf(Node)..]; |
| 413 | 433 | const aligned_index = alignedIndex(buf.ptr, 0, alignment); |
| 414 | 434 | |
| ... | ... | @@ -433,7 +453,7 @@ fn alloc(ctx: *anyopaque, n: usize, alignment: Alignment, ret_addr: usize) ?[*]u |
| 433 | 453 | const new_size = mem.alignForward(usize, @sizeOf(Node) + aligned_index + n, 2); |
| 434 | 454 | |
| 435 | 455 | if (arena.child_allocator.rawResize(allocated_slice, .of(Node), new_size, @returnAddress())) { |
| 436 | | node.size = new_size; |
| 456 | node.size = .fromInt(new_size); |
| 437 | 457 | break :candidate .{ node, best_fit_prev }; |
| 438 | 458 | } |
| 439 | 459 | } |
| ... | ... | @@ -486,12 +506,11 @@ fn alloc(ctx: *anyopaque, n: usize, alignment: Alignment, ret_addr: usize) ?[*]u |
| 486 | 506 | const big_enough_size = prev_size + min_size + 16; |
| 487 | 507 | break :size mem.alignForward(usize, big_enough_size + big_enough_size / 2, 2); |
| 488 | 508 | }; |
| 489 | | assert(size & Node.resize_bit == 0); |
| 490 | 509 | const ptr = arena.child_allocator.rawAlloc(size, .of(Node), @returnAddress()) orelse |
| 491 | 510 | return null; |
| 492 | 511 | const new_node: *Node = @ptrCast(@alignCast(ptr)); |
| 493 | 512 | new_node.* = .{ |
| 494 | | .size = size, |
| 513 | .size = .fromInt(size), |
| 495 | 514 | .end_index = undefined, // set below |
| 496 | 515 | .next = undefined, // set below |
| 497 | 516 | }; |
| ... | ... | @@ -501,7 +520,7 @@ fn alloc(ctx: *anyopaque, n: usize, alignment: Alignment, ret_addr: usize) ?[*]u |
| 501 | 520 | |
| 502 | 521 | const buf = new_node.allocatedSliceUnsafe()[@sizeOf(Node)..]; |
| 503 | 522 | const aligned_index = alignedIndex(buf.ptr, 0, alignment); |
| 504 | | assert(new_node.size >= @sizeOf(Node) + aligned_index + n); |
| 523 | assert(new_node.size.toInt() >= @sizeOf(Node) + aligned_index + n); |
| 505 | 524 | |
| 506 | 525 | new_node.end_index = aligned_index + n; |
| 507 | 526 | new_node.next = first_node; |
| ... | ... | @@ -649,6 +668,7 @@ test "reset while retaining a buffer" { |
| 649 | 668 | try std.testing.expect(arena_allocator.state.used_list.?.next != null); |
| 650 | 669 | |
| 651 | 670 | // This retains the first allocated buffer |
| 652 | | try std.testing.expect(arena_allocator.reset(.{ .retain_with_limit = 1 })); |
| 671 | try std.testing.expect(arena_allocator.reset(.{ .retain_with_limit = 2 })); |
| 653 | 672 | try std.testing.expect(arena_allocator.state.used_list.?.next == null); |
| 673 | try std.testing.expectEqual(2, arena_allocator.queryCapacity()); |
| 654 | 674 | } |