diff --git a/lib/std/c.zig b/lib/std/c.zig index 5a26b6f9f6..4b143d499f 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -246,20 +246,8 @@ pub extern "c" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) c_int; pub extern "c" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) c_int; pub extern "c" fn malloc(usize) ?*c_void; - -pub usingnamespace switch (builtin.os.tag) { - .linux, .freebsd, .kfreebsd, .netbsd => struct { - pub extern "c" fn malloc_usable_size(?*const c_void) usize; - }, - .macos, .ios, .watchos, .tvos => struct { - pub extern "c" fn malloc_size(?*const c_void) usize; - }, - else => struct {}, -}; - pub extern "c" fn realloc(?*c_void, usize) ?*c_void; pub extern "c" fn free(*c_void) void; -pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; pub extern "c" fn futimes(fd: fd_t, times: *[2]timeval) c_int; pub extern "c" fn utimes(path: [*:0]const u8, times: *[2]timeval) c_int; diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index e0acd6c746..86a2bf5ee4 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -45,6 +45,9 @@ pub const _fstatat = if (builtin.arch == .aarch64) fstatat else @"fstatat$INODE6 pub extern "c" fn mach_absolute_time() u64; pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void; +pub extern "c" fn malloc_size(?*const c_void) usize; +pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; + pub extern "c" fn kevent64( kq: c_int, changelist: [*]const kevent64_s, diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 2550cacc5b..3261d34b78 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -17,6 +17,8 @@ pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int; pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int; +pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; + pub const pthread_mutex_t = extern struct { inner: ?*c_void = null, }; diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 1545a0e153..8fa78b0d6f 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -13,6 +13,9 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; +pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn malloc_usable_size(?*const c_void) usize; + pub const sf_hdtr = extern struct { headers: [*]const iovec_const, hdr_cnt: c_int, diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig index d48c76c7a6..947a92a1e4 100644 --- a/lib/std/c/linux.zig +++ b/lib/std/c/linux.zig @@ -101,6 +101,8 @@ pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_ pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int; pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int; +pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn malloc_usable_size(?*const c_void) usize; pub const pthread_attr_t = extern struct { __size: [56]u8, diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 0eb8e32bae..e6d7e86bee 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -30,6 +30,9 @@ pub extern "c" fn __getrusage50(who: c_int, usage: *rusage) c_int; // libc aliases this as sched_yield pub extern "c" fn __libc_thr_yield() c_int; +pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn malloc_usable_size(?*const c_void) usize; + pub const pthread_mutex_t = extern struct { ptm_magic: u32 = 0x33330003, ptm_errorcheck: padded_pthread_spin_t = 0, diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index f31ab71eee..48feaff428 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -32,3 +32,6 @@ pub const pthread_spinlock_t = extern struct { pub const pthread_attr_t = extern struct { inner: ?*c_void = null, }; + +pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; +pub extern "c" fn malloc_usable_size(?*const c_void) usize; diff --git a/lib/std/c/windows.zig b/lib/std/c/windows.zig index 0c6019a218..f96da56c1f 100644 --- a/lib/std/c/windows.zig +++ b/lib/std/c/windows.zig @@ -5,6 +5,4 @@ // and substantial portions of the software. pub extern "c" fn _errno() *c_int; -pub extern "c" fn _aligned_free(memblock: ?*c_void) void; -pub extern "c" fn _aligned_malloc(size: usize, alignment: usize) ?*c_void; -pub extern "c" fn _aligned_realloc(memblock: ?*c_void, size: usize, alignment: usize) ?*c_void; +pub extern "c" fn _msize(memblock: ?*c_void) usize; diff --git a/lib/std/heap.zig b/lib/std/heap.zig index ef465fbae8..431e93d6c2 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -38,37 +38,63 @@ const CAllocator = struct { pub const supports_malloc_size = true; pub const malloc_size = c.malloc_usable_size; } + else if (comptime @hasDecl(c, "_msize")) + struct { + pub const supports_malloc_size = true; + pub const malloc_size = c._msize; + } else struct { pub const supports_malloc_size = false; }; - // The alignment guaranteed by malloc, the value matches the result of the C - // expression `alignof(max_alignment_t)` - const min_ptr_alignment = comptime std.math.max( - @alignOf(c_longdouble), - @alignOf(c_longlong), - ); + pub const supports_posix_memalign = false and @hasDecl(c, "posix_memalign"); - fn aligned_alloc(len: usize, alignment: usize) ?*c_void { - // The minimum alignment supported by both APIs is the size of a pointer - const eff_alignment = std.math.max(alignment, @sizeOf(usize)); + fn get_header(ptr: [*]u8) *[*]u8 { + return @intToPtr(*[*]u8, @ptrToInt(ptr) - @sizeOf(usize)); + } - if (builtin.os.tag == .windows) { - return c._aligned_malloc(len, eff_alignment); + fn aligned_alloc(len: usize, alignment: usize) ?[*]u8 { + if (supports_posix_memalign) { + // The minimum alignment supported posix_memalign is the pointer size + const eff_alignment = std.math.max(alignment, @sizeOf(usize)); + + var aligned_ptr: ?*c_void = undefined; + if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0) + return null; + + return @ptrCast([*]u8, aligned_ptr); } - var aligned_ptr: ?*c_void = undefined; - if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0) - return null; + // Thin wrapper around regular malloc, overallocate to account for + // alignment padding and store the orignal malloc()'ed pointer before + // the aligned address. + var unaligned_ptr = @ptrCast([*]u8, c.malloc(len + alignment - 1 + @sizeOf(usize)) orelse return null); + const unaligned_addr = @ptrToInt(unaligned_ptr); + const aligned_addr = mem.alignForward(unaligned_addr + @sizeOf(usize), alignment); + var aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr); + get_header(aligned_ptr).* = unaligned_ptr; + return aligned_ptr; } - fn aligned_free(ptr: *c_void) void { - if (builtin.os.tag == .windows) { - return c._aligned_free(ptr); + fn aligned_free(ptr: [*]u8) void { + if (supports_posix_memalign) { + return c.free(ptr); } - c.free(ptr); + + const unaligned_ptr = get_header(ptr).*; + c.free(unaligned_ptr); + } + + fn aligned_alloc_size(ptr: [*]u8) usize { + if (supports_posix_memalign) { + return malloc_size(ptr); + } + + const unaligned_ptr = get_header(ptr).*; + const delta = @ptrToInt(ptr) - @ptrToInt(unaligned_ptr); + return malloc_size(unaligned_ptr) - delta; } fn alloc( @@ -81,23 +107,18 @@ const CAllocator = struct { assert(len > 0); assert(std.math.isPowerOfTwo(alignment)); - var ptr = if (alignment <= min_ptr_alignment) - @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory) - else - @ptrCast([*]u8, aligned_alloc(len, alignment) orelse return error.OutOfMemory); - - if (len_align == 0) + var ptr = aligned_alloc(len, alignment) orelse return error.OutOfMemory; + if (len_align == 0) { return ptr[0..len]; - + } const full_len = init: { if (supports_malloc_size) { - const s = malloc_size(ptr); + const s = aligned_alloc_size(ptr); assert(s >= len); break :init s; } break :init len; }; - return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)]; } @@ -110,17 +131,14 @@ const CAllocator = struct { return_address: usize, ) Allocator.Error!usize { if (new_len == 0) { - if (buf_align <= min_ptr_alignment) - c.free(buf.ptr) - else - aligned_free(buf.ptr); + aligned_free(buf.ptr); return 0; } if (new_len <= buf.len) { return mem.alignAllocLen(buf.len, new_len, len_align); } if (supports_malloc_size) { - const full_len = malloc_size(buf.ptr); + const full_len = aligned_alloc_size(buf.ptr); if (new_len <= full_len) { return mem.alignAllocLen(full_len, new_len, len_align); }