| ... | ... | @@ -95,6 +95,7 @@ |
| 95 | 95 | const std = @import("std"); |
| 96 | 96 | const math = std.math; |
| 97 | 97 | const assert = std.debug.assert; |
| 98 | const mem = std.mem; |
| 98 | 99 | const Allocator = std.mem.Allocator; |
| 99 | 100 | const page_size = std.mem.page_size; |
| 100 | 101 | const StackTrace = std.builtin.StackTrace; |
| ... | ... | @@ -117,11 +118,15 @@ const sys_can_stack_trace = switch (std.Target.current.cpu.arch) { |
| 117 | 118 | |
| 118 | 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 | 127 | pub const Config = struct { |
| 123 | 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 | 131 | /// If true, the allocator will have two fields: |
| 127 | 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 | 168 | const one_trace_size = @sizeOf(usize) * stack_n; |
| 164 | 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 | 173 | const small_bucket_count = math.log2(page_size); |
| 169 | 174 | const largest_bucket_object_size = 1 << (small_bucket_count - 1); |
| ... | ... | @@ -246,7 +251,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 246 | 251 | } |
| 247 | 252 | |
| 248 | 253 | fn bucketStackFramesStart(size_class: usize) usize { |
| 249 | | return std.mem.alignForward( |
| 254 | return mem.alignForward( |
| 250 | 255 | @sizeOf(BucketHeader) + usedBitsCount(size_class), |
| 251 | 256 | @alignOf(usize), |
| 252 | 257 | ); |
| ... | ... | @@ -322,7 +327,8 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 322 | 327 | } |
| 323 | 328 | |
| 324 | 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 | 332 | var stack_trace = StackTrace{ |
| 327 | 333 | .instruction_addresses = addresses, |
| 328 | 334 | .index = 0, |
| ... | ... | @@ -679,14 +685,14 @@ test "realloc" { |
| 679 | 685 | // This reallocation should keep its pointer address. |
| 680 | 686 | const old_slice = slice; |
| 681 | 687 | slice = try allocator.realloc(slice, 2); |
| 682 | | assert(old_slice.ptr == slice.ptr); |
| 683 | | assert(slice[0] == 0x12); |
| 688 | std.testing.expect(old_slice.ptr == slice.ptr); |
| 689 | std.testing.expect(slice[0] == 0x12); |
| 684 | 690 | slice[1] = 0x34; |
| 685 | 691 | |
| 686 | 692 | // This requires upgrading to a larger size class |
| 687 | 693 | slice = try allocator.realloc(slice, 17); |
| 688 | | assert(slice[0] == 0x12); |
| 689 | | assert(slice[1] == 0x34); |
| 694 | std.testing.expect(slice[0] == 0x12); |
| 695 | std.testing.expect(slice[1] == 0x34); |
| 690 | 696 | } |
| 691 | 697 | |
| 692 | 698 | test "shrink" { |
| ... | ... | @@ -697,18 +703,18 @@ test "shrink" { |
| 697 | 703 | var slice = try allocator.alloc(u8, 20); |
| 698 | 704 | defer allocator.free(slice); |
| 699 | 705 | |
| 700 | | std.mem.set(u8, slice, 0x11); |
| 706 | mem.set(u8, slice, 0x11); |
| 701 | 707 | |
| 702 | 708 | slice = allocator.shrink(slice, 17); |
| 703 | 709 | |
| 704 | 710 | for (slice) |b| { |
| 705 | | assert(b == 0x11); |
| 711 | std.testing.expect(b == 0x11); |
| 706 | 712 | } |
| 707 | 713 | |
| 708 | 714 | slice = allocator.shrink(slice, 16); |
| 709 | 715 | |
| 710 | 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 | 728 | |
| 723 | 729 | var old = slice1; |
| 724 | 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 | 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 | 736 | slice1 = try allocator.realloc(slice1, page_size * 2 + 1); |
| 731 | 737 | } |
| ... | ... | @@ -743,8 +749,8 @@ test "realloc small object to large object" { |
| 743 | 749 | // This requires upgrading to a large object |
| 744 | 750 | const large_object_size = page_size * 2 + 50; |
| 745 | 751 | slice = try allocator.realloc(slice, large_object_size); |
| 746 | | assert(slice[0] == 0x12); |
| 747 | | assert(slice[60] == 0x34); |
| 752 | std.testing.expect(slice[0] == 0x12); |
| 753 | std.testing.expect(slice[60] == 0x34); |
| 748 | 754 | } |
| 749 | 755 | |
| 750 | 756 | test "shrink large object to large object" { |
| ... | ... | @@ -758,16 +764,16 @@ test "shrink large object to large object" { |
| 758 | 764 | slice[60] = 0x34; |
| 759 | 765 | |
| 760 | 766 | slice = try allocator.resize(slice, page_size * 2 + 1); |
| 761 | | assert(slice[0] == 0x12); |
| 762 | | assert(slice[60] == 0x34); |
| 767 | std.testing.expect(slice[0] == 0x12); |
| 768 | std.testing.expect(slice[60] == 0x34); |
| 763 | 769 | |
| 764 | 770 | slice = allocator.shrink(slice, page_size * 2 + 1); |
| 765 | | assert(slice[0] == 0x12); |
| 766 | | assert(slice[60] == 0x34); |
| 771 | std.testing.expect(slice[0] == 0x12); |
| 772 | std.testing.expect(slice[60] == 0x34); |
| 767 | 773 | |
| 768 | 774 | slice = try allocator.realloc(slice, page_size * 2); |
| 769 | | assert(slice[0] == 0x12); |
| 770 | | assert(slice[60] == 0x34); |
| 775 | std.testing.expect(slice[0] == 0x12); |
| 776 | std.testing.expect(slice[60] == 0x34); |
| 771 | 777 | } |
| 772 | 778 | |
| 773 | 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 | 789 | defer allocator.free(slice); |
| 784 | 790 | |
| 785 | 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 | 793 | try stuff_to_free.append(slice); |
| 788 | 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 | 800 | slice[60] = 0x34; |
| 795 | 801 | |
| 796 | 802 | slice = try allocator.reallocAdvanced(slice, page_size * 2, alloc_size / 2, .exact); |
| 797 | | assert(slice[0] == 0x12); |
| 798 | | assert(slice[60] == 0x34); |
| 803 | std.testing.expect(slice[0] == 0x12); |
| 804 | std.testing.expect(slice[60] == 0x34); |
| 799 | 805 | } |
| 800 | 806 | |
| 801 | 807 | test "realloc large object to small object" { |
| ... | ... | @@ -809,8 +815,8 @@ test "realloc large object to small object" { |
| 809 | 815 | slice[16] = 0x34; |
| 810 | 816 | |
| 811 | 817 | slice = try allocator.realloc(slice, 19); |
| 812 | | assert(slice[0] == 0x12); |
| 813 | | assert(slice[16] == 0x34); |
| 818 | std.testing.expect(slice[0] == 0x12); |
| 819 | std.testing.expect(slice[16] == 0x34); |
| 814 | 820 | } |
| 815 | 821 | |
| 816 | 822 | test "non-page-allocator backing allocator" { |
| ... | ... | @@ -834,7 +840,7 @@ test "realloc large object to larger alignment" { |
| 834 | 840 | defer allocator.free(slice); |
| 835 | 841 | |
| 836 | 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 | 844 | try stuff_to_free.append(slice); |
| 839 | 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 | 851 | slice[16] = 0x34; |
| 846 | 852 | |
| 847 | 853 | slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100, .exact); |
| 848 | | assert(slice[0] == 0x12); |
| 849 | | assert(slice[16] == 0x34); |
| 854 | std.testing.expect(slice[0] == 0x12); |
| 855 | std.testing.expect(slice[16] == 0x34); |
| 850 | 856 | |
| 851 | 857 | slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25, .exact); |
| 852 | | assert(slice[0] == 0x12); |
| 853 | | assert(slice[16] == 0x34); |
| 858 | std.testing.expect(slice[0] == 0x12); |
| 859 | std.testing.expect(slice[16] == 0x34); |
| 854 | 860 | |
| 855 | 861 | slice = try allocator.reallocAdvanced(slice, page_size * 2, page_size * 2 + 100, .exact); |
| 856 | | assert(slice[0] == 0x12); |
| 857 | | assert(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)); |
| 862 | std.testing.expect(slice[0] == 0x12); |
| 863 | std.testing.expect(slice[16] == 0x34); |
| 882 | 864 | } |
| 883 | 865 | |
| 884 | 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 | 877 | // Next allocation will fail in the backing allocator of the GeneralPurposeAllocator |
| 896 | 878 | |
| 897 | 879 | slice = allocator.shrink(slice, 4); |
| 898 | | assert(slice[0] == 0x12); |
| 899 | | assert(slice[3] == 0x34); |
| 880 | std.testing.expect(slice[0] == 0x12); |
| 881 | std.testing.expect(slice[3] == 0x34); |
| 900 | 882 | } |
| 901 | 883 | |
| 902 | 884 | test "objects of size 1024 and 2048" { |
| ... | ... | @@ -919,20 +901,20 @@ test "setting a memory cap" { |
| 919 | 901 | gpda.setRequestedMemoryLimit(1010); |
| 920 | 902 | |
| 921 | 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 | 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 | 909 | std.testing.expectError(error.OutOfMemory, allocator.create(u64)); |
| 928 | 910 | |
| 929 | 911 | allocator.destroy(small); |
| 930 | | assert(gpda.total_requested_bytes == 1000); |
| 912 | std.testing.expect(gpda.total_requested_bytes == 1000); |
| 931 | 913 | |
| 932 | 914 | allocator.free(big); |
| 933 | | assert(gpda.total_requested_bytes == 0); |
| 915 | std.testing.expect(gpda.total_requested_bytes == 0); |
| 934 | 916 | |
| 935 | 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 | 919 | allocator.free(exact); |
| 938 | 920 | } |