| ... | ... | @@ -541,10 +541,14 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 541 | 541 | fn searchBucket( |
| 542 | 542 | buckets: *Buckets, |
| 543 | 543 | addr: usize, |
| 544 | current_bucket: ?*BucketHeader, |
| 544 | 545 | ) ?*BucketHeader { |
| 545 | | const search_page = mem.alignBackward(usize, addr, page_size); |
| 546 | const search_page: [*]align(page_size) u8 = @ptrFromInt(mem.alignBackward(usize, addr, page_size)); |
| 547 | if (current_bucket != null and current_bucket.?.page == search_page) { |
| 548 | return current_bucket; |
| 549 | } |
| 546 | 550 | var search_header: BucketHeader = undefined; |
| 547 | | search_header.page = @ptrFromInt(search_page); |
| 551 | search_header.page = search_page; |
| 548 | 552 | const entry = buckets.getEntryFor(&search_header); |
| 549 | 553 | return if (entry.node) |node| node.key else null; |
| 550 | 554 | } |
| ... | ... | @@ -712,7 +716,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 712 | 716 | var bucket_index = math.log2(size_class_hint); |
| 713 | 717 | var size_class: usize = size_class_hint; |
| 714 | 718 | const bucket = while (bucket_index < small_bucket_count) : (bucket_index += 1) { |
| 715 | | if (searchBucket(&self.buckets[bucket_index], @intFromPtr(old_mem.ptr))) |bucket| { |
| 719 | if (searchBucket(&self.buckets[bucket_index], @intFromPtr(old_mem.ptr), self.cur_buckets[bucket_index])) |bucket| { |
| 716 | 720 | break bucket; |
| 717 | 721 | } |
| 718 | 722 | size_class *= 2; |
| ... | ... | @@ -720,7 +724,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 720 | 724 | if (config.retain_metadata) { |
| 721 | 725 | if (!self.large_allocations.contains(@intFromPtr(old_mem.ptr))) { |
| 722 | 726 | // object not in active buckets or a large allocation, so search empty buckets |
| 723 | | if (searchBucket(&self.empty_buckets, @intFromPtr(old_mem.ptr))) |bucket| { |
| 727 | if (searchBucket(&self.empty_buckets, @intFromPtr(old_mem.ptr), null)) |bucket| { |
| 724 | 728 | // bucket is empty so is_used below will always be false and we exit there |
| 725 | 729 | break :blk bucket; |
| 726 | 730 | } else { |
| ... | ... | @@ -830,7 +834,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 830 | 834 | var bucket_index = math.log2(size_class_hint); |
| 831 | 835 | var size_class: usize = size_class_hint; |
| 832 | 836 | const bucket = while (bucket_index < small_bucket_count) : (bucket_index += 1) { |
| 833 | | if (searchBucket(&self.buckets[bucket_index], @intFromPtr(old_mem.ptr))) |bucket| { |
| 837 | if (searchBucket(&self.buckets[bucket_index], @intFromPtr(old_mem.ptr), self.cur_buckets[bucket_index])) |bucket| { |
| 834 | 838 | break bucket; |
| 835 | 839 | } |
| 836 | 840 | size_class *= 2; |
| ... | ... | @@ -838,7 +842,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 838 | 842 | if (config.retain_metadata) { |
| 839 | 843 | if (!self.large_allocations.contains(@intFromPtr(old_mem.ptr))) { |
| 840 | 844 | // object not in active buckets or a large allocation, so search empty buckets |
| 841 | | if (searchBucket(&self.empty_buckets, @intFromPtr(old_mem.ptr))) |bucket| { |
| 845 | if (searchBucket(&self.empty_buckets, @intFromPtr(old_mem.ptr), null)) |bucket| { |
| 842 | 846 | // bucket is empty so is_used below will always be false and we exit there |
| 843 | 847 | break :blk bucket; |
| 844 | 848 | } else { |
| ... | ... | @@ -1387,10 +1391,10 @@ test "double frees" { |
| 1387 | 1391 | const index: usize = 6; |
| 1388 | 1392 | const size_class: usize = @as(usize, 1) << 6; |
| 1389 | 1393 | const small = try allocator.alloc(u8, size_class); |
| 1390 | | try std.testing.expect(GPA.searchBucket(&gpa.buckets[index], @intFromPtr(small.ptr)) != null); |
| 1394 | try std.testing.expect(GPA.searchBucket(&gpa.buckets[index], @intFromPtr(small.ptr), gpa.cur_buckets[index]) != null); |
| 1391 | 1395 | allocator.free(small); |
| 1392 | | try std.testing.expect(GPA.searchBucket(&gpa.buckets[index], @intFromPtr(small.ptr)) == null); |
| 1393 | | try std.testing.expect(GPA.searchBucket(&gpa.empty_buckets, @intFromPtr(small.ptr)) != null); |
| 1396 | try std.testing.expect(GPA.searchBucket(&gpa.buckets[index], @intFromPtr(small.ptr), gpa.cur_buckets[index]) == null); |
| 1397 | try std.testing.expect(GPA.searchBucket(&gpa.empty_buckets, @intFromPtr(small.ptr), null) != null); |
| 1394 | 1398 | |
| 1395 | 1399 | // detect a large allocation double free |
| 1396 | 1400 | const large = try allocator.alloc(u8, 2 * page_size); |
| ... | ... | @@ -1408,7 +1412,7 @@ test "double frees" { |
| 1408 | 1412 | // check that flushing retained metadata doesn't disturb live allocations |
| 1409 | 1413 | gpa.flushRetainedMetadata(); |
| 1410 | 1414 | try std.testing.expect(gpa.empty_buckets.root == null); |
| 1411 | | try std.testing.expect(GPA.searchBucket(&gpa.buckets[index], @intFromPtr(normal_small.ptr)) != null); |
| 1415 | try std.testing.expect(GPA.searchBucket(&gpa.buckets[index], @intFromPtr(normal_small.ptr), gpa.cur_buckets[index]) != null); |
| 1412 | 1416 | try std.testing.expect(gpa.large_allocations.contains(@intFromPtr(normal_large.ptr))); |
| 1413 | 1417 | try std.testing.expect(!gpa.large_allocations.contains(@intFromPtr(large.ptr))); |
| 1414 | 1418 | } |