authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-29 14:16:25-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-06 14:23:23-08:00
log284de7d957037c8a7032bd6e2a95bd5f55b73666
tree852d4ef1e8fd8c052485fd38d0dcdfafd28594bb
parent439667be0476f5bf60f3efbb82a3c4d5aae96ee4

adjust runtime page size APIs

* fix merge conflicts * rename the declarations * reword documentation * extract FixedBufferAllocator to separate file * take advantage of locals * remove the assertion about max alignment in Allocator API, leaving it Allocator implementation defined * fix non-inline function call in start logic The GeneralPurposeAllocator implementation is totally broken because it uses global state but I didn't address that in this commit.

23 files changed, 703 insertions(+), 698 deletions(-)

lib/fuzzer.zig+1-1
......@@ -480,7 +480,7 @@ pub const MemoryMappedList = struct {
480480 /// of this ArrayList in accordance with the respective documentation. In
481481 /// all cases, "invalidated" means that the memory has been passed to this
482482 /// allocator's resize or free function.
483 items: []align(std.heap.min_page_size) volatile u8,
483 items: []align(std.heap.page_size_min) volatile u8,
484484 /// How many bytes this list can hold without allocating additional memory.
485485 capacity: usize,
486486
lib/std/Build/Fuzz/WebServer.zig+1-1
......@@ -41,7 +41,7 @@ const fuzzer_arch_os_abi = "wasm32-freestanding";
4141const fuzzer_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
4242
4343const CoverageMap = struct {
44 mapped_memory: []align(std.heap.min_page_size) const u8,
44 mapped_memory: []align(std.heap.page_size_min) const u8,
4545 coverage: Coverage,
4646 source_locations: []Coverage.SourceLocation,
4747 /// Elements are indexes into `source_locations` pointing to the unit tests that are being fuzz tested.
lib/std/Thread.zig+1-1
......@@ -1155,7 +1155,7 @@ const LinuxThreadImpl = struct {
11551155 completion: Completion = Completion.init(.running),
11561156 child_tid: std.atomic.Value(i32) = std.atomic.Value(i32).init(1),
11571157 parent_tid: i32 = undefined,
1158 mapped: []align(std.heap.min_page_size) u8,
1158 mapped: []align(std.heap.page_size_min) u8,
11591159
11601160 /// Calls `munmap(mapped.ptr, mapped.len)` then `exit(1)` without touching the stack (which lives in `mapped.ptr`).
11611161 /// Ported over from musl libc's pthread detached implementation:
lib/std/c.zig+10-10
......@@ -3,7 +3,7 @@ const builtin = @import("builtin");
33const c = @This();
44const maxInt = std.math.maxInt;
55const assert = std.debug.assert;
6const min_page_size = std.heap.min_page_size;
6const page_size = std.heap.page_size_min;
77const native_abi = builtin.abi;
88const native_arch = builtin.cpu.arch;
99const native_os = builtin.os.tag;
......@@ -2229,7 +2229,7 @@ pub const SC = switch (native_os) {
22292229};
22302230
22312231pub const _SC = switch (native_os) {
2232 .bridgeos, .driverkit, .ios, .macos, .tvos, .visionos, .watchos => enum(c_int) {
2232 .driverkit, .ios, .macos, .tvos, .visionos, .watchos => enum(c_int) {
22332233 PAGESIZE = 29,
22342234 },
22352235 .dragonfly => enum(c_int) {
......@@ -9265,7 +9265,7 @@ pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
92659265pub extern "c" fn getpwuid(uid: uid_t) ?*passwd;
92669266pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int;
92679267pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64;
9268pub extern "c" fn mmap64(addr: ?*align(min_page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;
9268pub extern "c" fn mmap64(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;
92699269pub extern "c" fn open64(path: [*:0]const u8, oflag: O, ...) c_int;
92709270pub extern "c" fn openat64(fd: c_int, path: [*:0]const u8, oflag: O, ...) c_int;
92719271pub extern "c" fn pread64(fd: fd_t, buf: [*]u8, nbyte: usize, offset: i64) isize;
......@@ -9357,13 +9357,13 @@ pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) c_int;
93579357
93589358pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int;
93599359pub extern "c" fn mincore(
9360 addr: *align(min_page_size) anyopaque,
9360 addr: *align(page_size) anyopaque,
93619361 length: usize,
93629362 vec: [*]u8,
93639363) c_int;
93649364
93659365pub extern "c" fn madvise(
9366 addr: *align(min_page_size) anyopaque,
9366 addr: *align(page_size) anyopaque,
93679367 length: usize,
93689368 advice: u32,
93699369) c_int;
......@@ -9506,9 +9506,9 @@ pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) i
95069506pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: off_t) isize;
95079507pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;
95089508pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: off_t) isize;
9509pub extern "c" fn mmap(addr: ?*align(min_page_size) anyopaque, len: usize, prot: c_uint, flags: MAP, fd: fd_t, offset: off_t) *anyopaque;
9510pub extern "c" fn munmap(addr: *align(min_page_size) const anyopaque, len: usize) c_int;
9511pub extern "c" fn mprotect(addr: *align(min_page_size) anyopaque, len: usize, prot: c_uint) c_int;
9509pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: MAP, fd: fd_t, offset: off_t) *anyopaque;
9510pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;
9511pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int;
95129512pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8) c_int;
95139513pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_int) c_int;
95149514pub extern "c" fn unlink(path: [*:0]const u8) c_int;
......@@ -10191,7 +10191,7 @@ const private = struct {
1019110191 };
1019210192 extern "c" fn getrusage(who: c_int, usage: *rusage) c_int;
1019310193 extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
10194 extern "c" fn msync(addr: *align(min_page_size) const anyopaque, len: usize, flags: c_int) c_int;
10194 extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
1019510195 extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
1019610196 extern "c" fn pipe2(fds: *[2]fd_t, flags: O) c_int;
1019710197 extern "c" fn readdir(dir: *DIR) ?*dirent;
......@@ -10239,7 +10239,7 @@ const private = struct {
1023910239 extern "c" fn __getrusage50(who: c_int, usage: *rusage) c_int;
1024010240 extern "c" fn __gettimeofday50(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
1024110241 extern "c" fn __libc_thr_yield() c_int;
10242 extern "c" fn __msync13(addr: *align(min_page_size) const anyopaque, len: usize, flags: c_int) c_int;
10242 extern "c" fn __msync13(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
1024310243 extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int;
1024410244 extern "c" fn __sigaction14(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
1024510245 extern "c" fn __sigfillset14(set: ?*sigset_t) void;
lib/std/crypto/tlcsprng.zig+5-6
......@@ -6,7 +6,6 @@
66const std = @import("std");
77const builtin = @import("builtin");
88const mem = std.mem;
9const heap = std.heap;
109const native_os = builtin.os.tag;
1110const posix = std.posix;
1211
......@@ -43,7 +42,7 @@ var install_atfork_handler = std.once(struct {
4342 }
4443}.do);
4544
46threadlocal var wipe_mem: []align(heap.min_page_size) u8 = &[_]u8{};
45threadlocal var wipe_mem: []align(std.heap.page_size_min) u8 = &[_]u8{};
4746
4847fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
4948 if (os_has_arc4random) {
......@@ -78,7 +77,7 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
7877 } else {
7978 // Use a static thread-local buffer.
8079 const S = struct {
81 threadlocal var buf: Context align(heap.min_page_size) = .{
80 threadlocal var buf: Context align(std.heap.page_size_min) = .{
8281 .init_state = .uninitialized,
8382 .rng = undefined,
8483 };
......@@ -86,7 +85,7 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
8685 wipe_mem = mem.asBytes(&S.buf);
8786 }
8887 }
89 const ctx = @as(*Context, @ptrCast(wipe_mem.ptr));
88 const ctx: *Context = @ptrCast(wipe_mem.ptr);
9089
9190 switch (ctx.init_state) {
9291 .uninitialized => {
......@@ -142,7 +141,7 @@ fn childAtForkHandler() callconv(.c) void {
142141}
143142
144143fn fillWithCsprng(buffer: []u8) void {
145 const ctx = @as(*Context, @ptrCast(wipe_mem.ptr));
144 const ctx: *Context = @ptrCast(wipe_mem.ptr);
146145 return ctx.rng.fill(buffer);
147146}
148147
......@@ -158,7 +157,7 @@ fn initAndFill(buffer: []u8) void {
158157 // the `std.options.cryptoRandomSeed` function is provided.
159158 std.options.cryptoRandomSeed(&seed);
160159
161 const ctx = @as(*Context, @ptrCast(wipe_mem.ptr));
160 const ctx: *Context = @ptrCast(wipe_mem.ptr);
162161 ctx.rng = Rng.init(seed);
163162 std.crypto.secureZero(u8, &seed);
164163
lib/std/debug.zig+7-8
......@@ -2,7 +2,6 @@ const builtin = @import("builtin");
22const std = @import("std.zig");
33const math = std.math;
44const mem = std.mem;
5const heap = std.heap;
65const io = std.io;
76const posix = std.posix;
87const fs = std.fs;
......@@ -1238,7 +1237,7 @@ test printLineFromFileAnyOs {
12381237
12391238 const overlap = 10;
12401239 var writer = file.writer();
1241 try writer.writeByteNTimes('a', heap.min_page_size - overlap);
1240 try writer.writeByteNTimes('a', std.heap.page_size_min - overlap);
12421241 try writer.writeByte('\n');
12431242 try writer.writeByteNTimes('a', overlap);
12441243
......@@ -1253,10 +1252,10 @@ test printLineFromFileAnyOs {
12531252 defer allocator.free(path);
12541253
12551254 var writer = file.writer();
1256 try writer.writeByteNTimes('a', heap.max_page_size);
1255 try writer.writeByteNTimes('a', std.heap.page_size_max);
12571256
12581257 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1259 try expectEqualStrings(("a" ** heap.max_page_size) ++ "\n", output.items);
1258 try expectEqualStrings(("a" ** std.heap.page_size_max) ++ "\n", output.items);
12601259 output.clearRetainingCapacity();
12611260 }
12621261 {
......@@ -1266,18 +1265,18 @@ test printLineFromFileAnyOs {
12661265 defer allocator.free(path);
12671266
12681267 var writer = file.writer();
1269 try writer.writeByteNTimes('a', 3 * heap.max_page_size);
1268 try writer.writeByteNTimes('a', 3 * std.heap.page_size_max);
12701269
12711270 try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
12721271
12731272 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1274 try expectEqualStrings(("a" ** (3 * heap.max_page_size)) ++ "\n", output.items);
1273 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "\n", output.items);
12751274 output.clearRetainingCapacity();
12761275
12771276 try writer.writeAll("a\na");
12781277
12791278 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1280 try expectEqualStrings(("a" ** (3 * heap.max_page_size)) ++ "a\n", output.items);
1279 try expectEqualStrings(("a" ** (3 * std.heap.page_size_max)) ++ "a\n", output.items);
12811280 output.clearRetainingCapacity();
12821281
12831282 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
......@@ -1291,7 +1290,7 @@ test printLineFromFileAnyOs {
12911290 defer allocator.free(path);
12921291
12931292 var writer = file.writer();
1294 const real_file_start = 3 * heap.min_page_size;
1293 const real_file_start = 3 * std.heap.page_size_min;
12951294 try writer.writeByteNTimes('\n', real_file_start);
12961295 try writer.writeAll("abc\ndef");
12971296
lib/std/debug/Dwarf.zig+5-5
......@@ -2120,8 +2120,8 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
21202120pub const ElfModule = struct {
21212121 base_address: usize,
21222122 dwarf: Dwarf,
2123 mapped_memory: []align(std.heap.min_page_size) const u8,
2124 external_mapped_memory: ?[]align(std.heap.min_page_size) const u8,
2123 mapped_memory: []align(std.heap.page_size_min) const u8,
2124 external_mapped_memory: ?[]align(std.heap.page_size_min) const u8,
21252125
21262126 pub fn deinit(self: *@This(), allocator: Allocator) void {
21272127 self.dwarf.deinit(allocator);
......@@ -2167,11 +2167,11 @@ pub const ElfModule = struct {
21672167 /// sections from an external file.
21682168 pub fn load(
21692169 gpa: Allocator,
2170 mapped_mem: []align(std.heap.min_page_size) const u8,
2170 mapped_mem: []align(std.heap.page_size_min) const u8,
21712171 build_id: ?[]const u8,
21722172 expected_crc: ?u32,
21732173 parent_sections: *Dwarf.SectionArray,
2174 parent_mapped_mem: ?[]align(std.heap.min_page_size) const u8,
2174 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
21752175 elf_filename: ?[]const u8,
21762176 ) LoadError!Dwarf.ElfModule {
21772177 if (expected_crc) |crc| if (crc != std.hash.crc.Crc32.hash(mapped_mem)) return error.InvalidDebugInfo;
......@@ -2423,7 +2423,7 @@ pub const ElfModule = struct {
24232423 build_id: ?[]const u8,
24242424 expected_crc: ?u32,
24252425 parent_sections: *Dwarf.SectionArray,
2426 parent_mapped_mem: ?[]align(std.heap.min_page_size) const u8,
2426 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
24272427 ) LoadError!Dwarf.ElfModule {
24282428 const elf_file = elf_file_path.root_dir.handle.openFile(elf_file_path.sub_path, .{}) catch |err| switch (err) {
24292429 error.FileNotFound => return missing(),
lib/std/debug/MemoryAccessor.zig+2-2
......@@ -7,7 +7,7 @@ const native_os = builtin.os.tag;
77const std = @import("../std.zig");
88const posix = std.posix;
99const File = std.fs.File;
10const min_page_size = std.heap.min_page_size;
10const page_size_min = std.heap.page_size_min;
1111
1212const MemoryAccessor = @This();
1313
......@@ -96,7 +96,7 @@ pub fn isValidMemory(address: usize) bool {
9696 const page_size = std.heap.pageSize();
9797 const aligned_address = address & ~(page_size - 1);
9898 if (aligned_address == 0) return false;
99 const aligned_memory = @as([*]align(min_page_size) u8, @ptrFromInt(aligned_address))[0..page_size];
99 const aligned_memory = @as([*]align(page_size_min) u8, @ptrFromInt(aligned_address))[0..page_size];
100100
101101 if (native_os == .windows) {
102102 const windows = std.os.windows;
lib/std/debug/SelfInfo.zig+3-3
......@@ -504,7 +504,7 @@ pub const Module = switch (native_os) {
504504 .macos, .ios, .watchos, .tvos, .visionos => struct {
505505 base_address: usize,
506506 vmaddr_slide: usize,
507 mapped_memory: []align(std.heap.min_page_size) const u8,
507 mapped_memory: []align(std.heap.page_size_min) const u8,
508508 symbols: []const MachoSymbol,
509509 strings: [:0]const u8,
510510 ofiles: OFileTable,
......@@ -1046,7 +1046,7 @@ pub fn readElfDebugInfo(
10461046 build_id: ?[]const u8,
10471047 expected_crc: ?u32,
10481048 parent_sections: *Dwarf.SectionArray,
1049 parent_mapped_mem: ?[]align(std.heap.min_page_size) const u8,
1049 parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
10501050) !Dwarf.ElfModule {
10511051 nosuspend {
10521052 const elf_file = (if (elf_filename) |filename| blk: {
......@@ -1088,7 +1088,7 @@ const MachoSymbol = struct {
10881088
10891089/// Takes ownership of file, even on error.
10901090/// TODO it's weird to take ownership even on error, rework this code.
1091fn mapWholeFile(file: File) ![]align(std.heap.min_page_size) const u8 {
1091fn mapWholeFile(file: File) ![]align(std.heap.page_size_min) const u8 {
10921092 nosuspend {
10931093 defer file.close();
10941094
lib/std/dynamic_library.zig+7-6
......@@ -1,7 +1,6 @@
11const std = @import("std.zig");
22const builtin = @import("builtin");
33const mem = std.mem;
4const heap = std.heap;
54const testing = std.testing;
65const elf = std.elf;
76const windows = std.os.windows;
......@@ -144,7 +143,7 @@ pub const ElfDynLib = struct {
144143 hashtab: [*]posix.Elf_Symndx,
145144 versym: ?[*]elf.Versym,
146145 verdef: ?*elf.Verdef,
147 memory: []align(heap.min_page_size) u8,
146 memory: []align(std.heap.page_size_min) u8,
148147
149148 pub const Error = ElfDynLibError;
150149
......@@ -220,11 +219,13 @@ pub const ElfDynLib = struct {
220219 const stat = try file.stat();
221220 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
222221
222 const page_size = std.heap.pageSize();
223
223224 // This one is to read the ELF info. We do more mmapping later
224225 // corresponding to the actual LOAD sections.
225226 const file_bytes = try posix.mmap(
226227 null,
227 mem.alignForward(usize, size, heap.pageSize()),
228 mem.alignForward(usize, size, page_size),
228229 posix.PROT.READ,
229230 .{ .TYPE = .PRIVATE },
230231 fd,
......@@ -285,10 +286,10 @@ pub const ElfDynLib = struct {
285286 elf.PT_LOAD => {
286287 // The VirtAddr may not be page-aligned; in such case there will be
287288 // extra nonsense mapped before/after the VirtAddr,MemSiz
288 const aligned_addr = (base + ph.p_vaddr) & ~(@as(usize, heap.pageSize()) - 1);
289 const aligned_addr = (base + ph.p_vaddr) & ~(@as(usize, page_size) - 1);
289290 const extra_bytes = (base + ph.p_vaddr) - aligned_addr;
290 const extended_memsz = mem.alignForward(usize, ph.p_memsz + extra_bytes, heap.pageSize());
291 const ptr = @as([*]align(heap.min_page_size) u8, @ptrFromInt(aligned_addr));
291 const extended_memsz = mem.alignForward(usize, ph.p_memsz + extra_bytes, page_size);
292 const ptr = @as([*]align(std.heap.page_size_min) u8, @ptrFromInt(aligned_addr));
292293 const prot = elfToMmapProt(ph.p_flags);
293294 if ((ph.p_flags & elf.PF_W) == 0) {
294295 // If it does not need write access, it can be mapped from the fd.
lib/std/heap.zig+367-586
......@@ -8,337 +8,79 @@ const c = std.c;
88const Allocator = std.mem.Allocator;
99const windows = std.os.windows;
1010
11const default_min_page_size: ?usize = switch (builtin.os.tag) {
12 .bridgeos, .driverkit, .ios, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {
13 .x86_64 => 4 << 10,
14 .aarch64 => 16 << 10,
15 else => null,
16 },
17 .windows => switch (builtin.cpu.arch) {
18 // -- <https://devblogs.microsoft.com/oldnewthing/20210510-00/?p=105200>
19 .x86, .x86_64 => 4 << 10,
20 // SuperH => 4 << 10,
21 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
22 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
23 // DEC Alpha => 8 << 10,
24 // Itanium => 8 << 10,
25 .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
26 else => null,
27 },
28 .wasi => switch (builtin.cpu.arch) {
29 .wasm32, .wasm64 => 64 << 10,
30 else => null,
31 },
32 // https://github.com/tianocore/edk2/blob/b158dad150bf02879668f72ce306445250838201/MdePkg/Include/Uefi/UefiBaseType.h#L180-L187
33 .uefi => 4 << 10,
34 .freebsd => switch (builtin.cpu.arch) {
35 // FreeBSD/sys/*
36 .x86, .x86_64 => 4 << 10,
37 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
38 .aarch64, .aarch64_be => 4 << 10,
39 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
40 .riscv32, .riscv64 => 4 << 10,
41 else => null,
42 },
43 .netbsd => switch (builtin.cpu.arch) {
44 // NetBSD/sys/arch/*
45 .x86, .x86_64 => 4 << 10,
46 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
47 .aarch64, .aarch64_be => 4 << 10,
48 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
49 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
50 .sparc => 4 << 10,
51 .sparc64 => 8 << 10,
52 .riscv32, .riscv64 => 4 << 10,
53 // Sun-2
54 .m68k => 2 << 10,
55 else => null,
56 },
57 .dragonfly => switch (builtin.cpu.arch) {
58 .x86, .x86_64 => 4 << 10,
59 else => null,
60 },
61 .openbsd => switch (builtin.cpu.arch) {
62 // OpenBSD/sys/arch/*
63 .x86, .x86_64 => 4 << 10,
64 .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
65 .mips64, .mips64el => 4 << 10,
66 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
67 .riscv64 => 4 << 10,
68 .sparc64 => 8 << 10,
69 else => null,
70 },
71 .solaris, .illumos => switch (builtin.cpu.arch) {
72 // src/uts/*/sys/machparam.h
73 .x86, .x86_64 => 4 << 10,
74 .sparc, .sparc64 => 8 << 10,
75 else => null,
76 },
77 .fuchsia => switch (builtin.cpu.arch) {
78 // fuchsia/kernel/arch/*/include/arch/defines.h
79 .x86_64 => 4 << 10,
80 .aarch64, .aarch64_be => 4 << 10,
81 .riscv64 => 4 << 10,
82 else => null,
83 },
84 // https://github.com/SerenityOS/serenity/blob/62b938b798dc009605b5df8a71145942fc53808b/Kernel/API/POSIX/sys/limits.h#L11-L13
85 .serenity => 4 << 10,
86 .haiku => switch (builtin.cpu.arch) {
87 // haiku/headers/posix/arch/*/limits.h
88 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
89 .aarch64, .aarch64_be => 4 << 10,
90 .m68k => 4 << 10,
91 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
92 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
93 .riscv64 => 4 << 10,
94 .sparc64 => 8 << 10,
95 .x86, .x86_64 => 4 << 10,
96 else => null,
97 },
98 .hurd => switch (builtin.cpu.arch) {
99 // gnumach/*/include/mach/*/vm_param.h
100 .x86, .x86_64 => 4 << 10,
101 .aarch64 => null,
102 else => null,
103 },
104 .plan9 => switch (builtin.cpu.arch) {
105 // 9front/sys/src/9/*/mem.h
106 .x86, .x86_64 => 4 << 10,
107 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
108 .aarch64, .aarch64_be => 4 << 10,
109 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
110 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
111 .sparc => 4 << 10,
112 else => null,
113 },
114 .ps3 => switch (builtin.cpu.arch) {
115 // cell/SDK_doc/en/html/C_and_C++_standard_libraries/stdlib.html
116 .powerpc64 => 1 << 20, // 1 MiB
117 else => null,
118 },
119 .ps4 => switch (builtin.cpu.arch) {
120 // https://github.com/ps4dev/ps4sdk/blob/4df9d001b66ae4ec07d9a51b62d1e4c5e270eecc/include/machine/param.h#L95
121 .x86, .x86_64 => 4 << 10,
122 else => null,
123 },
124 .ps5 => switch (builtin.cpu.arch) {
125 // https://github.com/PS5Dev/PS5SDK/blob/a2e03a2a0231a3a3397fa6cd087a01ca6d04f273/include/machine/param.h#L95
126 .x86, .x86_64 => 16 << 10,
127 else => null,
128 },
129 // system/lib/libc/musl/arch/emscripten/bits/limits.h
130 .emscripten => 64 << 10,
131 .linux => switch (builtin.cpu.arch) {
132 // Linux/arch/*/Kconfig
133 .arc => 4 << 10,
134 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
135 .aarch64, .aarch64_be => 4 << 10,
136 .csky => 4 << 10,
137 .hexagon => 4 << 10,
138 .loongarch32, .loongarch64 => 4 << 10,
139 .m68k => 4 << 10,
140 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
141 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
142 .riscv32, .riscv64 => 4 << 10,
143 .s390x => 4 << 10,
144 .sparc => 4 << 10,
145 .sparc64 => 8 << 10,
146 .x86, .x86_64 => 4 << 10,
147 .xtensa => 4 << 10,
148 else => null,
149 },
150 .freestanding => switch (builtin.cpu.arch) {
151 .wasm32, .wasm64 => 64 << 10,
152 else => null,
153 },
154 else => null,
155};
11pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
12pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
13pub const ScopedLoggingAllocator = @import("heap/logging_allocator.zig").ScopedLoggingAllocator;
14pub const LogToWriterAllocator = @import("heap/log_to_writer_allocator.zig").LogToWriterAllocator;
15pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator;
16pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
17pub const GeneralPurposeAllocatorConfig = @import("heap/general_purpose_allocator.zig").Config;
18pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
19pub const Check = @import("heap/general_purpose_allocator.zig").Check;
20pub const WasmAllocator = @import("heap/WasmAllocator.zig");
21pub const PageAllocator = @import("heap/PageAllocator.zig");
22pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig");
23pub const SbrkAllocator = @import("heap/sbrk_allocator.zig").SbrkAllocator;
24pub const FixedBufferAllocator = @import("heap/FixedBufferAllocator.zig");
15625
157const default_max_page_size: ?usize = switch (builtin.os.tag) {
158 .bridgeos, .driverkit, .ios, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {
159 .x86_64 => 4 << 10,
160 .aarch64 => 16 << 10,
161 else => null,
162 },
163 .windows => switch (builtin.cpu.arch) {
164 // -- <https://devblogs.microsoft.com/oldnewthing/20210510-00/?p=105200>
165 .x86, .x86_64 => 4 << 10,
166 // SuperH => 4 << 10,
167 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
168 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
169 // DEC Alpha => 8 << 10,
170 // Itanium => 8 << 10,
171 .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
172 else => null,
173 },
174 .wasi => switch (builtin.cpu.arch) {
175 .wasm32, .wasm64 => 64 << 10,
176 else => null,
177 },
178 // https://github.com/tianocore/edk2/blob/b158dad150bf02879668f72ce306445250838201/MdePkg/Include/Uefi/UefiBaseType.h#L180-L187
179 .uefi => 4 << 10,
180 .freebsd => switch (builtin.cpu.arch) {
181 // FreeBSD/sys/*
182 .x86, .x86_64 => 4 << 10,
183 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
184 .aarch64, .aarch64_be => 4 << 10,
185 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
186 .riscv32, .riscv64 => 4 << 10,
187 else => null,
188 },
189 .netbsd => switch (builtin.cpu.arch) {
190 // NetBSD/sys/arch/*
191 .x86, .x86_64 => 4 << 10,
192 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
193 .aarch64, .aarch64_be => 64 << 10,
194 .mips, .mipsel, .mips64, .mips64el => 16 << 10,
195 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 16 << 10,
196 .sparc => 8 << 10,
197 .sparc64 => 8 << 10,
198 .riscv32, .riscv64 => 4 << 10,
199 .m68k => 8 << 10,
200 else => null,
201 },
202 .dragonfly => switch (builtin.cpu.arch) {
203 .x86, .x86_64 => 4 << 10,
204 else => null,
205 },
206 .openbsd => switch (builtin.cpu.arch) {
207 // OpenBSD/sys/arch/*
208 .x86, .x86_64 => 4 << 10,
209 .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
210 .mips64, .mips64el => 16 << 10,
211 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
212 .riscv64 => 4 << 10,
213 .sparc64 => 8 << 10,
214 else => null,
215 },
216 .solaris, .illumos => switch (builtin.cpu.arch) {
217 // src/uts/*/sys/machparam.h
218 .x86, .x86_64 => 4 << 10,
219 .sparc, .sparc64 => 8 << 10,
220 else => null,
221 },
222 .fuchsia => switch (builtin.cpu.arch) {
223 // fuchsia/kernel/arch/*/include/arch/defines.h
224 .x86_64 => 4 << 10,
225 .aarch64, .aarch64_be => 4 << 10,
226 .riscv64 => 4 << 10,
227 else => null,
228 },
229 // https://github.com/SerenityOS/serenity/blob/62b938b798dc009605b5df8a71145942fc53808b/Kernel/API/POSIX/sys/limits.h#L11-L13
230 .serenity => 4 << 10,
231 .haiku => switch (builtin.cpu.arch) {
232 // haiku/headers/posix/arch/*/limits.h
233 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
234 .aarch64, .aarch64_be => 4 << 10,
235 .m68k => 4 << 10,
236 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
237 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
238 .riscv64 => 4 << 10,
239 .sparc64 => 8 << 10,
240 .x86, .x86_64 => 4 << 10,
241 else => null,
242 },
243 .hurd => switch (builtin.cpu.arch) {
244 // gnumach/*/include/mach/*/vm_param.h
245 .x86, .x86_64 => 4 << 10,
246 .aarch64 => null,
247 else => null,
248 },
249 .plan9 => switch (builtin.cpu.arch) {
250 // 9front/sys/src/9/*/mem.h
251 .x86, .x86_64 => 4 << 10,
252 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
253 .aarch64, .aarch64_be => 64 << 10,
254 .mips, .mipsel, .mips64, .mips64el => 16 << 10,
255 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
256 .sparc => 4 << 10,
257 else => null,
258 },
259 .ps3 => switch (builtin.cpu.arch) {
260 // cell/SDK_doc/en/html/C_and_C++_standard_libraries/stdlib.html
261 .powerpc64 => 1 << 20, // 1 MiB
262 else => null,
263 },
264 .ps4 => switch (builtin.cpu.arch) {
265 // https://github.com/ps4dev/ps4sdk/blob/4df9d001b66ae4ec07d9a51b62d1e4c5e270eecc/include/machine/param.h#L95
266 .x86, .x86_64 => 4 << 10,
267 else => null,
268 },
269 .ps5 => switch (builtin.cpu.arch) {
270 // https://github.com/PS5Dev/PS5SDK/blob/a2e03a2a0231a3a3397fa6cd087a01ca6d04f273/include/machine/param.h#L95
271 .x86, .x86_64 => 16 << 10,
272 else => null,
273 },
274 // system/lib/libc/musl/arch/emscripten/bits/limits.h
275 .emscripten => 64 << 10,
276 .linux => switch (builtin.cpu.arch) {
277 // Linux/arch/*/Kconfig
278 .arc => 16 << 10,
279 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
280 .aarch64, .aarch64_be => 64 << 10,
281 .csky => 4 << 10,
282 .hexagon => 256 << 10,
283 .loongarch32, .loongarch64 => 64 << 10,
284 .m68k => 8 << 10,
285 .mips, .mipsel, .mips64, .mips64el => 64 << 10,
286 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 256 << 10,
287 .riscv32, .riscv64 => 4 << 10,
288 .s390x => 4 << 10,
289 .sparc => 4 << 10,
290 .sparc64 => 8 << 10,
291 .x86, .x86_64 => 4 << 10,
292 .xtensa => 4 << 10,
293 else => null,
294 },
295 .freestanding => switch (builtin.cpu.arch) {
296 .wasm32, .wasm64 => 64 << 10,
297 else => null,
298 },
299 else => null,
300};
26const memory_pool = @import("heap/memory_pool.zig");
27pub const MemoryPool = memory_pool.MemoryPool;
28pub const MemoryPoolAligned = memory_pool.MemoryPoolAligned;
29pub const MemoryPoolExtra = memory_pool.MemoryPoolExtra;
30pub const MemoryPoolOptions = memory_pool.Options;
31
32/// TODO Utilize this on Windows.
33pub var next_mmap_addr_hint: ?[*]align(page_size_min) u8 = null;
30134
302/// The compile-time minimum page size that the target might have.
303/// All pointers from `mmap` or `VirtualAlloc` are aligned to at least `min_page_size`, but their
304/// actual alignment may be much bigger.
305/// This value can be overridden via `std.options.min_page_size`.
306/// On many systems, the actual page size can only be determined at runtime with `pageSize()`.
307pub const min_page_size: usize = std.options.min_page_size orelse (default_min_page_size orelse if (builtin.os.tag == .freestanding or builtin.os.tag == .other)
308 @compileError("freestanding/other explicitly has no min_page_size. One can be provided with std.options.min_page_size")
35/// comptime-known minimum page size of the target.
36///
37/// All pointers from `mmap` or `VirtualAlloc` are aligned to at least
38/// `page_size_min`, but their actual alignment may be bigger.
39///
40/// This value can be overridden via `std.options.page_size_min`.
41///
42/// On many systems, the actual page size can only be determined at runtime
43/// with `pageSize`.
44pub const page_size_min: usize = std.options.page_size_min orelse (page_size_min_default orelse if (builtin.os.tag == .freestanding or builtin.os.tag == .other)
45 @compileError("freestanding/other page_size_min must provided with std.options.page_size_min")
30946else
310 @compileError(@tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " has no min_page_size. One can be provided with std.options.min_page_size"));
311
312/// The compile-time maximum page size that the target might have.
313/// Targeting a system with a larger page size may require overriding `std.options.max_page_size`,
314/// as well as using the linker arugment `-z max-page-size=`.
315/// The actual page size can only be determined at runtime with `pageSize()`.
316pub const max_page_size: usize = std.options.max_page_size orelse (default_max_page_size orelse if (builtin.os.tag == .freestanding or builtin.os.tag == .other)
317 @compileError("freestanding/other explicitly has no max_page_size. One can be provided with std.options.max_page_size")
47 @compileError(@tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " has unknown page_size_min; populate std.options.page_size_min"));
48
49/// comptime-known maximum page size of the target.
50///
51/// Targeting a system with a larger page size may require overriding
52/// `std.options.page_size_max`, as well as providing a corresponding linker
53/// option.
54///
55/// The actual page size can only be determined at runtime with `pageSize`.
56pub 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)
57 @compileError("freestanding/other page_size_max must provided with std.options.page_size_max")
31858else
319 @compileError(@tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " has no max_page_size. One can be provided with std.options.max_page_size"));
320
321/// Returns the system page size.
322/// If the page size is comptime-known, `pageSize()` returns it directly.
323/// Otherwise, `pageSize()` defers to `std.options.queryPageSizeFn()`.
324pub fn pageSize() usize {
325 if (min_page_size == max_page_size) {
326 return min_page_size;
327 }
328 return std.options.queryPageSizeFn();
59 @compileError(@tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " has unknown page_size_max; populate std.options.page_size_max"));
60
61/// If the page size is comptime-known, return value is comptime.
62/// Otherwise, calls `std.options.queryPageSize` which by default queries the
63/// host operating system at runtime.
64pub inline fn pageSize() usize {
65 if (page_size_min == page_size_max) return page_size_min;
66 return std.options.queryPageSize();
32967}
33068
331// A cache used by `defaultQueryPageSize()` to avoid repeating syscalls.
332var page_size_cache = std.atomic.Value(usize).init(0);
69test pageSize {
70 assert(std.math.isPowerOfTwo(pageSize()));
71}
33372
334// The default implementation in `std.options.queryPageSizeFn`.
335// The first time it is called, it asserts that the page size is within the comptime bounds.
73/// The default implementation of `std.options.queryPageSize`.
74/// Asserts that the page size is within `page_size_min` and `page_size_max`
33675pub fn defaultQueryPageSize() usize {
337 var size = page_size_cache.load(.unordered);
76 const global = struct {
77 var cached_result: std.atomic.Value(usize) = .init(0);
78 };
79 var size = global.cached_result.load(.unordered);
33880 if (size > 0) return size;
33981 size = switch (builtin.os.tag) {
34082 .linux => if (builtin.link_libc) @intCast(std.c.sysconf(@intFromEnum(std.c._SC.PAGESIZE))) else std.os.linux.getauxval(std.elf.AT_PAGESZ),
341 .bridgeos, .driverkit, .ios, .macos, .tvos, .visionos, .watchos => blk: {
83 .driverkit, .ios, .macos, .tvos, .visionos, .watchos => blk: {
34284 const task_port = std.c.mach_task_self();
34385 // mach_task_self may fail "if there are any resource failures or other errors".
34486 if (task_port == std.c.TASK_NULL)
......@@ -353,7 +95,7 @@ pub fn defaultQueryPageSize() usize {
35395 &info_count,
35496 );
35597 assert(vm_info.page_size != 0);
356 break :blk @as(usize, @intCast(vm_info.page_size));
98 break :blk @intCast(vm_info.page_size);
35799 },
358100 .windows => blk: {
359101 var info: std.os.windows.SYSTEM_INFO = undefined;
......@@ -361,45 +103,24 @@ pub fn defaultQueryPageSize() usize {
361103 break :blk info.dwPageSize;
362104 },
363105 else => if (builtin.link_libc)
364 if (std.c._SC != void and @hasDecl(std.c._SC, "PAGESIZE"))
365 @intCast(std.c.sysconf(@intFromEnum(std.c._SC.PAGESIZE)))
366 else
367 @compileError("missing _SC.PAGESIZE declaration for " ++ @tagName(builtin.os.tag) ++ "-" ++ @tagName(builtin.os.tag))
106 @intCast(std.c.sysconf(@intFromEnum(std.c._SC.PAGESIZE)))
368107 else if (builtin.os.tag == .freestanding or builtin.os.tag == .other)
369 @compileError("pageSize on freestanding/other is not supported with the default std.options.queryPageSizeFn")
108 @compileError("unsupported target: freestanding/other")
370109 else
371110 @compileError("pageSize on " ++ @tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " is not supported without linking libc, using the default implementation"),
372111 };
373112
374 assert(size >= min_page_size);
375 assert(size <= max_page_size);
376 page_size_cache.store(size, .unordered);
113 assert(size >= page_size_min);
114 assert(size <= page_size_max);
115 global.cached_result.store(size, .unordered);
377116
378117 return size;
379118}
380119
381pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
382pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
383pub const ScopedLoggingAllocator = @import("heap/logging_allocator.zig").ScopedLoggingAllocator;
384pub const LogToWriterAllocator = @import("heap/log_to_writer_allocator.zig").LogToWriterAllocator;
385pub const logToWriterAllocator = @import("heap/log_to_writer_allocator.zig").logToWriterAllocator;
386pub const ArenaAllocator = @import("heap/arena_allocator.zig").ArenaAllocator;
387pub const GeneralPurposeAllocatorConfig = @import("heap/general_purpose_allocator.zig").Config;
388pub const GeneralPurposeAllocator = @import("heap/general_purpose_allocator.zig").GeneralPurposeAllocator;
389pub const Check = @import("heap/general_purpose_allocator.zig").Check;
390pub const WasmAllocator = @import("heap/WasmAllocator.zig");
391pub const PageAllocator = @import("heap/PageAllocator.zig");
392pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig");
393pub const SbrkAllocator = @import("heap/sbrk_allocator.zig").SbrkAllocator;
394
395const memory_pool = @import("heap/memory_pool.zig");
396pub const MemoryPool = memory_pool.MemoryPool;
397pub const MemoryPoolAligned = memory_pool.MemoryPoolAligned;
398pub const MemoryPoolExtra = memory_pool.MemoryPoolExtra;
399pub const MemoryPoolOptions = memory_pool.Options;
400
401/// TODO Utilize this on Windows.
402pub var next_mmap_addr_hint: ?[*]align(min_page_size) u8 = null;
120test defaultQueryPageSize {
121 if (builtin.cpu.arch.isWasm()) return error.SkipZigTest;
122 assert(std.math.isPowerOfTwo(defaultQueryPageSize()));
123}
403124
404125const CAllocator = struct {
405126 comptime {
......@@ -623,13 +344,6 @@ pub const wasm_allocator: Allocator = .{
623344 .vtable = &WasmAllocator.vtable,
624345};
625346
626/// Verifies that the adjusted length will still map to the full length
627pub fn alignPageAllocLen(full_len: usize, len: usize) usize {
628 const aligned_len = mem.alignAllocLen(full_len, len);
629 assert(mem.alignForward(usize, aligned_len, pageSize()) == full_len);
630 return aligned_len;
631}
632
633347pub const HeapAllocator = switch (builtin.os.tag) {
634348 .windows => struct {
635349 heap_handle: ?HeapHandle,
......@@ -730,145 +444,6 @@ pub const HeapAllocator = switch (builtin.os.tag) {
730444 else => @compileError("Unsupported OS"),
731445};
732446
733fn sliceContainsPtr(container: []u8, ptr: [*]u8) bool {
734 return @intFromPtr(ptr) >= @intFromPtr(container.ptr) and
735 @intFromPtr(ptr) < (@intFromPtr(container.ptr) + container.len);
736}
737
738fn sliceContainsSlice(container: []u8, slice: []u8) bool {
739 return @intFromPtr(slice.ptr) >= @intFromPtr(container.ptr) and
740 (@intFromPtr(slice.ptr) + slice.len) <= (@intFromPtr(container.ptr) + container.len);
741}
742
743pub const FixedBufferAllocator = struct {
744 end_index: usize,
745 buffer: []u8,
746
747 pub fn init(buffer: []u8) FixedBufferAllocator {
748 return FixedBufferAllocator{
749 .buffer = buffer,
750 .end_index = 0,
751 };
752 }
753
754 /// *WARNING* using this at the same time as the interface returned by `threadSafeAllocator` is not thread safe
755 pub fn allocator(self: *FixedBufferAllocator) Allocator {
756 return .{
757 .ptr = self,
758 .vtable = &.{
759 .alloc = alloc,
760 .resize = resize,
761 .free = free,
762 },
763 };
764 }
765
766 /// Provides a lock free thread safe `Allocator` interface to the underlying `FixedBufferAllocator`
767 /// *WARNING* using this at the same time as the interface returned by `allocator` is not thread safe
768 pub fn threadSafeAllocator(self: *FixedBufferAllocator) Allocator {
769 return .{
770 .ptr = self,
771 .vtable = &.{
772 .alloc = threadSafeAlloc,
773 .resize = Allocator.noResize,
774 .free = Allocator.noFree,
775 },
776 };
777 }
778
779 pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool {
780 return sliceContainsPtr(self.buffer, ptr);
781 }
782
783 pub fn ownsSlice(self: *FixedBufferAllocator, slice: []u8) bool {
784 return sliceContainsSlice(self.buffer, slice);
785 }
786
787 /// NOTE: this will not work in all cases, if the last allocation had an adjusted_index
788 /// then we won't be able to determine what the last allocation was. This is because
789 /// the alignForward operation done in alloc is not reversible.
790 pub fn isLastAllocation(self: *FixedBufferAllocator, buf: []u8) bool {
791 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;
792 }
793
794 fn alloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
795 const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
796 _ = ra;
797 const ptr_align = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_ptr_align));
798 const adjust_off = mem.alignPointerOffset(self.buffer.ptr + self.end_index, ptr_align) orelse return null;
799 const adjusted_index = self.end_index + adjust_off;
800 const new_end_index = adjusted_index + n;
801 if (new_end_index > self.buffer.len) return null;
802 self.end_index = new_end_index;
803 return self.buffer.ptr + adjusted_index;
804 }
805
806 fn resize(
807 ctx: *anyopaque,
808 buf: []u8,
809 log2_buf_align: u8,
810 new_size: usize,
811 return_address: usize,
812 ) bool {
813 const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
814 _ = log2_buf_align;
815 _ = return_address;
816 assert(@inComptime() or self.ownsSlice(buf));
817
818 if (!self.isLastAllocation(buf)) {
819 if (new_size > buf.len) return false;
820 return true;
821 }
822
823 if (new_size <= buf.len) {
824 const sub = buf.len - new_size;
825 self.end_index -= sub;
826 return true;
827 }
828
829 const add = new_size - buf.len;
830 if (add + self.end_index > self.buffer.len) return false;
831
832 self.end_index += add;
833 return true;
834 }
835
836 fn free(
837 ctx: *anyopaque,
838 buf: []u8,
839 log2_buf_align: u8,
840 return_address: usize,
841 ) void {
842 const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
843 _ = log2_buf_align;
844 _ = return_address;
845 assert(@inComptime() or self.ownsSlice(buf));
846
847 if (self.isLastAllocation(buf)) {
848 self.end_index -= buf.len;
849 }
850 }
851
852 fn threadSafeAlloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
853 const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
854 _ = ra;
855 const ptr_align = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_ptr_align));
856 var end_index = @atomicLoad(usize, &self.end_index, .seq_cst);
857 while (true) {
858 const adjust_off = mem.alignPointerOffset(self.buffer.ptr + end_index, ptr_align) orelse return null;
859 const adjusted_index = end_index + adjust_off;
860 const new_end_index = adjusted_index + n;
861 if (new_end_index > self.buffer.len) return null;
862 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, .seq_cst, .seq_cst) orelse
863 return self.buffer[adjusted_index..new_end_index].ptr;
864 }
865 }
866
867 pub fn reset(self: *FixedBufferAllocator) void {
868 self.end_index = 0;
869 }
870};
871
872447/// Returns a `StackFallbackAllocator` allocating using either a
873448/// `FixedBufferAllocator` on an array of size `size` and falling back to
874449/// `fallback_allocator` if that fails.
......@@ -975,7 +550,7 @@ test "raw_c_allocator" {
975550 }
976551}
977552
978test "PageAllocator" {
553test PageAllocator {
979554 const allocator = page_allocator;
980555 try testAllocator(allocator);
981556 try testAllocatorAligned(allocator);
......@@ -985,7 +560,7 @@ test "PageAllocator" {
985560 }
986561
987562 if (builtin.os.tag == .windows) {
988 const slice = try allocator.alignedAlloc(u8, min_page_size, 128);
563 const slice = try allocator.alignedAlloc(u8, page_size_min, 128);
989564 slice[0] = 0x12;
990565 slice[127] = 0x34;
991566 allocator.free(slice);
......@@ -997,7 +572,7 @@ test "PageAllocator" {
997572 }
998573}
999574
1000test "HeapAllocator" {
575test HeapAllocator {
1001576 if (builtin.os.tag == .windows) {
1002577 // https://github.com/ziglang/zig/issues/13702
1003578 if (builtin.cpu.arch == .aarch64) return error.SkipZigTest;
......@@ -1013,7 +588,7 @@ test "HeapAllocator" {
1013588 }
1014589}
1015590
1016test "ArenaAllocator" {
591test ArenaAllocator {
1017592 var arena_allocator = ArenaAllocator.init(page_allocator);
1018593 defer arena_allocator.deinit();
1019594 const allocator = arena_allocator.allocator();
......@@ -1024,38 +599,6 @@ test "ArenaAllocator" {
1024599 try testAllocatorAlignedShrink(allocator);
1025600}
1026601
1027var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined;
1028test "FixedBufferAllocator" {
1029 var fixed_buffer_allocator = mem.validationWrap(FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]));
1030 const allocator = fixed_buffer_allocator.allocator();
1031
1032 try testAllocator(allocator);
1033 try testAllocatorAligned(allocator);
1034 try testAllocatorLargeAlignment(allocator);
1035 try testAllocatorAlignedShrink(allocator);
1036}
1037
1038test "FixedBufferAllocator.reset" {
1039 var buf: [8]u8 align(@alignOf(u64)) = undefined;
1040 var fba = FixedBufferAllocator.init(buf[0..]);
1041 const allocator = fba.allocator();
1042
1043 const X = 0xeeeeeeeeeeeeeeee;
1044 const Y = 0xffffffffffffffff;
1045
1046 const x = try allocator.create(u64);
1047 x.* = X;
1048 try testing.expectError(error.OutOfMemory, allocator.create(u64));
1049
1050 fba.reset();
1051 const y = try allocator.create(u64);
1052 y.* = Y;
1053
1054 // we expect Y to have overwritten X.
1055 try testing.expect(x.* == y.*);
1056 try testing.expect(y.* == Y);
1057}
1058
1059602test "StackFallbackAllocator" {
1060603 {
1061604 var stack_allocator = stackFallback(4096, std.testing.allocator);
......@@ -1075,46 +618,6 @@ test "StackFallbackAllocator" {
1075618 }
1076619}
1077620
1078test "FixedBufferAllocator Reuse memory on realloc" {
1079 var small_fixed_buffer: [10]u8 = undefined;
1080 // check if we re-use the memory
1081 {
1082 var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]);
1083 const allocator = fixed_buffer_allocator.allocator();
1084
1085 const slice0 = try allocator.alloc(u8, 5);
1086 try testing.expect(slice0.len == 5);
1087 const slice1 = try allocator.realloc(slice0, 10);
1088 try testing.expect(slice1.ptr == slice0.ptr);
1089 try testing.expect(slice1.len == 10);
1090 try testing.expectError(error.OutOfMemory, allocator.realloc(slice1, 11));
1091 }
1092 // check that we don't re-use the memory if it's not the most recent block
1093 {
1094 var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]);
1095 const allocator = fixed_buffer_allocator.allocator();
1096
1097 var slice0 = try allocator.alloc(u8, 2);
1098 slice0[0] = 1;
1099 slice0[1] = 2;
1100 const slice1 = try allocator.alloc(u8, 2);
1101 const slice2 = try allocator.realloc(slice0, 4);
1102 try testing.expect(slice0.ptr != slice2.ptr);
1103 try testing.expect(slice1.ptr != slice2.ptr);
1104 try testing.expect(slice2[0] == 1);
1105 try testing.expect(slice2[1] == 2);
1106 }
1107}
1108
1109test "Thread safe FixedBufferAllocator" {
1110 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
1111
1112 try testAllocator(fixed_buffer_allocator.threadSafeAllocator());
1113 try testAllocatorAligned(fixed_buffer_allocator.threadSafeAllocator());
1114 try testAllocatorLargeAlignment(fixed_buffer_allocator.threadSafeAllocator());
1115 try testAllocatorAlignedShrink(fixed_buffer_allocator.threadSafeAllocator());
1116}
1117
1118621/// This one should not try alignments that exceed what C malloc can handle.
1119622pub fn testAllocator(base_allocator: mem.Allocator) !void {
1120623 var validationAllocator = mem.validationWrap(base_allocator);
......@@ -1194,7 +697,7 @@ pub fn testAllocatorLargeAlignment(base_allocator: mem.Allocator) !void {
1194697 var validationAllocator = mem.validationWrap(base_allocator);
1195698 const allocator = validationAllocator.allocator();
1196699
1197 const large_align: usize = min_page_size / 2;
700 const large_align: usize = page_size_min / 2;
1198701
1199702 var align_mask: usize = undefined;
1200703 align_mask = @shlWithOverflow(~@as(usize, 0), @as(Allocator.Log2Align, @ctz(large_align)))[0];
......@@ -1251,19 +754,296 @@ pub fn testAllocatorAlignedShrink(base_allocator: mem.Allocator) !void {
1251754 try testing.expect(slice[60] == 0x34);
1252755}
1253756
1254test "pageSize() smoke test" {
1255 const size = std.heap.pageSize();
1256 // Check that pageSize is a power of 2.
1257 std.debug.assert(size & (size - 1) == 0);
1258}
757const page_size_min_default: ?usize = switch (builtin.os.tag) {
758 .driverkit, .ios, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {
759 .x86_64 => 4 << 10,
760 .aarch64 => 16 << 10,
761 else => null,
762 },
763 .windows => switch (builtin.cpu.arch) {
764 // -- <https://devblogs.microsoft.com/oldnewthing/20210510-00/?p=105200>
765 .x86, .x86_64 => 4 << 10,
766 // SuperH => 4 << 10,
767 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
768 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
769 // DEC Alpha => 8 << 10,
770 // Itanium => 8 << 10,
771 .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
772 else => null,
773 },
774 .wasi => switch (builtin.cpu.arch) {
775 .wasm32, .wasm64 => 64 << 10,
776 else => null,
777 },
778 // https://github.com/tianocore/edk2/blob/b158dad150bf02879668f72ce306445250838201/MdePkg/Include/Uefi/UefiBaseType.h#L180-L187
779 .uefi => 4 << 10,
780 .freebsd => switch (builtin.cpu.arch) {
781 // FreeBSD/sys/*
782 .x86, .x86_64 => 4 << 10,
783 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
784 .aarch64, .aarch64_be => 4 << 10,
785 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
786 .riscv32, .riscv64 => 4 << 10,
787 else => null,
788 },
789 .netbsd => switch (builtin.cpu.arch) {
790 // NetBSD/sys/arch/*
791 .x86, .x86_64 => 4 << 10,
792 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
793 .aarch64, .aarch64_be => 4 << 10,
794 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
795 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
796 .sparc => 4 << 10,
797 .sparc64 => 8 << 10,
798 .riscv32, .riscv64 => 4 << 10,
799 // Sun-2
800 .m68k => 2 << 10,
801 else => null,
802 },
803 .dragonfly => switch (builtin.cpu.arch) {
804 .x86, .x86_64 => 4 << 10,
805 else => null,
806 },
807 .openbsd => switch (builtin.cpu.arch) {
808 // OpenBSD/sys/arch/*
809 .x86, .x86_64 => 4 << 10,
810 .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
811 .mips64, .mips64el => 4 << 10,
812 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
813 .riscv64 => 4 << 10,
814 .sparc64 => 8 << 10,
815 else => null,
816 },
817 .solaris, .illumos => switch (builtin.cpu.arch) {
818 // src/uts/*/sys/machparam.h
819 .x86, .x86_64 => 4 << 10,
820 .sparc, .sparc64 => 8 << 10,
821 else => null,
822 },
823 .fuchsia => switch (builtin.cpu.arch) {
824 // fuchsia/kernel/arch/*/include/arch/defines.h
825 .x86_64 => 4 << 10,
826 .aarch64, .aarch64_be => 4 << 10,
827 .riscv64 => 4 << 10,
828 else => null,
829 },
830 // https://github.com/SerenityOS/serenity/blob/62b938b798dc009605b5df8a71145942fc53808b/Kernel/API/POSIX/sys/limits.h#L11-L13
831 .serenity => 4 << 10,
832 .haiku => switch (builtin.cpu.arch) {
833 // haiku/headers/posix/arch/*/limits.h
834 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
835 .aarch64, .aarch64_be => 4 << 10,
836 .m68k => 4 << 10,
837 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
838 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
839 .riscv64 => 4 << 10,
840 .sparc64 => 8 << 10,
841 .x86, .x86_64 => 4 << 10,
842 else => null,
843 },
844 .hurd => switch (builtin.cpu.arch) {
845 // gnumach/*/include/mach/*/vm_param.h
846 .x86, .x86_64 => 4 << 10,
847 .aarch64 => null,
848 else => null,
849 },
850 .plan9 => switch (builtin.cpu.arch) {
851 // 9front/sys/src/9/*/mem.h
852 .x86, .x86_64 => 4 << 10,
853 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
854 .aarch64, .aarch64_be => 4 << 10,
855 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
856 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
857 .sparc => 4 << 10,
858 else => null,
859 },
860 .ps3 => switch (builtin.cpu.arch) {
861 // cell/SDK_doc/en/html/C_and_C++_standard_libraries/stdlib.html
862 .powerpc64 => 1 << 20, // 1 MiB
863 else => null,
864 },
865 .ps4 => switch (builtin.cpu.arch) {
866 // https://github.com/ps4dev/ps4sdk/blob/4df9d001b66ae4ec07d9a51b62d1e4c5e270eecc/include/machine/param.h#L95
867 .x86, .x86_64 => 4 << 10,
868 else => null,
869 },
870 .ps5 => switch (builtin.cpu.arch) {
871 // https://github.com/PS5Dev/PS5SDK/blob/a2e03a2a0231a3a3397fa6cd087a01ca6d04f273/include/machine/param.h#L95
872 .x86, .x86_64 => 16 << 10,
873 else => null,
874 },
875 // system/lib/libc/musl/arch/emscripten/bits/limits.h
876 .emscripten => 64 << 10,
877 .linux => switch (builtin.cpu.arch) {
878 // Linux/arch/*/Kconfig
879 .arc => 4 << 10,
880 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
881 .aarch64, .aarch64_be => 4 << 10,
882 .csky => 4 << 10,
883 .hexagon => 4 << 10,
884 .loongarch32, .loongarch64 => 4 << 10,
885 .m68k => 4 << 10,
886 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
887 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
888 .riscv32, .riscv64 => 4 << 10,
889 .s390x => 4 << 10,
890 .sparc => 4 << 10,
891 .sparc64 => 8 << 10,
892 .x86, .x86_64 => 4 << 10,
893 .xtensa => 4 << 10,
894 else => null,
895 },
896 .freestanding => switch (builtin.cpu.arch) {
897 .wasm32, .wasm64 => 64 << 10,
898 else => null,
899 },
900 else => null,
901};
1259902
1260test "defaultQueryPageSize() smoke test" {
1261 // queryPageSize() does not always get called by pageSize()
1262 if (builtin.cpu.arch.isWasm()) return error.SkipZigTest;
1263 const size = defaultQueryPageSize();
1264 // Check that pageSize is a power of 2.
1265 std.debug.assert(size & (size - 1) == 0);
1266}
903const page_size_max_default: ?usize = switch (builtin.os.tag) {
904 .driverkit, .ios, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {
905 .x86_64 => 4 << 10,
906 .aarch64 => 16 << 10,
907 else => null,
908 },
909 .windows => switch (builtin.cpu.arch) {
910 // -- <https://devblogs.microsoft.com/oldnewthing/20210510-00/?p=105200>
911 .x86, .x86_64 => 4 << 10,
912 // SuperH => 4 << 10,
913 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
914 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
915 // DEC Alpha => 8 << 10,
916 // Itanium => 8 << 10,
917 .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
918 else => null,
919 },
920 .wasi => switch (builtin.cpu.arch) {
921 .wasm32, .wasm64 => 64 << 10,
922 else => null,
923 },
924 // https://github.com/tianocore/edk2/blob/b158dad150bf02879668f72ce306445250838201/MdePkg/Include/Uefi/UefiBaseType.h#L180-L187
925 .uefi => 4 << 10,
926 .freebsd => switch (builtin.cpu.arch) {
927 // FreeBSD/sys/*
928 .x86, .x86_64 => 4 << 10,
929 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
930 .aarch64, .aarch64_be => 4 << 10,
931 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
932 .riscv32, .riscv64 => 4 << 10,
933 else => null,
934 },
935 .netbsd => switch (builtin.cpu.arch) {
936 // NetBSD/sys/arch/*
937 .x86, .x86_64 => 4 << 10,
938 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
939 .aarch64, .aarch64_be => 64 << 10,
940 .mips, .mipsel, .mips64, .mips64el => 16 << 10,
941 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 16 << 10,
942 .sparc => 8 << 10,
943 .sparc64 => 8 << 10,
944 .riscv32, .riscv64 => 4 << 10,
945 .m68k => 8 << 10,
946 else => null,
947 },
948 .dragonfly => switch (builtin.cpu.arch) {
949 .x86, .x86_64 => 4 << 10,
950 else => null,
951 },
952 .openbsd => switch (builtin.cpu.arch) {
953 // OpenBSD/sys/arch/*
954 .x86, .x86_64 => 4 << 10,
955 .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10,
956 .mips64, .mips64el => 16 << 10,
957 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
958 .riscv64 => 4 << 10,
959 .sparc64 => 8 << 10,
960 else => null,
961 },
962 .solaris, .illumos => switch (builtin.cpu.arch) {
963 // src/uts/*/sys/machparam.h
964 .x86, .x86_64 => 4 << 10,
965 .sparc, .sparc64 => 8 << 10,
966 else => null,
967 },
968 .fuchsia => switch (builtin.cpu.arch) {
969 // fuchsia/kernel/arch/*/include/arch/defines.h
970 .x86_64 => 4 << 10,
971 .aarch64, .aarch64_be => 4 << 10,
972 .riscv64 => 4 << 10,
973 else => null,
974 },
975 // https://github.com/SerenityOS/serenity/blob/62b938b798dc009605b5df8a71145942fc53808b/Kernel/API/POSIX/sys/limits.h#L11-L13
976 .serenity => 4 << 10,
977 .haiku => switch (builtin.cpu.arch) {
978 // haiku/headers/posix/arch/*/limits.h
979 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
980 .aarch64, .aarch64_be => 4 << 10,
981 .m68k => 4 << 10,
982 .mips, .mipsel, .mips64, .mips64el => 4 << 10,
983 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10,
984 .riscv64 => 4 << 10,
985 .sparc64 => 8 << 10,
986 .x86, .x86_64 => 4 << 10,
987 else => null,
988 },
989 .hurd => switch (builtin.cpu.arch) {
990 // gnumach/*/include/mach/*/vm_param.h
991 .x86, .x86_64 => 4 << 10,
992 .aarch64 => null,
993 else => null,
994 },
995 .plan9 => switch (builtin.cpu.arch) {
996 // 9front/sys/src/9/*/mem.h
997 .x86, .x86_64 => 4 << 10,
998 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
999 .aarch64, .aarch64_be => 64 << 10,
1000 .mips, .mipsel, .mips64, .mips64el => 16 << 10,
1001 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 4 << 10,
1002 .sparc => 4 << 10,
1003 else => null,
1004 },
1005 .ps3 => switch (builtin.cpu.arch) {
1006 // cell/SDK_doc/en/html/C_and_C++_standard_libraries/stdlib.html
1007 .powerpc64 => 1 << 20, // 1 MiB
1008 else => null,
1009 },
1010 .ps4 => switch (builtin.cpu.arch) {
1011 // https://github.com/ps4dev/ps4sdk/blob/4df9d001b66ae4ec07d9a51b62d1e4c5e270eecc/include/machine/param.h#L95
1012 .x86, .x86_64 => 4 << 10,
1013 else => null,
1014 },
1015 .ps5 => switch (builtin.cpu.arch) {
1016 // https://github.com/PS5Dev/PS5SDK/blob/a2e03a2a0231a3a3397fa6cd087a01ca6d04f273/include/machine/param.h#L95
1017 .x86, .x86_64 => 16 << 10,
1018 else => null,
1019 },
1020 // system/lib/libc/musl/arch/emscripten/bits/limits.h
1021 .emscripten => 64 << 10,
1022 .linux => switch (builtin.cpu.arch) {
1023 // Linux/arch/*/Kconfig
1024 .arc => 16 << 10,
1025 .thumb, .thumbeb, .arm, .armeb => 4 << 10,
1026 .aarch64, .aarch64_be => 64 << 10,
1027 .csky => 4 << 10,
1028 .hexagon => 256 << 10,
1029 .loongarch32, .loongarch64 => 64 << 10,
1030 .m68k => 8 << 10,
1031 .mips, .mipsel, .mips64, .mips64el => 64 << 10,
1032 .powerpc, .powerpc64, .powerpc64le, .powerpcle => 256 << 10,
1033 .riscv32, .riscv64 => 4 << 10,
1034 .s390x => 4 << 10,
1035 .sparc => 4 << 10,
1036 .sparc64 => 8 << 10,
1037 .x86, .x86_64 => 4 << 10,
1038 .xtensa => 4 << 10,
1039 else => null,
1040 },
1041 .freestanding => switch (builtin.cpu.arch) {
1042 .wasm32, .wasm64 => 64 << 10,
1043 else => null,
1044 },
1045 else => null,
1046};
12671047
12681048test {
12691049 _ = LoggingAllocator;
......@@ -1272,6 +1052,7 @@ test {
12721052 _ = @import("heap/memory_pool.zig");
12731053 _ = ArenaAllocator;
12741054 _ = GeneralPurposeAllocator;
1055 _ = FixedBufferAllocator;
12751056 if (builtin.target.isWasm()) {
12761057 _ = WasmAllocator;
12771058 }
lib/std/heap/FixedBufferAllocator.zig created+218
......@@ -0,0 +1,218 @@
1const std = @import("../std.zig");
2const Allocator = std.mem.Allocator;
3const assert = std.debug.assert;
4const mem = std.mem;
5
6const FixedBufferAllocator = @This();
7
8end_index: usize,
9buffer: []u8,
10
11pub fn init(buffer: []u8) FixedBufferAllocator {
12 return FixedBufferAllocator{
13 .buffer = buffer,
14 .end_index = 0,
15 };
16}
17
18/// Using this at the same time as the interface returned by `threadSafeAllocator` is not thread safe.
19pub fn allocator(self: *FixedBufferAllocator) Allocator {
20 return .{
21 .ptr = self,
22 .vtable = &.{
23 .alloc = alloc,
24 .resize = resize,
25 .free = free,
26 },
27 };
28}
29
30/// Provides a lock free thread safe `Allocator` interface to the underlying `FixedBufferAllocator`
31///
32/// Using this at the same time as the interface returned by `allocator` is not thread safe.
33pub fn threadSafeAllocator(self: *FixedBufferAllocator) Allocator {
34 return .{
35 .ptr = self,
36 .vtable = &.{
37 .alloc = threadSafeAlloc,
38 .resize = Allocator.noResize,
39 .free = Allocator.noFree,
40 },
41 };
42}
43
44pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool {
45 return sliceContainsPtr(self.buffer, ptr);
46}
47
48pub fn ownsSlice(self: *FixedBufferAllocator, slice: []u8) bool {
49 return sliceContainsSlice(self.buffer, slice);
50}
51
52/// This has false negatives when the last allocation had an
53/// adjusted_index. In such case we won't be able to determine what the
54/// last allocation was because the alignForward operation done in alloc is
55/// not reversible.
56pub fn isLastAllocation(self: *FixedBufferAllocator, buf: []u8) bool {
57 return buf.ptr + buf.len == self.buffer.ptr + self.end_index;
58}
59
60pub fn alloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
61 const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
62 _ = ra;
63 const ptr_align = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_ptr_align));
64 const adjust_off = mem.alignPointerOffset(self.buffer.ptr + self.end_index, ptr_align) orelse return null;
65 const adjusted_index = self.end_index + adjust_off;
66 const new_end_index = adjusted_index + n;
67 if (new_end_index > self.buffer.len) return null;
68 self.end_index = new_end_index;
69 return self.buffer.ptr + adjusted_index;
70}
71
72pub fn resize(
73 ctx: *anyopaque,
74 buf: []u8,
75 log2_buf_align: u8,
76 new_size: usize,
77 return_address: usize,
78) bool {
79 const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
80 _ = log2_buf_align;
81 _ = return_address;
82 assert(@inComptime() or self.ownsSlice(buf));
83
84 if (!self.isLastAllocation(buf)) {
85 if (new_size > buf.len) return false;
86 return true;
87 }
88
89 if (new_size <= buf.len) {
90 const sub = buf.len - new_size;
91 self.end_index -= sub;
92 return true;
93 }
94
95 const add = new_size - buf.len;
96 if (add + self.end_index > self.buffer.len) return false;
97
98 self.end_index += add;
99 return true;
100}
101
102pub fn free(
103 ctx: *anyopaque,
104 buf: []u8,
105 log2_buf_align: u8,
106 return_address: usize,
107) void {
108 const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
109 _ = log2_buf_align;
110 _ = return_address;
111 assert(@inComptime() or self.ownsSlice(buf));
112
113 if (self.isLastAllocation(buf)) {
114 self.end_index -= buf.len;
115 }
116}
117
118fn threadSafeAlloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 {
119 const self: *FixedBufferAllocator = @ptrCast(@alignCast(ctx));
120 _ = ra;
121 const ptr_align = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_ptr_align));
122 var end_index = @atomicLoad(usize, &self.end_index, .seq_cst);
123 while (true) {
124 const adjust_off = mem.alignPointerOffset(self.buffer.ptr + end_index, ptr_align) orelse return null;
125 const adjusted_index = end_index + adjust_off;
126 const new_end_index = adjusted_index + n;
127 if (new_end_index > self.buffer.len) return null;
128 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, .seq_cst, .seq_cst) orelse
129 return self.buffer[adjusted_index..new_end_index].ptr;
130 }
131}
132
133pub fn reset(self: *FixedBufferAllocator) void {
134 self.end_index = 0;
135}
136
137fn sliceContainsPtr(container: []u8, ptr: [*]u8) bool {
138 return @intFromPtr(ptr) >= @intFromPtr(container.ptr) and
139 @intFromPtr(ptr) < (@intFromPtr(container.ptr) + container.len);
140}
141
142fn sliceContainsSlice(container: []u8, slice: []u8) bool {
143 return @intFromPtr(slice.ptr) >= @intFromPtr(container.ptr) and
144 (@intFromPtr(slice.ptr) + slice.len) <= (@intFromPtr(container.ptr) + container.len);
145}
146
147var test_fixed_buffer_allocator_memory: [800000 * @sizeOf(u64)]u8 = undefined;
148
149test FixedBufferAllocator {
150 var fixed_buffer_allocator = mem.validationWrap(FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]));
151 const a = fixed_buffer_allocator.allocator();
152
153 try std.heap.testAllocator(a);
154 try std.heap.testAllocatorAligned(a);
155 try std.heap.testAllocatorLargeAlignment(a);
156 try std.heap.testAllocatorAlignedShrink(a);
157}
158
159test reset {
160 var buf: [8]u8 align(@alignOf(u64)) = undefined;
161 var fba = FixedBufferAllocator.init(buf[0..]);
162 const a = fba.allocator();
163
164 const X = 0xeeeeeeeeeeeeeeee;
165 const Y = 0xffffffffffffffff;
166
167 const x = try a.create(u64);
168 x.* = X;
169 try std.testing.expectError(error.OutOfMemory, a.create(u64));
170
171 fba.reset();
172 const y = try a.create(u64);
173 y.* = Y;
174
175 // we expect Y to have overwritten X.
176 try std.testing.expect(x.* == y.*);
177 try std.testing.expect(y.* == Y);
178}
179
180test "reuse memory on realloc" {
181 var small_fixed_buffer: [10]u8 = undefined;
182 // check if we re-use the memory
183 {
184 var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]);
185 const a = fixed_buffer_allocator.allocator();
186
187 const slice0 = try a.alloc(u8, 5);
188 try std.testing.expect(slice0.len == 5);
189 const slice1 = try a.realloc(slice0, 10);
190 try std.testing.expect(slice1.ptr == slice0.ptr);
191 try std.testing.expect(slice1.len == 10);
192 try std.testing.expectError(error.OutOfMemory, a.realloc(slice1, 11));
193 }
194 // check that we don't re-use the memory if it's not the most recent block
195 {
196 var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]);
197 const a = fixed_buffer_allocator.allocator();
198
199 var slice0 = try a.alloc(u8, 2);
200 slice0[0] = 1;
201 slice0[1] = 2;
202 const slice1 = try a.alloc(u8, 2);
203 const slice2 = try a.realloc(slice0, 4);
204 try std.testing.expect(slice0.ptr != slice2.ptr);
205 try std.testing.expect(slice1.ptr != slice2.ptr);
206 try std.testing.expect(slice2[0] == 1);
207 try std.testing.expect(slice2[1] == 2);
208 }
209}
210
211test "thread safe version" {
212 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
213
214 try std.heap.testAllocator(fixed_buffer_allocator.threadSafeAllocator());
215 try std.heap.testAllocatorAligned(fixed_buffer_allocator.threadSafeAllocator());
216 try std.heap.testAllocatorLargeAlignment(fixed_buffer_allocator.threadSafeAllocator());
217 try std.heap.testAllocatorAlignedShrink(fixed_buffer_allocator.threadSafeAllocator());
218}
lib/std/heap/PageAllocator.zig+14-11
......@@ -2,14 +2,14 @@ const std = @import("../std.zig");
22const builtin = @import("builtin");
33const Allocator = std.mem.Allocator;
44const mem = std.mem;
5const heap = std.heap;
65const maxInt = std.math.maxInt;
76const assert = std.debug.assert;
87const native_os = builtin.os.tag;
98const windows = std.os.windows;
109const posix = std.posix;
10const page_size_min = std.heap.page_size_min;
1111
12pub const vtable = Allocator.VTable{
12pub const vtable: Allocator.VTable = .{
1313 .alloc = alloc,
1414 .resize = resize,
1515 .free = free,
......@@ -19,7 +19,6 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
1919 _ = ra;
2020 _ = log2_align;
2121 assert(n > 0);
22 if (n > maxInt(usize) - (heap.pageSize() - 1)) return null;
2322
2423 if (native_os == .windows) {
2524 const addr = windows.VirtualAlloc(
......@@ -35,7 +34,10 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
3534 return @ptrCast(addr);
3635 }
3736
38 const aligned_len = mem.alignForward(usize, n, heap.pageSize());
37 const page_size = std.heap.pageSize();
38 if (n >= maxInt(usize) - page_size) return null;
39
40 const aligned_len = mem.alignForward(usize, n, page_size);
3941 const hint = @atomicLoad(@TypeOf(std.heap.next_mmap_addr_hint), &std.heap.next_mmap_addr_hint, .unordered);
4042 const slice = posix.mmap(
4143 hint,
......@@ -45,8 +47,8 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
4547 -1,
4648 0,
4749 ) catch return null;
48 assert(mem.isAligned(@intFromPtr(slice.ptr), heap.pageSize()));
49 const new_hint: [*]align(heap.min_page_size) u8 = @alignCast(slice.ptr + aligned_len);
50 assert(mem.isAligned(@intFromPtr(slice.ptr), page_size_min));
51 const new_hint: [*]align(std.heap.page_size_min) u8 = @alignCast(slice.ptr + aligned_len);
5052 _ = @cmpxchgStrong(@TypeOf(std.heap.next_mmap_addr_hint), &std.heap.next_mmap_addr_hint, hint, new_hint, .monotonic, .monotonic);
5153 return slice.ptr;
5254}
......@@ -60,13 +62,14 @@ fn resize(
6062) bool {
6163 _ = log2_buf_align;
6264 _ = return_address;
63 const new_size_aligned = mem.alignForward(usize, new_size, heap.pageSize());
65 const page_size = std.heap.pageSize();
66 const new_size_aligned = mem.alignForward(usize, new_size, page_size);
6467
6568 if (native_os == .windows) {
6669 if (new_size <= buf_unaligned.len) {
6770 const base_addr = @intFromPtr(buf_unaligned.ptr);
6871 const old_addr_end = base_addr + buf_unaligned.len;
69 const new_addr_end = mem.alignForward(usize, base_addr + new_size, heap.pageSize());
72 const new_addr_end = mem.alignForward(usize, base_addr + new_size, page_size);
7073 if (old_addr_end > new_addr_end) {
7174 // For shrinking that is not releasing, we will only
7275 // decommit the pages not needed anymore.
......@@ -78,14 +81,14 @@ fn resize(
7881 }
7982 return true;
8083 }
81 const old_size_aligned = mem.alignForward(usize, buf_unaligned.len, heap.pageSize());
84 const old_size_aligned = mem.alignForward(usize, buf_unaligned.len, page_size);
8285 if (new_size_aligned <= old_size_aligned) {
8386 return true;
8487 }
8588 return false;
8689 }
8790
88 const buf_aligned_len = mem.alignForward(usize, buf_unaligned.len, heap.pageSize());
91 const buf_aligned_len = mem.alignForward(usize, buf_unaligned.len, page_size);
8992 if (new_size_aligned == buf_aligned_len)
9093 return true;
9194
......@@ -108,7 +111,7 @@ fn free(_: *anyopaque, slice: []u8, log2_buf_align: u8, return_address: usize) v
108111 if (native_os == .windows) {
109112 windows.VirtualFree(slice.ptr, 0, windows.MEM_RELEASE);
110113 } else {
111 const buf_aligned_len = mem.alignForward(usize, slice.len, heap.pageSize());
114 const buf_aligned_len = mem.alignForward(usize, slice.len, std.heap.pageSize());
112115 posix.munmap(@alignCast(slice.ptr[0..buf_aligned_len]));
113116 }
114117}
lib/std/heap/general_purpose_allocator.zig+8-8
......@@ -75,7 +75,7 @@
7575//! BucketHeader, followed by "used bits", and two stack traces for each slot
7676//! (allocation trace and free trace).
7777//!
78//! The buckets array contains buckets for every size class below `max_page_size`.
78//! The buckets array contains buckets for every size class below `page_size_max`.
7979//! At runtime, only size classes below `pageSize()` will actually be used for allocations.
8080//!
8181//! The "used bits" are 1 bit per slot representing whether the slot is used.
......@@ -102,13 +102,13 @@ const math = std.math;
102102const assert = std.debug.assert;
103103const mem = std.mem;
104104const Allocator = std.mem.Allocator;
105const min_page_size = std.heap.min_page_size;
106const max_page_size = std.heap.max_page_size;
105const page_size_min = std.heap.page_size_min;
106const page_size_max = std.heap.page_size_max;
107107const pageSize = std.heap.pageSize;
108108const StackTrace = std.builtin.StackTrace;
109109
110110/// Integer type for pointing to slots in a small allocation
111const SlotIndex = std.meta.Int(.unsigned, math.log2(max_page_size) + 1);
111const SlotIndex = std.meta.Int(.unsigned, math.log2(page_size_max) + 1);
112112
113113const default_test_stack_trace_frames: usize = if (builtin.is_test) 10 else 6;
114114const default_sys_stack_trace_frames: usize = if (std.debug.sys_can_stack_trace) default_test_stack_trace_frames else 0;
......@@ -214,7 +214,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
214214
215215 pub const Error = mem.Allocator.Error;
216216
217 const small_bucket_count = math.log2(max_page_size);
217 const small_bucket_count = math.log2(page_size_max);
218218 const largest_bucket_object_size = 1 << (small_bucket_count - 1);
219219 const LargestSizeClassInt = std.math.IntFittingRange(0, largest_bucket_object_size);
220220 fn used_small_bucket_count() usize {
......@@ -287,7 +287,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
287287 // * stack_trace_addresses: [N]usize, // traces_per_slot for every allocation
288288
289289 const BucketHeader = struct {
290 page: [*]align(min_page_size) u8,
290 page: [*]align(page_size_min) u8,
291291 alloc_cursor: SlotIndex,
292292 used_count: SlotIndex,
293293
......@@ -591,7 +591,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
591591 addr: usize,
592592 current_bucket: ?*BucketHeader,
593593 ) ?*BucketHeader {
594 const search_page: [*]align(min_page_size) u8 = @ptrFromInt(mem.alignBackward(usize, addr, pageSize()));
594 const search_page: [*]align(page_size_min) u8 = @ptrFromInt(mem.alignBackward(usize, addr, pageSize()));
595595 if (current_bucket != null and current_bucket.?.page == search_page) {
596596 return current_bucket;
597597 }
......@@ -1062,7 +1062,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
10621062 }
10631063
10641064 fn createBucket(self: *Self, size_class: usize) Error!*BucketHeader {
1065 const page = try self.backing_allocator.alignedAlloc(u8, min_page_size, pageSize());
1065 const page = try self.backing_allocator.alignedAlloc(u8, page_size_min, pageSize());
10661066 errdefer self.backing_allocator.free(page);
10671067
10681068 const bucket_size = bucketSize(size_class);
lib/std/mem.zig+12-10
......@@ -1048,17 +1048,18 @@ pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]co
10481048 // as we don't read into a new page. This should be the case for most architectures
10491049 // which use paged memory, however should be confirmed before adding a new arch below.
10501050 .aarch64, .x86, .x86_64 => if (std.simd.suggestVectorLength(T)) |block_len| {
1051 const page_size = std.heap.pageSize();
10511052 const block_size = @sizeOf(T) * block_len;
10521053 const Block = @Vector(block_len, T);
10531054 const mask: Block = @splat(sentinel);
10541055
1055 comptime std.debug.assert(std.heap.max_page_size % @sizeOf(Block) == 0);
1056 std.debug.assert(std.heap.pageSize() % @sizeOf(Block) == 0);
1056 comptime assert(std.heap.page_size_max % @sizeOf(Block) == 0);
1057 assert(page_size % @sizeOf(Block) == 0);
10571058
10581059 // First block may be unaligned
10591060 const start_addr = @intFromPtr(&p[i]);
1060 const offset_in_page = start_addr & (std.heap.pageSize() - 1);
1061 if (offset_in_page <= std.heap.pageSize() - @sizeOf(Block)) {
1061 const offset_in_page = start_addr & (page_size - 1);
1062 if (offset_in_page <= page_size - @sizeOf(Block)) {
10621063 // Will not read past the end of a page, full block.
10631064 const block: Block = p[i..][0..block_len].*;
10641065 const matches = block == mask;
......@@ -1078,7 +1079,7 @@ pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]co
10781079 }
10791080 }
10801081
1081 std.debug.assert(std.mem.isAligned(@intFromPtr(&p[i]), block_size));
1082 assert(std.mem.isAligned(@intFromPtr(&p[i]), block_size));
10821083 while (true) {
10831084 const block: *const Block = @ptrCast(@alignCast(p[i..][0..block_len]));
10841085 const matches = block.* == mask;
......@@ -1101,23 +1102,24 @@ pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]co
11011102test "indexOfSentinel vector paths" {
11021103 const Types = [_]type{ u8, u16, u32, u64 };
11031104 const allocator = std.testing.allocator;
1105 const page_size = std.heap.pageSize();
11041106
11051107 inline for (Types) |T| {
11061108 const block_len = std.simd.suggestVectorLength(T) orelse continue;
11071109
11081110 // Allocate three pages so we guarantee a page-crossing address with a full page after
1109 const memory = try allocator.alloc(T, 3 * std.heap.pageSize() / @sizeOf(T));
1111 const memory = try allocator.alloc(T, 3 * page_size / @sizeOf(T));
11101112 defer allocator.free(memory);
11111113 @memset(memory, 0xaa);
11121114
11131115 // Find starting page-alignment = 0
11141116 var start: usize = 0;
11151117 const start_addr = @intFromPtr(&memory);
1116 start += (std.mem.alignForward(usize, start_addr, std.heap.pageSize()) - start_addr) / @sizeOf(T);
1117 try testing.expect(start < std.heap.pageSize() / @sizeOf(T));
1118 start += (std.mem.alignForward(usize, start_addr, page_size) - start_addr) / @sizeOf(T);
1119 try testing.expect(start < page_size / @sizeOf(T));
11181120
11191121 // Validate all sub-block alignments
1120 const search_len = std.heap.pageSize() / @sizeOf(T);
1122 const search_len = page_size / @sizeOf(T);
11211123 memory[start + search_len] = 0;
11221124 for (0..block_len) |offset| {
11231125 try testing.expectEqual(search_len - offset, indexOfSentinel(T, 0, @ptrCast(&memory[start + offset])));
......@@ -1125,7 +1127,7 @@ test "indexOfSentinel vector paths" {
11251127 memory[start + search_len] = 0xaa;
11261128
11271129 // Validate page boundary crossing
1128 const start_page_boundary = start + (std.heap.pageSize() / @sizeOf(T));
1130 const start_page_boundary = start + (page_size / @sizeOf(T));
11291131 memory[start_page_boundary + block_len] = 0;
11301132 for (0..block_len) |offset| {
11311133 try testing.expectEqual(2 * block_len - offset, indexOfSentinel(T, 0, @ptrCast(&memory[start_page_boundary - block_len + offset])));
lib/std/mem/Allocator.zig+5-6
......@@ -18,11 +18,15 @@ ptr: *anyopaque,
1818vtable: *const VTable,
1919
2020pub const VTable = struct {
21 /// Attempt to allocate exactly `len` bytes aligned to `1 << ptr_align`.
21 /// Allocate exactly `len` bytes aligned to `1 << ptr_align`, or return `null`
22 /// indicating the allocation failed.
2223 ///
2324 /// `ret_addr` is optionally provided as the first return address of the
2425 /// allocation call stack. If the value is `0` it means no return address
2526 /// has been provided.
27 ///
28 /// The returned slice of memory must have been `@memset` to `undefined`
29 /// by the allocator implementation.
2630 alloc: *const fn (ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8,
2731
2832 /// Attempt to expand or shrink memory in place. `buf.len` must equal the
......@@ -215,11 +219,6 @@ fn allocWithSizeAndAlignment(self: Allocator, comptime size: usize, comptime ali
215219}
216220
217221fn allocBytesWithAlignment(self: Allocator, comptime alignment: u29, byte_count: usize, return_address: usize) Error![*]align(alignment) u8 {
218 // The Zig Allocator interface is not intended to solve alignments beyond
219 // the minimum OS page size. For these use cases, the caller must use OS
220 // APIs directly.
221 if (!@inComptime() and alignment > std.heap.pageSize()) @panic("Alignment must be smaller than page size.");
222
223222 if (byte_count == 0) {
224223 const ptr = comptime std.mem.alignBackward(usize, math.maxInt(usize), alignment);
225224 return @as([*]align(alignment) u8, @ptrFromInt(ptr));
lib/std/os/linux/IoUring.zig+8-8
......@@ -3,12 +3,12 @@ const std = @import("std");
33const builtin = @import("builtin");
44const assert = std.debug.assert;
55const mem = std.mem;
6const heap = std.heap;
76const net = std.net;
87const posix = std.posix;
98const linux = std.os.linux;
109const testing = std.testing;
1110const is_linux = builtin.os.tag == .linux;
11const page_size_min = std.heap.page_size_min;
1212
1313fd: posix.fd_t = -1,
1414sq: SubmissionQueue,
......@@ -1342,8 +1342,8 @@ pub const SubmissionQueue = struct {
13421342 dropped: *u32,
13431343 array: []u32,
13441344 sqes: []linux.io_uring_sqe,
1345 mmap: []align(heap.min_page_size) u8,
1346 mmap_sqes: []align(heap.min_page_size) u8,
1345 mmap: []align(page_size_min) u8,
1346 mmap_sqes: []align(page_size_min) u8,
13471347
13481348 // We use `sqe_head` and `sqe_tail` in the same way as liburing:
13491349 // We increment `sqe_tail` (but not `tail`) for each call to `get_sqe()`.
......@@ -1461,7 +1461,7 @@ pub const BufferGroup = struct {
14611461 /// Pointer to the memory shared by the kernel.
14621462 /// `buffers_count` of `io_uring_buf` structures are shared by the kernel.
14631463 /// First `io_uring_buf` is overlaid by `io_uring_buf_ring` struct.
1464 br: *align(heap.min_page_size) linux.io_uring_buf_ring,
1464 br: *align(page_size_min) linux.io_uring_buf_ring,
14651465 /// Contiguous block of memory of size (buffers_count * buffer_size).
14661466 buffers: []u8,
14671467 /// Size of each buffer in buffers.
......@@ -1556,7 +1556,7 @@ pub const BufferGroup = struct {
15561556/// `fd` is IO_Uring.fd for which the provided buffer ring is being registered.
15571557/// `entries` is the number of entries requested in the buffer ring, must be power of 2.
15581558/// `group_id` is the chosen buffer group ID, unique in IO_Uring.
1559pub fn setup_buf_ring(fd: posix.fd_t, entries: u16, group_id: u16) !*align(heap.min_page_size) linux.io_uring_buf_ring {
1559pub fn setup_buf_ring(fd: posix.fd_t, entries: u16, group_id: u16) !*align(page_size_min) linux.io_uring_buf_ring {
15601560 if (entries == 0 or entries > 1 << 15) return error.EntriesNotInRange;
15611561 if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo;
15621562
......@@ -1572,7 +1572,7 @@ pub fn setup_buf_ring(fd: posix.fd_t, entries: u16, group_id: u16) !*align(heap.
15721572 errdefer posix.munmap(mmap);
15731573 assert(mmap.len == mmap_size);
15741574
1575 const br: *align(heap.min_page_size) linux.io_uring_buf_ring = @ptrCast(mmap.ptr);
1575 const br: *align(page_size_min) linux.io_uring_buf_ring = @ptrCast(mmap.ptr);
15761576 try register_buf_ring(fd, @intFromPtr(br), entries, group_id);
15771577 return br;
15781578}
......@@ -1614,9 +1614,9 @@ fn handle_register_buf_ring_result(res: usize) !void {
16141614}
16151615
16161616// Unregisters a previously registered shared buffer ring, returned from io_uring_setup_buf_ring.
1617pub fn free_buf_ring(fd: posix.fd_t, br: *align(heap.min_page_size) linux.io_uring_buf_ring, entries: u32, group_id: u16) void {
1617pub fn free_buf_ring(fd: posix.fd_t, br: *align(page_size_min) linux.io_uring_buf_ring, entries: u32, group_id: u16) void {
16181618 unregister_buf_ring(fd, group_id) catch {};
1619 var mmap: []align(heap.min_page_size) u8 = undefined;
1619 var mmap: []align(page_size_min) u8 = undefined;
16201620 mmap.ptr = @ptrCast(br);
16211621 mmap.len = entries * @sizeOf(linux.io_uring_buf);
16221622 posix.munmap(mmap);
lib/std/os/linux/tls.zig+10-10
......@@ -11,13 +11,13 @@
1111
1212const std = @import("std");
1313const mem = std.mem;
14const heap = std.heap;
1514const elf = std.elf;
1615const math = std.math;
1716const assert = std.debug.assert;
1817const native_arch = @import("builtin").cpu.arch;
1918const linux = std.os.linux;
2019const posix = std.posix;
20const page_size_min = std.heap.page_size_min;
2121
2222/// Represents an ELF TLS variant.
2323///
......@@ -485,13 +485,13 @@ pub fn prepareArea(area: []u8) usize {
485485 };
486486}
487487
488// The main motivation for the size chosen here is that this is how much ends up being requested for
489// the thread-local variables of the `std.crypto.random` implementation. I'm not sure why it ends up
490// being so much; the struct itself is only 64 bytes. I think it has to do with being page-aligned
491// and LLVM or LLD is not smart enough to lay out the TLS data in a space-conserving way. Anyway, I
492// think it's fine because it's less than 3 pages of memory, and putting it in the ELF like this is
493// equivalent to moving the `mmap` call below into the kernel, avoiding syscall overhead.
494var main_thread_area_buffer: [0x2100]u8 align(heap.min_page_size) = undefined;
488/// The main motivation for the size chosen here is that this is how much ends up being requested for
489/// the thread-local variables of the `std.crypto.random` implementation. I'm not sure why it ends up
490/// being so much; the struct itself is only 64 bytes. I think it has to do with being page-aligned
491/// and LLVM or LLD is not smart enough to lay out the TLS data in a space-conserving way. Anyway, I
492/// think it's fine because it's less than 3 pages of memory, and putting it in the ELF like this is
493/// equivalent to moving the `mmap` call below into the kernel, avoiding syscall overhead.
494var main_thread_area_buffer: [0x2100]u8 align(page_size_min) = undefined;
495495
496496/// Computes the layout of the static TLS area, allocates the area, initializes all of its fields,
497497/// and assigns the architecture-specific value to the TP register.
......@@ -504,7 +504,7 @@ pub fn initStatic(phdrs: []elf.Phdr) void {
504504 const area = blk: {
505505 // Fast path for the common case where the TLS data is really small, avoid an allocation and
506506 // use our local buffer.
507 if (area_desc.alignment <= heap.min_page_size and area_desc.size <= main_thread_area_buffer.len) {
507 if (area_desc.alignment <= page_size_min and area_desc.size <= main_thread_area_buffer.len) {
508508 break :blk main_thread_area_buffer[0..area_desc.size];
509509 }
510510
......@@ -518,7 +518,7 @@ pub fn initStatic(phdrs: []elf.Phdr) void {
518518 );
519519 if (@as(isize, @bitCast(begin_addr)) < 0) @trap();
520520
521 const area_ptr: [*]align(heap.min_page_size) u8 = @ptrFromInt(begin_addr);
521 const area_ptr: [*]align(page_size_min) u8 = @ptrFromInt(begin_addr);
522522
523523 // Make sure the slice is correctly aligned.
524524 const begin_aligned_addr = alignForward(begin_addr, area_desc.alignment);
lib/std/posix.zig+10-10
......@@ -18,13 +18,13 @@ const builtin = @import("builtin");
1818const root = @import("root");
1919const std = @import("std.zig");
2020const mem = std.mem;
21const heap = std.heap;
2221const fs = std.fs;
2322const max_path_bytes = fs.max_path_bytes;
2423const maxInt = std.math.maxInt;
2524const cast = std.math.cast;
2625const assert = std.debug.assert;
2726const native_os = builtin.os.tag;
27const page_size_min = std.heap.page_size_min;
2828
2929test {
3030 _ = @import("posix/test.zig");
......@@ -4695,7 +4695,7 @@ pub const MProtectError = error{
46954695 OutOfMemory,
46964696} || UnexpectedError;
46974697
4698pub fn mprotect(memory: []align(heap.min_page_size) u8, protection: u32) MProtectError!void {
4698pub fn mprotect(memory: []align(page_size_min) u8, protection: u32) MProtectError!void {
46994699 if (native_os == .windows) {
47004700 const win_prot: windows.DWORD = switch (@as(u3, @truncate(protection))) {
47014701 0b000 => windows.PAGE_NOACCESS,
......@@ -4760,21 +4760,21 @@ pub const MMapError = error{
47604760/// * SIGSEGV - Attempted write into a region mapped as read-only.
47614761/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file
47624762pub fn mmap(
4763 ptr: ?[*]align(heap.min_page_size) u8,
4763 ptr: ?[*]align(page_size_min) u8,
47644764 length: usize,
47654765 prot: u32,
47664766 flags: system.MAP,
47674767 fd: fd_t,
47684768 offset: u64,
4769) MMapError![]align(heap.min_page_size) u8 {
4769) MMapError![]align(page_size_min) u8 {
47704770 const mmap_sym = if (lfs64_abi) system.mmap64 else system.mmap;
47714771 const rc = mmap_sym(ptr, length, prot, @bitCast(flags), fd, @bitCast(offset));
47724772 const err: E = if (builtin.link_libc) blk: {
4773 if (rc != std.c.MAP_FAILED) return @as([*]align(heap.min_page_size) u8, @ptrCast(@alignCast(rc)))[0..length];
4773 if (rc != std.c.MAP_FAILED) return @as([*]align(page_size_min) u8, @ptrCast(@alignCast(rc)))[0..length];
47744774 break :blk @enumFromInt(system._errno().*);
47754775 } else blk: {
47764776 const err = errno(rc);
4777 if (err == .SUCCESS) return @as([*]align(heap.min_page_size) u8, @ptrFromInt(rc))[0..length];
4777 if (err == .SUCCESS) return @as([*]align(page_size_min) u8, @ptrFromInt(rc))[0..length];
47784778 break :blk err;
47794779 };
47804780 switch (err) {
......@@ -4800,7 +4800,7 @@ pub fn mmap(
48004800/// Zig's munmap function does not, for two reasons:
48014801/// * It violates the Zig principle that resource deallocation must succeed.
48024802/// * The Windows function, VirtualFree, has this restriction.
4803pub fn munmap(memory: []align(heap.min_page_size) const u8) void {
4803pub fn munmap(memory: []align(page_size_min) const u8) void {
48044804 switch (errno(system.munmap(memory.ptr, memory.len))) {
48054805 .SUCCESS => return,
48064806 .INVAL => unreachable, // Invalid parameters.
......@@ -4814,7 +4814,7 @@ pub const MSyncError = error{
48144814 PermissionDenied,
48154815} || UnexpectedError;
48164816
4817pub fn msync(memory: []align(heap.min_page_size) u8, flags: i32) MSyncError!void {
4817pub fn msync(memory: []align(page_size_min) u8, flags: i32) MSyncError!void {
48184818 switch (errno(system.msync(memory.ptr, memory.len, flags))) {
48194819 .SUCCESS => return,
48204820 .PERM => return error.PermissionDenied,
......@@ -7136,7 +7136,7 @@ pub const MincoreError = error{
71367136} || UnexpectedError;
71377137
71387138/// Determine whether pages are resident in memory.
7139pub fn mincore(ptr: [*]align(heap.min_page_size) u8, length: usize, vec: [*]u8) MincoreError!void {
7139pub fn mincore(ptr: [*]align(page_size_min) u8, length: usize, vec: [*]u8) MincoreError!void {
71407140 return switch (errno(system.mincore(ptr, length, vec))) {
71417141 .SUCCESS => {},
71427142 .AGAIN => error.SystemResources,
......@@ -7182,7 +7182,7 @@ pub const MadviseError = error{
71827182
71837183/// Give advice about use of memory.
71847184/// This syscall is optional and is sometimes configured to be disabled.
7185pub fn madvise(ptr: [*]align(heap.min_page_size) u8, length: usize, advice: u32) MadviseError!void {
7185pub fn madvise(ptr: [*]align(page_size_min) u8, length: usize, advice: u32) MadviseError!void {
71867186 switch (errno(system.madvise(ptr, length, advice))) {
71877187 .SUCCESS => return,
71887188 .PERM => return error.PermissionDenied,
lib/std/process.zig+1-1
......@@ -1560,7 +1560,7 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
15601560 ReadGroupId,
15611561 };
15621562
1563 var buf: [std.heap.min_page_size]u8 = undefined;
1563 var buf: [std.heap.page_size_min]u8 = undefined;
15641564 var name_index: usize = 0;
15651565 var state = State.Start;
15661566 var uid: posix.uid_t = 0;
lib/std/start.zig+1-1
......@@ -576,7 +576,7 @@ fn expandStackSize(phdrs: []elf.Phdr) void {
576576 switch (phdr.p_type) {
577577 elf.PT_GNU_STACK => {
578578 if (phdr.p_memsz == 0) break;
579 assert(phdr.p_memsz % std.heap.pageSize() == 0);
579 assert(phdr.p_memsz % std.heap.page_size_min == 0);
580580
581581 // Silently fail if we are unable to get limits.
582582 const limits = std.posix.getrlimit(.STACK) catch break;
lib/std/std.zig+6-3
......@@ -119,9 +119,12 @@ pub const Options = struct {
119119 args: anytype,
120120 ) void = log.defaultLog,
121121
122 min_page_size: ?usize = null,
123 max_page_size: ?usize = null,
124 queryPageSizeFn: fn () usize = heap.defaultQueryPageSize,
122 /// Overrides `std.heap.page_size_min`.
123 page_size_min: ?usize = null,
124 /// Overrides `std.heap.page_size_max`.
125 page_size_max: ?usize = null,
126 /// Overrides default implementation for determining OS page size at runtime.
127 queryPageSize: fn () usize = heap.defaultQueryPageSize,
125128
126129 fmt_max_depth: usize = fmt.default_max_depth,
127130
src/Package/Fetch.zig+1-1
......@@ -1249,7 +1249,7 @@ fn unzip(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult {
12491249 .{@errorName(err)},
12501250 ));
12511251 defer zip_file.close();
1252 var buf: [std.heap.min_page_size]u8 = undefined;
1252 var buf: [4096]u8 = undefined;
12531253 while (true) {
12541254 const len = reader.readAll(&buf) catch |err| return f.fail(f.location_tok, try eb.printString(
12551255 "read zip stream failed: {s}",