authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-22 11:41:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-22 11:41:21-07:00
loge2d1f9874df2a9221aaa9ec55bd2974b70601f64
treeeff7919b0717e193aa53b70fcee862d6f33deddb
parent52b8239a22aa37fe3914427cd4e2905231769e59
parent58ee5f4e61cd9b7a9ba65798e2214efa3753a733

Merge remote-tracking branch 'origin/master' into llvm11


86 files changed, 5961 insertions(+), 510 deletions(-)

.builds/freebsd.yml+2-2
......@@ -1,7 +1,7 @@
11image: freebsd/latest
22secrets:
3 - 6c60aaee-92e7-4e7d-812c-114817689b4d
4 - dd0bd962-7664-4d3e-b0f3-41c9ee96b8b8
3 - 51bfddf5-86a6-4e01-8576-358c72a4a0a4
4 - 5cfede76-914e-4071-893e-e5e2e6ae3cea
55sources:
66 - https://github.com/ziglang/zig
77tasks:
README.md+18
......@@ -68,3 +68,21 @@ make install
6868##### Windows
6969
7070See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows
71
72## License
73
74The ultimate goal of the Zig project is to serve users. As a first-order
75effect, this means users of the compiler, helping programmers to write better
76code. Even more important, however, are the end users.
77
78Zig is intended to be used to help end users accomplish their goals. For
79example, it would be inappropriate and offensive to use Zig to implement
80[dark patterns](https://en.wikipedia.org/wiki/Dark_pattern) and it would be
81shameful to utilize Zig to exploit people instead of benefit them.
82
83However, such problems are best solved with social norms, not with software
84licenses. Any attempt to complicate the software license of Zig would risk
85compromising the value Zig provides to users.
86
87Therefore, Zig is available under the MIT (Expat) License, and comes with a
88humble request: use it to make software better serve the needs of end users.
build.zig+8-1
......@@ -123,7 +123,14 @@ pub fn build(b: *Builder) !void {
123123 .source_dir = "lib",
124124 .install_dir = .Lib,
125125 .install_subdir = "zig",
126 .exclude_extensions = &[_][]const u8{ "test.zig", "README.md" },
126 .exclude_extensions = &[_][]const u8{
127 "test.zig",
128 "README.md",
129 ".z.0",
130 ".z.9",
131 ".gz",
132 "rfc1951.txt",
133 },
127134 });
128135
129136 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
ci/azure/windows_msvc_script.bat+1-1
......@@ -24,7 +24,7 @@ cd %ZIGBUILDDIR%
2424cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b
2525msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b
2626
27"%ZIGINSTALLDIR%\bin\zig.exe" build test -Dskip-compile-errors || exit /b
27"%ZIGINSTALLDIR%\bin\zig.exe" build test -Dskip-non-native -Dskip-compile-errors || exit /b
2828
2929set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem"
3030SET "MSYSTEM=MINGW64"
ci/srht/freebsd_script+4-6
......@@ -28,11 +28,8 @@ make $JOBS install
2828
2929release/bin/zig build test-fmt
3030release/bin/zig build test-behavior
31
32# This test is disabled because it triggers "out of memory" on the sr.ht CI service.
33# See https://github.com/ziglang/zig/issues/3210
34# release/bin/zig build test-std
35
31# TODO get these tests passing on freebsd and re-enable
32#release/bin/zig build test-std
3633release/bin/zig build test-compiler-rt
3734release/bin/zig build test-compare-output
3835release/bin/zig build test-standalone
......@@ -44,7 +41,8 @@ release/bin/zig build test-translate-c
4441release/bin/zig build test-run-translated-c
4542# TODO disabled until we are shipping self-hosted
4643#release/bin/zig build test-gen-h
47release/bin/zig build test-compile-errors
44# TODO disabled to save time and hit that 45 minute limit
45#release/bin/zig build test-compile-errors
4846release/bin/zig build docs
4947
5048if [ -f ~/.s3cfg ]; then
ci/srht/on_master_success+2-2
......@@ -23,7 +23,7 @@ packages:
2323 - jq
2424 - xz
2525secrets:
26 - 6c60aaee-92e7-4e7d-812c-114817689b4d
26 - 51bfddf5-86a6-4e01-8576-358c72a4a0a4
2727sources:
2828 - https://github.com/ziglang/zig
2929tasks:
......@@ -36,4 +36,4 @@ jq <$YML_FILE -sR '{
3636 -H Authorization:"token $OAUTH_TOKEN" \
3737 -H Content-Type:application/json \
3838 -X POST \
39 -d @- "https://builds.sr.ht/api/jobs"
39 -d @- "https://builds.hut.lavatech.top/api/jobs"
doc/langref.html.in+1-1
......@@ -9728,7 +9728,7 @@ const c = @cImport({
97289728 <li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#}
97299729 please!</li>
97309730 </ul>
9731 <p>When a C pointer is pointing to a single struct (not an array), deference the C pointer to
9731 <p>When a C pointer is pointing to a single struct (not an array), dereference the C pointer to
97329732 access to the struct's fields or member data. That syntax looks like
97339733 this: </p>
97349734 <p>{#syntax#}ptr_to_struct.*.struct_member{#endsyntax#}</p>
lib/std/build.zig+82-44
......@@ -258,9 +258,14 @@ pub const Builder = struct {
258258 }));
259259 }
260260
261 pub fn addSharedLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep {
261 pub fn addSharedLibrary(
262 self: *Builder,
263 name: []const u8,
264 root_src: ?[]const u8,
265 kind: LibExeObjStep.SharedLibKind,
266 ) *LibExeObjStep {
262267 const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null;
263 return LibExeObjStep.createSharedLibrary(self, name, root_src_param, ver);
268 return LibExeObjStep.createSharedLibrary(self, name, root_src_param, kind);
264269 }
265270
266271 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
......@@ -338,11 +343,13 @@ pub const Builder = struct {
338343 return TranslateCStep.create(self, source);
339344 }
340345
341 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version {
342 return Version{
343 .major = major,
344 .minor = minor,
345 .patch = patch,
346 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind {
347 return .{
348 .versioned = .{
349 .major = major,
350 .minor = minor,
351 .patch = patch,
352 },
346353 };
347354 }
348355
......@@ -1048,6 +1055,7 @@ pub const Builder = struct {
10481055 .Bin => self.exe_dir,
10491056 .Lib => self.lib_dir,
10501057 .Header => self.h_dir,
1058 .Custom => |path| fs.path.join(self.allocator, &[_][]const u8{ self.install_path, path }) catch unreachable,
10511059 };
10521060 return fs.path.resolve(
10531061 self.allocator,
......@@ -1166,7 +1174,7 @@ pub const LibExeObjStep = struct {
11661174 version_script: ?[]const u8 = null,
11671175 out_filename: []const u8,
11681176 is_dynamic: bool,
1169 version: Version,
1177 version: ?Version,
11701178 build_mode: builtin.Mode,
11711179 kind: Kind,
11721180 major_only_filename: []const u8,
......@@ -1180,6 +1188,7 @@ pub const LibExeObjStep = struct {
11801188 emit_llvm_ir: bool = false,
11811189 emit_asm: bool = false,
11821190 emit_bin: bool = true,
1191 emit_docs: bool = false,
11831192 emit_h: bool = false,
11841193 bundle_compiler_rt: bool,
11851194 disable_stack_probing: bool,
......@@ -1212,6 +1221,8 @@ pub const LibExeObjStep = struct {
12121221 is_linking_libc: bool = false,
12131222 vcpkg_bin_path: ?[]const u8 = null,
12141223
1224 /// This may be set in order to override the default install directory
1225 override_dest_dir: ?InstallDir,
12151226 installed_path: ?[]const u8,
12161227 install_step: ?*InstallArtifactStep,
12171228
......@@ -1268,33 +1279,41 @@ pub const LibExeObjStep = struct {
12681279 Test,
12691280 };
12701281
1271 pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, ver: Version) *LibExeObjStep {
1282 const SharedLibKind = union(enum) {
1283 versioned: Version,
1284 unversioned: void,
1285 };
1286
1287 pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep {
12721288 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1273 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver);
1289 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, switch (kind) {
1290 .versioned => |ver| ver,
1291 .unversioned => null,
1292 });
12741293 return self;
12751294 }
12761295
12771296 pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
12781297 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1279 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0));
1298 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, null);
12801299 return self;
12811300 }
12821301
12831302 pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
12841303 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1285 self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0));
1304 self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, null);
12861305 return self;
12871306 }
12881307
12891308 pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep {
12901309 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1291 self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0));
1310 self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, null);
12921311 return self;
12931312 }
12941313
12951314 pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
12961315 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1297 self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0));
1316 self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, null);
12981317 return self;
12991318 }
13001319
......@@ -1304,7 +1323,7 @@ pub const LibExeObjStep = struct {
13041323 root_src: ?FileSource,
13051324 kind: Kind,
13061325 is_dynamic: bool,
1307 ver: Version,
1326 ver: ?Version,
13081327 ) LibExeObjStep {
13091328 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
13101329 panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
......@@ -1348,6 +1367,7 @@ pub const LibExeObjStep = struct {
13481367 .rdynamic = false,
13491368 .output_dir = null,
13501369 .single_threaded = false,
1370 .override_dest_dir = null,
13511371 .installed_path = null,
13521372 .install_step = null,
13531373 };
......@@ -1375,17 +1395,17 @@ pub const LibExeObjStep = struct {
13751395 self.target.staticLibSuffix(),
13761396 });
13771397 self.out_lib_filename = self.out_filename;
1378 } else {
1398 } else if (self.version) |version| {
13791399 if (self.target.isDarwin()) {
13801400 self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", .{
13811401 self.name,
1382 self.version.major,
1383 self.version.minor,
1384 self.version.patch,
1402 version.major,
1403 version.minor,
1404 version.patch,
13851405 });
13861406 self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{
13871407 self.name,
1388 self.version.major,
1408 version.major,
13891409 });
13901410 self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name});
13911411 self.out_lib_filename = self.out_filename;
......@@ -1395,14 +1415,25 @@ pub const LibExeObjStep = struct {
13951415 } else {
13961416 self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", .{
13971417 self.name,
1398 self.version.major,
1399 self.version.minor,
1400 self.version.patch,
1418 version.major,
1419 version.minor,
1420 version.patch,
14011421 });
1402 self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, self.version.major });
1422 self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, version.major });
14031423 self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name});
14041424 self.out_lib_filename = self.out_filename;
14051425 }
1426 } else {
1427 if (self.target.isDarwin()) {
1428 self.out_filename = self.builder.fmt("lib{}.dylib", .{self.name});
1429 self.out_lib_filename = self.out_filename;
1430 } else if (self.target.isWindows()) {
1431 self.out_filename = self.builder.fmt("{}.dll", .{self.name});
1432 self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name});
1433 } else {
1434 self.out_filename = self.builder.fmt("lib{}.so", .{self.name});
1435 self.out_lib_filename = self.out_filename;
1436 }
14061437 }
14071438 },
14081439 }
......@@ -2003,6 +2034,7 @@ pub const LibExeObjStep = struct {
20032034 if (self.emit_llvm_ir) try zig_args.append("-femit-llvm-ir");
20042035 if (self.emit_asm) try zig_args.append("-femit-asm");
20052036 if (!self.emit_bin) try zig_args.append("-fno-emit-bin");
2037 if (self.emit_docs) try zig_args.append("-femit-docs");
20062038 if (self.emit_h) try zig_args.append("-femit-h");
20072039
20082040 if (self.strip) {
......@@ -2037,14 +2069,16 @@ pub const LibExeObjStep = struct {
20372069 zig_args.append(self.name) catch unreachable;
20382070
20392071 if (self.kind == Kind.Lib and self.is_dynamic) {
2040 zig_args.append("--ver-major") catch unreachable;
2041 zig_args.append(builder.fmt("{}", .{self.version.major})) catch unreachable;
2072 if (self.version) |version| {
2073 zig_args.append("--ver-major") catch unreachable;
2074 zig_args.append(builder.fmt("{}", .{version.major})) catch unreachable;
20422075
2043 zig_args.append("--ver-minor") catch unreachable;
2044 zig_args.append(builder.fmt("{}", .{self.version.minor})) catch unreachable;
2076 zig_args.append("--ver-minor") catch unreachable;
2077 zig_args.append(builder.fmt("{}", .{version.minor})) catch unreachable;
20452078
2046 zig_args.append("--ver-patch") catch unreachable;
2047 zig_args.append(builder.fmt("{}", .{self.version.patch})) catch unreachable;
2079 zig_args.append("--ver-patch") catch unreachable;
2080 zig_args.append(builder.fmt("{}", .{version.patch})) catch unreachable;
2081 }
20482082 }
20492083 if (self.is_dynamic) {
20502084 try zig_args.append("-dynamic");
......@@ -2285,7 +2319,7 @@ pub const LibExeObjStep = struct {
22852319 }
22862320 }
22872321
2288 if (self.kind == Kind.Lib and self.is_dynamic and self.target.wantSharedLibSymLinks()) {
2322 if (self.kind == Kind.Lib and self.is_dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
22892323 try doAtomicSymLinks(builder.allocator, self.getOutputPath(), self.major_only_filename, self.name_only_filename);
22902324 }
22912325 }
......@@ -2309,17 +2343,17 @@ pub const InstallArtifactStep = struct {
23092343 .builder = builder,
23102344 .step = Step.init(.InstallArtifact, builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make),
23112345 .artifact = artifact,
2312 .dest_dir = switch (artifact.kind) {
2346 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
23132347 .Obj => unreachable,
23142348 .Test => unreachable,
2315 .Exe => .Bin,
2316 .Lib => .Lib,
2349 .Exe => InstallDir{ .Bin = {} },
2350 .Lib => InstallDir{ .Lib = {} },
23172351 },
23182352 .pdb_dir = if (artifact.producesPdbFile()) blk: {
23192353 if (artifact.kind == .Exe) {
2320 break :blk InstallDir.Bin;
2354 break :blk InstallDir{ .Bin = {} };
23212355 } else {
2322 break :blk InstallDir.Lib;
2356 break :blk InstallDir{ .Lib = {} };
23232357 }
23242358 } else null,
23252359 .h_dir = if (artifact.kind == .Lib and artifact.emit_h) .Header else null,
......@@ -2329,8 +2363,10 @@ pub const InstallArtifactStep = struct {
23292363
23302364 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
23312365 if (self.artifact.isDynamicLibrary()) {
2332 builder.pushInstalledFile(.Lib, artifact.major_only_filename);
2333 builder.pushInstalledFile(.Lib, artifact.name_only_filename);
2366 if (self.artifact.version != null) {
2367 builder.pushInstalledFile(.Lib, artifact.major_only_filename);
2368 builder.pushInstalledFile(.Lib, artifact.name_only_filename);
2369 }
23342370 if (self.artifact.target.isWindows()) {
23352371 builder.pushInstalledFile(.Lib, artifact.out_lib_filename);
23362372 }
......@@ -2350,7 +2386,7 @@ pub const InstallArtifactStep = struct {
23502386
23512387 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
23522388 try builder.updateFile(self.artifact.getOutputPath(), full_dest_path);
2353 if (self.artifact.isDynamicLibrary() and self.artifact.target.wantSharedLibSymLinks()) {
2389 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
23542390 try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename);
23552391 }
23562392 if (self.pdb_dir) |pdb_dir| {
......@@ -2615,11 +2651,13 @@ const VcpkgRootStatus = enum {
26152651
26162652pub const VcpkgLinkage = std.builtin.LinkMode;
26172653
2618pub const InstallDir = enum {
2619 Prefix,
2620 Lib,
2621 Bin,
2622 Header,
2654pub const InstallDir = union(enum) {
2655 Prefix: void,
2656 Lib: void,
2657 Bin: void,
2658 Header: void,
2659 /// A path relative to the prefix
2660 Custom: []const u8,
26232661};
26242662
26252663pub const InstalledFile = struct {
lib/std/builtin.zig-1
......@@ -317,7 +317,6 @@ pub const TypeInfo = union(enum) {
317317 /// therefore must be kept in sync with the compiler implementation.
318318 pub const UnionField = struct {
319319 name: []const u8,
320 enum_field: ?EnumField,
321320 field_type: type,
322321 };
323322
lib/std/c.zig+11-4
......@@ -132,8 +132,6 @@ pub usingnamespace switch (builtin.os.tag) {
132132 },
133133};
134134
135pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
136pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
137135pub extern "c" fn rmdir(path: [*:0]const u8) c_int;
138136pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8;
139137pub extern "c" fn sysctl(name: [*]const c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
......@@ -237,8 +235,15 @@ pub usingnamespace switch (builtin.os.tag) {
237235
238236pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
239237pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
240pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;
241pub extern "c" fn setuid(uid: c_uint) c_int;
238
239pub extern "c" fn setuid(uid: uid_t) c_int;
240pub extern "c" fn setgid(gid: gid_t) c_int;
241pub extern "c" fn seteuid(euid: uid_t) c_int;
242pub extern "c" fn setegid(egid: gid_t) c_int;
243pub extern "c" fn setreuid(ruid: uid_t, euid: uid_t) c_int;
244pub extern "c" fn setregid(rgid: gid_t, egid: gid_t) c_int;
245pub extern "c" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) c_int;
246pub extern "c" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) c_int;
242247
243248pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;
244249pub extern "c" fn malloc(usize) ?*c_void;
......@@ -335,3 +340,5 @@ pub extern "c" fn sync() void;
335340pub extern "c" fn syncfs(fd: c_int) c_int;
336341pub extern "c" fn fsync(fd: c_int) c_int;
337342pub extern "c" fn fdatasync(fd: c_int) c_int;
343
344pub extern "c" fn prctl(option: c_int, ...) c_int;
lib/std/compress.zig created+15
......@@ -0,0 +1,15 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("std.zig");
7
8pub const deflate = @import("compress/deflate.zig");
9pub const gzip = @import("compress/gzip.zig");
10pub const zlib = @import("compress/zlib.zig");
11
12test "" {
13 _ = gzip;
14 _ = zlib;
15}
lib/std/compress/deflate.zig created+635
......@@ -0,0 +1,635 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6//
7// Decompressor for DEFLATE data streams (RFC1951)
8//
9// Heavily inspired by the simple decompressor puff.c by Mark Adler
10
11const std = @import("std");
12const io = std.io;
13const math = std.math;
14const mem = std.mem;
15
16const assert = std.debug.assert;
17
18const MAXBITS = 15;
19const MAXLCODES = 286;
20const MAXDCODES = 30;
21const MAXCODES = MAXLCODES + MAXDCODES;
22const FIXLCODES = 288;
23
24// The maximum length of a Huffman code's prefix we can decode using the fast
25// path. The factor 9 is inherited from Zlib, tweaking the value showed little
26// or no changes in the profiler output.
27const PREFIX_LUT_BITS = 9;
28
29const Huffman = struct {
30 // Number of codes for each possible length
31 count: [MAXBITS + 1]u16,
32 // Mapping between codes and symbols
33 symbol: [MAXCODES]u16,
34
35 // The decoding process uses a trick explained by Mark Adler in [1].
36 // We basically precompute for a fixed number of codes (0 <= x <= 2^N-1)
37 // the symbol and the effective code length we'd get if the decoder was run
38 // on the given N-bit sequence.
39 // A code with length 0 means the sequence is not a valid prefix for this
40 // canonical Huffman code and we have to decode it using a slower method.
41 //
42 // [1] https://github.com/madler/zlib/blob/v1.2.11/doc/algorithm.txt#L58
43 prefix_lut: [1 << PREFIX_LUT_BITS]u16,
44 prefix_lut_len: [1 << PREFIX_LUT_BITS]u16,
45 // The following info refer to the codes of length PREFIX_LUT_BITS+1 and are
46 // used to bootstrap the bit-by-bit reading method if the fast-path fails.
47 last_code: u16,
48 last_index: u16,
49
50 fn construct(self: *Huffman, code_length: []const u16) !void {
51 for (self.count) |*val| {
52 val.* = 0;
53 }
54
55 for (code_length) |len| {
56 self.count[len] += 1;
57 }
58
59 // All zero.
60 if (self.count[0] == code_length.len)
61 return;
62
63 var left: isize = 1;
64 for (self.count[1..]) |val| {
65 // Each added bit doubles the amount of codes.
66 left *= 2;
67 // Make sure the number of codes with this length isn't too high.
68 left -= @as(isize, @bitCast(i16, val));
69 if (left < 0)
70 return error.InvalidTree;
71 }
72
73 // Compute the offset of the first symbol represented by a code of a
74 // given length in the symbol table, together with the first canonical
75 // Huffman code for that length.
76 var offset: [MAXBITS + 1]u16 = undefined;
77 var codes: [MAXBITS + 1]u16 = undefined;
78 {
79 offset[1] = 0;
80 codes[1] = 0;
81 var len: usize = 1;
82 while (len < MAXBITS) : (len += 1) {
83 offset[len + 1] = offset[len] + self.count[len];
84 codes[len + 1] = (codes[len] + self.count[len]) << 1;
85 }
86 }
87
88 self.prefix_lut_len = mem.zeroes(@TypeOf(self.prefix_lut_len));
89
90 for (code_length) |len, symbol| {
91 if (len != 0) {
92 // Fill the symbol table.
93 // The symbols are assigned sequentially for each length.
94 self.symbol[offset[len]] = @truncate(u16, symbol);
95 // Track the last assigned offset
96 offset[len] += 1;
97 }
98
99 if (len == 0 or len > PREFIX_LUT_BITS)
100 continue;
101
102 // Given a Huffman code of length N we have to massage it so
103 // that it becomes an index in the lookup table.
104 // The bit order is reversed as the fast path reads the bit
105 // sequence MSB to LSB using an &, the order is flipped wrt the
106 // one obtained by reading bit-by-bit.
107 // The codes are prefix-free, if the prefix matches we can
108 // safely ignore the trail bits. We do so by replicating the
109 // symbol info for each combination of the trailing bits.
110 const bits_to_fill = @intCast(u5, PREFIX_LUT_BITS - len);
111 const rev_code = bitReverse(codes[len], len);
112 // Track the last used code, but only for lengths < PREFIX_LUT_BITS
113 codes[len] += 1;
114
115 var j: usize = 0;
116 while (j < @as(usize, 1) << bits_to_fill) : (j += 1) {
117 const index = rev_code | (j << @intCast(u5, len));
118 assert(self.prefix_lut_len[index] == 0);
119 self.prefix_lut[index] = @truncate(u16, symbol);
120 self.prefix_lut_len[index] = @truncate(u16, len);
121 }
122 }
123
124 self.last_code = codes[PREFIX_LUT_BITS + 1];
125 self.last_index = offset[PREFIX_LUT_BITS + 1] - self.count[PREFIX_LUT_BITS + 1];
126 }
127};
128
129// Reverse bit-by-bit a N-bit value
130fn bitReverse(x: usize, N: usize) usize {
131 var tmp: usize = 0;
132 var i: usize = 0;
133 while (i < N) : (i += 1) {
134 tmp |= ((x >> @intCast(u5, i)) & 1) << @intCast(u5, N - i - 1);
135 }
136 return tmp;
137}
138
139pub fn InflateStream(comptime ReaderType: type) type {
140 return struct {
141 const Self = @This();
142
143 pub const Error = ReaderType.Error || error{
144 EndOfStream,
145 BadCounts,
146 InvalidBlockType,
147 InvalidDistance,
148 InvalidFixedCode,
149 InvalidLength,
150 InvalidStoredSize,
151 InvalidSymbol,
152 InvalidTree,
153 MissingEOBCode,
154 NoLastLength,
155 OutOfCodes,
156 };
157 pub const Reader = io.Reader(*Self, Error, read);
158
159 inner_reader: ReaderType,
160
161 // True if the decoder met the end of the compressed stream, no further
162 // data can be decompressed
163 seen_eos: bool,
164
165 state: union(enum) {
166 // Parse a compressed block header and set up the internal state for
167 // decompressing its contents.
168 DecodeBlockHeader: void,
169 // Decode all the symbols in a compressed block.
170 DecodeBlockData: void,
171 // Copy N bytes of uncompressed data from the underlying stream into
172 // the window.
173 Copy: usize,
174 // Copy 1 byte into the window.
175 CopyLit: u8,
176 // Copy L bytes from the window itself, starting from D bytes
177 // behind.
178 CopyFrom: struct { distance: u16, length: u16 },
179 },
180
181 // Sliding window for the LZ77 algorithm
182 window: struct {
183 const WSelf = @This();
184
185 // invariant: buffer length is always a power of 2
186 buf: []u8,
187 // invariant: ri <= wi
188 wi: usize = 0, // Write index
189 ri: usize = 0, // Read index
190 el: usize = 0, // Number of readable elements
191
192 fn readable(self: *WSelf) usize {
193 return self.el;
194 }
195
196 fn writable(self: *WSelf) usize {
197 return self.buf.len - self.el;
198 }
199
200 // Insert a single byte into the window.
201 // Returns 1 if there's enough space for the new byte and 0
202 // otherwise.
203 fn append(self: *WSelf, value: u8) usize {
204 if (self.writable() < 1) return 0;
205 self.appendUnsafe(value);
206 return 1;
207 }
208
209 // Insert a single byte into the window.
210 // Assumes there's enough space.
211 inline fn appendUnsafe(self: *WSelf, value: u8) void {
212 self.buf[self.wi] = value;
213 self.wi = (self.wi + 1) & (self.buf.len - 1);
214 self.el += 1;
215 }
216
217 // Fill dest[] with data from the window, starting from the read
218 // position. This updates the read pointer.
219 // Returns the number of read bytes or 0 if there's nothing to read
220 // yet.
221 fn read(self: *WSelf, dest: []u8) usize {
222 const N = math.min(dest.len, self.readable());
223
224 if (N == 0) return 0;
225
226 if (self.ri + N < self.buf.len) {
227 // The data doesn't wrap around
228 mem.copy(u8, dest, self.buf[self.ri .. self.ri + N]);
229 } else {
230 // The data wraps around the buffer, split the copy
231 std.mem.copy(u8, dest, self.buf[self.ri..]);
232 // How much data we've copied from `ri` to the end
233 const r = self.buf.len - self.ri;
234 std.mem.copy(u8, dest[r..], self.buf[0 .. N - r]);
235 }
236
237 self.ri = (self.ri + N) & (self.buf.len - 1);
238 self.el -= N;
239
240 return N;
241 }
242
243 // Copy `length` bytes starting from `distance` bytes behind the
244 // write pointer.
245 // Be careful as the length may be greater than the distance, that's
246 // how the compressor encodes run-length encoded sequences.
247 fn copyFrom(self: *WSelf, distance: usize, length: usize) usize {
248 const N = math.min(length, self.writable());
249
250 if (N == 0) return 0;
251
252 // TODO: Profile and, if needed, replace with smarter juggling
253 // of the window memory for the non-overlapping case.
254 var i: usize = 0;
255 while (i < N) : (i += 1) {
256 const index = (self.wi -% distance) & (self.buf.len - 1);
257 self.appendUnsafe(self.buf[index]);
258 }
259
260 return N;
261 }
262 },
263
264 // Compressor-local Huffman tables used to decompress blocks with
265 // dynamic codes.
266 huffman_tables: [2]Huffman = undefined,
267
268 // Huffman tables used for decoding length/distance pairs.
269 hdist: *Huffman,
270 hlen: *Huffman,
271
272 // Temporary buffer for the bitstream, only bits 0..`bits_left` are
273 // considered valid.
274 bits: u32,
275 bits_left: usize,
276
277 fn peekBits(self: *Self, bits: usize) !u32 {
278 while (self.bits_left < bits) {
279 const byte = try self.inner_reader.readByte();
280 self.bits |= @as(u32, byte) << @intCast(u5, self.bits_left);
281 self.bits_left += 8;
282 }
283 return self.bits & ((@as(u32, 1) << @intCast(u5, bits)) - 1);
284 }
285 fn readBits(self: *Self, bits: usize) !u32 {
286 const val = self.peekBits(bits);
287 self.discardBits(bits);
288 return val;
289 }
290 fn discardBits(self: *Self, bits: usize) void {
291 self.bits >>= @intCast(u5, bits);
292 self.bits_left -= bits;
293 }
294
295 fn stored(self: *Self) !void {
296 // Discard the remaining bits, the lenght field is always
297 // byte-aligned (and so is the data)
298 self.discardBits(self.bits_left);
299
300 const length = try self.inner_reader.readIntLittle(u16);
301 const length_cpl = try self.inner_reader.readIntLittle(u16);
302
303 if (length != ~length_cpl)
304 return error.InvalidStoredSize;
305
306 self.state = .{ .Copy = length };
307 }
308
309 fn fixed(self: *Self) !void {
310 comptime var lencode: Huffman = undefined;
311 comptime var distcode: Huffman = undefined;
312
313 // The Huffman codes are specified in the RFC1951, section 3.2.6
314 comptime {
315 @setEvalBranchQuota(100000);
316
317 const len_lengths = //
318 [_]u16{8} ** 144 ++
319 [_]u16{9} ** 112 ++
320 [_]u16{7} ** 24 ++
321 [_]u16{8} ** 8;
322 assert(len_lengths.len == FIXLCODES);
323 try lencode.construct(len_lengths[0..]);
324
325 const dist_lengths = [_]u16{5} ** MAXDCODES;
326 try distcode.construct(dist_lengths[0..]);
327 }
328
329 self.hlen = &lencode;
330 self.hdist = &distcode;
331 self.state = .DecodeBlockData;
332 }
333
334 fn dynamic(self: *Self) !void {
335 // Number of length codes
336 const nlen = (try self.readBits(5)) + 257;
337 // Number of distance codes
338 const ndist = (try self.readBits(5)) + 1;
339 // Number of code length codes
340 const ncode = (try self.readBits(4)) + 4;
341
342 if (nlen > MAXLCODES or ndist > MAXDCODES)
343 return error.BadCounts;
344
345 // Permutation of code length codes
346 const ORDER = [19]u16{
347 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4,
348 12, 3, 13, 2, 14, 1, 15,
349 };
350
351 // Build the Huffman table to decode the code length codes
352 var lencode: Huffman = undefined;
353 {
354 var lengths = std.mem.zeroes([19]u16);
355
356 // Read the code lengths, missing ones are left as zero
357 for (ORDER[0..ncode]) |val| {
358 lengths[val] = @intCast(u16, try self.readBits(3));
359 }
360
361 try lencode.construct(lengths[0..]);
362 }
363
364 // Read the length/literal and distance code length tables.
365 // Zero the table by default so we can avoid explicitly writing out
366 // zeros for codes 17 and 18
367 var lengths = std.mem.zeroes([MAXCODES]u16);
368
369 var i: usize = 0;
370 while (i < nlen + ndist) {
371 const symbol = try self.decode(&lencode);
372
373 switch (symbol) {
374 0...15 => {
375 lengths[i] = symbol;
376 i += 1;
377 },
378 16 => {
379 // repeat last length 3..6 times
380 if (i == 0) return error.NoLastLength;
381
382 const last_length = lengths[i - 1];
383 const repeat = 3 + (try self.readBits(2));
384 const last_index = i + repeat;
385 while (i < last_index) : (i += 1) {
386 lengths[i] = last_length;
387 }
388 },
389 17 => {
390 // repeat zero 3..10 times
391 i += 3 + (try self.readBits(3));
392 },
393 18 => {
394 // repeat zero 11..138 times
395 i += 11 + (try self.readBits(7));
396 },
397 else => return error.InvalidSymbol,
398 }
399 }
400
401 if (i > nlen + ndist)
402 return error.InvalidLength;
403
404 // Check if the end of block code is present
405 if (lengths[256] == 0)
406 return error.MissingEOBCode;
407
408 try self.huffman_tables[0].construct(lengths[0..nlen]);
409 try self.huffman_tables[1].construct(lengths[nlen .. nlen + ndist]);
410
411 self.hlen = &self.huffman_tables[0];
412 self.hdist = &self.huffman_tables[1];
413 self.state = .DecodeBlockData;
414 }
415
416 fn codes(self: *Self, lencode: *Huffman, distcode: *Huffman) !bool {
417 // Size base for length codes 257..285
418 const LENS = [29]u16{
419 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
420 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258,
421 };
422 // Extra bits for length codes 257..285
423 const LEXT = [29]u16{
424 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
425 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0,
426 };
427 // Offset base for distance codes 0..29
428 const DISTS = [30]u16{
429 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
430 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
431 };
432 // Extra bits for distance codes 0..29
433 const DEXT = [30]u16{
434 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
435 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
436 };
437
438 while (true) {
439 const symbol = try self.decode(lencode);
440
441 switch (symbol) {
442 0...255 => {
443 // Literal value
444 const c = @truncate(u8, symbol);
445 if (self.window.append(c) == 0) {
446 self.state = .{ .CopyLit = c };
447 return false;
448 }
449 },
450 256 => {
451 // End of block symbol
452 return true;
453 },
454 257...285 => {
455 // Length/distance pair
456 const length_symbol = symbol - 257;
457 const length = LENS[length_symbol] +
458 @intCast(u16, try self.readBits(LEXT[length_symbol]));
459
460 const distance_symbol = try self.decode(distcode);
461 const distance = DISTS[distance_symbol] +
462 @intCast(u16, try self.readBits(DEXT[distance_symbol]));
463
464 if (distance > self.window.buf.len)
465 return error.InvalidDistance;
466
467 const written = self.window.copyFrom(distance, length);
468 if (written != length) {
469 self.state = .{
470 .CopyFrom = .{
471 .distance = distance,
472 .length = length - @truncate(u16, written),
473 },
474 };
475 return false;
476 }
477 },
478 else => return error.InvalidFixedCode,
479 }
480 }
481 }
482
483 fn decode(self: *Self, h: *Huffman) !u16 {
484 // Fast path, read some bits and hope they're prefixes of some code
485 const prefix = try self.peekBits(PREFIX_LUT_BITS);
486 if (h.prefix_lut_len[prefix] != 0) {
487 self.discardBits(h.prefix_lut_len[prefix]);
488 return h.prefix_lut[prefix];
489 }
490
491 // The sequence we've read is not a prefix of any code of length <=
492 // PREFIX_LUT_BITS, keep decoding it using a slower method
493 self.discardBits(PREFIX_LUT_BITS);
494
495 // Speed up the decoding by starting from the first code length
496 // that's not covered by the table
497 var len: usize = PREFIX_LUT_BITS + 1;
498 var first: usize = h.last_code;
499 var index: usize = h.last_index;
500
501 // Reverse the prefix so that the LSB becomes the MSB and make space
502 // for the next bit
503 var code = bitReverse(prefix, PREFIX_LUT_BITS + 1);
504
505 while (len <= MAXBITS) : (len += 1) {
506 code |= try self.readBits(1);
507 const count = h.count[len];
508 if (code < first + count)
509 return h.symbol[index + (code - first)];
510 index += count;
511 first += count;
512 first <<= 1;
513 code <<= 1;
514 }
515
516 return error.OutOfCodes;
517 }
518
519 fn step(self: *Self) !void {
520 while (true) {
521 switch (self.state) {
522 .DecodeBlockHeader => {
523 // The compressed stream is done
524 if (self.seen_eos) return;
525
526 const last = @intCast(u1, try self.readBits(1));
527 const kind = @intCast(u2, try self.readBits(2));
528
529 self.seen_eos = last != 0;
530
531 // The next state depends on the block type
532 switch (kind) {
533 0 => try self.stored(),
534 1 => try self.fixed(),
535 2 => try self.dynamic(),
536 3 => return error.InvalidBlockType,
537 }
538 },
539 .DecodeBlockData => {
540 if (!try self.codes(self.hlen, self.hdist)) {
541 return;
542 }
543
544 self.state = .DecodeBlockHeader;
545 },
546 .Copy => |*length| {
547 const N = math.min(self.window.writable(), length.*);
548
549 // TODO: This loop can be more efficient. On the other
550 // hand uncompressed blocks are not that common so...
551 var i: usize = 0;
552 while (i < N) : (i += 1) {
553 var tmp: [1]u8 = undefined;
554 if ((try self.inner_reader.read(&tmp)) != 1) {
555 // Unexpected end of stream, keep this error
556 // consistent with the use of readBitsNoEof
557 return error.EndOfStream;
558 }
559 self.window.appendUnsafe(tmp[0]);
560 }
561
562 if (N != length.*) {
563 length.* -= N;
564 return;
565 }
566
567 self.state = .DecodeBlockHeader;
568 },
569 .CopyLit => |c| {
570 if (self.window.append(c) == 0) {
571 return;
572 }
573
574 self.state = .DecodeBlockData;
575 },
576 .CopyFrom => |*info| {
577 const written = self.window.copyFrom(info.distance, info.length);
578 if (written != info.length) {
579 info.length -= @truncate(u16, written);
580 return;
581 }
582
583 self.state = .DecodeBlockData;
584 },
585 }
586 }
587 }
588
589 fn init(source: ReaderType, window_slice: []u8) Self {
590 assert(math.isPowerOfTwo(window_slice.len));
591
592 return Self{
593 .inner_reader = source,
594 .window = .{ .buf = window_slice },
595 .seen_eos = false,
596 .state = .DecodeBlockHeader,
597 .hdist = undefined,
598 .hlen = undefined,
599 .bits = 0,
600 .bits_left = 0,
601 };
602 }
603
604 // Implements the io.Reader interface
605 pub fn read(self: *Self, buffer: []u8) Error!usize {
606 if (buffer.len == 0)
607 return 0;
608
609 // Try reading as much as possible from the window
610 var read_amt: usize = self.window.read(buffer);
611 while (read_amt < buffer.len) {
612 // Run the state machine, we can detect the "effective" end of
613 // stream condition by checking if any progress was made.
614 // Why "effective"? Because even though `seen_eos` is true we
615 // may still have to finish processing other decoding steps.
616 try self.step();
617 // No progress was made
618 if (self.window.readable() == 0)
619 break;
620
621 read_amt += self.window.read(buffer[read_amt..]);
622 }
623
624 return read_amt;
625 }
626
627 pub fn reader(self: *Self) Reader {
628 return .{ .context = self };
629 }
630 };
631}
632
633pub fn inflateStream(reader: anytype, window_slice: []u8) InflateStream(@TypeOf(reader)) {
634 return InflateStream(@TypeOf(reader)).init(reader, window_slice);
635}
lib/std/compress/gzip.zig created+248
......@@ -0,0 +1,248 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6//
7// Decompressor for GZIP data streams (RFC1952)
8
9const std = @import("std");
10const io = std.io;
11const fs = std.fs;
12const testing = std.testing;
13const mem = std.mem;
14const deflate = std.compress.deflate;
15
16// Flags for the FLG field in the header
17const FTEXT = 1 << 0;
18const FHCRC = 1 << 1;
19const FEXTRA = 1 << 2;
20const FNAME = 1 << 3;
21const FCOMMENT = 1 << 4;
22
23pub fn GzipStream(comptime ReaderType: type) type {
24 return struct {
25 const Self = @This();
26
27 pub const Error = ReaderType.Error ||
28 deflate.InflateStream(ReaderType).Error ||
29 error{ CorruptedData, WrongChecksum };
30 pub const Reader = io.Reader(*Self, Error, read);
31
32 allocator: *mem.Allocator,
33 inflater: deflate.InflateStream(ReaderType),
34 in_reader: ReaderType,
35 hasher: std.hash.Crc32,
36 window_slice: []u8,
37 read_amt: usize,
38
39 info: struct {
40 filename: ?[]const u8,
41 comment: ?[]const u8,
42 modification_time: u32,
43 },
44
45 fn init(allocator: *mem.Allocator, source: ReaderType) !Self {
46 // gzip header format is specified in RFC1952
47 const header = try source.readBytesNoEof(10);
48
49 // Check the ID1/ID2 fields
50 if (header[0] != 0x1f or header[1] != 0x8b)
51 return error.BadHeader;
52
53 const CM = header[2];
54 // The CM field must be 8 to indicate the use of DEFLATE
55 if (CM != 8) return error.InvalidCompression;
56 // Flags
57 const FLG = header[3];
58 // Modification time, as a Unix timestamp.
59 // If zero there's no timestamp available.
60 const MTIME = mem.readIntLittle(u32, header[4..8]);
61 // Extra flags
62 const XFL = header[8];
63 // Operating system where the compression took place
64 const OS = header[9];
65
66 if (FLG & FEXTRA != 0) {
67 // Skip the extra data, we could read and expose it to the user
68 // if somebody needs it.
69 const len = try source.readIntLittle(u16);
70 try source.skipBytes(len, .{});
71 }
72
73 var filename: ?[]const u8 = null;
74 if (FLG & FNAME != 0) {
75 filename = try source.readUntilDelimiterAlloc(
76 allocator,
77 0,
78 std.math.maxInt(usize),
79 );
80 }
81 errdefer if (filename) |p| allocator.free(p);
82
83 var comment: ?[]const u8 = null;
84 if (FLG & FCOMMENT != 0) {
85 comment = try source.readUntilDelimiterAlloc(
86 allocator,
87 0,
88 std.math.maxInt(usize),
89 );
90 }
91 errdefer if (comment) |p| allocator.free(p);
92
93 if (FLG & FHCRC != 0) {
94 // TODO: Evaluate and check the header checksum. The stdlib has
95 // no CRC16 yet :(
96 _ = try source.readIntLittle(u16);
97 }
98
99 // The RFC doesn't say anything about the DEFLATE window size to be
100 // used, default to 32K.
101 var window_slice = try allocator.alloc(u8, 32 * 1024);
102
103 return Self{
104 .allocator = allocator,
105 .inflater = deflate.inflateStream(source, window_slice),
106 .in_reader = source,
107 .hasher = std.hash.Crc32.init(),
108 .window_slice = window_slice,
109 .info = .{
110 .filename = filename,
111 .comment = comment,
112 .modification_time = MTIME,
113 },
114 .read_amt = 0,
115 };
116 }
117
118 pub fn deinit(self: *Self) void {
119 self.allocator.free(self.window_slice);
120 if (self.info.filename) |filename|
121 self.allocator.free(filename);
122 if (self.info.comment) |comment|
123 self.allocator.free(comment);
124 }
125
126 // Implements the io.Reader interface
127 pub fn read(self: *Self, buffer: []u8) Error!usize {
128 if (buffer.len == 0)
129 return 0;
130
131 // Read from the compressed stream and update the computed checksum
132 const r = try self.inflater.read(buffer);
133 if (r != 0) {
134 self.hasher.update(buffer[0..r]);
135 self.read_amt += r;
136 return r;
137 }
138
139 // We've reached the end of stream, check if the checksum matches
140 const hash = try self.in_reader.readIntLittle(u32);
141 if (hash != self.hasher.final())
142 return error.WrongChecksum;
143
144 // The ISIZE field is the size of the uncompressed input modulo 2^32
145 const input_size = try self.in_reader.readIntLittle(u32);
146 if (self.read_amt & 0xffffffff != input_size)
147 return error.CorruptedData;
148
149 return 0;
150 }
151
152 pub fn reader(self: *Self) Reader {
153 return .{ .context = self };
154 }
155 };
156}
157
158pub fn gzipStream(allocator: *mem.Allocator, reader: anytype) !GzipStream(@TypeOf(reader)) {
159 return GzipStream(@TypeOf(reader)).init(allocator, reader);
160}
161
162fn testReader(data: []const u8, comptime expected: []const u8) !void {
163 var in_stream = io.fixedBufferStream(data);
164
165 var gzip_stream = try gzipStream(testing.allocator, in_stream.reader());
166 defer gzip_stream.deinit();
167
168 // Read and decompress the whole file
169 const buf = try gzip_stream.reader().readAllAlloc(testing.allocator, std.math.maxInt(usize));
170 defer testing.allocator.free(buf);
171 // Calculate its SHA256 hash and check it against the reference
172 var hash: [32]u8 = undefined;
173 std.crypto.hash.sha2.Sha256.hash(buf, hash[0..], .{});
174
175 assertEqual(expected, &hash);
176}
177
178// Assert `expected` == `input` where `input` is a bytestring.
179pub fn assertEqual(comptime expected: []const u8, input: []const u8) void {
180 var expected_bytes: [expected.len / 2]u8 = undefined;
181 for (expected_bytes) |*r, i| {
182 r.* = std.fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable;
183 }
184
185 testing.expectEqualSlices(u8, &expected_bytes, input);
186}
187
188// All the test cases are obtained by compressing the RFC1952 text
189//
190// https://tools.ietf.org/rfc/rfc1952.txt length=25037 bytes
191// SHA256=164ef0897b4cbec63abf1b57f069f3599bd0fb7c72c2a4dee21bd7e03ec9af67
192test "compressed data" {
193 try testReader(
194 @embedFile("rfc1952.txt.gz"),
195 "164ef0897b4cbec63abf1b57f069f3599bd0fb7c72c2a4dee21bd7e03ec9af67",
196 );
197}
198
199test "sanity checks" {
200 // Truncated header
201 testing.expectError(
202 error.EndOfStream,
203 testReader(&[_]u8{ 0x1f, 0x8B }, ""),
204 );
205 // Wrong CM
206 testing.expectError(
207 error.InvalidCompression,
208 testReader(&[_]u8{
209 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
210 0x00, 0x03,
211 }, ""),
212 );
213 // Wrong checksum
214 testing.expectError(
215 error.WrongChecksum,
216 testReader(&[_]u8{
217 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01,
219 0x00, 0x00, 0x00, 0x00,
220 }, ""),
221 );
222 // Truncated checksum
223 testing.expectError(
224 error.EndOfStream,
225 testReader(&[_]u8{
226 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00,
228 }, ""),
229 );
230 // Wrong initial size
231 testing.expectError(
232 error.CorruptedData,
233 testReader(&[_]u8{
234 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x01,
237 }, ""),
238 );
239 // Truncated initial size field
240 testing.expectError(
241 error.EndOfStream,
242 testReader(&[_]u8{
243 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00,
246 }, ""),
247 );
248}
lib/std/compress/rfc1951.txt created+955
......@@ -0,0 +1,955 @@
1
2
3
4
5
6
7Network Working Group P. Deutsch
8Request for Comments: 1951 Aladdin Enterprises
9Category: Informational May 1996
10
11
12 DEFLATE Compressed Data Format Specification version 1.3
13
14Status of This Memo
15
16 This memo provides information for the Internet community. This memo
17 does not specify an Internet standard of any kind. Distribution of
18 this memo is unlimited.
19
20IESG Note:
21
22 The IESG takes no position on the validity of any Intellectual
23 Property Rights statements contained in this document.
24
25Notices
26
27 Copyright (c) 1996 L. Peter Deutsch
28
29 Permission is granted to copy and distribute this document for any
30 purpose and without charge, including translations into other
31 languages and incorporation into compilations, provided that the
32 copyright notice and this notice are preserved, and that any
33 substantive changes or deletions from the original are clearly
34 marked.
35
36 A pointer to the latest version of this and related documentation in
37 HTML format can be found at the URL
38 <ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html>.
39
40Abstract
41
42 This specification defines a lossless compressed data format that
43 compresses data using a combination of the LZ77 algorithm and Huffman
44 coding, with efficiency comparable to the best currently available
45 general-purpose compression methods. The data can be produced or
46 consumed, even for an arbitrarily long sequentially presented input
47 data stream, using only an a priori bounded amount of intermediate
48 storage. The format can be implemented readily in a manner not
49 covered by patents.
50
51
52
53
54
55
56
57
58Deutsch Informational [Page 1]
59
60RFC 1951 DEFLATE Compressed Data Format Specification May 1996
61
62
63Table of Contents
64
65 1. Introduction ................................................... 2
66 1.1. Purpose ................................................... 2
67 1.2. Intended audience ......................................... 3
68 1.3. Scope ..................................................... 3
69 1.4. Compliance ................................................ 3
70 1.5. Definitions of terms and conventions used ................ 3
71 1.6. Changes from previous versions ............................ 4
72 2. Compressed representation overview ............................. 4
73 3. Detailed specification ......................................... 5
74 3.1. Overall conventions ....................................... 5
75 3.1.1. Packing into bytes .................................. 5
76 3.2. Compressed block format ................................... 6
77 3.2.1. Synopsis of prefix and Huffman coding ............... 6
78 3.2.2. Use of Huffman coding in the "deflate" format ....... 7
79 3.2.3. Details of block format ............................. 9
80 3.2.4. Non-compressed blocks (BTYPE=00) ................... 11
81 3.2.5. Compressed blocks (length and distance codes) ...... 11
82 3.2.6. Compression with fixed Huffman codes (BTYPE=01) .... 12
83 3.2.7. Compression with dynamic Huffman codes (BTYPE=10) .. 13
84 3.3. Compliance ............................................... 14
85 4. Compression algorithm details ................................. 14
86 5. References .................................................... 16
87 6. Security Considerations ....................................... 16
88 7. Source code ................................................... 16
89 8. Acknowledgements .............................................. 16
90 9. Author's Address .............................................. 17
91
921. Introduction
93
94 1.1. Purpose
95
96 The purpose of this specification is to define a lossless
97 compressed data format that:
98 * Is independent of CPU type, operating system, file system,
99 and character set, and hence can be used for interchange;
100 * Can be produced or consumed, even for an arbitrarily long
101 sequentially presented input data stream, using only an a
102 priori bounded amount of intermediate storage, and hence
103 can be used in data communications or similar structures
104 such as Unix filters;
105 * Compresses data with efficiency comparable to the best
106 currently available general-purpose compression methods,
107 and in particular considerably better than the "compress"
108 program;
109 * Can be implemented readily in a manner not covered by
110 patents, and hence can be practiced freely;
111
112
113
114Deutsch Informational [Page 2]
115
116RFC 1951 DEFLATE Compressed Data Format Specification May 1996
117
118
119 * Is compatible with the file format produced by the current
120 widely used gzip utility, in that conforming decompressors
121 will be able to read data produced by the existing gzip
122 compressor.
123
124 The data format defined by this specification does not attempt to:
125
126 * Allow random access to compressed data;
127 * Compress specialized data (e.g., raster graphics) as well
128 as the best currently available specialized algorithms.
129
130 A simple counting argument shows that no lossless compression
131 algorithm can compress every possible input data set. For the
132 format defined here, the worst case expansion is 5 bytes per 32K-
133 byte block, i.e., a size increase of 0.015% for large data sets.
134 English text usually compresses by a factor of 2.5 to 3;
135 executable files usually compress somewhat less; graphical data
136 such as raster images may compress much more.
137
138 1.2. Intended audience
139
140 This specification is intended for use by implementors of software
141 to compress data into "deflate" format and/or decompress data from
142 "deflate" format.
143
144 The text of the specification assumes a basic background in
145 programming at the level of bits and other primitive data
146 representations. Familiarity with the technique of Huffman coding
147 is helpful but not required.
148
149 1.3. Scope
150
151 The specification specifies a method for representing a sequence
152 of bytes as a (usually shorter) sequence of bits, and a method for
153 packing the latter bit sequence into bytes.
154
155 1.4. Compliance
156
157 Unless otherwise indicated below, a compliant decompressor must be
158 able to accept and decompress any data set that conforms to all
159 the specifications presented here; a compliant compressor must
160 produce data sets that conform to all the specifications presented
161 here.
162
163 1.5. Definitions of terms and conventions used
164
165 Byte: 8 bits stored or transmitted as a unit (same as an octet).
166 For this specification, a byte is exactly 8 bits, even on machines
167
168
169
170Deutsch Informational [Page 3]
171
172RFC 1951 DEFLATE Compressed Data Format Specification May 1996
173
174
175 which store a character on a number of bits different from eight.
176 See below, for the numbering of bits within a byte.
177
178 String: a sequence of arbitrary bytes.
179
180 1.6. Changes from previous versions
181
182 There have been no technical changes to the deflate format since
183 version 1.1 of this specification. In version 1.2, some
184 terminology was changed. Version 1.3 is a conversion of the
185 specification to RFC style.
186
1872. Compressed representation overview
188
189 A compressed data set consists of a series of blocks, corresponding
190 to successive blocks of input data. The block sizes are arbitrary,
191 except that non-compressible blocks are limited to 65,535 bytes.
192
193 Each block is compressed using a combination of the LZ77 algorithm
194 and Huffman coding. The Huffman trees for each block are independent
195 of those for previous or subsequent blocks; the LZ77 algorithm may
196 use a reference to a duplicated string occurring in a previous block,
197 up to 32K input bytes before.
198
199 Each block consists of two parts: a pair of Huffman code trees that
200 describe the representation of the compressed data part, and a
201 compressed data part. (The Huffman trees themselves are compressed
202 using Huffman encoding.) The compressed data consists of a series of
203 elements of two types: literal bytes (of strings that have not been
204 detected as duplicated within the previous 32K input bytes), and
205 pointers to duplicated strings, where a pointer is represented as a
206 pair <length, backward distance>. The representation used in the
207 "deflate" format limits distances to 32K bytes and lengths to 258
208 bytes, but does not limit the size of a block, except for
209 uncompressible blocks, which are limited as noted above.
210
211 Each type of value (literals, distances, and lengths) in the
212 compressed data is represented using a Huffman code, using one code
213 tree for literals and lengths and a separate code tree for distances.
214 The code trees for each block appear in a compact form just before
215 the compressed data for that block.
216
217
218
219
220
221
222
223
224
225
226Deutsch Informational [Page 4]
227
228RFC 1951 DEFLATE Compressed Data Format Specification May 1996
229
230
2313. Detailed specification
232
233 3.1. Overall conventions In the diagrams below, a box like this:
234
235 +---+
236 | | <-- the vertical bars might be missing
237 +---+
238
239 represents one byte; a box like this:
240
241 +==============+
242 | |
243 +==============+
244
245 represents a variable number of bytes.
246
247 Bytes stored within a computer do not have a "bit order", since
248 they are always treated as a unit. However, a byte considered as
249 an integer between 0 and 255 does have a most- and least-
250 significant bit, and since we write numbers with the most-
251 significant digit on the left, we also write bytes with the most-
252 significant bit on the left. In the diagrams below, we number the
253 bits of a byte so that bit 0 is the least-significant bit, i.e.,
254 the bits are numbered:
255
256 +--------+
257 |76543210|
258 +--------+
259
260 Within a computer, a number may occupy multiple bytes. All
261 multi-byte numbers in the format described here are stored with
262 the least-significant byte first (at the lower memory address).
263 For example, the decimal number 520 is stored as:
264
265 0 1
266 +--------+--------+
267 |00001000|00000010|
268 +--------+--------+
269 ^ ^
270 | |
271 | + more significant byte = 2 x 256
272 + less significant byte = 8
273
274 3.1.1. Packing into bytes
275
276 This document does not address the issue of the order in which
277 bits of a byte are transmitted on a bit-sequential medium,
278 since the final data format described here is byte- rather than
279
280
281
282Deutsch Informational [Page 5]
283
284RFC 1951 DEFLATE Compressed Data Format Specification May 1996
285
286
287 bit-oriented. However, we describe the compressed block format
288 in below, as a sequence of data elements of various bit
289 lengths, not a sequence of bytes. We must therefore specify
290 how to pack these data elements into bytes to form the final
291 compressed byte sequence:
292
293 * Data elements are packed into bytes in order of
294 increasing bit number within the byte, i.e., starting
295 with the least-significant bit of the byte.
296 * Data elements other than Huffman codes are packed
297 starting with the least-significant bit of the data
298 element.
299 * Huffman codes are packed starting with the most-
300 significant bit of the code.
301
302 In other words, if one were to print out the compressed data as
303 a sequence of bytes, starting with the first byte at the
304 *right* margin and proceeding to the *left*, with the most-
305 significant bit of each byte on the left as usual, one would be
306 able to parse the result from right to left, with fixed-width
307 elements in the correct MSB-to-LSB order and Huffman codes in
308 bit-reversed order (i.e., with the first bit of the code in the
309 relative LSB position).
310
311 3.2. Compressed block format
312
313 3.2.1. Synopsis of prefix and Huffman coding
314
315 Prefix coding represents symbols from an a priori known
316 alphabet by bit sequences (codes), one code for each symbol, in
317 a manner such that different symbols may be represented by bit
318 sequences of different lengths, but a parser can always parse
319 an encoded string unambiguously symbol-by-symbol.
320
321 We define a prefix code in terms of a binary tree in which the
322 two edges descending from each non-leaf node are labeled 0 and
323 1 and in which the leaf nodes correspond one-for-one with (are
324 labeled with) the symbols of the alphabet; then the code for a
325 symbol is the sequence of 0's and 1's on the edges leading from
326 the root to the leaf labeled with that symbol. For example:
327
328
329
330
331
332
333
334
335
336
337
338Deutsch Informational [Page 6]
339
340RFC 1951 DEFLATE Compressed Data Format Specification May 1996
341
342
343 /\ Symbol Code
344 0 1 ------ ----
345 / \ A 00
346 /\ B B 1
347 0 1 C 011
348 / \ D 010
349 A /\
350 0 1
351 / \
352 D C
353
354 A parser can decode the next symbol from an encoded input
355 stream by walking down the tree from the root, at each step
356 choosing the edge corresponding to the next input bit.
357
358 Given an alphabet with known symbol frequencies, the Huffman
359 algorithm allows the construction of an optimal prefix code
360 (one which represents strings with those symbol frequencies
361 using the fewest bits of any possible prefix codes for that
362 alphabet). Such a code is called a Huffman code. (See
363 reference [1] in Chapter 5, references for additional
364 information on Huffman codes.)
365
366 Note that in the "deflate" format, the Huffman codes for the
367 various alphabets must not exceed certain maximum code lengths.
368 This constraint complicates the algorithm for computing code
369 lengths from symbol frequencies. Again, see Chapter 5,
370 references for details.
371
372 3.2.2. Use of Huffman coding in the "deflate" format
373
374 The Huffman codes used for each alphabet in the "deflate"
375 format have two additional rules:
376
377 * All codes of a given bit length have lexicographically
378 consecutive values, in the same order as the symbols
379 they represent;
380
381 * Shorter codes lexicographically precede longer codes.
382
383
384
385
386
387
388
389
390
391
392
393
394Deutsch Informational [Page 7]
395
396RFC 1951 DEFLATE Compressed Data Format Specification May 1996
397
398
399 We could recode the example above to follow this rule as
400 follows, assuming that the order of the alphabet is ABCD:
401
402 Symbol Code
403 ------ ----
404 A 10
405 B 0
406 C 110
407 D 111
408
409 I.e., 0 precedes 10 which precedes 11x, and 110 and 111 are
410 lexicographically consecutive.
411
412 Given this rule, we can define the Huffman code for an alphabet
413 just by giving the bit lengths of the codes for each symbol of
414 the alphabet in order; this is sufficient to determine the
415 actual codes. In our example, the code is completely defined
416 by the sequence of bit lengths (2, 1, 3, 3). The following
417 algorithm generates the codes as integers, intended to be read
418 from most- to least-significant bit. The code lengths are
419 initially in tree[I].Len; the codes are produced in
420 tree[I].Code.
421
422 1) Count the number of codes for each code length. Let
423 bl_count[N] be the number of codes of length N, N >= 1.
424
425 2) Find the numerical value of the smallest code for each
426 code length:
427
428 code = 0;
429 bl_count[0] = 0;
430 for (bits = 1; bits <= MAX_BITS; bits++) {
431 code = (code + bl_count[bits-1]) << 1;
432 next_code[bits] = code;
433 }
434
435 3) Assign numerical values to all codes, using consecutive
436 values for all codes of the same length with the base
437 values determined at step 2. Codes that are never used
438 (which have a bit length of zero) must not be assigned a
439 value.
440
441 for (n = 0; n <= max_code; n++) {
442 len = tree[n].Len;
443 if (len != 0) {
444 tree[n].Code = next_code[len];
445 next_code[len]++;
446 }
447
448
449
450Deutsch Informational [Page 8]
451
452RFC 1951 DEFLATE Compressed Data Format Specification May 1996
453
454
455 }
456
457 Example:
458
459 Consider the alphabet ABCDEFGH, with bit lengths (3, 3, 3, 3,
460 3, 2, 4, 4). After step 1, we have:
461
462 N bl_count[N]
463 - -----------
464 2 1
465 3 5
466 4 2
467
468 Step 2 computes the following next_code values:
469
470 N next_code[N]
471 - ------------
472 1 0
473 2 0
474 3 2
475 4 14
476
477 Step 3 produces the following code values:
478
479 Symbol Length Code
480 ------ ------ ----
481 A 3 010
482 B 3 011
483 C 3 100
484 D 3 101
485 E 3 110
486 F 2 00
487 G 4 1110
488 H 4 1111
489
490 3.2.3. Details of block format
491
492 Each block of compressed data begins with 3 header bits
493 containing the following data:
494
495 first bit BFINAL
496 next 2 bits BTYPE
497
498 Note that the header bits do not necessarily begin on a byte
499 boundary, since a block does not necessarily occupy an integral
500 number of bytes.
501
502
503
504
505
506Deutsch Informational [Page 9]
507
508RFC 1951 DEFLATE Compressed Data Format Specification May 1996
509
510
511 BFINAL is set if and only if this is the last block of the data
512 set.
513
514 BTYPE specifies how the data are compressed, as follows:
515
516 00 - no compression
517 01 - compressed with fixed Huffman codes
518 10 - compressed with dynamic Huffman codes
519 11 - reserved (error)
520
521 The only difference between the two compressed cases is how the
522 Huffman codes for the literal/length and distance alphabets are
523 defined.
524
525 In all cases, the decoding algorithm for the actual data is as
526 follows:
527
528 do
529 read block header from input stream.
530 if stored with no compression
531 skip any remaining bits in current partially
532 processed byte
533 read LEN and NLEN (see next section)
534 copy LEN bytes of data to output
535 otherwise
536 if compressed with dynamic Huffman codes
537 read representation of code trees (see
538 subsection below)
539 loop (until end of block code recognized)
540 decode literal/length value from input stream
541 if value < 256
542 copy value (literal byte) to output stream
543 otherwise
544 if value = end of block (256)
545 break from loop
546 otherwise (value = 257..285)
547 decode distance from input stream
548
549 move backwards distance bytes in the output
550 stream, and copy length bytes from this
551 position to the output stream.
552 end loop
553 while not last block
554
555 Note that a duplicated string reference may refer to a string
556 in a previous block; i.e., the backward distance may cross one
557 or more block boundaries. However a distance cannot refer past
558 the beginning of the output stream. (An application using a
559
560
561
562Deutsch Informational [Page 10]
563
564RFC 1951 DEFLATE Compressed Data Format Specification May 1996
565
566
567 preset dictionary might discard part of the output stream; a
568 distance can refer to that part of the output stream anyway)
569 Note also that the referenced string may overlap the current
570 position; for example, if the last 2 bytes decoded have values
571 X and Y, a string reference with <length = 5, distance = 2>
572 adds X,Y,X,Y,X to the output stream.
573
574 We now specify each compression method in turn.
575
576 3.2.4. Non-compressed blocks (BTYPE=00)
577
578 Any bits of input up to the next byte boundary are ignored.
579 The rest of the block consists of the following information:
580
581 0 1 2 3 4...
582 +---+---+---+---+================================+
583 | LEN | NLEN |... LEN bytes of literal data...|
584 +---+---+---+---+================================+
585
586 LEN is the number of data bytes in the block. NLEN is the
587 one's complement of LEN.
588
589 3.2.5. Compressed blocks (length and distance codes)
590
591 As noted above, encoded data blocks in the "deflate" format
592 consist of sequences of symbols drawn from three conceptually
593 distinct alphabets: either literal bytes, from the alphabet of
594 byte values (0..255), or <length, backward distance> pairs,
595 where the length is drawn from (3..258) and the distance is
596 drawn from (1..32,768). In fact, the literal and length
597 alphabets are merged into a single alphabet (0..285), where
598 values 0..255 represent literal bytes, the value 256 indicates
599 end-of-block, and values 257..285 represent length codes
600 (possibly in conjunction with extra bits following the symbol
601 code) as follows:
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618Deutsch Informational [Page 11]
619
620RFC 1951 DEFLATE Compressed Data Format Specification May 1996
621
622
623 Extra Extra Extra
624 Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
625 ---- ---- ------ ---- ---- ------- ---- ---- -------
626 257 0 3 267 1 15,16 277 4 67-82
627 258 0 4 268 1 17,18 278 4 83-98
628 259 0 5 269 2 19-22 279 4 99-114
629 260 0 6 270 2 23-26 280 4 115-130
630 261 0 7 271 2 27-30 281 5 131-162
631 262 0 8 272 2 31-34 282 5 163-194
632 263 0 9 273 3 35-42 283 5 195-226
633 264 0 10 274 3 43-50 284 5 227-257
634 265 1 11,12 275 3 51-58 285 0 258
635 266 1 13,14 276 3 59-66
636
637 The extra bits should be interpreted as a machine integer
638 stored with the most-significant bit first, e.g., bits 1110
639 represent the value 14.
640
641 Extra Extra Extra
642 Code Bits Dist Code Bits Dist Code Bits Distance
643 ---- ---- ---- ---- ---- ------ ---- ---- --------
644 0 0 1 10 4 33-48 20 9 1025-1536
645 1 0 2 11 4 49-64 21 9 1537-2048
646 2 0 3 12 5 65-96 22 10 2049-3072
647 3 0 4 13 5 97-128 23 10 3073-4096
648 4 1 5,6 14 6 129-192 24 11 4097-6144
649 5 1 7,8 15 6 193-256 25 11 6145-8192
650 6 2 9-12 16 7 257-384 26 12 8193-12288
651 7 2 13-16 17 7 385-512 27 12 12289-16384
652 8 3 17-24 18 8 513-768 28 13 16385-24576
653 9 3 25-32 19 8 769-1024 29 13 24577-32768
654
655 3.2.6. Compression with fixed Huffman codes (BTYPE=01)
656
657 The Huffman codes for the two alphabets are fixed, and are not
658 represented explicitly in the data. The Huffman code lengths
659 for the literal/length alphabet are:
660
661 Lit Value Bits Codes
662 --------- ---- -----
663 0 - 143 8 00110000 through
664 10111111
665 144 - 255 9 110010000 through
666 111111111
667 256 - 279 7 0000000 through
668 0010111
669 280 - 287 8 11000000 through
670 11000111
671
672
673
674Deutsch Informational [Page 12]
675
676RFC 1951 DEFLATE Compressed Data Format Specification May 1996
677
678
679 The code lengths are sufficient to generate the actual codes,
680 as described above; we show the codes in the table for added
681 clarity. Literal/length values 286-287 will never actually
682 occur in the compressed data, but participate in the code
683 construction.
684
685 Distance codes 0-31 are represented by (fixed-length) 5-bit
686 codes, with possible additional bits as shown in the table
687 shown in Paragraph 3.2.5, above. Note that distance codes 30-
688 31 will never actually occur in the compressed data.
689
690 3.2.7. Compression with dynamic Huffman codes (BTYPE=10)
691
692 The Huffman codes for the two alphabets appear in the block
693 immediately after the header bits and before the actual
694 compressed data, first the literal/length code and then the
695 distance code. Each code is defined by a sequence of code
696 lengths, as discussed in Paragraph 3.2.2, above. For even
697 greater compactness, the code length sequences themselves are
698 compressed using a Huffman code. The alphabet for code lengths
699 is as follows:
700
701 0 - 15: Represent code lengths of 0 - 15
702 16: Copy the previous code length 3 - 6 times.
703 The next 2 bits indicate repeat length
704 (0 = 3, ... , 3 = 6)
705 Example: Codes 8, 16 (+2 bits 11),
706 16 (+2 bits 10) will expand to
707 12 code lengths of 8 (1 + 6 + 5)
708 17: Repeat a code length of 0 for 3 - 10 times.
709 (3 bits of length)
710 18: Repeat a code length of 0 for 11 - 138 times
711 (7 bits of length)
712
713 A code length of 0 indicates that the corresponding symbol in
714 the literal/length or distance alphabet will not occur in the
715 block, and should not participate in the Huffman code
716 construction algorithm given earlier. If only one distance
717 code is used, it is encoded using one bit, not zero bits; in
718 this case there is a single code length of one, with one unused
719 code. One distance code of zero bits means that there are no
720 distance codes used at all (the data is all literals).
721
722 We can now define the format of the block:
723
724 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286)
725 5 Bits: HDIST, # of Distance codes - 1 (1 - 32)
726 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19)
727
728
729
730Deutsch Informational [Page 13]
731
732RFC 1951 DEFLATE Compressed Data Format Specification May 1996
733
734
735 (HCLEN + 4) x 3 bits: code lengths for the code length
736 alphabet given just above, in the order: 16, 17, 18,
737 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
738
739 These code lengths are interpreted as 3-bit integers
740 (0-7); as above, a code length of 0 means the
741 corresponding symbol (literal/length or distance code
742 length) is not used.
743
744 HLIT + 257 code lengths for the literal/length alphabet,
745 encoded using the code length Huffman code
746
747 HDIST + 1 code lengths for the distance alphabet,
748 encoded using the code length Huffman code
749
750 The actual compressed data of the block,
751 encoded using the literal/length and distance Huffman
752 codes
753
754 The literal/length symbol 256 (end of data),
755 encoded using the literal/length Huffman code
756
757 The code length repeat codes can cross from HLIT + 257 to the
758 HDIST + 1 code lengths. In other words, all code lengths form
759 a single sequence of HLIT + HDIST + 258 values.
760
761 3.3. Compliance
762
763 A compressor may limit further the ranges of values specified in
764 the previous section and still be compliant; for example, it may
765 limit the range of backward pointers to some value smaller than
766 32K. Similarly, a compressor may limit the size of blocks so that
767 a compressible block fits in memory.
768
769 A compliant decompressor must accept the full range of possible
770 values defined in the previous section, and must accept blocks of
771 arbitrary size.
772
7734. Compression algorithm details
774
775 While it is the intent of this document to define the "deflate"
776 compressed data format without reference to any particular
777 compression algorithm, the format is related to the compressed
778 formats produced by LZ77 (Lempel-Ziv 1977, see reference [2] below);
779 since many variations of LZ77 are patented, it is strongly
780 recommended that the implementor of a compressor follow the general
781 algorithm presented here, which is known not to be patented per se.
782 The material in this section is not part of the definition of the
783
784
785
786Deutsch Informational [Page 14]
787
788RFC 1951 DEFLATE Compressed Data Format Specification May 1996
789
790
791 specification per se, and a compressor need not follow it in order to
792 be compliant.
793
794 The compressor terminates a block when it determines that starting a
795 new block with fresh trees would be useful, or when the block size
796 fills up the compressor's block buffer.
797
798 The compressor uses a chained hash table to find duplicated strings,
799 using a hash function that operates on 3-byte sequences. At any
800 given point during compression, let XYZ be the next 3 input bytes to
801 be examined (not necessarily all different, of course). First, the
802 compressor examines the hash chain for XYZ. If the chain is empty,
803 the compressor simply writes out X as a literal byte and advances one
804 byte in the input. If the hash chain is not empty, indicating that
805 the sequence XYZ (or, if we are unlucky, some other 3 bytes with the
806 same hash function value) has occurred recently, the compressor
807 compares all strings on the XYZ hash chain with the actual input data
808 sequence starting at the current point, and selects the longest
809 match.
810
811 The compressor searches the hash chains starting with the most recent
812 strings, to favor small distances and thus take advantage of the
813 Huffman encoding. The hash chains are singly linked. There are no
814 deletions from the hash chains; the algorithm simply discards matches
815 that are too old. To avoid a worst-case situation, very long hash
816 chains are arbitrarily truncated at a certain length, determined by a
817 run-time parameter.
818
819 To improve overall compression, the compressor optionally defers the
820 selection of matches ("lazy matching"): after a match of length N has
821 been found, the compressor searches for a longer match starting at
822 the next input byte. If it finds a longer match, it truncates the
823 previous match to a length of one (thus producing a single literal
824 byte) and then emits the longer match. Otherwise, it emits the
825 original match, and, as described above, advances N bytes before
826 continuing.
827
828 Run-time parameters also control this "lazy match" procedure. If
829 compression ratio is most important, the compressor attempts a
830 complete second search regardless of the length of the first match.
831 In the normal case, if the current match is "long enough", the
832 compressor reduces the search for a longer match, thus speeding up
833 the process. If speed is most important, the compressor inserts new
834 strings in the hash table only when no match was found, or when the
835 match is not "too long". This degrades the compression ratio but
836 saves time since there are both fewer insertions and fewer searches.
837
838
839
840
841
842Deutsch Informational [Page 15]
843
844RFC 1951 DEFLATE Compressed Data Format Specification May 1996
845
846
8475. References
848
849 [1] Huffman, D. A., "A Method for the Construction of Minimum
850 Redundancy Codes", Proceedings of the Institute of Radio
851 Engineers, September 1952, Volume 40, Number 9, pp. 1098-1101.
852
853 [2] Ziv J., Lempel A., "A Universal Algorithm for Sequential Data
854 Compression", IEEE Transactions on Information Theory, Vol. 23,
855 No. 3, pp. 337-343.
856
857 [3] Gailly, J.-L., and Adler, M., ZLIB documentation and sources,
858 available in ftp://ftp.uu.net/pub/archiving/zip/doc/
859
860 [4] Gailly, J.-L., and Adler, M., GZIP documentation and sources,
861 available as gzip-*.tar in ftp://prep.ai.mit.edu/pub/gnu/
862
863 [5] Schwartz, E. S., and Kallick, B. "Generating a canonical prefix
864 encoding." Comm. ACM, 7,3 (Mar. 1964), pp. 166-169.
865
866 [6] Hirschberg and Lelewer, "Efficient decoding of prefix codes,"
867 Comm. ACM, 33,4, April 1990, pp. 449-459.
868
8696. Security Considerations
870
871 Any data compression method involves the reduction of redundancy in
872 the data. Consequently, any corruption of the data is likely to have
873 severe effects and be difficult to correct. Uncompressed text, on
874 the other hand, will probably still be readable despite the presence
875 of some corrupted bytes.
876
877 It is recommended that systems using this data format provide some
878 means of validating the integrity of the compressed data. See
879 reference [3], for example.
880
8817. Source code
882
883 Source code for a C language implementation of a "deflate" compliant
884 compressor and decompressor is available within the zlib package at
885 ftp://ftp.uu.net/pub/archiving/zip/zlib/.
886
8878. Acknowledgements
888
889 Trademarks cited in this document are the property of their
890 respective owners.
891
892 Phil Katz designed the deflate format. Jean-Loup Gailly and Mark
893 Adler wrote the related software described in this specification.
894 Glenn Randers-Pehrson converted this document to RFC and HTML format.
895
896
897
898Deutsch Informational [Page 16]
899
900RFC 1951 DEFLATE Compressed Data Format Specification May 1996
901
902
9039. Author's Address
904
905 L. Peter Deutsch
906 Aladdin Enterprises
907 203 Santa Margarita Ave.
908 Menlo Park, CA 94025
909
910 Phone: (415) 322-0103 (AM only)
911 FAX: (415) 322-1734
912 EMail: <ghost@aladdin.com>
913
914 Questions about the technical content of this specification can be
915 sent by email to:
916
917 Jean-Loup Gailly <gzip@prep.ai.mit.edu> and
918 Mark Adler <madler@alumni.caltech.edu>
919
920 Editorial comments on this specification can be sent by email to:
921
922 L. Peter Deutsch <ghost@aladdin.com> and
923 Glenn Randers-Pehrson <randeg@alumni.rpi.edu>
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954Deutsch Informational [Page 17]
955
lib/std/compress/rfc1951.txt.fixed.z.9 created
Binary files /dev/null and b/lib/std/compress/rfc1951.txt.fixed.z.9 differ
lib/std/compress/rfc1951.txt.z.0 created
Binary files /dev/null and b/lib/std/compress/rfc1951.txt.z.0 differ
lib/std/compress/rfc1951.txt.z.9 created
Binary files /dev/null and b/lib/std/compress/rfc1951.txt.z.9 differ
lib/std/compress/rfc1952.txt.gz created
Binary files /dev/null and b/lib/std/compress/rfc1952.txt.gz differ
lib/std/compress/zlib.zig created+178
......@@ -0,0 +1,178 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6//
7// Decompressor for ZLIB data streams (RFC1950)
8
9const std = @import("std");
10const io = std.io;
11const fs = std.fs;
12const testing = std.testing;
13const mem = std.mem;
14const deflate = std.compress.deflate;
15
16pub fn ZlibStream(comptime ReaderType: type) type {
17 return struct {
18 const Self = @This();
19
20 pub const Error = ReaderType.Error ||
21 deflate.InflateStream(ReaderType).Error ||
22 error{ WrongChecksum, Unsupported };
23 pub const Reader = io.Reader(*Self, Error, read);
24
25 allocator: *mem.Allocator,
26 inflater: deflate.InflateStream(ReaderType),
27 in_reader: ReaderType,
28 hasher: std.hash.Adler32,
29 window_slice: []u8,
30
31 fn init(allocator: *mem.Allocator, source: ReaderType) !Self {
32 // Zlib header format is specified in RFC1950
33 const header = try source.readBytesNoEof(2);
34
35 const CM = @truncate(u4, header[0]);
36 const CINFO = @truncate(u4, header[0] >> 4);
37 const FCHECK = @truncate(u5, header[1]);
38 const FDICT = @truncate(u1, header[1] >> 5);
39
40 if ((@as(u16, header[0]) << 8 | header[1]) % 31 != 0)
41 return error.BadHeader;
42
43 // The CM field must be 8 to indicate the use of DEFLATE
44 if (CM != 8) return error.InvalidCompression;
45 // CINFO is the base-2 logarithm of the window size, minus 8.
46 // Values above 7 are unspecified and therefore rejected.
47 if (CINFO > 7) return error.InvalidWindowSize;
48 const window_size: u16 = @as(u16, 1) << (CINFO + 8);
49
50 // TODO: Support this case
51 if (FDICT != 0)
52 return error.Unsupported;
53
54 var window_slice = try allocator.alloc(u8, window_size);
55
56 return Self{
57 .allocator = allocator,
58 .inflater = deflate.inflateStream(source, window_slice),
59 .in_reader = source,
60 .hasher = std.hash.Adler32.init(),
61 .window_slice = window_slice,
62 };
63 }
64
65 pub fn deinit(self: *Self) void {
66 self.allocator.free(self.window_slice);
67 }
68
69 // Implements the io.Reader interface
70 pub fn read(self: *Self, buffer: []u8) Error!usize {
71 if (buffer.len == 0)
72 return 0;
73
74 // Read from the compressed stream and update the computed checksum
75 const r = try self.inflater.read(buffer);
76 if (r != 0) {
77 self.hasher.update(buffer[0..r]);
78 return r;
79 }
80
81 // We've reached the end of stream, check if the checksum matches
82 const hash = try self.in_reader.readIntBig(u32);
83 if (hash != self.hasher.final())
84 return error.WrongChecksum;
85
86 return 0;
87 }
88
89 pub fn reader(self: *Self) Reader {
90 return .{ .context = self };
91 }
92 };
93}
94
95pub fn zlibStream(allocator: *mem.Allocator, reader: anytype) !ZlibStream(@TypeOf(reader)) {
96 return ZlibStream(@TypeOf(reader)).init(allocator, reader);
97}
98
99fn testReader(data: []const u8, comptime expected: []const u8) !void {
100 var in_stream = io.fixedBufferStream(data);
101
102 var zlib_stream = try zlibStream(testing.allocator, in_stream.reader());
103 defer zlib_stream.deinit();
104
105 // Read and decompress the whole file
106 const buf = try zlib_stream.reader().readAllAlloc(testing.allocator, std.math.maxInt(usize));
107 defer testing.allocator.free(buf);
108 // Calculate its SHA256 hash and check it against the reference
109 var hash: [32]u8 = undefined;
110 std.crypto.hash.sha2.Sha256.hash(buf, hash[0..], .{});
111
112 assertEqual(expected, &hash);
113}
114
115// Assert `expected` == `input` where `input` is a bytestring.
116pub fn assertEqual(comptime expected: []const u8, input: []const u8) void {
117 var expected_bytes: [expected.len / 2]u8 = undefined;
118 for (expected_bytes) |*r, i| {
119 r.* = std.fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable;
120 }
121
122 testing.expectEqualSlices(u8, &expected_bytes, input);
123}
124
125// All the test cases are obtained by compressing the RFC1950 text
126//
127// https://tools.ietf.org/rfc/rfc1950.txt length=36944 bytes
128// SHA256=5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009
129test "compressed data" {
130 // Compressed with compression level = 0
131 try testReader(
132 @embedFile("rfc1951.txt.z.0"),
133 "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009",
134 );
135 // Compressed with compression level = 9
136 try testReader(
137 @embedFile("rfc1951.txt.z.9"),
138 "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009",
139 );
140 // Compressed with compression level = 9 and fixed Huffman codes
141 try testReader(
142 @embedFile("rfc1951.txt.fixed.z.9"),
143 "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009",
144 );
145}
146
147test "sanity checks" {
148 // Truncated header
149 testing.expectError(
150 error.EndOfStream,
151 testReader(&[_]u8{0x78}, ""),
152 );
153 // Failed FCHECK check
154 testing.expectError(
155 error.BadHeader,
156 testReader(&[_]u8{ 0x78, 0x9D }, ""),
157 );
158 // Wrong CM
159 testing.expectError(
160 error.InvalidCompression,
161 testReader(&[_]u8{ 0x79, 0x94 }, ""),
162 );
163 // Wrong CINFO
164 testing.expectError(
165 error.InvalidWindowSize,
166 testReader(&[_]u8{ 0x88, 0x98 }, ""),
167 );
168 // Wrong checksum
169 testing.expectError(
170 error.WrongChecksum,
171 testReader(&[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, ""),
172 );
173 // Truncated checksum
174 testing.expectError(
175 error.EndOfStream,
176 testReader(&[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, ""),
177 );
178}
lib/std/crypto.zig+24
......@@ -35,6 +35,15 @@ pub const onetimeauth = struct {
3535 pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
3636};
3737
38/// A Key Derivation Function (KDF) is intended to turn a weak, human generated password into a
39/// strong key, suitable for cryptographic uses. It does this by salting and stretching the
40/// password. Salting injects non-secret random data, so that identical passwords will be converted
41/// into unique keys. Stretching applies a deliberately slow hashing function to frustrate
42/// brute-force guessing.
43pub const kdf = struct {
44 pub const pbkdf2 = @import("crypto/pbkdf2.zig").pbkdf2;
45};
46
3847/// Core functions, that should rarely be used directly by applications.
3948pub const core = struct {
4049 pub const aes = @import("crypto/aes.zig");
......@@ -70,6 +79,20 @@ const std = @import("std.zig");
7079pub const randomBytes = std.os.getrandom;
7180
7281test "crypto" {
82 inline for (std.meta.declarations(@This())) |decl| {
83 switch (decl.data) {
84 .Type => |t| {
85 std.meta.refAllDecls(t);
86 },
87 .Var => |v| {
88 _ = v;
89 },
90 .Fn => |f| {
91 _ = f;
92 },
93 }
94 }
95
7396 _ = @import("crypto/aes.zig");
7497 _ = @import("crypto/blake2.zig");
7598 _ = @import("crypto/blake3.zig");
......@@ -77,6 +100,7 @@ test "crypto" {
77100 _ = @import("crypto/gimli.zig");
78101 _ = @import("crypto/hmac.zig");
79102 _ = @import("crypto/md5.zig");
103 _ = @import("crypto/pbkdf2.zig");
80104 _ = @import("crypto/poly1305.zig");
81105 _ = @import("crypto/sha1.zig");
82106 _ = @import("crypto/sha2.zig");
lib/std/crypto/benchmark.zig+2-2
......@@ -5,8 +5,8 @@
55// and substantial portions of the software.
66// zig run benchmark.zig --release-fast --override-lib-dir ..
77
8const builtin = @import("builtin");
9const std = @import("std");
8const std = @import("../std.zig");
9const builtin = std.builtin;
1010const mem = std.mem;
1111const time = std.time;
1212const Timer = time.Timer;
lib/std/crypto/blake2.zig+4-4
......@@ -3,10 +3,10 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6const mem = @import("../mem.zig");
7const builtin = @import("builtin");
8const debug = @import("../debug.zig");
9const math = @import("../math.zig");
6const std = @import("../std.zig");
7const mem = std.mem;
8const math = std.math;
9const debug = std.debug;
1010const htest = @import("test.zig");
1111
1212const RoundParam = struct {
lib/std/crypto/chacha20.zig-2
......@@ -7,10 +7,8 @@
77
88const std = @import("../std.zig");
99const mem = std.mem;
10const endian = std.endian;
1110const assert = std.debug.assert;
1211const testing = std.testing;
13const builtin = @import("builtin");
1412const maxInt = std.math.maxInt;
1513const Poly1305 = std.crypto.onetimeauth.Poly1305;
1614
lib/std/crypto/md5.zig+4-6
......@@ -3,12 +3,10 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6const mem = @import("../mem.zig");
7const math = @import("../math.zig");
8const endian = @import("../endian.zig");
9const builtin = @import("builtin");
10const debug = @import("../debug.zig");
11const fmt = @import("../fmt.zig");
6const std = @import("../std.zig");
7const mem = std.mem;
8const math = std.math;
9const debug = std.debug;
1210
1311const RoundParam = struct {
1412 a: usize,
lib/std/crypto/pbkdf2.zig created+280
......@@ -0,0 +1,280 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7const std = @import("std");
8const mem = std.mem;
9const maxInt = std.math.maxInt;
10
11// RFC 2898 Section 5.2
12//
13// FromSpec:
14//
15// PBKDF2 applies a pseudorandom function (see Appendix B.1 for an
16// example) to derive keys. The length of the derived key is essentially
17// unbounded. (However, the maximum effective search space for the
18// derived key may be limited by the structure of the underlying
19// pseudorandom function. See Appendix B.1 for further discussion.)
20// PBKDF2 is recommended for new applications.
21//
22// PBKDF2 (P, S, c, dkLen)
23//
24// Options: PRF underlying pseudorandom function (hLen
25// denotes the length in octets of the
26// pseudorandom function output)
27//
28// Input: P password, an octet string
29// S salt, an octet string
30// c iteration count, a positive integer
31// dkLen intended length in octets of the derived
32// key, a positive integer, at most
33// (2^32 - 1) * hLen
34//
35// Output: DK derived key, a dkLen-octet string
36
37// Based on Apple's CommonKeyDerivation, based originally on code by Damien Bergamini.
38
39pub const Pbkdf2Error = error{
40 /// At least one round is required
41 TooFewRounds,
42
43 /// Maximum length of the derived key is `maxInt(u32) * Prf.mac_length`
44 DerivedKeyTooLong,
45};
46
47/// Apply PBKDF2 to generate a key from a password.
48///
49/// PBKDF2 is defined in RFC 2898, and is a recommendation of NIST SP 800-132.
50///
51/// derivedKey: Slice of appropriate size for generated key. Generally 16 or 32 bytes in length.
52/// May be uninitialized. All bytes will be overwritten.
53/// Maximum size is `maxInt(u32) * Hash.digest_length`
54/// It is a programming error to pass buffer longer than the maximum size.
55///
56/// password: Arbitrary sequence of bytes of any length, including empty.
57///
58/// salt: Arbitrary sequence of bytes of any length, including empty. A common length is 8 bytes.
59///
60/// rounds: Iteration count. Must be greater than 0. Common values range from 1,000 to 100,000.
61/// Larger iteration counts improve security by increasing the time required to compute
62/// the derivedKey. It is common to tune this parameter to achieve approximately 100ms.
63///
64/// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`.
65pub fn pbkdf2(derivedKey: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Pbkdf2Error!void {
66 if (rounds < 1) return error.TooFewRounds;
67
68 const dkLen = derivedKey.len;
69 const hLen = Prf.mac_length;
70 comptime std.debug.assert(hLen >= 1);
71
72 // FromSpec:
73 //
74 // 1. If dkLen > maxInt(u32) * hLen, output "derived key too long" and
75 // stop.
76 //
77 if (comptime (maxInt(usize) > maxInt(u32) * hLen) and (dkLen > @as(usize, maxInt(u32) * hLen))) {
78 // If maxInt(usize) is less than `maxInt(u32) * hLen` then dkLen is always inbounds
79 return error.DerivedKeyTooLong;
80 }
81
82 // FromSpec:
83 //
84 // 2. Let l be the number of hLen-long blocks of bytes in the derived key,
85 // rounding up, and let r be the number of bytes in the last
86 // block
87 //
88
89 // l will not overflow, proof:
90 // let `L(dkLen, hLen) = (dkLen + hLen - 1) / hLen`
91 // then `L^-1(l, hLen) = l*hLen - hLen + 1`
92 // 1) L^-1(maxInt(u32), hLen) <= maxInt(u32)*hLen
93 // 2) maxInt(u32)*hLen - hLen + 1 <= maxInt(u32)*hLen // subtract maxInt(u32)*hLen + 1
94 // 3) -hLen <= -1 // multiply by -1
95 // 4) hLen >= 1
96 const r_ = dkLen % hLen;
97 const l = @intCast(u32, (dkLen / hLen) + @as(u1, if (r_ == 0) 0 else 1)); // original: (dkLen + hLen - 1) / hLen
98 const r = if (r_ == 0) hLen else r_;
99
100 // FromSpec:
101 //
102 // 3. For each block of the derived key apply the function F defined
103 // below to the password P, the salt S, the iteration count c, and
104 // the block index to compute the block:
105 //
106 // T_1 = F (P, S, c, 1) ,
107 // T_2 = F (P, S, c, 2) ,
108 // ...
109 // T_l = F (P, S, c, l) ,
110 //
111 // where the function F is defined as the exclusive-or sum of the
112 // first c iterates of the underlying pseudorandom function PRF
113 // applied to the password P and the concatenation of the salt S
114 // and the block index i:
115 //
116 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
117 //
118 // where
119 //
120 // U_1 = PRF (P, S || INT (i)) ,
121 // U_2 = PRF (P, U_1) ,
122 // ...
123 // U_c = PRF (P, U_{c-1}) .
124 //
125 // Here, INT (i) is a four-octet encoding of the integer i, most
126 // significant octet first.
127 //
128 // 4. Concatenate the blocks and extract the first dkLen octets to
129 // produce a derived key DK:
130 //
131 // DK = T_1 || T_2 || ... || T_l<0..r-1>
132 var block: u32 = 0; // Spec limits to u32
133 while (block < l) : (block += 1) {
134 var prevBlock: [hLen]u8 = undefined;
135 var newBlock: [hLen]u8 = undefined;
136
137 // U_1 = PRF (P, S || INT (i))
138 const blockIndex = mem.toBytes(mem.nativeToBig(u32, block + 1)); // Block index starts at 0001
139 var ctx = Prf.init(password);
140 ctx.update(salt);
141 ctx.update(blockIndex[0..]);
142 ctx.final(prevBlock[0..]);
143
144 // Choose portion of DK to write into (T_n) and initialize
145 const offset = block * hLen;
146 const blockLen = if (block != l - 1) hLen else r;
147 const dkBlock: []u8 = derivedKey[offset..][0..blockLen];
148 mem.copy(u8, dkBlock, prevBlock[0..dkBlock.len]);
149
150 var i: u32 = 1;
151 while (i < rounds) : (i += 1) {
152 // U_c = PRF (P, U_{c-1})
153 Prf.create(&newBlock, prevBlock[0..], password);
154 mem.copy(u8, prevBlock[0..], newBlock[0..]);
155
156 // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
157 for (dkBlock) |_, j| {
158 dkBlock[j] ^= newBlock[j];
159 }
160 }
161 }
162}
163
164const htest = @import("test.zig");
165const HmacSha1 = std.crypto.auth.hmac.HmacSha1;
166
167// RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors
168test "RFC 6070 one iteration" {
169 const p = "password";
170 const s = "salt";
171 const c = 1;
172 const dkLen = 20;
173
174 var derivedKey: [dkLen]u8 = undefined;
175
176 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
177
178 const expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6";
179
180 htest.assertEqual(expected, derivedKey[0..]);
181}
182
183test "RFC 6070 two iterations" {
184 const p = "password";
185 const s = "salt";
186 const c = 2;
187 const dkLen = 20;
188
189 var derivedKey: [dkLen]u8 = undefined;
190
191 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
192
193 const expected = "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957";
194
195 htest.assertEqual(expected, derivedKey[0..]);
196}
197
198test "RFC 6070 4096 iterations" {
199 const p = "password";
200 const s = "salt";
201 const c = 4096;
202 const dkLen = 20;
203
204 var derivedKey: [dkLen]u8 = undefined;
205
206 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
207
208 const expected = "4b007901b765489abead49d926f721d065a429c1";
209
210 htest.assertEqual(expected, derivedKey[0..]);
211}
212
213test "RFC 6070 16,777,216 iterations" {
214 // These iteration tests are slow so we always skip them. Results have been verified.
215 if (true) {
216 return error.SkipZigTest;
217 }
218
219 const p = "password";
220 const s = "salt";
221 const c = 16777216;
222 const dkLen = 20;
223
224 var derivedKey = [_]u8{0} ** dkLen;
225
226 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
227
228 const expected = "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984";
229
230 htest.assertEqual(expected, derivedKey[0..]);
231}
232
233test "RFC 6070 multi-block salt and password" {
234 const p = "passwordPASSWORDpassword";
235 const s = "saltSALTsaltSALTsaltSALTsaltSALTsalt";
236 const c = 4096;
237 const dkLen = 25;
238
239 var derivedKey: [dkLen]u8 = undefined;
240
241 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
242
243 const expected = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038";
244
245 htest.assertEqual(expected, derivedKey[0..]);
246}
247
248test "RFC 6070 embedded NUL" {
249 const p = "pass\x00word";
250 const s = "sa\x00lt";
251 const c = 4096;
252 const dkLen = 16;
253
254 var derivedKey: [dkLen]u8 = undefined;
255
256 try pbkdf2(&derivedKey, p, s, c, HmacSha1);
257
258 const expected = "56fa6aa75548099dcc37d7f03425e0c3";
259
260 htest.assertEqual(expected, derivedKey[0..]);
261}
262
263test "Very large dkLen" {
264 // This test allocates 8GB of memory and is expected to take several hours to run.
265 if (true) {
266 return error.SkipZigTest;
267 }
268 const p = "password";
269 const s = "salt";
270 const c = 1;
271 const dkLen = 1 << 33;
272
273 var derivedKey = try std.testing.allocator.alloc(u8, dkLen);
274 defer {
275 std.testing.allocator.free(derivedKey);
276 }
277
278 try pbkdf2(derivedKey, p, s, c, HmacSha1);
279 // Just verify this doesn't crash with an overflow
280}
lib/std/crypto/poly1305.zig+1-1
......@@ -3,7 +3,7 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6const std = @import("std");
6const std = @import("../std.zig");
77const mem = std.mem;
88
99pub const Poly1305 = struct {
lib/std/crypto/sha1.zig+4-5
......@@ -3,11 +3,10 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6const mem = @import("../mem.zig");
7const math = @import("../math.zig");
8const endian = @import("../endian.zig");
9const debug = @import("../debug.zig");
10const builtin = @import("builtin");
6const std = @import("../std.zig");
7const mem = std.mem;
8const math = std.math;
9const debug = std.debug;
1110
1211const RoundParam = struct {
1312 a: usize,
lib/std/crypto/sha2.zig+4-5
......@@ -3,11 +3,10 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6const mem = @import("../mem.zig");
7const math = @import("../math.zig");
8const endian = @import("../endian.zig");
9const debug = @import("../debug.zig");
10const builtin = @import("builtin");
6const std = @import("../std.zig");
7const mem = std.mem;
8const math = std.math;
9const debug = std.debug;
1110const htest = @import("test.zig");
1211
1312/////////////////////
lib/std/crypto/sha3.zig+4-5
......@@ -3,11 +3,10 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6const mem = @import("../mem.zig");
7const math = @import("../math.zig");
8const endian = @import("../endian.zig");
9const debug = @import("../debug.zig");
10const builtin = @import("builtin");
6const std = @import("../std.zig");
7const mem = std.mem;
8const math = std.math;
9const debug = std.debug;
1110const htest = @import("test.zig");
1211
1312pub const Sha3_224 = Keccak(224, 0x06);
lib/std/crypto/siphash.zig+2-1
......@@ -218,8 +218,9 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize)
218218 }
219219
220220 /// Return an authentication tag for the current state
221 /// Assumes `out` is less than or equal to `mac_length`.
221222 pub fn final(self: *Self, out: []u8) void {
222 std.debug.assert(out.len >= mac_length);
223 std.debug.assert(out.len <= mac_length);
223224 mem.writeIntLittle(T, out[0..mac_length], self.state.final(self.buf[0..self.buf_len]));
224225 }
225226
lib/std/crypto/test.zig-1
......@@ -5,7 +5,6 @@
55// and substantial portions of the software.
66const std = @import("../std.zig");
77const testing = std.testing;
8const mem = std.mem;
98const fmt = std.fmt;
109
1110// Hash using the specified hasher `H` asserting `expected == H(input)`.
lib/std/elf.zig+1-1
......@@ -471,7 +471,7 @@ pub const SectionHeaderIterator = struct {
471471
472472 if (self.elf_header.is_64) {
473473 var shdr: Elf64_Shdr = undefined;
474 const offset = self.elf_header.phoff + @sizeOf(@TypeOf(shdr)) * self.index;
474 const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
475475 try preadNoEof(self.file, mem.asBytes(&shdr), offset);
476476
477477 // ELF endianness matches native endianness.
lib/std/event/loop.zig+2-1
......@@ -112,7 +112,8 @@ pub const Loop = struct {
112112 /// have the correct pointer value.
113113 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765
114114 pub fn init(self: *Loop) !void {
115 if (builtin.single_threaded) {
115 if (builtin.single_threaded
116 or (@hasDecl(root, "event_loop_mode") and root.event_loop_mode == .single_threaded)) {
116117 return self.initSingleThreaded();
117118 } else {
118119 return self.initMultiThreaded();
lib/std/fmt.zig+57-84
......@@ -22,7 +22,7 @@ pub const Alignment = enum {
2222pub const FormatOptions = struct {
2323 precision: ?usize = null,
2424 width: ?usize = null,
25 alignment: Alignment = .Left,
25 alignment: Alignment = .Right,
2626 fill: u8 = ' ',
2727};
2828
......@@ -327,7 +327,7 @@ pub fn formatType(
327327 max_depth: usize,
328328) @TypeOf(writer).Error!void {
329329 if (comptime std.mem.eql(u8, fmt, "*")) {
330 try writer.writeAll(@typeName(@typeInfo(@TypeOf(value)).Pointer.child));
330 try writer.writeAll(@typeName(std.meta.Child(@TypeOf(value))));
331331 try writer.writeAll("@");
332332 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
333333 return;
......@@ -399,7 +399,7 @@ pub fn formatType(
399399 try writer.writeAll(@tagName(@as(UnionTagType, value)));
400400 try writer.writeAll(" = ");
401401 inline for (info.fields) |u_field| {
402 if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {
402 if (value == @field(UnionTagType, u_field.name)) {
403403 try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);
404404 }
405405 }
......@@ -631,26 +631,22 @@ pub fn formatBuf(
631631 writer: anytype,
632632) !void {
633633 const width = options.width orelse buf.len;
634 var padding = if (width > buf.len) (width - buf.len) else 0;
635 const pad_byte = [1]u8{options.fill};
634 const padding = if (width > buf.len) (width - buf.len) else 0;
635
636636 switch (options.alignment) {
637637 .Left => {
638638 try writer.writeAll(buf);
639 while (padding > 0) : (padding -= 1) {
640 try writer.writeAll(&pad_byte);
641 }
639 try writer.writeByteNTimes(options.fill, padding);
642640 },
643641 .Center => {
644 const padl = padding / 2;
645 var i: usize = 0;
646 while (i < padl) : (i += 1) try writer.writeAll(&pad_byte);
642 const left_padding = padding / 2;
643 const right_padding = (padding + 1) / 2;
644 try writer.writeByteNTimes(options.fill, left_padding);
647645 try writer.writeAll(buf);
648 while (i < padding) : (i += 1) try writer.writeAll(&pad_byte);
646 try writer.writeByteNTimes(options.fill, right_padding);
649647 },
650648 .Right => {
651 while (padding > 0) : (padding -= 1) {
652 try writer.writeAll(&pad_byte);
653 }
649 try writer.writeByteNTimes(options.fill, padding);
654650 try writer.writeAll(buf);
655651 },
656652 }
......@@ -941,61 +937,27 @@ pub fn formatInt(
941937 options: FormatOptions,
942938 writer: anytype,
943939) !void {
940 assert(base >= 2);
941
944942 const int_value = if (@TypeOf(value) == comptime_int) blk: {
945943 const Int = math.IntFittingRange(value, value);
946944 break :blk @as(Int, value);
947945 } else
948946 value;
949947
950 if (@typeInfo(@TypeOf(int_value)).Int.is_signed) {
951 return formatIntSigned(int_value, base, uppercase, options, writer);
952 } else {
953 return formatIntUnsigned(int_value, base, uppercase, options, writer);
954 }
955}
948 const value_info = @typeInfo(@TypeOf(int_value)).Int;
956949
957fn formatIntSigned(
958 value: anytype,
959 base: u8,
960 uppercase: bool,
961 options: FormatOptions,
962 writer: anytype,
963) !void {
964 const new_options = FormatOptions{
965 .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,
966 .precision = options.precision,
967 .fill = options.fill,
968 };
969 const bit_count = @typeInfo(@TypeOf(value)).Int.bits;
970 const Uint = std.meta.Int(false, bit_count);
971 if (value < 0) {
972 try writer.writeAll("-");
973 const new_value = math.absCast(value);
974 return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
975 } else if (options.width == null or options.width.? == 0) {
976 return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, writer);
977 } else {
978 try writer.writeAll("+");
979 const new_value = @intCast(Uint, value);
980 return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
981 }
982}
950 // The type must have the same size as `base` or be wider in order for the
951 // division to work
952 const min_int_bits = comptime math.max(value_info.bits, 8);
953 const MinInt = std.meta.Int(false, min_int_bits);
983954
984fn formatIntUnsigned(
985 value: anytype,
986 base: u8,
987 uppercase: bool,
988 options: FormatOptions,
989 writer: anytype,
990) !void {
991 assert(base >= 2);
992 const value_info = @typeInfo(@TypeOf(value)).Int;
993 var buf: [math.max(value_info.bits, 1)]u8 = undefined;
994 const min_int_bits = comptime math.max(value_info.bits, @typeInfo(@TypeOf(base)).Int.bits);
995 const MinInt = std.meta.Int(value_info.is_signed, min_int_bits);
996 var a: MinInt = value;
997 var index: usize = buf.len;
955 const abs_value = math.absCast(int_value);
956 // The worst case in terms of space needed is base 2, plus 1 for the sign
957 var buf: [1 + math.max(value_info.bits, 1)]u8 = undefined;
998958
959 var a: MinInt = abs_value;
960 var index: usize = buf.len;
999961 while (true) {
1000962 const digit = a % base;
1001963 index -= 1;
......@@ -1004,25 +966,21 @@ fn formatIntUnsigned(
1004966 if (a == 0) break;
1005967 }
1006968
1007 const digits_buf = buf[index..];
1008 const width = options.width orelse 0;
1009 const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;
1010
1011 if (padding > index) {
1012 const zero_byte: u8 = options.fill;
1013 var leftover_padding = padding - index;
1014 while (true) {
1015 try writer.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
1016 leftover_padding -= 1;
1017 if (leftover_padding == 0) break;
969 if (value_info.is_signed) {
970 if (value < 0) {
971 // Negative integer
972 index -= 1;
973 buf[index] = '-';
974 } else if (options.width == null or options.width.? == 0) {
975 // Positive integer, omit the plus sign
976 } else {
977 // Positive integer
978 index -= 1;
979 buf[index] = '+';
1018980 }
1019 mem.set(u8, buf[0..index], options.fill);
1020 return writer.writeAll(&buf);
1021 } else {
1022 const padded_buf = buf[index - padding ..];
1023 mem.set(u8, padded_buf[0..padding], options.fill);
1024 return writer.writeAll(padded_buf);
1025981 }
982
983 return formatBuf(buf[index..], options, writer);
1026984}
1027985
1028986pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize {
......@@ -1246,6 +1204,10 @@ test "optional" {
12461204 const value: ?i32 = null;
12471205 try testFmt("optional: null\n", "optional: {}\n", .{value});
12481206 }
1207 {
1208 const value = @intToPtr(?*i32, 0xf000d000);
1209 try testFmt("optional: *i32@f000d000\n", "optional: {*}\n", .{value});
1210 }
12491211}
12501212
12511213test "error" {
......@@ -1283,7 +1245,17 @@ test "int.specifier" {
12831245
12841246test "int.padded" {
12851247 try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)});
1286 try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", .{@as(u8, 1)});
1248 try testFmt("u8: '1000'", "u8: '{:0<4}'", .{@as(u8, 1)});
1249 try testFmt("u8: '0001'", "u8: '{:0>4}'", .{@as(u8, 1)});
1250 try testFmt("u8: '0100'", "u8: '{:0^4}'", .{@as(u8, 1)});
1251 try testFmt("i8: '-1 '", "i8: '{:<4}'", .{@as(i8, -1)});
1252 try testFmt("i8: ' -1'", "i8: '{:>4}'", .{@as(i8, -1)});
1253 try testFmt("i8: ' -1 '", "i8: '{:^4}'", .{@as(i8, -1)});
1254 try testFmt("i16: '-1234'", "i16: '{:4}'", .{@as(i16, -1234)});
1255 try testFmt("i16: '+1234'", "i16: '{:4}'", .{@as(i16, 1234)});
1256 try testFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)});
1257 try testFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)});
1258 try testFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)});
12871259}
12881260
12891261test "buffer" {
......@@ -1329,7 +1301,7 @@ test "slice" {
13291301 try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value});
13301302 }
13311303
1332 try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"});
1304 try testFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"});
13331305 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
13341306}
13351307
......@@ -1362,7 +1334,7 @@ test "cstr" {
13621334 .{@ptrCast([*c]const u8, "Test C")},
13631335 );
13641336 try testFmt(
1365 "cstr: Test C \n",
1337 "cstr: Test C\n",
13661338 "cstr: {s:10}\n",
13671339 .{@ptrCast([*c]const u8, "Test C")},
13681340 );
......@@ -1805,7 +1777,7 @@ test "vector" {
18051777
18061778 try testFmt("{ true, false, true, false }", "{}", .{vbool});
18071779 try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64});
1808 try testFmt("{ - 2, - 1, + 0, + 1 }", "{d:5}", .{vi64});
1780 try testFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64});
18091781 try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});
18101782 try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64});
18111783 try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64});
......@@ -1818,15 +1790,16 @@ test "enum-literal" {
18181790
18191791test "padding" {
18201792 try testFmt("Simple", "{}", .{"Simple"});
1821 try testFmt("true ", "{:10}", .{true});
1793 try testFmt(" true", "{:10}", .{true});
18221794 try testFmt(" true", "{:>10}", .{true});
18231795 try testFmt("======true", "{:=>10}", .{true});
18241796 try testFmt("true======", "{:=<10}", .{true});
18251797 try testFmt(" true ", "{:^10}", .{true});
18261798 try testFmt("===true===", "{:=^10}", .{true});
1827 try testFmt("Minimum width", "{:18} width", .{"Minimum"});
1799 try testFmt(" Minimum width", "{:18} width", .{"Minimum"});
18281800 try testFmt("==================Filled", "{:=>24}", .{"Filled"});
18291801 try testFmt(" Centered ", "{:^24}", .{"Centered"});
1802 try testFmt("-", "{:-^1}", .{""});
18301803}
18311804
18321805test "decimal float padding" {
lib/std/fs.zig+61-5
......@@ -21,10 +21,6 @@ pub const wasi = @import("fs/wasi.zig");
2121
2222// TODO audit these APIs with respect to Dir and absolute paths
2323
24pub const rename = os.rename;
25pub const renameZ = os.renameZ;
26pub const renameC = @compileError("deprecated: renamed to renameZ");
27pub const renameW = os.renameW;
2824pub const realpath = os.realpath;
2925pub const realpathZ = os.realpathZ;
3026pub const realpathC = @compileError("deprecated: renamed to realpathZ");
......@@ -90,7 +86,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
9086 base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
9187
9288 if (cwd().symLink(existing_path, tmp_path, .{})) {
93 return rename(tmp_path, new_path);
89 return cwd().rename(tmp_path, new_path);
9490 } else |err| switch (err) {
9591 error.PathAlreadyExists => continue,
9692 else => return err, // TODO zig should know this set does not include PathAlreadyExists
......@@ -255,6 +251,45 @@ pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {
255251 return os.rmdirW(dir_path);
256252}
257253
254pub const renameC = @compileError("deprecated: use renameZ, dir.renameZ, or renameAbsoluteZ");
255
256/// Same as `Dir.rename` except the paths are absolute.
257pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void {
258 assert(path.isAbsolute(old_path));
259 assert(path.isAbsolute(new_path));
260 return os.rename(old_path, new_path);
261}
262
263/// Same as `renameAbsolute` except the path parameters are null-terminated.
264pub fn renameAbsoluteZ(old_path: [*:0]const u8, new_path: [*:0]const u8) !void {
265 assert(path.isAbsoluteZ(old_path));
266 assert(path.isAbsoluteZ(new_path));
267 return os.renameZ(old_path, new_path);
268}
269
270/// Same as `renameAbsolute` except the path parameters are WTF-16 and target OS is assumed Windows.
271pub fn renameAbsoluteW(old_path: [*:0]const u16, new_path: [*:0]const u16) !void {
272 assert(path.isAbsoluteWindowsW(old_path));
273 assert(path.isAbsoluteWindowsW(new_path));
274 return os.renameW(old_path, new_path);
275}
276
277/// Same as `Dir.rename`, except `new_sub_path` is relative to `new_dir`
278pub fn rename(old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) !void {
279 return os.renameat(old_dir.fd, old_sub_path, new_dir.fd, new_sub_path);
280}
281
282/// Same as `rename` except the parameters are null-terminated.
283pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_sub_path_z: [*:0]const u8) !void {
284 return os.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z);
285}
286
287/// Same as `rename` except the parameters are UTF16LE, NT prefixed.
288/// This function is Windows-only.
289pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_path_w: []const u16) !void {
290 return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);
291}
292
258293pub const Dir = struct {
259294 fd: os.fd_t,
260295
......@@ -1338,6 +1373,27 @@ pub const Dir = struct {
13381373 };
13391374 }
13401375
1376 pub const RenameError = os.RenameError;
1377
1378 /// Change the name or location of a file or directory.
1379 /// If new_sub_path already exists, it will be replaced.
1380 /// Renaming a file over an existing directory or a directory
1381 /// over an existing file will fail with `error.IsDir` or `error.NotDir`
1382 pub fn rename(self: Dir, old_sub_path: []const u8, new_sub_path: []const u8) RenameError!void {
1383 return os.renameat(self.fd, old_sub_path, self.fd, new_sub_path);
1384 }
1385
1386 /// Same as `rename` except the parameters are null-terminated.
1387 pub fn renameZ(self: Dir, old_sub_path_z: [*:0]const u8, new_sub_path_z: [*:0]const u8) RenameError!void {
1388 return os.renameatZ(self.fd, old_sub_path_z, self.fd, new_sub_path_z);
1389 }
1390
1391 /// Same as `rename` except the parameters are UTF16LE, NT prefixed.
1392 /// This function is Windows-only.
1393 pub fn renameW(self: Dir, old_sub_path_w: []const u16, new_sub_path_w: []const u16) RenameError!void {
1394 return os.renameatW(self.fd, old_sub_path_w, self.fd, new_sub_path_w);
1395 }
1396
13411397 /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
13421398 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
13431399 /// one; the latter case is known as a dangling link.
lib/std/fs/file.zig+5-3
......@@ -728,7 +728,7 @@ pub const File = struct {
728728 }
729729 var i: usize = 0;
730730 while (i < trailers.len) {
731 while (amt >= headers[i].iov_len) {
731 while (amt >= trailers[i].iov_len) {
732732 amt -= trailers[i].iov_len;
733733 i += 1;
734734 if (i >= trailers.len) return;
......@@ -740,14 +740,16 @@ pub const File = struct {
740740 }
741741
742742 pub const Reader = io.Reader(File, ReadError, read);
743
743744 /// Deprecated: use `Reader`
744745 pub const InStream = Reader;
745746
746 pub fn reader(file: File) io.Reader(File, ReadError, read) {
747 pub fn reader(file: File) Reader {
747748 return .{ .context = file };
748749 }
750
749751 /// Deprecated: use `reader`
750 pub fn inStream(file: File) io.InStream(File, ReadError, read) {
752 pub fn inStream(file: File) Reader {
751753 return .{ .context = file };
752754 }
753755
lib/std/fs/test.zig+161
......@@ -274,6 +274,167 @@ test "file operations on directories" {
274274 dir.close();
275275}
276276
277test "Dir.rename files" {
278 var tmp_dir = tmpDir(.{});
279 defer tmp_dir.cleanup();
280
281 testing.expectError(error.FileNotFound, tmp_dir.dir.rename("missing_file_name", "something_else"));
282
283 // Renaming files
284 const test_file_name = "test_file";
285 const renamed_test_file_name = "test_file_renamed";
286 var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
287 file.close();
288 try tmp_dir.dir.rename(test_file_name, renamed_test_file_name);
289
290 // Ensure the file was renamed
291 testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(test_file_name, .{}));
292 file = try tmp_dir.dir.openFile(renamed_test_file_name, .{});
293 file.close();
294
295 // Rename to self succeeds
296 try tmp_dir.dir.rename(renamed_test_file_name, renamed_test_file_name);
297
298 // Rename to existing file succeeds
299 var existing_file = try tmp_dir.dir.createFile("existing_file", .{ .read = true });
300 existing_file.close();
301 try tmp_dir.dir.rename(renamed_test_file_name, "existing_file");
302
303 testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(renamed_test_file_name, .{}));
304 file = try tmp_dir.dir.openFile("existing_file", .{});
305 file.close();
306}
307
308test "Dir.rename directories" {
309 // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
310 if (builtin.os.tag == .windows) return error.SkipZigTest;
311
312 var tmp_dir = tmpDir(.{});
313 defer tmp_dir.cleanup();
314
315 // Renaming directories
316 try tmp_dir.dir.makeDir("test_dir");
317 try tmp_dir.dir.rename("test_dir", "test_dir_renamed");
318
319 // Ensure the directory was renamed
320 testing.expectError(error.FileNotFound, tmp_dir.dir.openDir("test_dir", .{}));
321 var dir = try tmp_dir.dir.openDir("test_dir_renamed", .{});
322
323 // Put a file in the directory
324 var file = try dir.createFile("test_file", .{ .read = true });
325 file.close();
326 dir.close();
327
328 try tmp_dir.dir.rename("test_dir_renamed", "test_dir_renamed_again");
329
330 // Ensure the directory was renamed and the file still exists in it
331 testing.expectError(error.FileNotFound, tmp_dir.dir.openDir("test_dir_renamed", .{}));
332 dir = try tmp_dir.dir.openDir("test_dir_renamed_again", .{});
333 file = try dir.openFile("test_file", .{});
334 file.close();
335 dir.close();
336
337 // Try to rename to a non-empty directory now
338 var target_dir = try tmp_dir.dir.makeOpenPath("non_empty_target_dir", .{});
339 file = try target_dir.createFile("filler", .{ .read = true });
340 file.close();
341
342 testing.expectError(error.PathAlreadyExists, tmp_dir.dir.rename("test_dir_renamed_again", "non_empty_target_dir"));
343
344 // Ensure the directory was not renamed
345 dir = try tmp_dir.dir.openDir("test_dir_renamed_again", .{});
346 file = try dir.openFile("test_file", .{});
347 file.close();
348 dir.close();
349}
350
351test "Dir.rename file <-> dir" {
352 // TODO: Fix on Windows, see https://github.com/ziglang/zig/issues/6364
353 if (builtin.os.tag == .windows) return error.SkipZigTest;
354
355 var tmp_dir = tmpDir(.{});
356 defer tmp_dir.cleanup();
357
358 var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
359 file.close();
360 try tmp_dir.dir.makeDir("test_dir");
361 testing.expectError(error.IsDir, tmp_dir.dir.rename("test_file", "test_dir"));
362 testing.expectError(error.NotDir, tmp_dir.dir.rename("test_dir", "test_file"));
363}
364
365test "rename" {
366 var tmp_dir1 = tmpDir(.{});
367 defer tmp_dir1.cleanup();
368
369 var tmp_dir2 = tmpDir(.{});
370 defer tmp_dir2.cleanup();
371
372 // Renaming files
373 const test_file_name = "test_file";
374 const renamed_test_file_name = "test_file_renamed";
375 var file = try tmp_dir1.dir.createFile(test_file_name, .{ .read = true });
376 file.close();
377 try fs.rename(tmp_dir1.dir, test_file_name, tmp_dir2.dir, renamed_test_file_name);
378
379 // ensure the file was renamed
380 testing.expectError(error.FileNotFound, tmp_dir1.dir.openFile(test_file_name, .{}));
381 file = try tmp_dir2.dir.openFile(renamed_test_file_name, .{});
382 file.close();
383}
384
385test "renameAbsolute" {
386 if (builtin.os.tag == .wasi) return error.SkipZigTest;
387
388 var tmp_dir = tmpDir(.{});
389 defer tmp_dir.cleanup();
390
391 // Get base abs path
392 var arena = ArenaAllocator.init(testing.allocator);
393 defer arena.deinit();
394 const allocator = &arena.allocator;
395
396 const base_path = blk: {
397 const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..] });
398 break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
399 };
400
401 testing.expectError(error.FileNotFound, fs.renameAbsolute(
402 try fs.path.join(allocator, &[_][]const u8{ base_path, "missing_file_name" }),
403 try fs.path.join(allocator, &[_][]const u8{ base_path, "something_else" }),
404 ));
405
406 // Renaming files
407 const test_file_name = "test_file";
408 const renamed_test_file_name = "test_file_renamed";
409 var file = try tmp_dir.dir.createFile(test_file_name, .{ .read = true });
410 file.close();
411 try fs.renameAbsolute(
412 try fs.path.join(allocator, &[_][]const u8{ base_path, test_file_name }),
413 try fs.path.join(allocator, &[_][]const u8{ base_path, renamed_test_file_name }),
414 );
415
416 // ensure the file was renamed
417 testing.expectError(error.FileNotFound, tmp_dir.dir.openFile(test_file_name, .{}));
418 file = try tmp_dir.dir.openFile(renamed_test_file_name, .{});
419 const stat = try file.stat();
420 testing.expect(stat.kind == .File);
421 file.close();
422
423 // Renaming directories
424 const test_dir_name = "test_dir";
425 const renamed_test_dir_name = "test_dir_renamed";
426 try tmp_dir.dir.makeDir(test_dir_name);
427 try fs.renameAbsolute(
428 try fs.path.join(allocator, &[_][]const u8{ base_path, test_dir_name }),
429 try fs.path.join(allocator, &[_][]const u8{ base_path, renamed_test_dir_name }),
430 );
431
432 // ensure the directory was renamed
433 testing.expectError(error.FileNotFound, tmp_dir.dir.openDir(test_dir_name, .{}));
434 var dir = try tmp_dir.dir.openDir(renamed_test_dir_name, .{});
435 dir.close();
436}
437
277438test "openSelfExe" {
278439 if (builtin.os.tag == .wasi) return error.SkipZigTest;
279440
lib/std/hash/auto_hash.zig+2-3
......@@ -139,9 +139,8 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
139139 const tag = meta.activeTag(key);
140140 const s = hash(hasher, tag, strat);
141141 inline for (info.fields) |field| {
142 const enum_field = field.enum_field.?;
143 if (enum_field.value == @enumToInt(tag)) {
144 hash(hasher, @field(key, enum_field.name), strat);
142 if (@field(tag_type, field.name) == tag) {
143 hash(hasher, @field(key, field.name), strat);
145144 // TODO use a labelled break when it does not crash the compiler. cf #2908
146145 // break :blk;
147146 return;
lib/std/hash/crc.zig+1-4
......@@ -71,10 +71,7 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {
7171 const p = input[i .. i + 8];
7272
7373 // Unrolling this way gives ~50Mb/s increase
74 self.crc ^= (@as(u32, p[0]) << 0);
75 self.crc ^= (@as(u32, p[1]) << 8);
76 self.crc ^= (@as(u32, p[2]) << 16);
77 self.crc ^= (@as(u32, p[3]) << 24);
74 self.crc ^= std.mem.readIntLittle(u32, p[0..4]);
7875
7976 self.crc =
8077 lookup_tables[0][p[7]] ^
lib/std/hash_map.zig+1-1
......@@ -113,7 +113,7 @@ pub fn HashMap(
113113 return self.unmanaged.clearAndFree(self.allocator);
114114 }
115115
116 pub fn count(self: Self) usize {
116 pub fn count(self: Self) Size {
117117 return self.unmanaged.count();
118118 }
119119
lib/std/heap.zig+1-1
......@@ -489,7 +489,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {
489489 const full_len = os.windows.kernel32.HeapSize(heap_handle, 0, ptr);
490490 assert(full_len != std.math.maxInt(usize));
491491 assert(full_len >= amt);
492 break :init mem.alignBackwardAnyAlign(full_len - (aligned_addr - root_addr), len_align);
492 break :init mem.alignBackwardAnyAlign(full_len - (aligned_addr - root_addr) - @sizeOf(usize), len_align);
493493 };
494494 const buf = @intToPtr([*]u8, aligned_addr)[0..return_len];
495495 getRecordPtr(buf).* = root_addr;
lib/std/heap/arena_allocator.zig+23-1
......@@ -26,7 +26,7 @@ pub const ArenaAllocator = struct {
2626 return .{
2727 .allocator = Allocator{
2828 .allocFn = alloc,
29 .resizeFn = Allocator.noResize,
29 .resizeFn = resize,
3030 },
3131 .child_allocator = child_allocator,
3232 .state = self,
......@@ -84,4 +84,26 @@ pub const ArenaAllocator = struct {
8484 return result;
8585 }
8686 }
87
88 fn resize(allocator: *Allocator, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) Allocator.Error!usize {
89 const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
90
91 const cur_node = self.state.buffer_list.first orelse return error.OutOfMemory;
92 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
93 if (@ptrToInt(cur_buf.ptr) + self.state.end_index != @ptrToInt(buf.ptr) + buf.len) {
94 if (new_len > buf.len)
95 return error.OutOfMemory;
96 return new_len;
97 }
98
99 if (buf.len >= new_len) {
100 self.state.end_index -= buf.len - new_len;
101 return new_len;
102 } else if (cur_buf.len - self.state.end_index >= new_len - buf.len) {
103 self.state.end_index += new_len - buf.len;
104 return new_len;
105 } else {
106 return error.OutOfMemory;
107 }
108 }
87109};
lib/std/io/serialization.zig+2-2
......@@ -156,7 +156,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
156156 const tag = try self.deserializeInt(TagInt);
157157
158158 inline for (info.fields) |field_info| {
159 if (field_info.enum_field.?.value == tag) {
159 if (@enumToInt(@field(TagType, field_info.name)) == tag) {
160160 const name = field_info.name;
161161 const FieldType = field_info.field_type;
162162 ptr.* = @unionInit(C, name, undefined);
......@@ -320,7 +320,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
320320 // value, but @field requires a comptime value. Our alternative
321321 // is to check each field for a match
322322 inline for (info.fields) |field_info| {
323 if (field_info.enum_field.?.value == @enumToInt(active_tag)) {
323 if (@field(TagType, field_info.name) == active_tag) {
324324 const name = field_info.name;
325325 const FieldType = field_info.field_type;
326326 try self.serialize(@field(value, name));
lib/std/json.zig+2-2
......@@ -1613,7 +1613,7 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
16131613 .Union => |unionInfo| {
16141614 if (unionInfo.tag_type) |UnionTagType| {
16151615 inline for (unionInfo.fields) |u_field| {
1616 if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {
1616 if (value == @field(UnionTagType, u_field.name)) {
16171617 parseFree(u_field.field_type, @field(value, u_field.name), options);
16181618 break;
16191619 }
......@@ -2458,7 +2458,7 @@ pub fn stringify(
24582458 const info = @typeInfo(T).Union;
24592459 if (info.tag_type) |UnionTagType| {
24602460 inline for (info.fields) |u_field| {
2461 if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {
2461 if (value == @field(UnionTagType, u_field.name)) {
24622462 return try stringify(@field(value, u_field.name), options, out_stream);
24632463 }
24642464 }
lib/std/macho.zig+110-7
......@@ -647,6 +647,32 @@ pub const nlist_64 = extern struct {
647647 n_value: u64,
648648};
649649
650/// Format of a relocation entry of a Mach-O file. Modified from the 4.3BSD
651/// format. The modifications from the original format were changing the value
652/// of the r_symbolnum field for "local" (r_extern == 0) relocation entries.
653/// This modification is required to support symbols in an arbitrary number of
654/// sections not just the three sections (text, data and bss) in a 4.3BSD file.
655/// Also the last 4 bits have had the r_type tag added to them.
656pub const relocation_info = packed struct {
657 /// offset in the section to what is being relocated
658 r_address: i32,
659
660 /// symbol index if r_extern == 1 or section ordinal if r_extern == 0
661 r_symbolnum: u24,
662
663 /// was relocated pc relative already
664 r_pcrel: u1,
665
666 /// 0=byte, 1=word, 2=long, 3=quad
667 r_length: u2,
668
669 /// does not include value of sym referenced
670 r_extern: u1,
671
672 /// if not 0, machine specific relocation type
673 r_type: u4,
674};
675
650676/// After MacOS X 10.1 when a new load command is added that is required to be
651677/// understood by the dynamic linker for the image to execute properly the
652678/// LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic
......@@ -1086,13 +1112,58 @@ pub const N_ECOML = 0xe8;
10861112/// second stab entry with length information
10871113pub const N_LENG = 0xfe;
10881114
1089/// If a segment contains any sections marked with S_ATTR_DEBUG then all
1090/// sections in that segment must have this attribute. No section other than
1091/// a section marked with this attribute may reference the contents of this
1092/// section. A section with this attribute may contain no symbols and must have
1093/// a section type S_REGULAR. The static linker will not copy section contents
1094/// from sections with this attribute into its output file. These sections
1095/// generally contain DWARF debugging info.
1115// For the two types of symbol pointers sections and the symbol stubs section
1116// they have indirect symbol table entries. For each of the entries in the
1117// section the indirect symbol table entries, in corresponding order in the
1118// indirect symbol table, start at the index stored in the reserved1 field
1119// of the section structure. Since the indirect symbol table entries
1120// correspond to the entries in the section the number of indirect symbol table
1121// entries is inferred from the size of the section divided by the size of the
1122// entries in the section. For symbol pointers sections the size of the entries
1123// in the section is 4 bytes and for symbol stubs sections the byte size of the
1124// stubs is stored in the reserved2 field of the section structure.
1125
1126/// section with only non-lazy symbol pointers
1127pub const S_NON_LAZY_SYMBOL_POINTERS = 0x6;
1128
1129/// section with only lazy symbol pointers
1130pub const S_LAZY_SYMBOL_POINTERS = 0x7;
1131
1132/// section with only symbol stubs, byte size of stub in the reserved2 field
1133pub const S_SYMBOL_STUBS = 0x8;
1134
1135/// section with only function pointers for initialization
1136pub const S_MOD_INIT_FUNC_POINTERS = 0x9;
1137
1138/// section with only function pointers for termination
1139pub const S_MOD_TERM_FUNC_POINTERS = 0xa;
1140
1141/// section contains symbols that are to be coalesced
1142pub const S_COALESCED = 0xb;
1143
1144/// zero fill on demand section (that can be larger than 4 gigabytes)
1145pub const S_GB_ZEROFILL = 0xc;
1146
1147/// section with only pairs of function pointers for interposing
1148pub const S_INTERPOSING = 0xd;
1149
1150/// section with only 16 byte literals
1151pub const S_16BYTE_LITERALS = 0xe;
1152
1153/// section contains DTrace Object Format
1154pub const S_DTRACE_DOF = 0xf;
1155
1156/// section with only lazy symbol pointers to lazy loaded dylibs
1157pub const S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10;
1158
1159// If a segment contains any sections marked with S_ATTR_DEBUG then all
1160// sections in that segment must have this attribute. No section other than
1161// a section marked with this attribute may reference the contents of this
1162// section. A section with this attribute may contain no symbols and must have
1163// a section type S_REGULAR. The static linker will not copy section contents
1164// from sections with this attribute into its output file. These sections
1165// generally contain DWARF debugging info.
1166
10961167/// a debug section
10971168pub const S_ATTR_DEBUG = 0x02000000;
10981169
......@@ -1154,3 +1225,35 @@ pub const VM_PROT_WRITE: vm_prot_t = 0x2;
11541225
11551226/// VM execute permission
11561227pub const VM_PROT_EXECUTE: vm_prot_t = 0x4;
1228
1229pub const reloc_type_x86_64 = packed enum(u4) {
1230 /// for absolute addresses
1231 X86_64_RELOC_UNSIGNED = 0,
1232
1233 /// for signed 32-bit displacement
1234 X86_64_RELOC_SIGNED,
1235
1236 /// a CALL/JMP instruction with 32-bit displacement
1237 X86_64_RELOC_BRANCH,
1238
1239 /// a MOVQ load of a GOT entry
1240 X86_64_RELOC_GOT_LOAD,
1241
1242 /// other GOT references
1243 X86_64_RELOC_GOT,
1244
1245 /// must be followed by a X86_64_RELOC_UNSIGNED
1246 X86_64_RELOC_SUBTRACTOR,
1247
1248 /// for signed 32-bit displacement with a -1 addend
1249 X86_64_RELOC_SIGNED_1,
1250
1251 /// for signed 32-bit displacement with a -2 addend
1252 X86_64_RELOC_SIGNED_2,
1253
1254 /// for signed 32-bit displacement with a -4 addend
1255 X86_64_RELOC_SIGNED_4,
1256
1257 /// for thread local variables
1258 X86_64_RELOC_TLV,
1259};
lib/std/meta.zig+62-17
......@@ -465,10 +465,13 @@ pub fn TagPayloadType(comptime U: type, tag: @TagType(U)) type {
465465 testing.expect(trait.is(.Union)(U));
466466
467467 const info = @typeInfo(U).Union;
468 const tag_info = @typeInfo(@TagType(U)).Enum;
468469
469470 inline for (info.fields) |field_info| {
470 if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type;
471 if (comptime mem.eql(u8, field_info.name, @tagName(tag)))
472 return field_info.field_type;
471473 }
474
472475 unreachable;
473476}
474477
......@@ -504,15 +507,14 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool {
504507 }
505508 },
506509 .Union => |info| {
507 if (info.tag_type) |_| {
510 if (info.tag_type) |Tag| {
508511 const tag_a = activeTag(a);
509512 const tag_b = activeTag(b);
510513 if (tag_a != tag_b) return false;
511514
512515 inline for (info.fields) |field_info| {
513 const enum_field = field_info.enum_field.?;
514 if (enum_field.value == @enumToInt(tag_a)) {
515 return eql(@field(a, enum_field.name), @field(b, enum_field.name));
516 if (@field(Tag, field_info.name) == tag_a) {
517 return eql(@field(a, field_info.name), @field(b, field_info.name));
516518 }
517519 }
518520 return false;
......@@ -715,7 +717,7 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
715717 },
716718 .Optional => |opt| {
717719 if (@typeInfo(opt.child) == .Pointer) {
718 return @ptrCast(DestType, @alignCast(dest_ptr, target));
720 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
719721 }
720722 },
721723 else => {},
......@@ -723,23 +725,24 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
723725 },
724726 .Optional => |dest_opt| {
725727 if (@typeInfo(dest_opt.child) == .Pointer) {
728 const dest_ptr = @typeInfo(dest_opt.child).Pointer;
726729 switch (@typeInfo(TargetType)) {
727730 .Int, .ComptimeInt => {
728731 return @intToPtr(DestType, target);
729732 },
730733 .Pointer => {
731 return @ptrCast(DestType, @alignCast(@alignOf(dest_opt.child.Child), target));
734 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
732735 },
733736 .Optional => |target_opt| {
734737 if (@typeInfo(target_opt.child) == .Pointer) {
735 return @ptrCast(DestType, @alignCast(@alignOf(dest_opt.child.Child), target));
738 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
736739 }
737740 },
738741 else => {},
739742 }
740743 }
741744 },
742 .Enum, .EnumLiteral => {
745 .Enum => {
743746 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
744747 return @intToEnum(DestType, target);
745748 }
......@@ -747,15 +750,18 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
747750 .Int, .ComptimeInt => {
748751 switch (@typeInfo(TargetType)) {
749752 .Pointer => {
750 return @as(DestType, @ptrToInt(target));
753 return @intCast(DestType, @ptrToInt(target));
751754 },
752755 .Optional => |opt| {
753756 if (@typeInfo(opt.child) == .Pointer) {
754 return @as(DestType, @ptrToInt(target));
757 return @intCast(DestType, @ptrToInt(target));
755758 }
756759 },
757 .Enum, .EnumLiteral => {
758 return @as(DestType, @enumToInt(target));
760 .Enum => {
761 return @intCast(DestType, @enumToInt(target));
762 },
763 .Int, .ComptimeInt => {
764 return @intCast(DestType, target);
759765 },
760766 else => {},
761767 }
......@@ -774,10 +780,49 @@ test "std.meta.cast" {
774780
775781 var i = @as(i64, 10);
776782
777 testing.expect(cast(?*c_void, 0) == @intToPtr(?*c_void, 0));
778783 testing.expect(cast(*u8, 16) == @intToPtr(*u8, 16));
779 testing.expect(cast(u64, @as(u32, 10)) == @as(u64, 10));
780 testing.expect(cast(E, 1) == .One);
781 testing.expect(cast(u8, E.Two) == 2);
782784 testing.expect(cast(*u64, &i).* == @as(u64, 10));
785 testing.expect(cast(*i64, @as(?*align(1) i64, &i)) == &i);
786
787 testing.expect(cast(?*u8, 2) == @intToPtr(*u8, 2));
788 testing.expect(cast(?*i64, @as(*align(1) i64, &i)) == &i);
789 testing.expect(cast(?*i64, @as(?*align(1) i64, &i)) == &i);
790
791 testing.expect(cast(E, 1) == .One);
792
793 testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(*u32, 4)));
794 testing.expectEqual(@as(u32, 4), cast(u32, @intToPtr(?*u32, 4)));
795 testing.expectEqual(@as(u32, 10), cast(u32, @as(u64, 10)));
796 testing.expectEqual(@as(u8, 2), cast(u8, E.Two));
797}
798
799/// Given a value returns its size as C's sizeof operator would.
800/// This is for translate-c and is not intended for general use.
801pub fn sizeof(target: anytype) usize {
802 switch (@typeInfo(@TypeOf(target))) {
803 .Type => return @sizeOf(target),
804 .Float, .Int, .Struct, .Union, .Enum => return @sizeOf(@TypeOf(target)),
805 .ComptimeFloat => return @sizeOf(f64), // TODO c_double #3999
806 .ComptimeInt => {
807 // TODO to get the correct result we have to translate
808 // `1073741824 * 4` as `int(1073741824) *% int(4)` since
809 // sizeof(1073741824 * 4) != sizeof(4294967296).
810
811 // TODO test if target fits in int, long or long long
812 return @sizeOf(c_int);
813 },
814 else => @compileError("TODO implement std.meta.sizeof for type " ++ @typeName(@TypeOf(target))),
815 }
816}
817
818test "sizeof" {
819 const E = extern enum(c_int) { One, _ };
820 const S = extern struct { a: u32 };
821
822 testing.expect(sizeof(u32) == 4);
823 testing.expect(sizeof(@as(u32, 2)) == 4);
824 testing.expect(sizeof(2) == @sizeOf(c_int));
825 testing.expect(sizeof(E) == @sizeOf(c_int));
826 testing.expect(sizeof(E.One) == @sizeOf(c_int));
827 testing.expect(sizeof(S) == 4);
783828}
lib/std/os.zig+52-4
......@@ -320,6 +320,7 @@ pub const ReadError = error{
320320/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
321321/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
322322/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
323/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
323324/// For POSIX the limit is `math.maxInt(isize)`.
324325pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
325326 if (builtin.os.tag == .windows) {
......@@ -353,6 +354,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
353354 // Prevents EINVAL.
354355 const max_count = switch (std.Target.current.os.tag) {
355356 .linux => 0x7ffff000,
357 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
356358 else => math.maxInt(isize),
357359 };
358360 const adjusted_len = math.min(max_count, buf.len);
......@@ -693,6 +695,7 @@ pub const WriteError = error{
693695/// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000`
694696/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
695697/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
698/// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
696699/// The corresponding POSIX limit is `math.maxInt(isize)`.
697700pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
698701 if (builtin.os.tag == .windows) {
......@@ -726,6 +729,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
726729
727730 const max_count = switch (std.Target.current.os.tag) {
728731 .linux => 0x7ffff000,
732 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
729733 else => math.maxInt(isize),
730734 };
731735 const adjusted_len = math.min(max_count, bytes.len);
......@@ -851,6 +855,7 @@ pub const PWriteError = WriteError || error{Unseekable};
851855/// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000`
852856/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
853857/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
858/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
854859/// The corresponding POSIX limit is `math.maxInt(isize)`.
855860pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
856861 if (std.Target.current.os.tag == .windows) {
......@@ -888,6 +893,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
888893 // Prevent EINVAL.
889894 const max_count = switch (std.Target.current.os.tag) {
890895 .linux => 0x7ffff000,
896 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
891897 else => math.maxInt(isize),
892898 };
893899 const adjusted_len = math.min(max_count, bytes.len);
......@@ -1884,7 +1890,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError
18841890 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
18851891}
18861892
1887const RenameError = error{
1893pub const RenameError = error{
18881894 /// In WASI, this error may occur when the file descriptor does
18891895 /// not hold the required rights to rename a resource by path relative to it.
18901896 AccessDenied,
......@@ -2101,6 +2107,7 @@ pub fn renameatW(
21012107 .ACCESS_DENIED => return error.AccessDenied,
21022108 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
21032109 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
2110 .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
21042111 else => return windows.unexpectedStatus(rc),
21052112 }
21062113}
......@@ -2515,9 +2522,9 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read
25152522pub const SetEidError = error{
25162523 InvalidUserId,
25172524 PermissionDenied,
2518};
2525} || UnexpectedError;
25192526
2520pub const SetIdError = error{ResourceLimitReached} || SetEidError || UnexpectedError;
2527pub const SetIdError = error{ResourceLimitReached} || SetEidError;
25212528
25222529pub fn setuid(uid: uid_t) SetIdError!void {
25232530 switch (errno(system.setuid(uid))) {
......@@ -3084,7 +3091,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
30843091 .WSAECONNREFUSED => return error.ConnectionRefused,
30853092 .WSAETIMEDOUT => return error.ConnectionTimedOut,
30863093 .WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well?
3087 , .WSAENETUNREACH => return error.NetworkUnreachable,
3094 , .WSAENETUNREACH => return error.NetworkUnreachable,
30883095 .WSAEFAULT => unreachable,
30893096 .WSAEINVAL => unreachable,
30903097 .WSAEISCONN => unreachable,
......@@ -4711,6 +4718,7 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize {
47114718/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
47124719/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
47134720/// well as stuffing the errno codes into the last `4096` values. This is cited on the `sendfile` man page.
4721/// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
47144722/// The corresponding POSIX limit on this is `math.maxInt(isize)`.
47154723pub fn sendfile(
47164724 out_fd: fd_t,
......@@ -4733,6 +4741,7 @@ pub fn sendfile(
47334741 });
47344742 const max_count = switch (std.Target.current.os.tag) {
47354743 .linux => 0x7ffff000,
4744 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
47364745 else => math.maxInt(size_t),
47374746 };
47384747
......@@ -5418,3 +5427,42 @@ pub fn fdatasync(fd: fd_t) SyncError!void {
54185427 else => |err| return std.os.unexpectedErrno(err),
54195428 }
54205429}
5430
5431pub const PrctlError = error{
5432 /// Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or
5433 /// PR_SET_MM/PR_SET_MM_EXE_FILE
5434 AccessDenied,
5435 /// Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE
5436 InvalidFileDescriptor,
5437 InvalidAddress,
5438 /// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT,
5439 /// or PR_MPX_DISABLE_MANAGEMENT
5440 UnsupportedFeature,
5441 /// Can only occur wih PR_SET_FP_MODE
5442 OperationNotSupported,
5443 PermissionDenied,
5444} || UnexpectedError;
5445
5446pub fn prctl(option: i32, args: anytype) PrctlError!u31 {
5447 if (@typeInfo(@TypeOf(args)) != .Struct)
5448 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
5449 if (args.len > 4)
5450 @compileError("prctl takes a maximum of 4 optional arguments");
5451
5452 var buf: [4]usize = undefined;
5453 inline for (args) |arg, i| buf[i] = arg;
5454
5455 const rc = system.prctl(option, buf[0], buf[1], buf[2], buf[3]);
5456 switch (errno(rc)) {
5457 0 => return @intCast(u31, rc),
5458 EACCES => return error.AccessDenied,
5459 EBADF => return error.InvalidFileDescriptor,
5460 EFAULT => return error.InvalidAddress,
5461 EINVAL => unreachable,
5462 ENODEV, ENXIO => return error.UnsupportedFeature,
5463 EOPNOTSUPP => return error.OperationNotSupported,
5464 EPERM, EBUSY => return error.PermissionDenied,
5465 ERANGE => unreachable,
5466 else => |err| return std.os.unexpectedErrno(err),
5467 }
5468}
lib/std/os/bits/linux.zig+120
......@@ -20,10 +20,13 @@ pub usingnamespace switch (builtin.arch) {
2020 .arm => @import("linux/arm-eabi.zig"),
2121 .riscv64 => @import("linux/riscv64.zig"),
2222 .mips, .mipsel => @import("linux/mips.zig"),
23 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),
2324 else => struct {},
2425};
2526
2627pub usingnamespace @import("linux/netlink.zig");
28pub usingnamespace @import("linux/prctl.zig");
29pub usingnamespace @import("linux/securebits.zig");
2730
2831const is_mips = builtin.arch.isMIPS();
2932
......@@ -1590,6 +1593,123 @@ pub const RR_A = 1;
15901593pub const RR_CNAME = 5;
15911594pub const RR_AAAA = 28;
15921595
1596/// Turn off Nagle's algorithm
1597pub const TCP_NODELAY = 1;
1598/// Limit MSS
1599pub const TCP_MAXSEG = 2;
1600/// Never send partially complete segments.
1601pub const TCP_CORK = 3;
1602/// Start keeplives after this period, in seconds
1603pub const TCP_KEEPIDLE = 4;
1604/// Interval between keepalives
1605pub const TCP_KEEPINTVL = 5;
1606/// Number of keepalives before death
1607pub const TCP_KEEPCNT = 6;
1608/// Number of SYN retransmits
1609pub const TCP_SYNCNT = 7;
1610/// Life time of orphaned FIN-WAIT-2 state
1611pub const TCP_LINGER2 = 8;
1612/// Wake up listener only when data arrive
1613pub const TCP_DEFER_ACCEPT = 9;
1614/// Bound advertised window
1615pub const TCP_WINDOW_CLAMP = 10;
1616/// Information about this connection.
1617pub const TCP_INFO = 11;
1618/// Block/reenable quick acks
1619pub const TCP_QUICKACK = 12;
1620/// Congestion control algorithm
1621pub const TCP_CONGESTION = 13;
1622/// TCP MD5 Signature (RFC2385)
1623pub const TCP_MD5SIG = 14;
1624/// Use linear timeouts for thin streams
1625pub const TCP_THIN_LINEAR_TIMEOUTS = 16;
1626/// Fast retrans. after 1 dupack
1627pub const TCP_THIN_DUPACK = 17;
1628/// How long for loss retry before timeout
1629pub const TCP_USER_TIMEOUT = 18;
1630/// TCP sock is under repair right now
1631pub const TCP_REPAIR = 19;
1632pub const TCP_REPAIR_QUEUE = 20;
1633pub const TCP_QUEUE_SEQ = 21;
1634pub const TCP_REPAIR_OPTIONS = 22;
1635/// Enable FastOpen on listeners
1636pub const TCP_FASTOPEN = 23;
1637pub const TCP_TIMESTAMP = 24;
1638/// limit number of unsent bytes in write queue
1639pub const TCP_NOTSENT_LOWAT = 25;
1640/// Get Congestion Control (optional) info
1641pub const TCP_CC_INFO = 26;
1642/// Record SYN headers for new connections
1643pub const TCP_SAVE_SYN = 27;
1644/// Get SYN headers recorded for connection
1645pub const TCP_SAVED_SYN = 28;
1646/// Get/set window parameters
1647pub const TCP_REPAIR_WINDOW = 29;
1648/// Attempt FastOpen with connect
1649pub const TCP_FASTOPEN_CONNECT = 30;
1650/// Attach a ULP to a TCP connection
1651pub const TCP_ULP = 31;
1652/// TCP MD5 Signature with extensions
1653pub const TCP_MD5SIG_EXT = 32;
1654/// Set the key for Fast Open (cookie)
1655pub const TCP_FASTOPEN_KEY = 33;
1656/// Enable TFO without a TFO cookie
1657pub const TCP_FASTOPEN_NO_COOKIE = 34;
1658pub const TCP_ZEROCOPY_RECEIVE = 35;
1659/// Notify bytes available to read as a cmsg on read
1660pub const TCP_INQ = 36;
1661pub const TCP_CM_INQ = TCP_INQ;
1662/// delay outgoing packets by XX usec
1663pub const TCP_TX_DELAY = 37;
1664
1665pub const TCP_REPAIR_ON = 1;
1666pub const TCP_REPAIR_OFF = 0;
1667/// Turn off without window probes
1668pub const TCP_REPAIR_OFF_NO_WP = -1;
1669
1670pub const tcp_repair_opt = extern struct {
1671 opt_code: u32,
1672 opt_val: u32,
1673};
1674
1675pub const tcp_repair_window = extern struct {
1676 snd_wl1: u32,
1677 snd_wnd: u32,
1678 max_window: u32,
1679 rcv_wnd: u32,
1680 rcv_wup: u32,
1681};
1682
1683pub const TcpRepairOption = extern enum {
1684 TCP_NO_QUEUE,
1685 TCP_RECV_QUEUE,
1686 TCP_SEND_QUEUE,
1687 TCP_QUEUES_NR,
1688};
1689
1690/// why fastopen failed from client perspective
1691pub const tcp_fastopen_client_fail = extern enum {
1692 /// catch-all
1693 TFO_STATUS_UNSPEC,
1694 /// if not in TFO_CLIENT_NO_COOKIE mode
1695 TFO_COOKIE_UNAVAILABLE,
1696 /// SYN-ACK did not ack SYN data
1697 TFO_DATA_NOT_ACKED,
1698 /// SYN-ACK did not ack SYN data after timeout
1699 TFO_SYN_RETRANSMITTED,
1700};
1701
1702/// for TCP_INFO socket option
1703pub const TCPI_OPT_TIMESTAMPS = 1;
1704pub const TCPI_OPT_SACK = 2;
1705pub const TCPI_OPT_WSCALE = 4;
1706/// ECN was negociated at TCP session init
1707pub const TCPI_OPT_ECN = 8;
1708/// we received at least one packet with ECT
1709pub const TCPI_OPT_ECN_SEEN = 16;
1710/// SYN-ACK acked data in SYN sent or rcvd
1711pub const TCPI_OPT_SYN_DATA = 32;
1712
15931713pub const nfds_t = usize;
15941714pub const pollfd = extern struct {
15951715 fd: fd_t,
lib/std/os/bits/linux/powerpc64.zig created+602
......@@ -0,0 +1,602 @@
1const std = @import("../../../std.zig");
2const linux = std.os.linux;
3const socklen_t = linux.socklen_t;
4const iovec = linux.iovec;
5const iovec_const = linux.iovec_const;
6const uid_t = linux.uid_t;
7const gid_t = linux.gid_t;
8const pid_t = linux.pid_t;
9const stack_t = linux.stack_t;
10const sigset_t = linux.sigset_t;
11pub const SYS = extern enum(usize) {
12 restart_syscall = 0,
13 exit = 1,
14 fork = 2,
15 read = 3,
16 write = 4,
17 open = 5,
18 close = 6,
19 waitpid = 7,
20 creat = 8,
21 link = 9,
22 unlink = 10,
23 execve = 11,
24 chdir = 12,
25 time = 13,
26 mknod = 14,
27 chmod = 15,
28 lchown = 16,
29 @"break" = 17,
30 oldstat = 18,
31 lseek = 19,
32 getpid = 20,
33 mount = 21,
34 umount = 22,
35 setuid = 23,
36 getuid = 24,
37 stime = 25,
38 ptrace = 26,
39 alarm = 27,
40 oldfstat = 28,
41 pause = 29,
42 utime = 30,
43 stty = 31,
44 gtty = 32,
45 access = 33,
46 nice = 34,
47 ftime = 35,
48 sync = 36,
49 kill = 37,
50 rename = 38,
51 mkdir = 39,
52 rmdir = 40,
53 dup = 41,
54 pipe = 42,
55 times = 43,
56 prof = 44,
57 brk = 45,
58 setgid = 46,
59 getgid = 47,
60 signal = 48,
61 geteuid = 49,
62 getegid = 50,
63 acct = 51,
64 umount2 = 52,
65 lock = 53,
66 ioctl = 54,
67 fcntl = 55,
68 mpx = 56,
69 setpgid = 57,
70 ulimit = 58,
71 oldolduname = 59,
72 umask = 60,
73 chroot = 61,
74 ustat = 62,
75 dup2 = 63,
76 getppid = 64,
77 getpgrp = 65,
78 setsid = 66,
79 sigaction = 67,
80 sgetmask = 68,
81 ssetmask = 69,
82 setreuid = 70,
83 setregid = 71,
84 sigsuspend = 72,
85 sigpending = 73,
86 sethostname = 74,
87 setrlimit = 75,
88 getrlimit = 76,
89 getrusage = 77,
90 gettimeofday = 78,
91 settimeofday = 79,
92 getgroups = 80,
93 setgroups = 81,
94 select = 82,
95 symlink = 83,
96 oldlstat = 84,
97 readlink = 85,
98 uselib = 86,
99 swapon = 87,
100 reboot = 88,
101 readdir = 89,
102 mmap = 90,
103 munmap = 91,
104 truncate = 92,
105 ftruncate = 93,
106 fchmod = 94,
107 fchown = 95,
108 getpriority = 96,
109 setpriority = 97,
110 profil = 98,
111 statfs = 99,
112 fstatfs = 100,
113 ioperm = 101,
114 socketcall = 102,
115 syslog = 103,
116 setitimer = 104,
117 getitimer = 105,
118 stat = 106,
119 lstat = 107,
120 fstat = 108,
121 olduname = 109,
122 iopl = 110,
123 vhangup = 111,
124 idle = 112,
125 vm86 = 113,
126 wait4 = 114,
127 swapoff = 115,
128 sysinfo = 116,
129 ipc = 117,
130 fsync = 118,
131 sigreturn = 119,
132 clone = 120,
133 setdomainname = 121,
134 uname = 122,
135 modify_ldt = 123,
136 adjtimex = 124,
137 mprotect = 125,
138 sigprocmask = 126,
139 create_module = 127,
140 init_module = 128,
141 delete_module = 129,
142 get_kernel_syms = 130,
143 quotactl = 131,
144 getpgid = 132,
145 fchdir = 133,
146 bdflush = 134,
147 sysfs = 135,
148 personality = 136,
149 afs_syscall = 137,
150 setfsuid = 138,
151 setfsgid = 139,
152 _llseek = 140,
153 getdents = 141,
154 _newselect = 142,
155 flock = 143,
156 msync = 144,
157 readv = 145,
158 writev = 146,
159 getsid = 147,
160 fdatasync = 148,
161 _sysctl = 149,
162 mlock = 150,
163 munlock = 151,
164 mlockall = 152,
165 munlockall = 153,
166 sched_setparam = 154,
167 sched_getparam = 155,
168 sched_setscheduler = 156,
169 sched_getscheduler = 157,
170 sched_yield = 158,
171 sched_get_priority_max = 159,
172 sched_get_priority_min = 160,
173 sched_rr_get_interval = 161,
174 nanosleep = 162,
175 mremap = 163,
176 setresuid = 164,
177 getresuid = 165,
178 query_module = 166,
179 poll = 167,
180 nfsservctl = 168,
181 setresgid = 169,
182 getresgid = 170,
183 prctl = 171,
184 rt_sigreturn = 172,
185 rt_sigaction = 173,
186 rt_sigprocmask = 174,
187 rt_sigpending = 175,
188 rt_sigtimedwait = 176,
189 rt_sigqueueinfo = 177,
190 rt_sigsuspend = 178,
191 pread64 = 179,
192 pwrite64 = 180,
193 chown = 181,
194 getcwd = 182,
195 capget = 183,
196 capset = 184,
197 sigaltstack = 185,
198 sendfile = 186,
199 getpmsg = 187,
200 putpmsg = 188,
201 vfork = 189,
202 ugetrlimit = 190,
203 readahead = 191,
204 pciconfig_read = 198,
205 pciconfig_write = 199,
206 pciconfig_iobase = 200,
207 multiplexer = 201,
208 getdents64 = 202,
209 pivot_root = 203,
210 madvise = 205,
211 mincore = 206,
212 gettid = 207,
213 tkill = 208,
214 setxattr = 209,
215 lsetxattr = 210,
216 fsetxattr = 211,
217 getxattr = 212,
218 lgetxattr = 213,
219 fgetxattr = 214,
220 listxattr = 215,
221 llistxattr = 216,
222 flistxattr = 217,
223 removexattr = 218,
224 lremovexattr = 219,
225 fremovexattr = 220,
226 futex = 221,
227 sched_setaffinity = 222,
228 sched_getaffinity = 223,
229 tuxcall = 225,
230 io_setup = 227,
231 io_destroy = 228,
232 io_getevents = 229,
233 io_submit = 230,
234 io_cancel = 231,
235 set_tid_address = 232,
236 fadvise64 = 233,
237 exit_group = 234,
238 lookup_dcookie = 235,
239 epoll_create = 236,
240 epoll_ctl = 237,
241 epoll_wait = 238,
242 remap_file_pages = 239,
243 timer_create = 240,
244 timer_settime = 241,
245 timer_gettime = 242,
246 timer_getoverrun = 243,
247 timer_delete = 244,
248 clock_settime = 245,
249 clock_gettime = 246,
250 clock_getres = 247,
251 clock_nanosleep = 248,
252 swapcontext = 249,
253 tgkill = 250,
254 utimes = 251,
255 statfs64 = 252,
256 fstatfs64 = 253,
257 rtas = 255,
258 sys_debug_setcontext = 256,
259 migrate_pages = 258,
260 mbind = 259,
261 get_mempolicy = 260,
262 set_mempolicy = 261,
263 mq_open = 262,
264 mq_unlink = 263,
265 mq_timedsend = 264,
266 mq_timedreceive = 265,
267 mq_notify = 266,
268 mq_getsetattr = 267,
269 kexec_load = 268,
270 add_key = 269,
271 request_key = 270,
272 keyctl = 271,
273 waitid = 272,
274 ioprio_set = 273,
275 ioprio_get = 274,
276 inotify_init = 275,
277 inotify_add_watch = 276,
278 inotify_rm_watch = 277,
279 spu_run = 278,
280 spu_create = 279,
281 pselect6 = 280,
282 ppoll = 281,
283 unshare = 282,
284 splice = 283,
285 tee = 284,
286 vmsplice = 285,
287 openat = 286,
288 mkdirat = 287,
289 mknodat = 288,
290 fchownat = 289,
291 futimesat = 290,
292 newfstatat = 291,
293 unlinkat = 292,
294 renameat = 293,
295 linkat = 294,
296 symlinkat = 295,
297 readlinkat = 296,
298 fchmodat = 297,
299 faccessat = 298,
300 get_robust_list = 299,
301 set_robust_list = 300,
302 move_pages = 301,
303 getcpu = 302,
304 epoll_pwait = 303,
305 utimensat = 304,
306 signalfd = 305,
307 timerfd_create = 306,
308 eventfd = 307,
309 sync_file_range2 = 308,
310 fallocate = 309,
311 subpage_prot = 310,
312 timerfd_settime = 311,
313 timerfd_gettime = 312,
314 signalfd4 = 313,
315 eventfd2 = 314,
316 epoll_create1 = 315,
317 dup3 = 316,
318 pipe2 = 317,
319 inotify_init1 = 318,
320 perf_event_open = 319,
321 preadv = 320,
322 pwritev = 321,
323 rt_tgsigqueueinfo = 322,
324 fanotify_init = 323,
325 fanotify_mark = 324,
326 prlimit64 = 325,
327 socket = 326,
328 bind = 327,
329 connect = 328,
330 listen = 329,
331 accept = 330,
332 getsockname = 331,
333 getpeername = 332,
334 socketpair = 333,
335 send = 334,
336 sendto = 335,
337 recv = 336,
338 recvfrom = 337,
339 shutdown = 338,
340 setsockopt = 339,
341 getsockopt = 340,
342 sendmsg = 341,
343 recvmsg = 342,
344 recvmmsg = 343,
345 accept4 = 344,
346 name_to_handle_at = 345,
347 open_by_handle_at = 346,
348 clock_adjtime = 347,
349 syncfs = 348,
350 sendmmsg = 349,
351 setns = 350,
352 process_vm_readv = 351,
353 process_vm_writev = 352,
354 finit_module = 353,
355 kcmp = 354,
356 sched_setattr = 355,
357 sched_getattr = 356,
358 renameat2 = 357,
359 seccomp = 358,
360 getrandom = 359,
361 memfd_create = 360,
362 bpf = 361,
363 execveat = 362,
364 switch_endian = 363,
365 userfaultfd = 364,
366 membarrier = 365,
367 mlock2 = 378,
368 copy_file_range = 379,
369 preadv2 = 380,
370 pwritev2 = 381,
371 kexec_file_load = 382,
372 statx = 383,
373 pkey_alloc = 384,
374 pkey_free = 385,
375 pkey_mprotect = 386,
376 rseq = 387,
377 io_pgetevents = 388,
378 semtimedop = 392,
379 semget = 393,
380 semctl = 394,
381 shmget = 395,
382 shmctl = 396,
383 shmat = 397,
384 shmdt = 398,
385 msgget = 399,
386 msgsnd = 400,
387 msgrcv = 401,
388 msgctl = 402,
389 pidfd_send_signal = 424,
390 io_uring_setup = 425,
391 io_uring_enter = 426,
392 io_uring_register = 427,
393 open_tree = 428,
394 move_mount = 429,
395 fsopen = 430,
396 fsconfig = 431,
397 fsmount = 432,
398 fspick = 433,
399 pidfd_open = 434,
400 clone3 = 435,
401 openat2 = 437,
402 pidfd_getfd = 438,
403
404 _,
405};
406
407pub const O_CREAT = 0o100;
408pub const O_EXCL = 0o200;
409pub const O_NOCTTY = 0o400;
410pub const O_TRUNC = 0o1000;
411pub const O_APPEND = 0o2000;
412pub const O_NONBLOCK = 0o4000;
413pub const O_DSYNC = 0o10000;
414pub const O_SYNC = 0o4010000;
415pub const O_RSYNC = 0o4010000;
416pub const O_DIRECTORY = 0o40000;
417pub const O_NOFOLLOW = 0o100000;
418pub const O_CLOEXEC = 0o2000000;
419
420pub const O_ASYNC = 0o20000;
421pub const O_DIRECT = 0o400000;
422pub const O_LARGEFILE = 0o200000;
423pub const O_NOATIME = 0o1000000;
424pub const O_PATH = 0o10000000;
425pub const O_TMPFILE = 0o20200000;
426pub const O_NDELAY = O_NONBLOCK;
427
428pub const F_DUPFD = 0;
429pub const F_GETFD = 1;
430pub const F_SETFD = 2;
431pub const F_GETFL = 3;
432pub const F_SETFL = 4;
433
434pub const F_SETOWN = 8;
435pub const F_GETOWN = 9;
436pub const F_SETSIG = 10;
437pub const F_GETSIG = 11;
438
439pub const F_GETLK = 5;
440pub const F_SETLK = 6;
441pub const F_SETLKW = 7;
442
443pub const F_RDLCK = 0;
444pub const F_WRLCK = 1;
445pub const F_UNLCK = 2;
446
447pub const LOCK_SH = 1;
448pub const LOCK_EX = 2;
449pub const LOCK_UN = 8;
450pub const LOCK_NB = 4;
451
452pub const F_SETOWN_EX = 15;
453pub const F_GETOWN_EX = 16;
454
455pub const F_GETOWNER_UIDS = 17;
456
457/// stack-like segment
458pub const MAP_GROWSDOWN = 0x0100;
459
460/// ETXTBSY
461pub const MAP_DENYWRITE = 0x0800;
462
463/// mark it as an executable
464pub const MAP_EXECUTABLE = 0x1000;
465
466/// pages are locked
467pub const MAP_LOCKED = 0x0080;
468
469/// don't check for reservations
470pub const MAP_NORESERVE = 0x0040;
471
472pub const VDSO_CGT_SYM = "__kernel_clock_gettime";
473pub const VDSO_CGT_VER = "LINUX_2.6.15";
474
475pub const Flock = extern struct {
476 l_type: i16,
477 l_whence: i16,
478 l_start: off_t,
479 l_len: off_t,
480 l_pid: pid_t,
481 __unused: [4]u8,
482};
483
484pub const msghdr = extern struct {
485 msg_name: ?*sockaddr,
486 msg_namelen: socklen_t,
487 msg_iov: [*]iovec,
488 msg_iovlen: usize,
489 msg_control: ?*c_void,
490 msg_controllen: usize,
491 msg_flags: i32,
492};
493
494pub const msghdr_const = extern struct {
495 msg_name: ?*const sockaddr,
496 msg_namelen: socklen_t,
497 msg_iov: [*]iovec_const,
498 msg_iovlen: usize,
499 msg_control: ?*c_void,
500 msg_controllen: usize,
501 msg_flags: i32,
502};
503
504pub const blksize_t = i64;
505pub const nlink_t = u64;
506pub const time_t = i64;
507pub const mode_t = u32;
508pub const off_t = i64;
509pub const ino_t = u64;
510pub const dev_t = u64;
511pub const blkcnt_t = i64;
512
513/// Renamed to Stat to not conflict with the stat function.
514/// atime, mtime, and ctime have functions to return `timespec`,
515/// because although this is a POSIX API, the layout and names of
516/// the structs are inconsistent across operating systems, and
517/// in C, macros are used to hide the differences. Here we use
518/// methods to accomplish this.
519pub const Stat = extern struct {
520 dev: dev_t,
521 ino: ino_t,
522 nlink: nlink_t,
523 mode: mode_t,
524 uid: uid_t,
525 gid: gid_t,
526 rdev: dev_t,
527 size: off_t,
528 blksize: blksize_t,
529 blocks: blkcnt_t,
530 atim: timespec,
531 mtim: timespec,
532 ctim: timespec,
533 __unused: [3]u64,
534
535 pub fn atime(self: Stat) timespec {
536 return self.atim;
537 }
538
539 pub fn mtime(self: Stat) timespec {
540 return self.mtim;
541 }
542
543 pub fn ctime(self: Stat) timespec {
544 return self.ctim;
545 }
546};
547
548pub const timespec = extern struct {
549 tv_sec: time_t,
550 tv_nsec: isize,
551};
552
553pub const timeval = extern struct {
554 tv_sec: isize,
555 tv_usec: isize,
556};
557
558pub const timezone = extern struct {
559 tz_minuteswest: i32,
560 tz_dsttime: i32,
561};
562
563pub const greg_t = u64;
564pub const gregset_t = [48]greg_t;
565pub const fpregset_t = [33]f64;
566
567/// The position of the vscr register depends on endianness.
568/// On C, macros are used to change vscr_word's offset to
569/// account for this. Here we'll just define vscr_word_le
570/// and vscr_word_be. Code must take care to use the correct one.
571pub const vrregset = extern struct {
572 vrregs: [32][4]u32 align(16),
573 vscr_word_le: u32,
574 _pad1: [2]u32,
575 vscr_word_be: u32,
576 vrsave: u32,
577 _pad2: [3]u32,
578};
579pub const vrregset_t = vrregset;
580
581pub const mcontext_t = extern struct {
582 __unused: [4]u64,
583 signal: i32,
584 _pad0: i32,
585 handler: u64,
586 oldmask: u64,
587 regs: ?*c_void,
588 gp_regs: gregset_t,
589 fp_regs: fpregset_t,
590 v_regs: *vrregset_t,
591 vmx_reserve: [34 + 34 + 32 + 1]i64,
592};
593
594pub const ucontext_t = extern struct {
595 flags: u32,
596 link: *ucontext_t,
597 stack: stack_t,
598 sigmask: sigset_t,
599 mcontext: mcontext_t,
600};
601
602pub const Elf_Symndx = u32;
lib/std/os/bits/linux/prctl.zig created+158
......@@ -0,0 +1,158 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7pub const PR_SET_PDEATHSIG = 1;
8pub const PR_GET_PDEATHSIG = 2;
9
10pub const PR_GET_DUMPABLE = 3;
11pub const PR_SET_DUMPABLE = 4;
12
13pub const PR_GET_UNALIGN = 5;
14pub const PR_SET_UNALIGN = 6;
15pub const PR_UNALIGN_NOPRINT = 1;
16pub const PR_UNALIGN_SIGBUS = 2;
17
18pub const PR_GET_KEEPCAPS = 7;
19pub const PR_SET_KEEPCAPS = 8;
20
21pub const PR_GET_FPEMU = 9;
22pub const PR_SET_FPEMU = 10;
23pub const PR_FPEMU_NOPRINT = 1;
24pub const PR_FPEMU_SIGFPE = 2;
25
26pub const PR_GET_FPEXC = 11;
27pub const PR_SET_FPEXC = 12;
28pub const PR_FP_EXC_SW_ENABLE = 0x80;
29pub const PR_FP_EXC_DIV = 0x010000;
30pub const PR_FP_EXC_OVF = 0x020000;
31pub const PR_FP_EXC_UND = 0x040000;
32pub const PR_FP_EXC_RES = 0x080000;
33pub const PR_FP_EXC_INV = 0x100000;
34pub const PR_FP_EXC_DISABLED = 0;
35pub const PR_FP_EXC_NONRECOV = 1;
36pub const PR_FP_EXC_ASYNC = 2;
37pub const PR_FP_EXC_PRECISE = 3;
38
39pub const PR_GET_TIMING = 13;
40pub const PR_SET_TIMING = 14;
41pub const PR_TIMING_STATISTICAL = 0;
42pub const PR_TIMING_TIMESTAMP = 1;
43
44pub const PR_SET_NAME = 15;
45pub const PR_GET_NAME = 16;
46
47pub const PR_GET_ENDIAN = 19;
48pub const PR_SET_ENDIAN = 20;
49pub const PR_ENDIAN_BIG = 0;
50pub const PR_ENDIAN_LITTLE = 1;
51pub const PR_ENDIAN_PPC_LITTLE = 2;
52
53pub const PR_GET_SECCOMP = 21;
54pub const PR_SET_SECCOMP = 22;
55
56pub const PR_CAPBSET_READ = 23;
57pub const PR_CAPBSET_DROP = 24;
58
59pub const PR_GET_TSC = 25;
60pub const PR_SET_TSC = 26;
61pub const PR_TSC_ENABLE = 1;
62pub const PR_TSC_SIGSEGV = 2;
63
64pub const PR_GET_SECUREBITS = 27;
65pub const PR_SET_SECUREBITS = 28;
66
67pub const PR_SET_TIMERSLACK = 29;
68pub const PR_GET_TIMERSLACK = 30;
69
70pub const PR_TASK_PERF_EVENTS_DISABLE = 31;
71pub const PR_TASK_PERF_EVENTS_ENABLE = 32;
72
73pub const PR_MCE_KILL = 33;
74pub const PR_MCE_KILL_CLEAR = 0;
75pub const PR_MCE_KILL_SET = 1;
76
77pub const PR_MCE_KILL_LATE = 0;
78pub const PR_MCE_KILL_EARLY = 1;
79pub const PR_MCE_KILL_DEFAULT = 2;
80
81pub const PR_MCE_KILL_GET = 34;
82
83pub const PR_SET_MM = 35;
84pub const PR_SET_MM_START_CODE = 1;
85pub const PR_SET_MM_END_CODE = 2;
86pub const PR_SET_MM_START_DATA = 3;
87pub const PR_SET_MM_END_DATA = 4;
88pub const PR_SET_MM_START_STACK = 5;
89pub const PR_SET_MM_START_BRK = 6;
90pub const PR_SET_MM_BRK = 7;
91pub const PR_SET_MM_ARG_START = 8;
92pub const PR_SET_MM_ARG_END = 9;
93pub const PR_SET_MM_ENV_START = 10;
94pub const PR_SET_MM_ENV_END = 11;
95pub const PR_SET_MM_AUXV = 12;
96pub const PR_SET_MM_EXE_FILE = 13;
97pub const PR_SET_MM_MAP = 14;
98pub const PR_SET_MM_MAP_SIZE = 15;
99
100pub const prctl_mm_map = extern struct {
101 start_code: u64,
102 end_code: u64,
103 start_data: u64,
104 end_data: u64,
105 start_brk: u64,
106 brk: u64,
107 start_stack: u64,
108 arg_start: u64,
109 arg_end: u64,
110 env_start: u64,
111 env_end: u64,
112 auxv: *u64,
113 auxv_size: u32,
114 exe_fd: u32,
115};
116
117pub const PR_SET_PTRACER = 0x59616d61;
118pub const PR_SET_PTRACER_ANY = std.math.maxInt(c_ulong);
119
120pub const PR_SET_CHILD_SUBREAPER = 36;
121pub const PR_GET_CHILD_SUBREAPER = 37;
122
123pub const PR_SET_NO_NEW_PRIVS = 38;
124pub const PR_GET_NO_NEW_PRIVS = 39;
125
126pub const PR_GET_TID_ADDRESS = 40;
127
128pub const PR_SET_THP_DISABLE = 41;
129pub const PR_GET_THP_DISABLE = 42;
130
131pub const PR_MPX_ENABLE_MANAGEMENT = 43;
132pub const PR_MPX_DISABLE_MANAGEMENT = 44;
133
134pub const PR_SET_FP_MODE = 45;
135pub const PR_GET_FP_MODE = 46;
136pub const PR_FP_MODE_FR = 1 << 0;
137pub const PR_FP_MODE_FRE = 1 << 1;
138
139pub const PR_CAP_AMBIENT = 47;
140pub const PR_CAP_AMBIENT_IS_SET = 1;
141pub const PR_CAP_AMBIENT_RAISE = 2;
142pub const PR_CAP_AMBIENT_LOWER = 3;
143pub const PR_CAP_AMBIENT_CLEAR_ALL = 4;
144
145pub const PR_SVE_SET_VL = 50;
146pub const PR_SVE_SET_VL_ONEXEC = 1 << 18;
147pub const PR_SVE_GET_VL = 51;
148pub const PR_SVE_VL_LEN_MASK = 0xffff;
149pub const PR_SVE_VL_INHERIT = 1 << 17;
150
151pub const PR_GET_SPECULATION_CTRL = 52;
152pub const PR_SET_SPECULATION_CTRL = 53;
153pub const PR_SPEC_STORE_BYPASS = 0;
154pub const PR_SPEC_NOT_AFFECTED = 0;
155pub const PR_SPEC_PRCTL = 1 << 0;
156pub const PR_SPEC_ENABLE = 1 << 1;
157pub const PR_SPEC_DISABLE = 1 << 2;
158pub const PR_SPEC_FORCE_DISABLE = 1 << 3;
lib/std/os/bits/linux/securebits.zig created+41
......@@ -0,0 +1,41 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7fn issecure_mask(comptime x: comptime_int) comptime_int {
8 return 1 << x;
9}
10
11pub const SECUREBITS_DEFAULT = 0x00000000;
12
13pub const SECURE_NOROOT = 0;
14pub const SECURE_NOROOT_LOCKED = 1;
15
16pub const SECBIT_NOROOT = issecure_mask(SECURE_NOROOT);
17pub const SECBIT_NOROOT_LOCKED = issecure_mask(SECURE_NOROOT_LOCKED);
18
19pub const SECURE_NO_SETUID_FIXUP = 2;
20pub const SECURE_NO_SETUID_FIXUP_LOCKED = 3;
21
22pub const SECBIT_NO_SETUID_FIXUP = issecure_mask(SECURE_NO_SETUID_FIXUP);
23pub const SECBIT_NO_SETUID_FIXUP_LOCKED = issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED);
24
25pub const SECURE_KEEP_CAPS = 4;
26pub const SECURE_KEEP_CAPS_LOCKED = 5;
27
28pub const SECBIT_KEEP_CAPS = issecure_mask(SECURE_KEEP_CAPS);
29pub const SECBIT_KEEP_CAPS_LOCKED = issecure_mask(SECURE_KEEP_CAPS_LOCKED);
30
31pub const SECURE_NO_CAP_AMBIENT_RAISE = 6;
32pub const SECURE_NO_CAP_AMBIENT_RAISE_LOCKED = 7;
33
34pub const SECBIT_NO_CAP_AMBIENT_RAISE = issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE);
35pub const SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED = issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE_LOCKED);
36
37pub const SECURE_ALL_BITS = issecure_mask(SECURE_NOROOT) |
38 issecure_mask(SECURE_NO_SETUID_FIXUP) |
39 issecure_mask(SECURE_KEEP_CAPS) |
40 issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE);
41pub const SECURE_ALL_LOCKS = SECURE_ALL_BITS << 1;
lib/std/os/linux.zig+5
......@@ -25,6 +25,7 @@ pub usingnamespace switch (builtin.arch) {
2525 .arm => @import("linux/arm-eabi.zig"),
2626 .riscv64 => @import("linux/riscv64.zig"),
2727 .mips, .mipsel => @import("linux/mips.zig"),
28 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),
2829 else => struct {},
2930};
3031pub usingnamespace @import("bits.zig");
......@@ -1258,6 +1259,10 @@ pub fn fdatasync(fd: fd_t) usize {
12581259 return syscall1(.fdatasync, @bitCast(usize, @as(isize, fd)));
12591260}
12601261
1262pub fn prctl(option: i32, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize {
1263 return syscall5(.prctl, @bitCast(usize, @as(isize, option)), arg2, arg3, arg4, arg5);
1264}
1265
12611266test "" {
12621267 if (builtin.os.tag == .linux) {
12631268 _ = @import("linux/test.zig");
lib/std/os/linux/bpf.zig+761-68
......@@ -3,9 +3,16 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6usingnamespace std.os;
6usingnamespace std.os.linux;
77const std = @import("../../std.zig");
8const errno = getErrno;
9const unexpectedErrno = std.os.unexpectedErrno;
810const expectEqual = std.testing.expectEqual;
11const expectError = std.testing.expectError;
12const expect = std.testing.expect;
13
14pub const btf = @import("bpf/btf.zig");
15pub const kern = @import("bpf/kern.zig");
916
1017// instruction classes
1118pub const LD = 0x00;
......@@ -62,6 +69,7 @@ pub const MAXINSNS = 4096;
6269// instruction classes
6370/// jmp mode in word width
6471pub const JMP32 = 0x06;
72
6573/// alu mode in double word width
6674pub const ALU64 = 0x07;
6775
......@@ -72,14 +80,17 @@ pub const XADD = 0xc0;
7280// alu/jmp fields
7381/// mov reg to reg
7482pub const MOV = 0xb0;
83
7584/// sign extending arithmetic shift right */
7685pub const ARSH = 0xc0;
7786
7887// change endianness of a register
7988/// flags for endianness conversion:
8089pub const END = 0xd0;
90
8191/// convert to little-endian */
8292pub const TO_LE = 0x00;
93
8394/// convert to big-endian
8495pub const TO_BE = 0x08;
8596pub const FROM_LE = TO_LE;
......@@ -88,29 +99,39 @@ pub const FROM_BE = TO_BE;
8899// jmp encodings
89100/// jump != *
90101pub const JNE = 0x50;
102
91103/// LT is unsigned, '<'
92104pub const JLT = 0xa0;
105
93106/// LE is unsigned, '<=' *
94107pub const JLE = 0xb0;
108
95109/// SGT is signed '>', GT in x86
96110pub const JSGT = 0x60;
111
97112/// SGE is signed '>=', GE in x86
98113pub const JSGE = 0x70;
114
99115/// SLT is signed, '<'
100116pub const JSLT = 0xc0;
117
101118/// SLE is signed, '<='
102119pub const JSLE = 0xd0;
120
103121/// function call
104122pub const CALL = 0x80;
123
105124/// function return
106125pub const EXIT = 0x90;
107126
108127/// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the
109128/// program in this cgroup yields to sub-cgroup program.
110129pub const F_ALLOW_OVERRIDE = 0x1;
130
111131/// Flag for prog_attach command. If a sub-cgroup installs some bpf program,
112132/// that cgroup program gets run in addition to the program in this cgroup.
113133pub const F_ALLOW_MULTI = 0x2;
134
114135/// Flag for prog_attach command.
115136pub const F_REPLACE = 0x4;
116137
......@@ -164,47 +185,61 @@ pub const PSEUDO_CALL = 1;
164185
165186/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing
166187pub const ANY = 0;
188
167189/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist
168190pub const NOEXIST = 1;
191
169192/// flag for BPF_MAP_UPDATE_ELEM command. update existing element
170193pub const EXIST = 2;
194
171195/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update
172196pub const F_LOCK = 4;
173197
174198/// flag for BPF_MAP_CREATE command */
175199pub const BPF_F_NO_PREALLOC = 0x1;
200
176201/// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in
177202/// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can
178203/// scale and perform better. Note, the LRU nodes (including free nodes) cannot
179204/// be moved across different LRU lists.
180205pub const BPF_F_NO_COMMON_LRU = 0x2;
206
181207/// flag for BPF_MAP_CREATE command. Specify numa node during map creation
182208pub const BPF_F_NUMA_NODE = 0x4;
209
183210/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from
184211/// syscall side
185212pub const BPF_F_RDONLY = 0x8;
213
186214/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from
187215/// syscall side
188216pub const BPF_F_WRONLY = 0x10;
217
189218/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset
190219/// instead of pointer
191220pub const BPF_F_STACK_BUILD_ID = 0x20;
221
192222/// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This
193223/// should only be used for testing.
194224pub const BPF_F_ZERO_SEED = 0x40;
225
195226/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program
196227/// side.
197228pub const BPF_F_RDONLY_PROG = 0x80;
229
198230/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program
199231/// side.
200232pub const BPF_F_WRONLY_PROG = 0x100;
233
201234/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted
202235/// socket
203236pub const BPF_F_CLONE = 0x200;
237
204238/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
205239pub const BPF_F_MMAPABLE = 0x400;
206240
207/// These values correspond to "syscalls" within the BPF program's environment
241/// These values correspond to "syscalls" within the BPF program's environment,
242/// each one is documented in std.os.linux.BPF.kern
208243pub const Helper = enum(i32) {
209244 unspec,
210245 map_lookup_elem,
......@@ -325,9 +360,34 @@ pub const Helper = enum(i32) {
325360 tcp_send_ack,
326361 send_signal_thread,
327362 jiffies64,
363 read_branch_records,
364 get_ns_current_pid_tgid,
365 xdp_output,
366 get_netns_cookie,
367 get_current_ancestor_cgroup_id,
368 sk_assign,
369 ktime_get_boot_ns,
370 seq_printf,
371 seq_write,
372 sk_cgroup_id,
373 sk_ancestor_cgroup_id,
374 ringbuf_output,
375 ringbuf_reserve,
376 ringbuf_submit,
377 ringbuf_discard,
378 ringbuf_query,
379 csum_level,
380 skc_to_tcp6_sock,
381 skc_to_tcp_sock,
382 skc_to_tcp_timewait_sock,
383 skc_to_tcp_request_sock,
384 skc_to_udp6_sock,
385 get_task_stack,
328386 _,
329387};
330388
389// TODO: determine that this is the expected bit layout for both little and big
390// endian systems
331391/// a single BPF instruction
332392pub const Insn = packed struct {
333393 code: u8,
......@@ -340,19 +400,30 @@ pub const Insn = packed struct {
340400 /// frame
341401 pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };
342402 const Source = packed enum(u1) { reg, imm };
403
404 const Mode = packed enum(u8) {
405 imm = IMM,
406 abs = ABS,
407 ind = IND,
408 mem = MEM,
409 len = LEN,
410 msh = MSH,
411 };
412
343413 const AluOp = packed enum(u8) {
344414 add = ADD,
345415 sub = SUB,
346416 mul = MUL,
347417 div = DIV,
348 op_or = OR,
349 op_and = AND,
418 alu_or = OR,
419 alu_and = AND,
350420 lsh = LSH,
351421 rsh = RSH,
352422 neg = NEG,
353423 mod = MOD,
354424 xor = XOR,
355425 mov = MOV,
426 arsh = ARSH,
356427 };
357428
358429 pub const Size = packed enum(u8) {
......@@ -368,6 +439,13 @@ pub const Insn = packed struct {
368439 jgt = JGT,
369440 jge = JGE,
370441 jset = JSET,
442 jlt = JLT,
443 jle = JLE,
444 jne = JNE,
445 jsgt = JSGT,
446 jsge = JSGE,
447 jslt = JSLT,
448 jsle = JSLE,
371449 };
372450
373451 const ImmOrReg = union(Source) {
......@@ -419,22 +497,100 @@ pub const Insn = packed struct {
419497 return alu(64, .add, dst, src);
420498 }
421499
500 pub fn sub(dst: Reg, src: anytype) Insn {
501 return alu(64, .sub, dst, src);
502 }
503
504 pub fn mul(dst: Reg, src: anytype) Insn {
505 return alu(64, .mul, dst, src);
506 }
507
508 pub fn div(dst: Reg, src: anytype) Insn {
509 return alu(64, .div, dst, src);
510 }
511
512 pub fn alu_or(dst: Reg, src: anytype) Insn {
513 return alu(64, .alu_or, dst, src);
514 }
515
516 pub fn alu_and(dst: Reg, src: anytype) Insn {
517 return alu(64, .alu_and, dst, src);
518 }
519
520 pub fn lsh(dst: Reg, src: anytype) Insn {
521 return alu(64, .lsh, dst, src);
522 }
523
524 pub fn rsh(dst: Reg, src: anytype) Insn {
525 return alu(64, .rsh, dst, src);
526 }
527
528 pub fn neg(dst: Reg) Insn {
529 return alu(64, .neg, dst, 0);
530 }
531
532 pub fn mod(dst: Reg, src: anytype) Insn {
533 return alu(64, .mod, dst, src);
534 }
535
536 pub fn xor(dst: Reg, src: anytype) Insn {
537 return alu(64, .xor, dst, src);
538 }
539
540 pub fn arsh(dst: Reg, src: anytype) Insn {
541 return alu(64, .arsh, dst, src);
542 }
543
422544 fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {
423545 return imm_reg(JMP | @enumToInt(op), dst, src, off);
424546 }
425547
548 pub fn ja(off: i16) Insn {
549 return jmp(.ja, .r0, 0, off);
550 }
551
426552 pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {
427553 return jmp(.jeq, dst, src, off);
428554 }
429555
430 pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn {
431 return Insn{
432 .code = STX | @enumToInt(size) | MEM,
433 .dst = @enumToInt(dst),
434 .src = @enumToInt(src),
435 .off = off,
436 .imm = 0,
437 };
556 pub fn jgt(dst: Reg, src: anytype, off: i16) Insn {
557 return jmp(.jgt, dst, src, off);
558 }
559
560 pub fn jge(dst: Reg, src: anytype, off: i16) Insn {
561 return jmp(.jge, dst, src, off);
562 }
563
564 pub fn jlt(dst: Reg, src: anytype, off: i16) Insn {
565 return jmp(.jlt, dst, src, off);
566 }
567
568 pub fn jle(dst: Reg, src: anytype, off: i16) Insn {
569 return jmp(.jle, dst, src, off);
570 }
571
572 pub fn jset(dst: Reg, src: anytype, off: i16) Insn {
573 return jmp(.jset, dst, src, off);
574 }
575
576 pub fn jne(dst: Reg, src: anytype, off: i16) Insn {
577 return jmp(.jne, dst, src, off);
578 }
579
580 pub fn jsgt(dst: Reg, src: anytype, off: i16) Insn {
581 return jmp(.jsgt, dst, src, off);
582 }
583
584 pub fn jsge(dst: Reg, src: anytype, off: i16) Insn {
585 return jmp(.jsge, dst, src, off);
586 }
587
588 pub fn jslt(dst: Reg, src: anytype, off: i16) Insn {
589 return jmp(.jslt, dst, src, off);
590 }
591
592 pub fn jsle(dst: Reg, src: anytype, off: i16) Insn {
593 return jmp(.jsle, dst, src, off);
438594 }
439595
440596 pub fn xadd(dst: Reg, src: Reg) Insn {
......@@ -447,17 +603,34 @@ pub const Insn = packed struct {
447603 };
448604 }
449605
450 /// direct packet access, R0 = *(uint *)(skb->data + imm32)
451 pub fn ld_abs(size: Size, imm: i32) Insn {
606 fn ld(mode: Mode, size: Size, dst: Reg, src: Reg, imm: i32) Insn {
452607 return Insn{
453 .code = LD | @enumToInt(size) | ABS,
454 .dst = 0,
455 .src = 0,
608 .code = @enumToInt(mode) | @enumToInt(size) | LD,
609 .dst = @enumToInt(dst),
610 .src = @enumToInt(src),
456611 .off = 0,
457612 .imm = imm,
458613 };
459614 }
460615
616 pub fn ld_abs(size: Size, dst: Reg, src: Reg, imm: i32) Insn {
617 return ld(.abs, size, dst, src, imm);
618 }
619
620 pub fn ld_ind(size: Size, dst: Reg, src: Reg, imm: i32) Insn {
621 return ld(.ind, size, dst, src, imm);
622 }
623
624 pub fn ldx(size: Size, dst: Reg, src: Reg, off: i16) Insn {
625 return Insn{
626 .code = MEM | @enumToInt(size) | LDX,
627 .dst = @enumToInt(dst),
628 .src = @enumToInt(src),
629 .off = off,
630 .imm = 0,
631 };
632 }
633
461634 fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn {
462635 return Insn{
463636 .code = LD | DW | IMM,
......@@ -478,6 +651,14 @@ pub const Insn = packed struct {
478651 };
479652 }
480653
654 pub fn ld_dw1(dst: Reg, imm: u64) Insn {
655 return ld_imm_impl1(dst, .r0, imm);
656 }
657
658 pub fn ld_dw2(imm: u64) Insn {
659 return ld_imm_impl2(imm);
660 }
661
481662 pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn {
482663 return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd));
483664 }
......@@ -486,6 +667,53 @@ pub const Insn = packed struct {
486667 return ld_imm_impl2(@intCast(u64, map_fd));
487668 }
488669
670 pub fn st(comptime size: Size, dst: Reg, off: i16, imm: i32) Insn {
671 if (size == .double_word) @compileError("TODO: need to determine how to correctly handle double words");
672 return Insn{
673 .code = MEM | @enumToInt(size) | ST,
674 .dst = @enumToInt(dst),
675 .src = 0,
676 .off = off,
677 .imm = imm,
678 };
679 }
680
681 pub fn stx(size: Size, dst: Reg, off: i16, src: Reg) Insn {
682 return Insn{
683 .code = MEM | @enumToInt(size) | STX,
684 .dst = @enumToInt(dst),
685 .src = @enumToInt(src),
686 .off = off,
687 .imm = 0,
688 };
689 }
690
691 fn endian_swap(endian: std.builtin.Endian, comptime size: Size, dst: Reg) Insn {
692 return Insn{
693 .code = switch (endian) {
694 .Big => 0xdc,
695 .Little => 0xd4,
696 },
697 .dst = @enumToInt(dst),
698 .src = 0,
699 .off = 0,
700 .imm = switch (size) {
701 .byte => @compileError("can't swap a single byte"),
702 .half_word => 16,
703 .word => 32,
704 .double_word => 64,
705 },
706 };
707 }
708
709 pub fn le(comptime size: Size, dst: Reg) Insn {
710 return endian_swap(.Little, size, dst);
711 }
712
713 pub fn be(comptime size: Size, dst: Reg) Insn {
714 return endian_swap(.Big, size, dst);
715 }
716
489717 pub fn call(helper: Helper) Insn {
490718 return Insn{
491719 .code = JMP | CALL,
......@@ -508,95 +736,242 @@ pub const Insn = packed struct {
508736 }
509737};
510738
511fn expect_insn(insn: Insn, val: u64) void {
512 expectEqual(@bitCast(u64, insn), val);
513}
514
515739test "insn bitsize" {
516740 expectEqual(@bitSizeOf(Insn), 64);
517741}
518742
519// mov instructions
520test "mov imm" {
521 expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7);
522}
523
524test "mov reg" {
525 expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf);
526}
527
528// alu instructions
529test "add imm" {
530 expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207);
743fn expect_opcode(code: u8, insn: Insn) void {
744 expectEqual(code, insn.code);
531745}
532746
533// ld instructions
534test "ld_abs" {
535 expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030);
536}
537
538test "ld_map_fd" {
539 expect_insn(Insn.ld_map_fd1(.r1, 42), 0x0000002a00001118);
540 expect_insn(Insn.ld_map_fd2(42), 0x0000000000000000);
541}
542
543// st instructions
544test "stx_mem" {
545 expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63);
546}
547
548test "xadd" {
549 expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db);
550}
551
552// jmp instructions
553test "jeq imm" {
554 expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015);
555}
556
557// other instructions
558test "call" {
559 expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085);
560}
561
562test "exit" {
563 expect_insn(Insn.exit(), 0x0000000000000095);
747// The opcodes were grabbed from https://github.com/iovisor/bpf-docs/blob/master/eBPF.md
748test "opcodes" {
749 // instructions that have a name that end with 1 or 2 are consecutive for
750 // loading 64-bit immediates (imm is only 32 bits wide)
751
752 // alu instructions
753 expect_opcode(0x07, Insn.add(.r1, 0));
754 expect_opcode(0x0f, Insn.add(.r1, .r2));
755 expect_opcode(0x17, Insn.sub(.r1, 0));
756 expect_opcode(0x1f, Insn.sub(.r1, .r2));
757 expect_opcode(0x27, Insn.mul(.r1, 0));
758 expect_opcode(0x2f, Insn.mul(.r1, .r2));
759 expect_opcode(0x37, Insn.div(.r1, 0));
760 expect_opcode(0x3f, Insn.div(.r1, .r2));
761 expect_opcode(0x47, Insn.alu_or(.r1, 0));
762 expect_opcode(0x4f, Insn.alu_or(.r1, .r2));
763 expect_opcode(0x57, Insn.alu_and(.r1, 0));
764 expect_opcode(0x5f, Insn.alu_and(.r1, .r2));
765 expect_opcode(0x67, Insn.lsh(.r1, 0));
766 expect_opcode(0x6f, Insn.lsh(.r1, .r2));
767 expect_opcode(0x77, Insn.rsh(.r1, 0));
768 expect_opcode(0x7f, Insn.rsh(.r1, .r2));
769 expect_opcode(0x87, Insn.neg(.r1));
770 expect_opcode(0x97, Insn.mod(.r1, 0));
771 expect_opcode(0x9f, Insn.mod(.r1, .r2));
772 expect_opcode(0xa7, Insn.xor(.r1, 0));
773 expect_opcode(0xaf, Insn.xor(.r1, .r2));
774 expect_opcode(0xb7, Insn.mov(.r1, 0));
775 expect_opcode(0xbf, Insn.mov(.r1, .r2));
776 expect_opcode(0xc7, Insn.arsh(.r1, 0));
777 expect_opcode(0xcf, Insn.arsh(.r1, .r2));
778
779 // atomic instructions: might be more of these not documented in the wild
780 expect_opcode(0xdb, Insn.xadd(.r1, .r2));
781
782 // TODO: byteswap instructions
783 expect_opcode(0xd4, Insn.le(.half_word, .r1));
784 expectEqual(@intCast(i32, 16), Insn.le(.half_word, .r1).imm);
785 expect_opcode(0xd4, Insn.le(.word, .r1));
786 expectEqual(@intCast(i32, 32), Insn.le(.word, .r1).imm);
787 expect_opcode(0xd4, Insn.le(.double_word, .r1));
788 expectEqual(@intCast(i32, 64), Insn.le(.double_word, .r1).imm);
789 expect_opcode(0xdc, Insn.be(.half_word, .r1));
790 expectEqual(@intCast(i32, 16), Insn.be(.half_word, .r1).imm);
791 expect_opcode(0xdc, Insn.be(.word, .r1));
792 expectEqual(@intCast(i32, 32), Insn.be(.word, .r1).imm);
793 expect_opcode(0xdc, Insn.be(.double_word, .r1));
794 expectEqual(@intCast(i32, 64), Insn.be(.double_word, .r1).imm);
795
796 // memory instructions
797 expect_opcode(0x18, Insn.ld_dw1(.r1, 0));
798 expect_opcode(0x00, Insn.ld_dw2(0));
799
800 // loading a map fd
801 expect_opcode(0x18, Insn.ld_map_fd1(.r1, 0));
802 expectEqual(@intCast(u4, PSEUDO_MAP_FD), Insn.ld_map_fd1(.r1, 0).src);
803 expect_opcode(0x00, Insn.ld_map_fd2(0));
804
805 expect_opcode(0x38, Insn.ld_abs(.double_word, .r1, .r2, 0));
806 expect_opcode(0x20, Insn.ld_abs(.word, .r1, .r2, 0));
807 expect_opcode(0x28, Insn.ld_abs(.half_word, .r1, .r2, 0));
808 expect_opcode(0x30, Insn.ld_abs(.byte, .r1, .r2, 0));
809
810 expect_opcode(0x58, Insn.ld_ind(.double_word, .r1, .r2, 0));
811 expect_opcode(0x40, Insn.ld_ind(.word, .r1, .r2, 0));
812 expect_opcode(0x48, Insn.ld_ind(.half_word, .r1, .r2, 0));
813 expect_opcode(0x50, Insn.ld_ind(.byte, .r1, .r2, 0));
814
815 expect_opcode(0x79, Insn.ldx(.double_word, .r1, .r2, 0));
816 expect_opcode(0x61, Insn.ldx(.word, .r1, .r2, 0));
817 expect_opcode(0x69, Insn.ldx(.half_word, .r1, .r2, 0));
818 expect_opcode(0x71, Insn.ldx(.byte, .r1, .r2, 0));
819
820 expect_opcode(0x62, Insn.st(.word, .r1, 0, 0));
821 expect_opcode(0x6a, Insn.st(.half_word, .r1, 0, 0));
822 expect_opcode(0x72, Insn.st(.byte, .r1, 0, 0));
823
824 expect_opcode(0x63, Insn.stx(.word, .r1, 0, .r2));
825 expect_opcode(0x6b, Insn.stx(.half_word, .r1, 0, .r2));
826 expect_opcode(0x73, Insn.stx(.byte, .r1, 0, .r2));
827 expect_opcode(0x7b, Insn.stx(.double_word, .r1, 0, .r2));
828
829 // branch instructions
830 expect_opcode(0x05, Insn.ja(0));
831 expect_opcode(0x15, Insn.jeq(.r1, 0, 0));
832 expect_opcode(0x1d, Insn.jeq(.r1, .r2, 0));
833 expect_opcode(0x25, Insn.jgt(.r1, 0, 0));
834 expect_opcode(0x2d, Insn.jgt(.r1, .r2, 0));
835 expect_opcode(0x35, Insn.jge(.r1, 0, 0));
836 expect_opcode(0x3d, Insn.jge(.r1, .r2, 0));
837 expect_opcode(0xa5, Insn.jlt(.r1, 0, 0));
838 expect_opcode(0xad, Insn.jlt(.r1, .r2, 0));
839 expect_opcode(0xb5, Insn.jle(.r1, 0, 0));
840 expect_opcode(0xbd, Insn.jle(.r1, .r2, 0));
841 expect_opcode(0x45, Insn.jset(.r1, 0, 0));
842 expect_opcode(0x4d, Insn.jset(.r1, .r2, 0));
843 expect_opcode(0x55, Insn.jne(.r1, 0, 0));
844 expect_opcode(0x5d, Insn.jne(.r1, .r2, 0));
845 expect_opcode(0x65, Insn.jsgt(.r1, 0, 0));
846 expect_opcode(0x6d, Insn.jsgt(.r1, .r2, 0));
847 expect_opcode(0x75, Insn.jsge(.r1, 0, 0));
848 expect_opcode(0x7d, Insn.jsge(.r1, .r2, 0));
849 expect_opcode(0xc5, Insn.jslt(.r1, 0, 0));
850 expect_opcode(0xcd, Insn.jslt(.r1, .r2, 0));
851 expect_opcode(0xd5, Insn.jsle(.r1, 0, 0));
852 expect_opcode(0xdd, Insn.jsle(.r1, .r2, 0));
853 expect_opcode(0x85, Insn.call(.unspec));
854 expect_opcode(0x95, Insn.exit());
564855}
565856
566857pub const Cmd = extern enum(usize) {
858 /// Create a map and return a file descriptor that refers to the map. The
859 /// close-on-exec file descriptor flag is automatically enabled for the new
860 /// file descriptor.
861 ///
862 /// uses MapCreateAttr
567863 map_create,
864
865 /// Look up an element by key in a specified map and return its value.
866 ///
867 /// uses MapElemAttr
568868 map_lookup_elem,
869
870 /// Create or update an element (key/value pair) in a specified map.
871 ///
872 /// uses MapElemAttr
569873 map_update_elem,
874
875 /// Look up and delete an element by key in a specified map.
876 ///
877 /// uses MapElemAttr
570878 map_delete_elem,
879
880 /// Look up an element by key in a specified map and return the key of the
881 /// next element.
571882 map_get_next_key,
883
884 /// Verify and load an eBPF program, returning a new file descriptor
885 /// associated with the program. The close-on-exec file descriptor flag
886 /// is automatically enabled for the new file descriptor.
887 ///
888 /// uses ProgLoadAttr
572889 prog_load,
890
891 /// Pin a map or eBPF program to a path within the minimal BPF filesystem
892 ///
893 /// uses ObjAttr
573894 obj_pin,
895
896 /// Get the file descriptor of a BPF object pinned to a certain path
897 ///
898 /// uses ObjAttr
574899 obj_get,
900
901 /// uses ProgAttachAttr
575902 prog_attach,
903
904 /// uses ProgAttachAttr
576905 prog_detach,
906
907 /// uses TestRunAttr
577908 prog_test_run,
909
910 /// uses GetIdAttr
578911 prog_get_next_id,
912
913 /// uses GetIdAttr
579914 map_get_next_id,
915
916 /// uses GetIdAttr
580917 prog_get_fd_by_id,
918
919 /// uses GetIdAttr
581920 map_get_fd_by_id,
921
922 /// uses InfoAttr
582923 obj_get_info_by_fd,
924
925 /// uses QueryAttr
583926 prog_query,
927
928 /// uses RawTracepointAttr
584929 raw_tracepoint_open,
930
931 /// uses BtfLoadAttr
585932 btf_load,
933
934 /// uses GetIdAttr
586935 btf_get_fd_by_id,
936
937 /// uses TaskFdQueryAttr
587938 task_fd_query,
939
940 /// uses MapElemAttr
588941 map_lookup_and_delete_elem,
589942 map_freeze,
943
944 /// uses GetIdAttr
590945 btf_get_next_id,
946
947 /// uses MapBatchAttr
591948 map_lookup_batch,
949
950 /// uses MapBatchAttr
592951 map_lookup_and_delete_batch,
952
953 /// uses MapBatchAttr
593954 map_update_batch,
955
956 /// uses MapBatchAttr
594957 map_delete_batch,
958
959 /// uses LinkCreateAttr
595960 link_create,
961
962 /// uses LinkUpdateAttr
596963 link_update,
964
965 /// uses GetIdAttr
597966 link_get_fd_by_id,
967
968 /// uses GetIdAttr
598969 link_get_next_id,
970
971 /// uses EnableStatsAttr
599972 enable_stats,
973
974 /// uses IterCreateAttr
600975 iter_create,
601976 link_detach,
602977 _,
......@@ -630,42 +1005,138 @@ pub const MapType = extern enum(u32) {
6301005 sk_storage,
6311006 devmap_hash,
6321007 struct_ops,
1008
1009 /// An ordered and shared CPU version of perf_event_array. They have
1010 /// similar semantics:
1011 /// - variable length records
1012 /// - no blocking: when full, reservation fails
1013 /// - memory mappable for ease and speed
1014 /// - epoll notifications for new data, but can busy poll
1015 ///
1016 /// Ringbufs give BPF programs two sets of APIs:
1017 /// - ringbuf_output() allows copy data from one place to a ring
1018 /// buffer, similar to bpf_perf_event_output()
1019 /// - ringbuf_reserve()/ringbuf_commit()/ringbuf_discard() split the
1020 /// process into two steps. First a fixed amount of space is reserved,
1021 /// if that is successful then the program gets a pointer to a chunk of
1022 /// memory and can be submitted with commit() or discarded with
1023 /// discard()
1024 ///
1025 /// ringbuf_output() will incurr an extra memory copy, but allows to submit
1026 /// records of the length that's not known beforehand, and is an easy
1027 /// replacement for perf_event_outptu().
1028 ///
1029 /// ringbuf_reserve() avoids the extra memory copy but requires a known size
1030 /// of memory beforehand.
1031 ///
1032 /// ringbuf_query() allows to query properties of the map, 4 are currently
1033 /// supported:
1034 /// - BPF_RB_AVAIL_DATA: amount of unconsumed data in ringbuf
1035 /// - BPF_RB_RING_SIZE: returns size of ringbuf
1036 /// - BPF_RB_CONS_POS/BPF_RB_PROD_POS returns current logical position
1037 /// of consumer and producer respectively
1038 ///
1039 /// key size: 0
1040 /// value size: 0
1041 /// max entries: size of ringbuf, must be power of 2
6331042 ringbuf,
1043
6341044 _,
6351045};
6361046
6371047pub const ProgType = extern enum(u32) {
6381048 unspec,
1049
1050 /// context type: __sk_buff
6391051 socket_filter,
1052
1053 /// context type: bpf_user_pt_regs_t
6401054 kprobe,
1055
1056 /// context type: __sk_buff
6411057 sched_cls,
1058
1059 /// context type: __sk_buff
6421060 sched_act,
1061
1062 /// context type: u64
6431063 tracepoint,
1064
1065 /// context type: xdp_md
6441066 xdp,
1067
1068 /// context type: bpf_perf_event_data
6451069 perf_event,
1070
1071 /// context type: __sk_buff
6461072 cgroup_skb,
1073
1074 /// context type: bpf_sock
6471075 cgroup_sock,
1076
1077 /// context type: __sk_buff
6481078 lwt_in,
1079
1080 /// context type: __sk_buff
6491081 lwt_out,
1082
1083 /// context type: __sk_buff
6501084 lwt_xmit,
1085
1086 /// context type: bpf_sock_ops
6511087 sock_ops,
1088
1089 /// context type: __sk_buff
6521090 sk_skb,
1091
1092 /// context type: bpf_cgroup_dev_ctx
6531093 cgroup_device,
1094
1095 /// context type: sk_msg_md
6541096 sk_msg,
1097
1098 /// context type: bpf_raw_tracepoint_args
6551099 raw_tracepoint,
1100
1101 /// context type: bpf_sock_addr
6561102 cgroup_sock_addr,
1103
1104 /// context type: __sk_buff
6571105 lwt_seg6local,
1106
1107 /// context type: u32
6581108 lirc_mode2,
1109
1110 /// context type: sk_reuseport_md
6591111 sk_reuseport,
1112
1113 /// context type: __sk_buff
6601114 flow_dissector,
1115
1116 /// context type: bpf_sysctl
6611117 cgroup_sysctl,
1118
1119 /// context type: bpf_raw_tracepoint_args
6621120 raw_tracepoint_writable,
1121
1122 /// context type: bpf_sockopt
6631123 cgroup_sockopt,
1124
1125 /// context type: void *
6641126 tracing,
1127
1128 /// context type: void *
6651129 struct_ops,
1130
1131 /// context type: void *
6661132 ext,
1133
1134 /// context type: void *
6671135 lsm,
1136
1137 /// context type: bpf_sk_lookup
6681138 sk_lookup,
1139 _,
6691140};
6701141
6711142pub const AttachType = extern enum(u32) {
......@@ -715,27 +1186,38 @@ const obj_name_len = 16;
7151186pub const MapCreateAttr = extern struct {
7161187 /// one of MapType
7171188 map_type: u32,
1189
7181190 /// size of key in bytes
7191191 key_size: u32,
1192
7201193 /// size of value in bytes
7211194 value_size: u32,
1195
7221196 /// max number of entries in a map
7231197 max_entries: u32,
1198
7241199 /// .map_create related flags
7251200 map_flags: u32,
1201
7261202 /// fd pointing to the inner map
7271203 inner_map_fd: fd_t,
1204
7281205 /// numa node (effective only if MapCreateFlags.numa_node is set)
7291206 numa_node: u32,
7301207 map_name: [obj_name_len]u8,
1208
7311209 /// ifindex of netdev to create on
7321210 map_ifindex: u32,
1211
7331212 /// fd pointing to a BTF type data
7341213 btf_fd: fd_t,
1214
7351215 /// BTF type_id of the key
7361216 btf_key_type_id: u32,
1217
7371218 /// BTF type_id of the value
7381219 bpf_value_type_id: u32,
1220
7391221 /// BTF type_id of a kernel struct stored as the map value
7401222 btf_vmlinux_value_type_id: u32,
7411223};
......@@ -755,10 +1237,12 @@ pub const MapElemAttr = extern struct {
7551237pub const MapBatchAttr = extern struct {
7561238 /// start batch, NULL to start from beginning
7571239 in_batch: u64,
1240
7581241 /// output: next start batch
7591242 out_batch: u64,
7601243 keys: u64,
7611244 values: u64,
1245
7621246 /// input/output:
7631247 /// input: # of key/value elements
7641248 /// output: # of filled elements
......@@ -775,35 +1259,49 @@ pub const ProgLoadAttr = extern struct {
7751259 insn_cnt: u32,
7761260 insns: u64,
7771261 license: u64,
1262
7781263 /// verbosity level of verifier
7791264 log_level: u32,
1265
7801266 /// size of user buffer
7811267 log_size: u32,
1268
7821269 /// user supplied buffer
7831270 log_buf: u64,
1271
7841272 /// not used
7851273 kern_version: u32,
7861274 prog_flags: u32,
7871275 prog_name: [obj_name_len]u8,
788 /// ifindex of netdev to prep for. For some prog types expected attach
789 /// type must be known at load time to verify attach type specific parts
790 /// of prog (context accesses, allowed helpers, etc).
1276
1277 /// ifindex of netdev to prep for.
7911278 prog_ifindex: u32,
1279
1280 /// For some prog types expected attach type must be known at load time to
1281 /// verify attach type specific parts of prog (context accesses, allowed
1282 /// helpers, etc).
7921283 expected_attach_type: u32,
1284
7931285 /// fd pointing to BTF type data
7941286 prog_btf_fd: fd_t,
1287
7951288 /// userspace bpf_func_info size
7961289 func_info_rec_size: u32,
7971290 func_info: u64,
1291
7981292 /// number of bpf_func_info records
7991293 func_info_cnt: u32,
1294
8001295 /// userspace bpf_line_info size
8011296 line_info_rec_size: u32,
8021297 line_info: u64,
1298
8031299 /// number of bpf_line_info records
8041300 line_info_cnt: u32,
1301
8051302 /// in-kernel BTF type id to attach to
8061303 attact_btf_id: u32,
1304
8071305 /// 0 to attach to vmlinux
8081306 attach_prog_id: u32,
8091307};
......@@ -819,29 +1317,36 @@ pub const ObjAttr = extern struct {
8191317pub const ProgAttachAttr = extern struct {
8201318 /// container object to attach to
8211319 target_fd: fd_t,
1320
8221321 /// eBPF program to attach
8231322 attach_bpf_fd: fd_t,
1323
8241324 attach_type: u32,
8251325 attach_flags: u32,
1326
8261327 // TODO: BPF_F_REPLACE flags
8271328 /// previously attached eBPF program to replace if .replace is used
8281329 replace_bpf_fd: fd_t,
8291330};
8301331
8311332/// struct used by Cmd.prog_test_run command
832pub const TestAttr = extern struct {
1333pub const TestRunAttr = extern struct {
8331334 prog_fd: fd_t,
8341335 retval: u32,
1336
8351337 /// input: len of data_in
8361338 data_size_in: u32,
1339
8371340 /// input/output: len of data_out. returns ENOSPC if data_out is too small.
8381341 data_size_out: u32,
8391342 data_in: u64,
8401343 data_out: u64,
8411344 repeat: u32,
8421345 duration: u32,
1346
8431347 /// input: len of ctx_in
8441348 ctx_size_in: u32,
1349
8451350 /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small.
8461351 ctx_size_out: u32,
8471352 ctx_in: u64,
......@@ -894,26 +1399,35 @@ pub const BtfLoadAttr = extern struct {
8941399 btf_log_level: u32,
8951400};
8961401
1402/// struct used by Cmd.task_fd_query
8971403pub const TaskFdQueryAttr = extern struct {
8981404 /// input: pid
8991405 pid: pid_t,
1406
9001407 /// input: fd
9011408 fd: fd_t,
1409
9021410 /// input: flags
9031411 flags: u32,
1412
9041413 /// input/output: buf len
9051414 buf_len: u32,
1415
9061416 /// input/output:
9071417 /// tp_name for tracepoint
9081418 /// symbol for kprobe
9091419 /// filename for uprobe
9101420 buf: u64,
1421
9111422 /// output: prod_id
9121423 prog_id: u32,
1424
9131425 /// output: BPF_FD_TYPE
9141426 fd_type: u32,
1427
9151428 /// output: probe_offset
9161429 probe_offset: u64,
1430
9171431 /// output: probe_addr
9181432 probe_addr: u64,
9191433};
......@@ -922,9 +1436,11 @@ pub const TaskFdQueryAttr = extern struct {
9221436pub const LinkCreateAttr = extern struct {
9231437 /// eBPF program to attach
9241438 prog_fd: fd_t,
1439
9251440 /// object to attach to
9261441 target_fd: fd_t,
9271442 attach_type: u32,
1443
9281444 /// extra flags
9291445 flags: u32,
9301446};
......@@ -932,10 +1448,13 @@ pub const LinkCreateAttr = extern struct {
9321448/// struct used by Cmd.link_update command
9331449pub const LinkUpdateAttr = extern struct {
9341450 link_fd: fd_t,
1451
9351452 /// new program to update link with
9361453 new_prog_fd: fd_t,
1454
9371455 /// extra flags
9381456 flags: u32,
1457
9391458 /// expected link's program fd, it is specified only if BPF_F_REPLACE is
9401459 /// set in flags
9411460 old_prog_fd: fd_t,
......@@ -952,6 +1471,7 @@ pub const IterCreateAttr = extern struct {
9521471 flags: u32,
9531472};
9541473
1474/// Mega struct that is passed to the bpf() syscall
9551475pub const Attr = extern union {
9561476 map_create: MapCreateAttr,
9571477 map_elem: MapElemAttr,
......@@ -971,3 +1491,176 @@ pub const Attr = extern union {
9711491 enable_stats: EnableStatsAttr,
9721492 iter_create: IterCreateAttr,
9731493};
1494
1495pub const Log = struct {
1496 level: u32,
1497 buf: []u8,
1498};
1499
1500pub fn map_create(map_type: MapType, key_size: u32, value_size: u32, max_entries: u32) !fd_t {
1501 var attr = Attr{
1502 .map_create = std.mem.zeroes(MapCreateAttr),
1503 };
1504
1505 attr.map_create.map_type = @enumToInt(map_type);
1506 attr.map_create.key_size = key_size;
1507 attr.map_create.value_size = value_size;
1508 attr.map_create.max_entries = max_entries;
1509
1510 const rc = bpf(.map_create, &attr, @sizeOf(MapCreateAttr));
1511 return switch (errno(rc)) {
1512 0 => @intCast(fd_t, rc),
1513 EINVAL => error.MapTypeOrAttrInvalid,
1514 ENOMEM => error.SystemResources,
1515 EPERM => error.AccessDenied,
1516 else => |err| unexpectedErrno(rc),
1517 };
1518}
1519
1520test "map_create" {
1521 const map = try map_create(.hash, 4, 4, 32);
1522 defer std.os.close(map);
1523}
1524
1525pub fn map_lookup_elem(fd: fd_t, key: []const u8, value: []u8) !void {
1526 var attr = Attr{
1527 .map_elem = std.mem.zeroes(MapElemAttr),
1528 };
1529
1530 attr.map_elem.map_fd = fd;
1531 attr.map_elem.key = @ptrToInt(key.ptr);
1532 attr.map_elem.result.value = @ptrToInt(value.ptr);
1533
1534 const rc = bpf(.map_lookup_elem, &attr, @sizeOf(MapElemAttr));
1535 switch (errno(rc)) {
1536 0 => return,
1537 EBADF => return error.BadFd,
1538 EFAULT => unreachable,
1539 EINVAL => return error.FieldInAttrNeedsZeroing,
1540 ENOENT => return error.NotFound,
1541 EPERM => return error.AccessDenied,
1542 else => |err| return unexpectedErrno(rc),
1543 }
1544}
1545
1546pub fn map_update_elem(fd: fd_t, key: []const u8, value: []const u8, flags: u64) !void {
1547 var attr = Attr{
1548 .map_elem = std.mem.zeroes(MapElemAttr),
1549 };
1550
1551 attr.map_elem.map_fd = fd;
1552 attr.map_elem.key = @ptrToInt(key.ptr);
1553 attr.map_elem.result = .{ .value = @ptrToInt(value.ptr) };
1554 attr.map_elem.flags = flags;
1555
1556 const rc = bpf(.map_update_elem, &attr, @sizeOf(MapElemAttr));
1557 switch (errno(rc)) {
1558 0 => return,
1559 E2BIG => return error.ReachedMaxEntries,
1560 EBADF => return error.BadFd,
1561 EFAULT => unreachable,
1562 EINVAL => return error.FieldInAttrNeedsZeroing,
1563 ENOMEM => return error.SystemResources,
1564 EPERM => return error.AccessDenied,
1565 else => |err| return unexpectedErrno(err),
1566 }
1567}
1568
1569pub fn map_delete_elem(fd: fd_t, key: []const u8) !void {
1570 var attr = Attr{
1571 .map_elem = std.mem.zeroes(MapElemAttr),
1572 };
1573
1574 attr.map_elem.map_fd = fd;
1575 attr.map_elem.key = @ptrToInt(key.ptr);
1576
1577 const rc = bpf(.map_delete_elem, &attr, @sizeOf(MapElemAttr));
1578 switch (errno(rc)) {
1579 0 => return,
1580 EBADF => return error.BadFd,
1581 EFAULT => unreachable,
1582 EINVAL => return error.FieldInAttrNeedsZeroing,
1583 ENOENT => return error.NotFound,
1584 EPERM => return error.AccessDenied,
1585 else => |err| return unexpectedErrno(err),
1586 }
1587}
1588
1589test "map lookup, update, and delete" {
1590 const key_size = 4;
1591 const value_size = 4;
1592 const map = try map_create(.hash, key_size, value_size, 1);
1593 defer std.os.close(map);
1594
1595 const key = std.mem.zeroes([key_size]u8);
1596 var value = std.mem.zeroes([value_size]u8);
1597
1598 // fails looking up value that doesn't exist
1599 expectError(error.NotFound, map_lookup_elem(map, &key, &value));
1600
1601 // succeed at updating and looking up element
1602 try map_update_elem(map, &key, &value, 0);
1603 try map_lookup_elem(map, &key, &value);
1604
1605 // fails inserting more than max entries
1606 const second_key = [key_size]u8{ 0, 0, 0, 1 };
1607 expectError(error.ReachedMaxEntries, map_update_elem(map, &second_key, &value, 0));
1608
1609 // succeed at deleting an existing elem
1610 try map_delete_elem(map, &key);
1611 expectError(error.NotFound, map_lookup_elem(map, &key, &value));
1612
1613 // fail at deleting a non-existing elem
1614 expectError(error.NotFound, map_delete_elem(map, &key));
1615}
1616
1617pub fn prog_load(
1618 prog_type: ProgType,
1619 insns: []const Insn,
1620 log: ?*Log,
1621 license: []const u8,
1622 kern_version: u32,
1623) !fd_t {
1624 var attr = Attr{
1625 .prog_load = std.mem.zeroes(ProgLoadAttr),
1626 };
1627
1628 attr.prog_load.prog_type = @enumToInt(prog_type);
1629 attr.prog_load.insns = @ptrToInt(insns.ptr);
1630 attr.prog_load.insn_cnt = @intCast(u32, insns.len);
1631 attr.prog_load.license = @ptrToInt(license.ptr);
1632 attr.prog_load.kern_version = kern_version;
1633
1634 if (log) |l| {
1635 attr.prog_load.log_buf = @ptrToInt(l.buf.ptr);
1636 attr.prog_load.log_size = @intCast(u32, l.buf.len);
1637 attr.prog_load.log_level = l.level;
1638 }
1639
1640 const rc = bpf(.prog_load, &attr, @sizeOf(ProgLoadAttr));
1641 return switch (errno(rc)) {
1642 0 => @intCast(fd_t, rc),
1643 EACCES => error.UnsafeProgram,
1644 EFAULT => unreachable,
1645 EINVAL => error.InvalidProgram,
1646 EPERM => error.AccessDenied,
1647 else => |err| unexpectedErrno(err),
1648 };
1649}
1650
1651test "prog_load" {
1652 // this should fail because it does not set r0 before exiting
1653 const bad_prog = [_]Insn{
1654 Insn.exit(),
1655 };
1656
1657 const good_prog = [_]Insn{
1658 Insn.mov(.r0, 0),
1659 Insn.exit(),
1660 };
1661
1662 const prog = try prog_load(.socket_filter, &good_prog, null, "MIT", 0);
1663 defer std.os.close(prog);
1664
1665 expectError(error.UnsafeProgram, prog_load(.socket_filter, &bad_prog, null, "MIT", 0));
1666}
lib/std/os/linux/bpf/btf.zig created+156
......@@ -0,0 +1,156 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const magic = 0xeb9f;
7const version = 1;
8
9pub const ext = @import("ext.zig");
10
11/// All offsets are in bytes relative to the end of this header
12pub const Header = packed struct {
13 magic: u16,
14 version: u8,
15 flags: u8,
16 hdr_len: u32,
17
18 /// offset of type section
19 type_off: u32,
20
21 /// length of type section
22 type_len: u32,
23
24 /// offset of string section
25 str_off: u32,
26
27 /// length of string section
28 str_len: u32,
29};
30
31/// Max number of type identifiers
32pub const max_type = 0xfffff;
33
34/// Max offset into string section
35pub const max_name_offset = 0xffffff;
36
37/// Max number of struct/union/enum member of func args
38pub const max_vlen = 0xffff;
39
40pub const Type = packed struct {
41 name_off: u32,
42 info: packed struct {
43 /// number of struct's members
44 vlen: u16,
45
46 unused_1: u8,
47 kind: Kind,
48 unused_2: u3,
49
50 /// used by Struct, Union, and Fwd
51 kind_flag: bool,
52 },
53
54 /// size is used by Int, Enum, Struct, Union, and DataSec, it tells the size
55 /// of the type it is describing
56 ///
57 /// type is used by Ptr, Typedef, Volatile, Const, Restrict, Func,
58 /// FuncProto, and Var. It is a type_id referring to another type
59 size_type: union { size: u32, typ: u32 },
60};
61
62/// For some kinds, Type is immediately followed by extra data
63pub const Kind = enum(u4) {
64 unknown,
65 int,
66 ptr,
67 array,
68 structure,
69 kind_union,
70 enumeration,
71 fwd,
72 typedef,
73 kind_volatile,
74 constant,
75 restrict,
76 func,
77 funcProto,
78 variable,
79 dataSec,
80};
81
82/// Int kind is followed by this struct
83pub const IntInfo = packed struct {
84 bits: u8,
85 unused: u8,
86 offset: u8,
87 encoding: enum(u4) {
88 signed = 1 << 0,
89 char = 1 << 1,
90 boolean = 1 << 2,
91 },
92};
93
94test "IntInfo is 32 bits" {
95 std.testing.expectEqual(@bitSizeOf(IntInfo), 32);
96}
97
98/// Enum kind is followed by this struct
99pub const Enum = packed struct {
100 name_off: u32,
101 val: i32,
102};
103
104/// Array kind is followd by this struct
105pub const Array = packed struct {
106 typ: u32,
107 index_type: u32,
108 nelems: u32,
109};
110
111/// Struct and Union kinds are followed by multiple Member structs. The exact
112/// number is stored in vlen
113pub const Member = packed struct {
114 name_off: u32,
115 typ: u32,
116
117 /// if the kind_flag is set, offset contains both member bitfield size and
118 /// bit offset, the bitfield size is set for bitfield members. If the type
119 /// info kind_flag is not set, the offset contains only bit offset
120 offset: packed struct {
121 bit: u24,
122 bitfield_size: u8,
123 },
124};
125
126/// FuncProto is followed by multiple Params, the exact number is stored in vlen
127pub const Param = packed struct {
128 name_off: u32,
129 typ: u32,
130};
131
132pub const VarLinkage = enum {
133 static,
134 global_allocated,
135 global_extern,
136};
137
138pub const FuncLinkage = enum {
139 static,
140 global,
141 external,
142};
143
144/// Var kind is followd by a single Var struct to describe additional
145/// information related to the variable such as its linkage
146pub const Var = packed struct {
147 linkage: u32,
148};
149
150/// Datasec kind is followed by multible VarSecInfo to describe all Var kind
151/// types it contains along with it's in-section offset as well as size.
152pub const VarSecInfo = packed struct {
153 typ: u32,
154 offset: u32,
155 size: u32,
156};
lib/std/os/linux/bpf/btf_ext.zig created+24
......@@ -0,0 +1,24 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6pub const Header = packed struct {
7 magic: u16,
8 version: u8,
9 flags: u8,
10 hdr_len: u32,
11
12 /// All offsets are in bytes relative to the end of this header
13 func_info_off: u32,
14 func_info_len: u32,
15 line_info_off: u32,
16 line_info_len: u32,
17};
18
19pub const InfoSec = packed struct {
20 sec_name_off: u32,
21 num_info: u32,
22 // TODO: communicate that there is data here
23 //data: [0]u8,
24};
lib/std/os/linux/bpf/helpers.zig created+157
......@@ -0,0 +1,157 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const kern = @import("kern.zig");
7
8// in BPF, all the helper calls
9// TODO: when https://github.com/ziglang/zig/issues/1717 is here, make a nice
10// function that uses the Helper enum
11//
12// Note, these function signatures were created from documentation found in
13// '/usr/include/linux/bpf.h'
14pub const map_lookup_elem = @intToPtr(fn (map: *const kern.MapDef, key: ?*const c_void) ?*c_void, 1);
15pub const map_update_elem = @intToPtr(fn (map: *const kern.MapDef, key: ?*const c_void, value: ?*const c_void, flags: u64) c_long, 2);
16pub const map_delete_elem = @intToPtr(fn (map: *const kern.MapDef, key: ?*const c_void) c_long, 3);
17pub const probe_read = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 4);
18pub const ktime_get_ns = @intToPtr(fn () u64, 5);
19pub const trace_printk = @intToPtr(fn (fmt: [*:0]const u8, fmt_size: u32, arg1: u64, arg2: u64, arg3: u64) c_long, 6);
20pub const get_prandom_u32 = @intToPtr(fn () u32, 7);
21pub const get_smp_processor_id = @intToPtr(fn () u32, 8);
22pub const skb_store_bytes = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, from: ?*const c_void, len: u32, flags: u64) c_long, 9);
23pub const l3_csum_replace = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, from: u64, to: u64, size: u64) c_long, 10);
24pub const l4_csum_replace = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, from: u64, to: u64, flags: u64) c_long, 11);
25pub const tail_call = @intToPtr(fn (ctx: ?*c_void, prog_array_map: *const kern.MapDef, index: u32) c_long, 12);
26pub const clone_redirect = @intToPtr(fn (skb: *kern.SkBuff, ifindex: u32, flags: u64) c_long, 13);
27pub const get_current_pid_tgid = @intToPtr(fn () u64, 14);
28pub const get_current_uid_gid = @intToPtr(fn () u64, 15);
29pub const get_current_comm = @intToPtr(fn (buf: ?*c_void, size_of_buf: u32) c_long, 16);
30pub const get_cgroup_classid = @intToPtr(fn (skb: *kern.SkBuff) u32, 17);
31// Note vlan_proto is big endian
32pub const skb_vlan_push = @intToPtr(fn (skb: *kern.SkBuff, vlan_proto: u16, vlan_tci: u16) c_long, 18);
33pub const skb_vlan_pop = @intToPtr(fn (skb: *kern.SkBuff) c_long, 19);
34pub const skb_get_tunnel_key = @intToPtr(fn (skb: *kern.SkBuff, key: *kern.TunnelKey, size: u32, flags: u64) c_long, 20);
35pub const skb_set_tunnel_key = @intToPtr(fn (skb: *kern.SkBuff, key: *kern.TunnelKey, size: u32, flags: u64) c_long, 21);
36pub const perf_event_read = @intToPtr(fn (map: *const kern.MapDef, flags: u64) u64, 22);
37pub const redirect = @intToPtr(fn (ifindex: u32, flags: u64) c_long, 23);
38pub const get_route_realm = @intToPtr(fn (skb: *kern.SkBuff) u32, 24);
39pub const perf_event_output = @intToPtr(fn (ctx: ?*c_void, map: *const kern.MapDef, flags: u64, data: ?*c_void, size: u64) c_long, 25);
40pub const skb_load_bytes = @intToPtr(fn (skb: ?*c_void, offset: u32, to: ?*c_void, len: u32) c_long, 26);
41pub const get_stackid = @intToPtr(fn (ctx: ?*c_void, map: *const kern.MapDef, flags: u64) c_long, 27);
42// from and to point to __be32
43pub const csum_diff = @intToPtr(fn (from: *u32, from_size: u32, to: *u32, to_size: u32, seed: u32) i64, 28);
44pub const skb_get_tunnel_opt = @intToPtr(fn (skb: *kern.SkBuff, opt: ?*c_void, size: u32) c_long, 29);
45pub const skb_set_tunnel_opt = @intToPtr(fn (skb: *kern.SkBuff, opt: ?*c_void, size: u32) c_long, 30);
46// proto is __be16
47pub const skb_change_proto = @intToPtr(fn (skb: *kern.SkBuff, proto: u16, flags: u64) c_long, 31);
48pub const skb_change_type = @intToPtr(fn (skb: *kern.SkBuff, skb_type: u32) c_long, 32);
49pub const skb_under_cgroup = @intToPtr(fn (skb: *kern.SkBuff, map: ?*const c_void, index: u32) c_long, 33);
50pub const get_hash_recalc = @intToPtr(fn (skb: *kern.SkBuff) u32, 34);
51pub const get_current_task = @intToPtr(fn () u64, 35);
52pub const probe_write_user = @intToPtr(fn (dst: ?*c_void, src: ?*const c_void, len: u32) c_long, 36);
53pub const current_task_under_cgroup = @intToPtr(fn (map: *const kern.MapDef, index: u32) c_long, 37);
54pub const skb_change_tail = @intToPtr(fn (skb: *kern.SkBuff, len: u32, flags: u64) c_long, 38);
55pub const skb_pull_data = @intToPtr(fn (skb: *kern.SkBuff, len: u32) c_long, 39);
56pub const csum_update = @intToPtr(fn (skb: *kern.SkBuff, csum: u32) i64, 40);
57pub const set_hash_invalid = @intToPtr(fn (skb: *kern.SkBuff) void, 41);
58pub const get_numa_node_id = @intToPtr(fn () c_long, 42);
59pub const skb_change_head = @intToPtr(fn (skb: *kern.SkBuff, len: u32, flags: u64) c_long, 43);
60pub const xdp_adjust_head = @intToPtr(fn (xdp_md: *kern.XdpMd, delta: c_int) c_long, 44);
61pub const probe_read_str = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 45);
62pub const get_socket_cookie = @intToPtr(fn (ctx: ?*c_void) u64, 46);
63pub const get_socket_uid = @intToPtr(fn (skb: *kern.SkBuff) u32, 47);
64pub const set_hash = @intToPtr(fn (skb: *kern.SkBuff, hash: u32) c_long, 48);
65pub const setsockopt = @intToPtr(fn (bpf_socket: *kern.SockOps, level: c_int, optname: c_int, optval: ?*c_void, optlen: c_int) c_long, 49);
66pub const skb_adjust_room = @intToPtr(fn (skb: *kern.SkBuff, len_diff: i32, mode: u32, flags: u64) c_long, 50);
67pub const redirect_map = @intToPtr(fn (map: *const kern.MapDef, key: u32, flags: u64) c_long, 51);
68pub const sk_redirect_map = @intToPtr(fn (skb: *kern.SkBuff, map: *const kern.MapDef, key: u32, flags: u64) c_long, 52);
69pub const sock_map_update = @intToPtr(fn (skops: *kern.SockOps, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 53);
70pub const xdp_adjust_meta = @intToPtr(fn (xdp_md: *kern.XdpMd, delta: c_int) c_long, 54);
71pub const perf_event_read_value = @intToPtr(fn (map: *const kern.MapDef, flags: u64, buf: *kern.PerfEventValue, buf_size: u32) c_long, 55);
72pub const perf_prog_read_value = @intToPtr(fn (ctx: *kern.PerfEventData, buf: *kern.PerfEventValue, buf_size: u32) c_long, 56);
73pub const getsockopt = @intToPtr(fn (bpf_socket: ?*c_void, level: c_int, optname: c_int, optval: ?*c_void, optlen: c_int) c_long, 57);
74pub const override_return = @intToPtr(fn (regs: *PtRegs, rc: u64) c_long, 58);
75pub const sock_ops_cb_flags_set = @intToPtr(fn (bpf_sock: *kern.SockOps, argval: c_int) c_long, 59);
76pub const msg_redirect_map = @intToPtr(fn (msg: *kern.SkMsgMd, map: *const kern.MapDef, key: u32, flags: u64) c_long, 60);
77pub const msg_apply_bytes = @intToPtr(fn (msg: *kern.SkMsgMd, bytes: u32) c_long, 61);
78pub const msg_cork_bytes = @intToPtr(fn (msg: *kern.SkMsgMd, bytes: u32) c_long, 62);
79pub const msg_pull_data = @intToPtr(fn (msg: *kern.SkMsgMd, start: u32, end: u32, flags: u64) c_long, 63);
80pub const bind = @intToPtr(fn (ctx: *kern.BpfSockAddr, addr: *kern.SockAddr, addr_len: c_int) c_long, 64);
81pub const xdp_adjust_tail = @intToPtr(fn (xdp_md: *kern.XdpMd, delta: c_int) c_long, 65);
82pub const skb_get_xfrm_state = @intToPtr(fn (skb: *kern.SkBuff, index: u32, xfrm_state: *kern.XfrmState, size: u32, flags: u64) c_long, 66);
83pub const get_stack = @intToPtr(fn (ctx: ?*c_void, buf: ?*c_void, size: u32, flags: u64) c_long, 67);
84pub const skb_load_bytes_relative = @intToPtr(fn (skb: ?*const c_void, offset: u32, to: ?*c_void, len: u32, start_header: u32) c_long, 68);
85pub const fib_lookup = @intToPtr(fn (ctx: ?*c_void, params: *kern.FibLookup, plen: c_int, flags: u32) c_long, 69);
86pub const sock_hash_update = @intToPtr(fn (skops: *kern.SockOps, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 70);
87pub const msg_redirect_hash = @intToPtr(fn (msg: *kern.SkMsgMd, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 71);
88pub const sk_redirect_hash = @intToPtr(fn (skb: *kern.SkBuff, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 72);
89pub const lwt_push_encap = @intToPtr(fn (skb: *kern.SkBuff, typ: u32, hdr: ?*c_void, len: u32) c_long, 73);
90pub const lwt_seg6_store_bytes = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, from: ?*const c_void, len: u32) c_long, 74);
91pub const lwt_seg6_adjust_srh = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, delta: i32) c_long, 75);
92pub const lwt_seg6_action = @intToPtr(fn (skb: *kern.SkBuff, action: u32, param: ?*c_void, param_len: u32) c_long, 76);
93pub const rc_repeat = @intToPtr(fn (ctx: ?*c_void) c_long, 77);
94pub const rc_keydown = @intToPtr(fn (ctx: ?*c_void, protocol: u32, scancode: u64, toggle: u32) c_long, 78);
95pub const skb_cgroup_id = @intToPtr(fn (skb: *kern.SkBuff) u64, 79);
96pub const get_current_cgroup_id = @intToPtr(fn () u64, 80);
97pub const get_local_storage = @intToPtr(fn (map: ?*c_void, flags: u64) ?*c_void, 81);
98pub const sk_select_reuseport = @intToPtr(fn (reuse: *kern.SkReusePortMd, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 82);
99pub const skb_ancestor_cgroup_id = @intToPtr(fn (skb: *kern.SkBuff, ancestor_level: c_int) u64, 83);
100pub const sk_lookup_tcp = @intToPtr(fn (ctx: ?*c_void, tuple: *kern.SockTuple, tuple_size: u32, netns: u64, flags: u64) ?*kern.Sock, 84);
101pub const sk_lookup_udp = @intToPtr(fn (ctx: ?*c_void, tuple: *kern.SockTuple, tuple_size: u32, netns: u64, flags: u64) ?*kern.Sock, 85);
102pub const sk_release = @intToPtr(fn (sock: *kern.Sock) c_long, 86);
103pub const map_push_elem = @intToPtr(fn (map: *const kern.MapDef, value: ?*const c_void, flags: u64) c_long, 87);
104pub const map_pop_elem = @intToPtr(fn (map: *const kern.MapDef, value: ?*c_void) c_long, 88);
105pub const map_peek_elem = @intToPtr(fn (map: *const kern.MapDef, value: ?*c_void) c_long, 89);
106pub const msg_push_data = @intToPtr(fn (msg: *kern.SkMsgMd, start: u32, len: u32, flags: u64) c_long, 90);
107pub const msg_pop_data = @intToPtr(fn (msg: *kern.SkMsgMd, start: u32, len: u32, flags: u64) c_long, 91);
108pub const rc_pointer_rel = @intToPtr(fn (ctx: ?*c_void, rel_x: i32, rel_y: i32) c_long, 92);
109pub const spin_lock = @intToPtr(fn (lock: *kern.SpinLock) c_long, 93);
110pub const spin_unlock = @intToPtr(fn (lock: *kern.SpinLock) c_long, 94);
111pub const sk_fullsock = @intToPtr(fn (sk: *kern.Sock) ?*SkFullSock, 95);
112pub const tcp_sock = @intToPtr(fn (sk: *kern.Sock) ?*kern.TcpSock, 96);
113pub const skb_ecn_set_ce = @intToPtr(fn (skb: *kern.SkBuff) c_long, 97);
114pub const get_listener_sock = @intToPtr(fn (sk: *kern.Sock) ?*kern.Sock, 98);
115pub const skc_lookup_tcp = @intToPtr(fn (ctx: ?*c_void, tuple: *kern.SockTuple, tuple_size: u32, netns: u64, flags: u64) ?*kern.Sock, 99);
116pub const tcp_check_syncookie = @intToPtr(fn (sk: *kern.Sock, iph: ?*c_void, iph_len: u32, th: *TcpHdr, th_len: u32) c_long, 100);
117pub const sysctl_get_name = @intToPtr(fn (ctx: *kern.SysCtl, buf: ?*u8, buf_len: c_ulong, flags: u64) c_long, 101);
118pub const sysctl_get_current_value = @intToPtr(fn (ctx: *kern.SysCtl, buf: ?*u8, buf_len: c_ulong) c_long, 102);
119pub const sysctl_get_new_value = @intToPtr(fn (ctx: *kern.SysCtl, buf: ?*u8, buf_len: c_ulong) c_long, 103);
120pub const sysctl_set_new_value = @intToPtr(fn (ctx: *kern.SysCtl, buf: ?*const u8, buf_len: c_ulong) c_long, 104);
121pub const strtol = @intToPtr(fn (buf: *const u8, buf_len: c_ulong, flags: u64, res: *c_long) c_long, 105);
122pub const strtoul = @intToPtr(fn (buf: *const u8, buf_len: c_ulong, flags: u64, res: *c_ulong) c_long, 106);
123pub const sk_storage_get = @intToPtr(fn (map: *const kern.MapDef, sk: *kern.Sock, value: ?*c_void, flags: u64) ?*c_void, 107);
124pub const sk_storage_delete = @intToPtr(fn (map: *const kern.MapDef, sk: *kern.Sock) c_long, 108);
125pub const send_signal = @intToPtr(fn (sig: u32) c_long, 109);
126pub const tcp_gen_syncookie = @intToPtr(fn (sk: *kern.Sock, iph: ?*c_void, iph_len: u32, th: *TcpHdr, th_len: u32) i64, 110);
127pub const skb_output = @intToPtr(fn (ctx: ?*c_void, map: *const kern.MapDef, flags: u64, data: ?*c_void, size: u64) c_long, 111);
128pub const probe_read_user = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 112);
129pub const probe_read_kernel = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 113);
130pub const probe_read_user_str = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 114);
131pub const probe_read_kernel_str = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 115);
132pub const tcp_send_ack = @intToPtr(fn (tp: ?*c_void, rcv_nxt: u32) c_long, 116);
133pub const send_signal_thread = @intToPtr(fn (sig: u32) c_long, 117);
134pub const jiffies64 = @intToPtr(fn () u64, 118);
135pub const read_branch_records = @intToPtr(fn (ctx: *kern.PerfEventData, buf: ?*c_void, size: u32, flags: u64) c_long, 119);
136pub const get_ns_current_pid_tgid = @intToPtr(fn (dev: u64, ino: u64, nsdata: *kern.PidNsInfo, size: u32) c_long, 120);
137pub const xdp_output = @intToPtr(fn (ctx: ?*c_void, map: *const kern.MapDef, flags: u64, data: ?*c_void, size: u64) c_long, 121);
138pub const get_netns_cookie = @intToPtr(fn (ctx: ?*c_void) u64, 122);
139pub const get_current_ancestor_cgroup_id = @intToPtr(fn (ancestor_level: c_int) u64, 123);
140pub const sk_assign = @intToPtr(fn (skb: *kern.SkBuff, sk: *kern.Sock, flags: u64) c_long, 124);
141pub const ktime_get_boot_ns = @intToPtr(fn () u64, 125);
142pub const seq_printf = @intToPtr(fn (m: *kern.SeqFile, fmt: ?*const u8, fmt_size: u32, data: ?*const c_void, data_len: u32) c_long, 126);
143pub const seq_write = @intToPtr(fn (m: *kern.SeqFile, data: ?*const u8, len: u32) c_long, 127);
144pub const sk_cgroup_id = @intToPtr(fn (sk: *kern.BpfSock) u64, 128);
145pub const sk_ancestor_cgroup_id = @intToPtr(fn (sk: *kern.BpfSock, ancestor_level: c_long) u64, 129);
146pub const ringbuf_output = @intToPtr(fn (ringbuf: ?*c_void, data: ?*c_void, size: u64, flags: u64) ?*c_void, 130);
147pub const ringbuf_reserve = @intToPtr(fn (ringbuf: ?*c_void, size: u64, flags: u64) ?*c_void, 131);
148pub const ringbuf_submit = @intToPtr(fn (data: ?*c_void, flags: u64) void, 132);
149pub const ringbuf_discard = @intToPtr(fn (data: ?*c_void, flags: u64) void, 133);
150pub const ringbuf_query = @intToPtr(fn (ringbuf: ?*c_void, flags: u64) u64, 134);
151pub const csum_level = @intToPtr(fn (skb: *kern.SkBuff, level: u64) c_long, 134);
152pub const skc_to_tcp6_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.Tcp6Sock, 135);
153pub const skc_to_tcp_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.TcpSock, 136);
154pub const skc_to_tcp_timewait_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.TcpTimewaitSock, 137);
155pub const skc_to_tcp_request_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.TcpRequestSock, 138);
156pub const skc_to_udp6_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.Udp6Sock, 139);
157pub const get_task_stack = @intToPtr(fn (task: ?*c_void, buf: ?*c_void, size: u32, flags: u64) c_long, 140);
lib/std/os/linux/bpf/kern.zig created+39
......@@ -0,0 +1,39 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6const std = @import("../../../std.zig");
7
8const in_bpf_program = switch (std.builtin.arch) {
9 .bpfel, .bpfeb => true,
10 else => false,
11};
12
13pub const helpers = if (in_bpf_program) @import("helpers.zig") else struct {};
14
15pub const BpfSock = @Type(.Opaque);
16pub const BpfSockAddr = @Type(.Opaque);
17pub const FibLookup = @Type(.Opaque);
18pub const MapDef = @Type(.Opaque);
19pub const PerfEventData = @Type(.Opaque);
20pub const PerfEventValue = @Type(.Opaque);
21pub const PidNsInfo = @Type(.Opaque);
22pub const SeqFile = @Type(.Opaque);
23pub const SkBuff = @Type(.Opaque);
24pub const SkMsgMd = @Type(.Opaque);
25pub const SkReusePortMd = @Type(.Opaque);
26pub const Sock = @Type(.Opaque);
27pub const SockAddr = @Type(.Opaque);
28pub const SockOps = @Type(.Opaque);
29pub const SockTuple = @Type(.Opaque);
30pub const SpinLock = @Type(.Opaque);
31pub const SysCtl = @Type(.Opaque);
32pub const Tcp6Sock = @Type(.Opaque);
33pub const TcpRequestSock = @Type(.Opaque);
34pub const TcpSock = @Type(.Opaque);
35pub const TcpTimewaitSock = @Type(.Opaque);
36pub const TunnelKey = @Type(.Opaque);
37pub const Udp6Sock = @Type(.Opaque);
38pub const XdpMd = @Type(.Opaque);
39pub const XfrmState = @Type(.Opaque);
lib/std/os/linux/powerpc64.zig created+127
......@@ -0,0 +1,127 @@
1usingnamespace @import("../bits.zig");
2
3pub fn syscall0(number: SYS) usize {
4 return asm volatile (
5 \\ sc
6 \\ bns+ 1f
7 \\ neg 3, 3
8 \\ 1:
9 : [ret] "={r3}" (-> usize)
10 : [number] "{r0}" (@enumToInt(number))
11 : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
12 );
13}
14
15pub fn syscall1(number: SYS, arg1: usize) usize {
16 return asm volatile (
17 \\ sc
18 \\ bns+ 1f
19 \\ neg 3, 3
20 \\ 1:
21 : [ret] "={r3}" (-> usize)
22 : [number] "{r0}" (@enumToInt(number)),
23 [arg1] "{r3}" (arg1)
24 : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
25 );
26}
27
28pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize {
29 return asm volatile (
30 \\ sc
31 \\ bns+ 1f
32 \\ neg 3, 3
33 \\ 1:
34 : [ret] "={r3}" (-> usize)
35 : [number] "{r0}" (@enumToInt(number)),
36 [arg1] "{r3}" (arg1),
37 [arg2] "{r4}" (arg2)
38 : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
39 );
40}
41
42pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize {
43 return asm volatile (
44 \\ sc
45 \\ bns+ 1f
46 \\ neg 3, 3
47 \\ 1:
48 : [ret] "={r3}" (-> usize)
49 : [number] "{r0}" (@enumToInt(number)),
50 [arg1] "{r3}" (arg1),
51 [arg2] "{r4}" (arg2),
52 [arg3] "{r5}" (arg3)
53 : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
54 );
55}
56
57pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
58 return asm volatile (
59 \\ sc
60 \\ bns+ 1f
61 \\ neg 3, 3
62 \\ 1:
63 : [ret] "={r3}" (-> usize)
64 : [number] "{r0}" (@enumToInt(number)),
65 [arg1] "{r3}" (arg1),
66 [arg2] "{r4}" (arg2),
67 [arg3] "{r5}" (arg3),
68 [arg4] "{r6}" (arg4)
69 : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
70 );
71}
72
73pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize {
74 return asm volatile (
75 \\ sc
76 \\ bns+ 1f
77 \\ neg 3, 3
78 \\ 1:
79 : [ret] "={r3}" (-> usize)
80 : [number] "{r0}" (@enumToInt(number)),
81 [arg1] "{r3}" (arg1),
82 [arg2] "{r4}" (arg2),
83 [arg3] "{r5}" (arg3),
84 [arg4] "{r6}" (arg4),
85 [arg5] "{r7}" (arg5)
86 : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
87 );
88}
89
90pub fn syscall6(
91 number: SYS,
92 arg1: usize,
93 arg2: usize,
94 arg3: usize,
95 arg4: usize,
96 arg5: usize,
97 arg6: usize,
98) usize {
99 return asm volatile (
100 \\ sc
101 \\ bns+ 1f
102 \\ neg 3, 3
103 \\ 1:
104 : [ret] "={r3}" (-> usize)
105 : [number] "{r0}" (@enumToInt(number)),
106 [arg1] "{r3}" (arg1),
107 [arg2] "{r4}" (arg2),
108 [arg3] "{r5}" (arg3),
109 [arg4] "{r6}" (arg4),
110 [arg5] "{r7}" (arg5),
111 [arg6] "{r8}" (arg6)
112 : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
113 );
114}
115
116/// This matches the libc clone function.
117pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
118
119pub const restore = restore_rt;
120
121pub fn restore_rt() callconv(.Naked) void {
122 return asm volatile ("sc"
123 :
124 : [number] "{r0}" (@enumToInt(SYS.rt_sigreturn))
125 : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12"
126 );
127}
lib/std/os/linux/tls.zig+10-3
......@@ -53,7 +53,7 @@ const TLSVariant = enum {
5353};
5454
5555const tls_variant = switch (builtin.arch) {
56 .arm, .armeb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel => TLSVariant.VariantI,
56 .arm, .armeb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI,
5757 .x86_64, .i386 => TLSVariant.VariantII,
5858 else => @compileError("undefined tls_variant for this architecture"),
5959};
......@@ -77,12 +77,12 @@ const tls_tp_points_past_tcb = switch (builtin.arch) {
7777// make the generated code more efficient
7878
7979const tls_tp_offset = switch (builtin.arch) {
80 .mips, .mipsel => 0x7000,
80 .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x7000,
8181 else => 0,
8282};
8383
8484const tls_dtv_offset = switch (builtin.arch) {
85 .mips, .mipsel => 0x8000,
85 .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x8000,
8686 .riscv32, .riscv64 => 0x800,
8787 else => 0,
8888};
......@@ -165,6 +165,13 @@ pub fn setThreadPointer(addr: usize) void {
165165 const rc = std.os.linux.syscall1(.set_thread_area, addr);
166166 assert(rc == 0);
167167 },
168 .powerpc, .powerpc64, .powerpc64le => {
169 asm volatile (
170 \\ mr 13, %[addr]
171 :
172 : [addr] "r" (addr)
173 );
174 },
168175 else => @compileError("Unsupported architecture"),
169176 }
170177}
lib/std/os/windows.zig+2-1
......@@ -828,7 +828,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
828828 }
829829}
830830
831pub const MoveFileError = error{Unexpected};
831pub const MoveFileError = error{ FileNotFound, Unexpected };
832832
833833pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
834834 const old_path_w = try sliceToPrefixedFileW(old_path);
......@@ -839,6 +839,7 @@ pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) Move
839839pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
840840 if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
841841 switch (kernel32.GetLastError()) {
842 .FILE_NOT_FOUND => return error.FileNotFound,
842843 else => |err| return unexpectedError(err),
843844 }
844845 }
lib/std/priority_queue.zig+10-1
......@@ -195,7 +195,7 @@ pub fn PriorityQueue(comptime T: type) type {
195195 count: usize,
196196
197197 pub fn next(it: *Iterator) ?T {
198 if (it.count > it.queue.len - 1) return null;
198 if (it.count >= it.queue.len) return null;
199199 const out = it.count;
200200 it.count += 1;
201201 return it.queue.items[out];
......@@ -428,3 +428,12 @@ test "std.PriorityQueue: remove at index" {
428428 expectEqual(queue.remove(), 3);
429429 expectEqual(queue.removeOrNull(), null);
430430}
431
432test "std.PriorityQueue: iterator while empty" {
433 var queue = PQ.init(testing.allocator, lessThan);
434 defer queue.deinit();
435
436 var it = queue.iterator();
437
438 expectEqual(it.next(), null);
439}
lib/std/process.zig+8-6
......@@ -593,8 +593,10 @@ pub fn getUserInfo(name: []const u8) !UserInfo {
593593/// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
594594/// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
595595pub fn posixGetUserInfo(name: []const u8) !UserInfo {
596 var reader = try io.Reader.open("/etc/passwd", null);
597 defer reader.close();
596 const file = try std.fs.openFileAbsolute("/etc/passwd", .{});
597 defer file.close();
598
599 const reader = file.reader();
598600
599601 const State = enum {
600602 Start,
......@@ -650,8 +652,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
650652 '0'...'9' => byte - '0',
651653 else => return error.CorruptPasswordFile,
652654 };
653 if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile;
654 if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile;
655 if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile;
656 if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile;
655657 },
656658 },
657659 .ReadGroupId => switch (byte) {
......@@ -666,8 +668,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
666668 '0'...'9' => byte - '0',
667669 else => return error.CorruptPasswordFile,
668670 };
669 if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile;
670 if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile;
671 if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile;
672 if (@addWithOverflow(u32, gid, digit, &gid)) return error.CorruptPasswordFile;
671673 },
672674 },
673675 }
lib/std/special/c.zig+55
......@@ -394,6 +394,61 @@ fn clone() callconv(.Naked) void {
394394 \\ syscall
395395 );
396396 },
397
398 .powerpc64, .powerpc64le => {
399 asm volatile (
400 \\ # store non-volatile regs r30, r31 on stack in order to put our
401 \\ # start func and its arg there
402 \\ stwu 30, -16(1)
403 \\ stw 31, 4(1)
404 \\ # save r3 (func) into r30, and r6(arg) into r31
405 \\ mr 30, 3
406 \\ mr 31, 6
407 \\ # create initial stack frame for new thread
408 \\ clrrwi 4, 4, 4
409 \\ li 0, 0
410 \\ stwu 0, -16(4)
411 \\ #move c into first arg
412 \\ mr 3, 5
413 \\ mr 5, 7
414 \\ mr 6, 8
415 \\ mr 7, 9
416 \\ # move syscall number into r0
417 \\ li 0, 120
418 \\ sc
419
420 \\ # check for syscall error
421 \\ bns+ 1f # jump to label 1 if no summary overflow.
422 \\ #else
423 \\ neg 3, 3 #negate the result (errno)
424 \\1:
425 \\ # compare sc result with 0
426 \\ cmpwi cr7, 3, 0
427
428 \\ # if not 0, jump to end
429 \\ bne cr7, 2f
430
431 \\ #else: we're the child
432 \\ #call funcptr: move arg (d) into r3
433 \\ mr 3, 31
434 \\ #move r30 (funcptr) into CTR reg
435 \\ mtctr 30
436 \\ # call CTR reg
437 \\ bctrl
438 \\ # mov SYS_exit into r0 (the exit param is already in r3)
439 \\ li 0, 1
440 \\ sc
441
442 \\2:
443 \\ # restore stack
444 \\ lwz 30, 0(1)
445 \\ lwz 31, 4(1)
446 \\ addi 1, 1, 16
447
448 \\ blr
449 );
450 },
451
397452 else => @compileError("Implement clone() for this arch."),
398453 }
399454}
lib/std/start.zig+15
......@@ -121,6 +121,21 @@ fn _start() callconv(.Naked) noreturn {
121121 : [argc] "=r" (-> [*]usize)
122122 );
123123 },
124 .powerpc64le => {
125 // Before returning the stack pointer, we have to set up a backchain
126 // and a few other registers required by the ELFv2 ABI.
127 // TODO: Support powerpc64 (big endian) on ELFv2.
128 starting_stack_ptr = asm (
129 \\ mr 4, 1
130 \\ subi 1, 1, 32
131 \\ li 5, 0
132 \\ std 5, 0(1)
133 \\ mr %[argc], 4
134 : [argc] "=r" (-> [*]usize)
135 :
136 : "r4", "r5"
137 );
138 },
124139 else => @compileError("unsupported arch"),
125140 }
126141 // If LLVM inlines stack variables into _start, they will overwrite
lib/std/std.zig+1
......@@ -50,6 +50,7 @@ pub const builtin = @import("builtin.zig");
5050pub const c = @import("c.zig");
5151pub const cache_hash = @import("cache_hash.zig");
5252pub const coff = @import("coff.zig");
53pub const compress = @import("compress.zig");
5354pub const crypto = @import("crypto.zig");
5455pub const cstr = @import("cstr.zig");
5556pub const debug = @import("debug.zig");
src-self-hosted/libc_installation.zig+9-8
......@@ -9,6 +9,8 @@ const is_darwin = Target.current.isDarwin();
99const is_windows = Target.current.os.tag == .windows;
1010const is_gnu = Target.current.isGnu();
1111
12const log = std.log.scoped(.libc_installation);
13
1214usingnamespace @import("windows_sdk.zig");
1315
1416/// See the render function implementation for documentation of the fields.
......@@ -37,7 +39,6 @@ pub const LibCInstallation = struct {
3739 pub fn parse(
3840 allocator: *Allocator,
3941 libc_file: []const u8,
40 stderr: anytype,
4142 ) !LibCInstallation {
4243 var self: LibCInstallation = .{};
4344
......@@ -62,7 +63,7 @@ pub const LibCInstallation = struct {
6263 if (line.len == 0 or line[0] == '#') continue;
6364 var line_it = std.mem.split(line, "=");
6465 const name = line_it.next() orelse {
65 try stderr.print("missing equal sign after field name\n", .{});
66 log.err("missing equal sign after field name\n", .{});
6667 return error.ParseError;
6768 };
6869 const value = line_it.rest();
......@@ -81,31 +82,31 @@ pub const LibCInstallation = struct {
8182 }
8283 inline for (fields) |field, i| {
8384 if (!found_keys[i].found) {
84 try stderr.print("missing field: {}\n", .{field.name});
85 log.err("missing field: {}\n", .{field.name});
8586 return error.ParseError;
8687 }
8788 }
8889 if (self.include_dir == null) {
89 try stderr.print("include_dir may not be empty\n", .{});
90 log.err("include_dir may not be empty\n", .{});
9091 return error.ParseError;
9192 }
9293 if (self.sys_include_dir == null) {
93 try stderr.print("sys_include_dir may not be empty\n", .{});
94 log.err("sys_include_dir may not be empty\n", .{});
9495 return error.ParseError;
9596 }
9697 if (self.crt_dir == null and !is_darwin) {
97 try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)});
98 log.err("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)});
9899 return error.ParseError;
99100 }
100101 if (self.msvc_lib_dir == null and is_windows and !is_gnu) {
101 try stderr.print("msvc_lib_dir may not be empty for {}-{}\n", .{
102 log.err("msvc_lib_dir may not be empty for {}-{}\n", .{
102103 @tagName(Target.current.os.tag),
103104 @tagName(Target.current.abi),
104105 });
105106 return error.ParseError;
106107 }
107108 if (self.kernel32_lib_dir == null and is_windows and !is_gnu) {
108 try stderr.print("kernel32_lib_dir may not be empty for {}-{}\n", .{
109 log.err("kernel32_lib_dir may not be empty for {}-{}\n", .{
109110 @tagName(Target.current.os.tag),
110111 @tagName(Target.current.abi),
111112 });
src-self-hosted/link/MachO.zig+24-16
......@@ -32,6 +32,20 @@ const LoadCommand = union(enum) {
3232 .Dysymtab => |x| x.cmdsize,
3333 };
3434 }
35
36 pub fn write(self: LoadCommand, file: *fs.File, offset: u64) !void {
37 return switch (self) {
38 .Segment => |cmd| writeGeneric(cmd, file, offset),
39 .LinkeditData => |cmd| writeGeneric(cmd, file, offset),
40 .Symtab => |cmd| writeGeneric(cmd, file, offset),
41 .Dysymtab => |cmd| writeGeneric(cmd, file, offset),
42 };
43 }
44
45 fn writeGeneric(cmd: anytype, file: *fs.File, offset: u64) !void {
46 const slice = [1]@TypeOf(cmd){cmd};
47 return file.pwriteAll(mem.sliceAsBytes(slice[0..1]), offset);
48 }
3549};
3650
3751base: File,
......@@ -258,8 +272,7 @@ pub fn flush(self: *MachO, module: *Module) !void {
258272
259273 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
260274 for (self.load_commands.items) |cmd| {
261 const cmd_to_write = [1]@TypeOf(cmd){cmd};
262 try self.base.file.?.pwriteAll(mem.sliceAsBytes(cmd_to_write[0..1]), last_cmd_offset);
275 try cmd.write(&self.base.file.?, last_cmd_offset);
263276 last_cmd_offset += cmd.cmdsize();
264277 }
265278 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
......@@ -346,19 +359,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
346359 .n_desc = 0,
347360 .n_value = addr,
348361 };
349 self.offset_table.items[decl.link.macho.offset_table_index.?] = addr;
350362
363 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
364 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
365 try self.updateDeclExports(module, decl, decl_exports);
351366 try self.writeSymbol(decl.link.macho.symbol_table_index.?);
352367
353368 const text_section = self.sections.items[self.text_section_index.?];
354369 const section_offset = symbol.n_value - text_section.addr;
355370 const file_offset = text_section.offset + section_offset;
356371 log.debug("file_offset 0x{x}\n", .{file_offset});
357 try self.base.file.?.pwriteAll(code, file_offset);
358372
359 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
360 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
361 return self.updateDeclExports(module, decl, decl_exports);
373 try self.base.file.?.pwriteAll(code, file_offset);
362374}
363375
364376pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
......@@ -374,7 +386,7 @@ pub fn updateDeclExports(
374386
375387 if (decl.link.macho.symbol_table_index == null) return;
376388
377 var decl_sym = self.symbol_table.items[decl.link.macho.symbol_table_index.?];
389 const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
378390 // TODO implement
379391 if (exports.len == 0) return;
380392
......@@ -488,9 +500,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
488500 const addr = blk: {
489501 if (self.last_text_block) |last| {
490502 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
491 const ideal_capacity = last.size * alloc_num / alloc_den;
492 const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
493 const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
503 const end_addr = last_symbol.n_value + last.size;
504 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
494505 block_placement = last;
495506 break :blk new_start_addr;
496507 } else {
......@@ -504,10 +515,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
504515 const text_capacity = self.allocatedSize(text_section.offset);
505516 const needed_size = (addr + new_block_size) - text_section.addr;
506517 log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });
507
508 if (needed_size > text_capacity) {
509 // TODO handle growth
510 }
518 assert(needed_size <= text_capacity); // TODO handle growth
511519
512520 self.last_text_block = text_block;
513521 text_section.size = needed_size;
......@@ -659,7 +667,7 @@ fn writeSymbol(self: *MachO, index: usize) !void {
659667 defer tracy.end();
660668
661669 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
662 var sym = [1]macho.nlist_64{self.symbol_table.items[index]};
670 const sym = [1]macho.nlist_64{self.symbol_table.items[index]};
663671 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
664672 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
665673 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
src-self-hosted/stage2.zig+1-5
......@@ -598,12 +598,9 @@ const Stage2LibCInstallation = extern struct {
598598
599599// ABI warning
600600export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {
601 stderr_file = std.io.getStdErr();
602 stderr = stderr_file.outStream();
603601 const libc_file = mem.spanZ(libc_file_z);
604 var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) {
602 var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file) catch |err| switch (err) {
605603 error.ParseError => return .SemanticAnalyzeFail,
606 error.DiskQuota => return .DiskQuota,
607604 error.FileTooBig => return .FileTooBig,
608605 error.InputOutput => return .FileSystem,
609606 error.NoSpaceLeft => return .NoSpaceLeft,
......@@ -612,7 +609,6 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
612609 error.SystemResources => return .SystemResources,
613610 error.OperationAborted => return .OperationAborted,
614611 error.WouldBlock => unreachable,
615 error.NotOpenForWriting => unreachable,
616612 error.NotOpenForReading => unreachable,
617613 error.Unexpected => return .Unexpected,
618614 error.IsDir => return .IsDir,
src-self-hosted/translate_c.zig+48-2
......@@ -2032,7 +2032,7 @@ fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
20322032 // Handle the remaining escapes Zig doesn't support by turning them
20332033 // into their respective hex representation
20342034 else => if (std.ascii.isCntrl(c))
2035 std.fmt.bufPrint(char_buf, "\\x{x:0<2}", .{c}) catch unreachable
2035 std.fmt.bufPrint(char_buf, "\\x{x:0>2}", .{c}) catch unreachable
20362036 else
20372037 std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable,
20382038 };
......@@ -5881,7 +5881,7 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
58815881 },
58825882 .Identifier => {
58835883 const mangled_name = scope.getAlias(slice);
5884 return transCreateNodeIdentifier(c, mangled_name);
5884 return transCreateNodeIdentifier(c, checkForBuiltinTypedef(mangled_name) orelse mangled_name);
58855885 },
58865886 .LParen => {
58875887 const inner_node = try parseCExpr(c, m, scope);
......@@ -5899,6 +5899,10 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
58995899 saw_l_paren = true;
59005900 _ = m.next();
59015901 },
5902 // (type)sizeof(x)
5903 .Keyword_sizeof,
5904 // (type)alignof(x)
5905 .Keyword_alignof,
59025906 // (type)identifier
59035907 .Identifier => {},
59045908 // (type)integer
......@@ -6309,6 +6313,48 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.
63096313 node.rhs = try parseCPrefixOpExpr(c, m, scope);
63106314 return &node.base;
63116315 },
6316 .Keyword_sizeof => {
6317 const inner = if (m.peek().? == .LParen) blk: {
6318 _ = m.next();
6319 const inner = try parseCExpr(c, m, scope);
6320 if (m.next().? != .RParen) {
6321 try m.fail(c, "unable to translate C expr: expected ')'", .{});
6322 return error.ParseError;
6323 }
6324 break :blk inner;
6325 } else try parseCPrefixOpExpr(c, m, scope);
6326
6327 //(@import("std").meta.sizeof(dest, x))
6328 const import_fn_call = try c.createBuiltinCall("@import", 1);
6329 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
6330 import_fn_call.params()[0] = std_node;
6331 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
6332 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
6333 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "sizeof");
6334
6335 const sizeof_call = try c.createCall(outer_field_access, 1);
6336 sizeof_call.params()[0] = inner;
6337 sizeof_call.rtoken = try appendToken(c, .RParen, ")");
6338 return &sizeof_call.base;
6339 },
6340 .Keyword_alignof => {
6341 // TODO this won't work if using <stdalign.h>'s
6342 // #define alignof _Alignof
6343 if (m.next().? != .LParen) {
6344 try m.fail(c, "unable to translate C expr: expected '('", .{});
6345 return error.ParseError;
6346 }
6347 const inner = try parseCExpr(c, m, scope);
6348 if (m.next().? != .RParen) {
6349 try m.fail(c, "unable to translate C expr: expected ')'", .{});
6350 return error.ParseError;
6351 }
6352
6353 const builtin_call = try c.createBuiltinCall("@alignOf", 1);
6354 builtin_call.params()[0] = inner;
6355 builtin_call.rparen_token = try appendToken(c, .RParen, ")");
6356 return &builtin_call.base;
6357 },
63126358 else => {
63136359 m.i -= 1;
63146360 return try parseCSuffixOpExpr(c, m, scope);
src/all_types.hpp+1
......@@ -2265,6 +2265,7 @@ struct CodeGen {
22652265
22662266 Stage2LibCInstallation *libc;
22672267
2268 bool is_versioned;
22682269 size_t version_major;
22692270 size_t version_minor;
22702271 size_t version_patch;
src/analyze.cpp+156-125
......@@ -1003,7 +1003,8 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
10031003 g->zig_target->arch == ZigLLVM_x86_64 ||
10041004 target_is_arm(g->zig_target) ||
10051005 target_is_riscv(g->zig_target) ||
1006 target_is_wasm(g->zig_target))
1006 target_is_wasm(g->zig_target) ||
1007 target_is_ppc(g->zig_target))
10071008 {
10081009 X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
10091010 return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval;
......@@ -2372,7 +2373,10 @@ static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) {
23722373 if (field->gen_index == UINT32_MAX)
23732374 continue;
23742375
2375 AstNode *align_expr = field->decl_node->data.struct_field.align_expr;
2376 AstNode *align_expr = nullptr;
2377 if (union_type->data.unionation.decl_node->type == NodeTypeContainerDecl) {
2378 align_expr = field->decl_node->data.struct_field.align_expr;
2379 }
23762380 if (align_expr != nullptr) {
23772381 if (!analyze_const_align(g, &union_type->data.unionation.decls_scope->base, align_expr,
23782382 &field->align))
......@@ -2468,9 +2472,6 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) {
24682472
24692473 AstNode *decl_node = union_type->data.unionation.decl_node;
24702474
2471
2472 assert(decl_node->type == NodeTypeContainerDecl);
2473
24742475 uint32_t field_count = union_type->data.unionation.src_field_count;
24752476 TypeUnionField *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member;
24762477
......@@ -2603,16 +2604,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
26032604 if (decl_node->type == NodeTypeContainerDecl) {
26042605 assert(!enum_type->data.enumeration.fields);
26052606 field_count = (uint32_t)decl_node->data.container_decl.fields.length;
2606 if (field_count == 0) {
2607 add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
2608
2609 enum_type->data.enumeration.src_field_count = field_count;
2610 enum_type->data.enumeration.fields = nullptr;
2611 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2612 return ErrorSemanticAnalyzeFail;
2613 }
26142607 } else {
2615 field_count = enum_type->data.enumeration.src_field_count;
2608 field_count = enum_type->data.enumeration.src_field_count + enum_type->data.enumeration.non_exhaustive;
2609 }
2610
2611 if (field_count == 0) {
2612 add_node_error(g, decl_node, buf_sprintf("enums must have 1 or more fields"));
2613 enum_type->data.enumeration.src_field_count = field_count;
2614 enum_type->data.enumeration.fields = nullptr;
2615 enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;
2616 return ErrorSemanticAnalyzeFail;
26162617 }
26172618
26182619 Scope *scope = &enum_type->data.enumeration.decls_scope->base;
......@@ -3055,7 +3056,6 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
30553056 return ErrorNone;
30563057
30573058 AstNode *decl_node = union_type->data.unionation.decl_node;
3058 assert(decl_node->type == NodeTypeContainerDecl);
30593059
30603060 if (union_type->data.unionation.resolve_loop_flag_zero_bits) {
30613061 if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {
......@@ -3069,30 +3069,51 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
30693069
30703070 union_type->data.unionation.resolve_loop_flag_zero_bits = true;
30713071
3072 assert(union_type->data.unionation.fields == nullptr);
3073 uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
3072 uint32_t field_count;
3073 if (decl_node->type == NodeTypeContainerDecl) {
3074 assert(union_type->data.unionation.fields == nullptr);
3075 field_count = (uint32_t)decl_node->data.container_decl.fields.length;
3076 union_type->data.unionation.src_field_count = field_count;
3077 union_type->data.unionation.fields = heap::c_allocator.allocate<TypeUnionField>(field_count);
3078 union_type->data.unionation.fields_by_name.init(field_count);
3079 } else {
3080 field_count = union_type->data.unionation.src_field_count;
3081 assert(field_count == 0 || union_type->data.unionation.fields != nullptr);
3082 }
3083
30743084 if (field_count == 0) {
30753085 add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));
30763086 union_type->data.unionation.src_field_count = field_count;
30773087 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
30783088 return ErrorSemanticAnalyzeFail;
30793089 }
3080 union_type->data.unionation.src_field_count = field_count;
3081 union_type->data.unionation.fields = heap::c_allocator.allocate<TypeUnionField>(field_count);
3082 union_type->data.unionation.fields_by_name.init(field_count);
30833090
30843091 Scope *scope = &union_type->data.unionation.decls_scope->base;
30853092
30863093 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
30873094
3088 AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr;
3089 union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum ||
3090 enum_type_node != nullptr;
3091 bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
3092 bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr) && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease);
3095 bool is_auto_enum; // union(enum) or union(enum(expr))
3096 bool is_explicit_enum; // union(expr)
3097 AstNode *enum_type_node; // expr in union(enum(expr)) or union(expr)
3098 if (decl_node->type == NodeTypeContainerDecl) {
3099 is_auto_enum = decl_node->data.container_decl.auto_enum;
3100 is_explicit_enum = decl_node->data.container_decl.init_arg_expr != nullptr;
3101 enum_type_node = decl_node->data.container_decl.init_arg_expr;
3102 } else {
3103 is_auto_enum = false;
3104 is_explicit_enum = union_type->data.unionation.tag_type != nullptr;
3105 enum_type_node = nullptr;
3106 }
3107 union_type->data.unionation.have_explicit_tag_type = is_auto_enum || is_explicit_enum;
3108
3109 bool is_auto_layout = union_type->data.unionation.layout == ContainerLayoutAuto;
3110 bool want_safety = (field_count >= 2)
3111 && (is_auto_layout || is_explicit_enum)
3112 && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease);
30933113 ZigType *tag_type;
3094 bool create_enum_type = decl_node->data.container_decl.auto_enum || (enum_type_node == nullptr && want_safety);
3114 bool create_enum_type = is_auto_enum || (!is_explicit_enum && want_safety);
30953115 bool *covered_enum_fields;
3116 bool *is_zero_bits = heap::c_allocator.allocate<bool>(field_count);
30963117 ZigLLVMDIEnumerator **di_enumerators;
30973118 if (create_enum_type) {
30983119 occupied_tag_values.init(field_count);
......@@ -3134,87 +3155,96 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
31343155 tag_type->data.enumeration.fields_by_name.init(field_count);
31353156 tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope;
31363157 } else if (enum_type_node != nullptr) {
3137 ZigType *enum_type = analyze_type_expr(g, scope, enum_type_node);
3138 if (type_is_invalid(enum_type)) {
3158 tag_type = analyze_type_expr(g, scope, enum_type_node);
3159 } else {
3160 if (decl_node->type == NodeTypeContainerDecl) {
3161 tag_type = nullptr;
3162 } else {
3163 tag_type = union_type->data.unionation.tag_type;
3164 }
3165 }
3166 if (tag_type != nullptr) {
3167 if (type_is_invalid(tag_type)) {
31393168 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
31403169 return ErrorSemanticAnalyzeFail;
31413170 }
3142 if (enum_type->id != ZigTypeIdEnum) {
3171 if (tag_type->id != ZigTypeIdEnum) {
31433172 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3144 add_node_error(g, enum_type_node,
3145 buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
3173 add_node_error(g, enum_type_node != nullptr ? enum_type_node : decl_node,
3174 buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&tag_type->name)));
31463175 return ErrorSemanticAnalyzeFail;
31473176 }
3148 if ((err = type_resolve(g, enum_type, ResolveStatusAlignmentKnown))) {
3177 if ((err = type_resolve(g, tag_type, ResolveStatusAlignmentKnown))) {
31493178 assert(g->errors.length != 0);
31503179 return err;
31513180 }
3152 tag_type = enum_type;
3153 covered_enum_fields = heap::c_allocator.allocate<bool>(enum_type->data.enumeration.src_field_count);
3154 } else {
3155 tag_type = nullptr;
3181 covered_enum_fields = heap::c_allocator.allocate<bool>(tag_type->data.enumeration.src_field_count);
31563182 }
31573183 union_type->data.unionation.tag_type = tag_type;
31583184
3159 uint32_t gen_field_index = 0;
31603185 for (uint32_t i = 0; i < field_count; i += 1) {
3161 AstNode *field_node = decl_node->data.container_decl.fields.at(i);
3162 Buf *field_name = field_node->data.struct_field.name;
31633186 TypeUnionField *union_field = &union_type->data.unionation.fields[i];
3164 union_field->name = field_node->data.struct_field.name;
3165 union_field->decl_node = field_node;
3166 union_field->gen_index = UINT32_MAX;
3167
3168 auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);
3169 if (field_entry != nullptr) {
3170 ErrorMsg *msg = add_node_error(g, field_node,
3171 buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name)));
3172 add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
3173 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3174 return ErrorSemanticAnalyzeFail;
3175 }
3187 if (decl_node->type == NodeTypeContainerDecl) {
3188 AstNode *field_node = decl_node->data.container_decl.fields.at(i);
3189 union_field->name = field_node->data.struct_field.name;
3190 union_field->decl_node = field_node;
3191 union_field->gen_index = UINT32_MAX;
3192 is_zero_bits[i] = false;
31763193
3177 bool field_is_zero_bits;
3178 if (field_node->data.struct_field.type == nullptr) {
3179 if (decl_node->data.container_decl.auto_enum ||
3180 decl_node->data.container_decl.init_arg_expr != nullptr)
3181 {
3182 union_field->type_entry = g->builtin_types.entry_void;
3183 field_is_zero_bits = true;
3184 } else {
3185 add_node_error(g, field_node, buf_sprintf("union field missing type"));
3194 auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);
3195 if (field_entry != nullptr) {
3196 ErrorMsg *msg = add_node_error(g, union_field->decl_node,
3197 buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name)));
3198 add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
31863199 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
31873200 return ErrorSemanticAnalyzeFail;
31883201 }
3189 } else {
3190 ZigValue *field_type_val = analyze_const_value(g, scope,
3191 field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);
3192 if (type_is_invalid(field_type_val->type)) {
3193 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3194 return ErrorSemanticAnalyzeFail;
3202
3203 if (field_node->data.struct_field.type == nullptr) {
3204 if (is_auto_enum || is_explicit_enum) {
3205 union_field->type_entry = g->builtin_types.entry_void;
3206 is_zero_bits[i] = true;
3207 } else {
3208 add_node_error(g, field_node, buf_sprintf("union field missing type"));
3209 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3210 return ErrorSemanticAnalyzeFail;
3211 }
3212 } else {
3213 ZigValue *field_type_val = analyze_const_value(g, scope,
3214 field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);
3215 if (type_is_invalid(field_type_val->type)) {
3216 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3217 return ErrorSemanticAnalyzeFail;
3218 }
3219 assert(field_type_val->special != ConstValSpecialRuntime);
3220 union_field->type_val = field_type_val;
31953221 }
3196 assert(field_type_val->special != ConstValSpecialRuntime);
3197 union_field->type_val = field_type_val;
3198 if (union_type->data.unionation.resolve_status == ResolveStatusInvalid)
3199 return ErrorSemanticAnalyzeFail;
32003222
3223 if (field_node->data.struct_field.value != nullptr && !is_auto_enum) {
3224 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
3225 buf_create_from_str("untagged union field assignment"));
3226 add_error_note(g, msg, decl_node, buf_create_from_str("consider 'union(enum)' here"));
3227 }
3228 }
3229
3230 if (union_field->type_val != nullptr) {
32013231 bool field_is_opaque_type;
3202 if ((err = type_val_resolve_is_opaque_type(g, field_type_val, &field_is_opaque_type))) {
3232 if ((err = type_val_resolve_is_opaque_type(g, union_field->type_val, &field_is_opaque_type))) {
32033233 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
32043234 return ErrorSemanticAnalyzeFail;
32053235 }
32063236 if (field_is_opaque_type) {
3207 add_node_error(g, field_node,
3237 add_node_error(g, union_field->decl_node,
32083238 buf_create_from_str(
32093239 "opaque types have unknown size and therefore cannot be directly embedded in unions"));
32103240 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
32113241 return ErrorSemanticAnalyzeFail;
32123242 }
32133243
3214 switch (type_val_resolve_requires_comptime(g, field_type_val)) {
3244 switch (type_val_resolve_requires_comptime(g, union_field->type_val)) {
32153245 case ReqCompTimeInvalid:
32163246 if (g->trace_err != nullptr) {
3217 g->trace_err = add_error_note(g, g->trace_err, field_node,
3247 g->trace_err = add_error_note(g, g->trace_err, union_field->decl_node,
32183248 buf_create_from_str("while checking this field"));
32193249 }
32203250 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
......@@ -3226,29 +3256,25 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
32263256 break;
32273257 }
32283258
3229 if ((err = type_val_resolve_zero_bits(g, field_type_val, union_type, nullptr, &field_is_zero_bits))) {
3259 if ((err = type_val_resolve_zero_bits(g, union_field->type_val, union_type, nullptr, &is_zero_bits[i]))) {
32303260 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
32313261 return ErrorSemanticAnalyzeFail;
32323262 }
32333263 }
32343264
3235 if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) {
3236 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
3237 buf_create_from_str("untagged union field assignment"));
3238 add_error_note(g, msg, decl_node, buf_create_from_str("consider 'union(enum)' here"));
3239 }
3240
32413265 if (create_enum_type) {
3242 di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(field_name), i);
3266 di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(union_field->name), i);
32433267 union_field->enum_field = &tag_type->data.enumeration.fields[i];
3244 union_field->enum_field->name = field_name;
3268 union_field->enum_field->name = union_field->name;
32453269 union_field->enum_field->decl_index = i;
3246 union_field->enum_field->decl_node = field_node;
3270 union_field->enum_field->decl_node = union_field->decl_node;
32473271
32483272 auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field);
32493273 assert(prev_entry == nullptr); // caught by union de-duplicator above
32503274
3251 AstNode *tag_value = field_node->data.struct_field.value;
3275 AstNode *tag_value = decl_node->type == NodeTypeContainerDecl
3276 ? union_field->decl_node->data.struct_field.value : nullptr;
3277
32523278 // In this first pass we resolve explicit tag values.
32533279 // In a second pass we will fill in the unspecified ones.
32543280 if (tag_value != nullptr) {
......@@ -3276,11 +3302,11 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
32763302 return ErrorSemanticAnalyzeFail;
32773303 }
32783304 }
3279 } else if (enum_type_node != nullptr) {
3280 union_field->enum_field = find_enum_type_field(tag_type, field_name);
3305 } else if (tag_type != nullptr) {
3306 union_field->enum_field = find_enum_type_field(tag_type, union_field->name);
32813307 if (union_field->enum_field == nullptr) {
3282 ErrorMsg *msg = add_node_error(g, field_node,
3283 buf_sprintf("enum field not found: '%s'", buf_ptr(field_name)));
3308 ErrorMsg *msg = add_node_error(g, union_field->decl_node,
3309 buf_sprintf("enum field not found: '%s'", buf_ptr(union_field->name)));
32843310 add_error_note(g, msg, tag_type->data.enumeration.decl_node,
32853311 buf_sprintf("enum declared here"));
32863312 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
......@@ -3289,21 +3315,23 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
32893315 covered_enum_fields[union_field->enum_field->decl_index] = true;
32903316 } else {
32913317 union_field->enum_field = heap::c_allocator.create<TypeEnumField>();
3292 union_field->enum_field->name = field_name;
3318 union_field->enum_field->name = union_field->name;
32933319 union_field->enum_field->decl_index = i;
32943320 bigint_init_unsigned(&union_field->enum_field->value, i);
32953321 }
32963322 assert(union_field->enum_field != nullptr);
3323 }
32973324
3298 if (field_is_zero_bits)
3299 continue;
3300
3301 union_field->gen_index = gen_field_index;
3302 gen_field_index += 1;
3325 uint32_t gen_field_index = 0;
3326 for (uint32_t i = 0; i < field_count; i += 1) {
3327 TypeUnionField *union_field = &union_type->data.unionation.fields[i];
3328 if (!is_zero_bits[i]) {
3329 union_field->gen_index = gen_field_index;
3330 gen_field_index += 1;
3331 }
33033332 }
33043333
3305 bool src_have_tag = decl_node->data.container_decl.auto_enum ||
3306 decl_node->data.container_decl.init_arg_expr != nullptr;
3334 bool src_have_tag = is_auto_enum || is_explicit_enum;
33073335
33083336 if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) {
33093337 const char *qual_str;
......@@ -3317,8 +3345,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
33173345 qual_str = "extern";
33183346 break;
33193347 }
3320 AstNode *source_node = (decl_node->data.container_decl.init_arg_expr != nullptr) ?
3321 decl_node->data.container_decl.init_arg_expr : decl_node;
3348 AstNode *source_node = enum_type_node != nullptr ? enum_type_node : decl_node;
33223349 add_node_error(g, source_node,
33233350 buf_sprintf("%s union does not support enum tag type", qual_str));
33243351 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
......@@ -3326,43 +3353,47 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
33263353 }
33273354
33283355 if (create_enum_type) {
3329 // Now iterate again and populate the unspecified tag values
3330 uint32_t next_maybe_unoccupied_index = 0;
3356 if (decl_node->type == NodeTypeContainerDecl) {
3357 // Now iterate again and populate the unspecified tag values
3358 uint32_t next_maybe_unoccupied_index = 0;
33313359
3332 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
3333 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
3334 TypeUnionField *union_field = &union_type->data.unionation.fields[field_i];
3335 AstNode *tag_value = field_node->data.struct_field.value;
3360 for (uint32_t field_i = 0; field_i < field_count; field_i += 1) {
3361 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
3362 TypeUnionField *union_field = &union_type->data.unionation.fields[field_i];
3363 AstNode *tag_value = field_node->data.struct_field.value;
33363364
3337 if (tag_value == nullptr) {
3338 if (occupied_tag_values.size() == 0) {
3339 bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index);
3340 next_maybe_unoccupied_index += 1;
3341 } else {
3342 BigInt proposed_value;
3343 for (;;) {
3344 bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);
3365 if (tag_value == nullptr) {
3366 if (occupied_tag_values.size() == 0) {
3367 bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index);
33453368 next_maybe_unoccupied_index += 1;
3346 auto entry = occupied_tag_values.put_unique(proposed_value, field_node);
3347 if (entry != nullptr) {
3348 continue;
3369 } else {
3370 BigInt proposed_value;
3371 for (;;) {
3372 bigint_init_unsigned(&proposed_value, next_maybe_unoccupied_index);
3373 next_maybe_unoccupied_index += 1;
3374 auto entry = occupied_tag_values.put_unique(proposed_value, field_node);
3375 if (entry != nullptr) {
3376 continue;
3377 }
3378 break;
33493379 }
3350 break;
3380 bigint_init_bigint(&union_field->enum_field->value, &proposed_value);
33513381 }
3352 bigint_init_bigint(&union_field->enum_field->value, &proposed_value);
33533382 }
33543383 }
33553384 }
3356 } else if (enum_type_node != nullptr) {
3385 } else if (tag_type != nullptr) {
33573386 for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {
33583387 TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];
33593388 if (!covered_enum_fields[i]) {
3360 AstNode *enum_decl_node = tag_type->data.enumeration.decl_node;
3361 AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i);
33623389 ErrorMsg *msg = add_node_error(g, decl_node,
33633390 buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name)));
3364 add_error_note(g, msg, field_node,
3365 buf_sprintf("declared here"));
3391 if (decl_node->type == NodeTypeContainerDecl) {
3392 AstNode *enum_decl_node = tag_type->data.enumeration.decl_node;
3393 AstNode *field_node = enum_decl_node->data.container_decl.fields.at(i);
3394 add_error_note(g, msg, field_node,
3395 buf_sprintf("declared here"));
3396 }
33663397 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
33673398 }
33683399 }
......@@ -8350,7 +8381,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
83508381 ZigLLVMDIFile *di_file;
83518382 ZigLLVMDIScope *di_scope;
83528383 unsigned line;
8353 if (decl_node != nullptr && !struct_type->data.structure.created_by_at_type) {
8384 if (decl_node != nullptr) {
83548385 Scope *scope = &struct_type->data.structure.decls_scope->base;
83558386 ZigType *import = get_scope_import(scope);
83568387 di_file = import->data.structure.root_struct->di_file;
......@@ -8713,7 +8744,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
87138744
87148745 uint64_t store_size_in_bits = union_field->type_entry->size_in_bits;
87158746 uint64_t abi_align_in_bits = 8*union_field->type_entry->abi_align;
8716 AstNode *field_node = decl_node->data.container_decl.fields.at(i);
8747 AstNode *field_node = union_field->decl_node;
87178748 union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
87188749 ZigLLVMTypeToScope(union_type->llvm_di_type), buf_ptr(union_field->enum_field->name),
87198750 import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1),
src/codegen.cpp+4-2
......@@ -91,7 +91,8 @@ void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix) {
9191 g->test_name_prefix = prefix;
9292}
9393
94void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch) {
94void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch) {
95 g->is_versioned = is_versioned;
9596 g->version_major = major;
9697 g->version_minor = minor;
9798 g->version_patch = patch;
......@@ -10824,6 +10825,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
1082410825 cache_bool(ch, g->emit_bin);
1082510826 cache_bool(ch, g->emit_llvm_ir);
1082610827 cache_bool(ch, g->emit_asm);
10828 cache_bool(ch, g->is_versioned);
1082710829 cache_usize(ch, g->version_major);
1082810830 cache_usize(ch, g->version_minor);
1082910831 cache_usize(ch, g->version_patch);
......@@ -10894,7 +10896,7 @@ static void resolve_out_paths(CodeGen *g) {
1089410896 buf_resize(out_basename, 0);
1089510897 buf_append_str(out_basename, target_lib_file_prefix(g->zig_target));
1089610898 buf_append_buf(out_basename, g->root_out_name);
10897 buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic,
10899 buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic, g->is_versioned,
1089810900 g->version_major, g->version_minor, g->version_patch));
1089910901 break;
1090010902 }
src/codegen.hpp+1-1
......@@ -38,7 +38,7 @@ void codegen_set_rdynamic(CodeGen *g, bool rdynamic);
3838void codegen_set_linker_script(CodeGen *g, const char *linker_script);
3939void codegen_set_test_filter(CodeGen *g, Buf *filter);
4040void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix);
41void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch);
41void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch);
4242void codegen_add_time_event(CodeGen *g, const char *name);
4343void codegen_print_timing_report(CodeGen *g, FILE *f);
4444void codegen_link(CodeGen *g);
src/glibc.cpp+1-1
......@@ -335,7 +335,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
335335 bool is_ld = (strcmp(lib->name, "ld") == 0);
336336
337337 CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node);
338 codegen_set_lib_version(child_gen, lib->sover, 0, 0);
338 codegen_set_lib_version(child_gen, true, lib->sover, 0, 0);
339339 child_gen->is_dynamic = true;
340340 child_gen->is_dummy_so = true;
341341 child_gen->version_script_path = map_file_path;
src/ir.cpp+108-21
......@@ -64,6 +64,7 @@ enum ConstCastResultId {
6464 ConstCastResultIdPointerChild,
6565 ConstCastResultIdSliceChild,
6666 ConstCastResultIdOptionalChild,
67 ConstCastResultIdOptionalShape,
6768 ConstCastResultIdErrorUnionPayload,
6869 ConstCastResultIdErrorUnionErrorSet,
6970 ConstCastResultIdFnAlign,
......@@ -11947,8 +11948,22 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
1194711948 }
1194811949 }
1194911950
11950 // maybe
11951 // optional types
1195111952 if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdOptional) {
11953 // Consider the case where the wanted type is ??[*]T and the actual one
11954 // is ?[*]T, we cannot turn the former into the latter even though the
11955 // child types are compatible (?[*]T and [*]T are both represented as a
11956 // pointer). The extra level of indirection in ??[*]T means it's
11957 // represented as a regular, fat, optional type and, as a consequence,
11958 // has a different shape than the one of ?[*]T.
11959 if ((wanted_ptr_type != nullptr) != (actual_ptr_type != nullptr)) {
11960 // The use of type_mismatch is intentional
11961 result.id = ConstCastResultIdOptionalShape;
11962 result.data.type_mismatch = heap::c_allocator.allocate_nonzero<ConstCastTypeMismatch>(1);
11963 result.data.type_mismatch->wanted_type = wanted_type;
11964 result.data.type_mismatch->actual_type = actual_type;
11965 return result;
11966 }
1195211967 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type,
1195311968 actual_type->data.maybe.child_type, source_node, wanted_is_mutable);
1195411969 if (child.id == ConstCastResultIdInvalid)
......@@ -14550,6 +14565,13 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
1455014565 report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg);
1455114566 break;
1455214567 }
14568 case ConstCastResultIdOptionalShape: {
14569 add_error_note(ira->codegen, parent_msg, source_node,
14570 buf_sprintf("optional type child '%s' cannot cast into optional type '%s'",
14571 buf_ptr(&cast_result->data.type_mismatch->actual_type->name),
14572 buf_ptr(&cast_result->data.type_mismatch->wanted_type->name)));
14573 break;
14574 }
1455314575 case ConstCastResultIdErrorUnionErrorSet: {
1455414576 ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
1455514577 buf_sprintf("error set '%s' cannot cast into error set '%s'",
......@@ -25425,8 +25447,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2542525447
2542625448 init_const_slice(ira->codegen, fields[2], union_field_array, 0, union_field_count, false);
2542725449
25428 ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr);
25429
2543025450 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
2543125451 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
2543225452 ZigValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index];
......@@ -25434,20 +25454,10 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2543425454 union_field_val->special = ConstValSpecialStatic;
2543525455 union_field_val->type = type_info_union_field_type;
2543625456
25437 ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3);
25457 ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2);
2543825458 inner_fields[1]->special = ConstValSpecialStatic;
25439 inner_fields[1]->type = get_optional_type(ira->codegen, type_info_enum_field_type);
25440
25441 if (fields[1]->data.x_optional == nullptr) {
25442 inner_fields[1]->data.x_optional = nullptr;
25443 } else {
25444 inner_fields[1]->data.x_optional = ira->codegen->pass1_arena->create<ZigValue>();
25445 make_enum_field_val(ira, inner_fields[1]->data.x_optional, union_field->enum_field, type_info_enum_field_type);
25446 }
25447
25448 inner_fields[2]->special = ConstValSpecialStatic;
25449 inner_fields[2]->type = ira->codegen->builtin_types.entry_type;
25450 inner_fields[2]->data.x_type = union_field->type_entry;
25459 inner_fields[1]->type = ira->codegen->builtin_types.entry_type;
25460 inner_fields[1]->data.x_type = union_field->type_entry;
2545125461
2545225462 ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee;
2545325463 init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true);
......@@ -26103,7 +26113,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2610326113 entry->data.structure.layout = layout;
2610426114 entry->data.structure.special = is_tuple ? StructSpecialInferredTuple : StructSpecialNone;
2610526115 entry->data.structure.created_by_at_type = true;
26106 entry->data.structure.decls_scope = create_decls_scope(ira->codegen, nullptr, nullptr, entry, entry, &entry->name);
26116 entry->data.structure.decls_scope = create_decls_scope(
26117 ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name);
2610726118
2610826119 assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
2610926120 assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0);
......@@ -26227,13 +26238,89 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
2622726238 return ira->codegen->invalid_inst_gen->value->type;
2622826239 field->value = *field_int_value;
2622926240 }
26241 return entry;
26242 }
26243 case ZigTypeIdUnion: {
26244 assert(payload->special == ConstValSpecialStatic);
26245 assert(payload->type == ir_type_info_get_type(ira, "Union", nullptr));
26246
26247 ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0);
26248 if (layout_value == nullptr)
26249 return ira->codegen->invalid_inst_gen->value->type;
26250 assert(layout_value->special == ConstValSpecialStatic);
26251 assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr));
26252 ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);
26253
26254 ZigType *tag_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "tag_type", 1);
26255 if (tag_type != nullptr && type_is_invalid(tag_type)) {
26256 return ira->codegen->invalid_inst_gen->value->type;
26257 }
26258 if (tag_type != nullptr && tag_type->id != ZigTypeIdEnum) {
26259 ir_add_error(ira, source_instr, buf_sprintf(
26260 "expected enum type, found '%s'", type_id_name(tag_type->id)));
26261 return ira->codegen->invalid_inst_gen->value->type;
26262 }
26263
26264 ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2);
26265 if (fields_value == nullptr)
26266 return ira->codegen->invalid_inst_gen->value->type;
2623026267
26268 assert(fields_value->special == ConstValSpecialStatic);
26269 assert(is_slice(fields_value->type));
26270 ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index];
26271 ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index];
26272 size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint);
26273
26274 ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 3);
26275 if (decls_value == nullptr)
26276 return ira->codegen->invalid_inst_gen->value->type;
26277
26278 assert(decls_value->special == ConstValSpecialStatic);
26279 assert(is_slice(decls_value->type));
26280 ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index];
26281 size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint);
26282 if (decls_len != 0) {
26283 ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Union.decls must be empty for @Type"));
26284 return ira->codegen->invalid_inst_gen->value->type;
26285 }
26286
26287 ZigType *entry = new_type_table_entry(ZigTypeIdUnion);
26288 buf_init_from_buf(&entry->name,
26289 get_anon_type_name(ira->codegen, ira->old_irb.exec, "union", source_instr->scope, source_instr->source_node, &entry->name));
26290 entry->data.unionation.decl_node = source_instr->source_node;
26291 entry->data.unionation.fields = heap::c_allocator.allocate<TypeUnionField>(fields_len);
26292 entry->data.unionation.fields_by_name.init(fields_len);
26293 entry->data.unionation.decls_scope = create_decls_scope(
26294 ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name);
26295 entry->data.unionation.tag_type = tag_type;
26296 entry->data.unionation.src_field_count = fields_len;
26297 entry->data.unionation.layout = layout;
26298
26299 assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
26300 assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0);
26301 ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val;
26302 assert(fields_arr->special == ConstValSpecialStatic);
26303 assert(fields_arr->data.x_array.special == ConstArraySpecialNone);
26304 for (size_t i = 0; i < fields_len; i++) {
26305 ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i];
26306 assert(field_value->type == ir_type_info_get_type(ira, "UnionField", nullptr));
26307 TypeUnionField *field = &entry->data.unionation.fields[i];
26308 field->name = buf_alloc();
26309 if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name)))
26310 return ira->codegen->invalid_inst_gen->value->type;
26311 if (entry->data.unionation.fields_by_name.put_unique(field->name, field) != nullptr) {
26312 ir_add_error(ira, source_instr, buf_sprintf("duplicate union field '%s'", buf_ptr(field->name)));
26313 return ira->codegen->invalid_inst_gen->value->type;
26314 }
26315 field->decl_node = source_instr->source_node;
26316 ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1);
26317 if (type_value == nullptr)
26318 return ira->codegen->invalid_inst_gen->value->type;
26319 field->type_val = type_value;
26320 field->type_entry = type_value->data.x_type;
26321 }
2623126322 return entry;
2623226323 }
26233 case ZigTypeIdUnion:
26234 ir_add_error(ira, source_instr, buf_sprintf(
26235 "TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));
26236 return ira->codegen->invalid_inst_gen->value->type;
2623726324 case ZigTypeIdFn:
2623826325 case ZigTypeIdBoundFn:
2623926326 ir_add_error(ira, source_instr, buf_sprintf(
src/main.cpp+7-1
......@@ -416,6 +416,7 @@ static int main0(int argc, char **argv) {
416416 const char *test_filter = nullptr;
417417 const char *test_name_prefix = nullptr;
418418 bool test_evented_io = false;
419 bool is_versioned = false;
419420 size_t ver_major = 0;
420421 size_t ver_minor = 0;
421422 size_t ver_patch = 0;
......@@ -870,6 +871,7 @@ static int main0(int argc, char **argv) {
870871 fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
871872 return EXIT_FAILURE;
872873 }
874 is_versioned = true;
873875 ver_major = atoi(buf_ptr(linker_args.at(i)));
874876 } else if (buf_eql_str(arg, "--minor-image-version")) {
875877 i += 1;
......@@ -877,6 +879,7 @@ static int main0(int argc, char **argv) {
877879 fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
878880 return EXIT_FAILURE;
879881 }
882 is_versioned = true;
880883 ver_minor = atoi(buf_ptr(linker_args.at(i)));
881884 } else if (buf_eql_str(arg, "--stack")) {
882885 i += 1;
......@@ -1228,10 +1231,13 @@ static int main0(int argc, char **argv) {
12281231 } else if (strcmp(arg, "--test-name-prefix") == 0) {
12291232 test_name_prefix = argv[i];
12301233 } else if (strcmp(arg, "--ver-major") == 0) {
1234 is_versioned = true;
12311235 ver_major = atoi(argv[i]);
12321236 } else if (strcmp(arg, "--ver-minor") == 0) {
1237 is_versioned = true;
12331238 ver_minor = atoi(argv[i]);
12341239 } else if (strcmp(arg, "--ver-patch") == 0) {
1240 is_versioned = true;
12351241 ver_patch = atoi(argv[i]);
12361242 } else if (strcmp(arg, "--test-cmd") == 0) {
12371243 test_exec_args.append(argv[i]);
......@@ -1590,7 +1596,7 @@ static int main0(int argc, char **argv) {
15901596 g->emit_llvm_ir = emit_llvm_ir;
15911597
15921598 codegen_set_out_name(g, buf_out_name);
1593 codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
1599 codegen_set_lib_version(g, is_versioned, ver_major, ver_minor, ver_patch);
15941600 g->want_single_threaded = want_single_threaded;
15951601 codegen_set_linker_script(g, linker_script);
15961602 g->version_script_path = version_script;
src/target.cpp+21-8
......@@ -779,7 +779,7 @@ const char *target_lib_file_prefix(const ZigTarget *target) {
779779 }
780780}
781781
782const char *target_lib_file_ext(const ZigTarget *target, bool is_static,
782const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned,
783783 size_t version_major, size_t version_minor, size_t version_patch)
784784{
785785 if (target_is_wasm(target)) {
......@@ -799,11 +799,19 @@ const char *target_lib_file_ext(const ZigTarget *target, bool is_static,
799799 if (is_static) {
800800 return ".a";
801801 } else if (target_os_is_darwin(target->os)) {
802 return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
803 version_major, version_minor, version_patch));
802 if (is_versioned) {
803 return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
804 version_major, version_minor, version_patch));
805 } else {
806 return ".dylib";
807 }
804808 } else {
805 return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
806 version_major, version_minor, version_patch));
809 if (is_versioned) {
810 return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
811 version_major, version_minor, version_patch));
812 } else {
813 return ".so";
814 }
807815 }
808816 }
809817}
......@@ -853,6 +861,9 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
853861 case ZigLLVM_riscv32:
854862 case ZigLLVM_riscv64:
855863 case ZigLLVM_mipsel:
864 case ZigLLVM_ppc:
865 case ZigLLVM_ppc64:
866 case ZigLLVM_ppc64le:
856867 return "sp";
857868
858869 case ZigLLVM_wasm32:
......@@ -879,7 +890,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
879890 case ZigLLVM_msp430:
880891 case ZigLLVM_nvptx:
881892 case ZigLLVM_nvptx64:
882 case ZigLLVM_ppc64le:
883893 case ZigLLVM_r600:
884894 case ZigLLVM_renderscript32:
885895 case ZigLLVM_renderscript64:
......@@ -893,8 +903,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
893903 case ZigLLVM_tce:
894904 case ZigLLVM_tcele:
895905 case ZigLLVM_xcore:
896 case ZigLLVM_ppc:
897 case ZigLLVM_ppc64:
898906 case ZigLLVM_ve:
899907 zig_panic("TODO populate this table with stack pointer register name for this CPU architecture");
900908 }
......@@ -1325,6 +1333,11 @@ bool target_is_mips(const ZigTarget *target) {
13251333 target->arch == ZigLLVM_mips64 || target->arch == ZigLLVM_mips64el;
13261334}
13271335
1336bool target_is_ppc(const ZigTarget *target) {
1337 return target->arch == ZigLLVM_ppc || target->arch == ZigLLVM_ppc64 ||
1338 target->arch == ZigLLVM_ppc64le;
1339}
1340
13281341unsigned target_fn_align(const ZigTarget *target) {
13291342 return 16;
13301343}
src/target.hpp+2-1
......@@ -87,7 +87,7 @@ const char *target_asm_file_ext(const ZigTarget *target);
8787const char *target_llvm_ir_file_ext(const ZigTarget *target);
8888const char *target_exe_file_ext(const ZigTarget *target);
8989const char *target_lib_file_prefix(const ZigTarget *target);
90const char *target_lib_file_ext(const ZigTarget *target, bool is_static,
90const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned,
9191 size_t version_major, size_t version_minor, size_t version_patch);
9292
9393bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target);
......@@ -95,6 +95,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type);
9595
9696bool target_is_arm(const ZigTarget *target);
9797bool target_is_mips(const ZigTarget *target);
98bool target_is_ppc(const ZigTarget *target);
9899bool target_allows_addr_zero(const ZigTarget *target);
99100bool target_has_valgrind_support(const ZigTarget *target);
100101bool target_os_is_darwin(Os os);
test/compile_errors.zig+130-1
......@@ -10,6 +10,135 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1010 "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'",
1111 });
1212
13 cases.add("@Type for union with opaque field",
14 \\const TypeInfo = @import("builtin").TypeInfo;
15 \\const Untagged = @Type(.{
16 \\ .Union = .{
17 \\ .layout = .Auto,
18 \\ .tag_type = null,
19 \\ .fields = &[_]TypeInfo.UnionField{
20 \\ .{ .name = "foo", .field_type = @Type(.Opaque) },
21 \\ },
22 \\ .decls = &[_]TypeInfo.Declaration{},
23 \\ },
24 \\});
25 \\export fn entry() void {
26 \\ _ = Untagged{};
27 \\}
28 , &[_][]const u8{
29 "tmp.zig:2:25: error: opaque types have unknown size and therefore cannot be directly embedded in unions",
30 "tmp.zig:13:17: note: referenced here",
31 });
32
33 cases.add("@Type for union with zero fields",
34 \\const TypeInfo = @import("builtin").TypeInfo;
35 \\const Untagged = @Type(.{
36 \\ .Union = .{
37 \\ .layout = .Auto,
38 \\ .tag_type = null,
39 \\ .fields = &[_]TypeInfo.UnionField{},
40 \\ .decls = &[_]TypeInfo.Declaration{},
41 \\ },
42 \\});
43 \\export fn entry() void {
44 \\ _ = Untagged{};
45 \\}
46 , &[_][]const u8{
47 "tmp.zig:2:25: error: unions must have 1 or more fields",
48 "tmp.zig:11:17: note: referenced here",
49 });
50
51 cases.add("@Type for exhaustive enum with zero fields",
52 \\const TypeInfo = @import("builtin").TypeInfo;
53 \\const Tag = @Type(.{
54 \\ .Enum = .{
55 \\ .layout = .Auto,
56 \\ .tag_type = u1,
57 \\ .fields = &[_]TypeInfo.EnumField{},
58 \\ .decls = &[_]TypeInfo.Declaration{},
59 \\ .is_exhaustive = true,
60 \\ },
61 \\});
62 \\export fn entry() void {
63 \\ _ = @intToEnum(Tag, 0);
64 \\}
65 , &[_][]const u8{
66 "tmp.zig:2:20: error: enums must have 1 or more fields",
67 "tmp.zig:12:9: note: referenced here",
68 });
69
70 cases.add("@Type for tagged union with extra union field",
71 \\const TypeInfo = @import("builtin").TypeInfo;
72 \\const Tag = @Type(.{
73 \\ .Enum = .{
74 \\ .layout = .Auto,
75 \\ .tag_type = u1,
76 \\ .fields = &[_]TypeInfo.EnumField{
77 \\ .{ .name = "signed", .value = 0 },
78 \\ .{ .name = "unsigned", .value = 1 },
79 \\ },
80 \\ .decls = &[_]TypeInfo.Declaration{},
81 \\ .is_exhaustive = true,
82 \\ },
83 \\});
84 \\const Tagged = @Type(.{
85 \\ .Union = .{
86 \\ .layout = .Auto,
87 \\ .tag_type = Tag,
88 \\ .fields = &[_]TypeInfo.UnionField{
89 \\ .{ .name = "signed", .field_type = i32 },
90 \\ .{ .name = "unsigned", .field_type = u32 },
91 \\ .{ .name = "arst", .field_type = f32 },
92 \\ },
93 \\ .decls = &[_]TypeInfo.Declaration{},
94 \\ },
95 \\});
96 \\export fn entry() void {
97 \\ var tagged = Tagged{ .signed = -1 };
98 \\ tagged = .{ .unsigned = 1 };
99 \\}
100 , &[_][]const u8{
101 "tmp.zig:14:23: error: enum field not found: 'arst'",
102 "tmp.zig:2:20: note: enum declared here",
103 "tmp.zig:27:24: note: referenced here",
104 });
105
106 cases.add("@Type for tagged union with extra enum field",
107 \\const TypeInfo = @import("builtin").TypeInfo;
108 \\const Tag = @Type(.{
109 \\ .Enum = .{
110 \\ .layout = .Auto,
111 \\ .tag_type = u2,
112 \\ .fields = &[_]TypeInfo.EnumField{
113 \\ .{ .name = "signed", .value = 0 },
114 \\ .{ .name = "unsigned", .value = 1 },
115 \\ .{ .name = "arst", .field_type = 2 },
116 \\ },
117 \\ .decls = &[_]TypeInfo.Declaration{},
118 \\ .is_exhaustive = true,
119 \\ },
120 \\});
121 \\const Tagged = @Type(.{
122 \\ .Union = .{
123 \\ .layout = .Auto,
124 \\ .tag_type = Tag,
125 \\ .fields = &[_]TypeInfo.UnionField{
126 \\ .{ .name = "signed", .field_type = i32 },
127 \\ .{ .name = "unsigned", .field_type = u32 },
128 \\ },
129 \\ .decls = &[_]TypeInfo.Declaration{},
130 \\ },
131 \\});
132 \\export fn entry() void {
133 \\ var tagged = Tagged{ .signed = -1 };
134 \\ tagged = .{ .unsigned = 1 };
135 \\}
136 , &[_][]const u8{
137 "tmp.zig:9:32: error: no member named 'field_type' in struct 'std.builtin.EnumField'",
138 "tmp.zig:18:21: note: referenced here",
139 "tmp.zig:27:18: note: referenced here",
140 });
141
13142 cases.add("@Type with undefined",
14143 \\comptime {
15144 \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } });
......@@ -7419,7 +7548,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
74197548 });
74207549
74217550 cases.add( // fixed bug #2032
7422 "compile diagnostic string for top level decl type",
7551 "compile diagnostic string for top level decl type",
74237552 \\export fn entry() void {
74247553 \\ var foo: u32 = @This(){};
74257554 \\}
test/stack_traces.zig+3-3
......@@ -282,10 +282,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
282282 \\source.zig:10:8: [address] in main (test)
283283 \\ foo();
284284 \\ ^
285 \\start.zig:254:29: [address] in std.start.posixCallMainAndExit (test)
285 \\start.zig:269:29: [address] in std.start.posixCallMainAndExit (test)
286286 \\ return root.main();
287287 \\ ^
288 \\start.zig:128:5: [address] in std.start._start (test)
288 \\start.zig:143:5: [address] in std.start._start (test)
289289 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
290290 \\ ^
291291 \\
......@@ -294,7 +294,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
294294 switch (std.Target.current.cpu.arch) {
295295 .aarch64 => "", // TODO disabled; results in segfault
296296 else =>
297 \\start.zig:128:5: [address] in std.start._start (test)
297 \\start.zig:143:5: [address] in std.start._start (test)
298298 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
299299 \\ ^
300300 \\
test/stage1/behavior/cast.zig+5
......@@ -849,3 +849,8 @@ test "comptime float casts" {
849849 expect(b == 2);
850850 expect(@TypeOf(b) == comptime_int);
851851}
852
853test "cast from ?[*]T to ??[*]T" {
854 const a: ??[*]u8 = @as(?[*]u8, null);
855 expect(a != null and a.? == null);
856}
test/stage1/behavior/translate_c_macros.h+4-1
......@@ -6,4 +6,7 @@ typedef struct Color {
66 unsigned char a;
77} Color;
88#define CLITERAL(type) (type)
9#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
\ No newline at end of file
9#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
10
11#define MY_SIZEOF(x) ((int)sizeof(x))
12#define MY_SIZEOF2(x) ((int)sizeof x)
test/stage1/behavior/translate_c_macros.zig+7-1
......@@ -1,12 +1,18 @@
11const expect = @import("std").testing.expect;
2const expectEqual = @import("std").testing.expectEqual;
23
34const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h"));
45
56test "initializer list expression" {
6 @import("std").testing.expectEqual(h.Color{
7 expectEqual(h.Color{
78 .r = 200,
89 .g = 200,
910 .b = 200,
1011 .a = 255,
1112 }, h.LIGHTGRAY);
1213}
14
15test "sizeof in macros" {
16 expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF(u32));
17 expectEqual(@as(c_int, @sizeOf(u32)), h.MY_SIZEOF2(u32));
18}
test/stage1/behavior/type.zig+103
......@@ -313,3 +313,106 @@ test "Type.Enum" {
313313 testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b));
314314 testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6)));
315315}
316
317test "Type.Union" {
318 const Untagged = @Type(.{
319 .Union = .{
320 .layout = .Auto,
321 .tag_type = null,
322 .fields = &[_]TypeInfo.UnionField{
323 .{ .name = "int", .field_type = i32 },
324 .{ .name = "float", .field_type = f32 },
325 },
326 .decls = &[_]TypeInfo.Declaration{},
327 },
328 });
329 var untagged = Untagged{ .int = 1 };
330 untagged.float = 2.0;
331 untagged.int = 3;
332 testing.expectEqual(@as(i32, 3), untagged.int);
333
334 const PackedUntagged = @Type(.{
335 .Union = .{
336 .layout = .Packed,
337 .tag_type = null,
338 .fields = &[_]TypeInfo.UnionField{
339 .{ .name = "signed", .field_type = i32 },
340 .{ .name = "unsigned", .field_type = u32 },
341 },
342 .decls = &[_]TypeInfo.Declaration{},
343 },
344 });
345 var packed_untagged = PackedUntagged{ .signed = -1 };
346 testing.expectEqual(@as(i32, -1), packed_untagged.signed);
347 testing.expectEqual(~@as(u32, 0), packed_untagged.unsigned);
348
349 const Tag = @Type(.{
350 .Enum = .{
351 .layout = .Auto,
352 .tag_type = u1,
353 .fields = &[_]TypeInfo.EnumField{
354 .{ .name = "signed", .value = 0 },
355 .{ .name = "unsigned", .value = 1 },
356 },
357 .decls = &[_]TypeInfo.Declaration{},
358 .is_exhaustive = true,
359 },
360 });
361 const Tagged = @Type(.{
362 .Union = .{
363 .layout = .Auto,
364 .tag_type = Tag,
365 .fields = &[_]TypeInfo.UnionField{
366 .{ .name = "signed", .field_type = i32 },
367 .{ .name = "unsigned", .field_type = u32 },
368 },
369 .decls = &[_]TypeInfo.Declaration{},
370 },
371 });
372 var tagged = Tagged{ .signed = -1 };
373 testing.expectEqual(Tag.signed, tagged);
374 tagged = .{ .unsigned = 1 };
375 testing.expectEqual(Tag.unsigned, tagged);
376}
377
378test "Type.Union from Type.Enum" {
379 const Tag = @Type(.{
380 .Enum = .{
381 .layout = .Auto,
382 .tag_type = u0,
383 .fields = &[_]TypeInfo.EnumField{
384 .{ .name = "working_as_expected", .value = 0 },
385 },
386 .decls = &[_]TypeInfo.Declaration{},
387 .is_exhaustive = true,
388 },
389 });
390 const T = @Type(.{
391 .Union = .{
392 .layout = .Auto,
393 .tag_type = Tag,
394 .fields = &[_]TypeInfo.UnionField{
395 .{ .name = "working_as_expected", .field_type = u32 },
396 },
397 .decls = &[_]TypeInfo.Declaration{},
398 },
399 });
400 _ = T;
401 _ = @typeInfo(T).Union;
402}
403
404test "Type.Union from regular enum" {
405 const E = enum { working_as_expected = 0 };
406 const T = @Type(.{
407 .Union = .{
408 .layout = .Auto,
409 .tag_type = E,
410 .fields = &[_]TypeInfo.UnionField{
411 .{ .name = "working_as_expected", .field_type = u32 },
412 },
413 .decls = &[_]TypeInfo.Declaration{},
414 },
415 });
416 _ = T;
417 _ = @typeInfo(T).Union;
418}
test/stage1/behavior/type_info.zig-4
......@@ -198,8 +198,6 @@ fn testUnion() void {
198198 expect(typeinfo_info.Union.layout == .Auto);
199199 expect(typeinfo_info.Union.tag_type.? == TypeId);
200200 expect(typeinfo_info.Union.fields.len == 25);
201 expect(typeinfo_info.Union.fields[4].enum_field != null);
202 expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
203201 expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
204202 expect(typeinfo_info.Union.decls.len == 21);
205203
......@@ -213,7 +211,6 @@ fn testUnion() void {
213211 expect(notag_union_info.Union.tag_type == null);
214212 expect(notag_union_info.Union.layout == .Auto);
215213 expect(notag_union_info.Union.fields.len == 2);
216 expect(notag_union_info.Union.fields[0].enum_field == null);
217214 expect(notag_union_info.Union.fields[1].field_type == u32);
218215
219216 const TestExternUnion = extern union {
......@@ -223,7 +220,6 @@ fn testUnion() void {
223220 const extern_union_info = @typeInfo(TestExternUnion);
224221 expect(extern_union_info.Union.layout == .Extern);
225222 expect(extern_union_info.Union.tag_type == null);
226 expect(extern_union_info.Union.fields[0].enum_field == null);
227223 expect(extern_union_info.Union.fields[0].field_type == *c_void);
228224}
229225
test/translate_c.zig+3
......@@ -2761,12 +2761,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
27612761 cases.add("macro cast",
27622762 \\#define FOO(bar) baz((void *)(baz))
27632763 \\#define BAR (void*) a
2764 \\#define BAZ (uint32_t)(2)
27642765 , &[_][]const u8{
27652766 \\pub inline fn FOO(bar: anytype) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {
27662767 \\ return baz((@import("std").meta.cast(?*c_void, baz)));
27672768 \\}
27682769 ,
27692770 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));
2771 ,
2772 \\pub const BAZ = (@import("std").meta.cast(u32, 2));
27702773 });
27712774
27722775 cases.add("macro with cast to unsigned short, long, and long long",