| ... | ... | @@ -21,67 +21,138 @@ pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig" |
| 21 | 21 | |
| 22 | 22 | const Allocator = mem.Allocator; |
| 23 | 23 | |
| 24 | | usingnamespace if (comptime @hasDecl(c, "malloc_size")) |
| 25 | | struct { |
| 26 | | pub const supports_malloc_size = true; |
| 27 | | pub const malloc_size = c.malloc_size; |
| 24 | const CAllocator = struct { |
| 25 | comptime { |
| 26 | if (!builtin.link_libc) { |
| 27 | @compileError("C allocator is only available when linking against libc"); |
| 28 | } |
| 28 | 29 | } |
| 29 | | else if (comptime @hasDecl(c, "malloc_usable_size")) |
| 30 | | struct { |
| 31 | | pub const supports_malloc_size = true; |
| 32 | | pub const malloc_size = c.malloc_usable_size; |
| 30 | |
| 31 | usingnamespace if (comptime @hasDecl(c, "malloc_size")) |
| 32 | struct { |
| 33 | pub const supports_malloc_size = true; |
| 34 | pub const malloc_size = c.malloc_size; |
| 35 | } |
| 36 | else if (comptime @hasDecl(c, "malloc_usable_size")) |
| 37 | struct { |
| 38 | pub const supports_malloc_size = true; |
| 39 | pub const malloc_size = c.malloc_usable_size; |
| 40 | } |
| 41 | else if (comptime @hasDecl(c, "_msize")) |
| 42 | struct { |
| 43 | pub const supports_malloc_size = true; |
| 44 | pub const malloc_size = c._msize; |
| 45 | } |
| 46 | else |
| 47 | struct { |
| 48 | pub const supports_malloc_size = false; |
| 49 | }; |
| 50 | |
| 51 | pub const supports_posix_memalign = @hasDecl(c, "posix_memalign"); |
| 52 | |
| 53 | fn getHeader(ptr: [*]u8) *[*]u8 { |
| 54 | return @intToPtr(*[*]u8, @ptrToInt(ptr) - @sizeOf(usize)); |
| 33 | 55 | } |
| 34 | | else |
| 35 | | struct { |
| 36 | | pub const supports_malloc_size = false; |
| 37 | | }; |
| 38 | 56 | |
| 39 | | pub const c_allocator = &c_allocator_state; |
| 40 | | var c_allocator_state = Allocator{ |
| 41 | | .allocFn = cAlloc, |
| 42 | | .resizeFn = cResize, |
| 43 | | }; |
| 57 | fn alignedAlloc(len: usize, alignment: usize) ?[*]u8 { |
| 58 | if (supports_posix_memalign) { |
| 59 | // The posix_memalign only accepts alignment values that are a |
| 60 | // multiple of the pointer size |
| 61 | const eff_alignment = std.math.max(alignment, @sizeOf(usize)); |
| 62 | |
| 63 | var aligned_ptr: ?*c_void = undefined; |
| 64 | if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0) |
| 65 | return null; |
| 66 | |
| 67 | return @ptrCast([*]u8, aligned_ptr); |
| 68 | } |
| 44 | 69 | |
| 45 | | fn cAlloc(self: *Allocator, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) Allocator.Error![]u8 { |
| 46 | | assert(ptr_align <= @alignOf(c_longdouble)); |
| 47 | | const ptr = @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory); |
| 48 | | if (len_align == 0) { |
| 49 | | return ptr[0..len]; |
| 70 | // Thin wrapper around regular malloc, overallocate to account for |
| 71 | // alignment padding and store the orignal malloc()'ed pointer before |
| 72 | // the aligned address. |
| 73 | var unaligned_ptr = @ptrCast([*]u8, c.malloc(len + alignment - 1 + @sizeOf(usize)) orelse return null); |
| 74 | const unaligned_addr = @ptrToInt(unaligned_ptr); |
| 75 | const aligned_addr = mem.alignForward(unaligned_addr + @sizeOf(usize), alignment); |
| 76 | var aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr); |
| 77 | getHeader(aligned_ptr).* = unaligned_ptr; |
| 78 | |
| 79 | return aligned_ptr; |
| 50 | 80 | } |
| 51 | | const full_len = init: { |
| 52 | | if (supports_malloc_size) { |
| 53 | | const s = malloc_size(ptr); |
| 54 | | assert(s >= len); |
| 55 | | break :init s; |
| 81 | |
| 82 | fn alignedFree(ptr: [*]u8) void { |
| 83 | if (supports_posix_memalign) { |
| 84 | return c.free(ptr); |
| 56 | 85 | } |
| 57 | | break :init len; |
| 58 | | }; |
| 59 | | return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)]; |
| 60 | | } |
| 61 | 86 | |
| 62 | | fn cResize( |
| 63 | | self: *Allocator, |
| 64 | | buf: []u8, |
| 65 | | old_align: u29, |
| 66 | | new_len: usize, |
| 67 | | len_align: u29, |
| 68 | | ret_addr: usize, |
| 69 | | ) Allocator.Error!usize { |
| 70 | | if (new_len == 0) { |
| 71 | | c.free(buf.ptr); |
| 72 | | return 0; |
| 87 | const unaligned_ptr = getHeader(ptr).*; |
| 88 | c.free(unaligned_ptr); |
| 73 | 89 | } |
| 74 | | if (new_len <= buf.len) { |
| 75 | | return mem.alignAllocLen(buf.len, new_len, len_align); |
| 90 | |
| 91 | fn alignedAllocSize(ptr: [*]u8) usize { |
| 92 | if (supports_posix_memalign) { |
| 93 | return malloc_size(ptr); |
| 94 | } |
| 95 | |
| 96 | const unaligned_ptr = getHeader(ptr).*; |
| 97 | const delta = @ptrToInt(ptr) - @ptrToInt(unaligned_ptr); |
| 98 | return malloc_size(unaligned_ptr) - delta; |
| 76 | 99 | } |
| 77 | | if (supports_malloc_size) { |
| 78 | | const full_len = malloc_size(buf.ptr); |
| 79 | | if (new_len <= full_len) { |
| 80 | | return mem.alignAllocLen(full_len, new_len, len_align); |
| 100 | |
| 101 | fn alloc( |
| 102 | allocator: *Allocator, |
| 103 | len: usize, |
| 104 | alignment: u29, |
| 105 | len_align: u29, |
| 106 | return_address: usize, |
| 107 | ) error{OutOfMemory}![]u8 { |
| 108 | assert(len > 0); |
| 109 | assert(std.math.isPowerOfTwo(alignment)); |
| 110 | |
| 111 | var ptr = alignedAlloc(len, alignment) orelse return error.OutOfMemory; |
| 112 | if (len_align == 0) { |
| 113 | return ptr[0..len]; |
| 81 | 114 | } |
| 115 | const full_len = init: { |
| 116 | if (supports_malloc_size) { |
| 117 | const s = alignedAllocSize(ptr); |
| 118 | assert(s >= len); |
| 119 | break :init s; |
| 120 | } |
| 121 | break :init len; |
| 122 | }; |
| 123 | return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)]; |
| 82 | 124 | } |
| 83 | | return error.OutOfMemory; |
| 84 | | } |
| 125 | |
| 126 | fn resize( |
| 127 | allocator: *Allocator, |
| 128 | buf: []u8, |
| 129 | buf_align: u29, |
| 130 | new_len: usize, |
| 131 | len_align: u29, |
| 132 | return_address: usize, |
| 133 | ) Allocator.Error!usize { |
| 134 | if (new_len == 0) { |
| 135 | alignedFree(buf.ptr); |
| 136 | return 0; |
| 137 | } |
| 138 | if (new_len <= buf.len) { |
| 139 | return mem.alignAllocLen(buf.len, new_len, len_align); |
| 140 | } |
| 141 | if (supports_malloc_size) { |
| 142 | const full_len = alignedAllocSize(buf.ptr); |
| 143 | if (new_len <= full_len) { |
| 144 | return mem.alignAllocLen(full_len, new_len, len_align); |
| 145 | } |
| 146 | } |
| 147 | return error.OutOfMemory; |
| 148 | } |
| 149 | }; |
| 150 | |
| 151 | pub const c_allocator = &c_allocator_state; |
| 152 | var c_allocator_state = Allocator{ |
| 153 | .allocFn = CAllocator.alloc, |
| 154 | .resizeFn = CAllocator.resize, |
| 155 | }; |
| 85 | 156 | |
| 86 | 157 | /// This allocator makes a syscall directly for every allocation and free. |
| 87 | 158 | /// Thread-safe and lock-free. |
| ... | ... | @@ -726,9 +797,10 @@ pub fn StackFallbackAllocator(comptime size: usize) type { |
| 726 | 797 | |
| 727 | 798 | test "c_allocator" { |
| 728 | 799 | if (builtin.link_libc) { |
| 729 | | var slice = try c_allocator.alloc(u8, 50); |
| 730 | | defer c_allocator.free(slice); |
| 731 | | slice = try c_allocator.realloc(slice, 100); |
| 800 | try testAllocator(c_allocator); |
| 801 | try testAllocatorAligned(c_allocator); |
| 802 | try testAllocatorLargeAlignment(c_allocator); |
| 803 | try testAllocatorAlignedShrink(c_allocator); |
| 732 | 804 | } |
| 733 | 805 | } |
| 734 | 806 | |
| ... | ... | @@ -772,7 +844,7 @@ test "WasmPageAllocator internals" { |
| 772 | 844 | test "PageAllocator" { |
| 773 | 845 | const allocator = page_allocator; |
| 774 | 846 | try testAllocator(allocator); |
| 775 | | try testAllocatorAligned(allocator, 16); |
| 847 | try testAllocatorAligned(allocator); |
| 776 | 848 | if (!std.Target.current.isWasm()) { |
| 777 | 849 | try testAllocatorLargeAlignment(allocator); |
| 778 | 850 | try testAllocatorAlignedShrink(allocator); |
| ... | ... | @@ -802,7 +874,7 @@ test "HeapAllocator" { |
| 802 | 874 | |
| 803 | 875 | const allocator = &heap_allocator.allocator; |
| 804 | 876 | try testAllocator(allocator); |
| 805 | | try testAllocatorAligned(allocator, 16); |
| 877 | try testAllocatorAligned(allocator); |
| 806 | 878 | try testAllocatorLargeAlignment(allocator); |
| 807 | 879 | try testAllocatorAlignedShrink(allocator); |
| 808 | 880 | } |
| ... | ... | @@ -813,7 +885,7 @@ test "ArenaAllocator" { |
| 813 | 885 | defer arena_allocator.deinit(); |
| 814 | 886 | |
| 815 | 887 | try testAllocator(&arena_allocator.allocator); |
| 816 | | try testAllocatorAligned(&arena_allocator.allocator, 16); |
| 888 | try testAllocatorAligned(&arena_allocator.allocator); |
| 817 | 889 | try testAllocatorLargeAlignment(&arena_allocator.allocator); |
| 818 | 890 | try testAllocatorAlignedShrink(&arena_allocator.allocator); |
| 819 | 891 | } |
| ... | ... | @@ -823,7 +895,7 @@ test "FixedBufferAllocator" { |
| 823 | 895 | var fixed_buffer_allocator = mem.validationWrap(FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..])); |
| 824 | 896 | |
| 825 | 897 | try testAllocator(&fixed_buffer_allocator.allocator); |
| 826 | | try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16); |
| 898 | try testAllocatorAligned(&fixed_buffer_allocator.allocator); |
| 827 | 899 | try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); |
| 828 | 900 | try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator); |
| 829 | 901 | } |
| ... | ... | @@ -881,7 +953,7 @@ test "ThreadSafeFixedBufferAllocator" { |
| 881 | 953 | var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); |
| 882 | 954 | |
| 883 | 955 | try testAllocator(&fixed_buffer_allocator.allocator); |
| 884 | | try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16); |
| 956 | try testAllocatorAligned(&fixed_buffer_allocator.allocator); |
| 885 | 957 | try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); |
| 886 | 958 | try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator); |
| 887 | 959 | } |
| ... | ... | @@ -916,6 +988,10 @@ pub fn testAllocator(base_allocator: *mem.Allocator) !void { |
| 916 | 988 | |
| 917 | 989 | allocator.free(slice); |
| 918 | 990 | |
| 991 | // Zero-length allocation |
| 992 | var empty = try allocator.alloc(u8, 0); |
| 993 | allocator.free(empty); |
| 994 | // Allocation with zero-sized types |
| 919 | 995 | const zero_bit_ptr = try allocator.create(u0); |
| 920 | 996 | zero_bit_ptr.* = 0; |
| 921 | 997 | allocator.destroy(zero_bit_ptr); |
| ... | ... | @@ -928,31 +1004,34 @@ pub fn testAllocator(base_allocator: *mem.Allocator) !void { |
| 928 | 1004 | allocator.free(oversize); |
| 929 | 1005 | } |
| 930 | 1006 | |
| 931 | | pub fn testAllocatorAligned(base_allocator: *mem.Allocator, comptime alignment: u29) !void { |
| 1007 | pub fn testAllocatorAligned(base_allocator: *mem.Allocator) !void { |
| 932 | 1008 | var validationAllocator = mem.validationWrap(base_allocator); |
| 933 | 1009 | const allocator = &validationAllocator.allocator; |
| 934 | 1010 | |
| 935 | | // initial |
| 936 | | var slice = try allocator.alignedAlloc(u8, alignment, 10); |
| 937 | | testing.expect(slice.len == 10); |
| 938 | | // grow |
| 939 | | slice = try allocator.realloc(slice, 100); |
| 940 | | testing.expect(slice.len == 100); |
| 941 | | // shrink |
| 942 | | slice = allocator.shrink(slice, 10); |
| 943 | | testing.expect(slice.len == 10); |
| 944 | | // go to zero |
| 945 | | slice = allocator.shrink(slice, 0); |
| 946 | | testing.expect(slice.len == 0); |
| 947 | | // realloc from zero |
| 948 | | slice = try allocator.realloc(slice, 100); |
| 949 | | testing.expect(slice.len == 100); |
| 950 | | // shrink with shrink |
| 951 | | slice = allocator.shrink(slice, 10); |
| 952 | | testing.expect(slice.len == 10); |
| 953 | | // shrink to zero |
| 954 | | slice = allocator.shrink(slice, 0); |
| 955 | | testing.expect(slice.len == 0); |
| 1011 | // Test a few alignment values, smaller and bigger than the type's one |
| 1012 | inline for ([_]u29{ 1, 2, 4, 8, 16, 32, 64 }) |alignment| { |
| 1013 | // initial |
| 1014 | var slice = try allocator.alignedAlloc(u8, alignment, 10); |
| 1015 | testing.expect(slice.len == 10); |
| 1016 | // grow |
| 1017 | slice = try allocator.realloc(slice, 100); |
| 1018 | testing.expect(slice.len == 100); |
| 1019 | // shrink |
| 1020 | slice = allocator.shrink(slice, 10); |
| 1021 | testing.expect(slice.len == 10); |
| 1022 | // go to zero |
| 1023 | slice = allocator.shrink(slice, 0); |
| 1024 | testing.expect(slice.len == 0); |
| 1025 | // realloc from zero |
| 1026 | slice = try allocator.realloc(slice, 100); |
| 1027 | testing.expect(slice.len == 100); |
| 1028 | // shrink with shrink |
| 1029 | slice = allocator.shrink(slice, 10); |
| 1030 | testing.expect(slice.len == 10); |
| 1031 | // shrink to zero |
| 1032 | slice = allocator.shrink(slice, 0); |
| 1033 | testing.expect(slice.len == 0); |
| 1034 | } |
| 956 | 1035 | } |
| 957 | 1036 | |
| 958 | 1037 | pub fn testAllocatorLargeAlignment(base_allocator: *mem.Allocator) mem.Allocator.Error!void { |