| ... | @@ -95,6 +95,7 @@ | ... | @@ -95,6 +95,7 @@ |
| 95 | const std = @import("std"); | 95 | const std = @import("std"); |
| 96 | const math = std.math; | 96 | const math = std.math; |
| 97 | const assert = std.debug.assert; | 97 | const assert = std.debug.assert; |
| | 98 | const mem = std.mem; |
| 98 | const Allocator = std.mem.Allocator; | 99 | const Allocator = std.mem.Allocator; |
| 99 | const page_size = std.mem.page_size; | 100 | const page_size = std.mem.page_size; |
| 100 | const StackTrace = std.builtin.StackTrace; | 101 | const StackTrace = std.builtin.StackTrace; |
| ... | @@ -117,11 +118,15 @@ const sys_can_stack_trace = switch (std.Target.current.cpu.arch) { | ... | @@ -117,11 +118,15 @@ const sys_can_stack_trace = switch (std.Target.current.cpu.arch) { |
| 117 | | 118 | |
| 118 | else => true, | 119 | else => true, |
| 119 | }; | 120 | }; |
| 120 | const default_stack_trace_frames: usize = if (sys_can_stack_trace) 4 else 0; | 121 | const default_sys_stack_trace_frames: usize = if (sys_can_stack_trace) 4 else 0; |
| | 122 | const default_stack_trace_frames: usize = switch (std.builtin.mode) { |
| | 123 | .Debug => default_sys_stack_trace_frames, |
| | 124 | else => 0, |
| | 125 | }; |
| 121 | | 126 | |
| 122 | pub const Config = struct { | 127 | pub const Config = struct { |
| 123 | /// Number of stack frames to capture. | 128 | /// Number of stack frames to capture. |
| 124 | stack_trace_frames: usize = if (std.debug.runtime_safety) default_stack_trace_frames else @as(usize, 0), | 129 | stack_trace_frames: usize = default_stack_trace_frames, |
| 125 | | 130 | |
| 126 | /// If true, the allocator will have two fields: | 131 | /// If true, the allocator will have two fields: |
| 127 | /// * `total_requested_bytes` which tracks the total allocated bytes of memory requested. | 132 | /// * `total_requested_bytes` which tracks the total allocated bytes of memory requested. |
| ... | @@ -163,7 +168,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -163,7 +168,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 163 | const one_trace_size = @sizeOf(usize) * stack_n; | 168 | const one_trace_size = @sizeOf(usize) * stack_n; |
| 164 | const traces_per_slot = 2; | 169 | const traces_per_slot = 2; |
| 165 | | 170 | |
| 166 | pub const Error = std.mem.Allocator.Error; | 171 | pub const Error = mem.Allocator.Error; |
| 167 | | 172 | |
| 168 | const small_bucket_count = math.log2(page_size); | 173 | const small_bucket_count = math.log2(page_size); |
| 169 | const largest_bucket_object_size = 1 << (small_bucket_count - 1); | 174 | const largest_bucket_object_size = 1 << (small_bucket_count - 1); |
| ... | @@ -246,7 +251,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -246,7 +251,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 246 | } | 251 | } |
| 247 | | 252 | |
| 248 | fn bucketStackFramesStart(size_class: usize) usize { | 253 | fn bucketStackFramesStart(size_class: usize) usize { |
| 249 | return std.mem.alignForward( | 254 | return mem.alignForward( |
| 250 | @sizeOf(BucketHeader) + usedBitsCount(size_class), | 255 | @sizeOf(BucketHeader) + usedBitsCount(size_class), |
| 251 | @alignOf(usize), | 256 | @alignOf(usize), |
| 252 | ); | 257 | ); |
| ... | @@ -322,7 +327,8 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -322,7 +327,8 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 322 | } | 327 | } |
| 323 | | 328 | |
| 324 | fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void { | 329 | fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void { |
| 325 | std.mem.set(usize, addresses, 0); | 330 | if (stack_n == 0) return; |
| | 331 | mem.set(usize, addresses, 0); |
| 326 | var stack_trace = StackTrace{ | 332 | var stack_trace = StackTrace{ |
| 327 | .instruction_addresses = addresses, | 333 | .instruction_addresses = addresses, |
| 328 | .index = 0, | 334 | .index = 0, |
| ... | @@ -679,14 +685,14 @@ test "realloc" { | ... | @@ -679,14 +685,14 @@ test "realloc" { |
| 679 | // This reallocation should keep its pointer address. | 685 | // This reallocation should keep its pointer address. |
| 680 | const old_slice = slice; | 686 | const old_slice = slice; |
| 681 | slice = try allocator.realloc(slice, 2); | 687 | slice = try allocator.realloc(slice, 2); |
| 682 | assert(old_slice.ptr == slice.ptr); | 688 | std.testing.expect(old_slice.ptr == slice.ptr); |
| 683 | assert(slice[0] == 0x12); | 689 | std.testing.expect(slice[0] == 0x12); |
| 684 | slice[1] = 0x34; | 690 | slice[1] = 0x34; |
| 685 | | 691 | |
| 686 | // This requires upgrading to a larger size class | 692 | // This requires upgrading to a larger size class |
| 687 | slice = try allocator.realloc(slice, 17); | 693 | slice = try allocator.realloc(slice, 17); |
| 688 | assert(slice[0] == 0x12); | 694 | std.testing.expect(slice[0] == 0x12); |
| 689 | assert(slice[1] == 0x34); | 695 | std.testing.expect(slice[1] == 0x34); |
| 690 | } | 696 | } |
| 691 | | 697 | |
| 692 | test "shrink" { | 698 | test "shrink" { |
| ... | @@ -697,18 +703,18 @@ test "shrink" { | ... | @@ -697,18 +703,18 @@ test "shrink" { |
| 697 | var slice = try allocator.alloc(u8, 20); | 703 | var slice = try allocator.alloc(u8, 20); |
| 698 | defer allocator.free(slice); | 704 | defer allocator.free(slice); |
| 699 | | 705 | |
| 700 | std.mem.set(u8, slice, 0x11); | 706 | mem.set(u8, slice, 0x11); |
| 701 | | 707 | |
| 702 | slice = allocator.shrink(slice, 17); | 708 | slice = allocator.shrink(slice, 17); |
| 703 | | 709 | |
| 704 | for (slice) |b| { | 710 | for (slice) |b| { |
| 705 | assert(b == 0x11); | 711 | std.testing.expect(b == 0x11); |
| 706 | } | 712 | } |
| 707 | | 713 | |
| 708 | slice = allocator.shrink(slice, 16); | 714 | slice = allocator.shrink(slice, 16); |
| 709 | | 715 | |
| 710 | for (slice) |b| { | 716 | for (slice) |b| { |
| 711 | assert(b == 0x11); | 717 | std.testing.expect(b == 0x11); |
| 712 | } | 718 | } |
| 713 | } | 719 | } |
| 714 | | 720 | |
| ... | @@ -722,10 +728,10 @@ test "large object - grow" { | ... | @@ -722,10 +728,10 @@ test "large object - grow" { |
| 722 | | 728 | |
| 723 | var old = slice1; | 729 | var old = slice1; |
| 724 | slice1 = try allocator.realloc(slice1, page_size * 2 - 10); | 730 | slice1 = try allocator.realloc(slice1, page_size * 2 - 10); |
| 725 | assert(slice1.ptr == old.ptr); | 731 | std.testing.expect(slice1.ptr == old.ptr); |
| 726 | | 732 | |
| 727 | slice1 = try allocator.realloc(slice1, page_size * 2); | 733 | slice1 = try allocator.realloc(slice1, page_size * 2); |
| 728 | assert(slice1.ptr == old.ptr); | 734 | std.testing.expect(slice1.ptr == old.ptr); |
| 729 | | 735 | |
| 730 | slice1 = try allocator.realloc(slice1, page_size * 2 + 1); | 736 | slice1 = try allocator.realloc(slice1, page_size * 2 + 1); |
| 731 | } | 737 | } |
| ... | @@ -743,8 +749,8 @@ test "realloc small object to large object" { | ... | @@ -743,8 +749,8 @@ test "realloc small object to large object" { |
| 743 | // This requires upgrading to a large object | 749 | // This requires upgrading to a large object |
| 744 | const large_object_size = page_size * 2 + 50; | 750 | const large_object_size = page_size * 2 + 50; |
| 745 | slice = try allocator.realloc(slice, large_object_size); | 751 | slice = try allocator.realloc(slice, large_object_size); |
| 746 | assert(slice[0] == 0x12); | 752 | std.testing.expect(slice[0] == 0x12); |
| 747 | assert(slice[60] == 0x34); | 753 | std.testing.expect(slice[60] == 0x34); |
| 748 | } | 754 | } |
| 749 | | 755 | |
| 750 | test "shrink large object to large object" { | 756 | test "shrink large object to large object" { |
| ... | @@ -758,16 +764,16 @@ test "shrink large object to large object" { | ... | @@ -758,16 +764,16 @@ test "shrink large object to large object" { |
| 758 | slice[60] = 0x34; | 764 | slice[60] = 0x34; |
| 759 | | 765 | |
| 760 | slice = try allocator.resize(slice, page_size * 2 + 1); | 766 | slice = try allocator.resize(slice, page_size * 2 + 1); |
| 761 | assert(slice[0] == 0x12); | 767 | std.testing.expect(slice[0] == 0x12); |
| 762 | assert(slice[60] == 0x34); | 768 | std.testing.expect(slice[60] == 0x34); |
| 763 | | 769 | |
| 764 | slice = allocator.shrink(slice, page_size * 2 + 1); | 770 | slice = allocator.shrink(slice, page_size * 2 + 1); |
| 765 | assert(slice[0] == 0x12); | 771 | std.testing.expect(slice[0] == 0x12); |
| 766 | assert(slice[60] == 0x34); | 772 | std.testing.expect(slice[60] == 0x34); |
| 767 | | 773 | |
| 768 | slice = try allocator.realloc(slice, page_size * 2); | 774 | slice = try allocator.realloc(slice, page_size * 2); |
| 769 | assert(slice[0] == 0x12); | 775 | std.testing.expect(slice[0] == 0x12); |
| 770 | assert(slice[60] == 0x34); | 776 | std.testing.expect(slice[60] == 0x34); |
| 771 | } | 777 | } |
| 772 | | 778 | |
| 773 | test "shrink large object to large object with larger alignment" { | 779 | test "shrink large object to large object with larger alignment" { |
| ... | @@ -783,7 +789,7 @@ test "shrink large object to large object with larger alignment" { | ... | @@ -783,7 +789,7 @@ test "shrink large object to large object with larger alignment" { |
| 783 | defer allocator.free(slice); | 789 | defer allocator.free(slice); |
| 784 | | 790 | |
| 785 | var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator); | 791 | var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator); |
| 786 | while (isAligned(@ptrToInt(slice.ptr), page_size * 2)) { | 792 | while (mem.isAligned(@ptrToInt(slice.ptr), page_size * 2)) { |
| 787 | try stuff_to_free.append(slice); | 793 | try stuff_to_free.append(slice); |
| 788 | slice = try allocator.alignedAlloc(u8, 16, alloc_size); | 794 | slice = try allocator.alignedAlloc(u8, 16, alloc_size); |
| 789 | } | 795 | } |
| ... | @@ -794,8 +800,8 @@ test "shrink large object to large object with larger alignment" { | ... | @@ -794,8 +800,8 @@ test "shrink large object to large object with larger alignment" { |
| 794 | slice[60] = 0x34; | 800 | slice[60] = 0x34; |
| 795 | | 801 | |
| 796 | slice = try allocator.reallocAdvanced(slice, page_size * 2, alloc_size / 2, .exact); | 802 | slice = try allocator.reallocAdvanced(slice, page_size * 2, alloc_size / 2, .exact); |
| 797 | assert(slice[0] == 0x12); | 803 | std.testing.expect(slice[0] == 0x12); |
| 798 | assert(slice[60] == 0x34); | 804 | std.testing.expect(slice[60] == 0x34); |
| 799 | } | 805 | } |
| 800 | | 806 | |
| 801 | test "realloc large object to small object" { | 807 | test "realloc large object to small object" { |
| ... | @@ -809,8 +815,8 @@ test "realloc large object to small object" { | ... | @@ -809,8 +815,8 @@ test "realloc large object to small object" { |
| 809 | slice[16] = 0x34; | 815 | slice[16] = 0x34; |
| 810 | | 816 | |
| 811 | slice = try allocator.realloc(slice, 19); | 817 | slice = try allocator.realloc(slice, 19); |
| 812 | assert(slice[0] == 0x12); | 818 | std.testing.expect(slice[0] == 0x12); |
| 813 | assert(slice[16] == 0x34); | 819 | std.testing.expect(slice[16] == 0x34); |
| 814 | } | 820 | } |
| 815 | | 821 | |
| 816 | test "non-page-allocator backing allocator" { | 822 | test "non-page-allocator backing allocator" { |
| ... | @@ -834,7 +840,7 @@ test "realloc large object to larger alignment" { | ... | @@ -834,7 +840,7 @@ test "realloc large object to larger alignment" { |
| 834 | defer allocator.free(slice); | 840 | defer allocator.free(slice); |
| 835 | | 841 | |
| 836 | var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator); | 842 | var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator); |
| 837 | while (isAligned(@ptrToInt(slice.ptr), page_size * 2)) { | 843 | while (mem.isAligned(@ptrToInt(slice.ptr), page_size * 2)) { |
| 838 | try stuff_to_free.append(slice); | 844 | try stuff_to_free.append(slice); |
| 839 | slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50); | 845 | slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50); |
| 840 | } | 846 | } |
| ... | @@ -845,40 +851,16 @@ test "realloc large object to larger alignment" { | ... | @@ -845,40 +851,16 @@ test "realloc large object to larger alignment" { |
| 845 | slice[16] = 0x34; | 851 | slice[16] = 0x34; |
| 846 | | 852 | |
| 847 | slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100, .exact); | 853 | slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100, .exact); |
| 848 | assert(slice[0] == 0x12); | 854 | std.testing.expect(slice[0] == 0x12); |
| 849 | assert(slice[16] == 0x34); | 855 | std.testing.expect(slice[16] == 0x34); |
| 850 | | 856 | |
| 851 | slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25, .exact); | 857 | slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25, .exact); |
| 852 | assert(slice[0] == 0x12); | 858 | std.testing.expect(slice[0] == 0x12); |
| 853 | assert(slice[16] == 0x34); | 859 | std.testing.expect(slice[16] == 0x34); |
| 854 | | 860 | |
| 855 | slice = try allocator.reallocAdvanced(slice, page_size * 2, page_size * 2 + 100, .exact); | 861 | slice = try allocator.reallocAdvanced(slice, page_size * 2, page_size * 2 + 100, .exact); |
| 856 | assert(slice[0] == 0x12); | 862 | std.testing.expect(slice[0] == 0x12); |
| 857 | assert(slice[16] == 0x34); | 863 | std.testing.expect(slice[16] == 0x34); |
| 858 | } | | |
| 859 | | | |
| 860 | fn isAligned(addr: usize, alignment: usize) bool { | | |
| 861 | // 000010000 // example addr | | |
| 862 | // 000001111 // subtract 1 | | |
| 863 | // 111110000 // binary not | | |
| 864 | const aligned_addr = (addr & ~(alignment - 1)); | | |
| 865 | return aligned_addr == addr; | | |
| 866 | } | | |
| 867 | | | |
| 868 | test "isAligned works" { | | |
| 869 | assert(isAligned(0, 4)); | | |
| 870 | assert(isAligned(1, 1)); | | |
| 871 | assert(isAligned(2, 1)); | | |
| 872 | assert(isAligned(2, 2)); | | |
| 873 | assert(!isAligned(2, 4)); | | |
| 874 | assert(isAligned(3, 1)); | | |
| 875 | assert(!isAligned(3, 2)); | | |
| 876 | assert(!isAligned(3, 4)); | | |
| 877 | assert(isAligned(4, 4)); | | |
| 878 | assert(isAligned(4, 2)); | | |
| 879 | assert(isAligned(4, 1)); | | |
| 880 | assert(!isAligned(4, 8)); | | |
| 881 | assert(!isAligned(4, 16)); | | |
| 882 | } | 864 | } |
| 883 | | 865 | |
| 884 | test "large object shrinks to small but allocation fails during shrink" { | 866 | test "large object shrinks to small but allocation fails during shrink" { |
| ... | @@ -895,8 +877,8 @@ test "large object shrinks to small but allocation fails during shrink" { | ... | @@ -895,8 +877,8 @@ test "large object shrinks to small but allocation fails during shrink" { |
| 895 | // Next allocation will fail in the backing allocator of the GeneralPurposeAllocator | 877 | // Next allocation will fail in the backing allocator of the GeneralPurposeAllocator |
| 896 | | 878 | |
| 897 | slice = allocator.shrink(slice, 4); | 879 | slice = allocator.shrink(slice, 4); |
| 898 | assert(slice[0] == 0x12); | 880 | std.testing.expect(slice[0] == 0x12); |
| 899 | assert(slice[3] == 0x34); | 881 | std.testing.expect(slice[3] == 0x34); |
| 900 | } | 882 | } |
| 901 | | 883 | |
| 902 | test "objects of size 1024 and 2048" { | 884 | test "objects of size 1024 and 2048" { |
| ... | @@ -919,20 +901,20 @@ test "setting a memory cap" { | ... | @@ -919,20 +901,20 @@ test "setting a memory cap" { |
| 919 | gpda.setRequestedMemoryLimit(1010); | 901 | gpda.setRequestedMemoryLimit(1010); |
| 920 | | 902 | |
| 921 | const small = try allocator.create(i32); | 903 | const small = try allocator.create(i32); |
| 922 | assert(gpda.total_requested_bytes == 4); | 904 | std.testing.expect(gpda.total_requested_bytes == 4); |
| 923 | | 905 | |
| 924 | const big = try allocator.alloc(u8, 1000); | 906 | const big = try allocator.alloc(u8, 1000); |
| 925 | assert(gpda.total_requested_bytes == 1004); | 907 | std.testing.expect(gpda.total_requested_bytes == 1004); |
| 926 | | 908 | |
| 927 | std.testing.expectError(error.OutOfMemory, allocator.create(u64)); | 909 | std.testing.expectError(error.OutOfMemory, allocator.create(u64)); |
| 928 | | 910 | |
| 929 | allocator.destroy(small); | 911 | allocator.destroy(small); |
| 930 | assert(gpda.total_requested_bytes == 1000); | 912 | std.testing.expect(gpda.total_requested_bytes == 1000); |
| 931 | | 913 | |
| 932 | allocator.free(big); | 914 | allocator.free(big); |
| 933 | assert(gpda.total_requested_bytes == 0); | 915 | std.testing.expect(gpda.total_requested_bytes == 0); |
| 934 | | 916 | |
| 935 | const exact = try allocator.alloc(u8, 1010); | 917 | const exact = try allocator.alloc(u8, 1010); |
| 936 | assert(gpda.total_requested_bytes == 1010); | 918 | std.testing.expect(gpda.total_requested_bytes == 1010); |
| 937 | allocator.free(exact); | 919 | allocator.free(exact); |
| 938 | } | 920 | } |