authorgravatar for archbirdplus@gmail.comArchbirdplus <archbirdplus@gmail.com> 2024-10-20 14:55:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-06 14:23:23-08:00
log439667be0476f5bf60f3efbb82a3c4d5aae96ee4
tree982fdb5d9260c1c30dee76f89319848e0371b4e2
parentb0ed602d5d9358128471588f00a073f2545809fa

runtime page size detection

heap.zig: define new default page sizes heap.zig: add min/max_page_size and their options lib/std/c: add miscellaneous declarations heap.zig: add pageSize() and its options switch to new page sizes, especially in GPA/stdlib mem.zig: remove page_size

29 files changed, 614 insertions(+), 176 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.mem.page_size) volatile u8,
483 items: []align(std.heap.min_page_size) 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.mem.page_size) const u8,
44 mapped_memory: []align(std.heap.min_page_size) 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+3-3
......@@ -769,7 +769,7 @@ const PosixThreadImpl = struct {
769769 // Use the same set of parameters used by the libc-less impl.
770770 const stack_size = @max(config.stack_size, 16 * 1024);
771771 assert(c.pthread_attr_setstacksize(&attr, stack_size) == .SUCCESS);
772 assert(c.pthread_attr_setguardsize(&attr, std.mem.page_size) == .SUCCESS);
772 assert(c.pthread_attr_setguardsize(&attr, std.heap.pageSize()) == .SUCCESS);
773773
774774 var handle: c.pthread_t = undefined;
775775 switch (c.pthread_create(
......@@ -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.mem.page_size) u8,
1158 mapped: []align(std.heap.min_page_size) 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:
......@@ -1362,7 +1362,7 @@ const LinuxThreadImpl = struct {
13621362 };
13631363
13641364 fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl {
1365 const page_size = std.mem.page_size;
1365 const page_size = std.heap.pageSize();
13661366 const Args = @TypeOf(args);
13671367 const Instance = struct {
13681368 fn_args: Args,
lib/std/c.zig+48-11
......@@ -3,7 +3,7 @@ const builtin = @import("builtin");
33const c = @This();
44const maxInt = std.math.maxInt;
55const assert = std.debug.assert;
6const page_size = std.mem.page_size;
6const min_page_size = std.heap.min_page_size;
77const native_abi = builtin.abi;
88const native_arch = builtin.cpu.arch;
99const native_os = builtin.os.tag;
......@@ -2227,6 +2227,39 @@ pub const SC = switch (native_os) {
22272227 .linux => linux.SC,
22282228 else => void,
22292229};
2230
2231pub const _SC = switch (native_os) {
2232 .bridgeos, .driverkit, .ios, .macos, .tvos, .visionos, .watchos => enum(c_int) {
2233 PAGESIZE = 29,
2234 },
2235 .dragonfly => enum(c_int) {
2236 PAGESIZE = 47,
2237 },
2238 .freebsd => enum(c_int) {
2239 PAGESIZE = 47,
2240 },
2241 .fuchsia => enum(c_int) {
2242 PAGESIZE = 30,
2243 },
2244 .haiku => enum(c_int) {
2245 PAGESIZE = 27,
2246 },
2247 .linux => enum(c_int) {
2248 PAGESIZE = 30,
2249 },
2250 .netbsd => enum(c_int) {
2251 PAGESIZE = 28,
2252 },
2253 .openbsd => enum(c_int) {
2254 PAGESIZE = 28,
2255 },
2256 .solaris, .illumos => enum(c_int) {
2257 PAGESIZE = 11,
2258 NPROCESSORS_ONLN = 15,
2259 },
2260 else => void,
2261};
2262
22302263pub const SEEK = switch (native_os) {
22312264 .linux => linux.SEEK,
22322265 .emscripten => emscripten.SEEK,
......@@ -9232,7 +9265,7 @@ pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
92329265pub extern "c" fn getpwuid(uid: uid_t) ?*passwd;
92339266pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int;
92349267pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64;
9235pub extern "c" fn mmap64(addr: ?*align(std.mem.page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: i64) *anyopaque;
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;
92369269pub extern "c" fn open64(path: [*:0]const u8, oflag: O, ...) c_int;
92379270pub extern "c" fn openat64(fd: c_int, path: [*:0]const u8, oflag: O, ...) c_int;
92389271pub extern "c" fn pread64(fd: fd_t, buf: [*]u8, nbyte: usize, offset: i64) isize;
......@@ -9324,13 +9357,13 @@ pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) c_int;
93249357
93259358pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int;
93269359pub extern "c" fn mincore(
9327 addr: *align(std.mem.page_size) anyopaque,
9360 addr: *align(min_page_size) anyopaque,
93289361 length: usize,
93299362 vec: [*]u8,
93309363) c_int;
93319364
93329365pub extern "c" fn madvise(
9333 addr: *align(std.mem.page_size) anyopaque,
9366 addr: *align(min_page_size) anyopaque,
93349367 length: usize,
93359368 advice: u32,
93369369) c_int;
......@@ -9428,6 +9461,10 @@ pub const posix_memalign = switch (native_os) {
94289461 .dragonfly, .netbsd, .freebsd, .solaris, .openbsd, .linux, .macos, .ios, .tvos, .watchos, .visionos => private.posix_memalign,
94299462 else => {},
94309463};
9464pub const sysconf = switch (native_os) {
9465 .solaris => solaris.sysconf,
9466 else => private.sysconf,
9467};
94319468
94329469pub const sf_hdtr = switch (native_os) {
94339470 .freebsd, .macos, .ios, .tvos, .watchos, .visionos => extern struct {
......@@ -9469,9 +9506,9 @@ pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) i
94699506pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: off_t) isize;
94709507pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;
94719508pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: off_t) isize;
9472pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: MAP, fd: fd_t, offset: off_t) *anyopaque;
9473pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;
9474pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int;
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;
94759512pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8) c_int;
94769513pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_int) c_int;
94779514pub extern "c" fn unlink(path: [*:0]const u8) c_int;
......@@ -9823,7 +9860,6 @@ pub const SCM = solaris.SCM;
98239860pub const SETCONTEXT = solaris.SETCONTEXT;
98249861pub const SETUSTACK = solaris.GETUSTACK;
98259862pub const SFD = solaris.SFD;
9826pub const _SC = solaris._SC;
98279863pub const cmsghdr = solaris.cmsghdr;
98289864pub const ctid_t = solaris.ctid_t;
98299865pub const file_obj = solaris.file_obj;
......@@ -9840,7 +9876,6 @@ pub const priority = solaris.priority;
98409876pub const procfs = solaris.procfs;
98419877pub const projid_t = solaris.projid_t;
98429878pub const signalfd_siginfo = solaris.signalfd_siginfo;
9843pub const sysconf = solaris.sysconf;
98449879pub const taskid_t = solaris.taskid_t;
98459880pub const zoneid_t = solaris.zoneid_t;
98469881
......@@ -9997,6 +10032,7 @@ pub const host_t = darwin.host_t;
999710032pub const ipc_space_t = darwin.ipc_space_t;
999810033pub const ipc_space_port_t = darwin.ipc_space_port_t;
999910034pub const kern_return_t = darwin.kern_return_t;
10035pub const vm_size_t = darwin.vm_size_t;
1000010036pub const kevent64 = darwin.kevent64;
1000110037pub const kevent64_s = darwin.kevent64_s;
1000210038pub const mach_absolute_time = darwin.mach_absolute_time;
......@@ -10155,7 +10191,7 @@ const private = struct {
1015510191 };
1015610192 extern "c" fn getrusage(who: c_int, usage: *rusage) c_int;
1015710193 extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
10158 extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
10194 extern "c" fn msync(addr: *align(min_page_size) const anyopaque, len: usize, flags: c_int) c_int;
1015910195 extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
1016010196 extern "c" fn pipe2(fds: *[2]fd_t, flags: O) c_int;
1016110197 extern "c" fn readdir(dir: *DIR) ?*dirent;
......@@ -10168,6 +10204,7 @@ const private = struct {
1016810204 extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
1016910205 extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
1017010206 extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
10207 extern "c" fn sysconf(sc: c_int) c_long;
1017110208
1017210209 extern "c" fn pthread_setname_np(thread: pthread_t, name: [*:0]const u8) c_int;
1017310210 extern "c" fn getcontext(ucp: *ucontext_t) c_int;
......@@ -10202,7 +10239,7 @@ const private = struct {
1020210239 extern "c" fn __getrusage50(who: c_int, usage: *rusage) c_int;
1020310240 extern "c" fn __gettimeofday50(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
1020410241 extern "c" fn __libc_thr_yield() c_int;
10205 extern "c" fn __msync13(addr: *align(std.mem.page_size) const anyopaque, len: usize, flags: c_int) c_int;
10242 extern "c" fn __msync13(addr: *align(min_page_size) const anyopaque, len: usize, flags: c_int) c_int;
1020610243 extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int;
1020710244 extern "c" fn __sigaction14(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
1020810245 extern "c" fn __sigfillset14(set: ?*sigset_t) void;
lib/std/c/solaris.zig-4
......@@ -154,10 +154,6 @@ pub const AF_SUN = struct {
154154 pub const NOPLM = 0x00000004;
155155};
156156
157pub const _SC = struct {
158 pub const NPROCESSORS_ONLN = 15;
159};
160
161157pub const procfs = struct {
162158 pub const misc_header = extern struct {
163159 size: u32,
lib/std/crypto/tlcsprng.zig+3-2
......@@ -6,6 +6,7 @@
66const std = @import("std");
77const builtin = @import("builtin");
88const mem = std.mem;
9const heap = std.heap;
910const native_os = builtin.os.tag;
1011const posix = std.posix;
1112
......@@ -42,7 +43,7 @@ var install_atfork_handler = std.once(struct {
4243 }
4344}.do);
4445
45threadlocal var wipe_mem: []align(mem.page_size) u8 = &[_]u8{};
46threadlocal var wipe_mem: []align(heap.min_page_size) u8 = &[_]u8{};
4647
4748fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
4849 if (os_has_arc4random) {
......@@ -77,7 +78,7 @@ fn tlsCsprngFill(_: *anyopaque, buffer: []u8) void {
7778 } else {
7879 // Use a static thread-local buffer.
7980 const S = struct {
80 threadlocal var buf: Context align(mem.page_size) = .{
81 threadlocal var buf: Context align(heap.min_page_size) = .{
8182 .init_state = .uninitialized,
8283 .rng = undefined,
8384 };
lib/std/debug.zig+9-8
......@@ -2,6 +2,7 @@ const builtin = @import("builtin");
22const std = @import("std.zig");
33const math = std.math;
44const mem = std.mem;
5const heap = std.heap;
56const io = std.io;
67const posix = std.posix;
78const fs = std.fs;
......@@ -1134,7 +1135,7 @@ fn printLineFromFileAnyOs(out_stream: anytype, source_location: SourceLocation)
11341135 defer f.close();
11351136 // TODO fstat and make sure that the file has the correct size
11361137
1137 var buf: [mem.page_size]u8 = undefined;
1138 var buf: [4096]u8 = undefined;
11381139 var amt_read = try f.read(buf[0..]);
11391140 const line_start = seek: {
11401141 var current_line_start: usize = 0;
......@@ -1237,7 +1238,7 @@ test printLineFromFileAnyOs {
12371238
12381239 const overlap = 10;
12391240 var writer = file.writer();
1240 try writer.writeByteNTimes('a', mem.page_size - overlap);
1241 try writer.writeByteNTimes('a', heap.min_page_size - overlap);
12411242 try writer.writeByte('\n');
12421243 try writer.writeByteNTimes('a', overlap);
12431244
......@@ -1252,10 +1253,10 @@ test printLineFromFileAnyOs {
12521253 defer allocator.free(path);
12531254
12541255 var writer = file.writer();
1255 try writer.writeByteNTimes('a', mem.page_size);
1256 try writer.writeByteNTimes('a', heap.max_page_size);
12561257
12571258 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1258 try expectEqualStrings(("a" ** mem.page_size) ++ "\n", output.items);
1259 try expectEqualStrings(("a" ** heap.max_page_size) ++ "\n", output.items);
12591260 output.clearRetainingCapacity();
12601261 }
12611262 {
......@@ -1265,18 +1266,18 @@ test printLineFromFileAnyOs {
12651266 defer allocator.free(path);
12661267
12671268 var writer = file.writer();
1268 try writer.writeByteNTimes('a', 3 * mem.page_size);
1269 try writer.writeByteNTimes('a', 3 * heap.max_page_size);
12691270
12701271 try expectError(error.EndOfFile, printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 }));
12711272
12721273 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1273 try expectEqualStrings(("a" ** (3 * mem.page_size)) ++ "\n", output.items);
1274 try expectEqualStrings(("a" ** (3 * heap.max_page_size)) ++ "\n", output.items);
12741275 output.clearRetainingCapacity();
12751276
12761277 try writer.writeAll("a\na");
12771278
12781279 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 1, .column = 0 });
1279 try expectEqualStrings(("a" ** (3 * mem.page_size)) ++ "a\n", output.items);
1280 try expectEqualStrings(("a" ** (3 * heap.max_page_size)) ++ "a\n", output.items);
12801281 output.clearRetainingCapacity();
12811282
12821283 try printLineFromFileAnyOs(output_stream, .{ .file_name = path, .line = 2, .column = 0 });
......@@ -1290,7 +1291,7 @@ test printLineFromFileAnyOs {
12901291 defer allocator.free(path);
12911292
12921293 var writer = file.writer();
1293 const real_file_start = 3 * mem.page_size;
1294 const real_file_start = 3 * heap.min_page_size;
12941295 try writer.writeByteNTimes('\n', real_file_start);
12951296 try writer.writeAll("abc\ndef");
12961297
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.mem.page_size) const u8,
2124 external_mapped_memory: ?[]align(std.mem.page_size) const u8,
2123 mapped_memory: []align(std.heap.min_page_size) const u8,
2124 external_mapped_memory: ?[]align(std.heap.min_page_size) 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.mem.page_size) const u8,
2170 mapped_mem: []align(std.heap.min_page_size) const u8,
21712171 build_id: ?[]const u8,
21722172 expected_crc: ?u32,
21732173 parent_sections: *Dwarf.SectionArray,
2174 parent_mapped_mem: ?[]align(std.mem.page_size) const u8,
2174 parent_mapped_mem: ?[]align(std.heap.min_page_size) 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.mem.page_size) const u8,
2426 parent_mapped_mem: ?[]align(std.heap.min_page_size) 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/Info.zig-1
......@@ -10,7 +10,6 @@ const std = @import("../std.zig");
1010const Allocator = std.mem.Allocator;
1111const Path = std.Build.Cache.Path;
1212const Dwarf = std.debug.Dwarf;
13const page_size = std.mem.page_size;
1413const assert = std.debug.assert;
1514const Coverage = std.debug.Coverage;
1615const SourceLocation = std.debug.Coverage.SourceLocation;
lib/std/debug/MemoryAccessor.zig+5-4
......@@ -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 page_size = std.mem.page_size;
10const min_page_size = std.heap.min_page_size;
1111
1212const MemoryAccessor = @This();
1313
......@@ -93,9 +93,10 @@ pub fn isValidMemory(address: usize) bool {
9393 // We are unable to determine validity of memory for freestanding targets
9494 if (native_os == .freestanding or native_os == .other or native_os == .uefi) return true;
9595
96 const aligned_address = address & ~@as(usize, @intCast((page_size - 1)));
96 const page_size = std.heap.pageSize();
97 const aligned_address = address & ~(page_size - 1);
9798 if (aligned_address == 0) return false;
98 const aligned_memory = @as([*]align(page_size) u8, @ptrFromInt(aligned_address))[0..page_size];
99 const aligned_memory = @as([*]align(min_page_size) u8, @ptrFromInt(aligned_address))[0..page_size];
99100
100101 if (native_os == .windows) {
101102 const windows = std.os.windows;
......@@ -104,7 +105,7 @@ pub fn isValidMemory(address: usize) bool {
104105
105106 // The only error this function can throw is ERROR_INVALID_PARAMETER.
106107 // supply an address that invalid i'll be thrown.
107 const rc = windows.VirtualQuery(aligned_memory, &memory_info, aligned_memory.len) catch {
108 const rc = windows.VirtualQuery(@ptrCast(aligned_memory), &memory_info, aligned_memory.len) catch {
108109 return false;
109110 };
110111
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(mem.page_size) const u8,
507 mapped_memory: []align(std.heap.min_page_size) 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(mem.page_size) const u8,
1049 parent_mapped_mem: ?[]align(std.heap.min_page_size) 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(mem.page_size) const u8 {
1091fn mapWholeFile(file: File) ![]align(std.heap.min_page_size) const u8 {
10921092 nosuspend {
10931093 defer file.close();
10941094
lib/std/dynamic_library.zig+6-5
......@@ -1,6 +1,7 @@
11const std = @import("std.zig");
22const builtin = @import("builtin");
33const mem = std.mem;
4const heap = std.heap;
45const testing = std.testing;
56const elf = std.elf;
67const windows = std.os.windows;
......@@ -143,7 +144,7 @@ pub const ElfDynLib = struct {
143144 hashtab: [*]posix.Elf_Symndx,
144145 versym: ?[*]elf.Versym,
145146 verdef: ?*elf.Verdef,
146 memory: []align(mem.page_size) u8,
147 memory: []align(heap.min_page_size) u8,
147148
148149 pub const Error = ElfDynLibError;
149150
......@@ -223,7 +224,7 @@ pub const ElfDynLib = struct {
223224 // corresponding to the actual LOAD sections.
224225 const file_bytes = try posix.mmap(
225226 null,
226 mem.alignForward(usize, size, mem.page_size),
227 mem.alignForward(usize, size, heap.pageSize()),
227228 posix.PROT.READ,
228229 .{ .TYPE = .PRIVATE },
229230 fd,
......@@ -284,10 +285,10 @@ pub const ElfDynLib = struct {
284285 elf.PT_LOAD => {
285286 // The VirtAddr may not be page-aligned; in such case there will be
286287 // extra nonsense mapped before/after the VirtAddr,MemSiz
287 const aligned_addr = (base + ph.p_vaddr) & ~(@as(usize, mem.page_size) - 1);
288 const aligned_addr = (base + ph.p_vaddr) & ~(@as(usize, heap.pageSize()) - 1);
288289 const extra_bytes = (base + ph.p_vaddr) - aligned_addr;
289 const extended_memsz = mem.alignForward(usize, ph.p_memsz + extra_bytes, mem.page_size);
290 const ptr = @as([*]align(mem.page_size) u8, @ptrFromInt(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));
291292 const prot = elfToMmapProt(ph.p_flags);
292293 if ((ph.p_flags & elf.PF_W) == 0) {
293294 // If it does not need write access, it can be mapped from the fd.
lib/std/fifo.zig+1-1
......@@ -91,7 +91,7 @@ pub fn LinearFifo(
9191 mem.copyForwards(T, self.buf[0..self.count], self.buf[self.head..][0..self.count]);
9292 self.head = 0;
9393 } else {
94 var tmp: [mem.page_size / 2 / @sizeOf(T)]T = undefined;
94 var tmp: [4096 / 2 / @sizeOf(T)]T = undefined;
9595
9696 while (self.head != 0) {
9797 const n = @min(self.head, tmp.len);
lib/std/heap.zig+391-7
......@@ -8,6 +8,376 @@ 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};
156
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};
301
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")
309else
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")
318else
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();
329}
330
331// A cache used by `defaultQueryPageSize()` to avoid repeating syscalls.
332var page_size_cache = std.atomic.Value(usize).init(0);
333
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.
336pub fn defaultQueryPageSize() usize {
337 var size = page_size_cache.load(.unordered);
338 if (size > 0) return size;
339 size = switch (builtin.os.tag) {
340 .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: {
342 const task_port = std.c.mach_task_self();
343 // mach_task_self may fail "if there are any resource failures or other errors".
344 if (task_port == std.c.TASK_NULL)
345 break :blk 0;
346 var info_count = std.c.TASK_VM_INFO_COUNT;
347 var vm_info: std.c.task_vm_info_data_t = undefined;
348 vm_info.page_size = 0;
349 _ = std.c.task_info(
350 task_port,
351 std.c.TASK_VM_INFO,
352 @as(std.c.task_info_t, @ptrCast(&vm_info)),
353 &info_count,
354 );
355 assert(vm_info.page_size != 0);
356 break :blk @as(usize, @intCast(vm_info.page_size));
357 },
358 .windows => blk: {
359 var info: std.os.windows.SYSTEM_INFO = undefined;
360 std.os.windows.kernel32.GetSystemInfo(&info);
361 break :blk info.dwPageSize;
362 },
363 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))
368 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")
370 else
371 @compileError("pageSize on " ++ @tagName(builtin.cpu.arch) ++ "-" ++ @tagName(builtin.os.tag) ++ " is not supported without linking libc, using the default implementation"),
372 };
373
374 assert(size >= min_page_size);
375 assert(size <= max_page_size);
376 page_size_cache.store(size, .unordered);
377
378 return size;
379}
380
11381pub const LoggingAllocator = @import("heap/logging_allocator.zig").LoggingAllocator;
12382pub const loggingAllocator = @import("heap/logging_allocator.zig").loggingAllocator;
13383pub const ScopedLoggingAllocator = @import("heap/logging_allocator.zig").ScopedLoggingAllocator;
......@@ -29,7 +399,7 @@ pub const MemoryPoolExtra = memory_pool.MemoryPoolExtra;
29399pub const MemoryPoolOptions = memory_pool.Options;
30400
31401/// TODO Utilize this on Windows.
32pub var next_mmap_addr_hint: ?[*]align(mem.page_size) u8 = null;
402pub var next_mmap_addr_hint: ?[*]align(min_page_size) u8 = null;
33403
34404const CAllocator = struct {
35405 comptime {
......@@ -256,7 +626,7 @@ pub const wasm_allocator: Allocator = .{
256626/// Verifies that the adjusted length will still map to the full length
257627pub fn alignPageAllocLen(full_len: usize, len: usize) usize {
258628 const aligned_len = mem.alignAllocLen(full_len, len);
259 assert(mem.alignForward(usize, aligned_len, mem.page_size) == full_len);
629 assert(mem.alignForward(usize, aligned_len, pageSize()) == full_len);
260630 return aligned_len;
261631}
262632
......@@ -615,13 +985,13 @@ test "PageAllocator" {
615985 }
616986
617987 if (builtin.os.tag == .windows) {
618 const slice = try allocator.alignedAlloc(u8, mem.page_size, 128);
988 const slice = try allocator.alignedAlloc(u8, min_page_size, 128);
619989 slice[0] = 0x12;
620990 slice[127] = 0x34;
621991 allocator.free(slice);
622992 }
623993 {
624 var buf = try allocator.alloc(u8, mem.page_size + 1);
994 var buf = try allocator.alloc(u8, pageSize() + 1);
625995 defer allocator.free(buf);
626996 buf = try allocator.realloc(buf, 1); // shrink past the page boundary
627997 }
......@@ -824,7 +1194,7 @@ pub fn testAllocatorLargeAlignment(base_allocator: mem.Allocator) !void {
8241194 var validationAllocator = mem.validationWrap(base_allocator);
8251195 const allocator = validationAllocator.allocator();
8261196
827 const large_align: usize = mem.page_size / 2;
1197 const large_align: usize = min_page_size / 2;
8281198
8291199 var align_mask: usize = undefined;
8301200 align_mask = @shlWithOverflow(~@as(usize, 0), @as(Allocator.Log2Align, @ctz(large_align)))[0];
......@@ -857,7 +1227,7 @@ pub fn testAllocatorAlignedShrink(base_allocator: mem.Allocator) !void {
8571227 var fib = FixedBufferAllocator.init(&debug_buffer);
8581228 const debug_allocator = fib.allocator();
8591229
860 const alloc_size = mem.page_size * 2 + 50;
1230 const alloc_size = pageSize() * 2 + 50;
8611231 var slice = try allocator.alignedAlloc(u8, 16, alloc_size);
8621232 defer allocator.free(slice);
8631233
......@@ -866,7 +1236,7 @@ pub fn testAllocatorAlignedShrink(base_allocator: mem.Allocator) !void {
8661236 // which is 16 pages, hence the 32. This test may require to increase
8671237 // the size of the allocations feeding the `allocator` parameter if they
8681238 // fail, because of this high over-alignment we want to have.
869 while (@intFromPtr(slice.ptr) == mem.alignForward(usize, @intFromPtr(slice.ptr), mem.page_size * 32)) {
1239 while (@intFromPtr(slice.ptr) == mem.alignForward(usize, @intFromPtr(slice.ptr), pageSize() * 32)) {
8701240 try stuff_to_free.append(slice);
8711241 slice = try allocator.alignedAlloc(u8, 16, alloc_size);
8721242 }
......@@ -881,6 +1251,20 @@ pub fn testAllocatorAlignedShrink(base_allocator: mem.Allocator) !void {
8811251 try testing.expect(slice[60] == 0x34);
8821252}
8831253
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}
1259
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}
1267
8841268test {
8851269 _ = LoggingAllocator;
8861270 _ = LogToWriterAllocator;
lib/std/heap/PageAllocator.zig+10-9
......@@ -2,6 +2,7 @@ const std = @import("../std.zig");
22const builtin = @import("builtin");
33const Allocator = std.mem.Allocator;
44const mem = std.mem;
5const heap = std.heap;
56const maxInt = std.math.maxInt;
67const assert = std.debug.assert;
78const native_os = builtin.os.tag;
......@@ -18,7 +19,7 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
1819 _ = ra;
1920 _ = log2_align;
2021 assert(n > 0);
21 if (n > maxInt(usize) - (mem.page_size - 1)) return null;
22 if (n > maxInt(usize) - (heap.pageSize() - 1)) return null;
2223
2324 if (native_os == .windows) {
2425 const addr = windows.VirtualAlloc(
......@@ -34,7 +35,7 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
3435 return @ptrCast(addr);
3536 }
3637
37 const aligned_len = mem.alignForward(usize, n, mem.page_size);
38 const aligned_len = mem.alignForward(usize, n, heap.pageSize());
3839 const hint = @atomicLoad(@TypeOf(std.heap.next_mmap_addr_hint), &std.heap.next_mmap_addr_hint, .unordered);
3940 const slice = posix.mmap(
4041 hint,
......@@ -44,8 +45,8 @@ fn alloc(_: *anyopaque, n: usize, log2_align: u8, ra: usize) ?[*]u8 {
4445 -1,
4546 0,
4647 ) catch return null;
47 assert(mem.isAligned(@intFromPtr(slice.ptr), mem.page_size));
48 const new_hint: [*]align(mem.page_size) u8 = @alignCast(slice.ptr + aligned_len);
48 assert(mem.isAligned(@intFromPtr(slice.ptr), heap.pageSize()));
49 const new_hint: [*]align(heap.min_page_size) u8 = @alignCast(slice.ptr + aligned_len);
4950 _ = @cmpxchgStrong(@TypeOf(std.heap.next_mmap_addr_hint), &std.heap.next_mmap_addr_hint, hint, new_hint, .monotonic, .monotonic);
5051 return slice.ptr;
5152}
......@@ -59,13 +60,13 @@ fn resize(
5960) bool {
6061 _ = log2_buf_align;
6162 _ = return_address;
62 const new_size_aligned = mem.alignForward(usize, new_size, mem.page_size);
63 const new_size_aligned = mem.alignForward(usize, new_size, heap.pageSize());
6364
6465 if (native_os == .windows) {
6566 if (new_size <= buf_unaligned.len) {
6667 const base_addr = @intFromPtr(buf_unaligned.ptr);
6768 const old_addr_end = base_addr + buf_unaligned.len;
68 const new_addr_end = mem.alignForward(usize, base_addr + new_size, mem.page_size);
69 const new_addr_end = mem.alignForward(usize, base_addr + new_size, heap.pageSize());
6970 if (old_addr_end > new_addr_end) {
7071 // For shrinking that is not releasing, we will only
7172 // decommit the pages not needed anymore.
......@@ -77,14 +78,14 @@ fn resize(
7778 }
7879 return true;
7980 }
80 const old_size_aligned = mem.alignForward(usize, buf_unaligned.len, mem.page_size);
81 const old_size_aligned = mem.alignForward(usize, buf_unaligned.len, heap.pageSize());
8182 if (new_size_aligned <= old_size_aligned) {
8283 return true;
8384 }
8485 return false;
8586 }
8687
87 const buf_aligned_len = mem.alignForward(usize, buf_unaligned.len, mem.page_size);
88 const buf_aligned_len = mem.alignForward(usize, buf_unaligned.len, heap.pageSize());
8889 if (new_size_aligned == buf_aligned_len)
8990 return true;
9091
......@@ -107,7 +108,7 @@ fn free(_: *anyopaque, slice: []u8, log2_buf_align: u8, return_address: usize) v
107108 if (native_os == .windows) {
108109 windows.VirtualFree(slice.ptr, 0, windows.MEM_RELEASE);
109110 } else {
110 const buf_aligned_len = mem.alignForward(usize, slice.len, mem.page_size);
111 const buf_aligned_len = mem.alignForward(usize, slice.len, heap.pageSize());
111112 posix.munmap(@alignCast(slice.ptr[0..buf_aligned_len]));
112113 }
113114}
lib/std/heap/general_purpose_allocator.zig+78-51
......@@ -48,7 +48,7 @@
4848//!
4949//! ## Basic Design:
5050//!
51//! Small allocations are divided into buckets:
51//! Small allocations are divided into buckets. For a max page size of 4K:
5252//!
5353//! ```
5454//! index obj_size
......@@ -75,6 +75,9 @@
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`.
79//! At runtime, only size classes below `pageSize()` will actually be used for allocations.
80//!
7881//! The "used bits" are 1 bit per slot representing whether the slot is used.
7982//! Allocations use the data to iterate to find a free slot. Frees assert that the
8083//! corresponding bit is 1 and set it to 0.
......@@ -99,11 +102,13 @@ const math = std.math;
99102const assert = std.debug.assert;
100103const mem = std.mem;
101104const Allocator = std.mem.Allocator;
102const page_size = std.mem.page_size;
105const min_page_size = std.heap.min_page_size;
106const max_page_size = std.heap.max_page_size;
107const pageSize = std.heap.pageSize;
103108const StackTrace = std.builtin.StackTrace;
104109
105110/// Integer type for pointing to slots in a small allocation
106const SlotIndex = std.meta.Int(.unsigned, math.log2(page_size) + 1);
111const SlotIndex = std.meta.Int(.unsigned, math.log2(max_page_size) + 1);
107112
108113const default_test_stack_trace_frames: usize = if (builtin.is_test) 10 else 6;
109114const default_sys_stack_trace_frames: usize = if (std.debug.sys_can_stack_trace) default_test_stack_trace_frames else 0;
......@@ -157,6 +162,9 @@ pub const Config = struct {
157162
158163pub const Check = enum { ok, leak };
159164
165var used_small_bucket_count_cache = std.atomic.Value(usize).init(0);
166var largest_used_bucket_object_size_cache = std.atomic.Value(usize).init(0);
167
160168/// Default initialization of this struct is deprecated; use `.init` instead.
161169pub fn GeneralPurposeAllocator(comptime config: Config) type {
162170 return struct {
......@@ -206,9 +214,27 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
206214
207215 pub const Error = mem.Allocator.Error;
208216
209 const small_bucket_count = math.log2(page_size);
217 const small_bucket_count = math.log2(max_page_size);
210218 const largest_bucket_object_size = 1 << (small_bucket_count - 1);
211219 const LargestSizeClassInt = std.math.IntFittingRange(0, largest_bucket_object_size);
220 fn used_small_bucket_count() usize {
221 const cached = used_small_bucket_count_cache.load(.monotonic);
222 if (cached != 0) {
223 return cached;
224 }
225 const val = math.log2(pageSize());
226 used_small_bucket_count_cache.store(val, .monotonic);
227 return val;
228 }
229 fn largest_used_bucket_object_size() usize {
230 const cached = largest_used_bucket_object_size_cache.load(.monotonic);
231 if (cached != 0) {
232 return cached;
233 }
234 const val = @as(usize, 1) << @truncate(used_small_bucket_count() - 1);
235 largest_used_bucket_object_size_cache.store(val, .monotonic);
236 return val;
237 }
212238
213239 const bucketCompare = struct {
214240 fn compare(a: *BucketHeader, b: *BucketHeader) std.math.Order {
......@@ -261,7 +287,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
261287 // * stack_trace_addresses: [N]usize, // traces_per_slot for every allocation
262288
263289 const BucketHeader = struct {
264 page: [*]align(page_size) u8,
290 page: [*]align(min_page_size) u8,
265291 alloc_cursor: SlotIndex,
266292 used_count: SlotIndex,
267293
......@@ -273,14 +299,14 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
273299 if (!config.safety) @compileError("requested size is only stored when safety is enabled");
274300 const start_ptr = @as([*]u8, @ptrCast(bucket)) + bucketRequestedSizesStart(size_class);
275301 const sizes = @as([*]LargestSizeClassInt, @ptrCast(@alignCast(start_ptr)));
276 const slot_count = @divExact(page_size, size_class);
302 const slot_count = @divExact(pageSize(), size_class);
277303 return sizes[0..slot_count];
278304 }
279305
280306 fn log2PtrAligns(bucket: *BucketHeader, size_class: usize) []u8 {
281307 if (!config.safety) @compileError("requested size is only stored when safety is enabled");
282308 const aligns_ptr = @as([*]u8, @ptrCast(bucket)) + bucketAlignsStart(size_class);
283 const slot_count = @divExact(page_size, size_class);
309 const slot_count = @divExact(pageSize(), size_class);
284310 return aligns_ptr[0..slot_count];
285311 }
286312
......@@ -312,7 +338,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
312338 /// Only valid for buckets within `empty_buckets`, and relies on the `alloc_cursor`
313339 /// of empty buckets being set to `slot_count` when they are added to `empty_buckets`
314340 fn emptyBucketSizeClass(bucket: *BucketHeader) usize {
315 return @divExact(page_size, bucket.alloc_cursor);
341 return @divExact(pageSize(), bucket.alloc_cursor);
316342 }
317343 };
318344
......@@ -355,13 +381,13 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
355381
356382 fn bucketAlignsStart(size_class: usize) usize {
357383 if (!config.safety) @compileError("requested sizes are not stored unless safety is enabled");
358 const slot_count = @divExact(page_size, size_class);
384 const slot_count = @divExact(pageSize(), size_class);
359385 return bucketRequestedSizesStart(size_class) + (@sizeOf(LargestSizeClassInt) * slot_count);
360386 }
361387
362388 fn bucketStackFramesStart(size_class: usize) usize {
363389 const unaligned_start = if (config.safety) blk: {
364 const slot_count = @divExact(page_size, size_class);
390 const slot_count = @divExact(pageSize(), size_class);
365391 break :blk bucketAlignsStart(size_class) + slot_count;
366392 } else @sizeOf(BucketHeader) + usedBitsCount(size_class);
367393 return mem.alignForward(
......@@ -372,12 +398,12 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
372398 }
373399
374400 fn bucketSize(size_class: usize) usize {
375 const slot_count = @divExact(page_size, size_class);
401 const slot_count = @divExact(pageSize(), size_class);
376402 return bucketStackFramesStart(size_class) + one_trace_size * traces_per_slot * slot_count;
377403 }
378404
379405 fn usedBitsCount(size_class: usize) usize {
380 const slot_count = @divExact(page_size, size_class);
406 const slot_count = @divExact(pageSize(), size_class);
381407 if (slot_count < 8) return 1;
382408 return @divExact(slot_count, 8);
383409 }
......@@ -416,7 +442,8 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
416442 pub fn detectLeaks(self: *Self) bool {
417443 var leaks = false;
418444
419 for (&self.buckets, 0..) |*buckets, bucket_i| {
445 for (0..used_small_bucket_count()) |bucket_i| {
446 const buckets = &self.buckets[bucket_i];
420447 if (buckets.root == null) continue;
421448 const size_class = @as(usize, 1) << @as(math.Log2Int(usize), @intCast(bucket_i));
422449 const used_bits_count = usedBitsCount(size_class);
......@@ -464,7 +491,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
464491 var bucket = node.key;
465492 if (config.never_unmap) {
466493 // free page that was intentionally leaked by never_unmap
467 self.backing_allocator.free(bucket.page[0..page_size]);
494 self.backing_allocator.free(bucket.page[0..pageSize()]);
468495 }
469496 // alloc_cursor was set to slot count when bucket added to empty_buckets
470497 self.freeBucket(bucket, bucket.emptyBucketSizeClass());
......@@ -531,7 +558,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
531558 fn allocSlot(self: *Self, size_class: usize, trace_addr: usize) Error!Slot {
532559 const bucket_index = math.log2(size_class);
533560 var buckets = &self.buckets[bucket_index];
534 const slot_count = @divExact(page_size, size_class);
561 const slot_count = @divExact(pageSize(), size_class);
535562 if (self.cur_buckets[bucket_index] == null or self.cur_buckets[bucket_index].?.alloc_cursor == slot_count) {
536563 const new_bucket = try self.createBucket(size_class);
537564 errdefer self.freeBucket(new_bucket, size_class);
......@@ -564,7 +591,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
564591 addr: usize,
565592 current_bucket: ?*BucketHeader,
566593 ) ?*BucketHeader {
567 const search_page: [*]align(page_size) u8 = @ptrFromInt(mem.alignBackward(usize, addr, page_size));
594 const search_page: [*]align(min_page_size) u8 = @ptrFromInt(mem.alignBackward(usize, addr, pageSize()));
568595 if (current_bucket != null and current_bucket.?.page == search_page) {
569596 return current_bucket;
570597 }
......@@ -729,14 +756,14 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
729756 assert(old_mem.len != 0);
730757
731758 const aligned_size = @max(old_mem.len, @as(usize, 1) << log2_old_align);
732 if (aligned_size > largest_bucket_object_size) {
759 if (aligned_size > largest_used_bucket_object_size()) {
733760 return self.resizeLarge(old_mem, log2_old_align, new_size, ret_addr);
734761 }
735762 const size_class_hint = math.ceilPowerOfTwoAssert(usize, aligned_size);
736763
737764 var bucket_index = math.log2(size_class_hint);
738765 var size_class: usize = size_class_hint;
739 const bucket = while (bucket_index < small_bucket_count) : (bucket_index += 1) {
766 const bucket = while (bucket_index < used_small_bucket_count()) : (bucket_index += 1) {
740767 if (searchBucket(&self.buckets[bucket_index], @intFromPtr(old_mem.ptr), self.cur_buckets[bucket_index])) |bucket| {
741768 break bucket;
742769 }
......@@ -847,7 +874,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
847874 assert(old_mem.len != 0);
848875
849876 const aligned_size = @max(old_mem.len, @as(usize, 1) << log2_old_align);
850 if (aligned_size > largest_bucket_object_size) {
877 if (aligned_size > largest_used_bucket_object_size()) {
851878 self.freeLarge(old_mem, log2_old_align, ret_addr);
852879 return;
853880 }
......@@ -855,7 +882,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
855882
856883 var bucket_index = math.log2(size_class_hint);
857884 var size_class: usize = size_class_hint;
858 const bucket = while (bucket_index < small_bucket_count) : (bucket_index += 1) {
885 const bucket = while (bucket_index < used_small_bucket_count()) : (bucket_index += 1) {
859886 if (searchBucket(&self.buckets[bucket_index], @intFromPtr(old_mem.ptr), self.cur_buckets[bucket_index])) |bucket| {
860887 break bucket;
861888 }
......@@ -944,14 +971,14 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
944971 self.cur_buckets[bucket_index] = null;
945972 }
946973 if (!config.never_unmap) {
947 self.backing_allocator.free(bucket.page[0..page_size]);
974 self.backing_allocator.free(bucket.page[0..pageSize()]);
948975 }
949976 if (!config.retain_metadata) {
950977 self.freeBucket(bucket, size_class);
951978 self.bucket_node_pool.destroy(node);
952979 } else {
953980 // move alloc_cursor to end so we can tell size_class later
954 const slot_count = @divExact(page_size, size_class);
981 const slot_count = @divExact(pageSize(), size_class);
955982 bucket.alloc_cursor = @as(SlotIndex, @truncate(slot_count));
956983 var empty_entry = self.empty_buckets.getEntryFor(node.key);
957984 empty_entry.set(node);
......@@ -992,7 +1019,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
9921019 ret_addr: usize,
9931020 ) Allocator.Error![*]u8 {
9941021 const new_aligned_size = @max(len, @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_ptr_align)));
995 if (new_aligned_size > largest_bucket_object_size) {
1022 if (new_aligned_size > largest_used_bucket_object_size()) {
9961023 try self.large_allocations.ensureUnusedCapacity(self.backing_allocator, 1);
9971024 const ptr = self.backing_allocator.rawAlloc(len, log2_ptr_align, ret_addr) orelse
9981025 return error.OutOfMemory;
......@@ -1035,7 +1062,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
10351062 }
10361063
10371064 fn createBucket(self: *Self, size_class: usize) Error!*BucketHeader {
1038 const page = try self.backing_allocator.alignedAlloc(u8, page_size, page_size);
1065 const page = try self.backing_allocator.alignedAlloc(u8, min_page_size, pageSize());
10391066 errdefer self.backing_allocator.free(page);
10401067
10411068 const bucket_size = bucketSize(size_class);
......@@ -1179,17 +1206,17 @@ test "large object - grow" {
11791206 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
11801207 const allocator = gpa.allocator();
11811208
1182 var slice1 = try allocator.alloc(u8, page_size * 2 - 20);
1209 var slice1 = try allocator.alloc(u8, pageSize() * 2 - 20);
11831210 defer allocator.free(slice1);
11841211
11851212 const old = slice1;
1186 slice1 = try allocator.realloc(slice1, page_size * 2 - 10);
1213 slice1 = try allocator.realloc(slice1, pageSize() * 2 - 10);
11871214 try std.testing.expect(slice1.ptr == old.ptr);
11881215
1189 slice1 = try allocator.realloc(slice1, page_size * 2);
1216 slice1 = try allocator.realloc(slice1, pageSize() * 2);
11901217 try std.testing.expect(slice1.ptr == old.ptr);
11911218
1192 slice1 = try allocator.realloc(slice1, page_size * 2 + 1);
1219 slice1 = try allocator.realloc(slice1, pageSize() * 2 + 1);
11931220}
11941221
11951222test "realloc small object to large object" {
......@@ -1203,7 +1230,7 @@ test "realloc small object to large object" {
12031230 slice[60] = 0x34;
12041231
12051232 // This requires upgrading to a large object
1206 const large_object_size = page_size * 2 + 50;
1233 const large_object_size = pageSize() * 2 + 50;
12071234 slice = try allocator.realloc(slice, large_object_size);
12081235 try std.testing.expect(slice[0] == 0x12);
12091236 try std.testing.expect(slice[60] == 0x34);
......@@ -1214,22 +1241,22 @@ test "shrink large object to large object" {
12141241 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
12151242 const allocator = gpa.allocator();
12161243
1217 var slice = try allocator.alloc(u8, page_size * 2 + 50);
1244 var slice = try allocator.alloc(u8, pageSize() * 2 + 50);
12181245 defer allocator.free(slice);
12191246 slice[0] = 0x12;
12201247 slice[60] = 0x34;
12211248
1222 if (!allocator.resize(slice, page_size * 2 + 1)) return;
1223 slice = slice.ptr[0 .. page_size * 2 + 1];
1249 if (!allocator.resize(slice, pageSize() * 2 + 1)) return;
1250 slice = slice.ptr[0 .. pageSize() * 2 + 1];
12241251 try std.testing.expect(slice[0] == 0x12);
12251252 try std.testing.expect(slice[60] == 0x34);
12261253
1227 try std.testing.expect(allocator.resize(slice, page_size * 2 + 1));
1228 slice = slice[0 .. page_size * 2 + 1];
1254 try std.testing.expect(allocator.resize(slice, pageSize() * 2 + 1));
1255 slice = slice[0 .. pageSize() * 2 + 1];
12291256 try std.testing.expect(slice[0] == 0x12);
12301257 try std.testing.expect(slice[60] == 0x34);
12311258
1232 slice = try allocator.realloc(slice, page_size * 2);
1259 slice = try allocator.realloc(slice, pageSize() * 2);
12331260 try std.testing.expect(slice[0] == 0x12);
12341261 try std.testing.expect(slice[60] == 0x34);
12351262}
......@@ -1245,13 +1272,13 @@ test "shrink large object to large object with larger alignment" {
12451272 var fba = std.heap.FixedBufferAllocator.init(&debug_buffer);
12461273 const debug_allocator = fba.allocator();
12471274
1248 const alloc_size = page_size * 2 + 50;
1275 const alloc_size = pageSize() * 2 + 50;
12491276 var slice = try allocator.alignedAlloc(u8, 16, alloc_size);
12501277 defer allocator.free(slice);
12511278
12521279 const big_alignment: usize = switch (builtin.os.tag) {
1253 .windows => page_size * 32, // Windows aligns to 64K.
1254 else => page_size * 2,
1280 .windows => pageSize() * 32, // Windows aligns to 64K.
1281 else => pageSize() * 2,
12551282 };
12561283 // This loop allocates until we find a page that is not aligned to the big
12571284 // alignment. Then we shrink the allocation after the loop, but increase the
......@@ -1277,7 +1304,7 @@ test "realloc large object to small object" {
12771304 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
12781305 const allocator = gpa.allocator();
12791306
1280 var slice = try allocator.alloc(u8, page_size * 2 + 50);
1307 var slice = try allocator.alloc(u8, pageSize() * 2 + 50);
12811308 defer allocator.free(slice);
12821309 slice[0] = 0x12;
12831310 slice[16] = 0x34;
......@@ -1319,18 +1346,18 @@ test "realloc large object to larger alignment" {
13191346 var fba = std.heap.FixedBufferAllocator.init(&debug_buffer);
13201347 const debug_allocator = fba.allocator();
13211348
1322 var slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50);
1349 var slice = try allocator.alignedAlloc(u8, 16, pageSize() * 2 + 50);
13231350 defer allocator.free(slice);
13241351
13251352 const big_alignment: usize = switch (builtin.os.tag) {
1326 .windows => page_size * 32, // Windows aligns to 64K.
1327 else => page_size * 2,
1353 .windows => pageSize() * 32, // Windows aligns to 64K.
1354 else => pageSize() * 2,
13281355 };
13291356 // This loop allocates until we find a page that is not aligned to the big alignment.
13301357 var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);
13311358 while (mem.isAligned(@intFromPtr(slice.ptr), big_alignment)) {
13321359 try stuff_to_free.append(slice);
1333 slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50);
1360 slice = try allocator.alignedAlloc(u8, 16, pageSize() * 2 + 50);
13341361 }
13351362 while (stuff_to_free.popOrNull()) |item| {
13361363 allocator.free(item);
......@@ -1338,15 +1365,15 @@ test "realloc large object to larger alignment" {
13381365 slice[0] = 0x12;
13391366 slice[16] = 0x34;
13401367
1341 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100);
1368 slice = try allocator.reallocAdvanced(slice, 32, pageSize() * 2 + 100);
13421369 try std.testing.expect(slice[0] == 0x12);
13431370 try std.testing.expect(slice[16] == 0x34);
13441371
1345 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25);
1372 slice = try allocator.reallocAdvanced(slice, 32, pageSize() * 2 + 25);
13461373 try std.testing.expect(slice[0] == 0x12);
13471374 try std.testing.expect(slice[16] == 0x34);
13481375
1349 slice = try allocator.reallocAdvanced(slice, big_alignment, page_size * 2 + 100);
1376 slice = try allocator.reallocAdvanced(slice, big_alignment, pageSize() * 2 + 100);
13501377 try std.testing.expect(slice[0] == 0x12);
13511378 try std.testing.expect(slice[16] == 0x34);
13521379}
......@@ -1362,7 +1389,7 @@ test "large object shrinks to small but allocation fails during shrink" {
13621389 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
13631390 const allocator = gpa.allocator();
13641391
1365 var slice = try allocator.alloc(u8, page_size * 2 + 50);
1392 var slice = try allocator.alloc(u8, pageSize() * 2 + 50);
13661393 defer allocator.free(slice);
13671394 slice[0] = 0x12;
13681395 slice[3] = 0x34;
......@@ -1433,7 +1460,7 @@ test "double frees" {
14331460 try std.testing.expect(GPA.searchBucket(&gpa.empty_buckets, @intFromPtr(small.ptr), null) != null);
14341461
14351462 // detect a large allocation double free
1436 const large = try allocator.alloc(u8, 2 * page_size);
1463 const large = try allocator.alloc(u8, 2 * pageSize());
14371464 try std.testing.expect(gpa.large_allocations.contains(@intFromPtr(large.ptr)));
14381465 try std.testing.expectEqual(gpa.large_allocations.getEntry(@intFromPtr(large.ptr)).?.value_ptr.bytes, large);
14391466 allocator.free(large);
......@@ -1442,7 +1469,7 @@ test "double frees" {
14421469
14431470 const normal_small = try allocator.alloc(u8, size_class);
14441471 defer allocator.free(normal_small);
1445 const normal_large = try allocator.alloc(u8, 2 * page_size);
1472 const normal_large = try allocator.alloc(u8, 2 * pageSize());
14461473 defer allocator.free(normal_large);
14471474
14481475 // check that flushing retained metadata doesn't disturb live allocations
......@@ -1475,8 +1502,8 @@ test "bug 9995 fix, large allocs count requested size not backing size" {
14751502 var gpa = GeneralPurposeAllocator(.{ .enable_memory_limit = true }){};
14761503 const allocator = gpa.allocator();
14771504
1478 var buf = try allocator.alignedAlloc(u8, 1, page_size + 1);
1479 try std.testing.expect(gpa.total_requested_bytes == page_size + 1);
1505 var buf = try allocator.alignedAlloc(u8, 1, pageSize() + 1);
1506 try std.testing.expect(gpa.total_requested_bytes == pageSize() + 1);
14801507 buf = try allocator.realloc(buf, 1);
14811508 try std.testing.expect(gpa.total_requested_bytes == 1);
14821509 buf = try allocator.realloc(buf, 2);
lib/std/heap/sbrk_allocator.zig+4-3
......@@ -3,6 +3,7 @@ const builtin = @import("builtin");
33const math = std.math;
44const Allocator = std.mem.Allocator;
55const mem = std.mem;
6const heap = std.heap;
67const assert = std.debug.assert;
78
89pub fn SbrkAllocator(comptime sbrk: *const fn (n: usize) usize) type {
......@@ -18,7 +19,7 @@ pub fn SbrkAllocator(comptime sbrk: *const fn (n: usize) usize) type {
1819 const max_usize = math.maxInt(usize);
1920 const ushift = math.Log2Int(usize);
2021 const bigpage_size = 64 * 1024;
21 const pages_per_bigpage = bigpage_size / mem.page_size;
22 const pages_per_bigpage = bigpage_size / heap.pageSize();
2223 const bigpage_count = max_usize / bigpage_size;
2324
2425 /// Because of storing free list pointers, the minimum size class is 3.
......@@ -58,7 +59,7 @@ pub fn SbrkAllocator(comptime sbrk: *const fn (n: usize) usize) type {
5859 }
5960
6061 const next_addr = next_addrs[class];
61 if (next_addr % mem.page_size == 0) {
62 if (next_addr % heap.pageSize == 0) {
6263 const addr = allocBigPages(1);
6364 if (addr == 0) return null;
6465 //std.debug.print("allocated fresh slot_size={d} class={d} addr=0x{x}\n", .{
......@@ -153,7 +154,7 @@ pub fn SbrkAllocator(comptime sbrk: *const fn (n: usize) usize) type {
153154 big_frees[class] = node.*;
154155 return top_free_ptr;
155156 }
156 return sbrk(pow2_pages * pages_per_bigpage * mem.page_size);
157 return sbrk(pow2_pages * pages_per_bigpage * heap.pageSize());
157158 }
158159 };
159160}
lib/std/mem.zig+9-28
......@@ -8,26 +8,6 @@ const testing = std.testing;
88const Endian = std.builtin.Endian;
99const native_endian = builtin.cpu.arch.endian();
1010
11/// Compile time known minimum page size.
12/// https://github.com/ziglang/zig/issues/4082
13pub const page_size = switch (builtin.cpu.arch) {
14 .wasm32, .wasm64 => 64 * 1024,
15 .aarch64 => switch (builtin.os.tag) {
16 .macos, .ios, .watchos, .tvos, .visionos => 16 * 1024,
17 else => 4 * 1024,
18 },
19 .sparc64 => 8 * 1024,
20 .loongarch32, .loongarch64 => switch (builtin.os.tag) {
21 // Linux default KConfig value is 16KiB
22 .linux => 16 * 1024,
23 // FIXME:
24 // There is no other OS supported yet. Use the same value
25 // as Linux for now.
26 else => 16 * 1024,
27 },
28 else => 4 * 1024,
29};
30
3111/// The standard library currently thoroughly depends on byte size
3212/// being 8 bits. (see the use of u8 throughout allocation code as
3313/// the "byte" type.) Code which depends on this can reference this
......@@ -1072,12 +1052,13 @@ pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]co
10721052 const Block = @Vector(block_len, T);
10731053 const mask: Block = @splat(sentinel);
10741054
1075 comptime std.debug.assert(std.mem.page_size % block_size == 0);
1055 comptime std.debug.assert(std.heap.max_page_size % @sizeOf(Block) == 0);
1056 std.debug.assert(std.heap.pageSize() % @sizeOf(Block) == 0);
10761057
10771058 // First block may be unaligned
10781059 const start_addr = @intFromPtr(&p[i]);
1079 const offset_in_page = start_addr & (std.mem.page_size - 1);
1080 if (offset_in_page <= std.mem.page_size - block_size) {
1060 const offset_in_page = start_addr & (std.heap.pageSize() - 1);
1061 if (offset_in_page <= std.heap.pageSize() - @sizeOf(Block)) {
10811062 // Will not read past the end of a page, full block.
10821063 const block: Block = p[i..][0..block_len].*;
10831064 const matches = block == mask;
......@@ -1125,18 +1106,18 @@ test "indexOfSentinel vector paths" {
11251106 const block_len = std.simd.suggestVectorLength(T) orelse continue;
11261107
11271108 // Allocate three pages so we guarantee a page-crossing address with a full page after
1128 const memory = try allocator.alloc(T, 3 * std.mem.page_size / @sizeOf(T));
1109 const memory = try allocator.alloc(T, 3 * std.heap.pageSize() / @sizeOf(T));
11291110 defer allocator.free(memory);
11301111 @memset(memory, 0xaa);
11311112
11321113 // Find starting page-alignment = 0
11331114 var start: usize = 0;
11341115 const start_addr = @intFromPtr(&memory);
1135 start += (std.mem.alignForward(usize, start_addr, std.mem.page_size) - start_addr) / @sizeOf(T);
1136 try testing.expect(start < std.mem.page_size / @sizeOf(T));
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));
11371118
11381119 // Validate all sub-block alignments
1139 const search_len = std.mem.page_size / @sizeOf(T);
1120 const search_len = std.heap.pageSize() / @sizeOf(T);
11401121 memory[start + search_len] = 0;
11411122 for (0..block_len) |offset| {
11421123 try testing.expectEqual(search_len - offset, indexOfSentinel(T, 0, @ptrCast(&memory[start + offset])));
......@@ -1144,7 +1125,7 @@ test "indexOfSentinel vector paths" {
11441125 memory[start + search_len] = 0xaa;
11451126
11461127 // Validate page boundary crossing
1147 const start_page_boundary = start + (std.mem.page_size / @sizeOf(T));
1128 const start_page_boundary = start + (std.heap.pageSize() / @sizeOf(T));
11481129 memory[start_page_boundary + block_len] = 0;
11491130 for (0..block_len) |offset| {
11501131 try testing.expectEqual(2 * block_len - offset, indexOfSentinel(T, 0, @ptrCast(&memory[start_page_boundary - block_len + offset])));
lib/std/mem/Allocator.zig+1-1
......@@ -218,7 +218,7 @@ fn allocBytesWithAlignment(self: Allocator, comptime alignment: u29, byte_count:
218218 // The Zig Allocator interface is not intended to solve alignments beyond
219219 // the minimum OS page size. For these use cases, the caller must use OS
220220 // APIs directly.
221 comptime assert(alignment <= mem.page_size);
221 if (!@inComptime() and alignment > std.heap.pageSize()) @panic("Alignment must be smaller than page size.");
222222
223223 if (byte_count == 0) {
224224 const ptr = comptime std.mem.alignBackward(usize, math.maxInt(usize), alignment);
lib/std/os/linux/IoUring.zig+8-7
......@@ -3,6 +3,7 @@ const std = @import("std");
33const builtin = @import("builtin");
44const assert = std.debug.assert;
55const mem = std.mem;
6const heap = std.heap;
67const net = std.net;
78const posix = std.posix;
89const linux = std.os.linux;
......@@ -1341,8 +1342,8 @@ pub const SubmissionQueue = struct {
13411342 dropped: *u32,
13421343 array: []u32,
13431344 sqes: []linux.io_uring_sqe,
1344 mmap: []align(mem.page_size) u8,
1345 mmap_sqes: []align(mem.page_size) u8,
1345 mmap: []align(heap.min_page_size) u8,
1346 mmap_sqes: []align(heap.min_page_size) u8,
13461347
13471348 // We use `sqe_head` and `sqe_tail` in the same way as liburing:
13481349 // We increment `sqe_tail` (but not `tail`) for each call to `get_sqe()`.
......@@ -1460,7 +1461,7 @@ pub const BufferGroup = struct {
14601461 /// Pointer to the memory shared by the kernel.
14611462 /// `buffers_count` of `io_uring_buf` structures are shared by the kernel.
14621463 /// First `io_uring_buf` is overlaid by `io_uring_buf_ring` struct.
1463 br: *align(mem.page_size) linux.io_uring_buf_ring,
1464 br: *align(heap.min_page_size) linux.io_uring_buf_ring,
14641465 /// Contiguous block of memory of size (buffers_count * buffer_size).
14651466 buffers: []u8,
14661467 /// Size of each buffer in buffers.
......@@ -1555,7 +1556,7 @@ pub const BufferGroup = struct {
15551556/// `fd` is IO_Uring.fd for which the provided buffer ring is being registered.
15561557/// `entries` is the number of entries requested in the buffer ring, must be power of 2.
15571558/// `group_id` is the chosen buffer group ID, unique in IO_Uring.
1558pub fn setup_buf_ring(fd: posix.fd_t, entries: u16, group_id: u16) !*align(mem.page_size) linux.io_uring_buf_ring {
1559pub fn setup_buf_ring(fd: posix.fd_t, entries: u16, group_id: u16) !*align(heap.min_page_size) linux.io_uring_buf_ring {
15591560 if (entries == 0 or entries > 1 << 15) return error.EntriesNotInRange;
15601561 if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo;
15611562
......@@ -1571,7 +1572,7 @@ pub fn setup_buf_ring(fd: posix.fd_t, entries: u16, group_id: u16) !*align(mem.p
15711572 errdefer posix.munmap(mmap);
15721573 assert(mmap.len == mmap_size);
15731574
1574 const br: *align(mem.page_size) linux.io_uring_buf_ring = @ptrCast(mmap.ptr);
1575 const br: *align(heap.min_page_size) linux.io_uring_buf_ring = @ptrCast(mmap.ptr);
15751576 try register_buf_ring(fd, @intFromPtr(br), entries, group_id);
15761577 return br;
15771578}
......@@ -1613,9 +1614,9 @@ fn handle_register_buf_ring_result(res: usize) !void {
16131614}
16141615
16151616// Unregisters a previously registered shared buffer ring, returned from io_uring_setup_buf_ring.
1616pub fn free_buf_ring(fd: posix.fd_t, br: *align(mem.page_size) linux.io_uring_buf_ring, entries: u32, group_id: u16) void {
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 {
16171618 unregister_buf_ring(fd, group_id) catch {};
1618 var mmap: []align(mem.page_size) u8 = undefined;
1619 var mmap: []align(heap.min_page_size) u8 = undefined;
16191620 mmap.ptr = @ptrCast(br);
16201621 mmap.len = entries * @sizeOf(linux.io_uring_buf);
16211622 posix.munmap(mmap);
lib/std/os/linux/tls.zig+4-3
......@@ -11,6 +11,7 @@
1111
1212const std = @import("std");
1313const mem = std.mem;
14const heap = std.heap;
1415const elf = std.elf;
1516const math = std.math;
1617const assert = std.debug.assert;
......@@ -490,7 +491,7 @@ pub fn prepareArea(area: []u8) usize {
490491// and LLVM or LLD is not smart enough to lay out the TLS data in a space-conserving way. Anyway, I
491492// think it's fine because it's less than 3 pages of memory, and putting it in the ELF like this is
492493// equivalent to moving the `mmap` call below into the kernel, avoiding syscall overhead.
493var main_thread_area_buffer: [0x2100]u8 align(mem.page_size) = undefined;
494var main_thread_area_buffer: [0x2100]u8 align(heap.min_page_size) = undefined;
494495
495496/// Computes the layout of the static TLS area, allocates the area, initializes all of its fields,
496497/// and assigns the architecture-specific value to the TP register.
......@@ -503,7 +504,7 @@ pub fn initStatic(phdrs: []elf.Phdr) void {
503504 const area = blk: {
504505 // Fast path for the common case where the TLS data is really small, avoid an allocation and
505506 // use our local buffer.
506 if (area_desc.alignment <= mem.page_size and area_desc.size <= main_thread_area_buffer.len) {
507 if (area_desc.alignment <= heap.min_page_size and area_desc.size <= main_thread_area_buffer.len) {
507508 break :blk main_thread_area_buffer[0..area_desc.size];
508509 }
509510
......@@ -517,7 +518,7 @@ pub fn initStatic(phdrs: []elf.Phdr) void {
517518 );
518519 if (@as(isize, @bitCast(begin_addr)) < 0) @trap();
519520
520 const area_ptr: [*]align(mem.page_size) u8 = @ptrFromInt(begin_addr);
521 const area_ptr: [*]align(heap.min_page_size) u8 = @ptrFromInt(begin_addr);
521522
522523 // Make sure the slice is correctly aligned.
523524 const begin_aligned_addr = alignForward(begin_addr, area_desc.alignment);
lib/std/os/plan9.zig+2-2
......@@ -367,8 +367,8 @@ pub fn sbrk(n: usize) usize {
367367 bloc = @intFromPtr(&ExecData.end);
368368 bloc_max = @intFromPtr(&ExecData.end);
369369 }
370 const bl = std.mem.alignForward(usize, bloc, std.mem.page_size);
371 const n_aligned = std.mem.alignForward(usize, n, std.mem.page_size);
370 const bl = std.mem.alignForward(usize, bloc, std.heap.pageSize());
371 const n_aligned = std.mem.alignForward(usize, n, std.heap.pageSize());
372372 if (bl + n_aligned > bloc_max) {
373373 // we need to allocate
374374 if (brk_(bl + n_aligned) < 0) return 0;
lib/std/os/windows/kernel32.zig+4-3
......@@ -42,6 +42,7 @@ const WCHAR = windows.WCHAR;
4242const WIN32_FIND_DATAW = windows.WIN32_FIND_DATAW;
4343const Win32Error = windows.Win32Error;
4444const WORD = windows.WORD;
45const SYSTEM_INFO = windows.SYSTEM_INFO;
4546
4647// I/O - Filesystem
4748
......@@ -667,6 +668,6 @@ pub extern "kernel32" fn SetLastError(
667668// TODO:
668669// Wrapper around KUSER_SHARED_DATA.SystemTime.
669670// Much better to use NtQuerySystemTime or NtQuerySystemTimePrecise for guaranteed 0.1ns precision.
670pub extern "kernel32" fn GetSystemTimeAsFileTime(
671 lpSystemTimeAsFileTime: *FILETIME,
672) callconv(.winapi) void;
671pub extern "kernel32" fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: *FILETIME) callconv(.winapi) void;
672
673pub extern "kernel32" fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) callconv(.winapi) void;
lib/std/posix.zig+10-9
......@@ -18,6 +18,7 @@ const builtin = @import("builtin");
1818const root = @import("root");
1919const std = @import("std.zig");
2020const mem = std.mem;
21const heap = std.heap;
2122const fs = std.fs;
2223const max_path_bytes = fs.max_path_bytes;
2324const maxInt = std.math.maxInt;
......@@ -4694,7 +4695,7 @@ pub const MProtectError = error{
46944695 OutOfMemory,
46954696} || UnexpectedError;
46964697
4697pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void {
4698pub fn mprotect(memory: []align(heap.min_page_size) u8, protection: u32) MProtectError!void {
46984699 if (native_os == .windows) {
46994700 const win_prot: windows.DWORD = switch (@as(u3, @truncate(protection))) {
47004701 0b000 => windows.PAGE_NOACCESS,
......@@ -4759,21 +4760,21 @@ pub const MMapError = error{
47594760/// * SIGSEGV - Attempted write into a region mapped as read-only.
47604761/// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file
47614762pub fn mmap(
4762 ptr: ?[*]align(mem.page_size) u8,
4763 ptr: ?[*]align(heap.min_page_size) u8,
47634764 length: usize,
47644765 prot: u32,
47654766 flags: system.MAP,
47664767 fd: fd_t,
47674768 offset: u64,
4768) MMapError![]align(mem.page_size) u8 {
4769) MMapError![]align(heap.min_page_size) u8 {
47694770 const mmap_sym = if (lfs64_abi) system.mmap64 else system.mmap;
47704771 const rc = mmap_sym(ptr, length, prot, @bitCast(flags), fd, @bitCast(offset));
47714772 const err: E = if (builtin.link_libc) blk: {
4772 if (rc != std.c.MAP_FAILED) return @as([*]align(mem.page_size) u8, @ptrCast(@alignCast(rc)))[0..length];
4773 if (rc != std.c.MAP_FAILED) return @as([*]align(heap.min_page_size) u8, @ptrCast(@alignCast(rc)))[0..length];
47734774 break :blk @enumFromInt(system._errno().*);
47744775 } else blk: {
47754776 const err = errno(rc);
4776 if (err == .SUCCESS) return @as([*]align(mem.page_size) u8, @ptrFromInt(rc))[0..length];
4777 if (err == .SUCCESS) return @as([*]align(heap.min_page_size) u8, @ptrFromInt(rc))[0..length];
47774778 break :blk err;
47784779 };
47794780 switch (err) {
......@@ -4799,7 +4800,7 @@ pub fn mmap(
47994800/// Zig's munmap function does not, for two reasons:
48004801/// * It violates the Zig principle that resource deallocation must succeed.
48014802/// * The Windows function, VirtualFree, has this restriction.
4802pub fn munmap(memory: []align(mem.page_size) const u8) void {
4803pub fn munmap(memory: []align(heap.min_page_size) const u8) void {
48034804 switch (errno(system.munmap(memory.ptr, memory.len))) {
48044805 .SUCCESS => return,
48054806 .INVAL => unreachable, // Invalid parameters.
......@@ -4813,7 +4814,7 @@ pub const MSyncError = error{
48134814 PermissionDenied,
48144815} || UnexpectedError;
48154816
4816pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void {
4817pub fn msync(memory: []align(heap.min_page_size) u8, flags: i32) MSyncError!void {
48174818 switch (errno(system.msync(memory.ptr, memory.len, flags))) {
48184819 .SUCCESS => return,
48194820 .PERM => return error.PermissionDenied,
......@@ -7135,7 +7136,7 @@ pub const MincoreError = error{
71357136} || UnexpectedError;
71367137
71377138/// Determine whether pages are resident in memory.
7138pub fn mincore(ptr: [*]align(mem.page_size) u8, length: usize, vec: [*]u8) MincoreError!void {
7139pub fn mincore(ptr: [*]align(heap.min_page_size) u8, length: usize, vec: [*]u8) MincoreError!void {
71397140 return switch (errno(system.mincore(ptr, length, vec))) {
71407141 .SUCCESS => {},
71417142 .AGAIN => error.SystemResources,
......@@ -7181,7 +7182,7 @@ pub const MadviseError = error{
71817182
71827183/// Give advice about use of memory.
71837184/// This syscall is optional and is sometimes configured to be disabled.
7184pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
7185pub fn madvise(ptr: [*]align(heap.min_page_size) u8, length: usize, advice: u32) MadviseError!void {
71857186 switch (errno(system.madvise(ptr, length, advice))) {
71867187 .SUCCESS => return,
71877188 .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.mem.page_size]u8 = undefined;
1563 var buf: [std.heap.min_page_size]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.mem.page_size == 0);
579 assert(phdr.p_memsz % std.heap.pageSize() == 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+4
......@@ -119,6 +119,10 @@ 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,
125
122126 fmt_max_depth: usize = fmt.default_max_depth,
123127
124128 cryptoRandomSeed: fn (buffer: []u8) void = @import("crypto/tlcsprng.zig").defaultRandomSeed,
lib/std/zip.zig+1-1
......@@ -162,7 +162,7 @@ pub fn decompress(
162162 var total_uncompressed: u64 = 0;
163163 switch (method) {
164164 .store => {
165 var buf: [std.mem.page_size]u8 = undefined;
165 var buf: [4096]u8 = undefined;
166166 while (true) {
167167 const len = try reader.read(&buf);
168168 if (len == 0) break;
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.mem.page_size]u8 = undefined;
1252 var buf: [std.heap.min_page_size]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}",