mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
The main goal here was to avoid allocating padding and header space if `malloc` already guarantees the alignment we need via `max_align_t`. Previously, the compiler was using `std.heap.raw_c_allocator` as its GPA in some cases depending on `std.c.max_align_t`, but that's pretty fragile (it meant we had to encode our alignment requirements into `src/main.zig`!). Perhaps more importantly, that solution is unnecessarily restrictive: since Zig's `Allocator` API passes the `Alignment` not only to `alloc`, but also to `free` etc, we are able to use a different strategy depending on its value. So `c_allocator` can simply compare the requested align to `Alignment.of(std.c.max_align_t)`, and use a raw `malloc` call (no header needed!) if it will guarantee a suitable alignment (which, in practice, will be true the vast majority of the time). So in short, this makes `std.heap.c_allocator` more memory efficient, and probably removes any incentive to use `std.heap.raw_c_allocator`. I also refactored the `c_allocator` implementation while doing this, just to neaten things up a little.
1102 lines
40 KiB
Zig
1102 lines
40 KiB
Zig
const std = @import("std.zig");
|
|
const builtin = @import("builtin");
|
|
const root = @import("root");
|
|
const assert = std.debug.assert;
|
|
const testing = std.testing;
|
|
const mem = std.mem;
|
|
const c = std.c;
|
|
const Allocator = std.mem.Allocator;
|
|
const windows = std.os.windows;
|
|
const Alignment = std.mem.Alignment;
|
|
|
|
pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
|
|
pub const SmpAllocator = @import("heap/SmpAllocator.zig");
|
|
pub const FixedBufferAllocator = @import("heap/FixedBufferAllocator.zig");
|
|
pub const PageAllocator = @import("heap/PageAllocator.zig");
|
|
pub const SbrkAllocator = @import("heap/sbrk_allocator.zig").SbrkAllocator;
|
|
pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig");
|
|
pub const WasmAllocator = @import("heap/WasmAllocator.zig");
|
|
|
|
pub const DebugAllocatorConfig = @import("heap/debug_allocator.zig").Config;
|
|
pub const DebugAllocator = @import("heap/debug_allocator.zig").DebugAllocator;
|
|
pub const Check = enum { ok, leak };
|
|
/// Deprecated; to be removed after 0.14.0 is tagged.
|
|
pub const GeneralPurposeAllocatorConfig = DebugAllocatorConfig;
|
|
/// Deprecated; to be removed after 0.14.0 is tagged.
|
|
pub const GeneralPurposeAllocator = DebugAllocator;
|
|
|
|
/// A memory pool that can allocate objects of a single type very quickly.
|
|
/// Use this when you need to allocate a lot of objects of the same type,
|
|
/// because it outperforms general purpose allocators.
|
|
/// Functions that potentially allocate memory accept an `Allocator` parameter.
|
|
pub fn MemoryPool(comptime Item: type) type {
|
|
return memory_pool.Extra(Item, .{ .alignment = null });
|
|
}
|
|
pub const memory_pool = @import("heap/memory_pool.zig");
|
|
|
|
/// Deprecated; use `memory_pool.Aligned`.
|
|
pub const MemoryPoolAligned = memory_pool.Aligned;
|
|
/// Deprecated; use `memory_pool.Extra`.
|
|
pub const MemoryPoolExtra = memory_pool.Extra;
|
|
/// Deprecated; use `memory_pool.Options`.
|
|
pub const MemoryPoolOptions = memory_pool.Options;
|
|
|
|
/// TODO Utilize this on Windows.
|
|
pub var next_mmap_addr_hint: ?[*]align(page_size_min) u8 = null;
|
|
|
|
/// comptime-known minimum page size of the target.
|
|
///
|
|
/// All pointers from `mmap` or `NtAllocateVirtualMemory` are aligned to at least
|
|
/// `page_size_min`, but their actual alignment may be bigger.
|
|
///
|
|
/// This value can be overridden via `std.options.page_size_min`.
|
|
///
|
|
/// On many systems, the actual page size can only be determined at runtime
|
|
/// with `pageSize`.
|
|
pub const page_size_min: usize = std.options.page_size_min orelse (page_size_min_default orelse
|
|
@compileError(@tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " has unknown page_size_min; populate std.options.page_size_min"));
|
|
|
|
/// comptime-known maximum page size of the target.
|
|
///
|
|
/// Targeting a system with a larger page size may require overriding
|
|
/// `std.options.page_size_max`, as well as providing a corresponding linker
|
|
/// option.
|
|
///
|
|
/// The actual page size can only be determined at runtime with `pageSize`.
|
|
pub const page_size_max: usize = std.options.page_size_max orelse (page_size_max_default orelse if (builtin.os.tag == .freestanding or builtin.os.tag == .other)
|
|
@compileError("freestanding/other page_size_max must provided with std.options.page_size_max")
|
|
else
|
|
@compileError(@tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " has unknown page_size_max; populate std.options.page_size_max"));
|
|
|
|
/// If the page size is comptime-known, return value is comptime.
|
|
/// Otherwise, calls `std.options.queryPageSize` which by default queries the
|
|
/// host operating system at runtime.
|
|
pub inline fn pageSize() usize {
|
|
if (page_size_min == page_size_max) return page_size_min;
|
|
return std.options.queryPageSize();
|
|
}
|
|
|
|
test pageSize {
|
|
assert(std.math.isPowerOfTwo(pageSize()));
|
|
}
|
|
|
|
/// The default implementation of `std.options.queryPageSize`.
|
|
/// Asserts that the page size is within `page_size_min` and `page_size_max`
|
|
pub fn defaultQueryPageSize() usize {
|
|
const global = struct {
|
|
var cached_result: std.atomic.Value(usize) = .init(0);
|
|
};
|
|
var size = global.cached_result.load(.unordered);
|
|
if (size > 0) return size;
|
|
size = size: switch (builtin.os.tag) {
|
|
.linux => if (builtin.link_libc)
|
|
@max(std.c.sysconf(@intFromEnum(std.c._SC.PAGESIZE)), 0)
|
|
else
|
|
std.os.linux.getauxval(std.elf.AT_PAGESZ),
|
|
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
|
|
const task_port = std.c.mach_task_self();
|
|
// mach_task_self may fail "if there are any resource failures or other errors".
|
|
if (task_port == std.c.TASK.NULL) break :size 0;
|
|
var info_count = std.c.TASK.VM.INFO_COUNT;
|
|
var vm_info: std.c.task_vm_info_data_t = undefined;
|
|
vm_info.page_size = 0;
|
|
_ = std.c.task_info(
|
|
task_port,
|
|
std.c.TASK.VM.INFO,
|
|
@as(std.c.task_info_t, @ptrCast(&vm_info)),
|
|
&info_count,
|
|
);
|
|
break :size @intCast(vm_info.page_size);
|
|
},
|
|
.windows => {
|
|
var sbi: windows.SYSTEM_BASIC_INFORMATION = undefined;
|
|
switch (windows.ntdll.NtQuerySystemInformation(
|
|
.SystemBasicInformation,
|
|
&sbi,
|
|
@sizeOf(windows.SYSTEM_BASIC_INFORMATION),
|
|
null,
|
|
)) {
|
|
.SUCCESS => break :size sbi.PageSize,
|
|
else => break :size 0,
|
|
}
|
|
},
|
|
else => if (builtin.link_libc)
|
|
@max(std.c.sysconf(@intFromEnum(std.c._SC.PAGESIZE)), 0)
|
|
else if (builtin.os.tag == .freestanding or builtin.os.tag == .other)
|
|
@compileError("unsupported target: freestanding/other")
|
|
else
|
|
@compileError("pageSize on " ++ @tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " is not supported without linking libc, using the default implementation"),
|
|
};
|
|
if (size == 0) size = page_size_max;
|
|
|
|
assert(size >= page_size_min);
|
|
assert(size <= page_size_max);
|
|
global.cached_result.store(size, .unordered);
|
|
|
|
return size;
|
|
}
|
|
|
|
test defaultQueryPageSize {
|
|
if (builtin.cpu.arch.isWasm()) return error.SkipZigTest;
|
|
assert(std.math.isPowerOfTwo(defaultQueryPageSize()));
|
|
}
|
|
|
|
/// A wrapper around the C memory allocation API which supports the full `Allocator`
|
|
/// interface, including arbitrary alignment. Simple `malloc` calls are used when
|
|
/// possible, but large requested alignments may require larger buffers in order to
|
|
/// satisfy the request. As well as `malloc`, `realloc`, and `free`, the extension
|
|
/// functions `malloc_usable_size` and `posix_memalign` are used when available.
|
|
///
|
|
/// For an allocator that directly calls `malloc`/`realloc`/`free`, with no padding
|
|
/// or special handling, see `raw_c_allocator`.
|
|
pub const c_allocator: Allocator = .{
|
|
.ptr = undefined,
|
|
.vtable = &c_allocator_impl.vtable,
|
|
};
|
|
const c_allocator_impl = struct {
|
|
comptime {
|
|
if (!builtin.link_libc) {
|
|
@compileError("C allocator is only available when linking against libc");
|
|
}
|
|
}
|
|
|
|
const vtable: Allocator.VTable = .{
|
|
.alloc = alloc,
|
|
.resize = resize,
|
|
.remap = remap,
|
|
.free = free,
|
|
};
|
|
|
|
const have_posix_memalign = switch (builtin.os.tag) {
|
|
.dragonfly,
|
|
.netbsd,
|
|
.freebsd,
|
|
.illumos,
|
|
.openbsd,
|
|
.linux,
|
|
.driverkit,
|
|
.ios,
|
|
.maccatalyst,
|
|
.macos,
|
|
.tvos,
|
|
.visionos,
|
|
.watchos,
|
|
.serenity,
|
|
=> true,
|
|
else => false,
|
|
};
|
|
|
|
fn allocStrat(need_align: Alignment) union(enum) {
|
|
raw,
|
|
posix_memalign: if (have_posix_memalign) void else noreturn,
|
|
manual_align: if (have_posix_memalign) noreturn else void,
|
|
} {
|
|
// If `malloc` guarantees `need_align`, always prefer a raw allocation.
|
|
if (Alignment.compare(need_align, .lte, .of(c.max_align_t))) {
|
|
return .raw;
|
|
}
|
|
// Use `posix_memalign` if available. Otherwise, we must manually align the allocation.
|
|
return if (have_posix_memalign) .posix_memalign else .manual_align;
|
|
}
|
|
|
|
/// If `allocStrat(a) == .manual_align`, an allocation looks like this:
|
|
///
|
|
/// unaligned_ptr hdr_ptr aligned_ptr
|
|
/// v v v
|
|
/// +---------------+--------+--------------+
|
|
/// | padding | header | usable bytes |
|
|
/// +---------------+--------+--------------+
|
|
///
|
|
/// * `unaligned_ptr` is the raw return value of `malloc`.
|
|
/// * `aligned_ptr` is computed by aligning `unaligned_ptr` forward; it is what `alloc` returns.
|
|
/// * `hdr_ptr` points to a pointer-sized header directly before the usable space. This header
|
|
/// contains the value `unaligned_ptr`, so that we can pass it to `free` later. This is
|
|
/// necessary because the width of the padding is unknown.
|
|
///
|
|
/// This function accepts `aligned_ptr` and offsets it backwards to return `hdr_ptr`.
|
|
fn manualAlignHeader(aligned_ptr: [*]u8) *[*]u8 {
|
|
return @ptrCast(@alignCast(aligned_ptr - @sizeOf(usize)));
|
|
}
|
|
|
|
fn alloc(
|
|
_: *anyopaque,
|
|
len: usize,
|
|
alignment: Alignment,
|
|
return_address: usize,
|
|
) ?[*]u8 {
|
|
_ = return_address;
|
|
assert(len > 0);
|
|
switch (allocStrat(alignment)) {
|
|
.raw => {
|
|
// C only needs to respect `max_align_t` up to the allocation size due to object
|
|
// alignment rules. If necessary, extend the allocation size.
|
|
const actual_len = @max(len, @alignOf(std.c.max_align_t));
|
|
const ptr = c.malloc(actual_len) orelse return null;
|
|
assert(alignment.check(@intFromPtr(ptr)));
|
|
return @ptrCast(ptr);
|
|
},
|
|
.posix_memalign => {
|
|
// The posix_memalign only accepts alignment values that are a
|
|
// multiple of the pointer size
|
|
const effective_alignment = @max(alignment.toByteUnits(), @sizeOf(usize));
|
|
var aligned_ptr: ?*anyopaque = undefined;
|
|
if (c.posix_memalign(&aligned_ptr, effective_alignment, len) != 0) {
|
|
return null;
|
|
}
|
|
assert(alignment.check(@intFromPtr(aligned_ptr)));
|
|
return @ptrCast(aligned_ptr);
|
|
},
|
|
.manual_align => {
|
|
// Overallocate to account for alignment padding and store the original pointer
|
|
// returned by `malloc` before the aligned address.
|
|
const padded_len = len + @sizeOf(usize) + alignment.toByteUnits() - 1;
|
|
const unaligned_ptr: [*]u8 = @ptrCast(c.malloc(padded_len) orelse return null);
|
|
const unaligned_addr = @intFromPtr(unaligned_ptr);
|
|
const aligned_addr = alignment.forward(unaligned_addr + @sizeOf(usize));
|
|
const aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr);
|
|
manualAlignHeader(aligned_ptr).* = unaligned_ptr;
|
|
return aligned_ptr;
|
|
},
|
|
}
|
|
}
|
|
|
|
fn resize(
|
|
_: *anyopaque,
|
|
memory: []u8,
|
|
alignment: Alignment,
|
|
new_len: usize,
|
|
return_address: usize,
|
|
) bool {
|
|
_ = return_address;
|
|
assert(new_len > 0);
|
|
if (new_len <= memory.len) {
|
|
return true; // in-place shrink always works
|
|
}
|
|
const mallocSize = func: {
|
|
if (@TypeOf(c.malloc_size) != void) break :func c.malloc_size;
|
|
if (@TypeOf(c.malloc_usable_size) != void) break :func c.malloc_usable_size;
|
|
if (@TypeOf(c._msize) != void) break :func c._msize;
|
|
return false; // we don't know how much space is actually available
|
|
};
|
|
const usable_len: usize = switch (allocStrat(alignment)) {
|
|
.raw, .posix_memalign => mallocSize(memory.ptr),
|
|
.manual_align => usable_len: {
|
|
const unaligned_ptr = manualAlignHeader(memory.ptr).*;
|
|
const full_len = mallocSize(unaligned_ptr);
|
|
const padding = @intFromPtr(memory.ptr) - @intFromPtr(unaligned_ptr);
|
|
break :usable_len full_len - padding;
|
|
},
|
|
};
|
|
return new_len <= usable_len;
|
|
}
|
|
|
|
fn remap(
|
|
ctx: *anyopaque,
|
|
memory: []u8,
|
|
alignment: Alignment,
|
|
new_len: usize,
|
|
return_address: usize,
|
|
) ?[*]u8 {
|
|
assert(new_len > 0);
|
|
// Prefer resizing in-place if possible, since `realloc` could be expensive even if legal.
|
|
if (resize(ctx, memory, alignment, new_len, return_address)) {
|
|
return memory.ptr;
|
|
}
|
|
switch (allocStrat(alignment)) {
|
|
.raw => {
|
|
// `malloc` and friends guarantee the required alignment, so we can try `realloc`.
|
|
// C only needs to respect `max_align_t` up to the allocation size due to object
|
|
// alignment rules. If necessary, extend the allocation size.
|
|
const actual_len = @max(new_len, @alignOf(std.c.max_align_t));
|
|
const new_ptr = c.realloc(memory.ptr, actual_len) orelse return null;
|
|
assert(alignment.check(@intFromPtr(new_ptr)));
|
|
return @ptrCast(new_ptr);
|
|
},
|
|
.posix_memalign, .manual_align => {
|
|
// `realloc` would potentially return a new allocation which does not respect
|
|
// the original alignment, so we can't do anything more.
|
|
return null;
|
|
},
|
|
}
|
|
}
|
|
|
|
fn free(
|
|
_: *anyopaque,
|
|
memory: []u8,
|
|
alignment: Alignment,
|
|
return_address: usize,
|
|
) void {
|
|
_ = return_address;
|
|
switch (allocStrat(alignment)) {
|
|
.raw, .posix_memalign => c.free(memory.ptr),
|
|
.manual_align => c.free(manualAlignHeader(memory.ptr).*),
|
|
}
|
|
}
|
|
};
|
|
|
|
/// Asserts that allocations have alignments which `malloc` can satisfy. This means that
|
|
/// the requested alignment is no greater than `@min(@alignOf(std.c.max_align_t), size)`.
|
|
///
|
|
/// This allocator is rarely appropriate to use. In general, prefer `c_allocator`, which
|
|
/// does not have any special requirements of its input, but is still highly efficient for
|
|
/// allocation requests which obey `malloc` alignment rules.
|
|
pub const raw_c_allocator: Allocator = .{
|
|
.ptr = undefined,
|
|
.vtable = &raw_c_allocator_vtable,
|
|
};
|
|
const raw_c_allocator_vtable: Allocator.VTable = .{
|
|
.alloc = rawCAlloc,
|
|
.resize = rawCResize,
|
|
.remap = rawCRemap,
|
|
.free = rawCFree,
|
|
};
|
|
|
|
fn rawCAlloc(
|
|
context: *anyopaque,
|
|
len: usize,
|
|
alignment: Alignment,
|
|
return_address: usize,
|
|
) ?[*]u8 {
|
|
_ = context;
|
|
_ = return_address;
|
|
// `std.c.max_align_t` isn't the whole story, because if `len` is smaller than
|
|
// every C type with alignment `max_align_t`, the allocation can be less-aligned.
|
|
// The implementation need only guarantee that any type of length `len` would be
|
|
// suitably aligned.
|
|
//
|
|
// For instance, if `len == 8` and `alignment == .@"16"`, then `malloc` may not
|
|
// fulfil this request, because there is necessarily no C type with 8-byte size
|
|
// but 16-byte alignment.
|
|
//
|
|
// In theory, the resulting rule here would be target-specific, but in practice,
|
|
// the smallest type with an alignment of `max_align_t` has the same size (it's
|
|
// usually `c_longdouble`), so we can just check that `alignment <= len`.
|
|
assert(alignment.toByteUnits() <= len);
|
|
assert(Alignment.compare(alignment, .lte, .of(std.c.max_align_t)));
|
|
return @ptrCast(c.malloc(len));
|
|
}
|
|
|
|
fn rawCResize(
|
|
context: *anyopaque,
|
|
memory: []u8,
|
|
alignment: Alignment,
|
|
new_len: usize,
|
|
return_address: usize,
|
|
) bool {
|
|
_ = context;
|
|
_ = memory;
|
|
_ = alignment;
|
|
_ = new_len;
|
|
_ = return_address;
|
|
return false;
|
|
}
|
|
|
|
fn rawCRemap(
|
|
context: *anyopaque,
|
|
memory: []u8,
|
|
alignment: Alignment,
|
|
new_len: usize,
|
|
return_address: usize,
|
|
) ?[*]u8 {
|
|
_ = context;
|
|
_ = return_address;
|
|
// See `rawCMalloc` for an explanation of this `assert` call.
|
|
assert(alignment.toByteUnits() <= new_len);
|
|
return @ptrCast(c.realloc(memory.ptr, new_len));
|
|
}
|
|
|
|
fn rawCFree(
|
|
context: *anyopaque,
|
|
memory: []u8,
|
|
alignment: Alignment,
|
|
return_address: usize,
|
|
) void {
|
|
_ = context;
|
|
_ = alignment;
|
|
_ = return_address;
|
|
c.free(memory.ptr);
|
|
}
|
|
|
|
/// On operating systems that support memory mapping, this allocator makes a
|
|
/// syscall directly for every allocation and free.
|
|
///
|
|
/// Otherwise, it falls back to the preferred singleton for the target.
|
|
///
|
|
/// Thread-safe.
|
|
pub const page_allocator: Allocator = if (@hasDecl(root, "os") and
|
|
@hasDecl(root.os, "heap") and
|
|
@hasDecl(root.os.heap, "page_allocator"))
|
|
root.os.heap.page_allocator
|
|
else if (builtin.target.cpu.arch.isWasm()) .{
|
|
.ptr = undefined,
|
|
.vtable = &WasmAllocator.vtable,
|
|
} else if (builtin.target.os.tag == .plan9) .{
|
|
.ptr = undefined,
|
|
.vtable = &SbrkAllocator(std.os.plan9.sbrk).vtable,
|
|
} else .{
|
|
.ptr = undefined,
|
|
.vtable = &PageAllocator.vtable,
|
|
};
|
|
|
|
pub const smp_allocator: Allocator = .{
|
|
.ptr = undefined,
|
|
.vtable = &SmpAllocator.vtable,
|
|
};
|
|
|
|
/// This allocator is fast, small, and specific to WebAssembly. In the future,
|
|
/// this will be the implementation automatically selected by
|
|
/// `GeneralPurposeAllocator` when compiling in `ReleaseSmall` mode for wasm32
|
|
/// and wasm64 architectures.
|
|
/// Until then, it is available here to play with.
|
|
pub const wasm_allocator: Allocator = .{
|
|
.ptr = undefined,
|
|
.vtable = &WasmAllocator.vtable,
|
|
};
|
|
|
|
/// Returns a `StackFallbackAllocator` allocating using either a
|
|
/// `FixedBufferAllocator` on an array of size `size` and falling back to
|
|
/// `fallback_allocator` if that fails.
|
|
pub fn stackFallback(comptime size: usize, fallback_allocator: Allocator) StackFallbackAllocator(size) {
|
|
return StackFallbackAllocator(size){
|
|
.buffer = undefined,
|
|
.fallback_allocator = fallback_allocator,
|
|
.fixed_buffer_allocator = undefined,
|
|
};
|
|
}
|
|
|
|
/// An allocator that attempts to allocate using a
|
|
/// `FixedBufferAllocator` using an array of size `size`. If the
|
|
/// allocation fails, it will fall back to using
|
|
/// `fallback_allocator`. Easily created with `stackFallback`.
|
|
pub fn StackFallbackAllocator(comptime size: usize) type {
|
|
return struct {
|
|
const Self = @This();
|
|
|
|
buffer: [size]u8,
|
|
fallback_allocator: Allocator,
|
|
fixed_buffer_allocator: FixedBufferAllocator,
|
|
get_called: if (std.debug.runtime_safety) bool else void =
|
|
if (std.debug.runtime_safety) false else {},
|
|
|
|
/// This function both fetches a `Allocator` interface to this
|
|
/// allocator *and* resets the internal buffer allocator.
|
|
pub fn get(self: *Self) Allocator {
|
|
if (std.debug.runtime_safety) {
|
|
assert(!self.get_called); // `get` called multiple times; instead use `const allocator = stackFallback(N).get();`
|
|
self.get_called = true;
|
|
}
|
|
self.fixed_buffer_allocator = FixedBufferAllocator.init(self.buffer[0..]);
|
|
return .{
|
|
.ptr = self,
|
|
.vtable = &.{
|
|
.alloc = alloc,
|
|
.resize = resize,
|
|
.remap = remap,
|
|
.free = free,
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Unlike most std allocators `StackFallbackAllocator` modifies
|
|
/// its internal state before returning an implementation of
|
|
/// the`Allocator` interface and therefore also doesn't use
|
|
/// the usual `.allocator()` method.
|
|
pub const allocator = @compileError("use 'const allocator = stackFallback(N).get();' instead");
|
|
|
|
fn alloc(
|
|
ctx: *anyopaque,
|
|
len: usize,
|
|
alignment: Alignment,
|
|
ra: usize,
|
|
) ?[*]u8 {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, alignment, ra) orelse
|
|
return self.fallback_allocator.rawAlloc(len, alignment, ra);
|
|
}
|
|
|
|
fn resize(
|
|
ctx: *anyopaque,
|
|
buf: []u8,
|
|
alignment: Alignment,
|
|
new_len: usize,
|
|
ra: usize,
|
|
) bool {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
|
|
return FixedBufferAllocator.resize(&self.fixed_buffer_allocator, buf, alignment, new_len, ra);
|
|
} else {
|
|
return self.fallback_allocator.rawResize(buf, alignment, new_len, ra);
|
|
}
|
|
}
|
|
|
|
fn remap(
|
|
context: *anyopaque,
|
|
memory: []u8,
|
|
alignment: Alignment,
|
|
new_len: usize,
|
|
return_address: usize,
|
|
) ?[*]u8 {
|
|
const self: *Self = @ptrCast(@alignCast(context));
|
|
if (self.fixed_buffer_allocator.ownsPtr(memory.ptr)) {
|
|
return FixedBufferAllocator.remap(&self.fixed_buffer_allocator, memory, alignment, new_len, return_address);
|
|
} else {
|
|
return self.fallback_allocator.rawRemap(memory, alignment, new_len, return_address);
|
|
}
|
|
}
|
|
|
|
fn free(
|
|
ctx: *anyopaque,
|
|
buf: []u8,
|
|
alignment: Alignment,
|
|
ra: usize,
|
|
) void {
|
|
const self: *Self = @ptrCast(@alignCast(ctx));
|
|
if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
|
|
return FixedBufferAllocator.free(&self.fixed_buffer_allocator, buf, alignment, ra);
|
|
} else {
|
|
return self.fallback_allocator.rawFree(buf, alignment, ra);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
test c_allocator {
|
|
if (builtin.link_libc) {
|
|
try testAllocator(c_allocator);
|
|
try testAllocatorAligned(c_allocator);
|
|
try testAllocatorLargeAlignment(c_allocator);
|
|
try testAllocatorAlignedShrink(c_allocator);
|
|
}
|
|
}
|
|
|
|
test raw_c_allocator {
|
|
if (builtin.link_libc) {
|
|
try testAllocator(raw_c_allocator);
|
|
}
|
|
}
|
|
|
|
test smp_allocator {
|
|
if (builtin.single_threaded) return;
|
|
try testAllocator(smp_allocator);
|
|
try testAllocatorAligned(smp_allocator);
|
|
try testAllocatorLargeAlignment(smp_allocator);
|
|
try testAllocatorAlignedShrink(smp_allocator);
|
|
}
|
|
|
|
test PageAllocator {
|
|
const allocator = page_allocator;
|
|
try testAllocator(allocator);
|
|
try testAllocatorAligned(allocator);
|
|
if (!builtin.target.cpu.arch.isWasm()) {
|
|
try testAllocatorLargeAlignment(allocator);
|
|
try testAllocatorAlignedShrink(allocator);
|
|
}
|
|
|
|
if (builtin.os.tag == .windows) {
|
|
const slice = try allocator.alignedAlloc(u8, .fromByteUnits(page_size_min), 128);
|
|
slice[0] = 0x12;
|
|
slice[127] = 0x34;
|
|
allocator.free(slice);
|
|
}
|
|
{
|
|
var buf = try allocator.alloc(u8, pageSize() + 1);
|
|
defer allocator.free(buf);
|
|
buf = try allocator.realloc(buf, 1); // shrink past the page boundary
|
|
}
|
|
}
|
|
|
|
test ArenaAllocator {
|
|
var arena_allocator = ArenaAllocator.init(page_allocator);
|
|
defer arena_allocator.deinit();
|
|
const allocator = arena_allocator.allocator();
|
|
|
|
try testAllocator(allocator);
|
|
try testAllocatorAligned(allocator);
|
|
try testAllocatorLargeAlignment(allocator);
|
|
try testAllocatorAlignedShrink(allocator);
|
|
}
|
|
|
|
test "StackFallbackAllocator" {
|
|
{
|
|
var stack_allocator = stackFallback(4096, std.testing.allocator);
|
|
try testAllocator(stack_allocator.get());
|
|
}
|
|
{
|
|
var stack_allocator = stackFallback(4096, std.testing.allocator);
|
|
try testAllocatorAligned(stack_allocator.get());
|
|
}
|
|
{
|
|
var stack_allocator = stackFallback(4096, std.testing.allocator);
|
|
try testAllocatorLargeAlignment(stack_allocator.get());
|
|
}
|
|
{
|
|
var stack_allocator = stackFallback(4096, std.testing.allocator);
|
|
try testAllocatorAlignedShrink(stack_allocator.get());
|
|
}
|
|
}
|
|
|
|
/// This one should not try alignments that exceed what C malloc can handle.
|
|
pub fn testAllocator(base_allocator: mem.Allocator) !void {
|
|
var validationAllocator = mem.validationWrap(base_allocator);
|
|
const allocator = validationAllocator.allocator();
|
|
|
|
var slice = try allocator.alloc(*i32, 100);
|
|
try testing.expect(slice.len == 100);
|
|
for (slice, 0..) |*item, i| {
|
|
item.* = try allocator.create(i32);
|
|
item.*.* = @as(i32, @intCast(i));
|
|
}
|
|
|
|
slice = try allocator.realloc(slice, 20000);
|
|
try testing.expect(slice.len == 20000);
|
|
|
|
for (slice[0..100], 0..) |item, i| {
|
|
try testing.expect(item.* == @as(i32, @intCast(i)));
|
|
allocator.destroy(item);
|
|
}
|
|
|
|
if (allocator.resize(slice, 50)) {
|
|
slice = slice[0..50];
|
|
if (allocator.resize(slice, 25)) {
|
|
slice = slice[0..25];
|
|
try testing.expect(allocator.resize(slice, 0));
|
|
slice = slice[0..0];
|
|
slice = try allocator.realloc(slice, 10);
|
|
try testing.expect(slice.len == 10);
|
|
}
|
|
}
|
|
allocator.free(slice);
|
|
|
|
// Zero-length allocation
|
|
const empty = try allocator.alloc(u8, 0);
|
|
allocator.free(empty);
|
|
// Allocation with zero-sized types
|
|
const zero_bit_ptr = try allocator.create(u0);
|
|
zero_bit_ptr.* = 0;
|
|
allocator.destroy(zero_bit_ptr);
|
|
const zero_len_array = try allocator.create([0]u64);
|
|
allocator.destroy(zero_len_array);
|
|
|
|
const oversize = try allocator.alignedAlloc(u32, null, 5);
|
|
try testing.expect(oversize.len >= 5);
|
|
for (oversize) |*item| {
|
|
item.* = 0xDEADBEEF;
|
|
}
|
|
allocator.free(oversize);
|
|
}
|
|
|
|
pub fn testAllocatorAligned(base_allocator: mem.Allocator) !void {
|
|
var validationAllocator = mem.validationWrap(base_allocator);
|
|
const allocator = validationAllocator.allocator();
|
|
|
|
// Test a few alignment values, smaller and bigger than the type's one
|
|
inline for ([_]Alignment{ .@"1", .@"2", .@"4", .@"8", .@"16", .@"32", .@"64" }) |alignment| {
|
|
// initial
|
|
var slice = try allocator.alignedAlloc(u8, alignment, 10);
|
|
try testing.expect(slice.len == 10);
|
|
// grow
|
|
slice = try allocator.realloc(slice, 100);
|
|
try testing.expect(slice.len == 100);
|
|
if (allocator.resize(slice, 10)) {
|
|
slice = slice[0..10];
|
|
}
|
|
try testing.expect(allocator.resize(slice, 0));
|
|
slice = slice[0..0];
|
|
// realloc from zero
|
|
slice = try allocator.realloc(slice, 100);
|
|
try testing.expect(slice.len == 100);
|
|
if (allocator.resize(slice, 10)) {
|
|
slice = slice[0..10];
|
|
}
|
|
try testing.expect(allocator.resize(slice, 0));
|
|
}
|
|
}
|
|
|
|
pub fn testAllocatorLargeAlignment(base_allocator: mem.Allocator) !void {
|
|
var validationAllocator = mem.validationWrap(base_allocator);
|
|
const allocator = validationAllocator.allocator();
|
|
|
|
const large_align: usize = page_size_min / 2;
|
|
|
|
var align_mask: usize = undefined;
|
|
align_mask = @shlWithOverflow(~@as(usize, 0), @as(Allocator.Log2Align, @ctz(large_align)))[0];
|
|
|
|
var slice = try allocator.alignedAlloc(u8, .fromByteUnits(large_align), 500);
|
|
try testing.expect(@intFromPtr(slice.ptr) & align_mask == @intFromPtr(slice.ptr));
|
|
|
|
if (allocator.resize(slice, 100)) {
|
|
slice = slice[0..100];
|
|
}
|
|
|
|
slice = try allocator.realloc(slice, 5000);
|
|
try testing.expect(@intFromPtr(slice.ptr) & align_mask == @intFromPtr(slice.ptr));
|
|
|
|
if (allocator.resize(slice, 10)) {
|
|
slice = slice[0..10];
|
|
}
|
|
|
|
slice = try allocator.realloc(slice, 20000);
|
|
try testing.expect(@intFromPtr(slice.ptr) & align_mask == @intFromPtr(slice.ptr));
|
|
|
|
allocator.free(slice);
|
|
}
|
|
|
|
pub fn testAllocatorAlignedShrink(base_allocator: mem.Allocator) !void {
|
|
var validationAllocator = mem.validationWrap(base_allocator);
|
|
const allocator = validationAllocator.allocator();
|
|
|
|
var debug_buffer: [1000]u8 = undefined;
|
|
var fib = FixedBufferAllocator.init(&debug_buffer);
|
|
const debug_allocator = fib.allocator();
|
|
|
|
const alloc_size = pageSize() * 2 + 50;
|
|
var slice = try allocator.alignedAlloc(u8, .@"16", alloc_size);
|
|
defer allocator.free(slice);
|
|
|
|
var stuff_to_free = std.array_list.Managed([]align(16) u8).init(debug_allocator);
|
|
// On Windows, VirtualAlloc returns addresses aligned to a 64K boundary,
|
|
// which is 16 pages, hence the 32. This test may require to increase
|
|
// the size of the allocations feeding the `allocator` parameter if they
|
|
// fail, because of this high over-alignment we want to have.
|
|
while (@intFromPtr(slice.ptr) == mem.alignForward(usize, @intFromPtr(slice.ptr), pageSize() * 32)) {
|
|
try stuff_to_free.append(slice);
|
|
slice = try allocator.alignedAlloc(u8, .@"16", alloc_size);
|
|
}
|
|
while (stuff_to_free.pop()) |item| {
|
|
allocator.free(item);
|
|
}
|
|
slice[0] = 0x12;
|
|
slice[60] = 0x34;
|
|
|
|
slice = try allocator.reallocAdvanced(slice, alloc_size / 2, 0);
|
|
try testing.expect(slice[0] == 0x12);
|
|
try testing.expect(slice[60] == 0x34);
|
|
}
|
|
|
|
const page_size_min_default: ?usize = switch (builtin.os.tag) {
|
|
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {
|
|
.x86_64 => 4 << 10,
|
|
.aarch64 => 16 << 10,
|
|
else => null,
|
|
},
|
|
.windows => switch (builtin.cpu.arch) {
|
|
// -- <https://devblogs.microsoft.com/oldnewthing/20210510-00/?p=105200>
|
|
.x86, .x86_64 => 4 << 10,
|
|
// SuperH => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 4 << 10,
|
|
.powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
|
|
// DEC Alpha => 8 << 10,
|
|
// Itanium => 8 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
|
|
else => null,
|
|
},
|
|
.wasi => switch (builtin.cpu.arch) {
|
|
.wasm32, .wasm64 => 64 << 10,
|
|
else => null,
|
|
},
|
|
// https://github.com/tianocore/edk2/blob/b158dad150bf02879668f72ce306445250838201/MdePkg/Include/Uefi/UefiBaseType.h#L180-L187
|
|
.uefi => 4 << 10,
|
|
.freebsd => switch (builtin.cpu.arch) {
|
|
// FreeBSD/sys/*
|
|
.x86, .x86_64 => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
|
|
.riscv32, .riscv64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
.netbsd => switch (builtin.cpu.arch) {
|
|
// NetBSD/sys/arch/*
|
|
.alpha => 8 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.hppa => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 4 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
|
|
.sh, .sheb => 4 << 10,
|
|
.sparc => 4 << 10,
|
|
.sparc64 => 8 << 10,
|
|
.riscv32, .riscv64 => 4 << 10,
|
|
// Sun-2
|
|
.m68k => 2 << 10,
|
|
else => null,
|
|
},
|
|
.dragonfly => switch (builtin.cpu.arch) {
|
|
.x86, .x86_64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
.openbsd => switch (builtin.cpu.arch) {
|
|
// OpenBSD/sys/arch/*
|
|
.alpha => 8 << 10,
|
|
.hppa => 4 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
|
|
.mips64, .mips64el => 4 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
|
|
.riscv64 => 4 << 10,
|
|
.sh, .sheb => 4 << 10,
|
|
.sparc64 => 8 << 10,
|
|
else => null,
|
|
},
|
|
.illumos => switch (builtin.cpu.arch) {
|
|
// src/uts/*/sys/machparam.h
|
|
.x86, .x86_64 => 4 << 10,
|
|
.sparc, .sparc64 => 8 << 10,
|
|
else => null,
|
|
},
|
|
.fuchsia => switch (builtin.cpu.arch) {
|
|
// fuchsia/kernel/arch/*/include/arch/defines.h
|
|
.x86_64 => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.riscv64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
// https://github.com/SerenityOS/serenity/blob/62b938b798dc009605b5df8a71145942fc53808b/Kernel/API/POSIX/sys/limits.h#L11-L13
|
|
.serenity => 4 << 10,
|
|
.haiku => switch (builtin.cpu.arch) {
|
|
// haiku/headers/posix/arch/*/limits.h
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.m68k => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 4 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
|
|
.riscv64 => 4 << 10,
|
|
.sparc64 => 8 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
.hurd => switch (builtin.cpu.arch) {
|
|
// gnumach/*/include/mach/*/vm_param.h
|
|
.x86, .x86_64 => 4 << 10,
|
|
.aarch64 => null,
|
|
else => null,
|
|
},
|
|
.plan9 => switch (builtin.cpu.arch) {
|
|
// 9front/sys/src/9/*/mem.h
|
|
.x86, .x86_64 => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 4 << 10,
|
|
.powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
|
|
.sparc => 4 << 10,
|
|
else => null,
|
|
},
|
|
.ps3 => switch (builtin.cpu.arch) {
|
|
// cell/SDK_doc/en/html/C_and_C++_standard_libraries/stdlib.html
|
|
.powerpc64 => 1 << 20, // 1 MiB
|
|
else => null,
|
|
},
|
|
.ps4 => switch (builtin.cpu.arch) {
|
|
// https://github.com/ps4dev/ps4sdk/blob/4df9d001b66ae4ec07d9a51b62d1e4c5e270eecc/include/machine/param.h#L95
|
|
.x86, .x86_64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
.ps5 => switch (builtin.cpu.arch) {
|
|
// https://github.com/PS5Dev/PS5SDK/blob/a2e03a2a0231a3a3397fa6cd087a01ca6d04f273/include/machine/param.h#L95
|
|
.x86, .x86_64 => 16 << 10,
|
|
else => null,
|
|
},
|
|
// system/lib/libc/musl/arch/emscripten/bits/limits.h
|
|
.emscripten => 64 << 10,
|
|
.linux => switch (builtin.cpu.arch) {
|
|
// Linux/arch/*/Kconfig
|
|
.alpha => 8 << 10,
|
|
.arc, .arceb => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.csky => 4 << 10,
|
|
.hexagon => 4 << 10,
|
|
.hppa => 4 << 10,
|
|
.loongarch32, .loongarch64 => 4 << 10,
|
|
.m68k => 4 << 10,
|
|
.microblaze, .microblazeel => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 4 << 10,
|
|
.or1k => 8 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
|
|
.riscv32, .riscv64 => 4 << 10,
|
|
.s390x => 4 << 10,
|
|
.sh, .sheb => 4 << 10,
|
|
.sparc => 4 << 10,
|
|
.sparc64 => 8 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
.xtensa, .xtensaeb => 4 << 10,
|
|
else => null,
|
|
},
|
|
.freestanding, .other => switch (builtin.cpu.arch) {
|
|
.wasm32, .wasm64 => 64 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
else => null,
|
|
},
|
|
else => null,
|
|
};
|
|
|
|
const page_size_max_default: ?usize = switch (builtin.os.tag) {
|
|
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {
|
|
.x86_64 => 4 << 10,
|
|
.aarch64 => 16 << 10,
|
|
else => null,
|
|
},
|
|
.windows => switch (builtin.cpu.arch) {
|
|
// -- <https://devblogs.microsoft.com/oldnewthing/20210510-00/?p=105200>
|
|
.x86, .x86_64 => 4 << 10,
|
|
// SuperH => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 4 << 10,
|
|
.powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
|
|
// DEC Alpha => 8 << 10,
|
|
// Itanium => 8 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
|
|
else => null,
|
|
},
|
|
.wasi => switch (builtin.cpu.arch) {
|
|
.wasm32, .wasm64 => 64 << 10,
|
|
else => null,
|
|
},
|
|
// https://github.com/tianocore/edk2/blob/b158dad150bf02879668f72ce306445250838201/MdePkg/Include/Uefi/UefiBaseType.h#L180-L187
|
|
.uefi => 4 << 10,
|
|
.freebsd => switch (builtin.cpu.arch) {
|
|
// FreeBSD/sys/*
|
|
.x86, .x86_64 => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
|
|
.riscv32, .riscv64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
.netbsd => switch (builtin.cpu.arch) {
|
|
// NetBSD/sys/arch/*
|
|
.alpha => 8 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 64 << 10,
|
|
.hppa => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 16 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 16 << 10,
|
|
.sh, .sheb => 4 << 10,
|
|
.sparc => 8 << 10,
|
|
.sparc64 => 8 << 10,
|
|
.riscv32, .riscv64 => 4 << 10,
|
|
.m68k => 8 << 10,
|
|
else => null,
|
|
},
|
|
.dragonfly => switch (builtin.cpu.arch) {
|
|
.x86, .x86_64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
.openbsd => switch (builtin.cpu.arch) {
|
|
// OpenBSD/sys/arch/*
|
|
.alpha => 8 << 10,
|
|
.hppa => 4 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
|
|
.mips64, .mips64el => 16 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
|
|
.riscv64 => 4 << 10,
|
|
.sh, .sheb => 4 << 10,
|
|
.sparc64 => 8 << 10,
|
|
else => null,
|
|
},
|
|
.illumos => switch (builtin.cpu.arch) {
|
|
// src/uts/*/sys/machparam.h
|
|
.x86, .x86_64 => 4 << 10,
|
|
.sparc, .sparc64 => 8 << 10,
|
|
else => null,
|
|
},
|
|
.fuchsia => switch (builtin.cpu.arch) {
|
|
// fuchsia/kernel/arch/*/include/arch/defines.h
|
|
.x86_64 => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.riscv64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
// https://github.com/SerenityOS/serenity/blob/62b938b798dc009605b5df8a71145942fc53808b/Kernel/API/POSIX/sys/limits.h#L11-L13
|
|
.serenity => 4 << 10,
|
|
.haiku => switch (builtin.cpu.arch) {
|
|
// haiku/headers/posix/arch/*/limits.h
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 4 << 10,
|
|
.m68k => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 4 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
|
|
.riscv64 => 4 << 10,
|
|
.sparc64 => 8 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
.hurd => switch (builtin.cpu.arch) {
|
|
// gnumach/*/include/mach/*/vm_param.h
|
|
.x86, .x86_64 => 4 << 10,
|
|
.aarch64 => null,
|
|
else => null,
|
|
},
|
|
.plan9 => switch (builtin.cpu.arch) {
|
|
// 9front/sys/src/9/*/mem.h
|
|
.x86, .x86_64 => 4 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 64 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 16 << 10,
|
|
.powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
|
|
.sparc => 4 << 10,
|
|
else => null,
|
|
},
|
|
.ps3 => switch (builtin.cpu.arch) {
|
|
// cell/SDK_doc/en/html/C_and_C++_standard_libraries/stdlib.html
|
|
.powerpc64 => 1 << 20, // 1 MiB
|
|
else => null,
|
|
},
|
|
.ps4 => switch (builtin.cpu.arch) {
|
|
// https://github.com/ps4dev/ps4sdk/blob/4df9d001b66ae4ec07d9a51b62d1e4c5e270eecc/include/machine/param.h#L95
|
|
.x86, .x86_64 => 4 << 10,
|
|
else => null,
|
|
},
|
|
.ps5 => switch (builtin.cpu.arch) {
|
|
// https://github.com/PS5Dev/PS5SDK/blob/a2e03a2a0231a3a3397fa6cd087a01ca6d04f273/include/machine/param.h#L95
|
|
.x86, .x86_64 => 16 << 10,
|
|
else => null,
|
|
},
|
|
// system/lib/libc/musl/arch/emscripten/bits/limits.h
|
|
.emscripten => 64 << 10,
|
|
.linux => switch (builtin.cpu.arch) {
|
|
// Linux/arch/*/Kconfig
|
|
.alpha => 8 << 10,
|
|
.arc, .arceb => 16 << 10,
|
|
.thumb, .thumbeb, .arm, .armeb => 4 << 10,
|
|
.aarch64, .aarch64_be => 64 << 10,
|
|
.csky => 4 << 10,
|
|
.hexagon => 256 << 10,
|
|
.hppa => 64 << 10,
|
|
.loongarch32, .loongarch64 => 64 << 10,
|
|
.m68k => 8 << 10,
|
|
.microblaze, .microblazeel => 4 << 10,
|
|
.mips, .mipsel, .mips64, .mips64el => 64 << 10,
|
|
.or1k => 8 << 10,
|
|
.powerpc, .powerpc64, .powerpc64le, .powerpcle => 256 << 10,
|
|
.riscv32, .riscv64 => 4 << 10,
|
|
.s390x => 4 << 10,
|
|
.sh, .sheb => 64 << 10,
|
|
.sparc => 4 << 10,
|
|
.sparc64 => 8 << 10,
|
|
.x86, .x86_64 => 4 << 10,
|
|
.xtensa, .xtensaeb => 4 << 10,
|
|
else => null,
|
|
},
|
|
.freestanding => switch (builtin.cpu.arch) {
|
|
.wasm32, .wasm64 => 64 << 10,
|
|
else => null,
|
|
},
|
|
else => null,
|
|
};
|
|
|
|
test {
|
|
_ = @import("heap/memory_pool.zig");
|
|
_ = ArenaAllocator;
|
|
_ = GeneralPurposeAllocator;
|
|
_ = FixedBufferAllocator;
|
|
_ = ThreadSafeAllocator;
|
|
_ = SbrkAllocator;
|
|
if (builtin.target.cpu.arch.isWasm()) {
|
|
_ = WasmAllocator;
|
|
}
|
|
if (!builtin.single_threaded) _ = smp_allocator;
|
|
}
|