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 @@...@@ -1,7 +1,7 @@
1image: freebsd/latest1image: freebsd/latest
2secrets:2secrets:
3 - 6c60aaee-92e7-4e7d-812c-114817689b4d3 - 51bfddf5-86a6-4e01-8576-358c72a4a0a4
4 - dd0bd962-7664-4d3e-b0f3-41c9ee96b8b84 - 5cfede76-914e-4071-893e-e5e2e6ae3cea
5sources:5sources:
6 - https://github.com/ziglang/zig6 - https://github.com/ziglang/zig
7tasks:7tasks:
README.md+18
...@@ -68,3 +68,21 @@ make install...@@ -68,3 +68,21 @@ make install
68##### Windows68##### Windows
6969
70See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows70See 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 {...@@ -123,7 +123,14 @@ pub fn build(b: *Builder) !void {
123 .source_dir = "lib",123 .source_dir = "lib",
124 .install_dir = .Lib,124 .install_dir = .Lib,
125 .install_subdir = "zig",125 .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 },
127 });134 });
128135
129 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");136 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%...@@ -24,7 +24,7 @@ cd %ZIGBUILDDIR%
24cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b24cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b
25msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b25msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b
2626
27"%ZIGINSTALLDIR%\bin\zig.exe" build test -Dskip-compile-errors || exit /b27"%ZIGINSTALLDIR%\bin\zig.exe" build test -Dskip-non-native -Dskip-compile-errors || exit /b
2828
29set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem"29set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem"
30SET "MSYSTEM=MINGW64"30SET "MSYSTEM=MINGW64"
ci/srht/freebsd_script+4-6
...@@ -28,11 +28,8 @@ make $JOBS install...@@ -28,11 +28,8 @@ make $JOBS install
2828
29release/bin/zig build test-fmt29release/bin/zig build test-fmt
30release/bin/zig build test-behavior30release/bin/zig build test-behavior
3131# TODO get these tests passing on freebsd and re-enable
32# This test is disabled because it triggers "out of memory" on the sr.ht CI service.32#release/bin/zig build test-std
33# See https://github.com/ziglang/zig/issues/3210
34# release/bin/zig build test-std
35
36release/bin/zig build test-compiler-rt33release/bin/zig build test-compiler-rt
37release/bin/zig build test-compare-output34release/bin/zig build test-compare-output
38release/bin/zig build test-standalone35release/bin/zig build test-standalone
...@@ -44,7 +41,8 @@ release/bin/zig build test-translate-c...@@ -44,7 +41,8 @@ release/bin/zig build test-translate-c
44release/bin/zig build test-run-translated-c41release/bin/zig build test-run-translated-c
45# TODO disabled until we are shipping self-hosted42# TODO disabled until we are shipping self-hosted
46#release/bin/zig build test-gen-h43#release/bin/zig build test-gen-h
47release/bin/zig build test-compile-errors44# TODO disabled to save time and hit that 45 minute limit
45#release/bin/zig build test-compile-errors
48release/bin/zig build docs46release/bin/zig build docs
4947
50if [ -f ~/.s3cfg ]; then48if [ -f ~/.s3cfg ]; then
ci/srht/on_master_success+2-2
...@@ -23,7 +23,7 @@ packages:...@@ -23,7 +23,7 @@ packages:
23 - jq23 - jq
24 - xz24 - xz
25secrets:25secrets:
26 - 6c60aaee-92e7-4e7d-812c-114817689b4d26 - 51bfddf5-86a6-4e01-8576-358c72a4a0a4
27sources:27sources:
28 - https://github.com/ziglang/zig28 - https://github.com/ziglang/zig
29tasks:29tasks:
...@@ -36,4 +36,4 @@ jq <$YML_FILE -sR '{...@@ -36,4 +36,4 @@ jq <$YML_FILE -sR '{
36 -H Authorization:"token $OAUTH_TOKEN" \36 -H Authorization:"token $OAUTH_TOKEN" \
37 -H Content-Type:application/json \37 -H Content-Type:application/json \
38 -X POST \38 -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({...@@ -9728,7 +9728,7 @@ const c = @cImport({
9728 <li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#}9728 <li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#}
9729 please!</li>9729 please!</li>
9730 </ul>9730 </ul>
9731 <p>When a C pointer is pointing to a single struct (not an array), deference the C pointer to9731 <p>When a C pointer is pointing to a single struct (not an array), dereference the C pointer to
9732 access to the struct's fields or member data. That syntax looks like9732 access to the struct's fields or member data. That syntax looks like
9733 this: </p>9733 this: </p>
9734 <p>{#syntax#}ptr_to_struct.*.struct_member{#endsyntax#}</p>9734 <p>{#syntax#}ptr_to_struct.*.struct_member{#endsyntax#}</p>
lib/std/build.zig+82-44
...@@ -258,9 +258,14 @@ pub const Builder = struct {...@@ -258,9 +258,14 @@ pub const Builder = struct {
258 }));258 }));
259 }259 }
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 {
262 const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null;267 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);
264 }269 }
265270
266 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {271 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
...@@ -338,11 +343,13 @@ pub const Builder = struct {...@@ -338,11 +343,13 @@ pub const Builder = struct {
338 return TranslateCStep.create(self, source);343 return TranslateCStep.create(self, source);
339 }344 }
340345
341 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version {346 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind {
342 return Version{347 return .{
343 .major = major,348 .versioned = .{
344 .minor = minor,349 .major = major,
345 .patch = patch,350 .minor = minor,
351 .patch = patch,
352 },
346 };353 };
347 }354 }
348355
...@@ -1048,6 +1055,7 @@ pub const Builder = struct {...@@ -1048,6 +1055,7 @@ pub const Builder = struct {
1048 .Bin => self.exe_dir,1055 .Bin => self.exe_dir,
1049 .Lib => self.lib_dir,1056 .Lib => self.lib_dir,
1050 .Header => self.h_dir,1057 .Header => self.h_dir,
1058 .Custom => |path| fs.path.join(self.allocator, &[_][]const u8{ self.install_path, path }) catch unreachable,
1051 };1059 };
1052 return fs.path.resolve(1060 return fs.path.resolve(
1053 self.allocator,1061 self.allocator,
...@@ -1166,7 +1174,7 @@ pub const LibExeObjStep = struct {...@@ -1166,7 +1174,7 @@ pub const LibExeObjStep = struct {
1166 version_script: ?[]const u8 = null,1174 version_script: ?[]const u8 = null,
1167 out_filename: []const u8,1175 out_filename: []const u8,
1168 is_dynamic: bool,1176 is_dynamic: bool,
1169 version: Version,1177 version: ?Version,
1170 build_mode: builtin.Mode,1178 build_mode: builtin.Mode,
1171 kind: Kind,1179 kind: Kind,
1172 major_only_filename: []const u8,1180 major_only_filename: []const u8,
...@@ -1180,6 +1188,7 @@ pub const LibExeObjStep = struct {...@@ -1180,6 +1188,7 @@ pub const LibExeObjStep = struct {
1180 emit_llvm_ir: bool = false,1188 emit_llvm_ir: bool = false,
1181 emit_asm: bool = false,1189 emit_asm: bool = false,
1182 emit_bin: bool = true,1190 emit_bin: bool = true,
1191 emit_docs: bool = false,
1183 emit_h: bool = false,1192 emit_h: bool = false,
1184 bundle_compiler_rt: bool,1193 bundle_compiler_rt: bool,
1185 disable_stack_probing: bool,1194 disable_stack_probing: bool,
...@@ -1212,6 +1221,8 @@ pub const LibExeObjStep = struct {...@@ -1212,6 +1221,8 @@ pub const LibExeObjStep = struct {
1212 is_linking_libc: bool = false,1221 is_linking_libc: bool = false,
1213 vcpkg_bin_path: ?[]const u8 = null,1222 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,
1215 installed_path: ?[]const u8,1226 installed_path: ?[]const u8,
1216 install_step: ?*InstallArtifactStep,1227 install_step: ?*InstallArtifactStep,
12171228
...@@ -1268,33 +1279,41 @@ pub const LibExeObjStep = struct {...@@ -1268,33 +1279,41 @@ pub const LibExeObjStep = struct {
1268 Test,1279 Test,
1269 };1280 };
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 {
1272 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1288 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 });
1274 return self;1293 return self;
1275 }1294 }
12761295
1277 pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {1296 pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
1278 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1297 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);
1280 return self;1299 return self;
1281 }1300 }
12821301
1283 pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {1302 pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
1284 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1303 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);
1286 return self;1305 return self;
1287 }1306 }
12881307
1289 pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep {1308 pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep {
1290 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1309 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);
1292 return self;1311 return self;
1293 }1312 }
12941313
1295 pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {1314 pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
1296 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1315 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);
1298 return self;1317 return self;
1299 }1318 }
13001319
...@@ -1304,7 +1323,7 @@ pub const LibExeObjStep = struct {...@@ -1304,7 +1323,7 @@ pub const LibExeObjStep = struct {
1304 root_src: ?FileSource,1323 root_src: ?FileSource,
1305 kind: Kind,1324 kind: Kind,
1306 is_dynamic: bool,1325 is_dynamic: bool,
1307 ver: Version,1326 ver: ?Version,
1308 ) LibExeObjStep {1327 ) LibExeObjStep {
1309 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {1328 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
1310 panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});1329 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 {...@@ -1348,6 +1367,7 @@ pub const LibExeObjStep = struct {
1348 .rdynamic = false,1367 .rdynamic = false,
1349 .output_dir = null,1368 .output_dir = null,
1350 .single_threaded = false,1369 .single_threaded = false,
1370 .override_dest_dir = null,
1351 .installed_path = null,1371 .installed_path = null,
1352 .install_step = null,1372 .install_step = null,
1353 };1373 };
...@@ -1375,17 +1395,17 @@ pub const LibExeObjStep = struct {...@@ -1375,17 +1395,17 @@ pub const LibExeObjStep = struct {
1375 self.target.staticLibSuffix(),1395 self.target.staticLibSuffix(),
1376 });1396 });
1377 self.out_lib_filename = self.out_filename;1397 self.out_lib_filename = self.out_filename;
1378 } else {1398 } else if (self.version) |version| {
1379 if (self.target.isDarwin()) {1399 if (self.target.isDarwin()) {
1380 self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", .{1400 self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", .{
1381 self.name,1401 self.name,
1382 self.version.major,1402 version.major,
1383 self.version.minor,1403 version.minor,
1384 self.version.patch,1404 version.patch,
1385 });1405 });
1386 self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{1406 self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{
1387 self.name,1407 self.name,
1388 self.version.major,1408 version.major,
1389 });1409 });
1390 self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name});1410 self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name});
1391 self.out_lib_filename = self.out_filename;1411 self.out_lib_filename = self.out_filename;
...@@ -1395,14 +1415,25 @@ pub const LibExeObjStep = struct {...@@ -1395,14 +1415,25 @@ pub const LibExeObjStep = struct {
1395 } else {1415 } else {
1396 self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", .{1416 self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", .{
1397 self.name,1417 self.name,
1398 self.version.major,1418 version.major,
1399 self.version.minor,1419 version.minor,
1400 self.version.patch,1420 version.patch,
1401 });1421 });
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 });
1403 self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name});1423 self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name});
1404 self.out_lib_filename = self.out_filename;1424 self.out_lib_filename = self.out_filename;
1405 }1425 }
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 }
1406 }1437 }
1407 },1438 },
1408 }1439 }
...@@ -2003,6 +2034,7 @@ pub const LibExeObjStep = struct {...@@ -2003,6 +2034,7 @@ pub const LibExeObjStep = struct {
2003 if (self.emit_llvm_ir) try zig_args.append("-femit-llvm-ir");2034 if (self.emit_llvm_ir) try zig_args.append("-femit-llvm-ir");
2004 if (self.emit_asm) try zig_args.append("-femit-asm");2035 if (self.emit_asm) try zig_args.append("-femit-asm");
2005 if (!self.emit_bin) try zig_args.append("-fno-emit-bin");2036 if (!self.emit_bin) try zig_args.append("-fno-emit-bin");
2037 if (self.emit_docs) try zig_args.append("-femit-docs");
2006 if (self.emit_h) try zig_args.append("-femit-h");2038 if (self.emit_h) try zig_args.append("-femit-h");
20072039
2008 if (self.strip) {2040 if (self.strip) {
...@@ -2037,14 +2069,16 @@ pub const LibExeObjStep = struct {...@@ -2037,14 +2069,16 @@ pub const LibExeObjStep = struct {
2037 zig_args.append(self.name) catch unreachable;2069 zig_args.append(self.name) catch unreachable;
20382070
2039 if (self.kind == Kind.Lib and self.is_dynamic) {2071 if (self.kind == Kind.Lib and self.is_dynamic) {
2040 zig_args.append("--ver-major") catch unreachable;2072 if (self.version) |version| {
2041 zig_args.append(builder.fmt("{}", .{self.version.major})) catch unreachable;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;2076 zig_args.append("--ver-minor") catch unreachable;
2044 zig_args.append(builder.fmt("{}", .{self.version.minor})) catch unreachable;2077 zig_args.append(builder.fmt("{}", .{version.minor})) catch unreachable;
20452078
2046 zig_args.append("--ver-patch") catch unreachable;2079 zig_args.append("--ver-patch") catch unreachable;
2047 zig_args.append(builder.fmt("{}", .{self.version.patch})) catch unreachable;2080 zig_args.append(builder.fmt("{}", .{version.patch})) catch unreachable;
2081 }
2048 }2082 }
2049 if (self.is_dynamic) {2083 if (self.is_dynamic) {
2050 try zig_args.append("-dynamic");2084 try zig_args.append("-dynamic");
...@@ -2285,7 +2319,7 @@ pub const LibExeObjStep = struct {...@@ -2285,7 +2319,7 @@ pub const LibExeObjStep = struct {
2285 }2319 }
2286 }2320 }
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()) {
2289 try doAtomicSymLinks(builder.allocator, self.getOutputPath(), self.major_only_filename, self.name_only_filename);2323 try doAtomicSymLinks(builder.allocator, self.getOutputPath(), self.major_only_filename, self.name_only_filename);
2290 }2324 }
2291 }2325 }
...@@ -2309,17 +2343,17 @@ pub const InstallArtifactStep = struct {...@@ -2309,17 +2343,17 @@ pub const InstallArtifactStep = struct {
2309 .builder = builder,2343 .builder = builder,
2310 .step = Step.init(.InstallArtifact, builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make),2344 .step = Step.init(.InstallArtifact, builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make),
2311 .artifact = artifact,2345 .artifact = artifact,
2312 .dest_dir = switch (artifact.kind) {2346 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
2313 .Obj => unreachable,2347 .Obj => unreachable,
2314 .Test => unreachable,2348 .Test => unreachable,
2315 .Exe => .Bin,2349 .Exe => InstallDir{ .Bin = {} },
2316 .Lib => .Lib,2350 .Lib => InstallDir{ .Lib = {} },
2317 },2351 },
2318 .pdb_dir = if (artifact.producesPdbFile()) blk: {2352 .pdb_dir = if (artifact.producesPdbFile()) blk: {
2319 if (artifact.kind == .Exe) {2353 if (artifact.kind == .Exe) {
2320 break :blk InstallDir.Bin;2354 break :blk InstallDir{ .Bin = {} };
2321 } else {2355 } else {
2322 break :blk InstallDir.Lib;2356 break :blk InstallDir{ .Lib = {} };
2323 }2357 }
2324 } else null,2358 } else null,
2325 .h_dir = if (artifact.kind == .Lib and artifact.emit_h) .Header else null,2359 .h_dir = if (artifact.kind == .Lib and artifact.emit_h) .Header else null,
...@@ -2329,8 +2363,10 @@ pub const InstallArtifactStep = struct {...@@ -2329,8 +2363,10 @@ pub const InstallArtifactStep = struct {
23292363
2330 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);2364 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
2331 if (self.artifact.isDynamicLibrary()) {2365 if (self.artifact.isDynamicLibrary()) {
2332 builder.pushInstalledFile(.Lib, artifact.major_only_filename);2366 if (self.artifact.version != null) {
2333 builder.pushInstalledFile(.Lib, artifact.name_only_filename);2367 builder.pushInstalledFile(.Lib, artifact.major_only_filename);
2368 builder.pushInstalledFile(.Lib, artifact.name_only_filename);
2369 }
2334 if (self.artifact.target.isWindows()) {2370 if (self.artifact.target.isWindows()) {
2335 builder.pushInstalledFile(.Lib, artifact.out_lib_filename);2371 builder.pushInstalledFile(.Lib, artifact.out_lib_filename);
2336 }2372 }
...@@ -2350,7 +2386,7 @@ pub const InstallArtifactStep = struct {...@@ -2350,7 +2386,7 @@ pub const InstallArtifactStep = struct {
23502386
2351 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);2387 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
2352 try builder.updateFile(self.artifact.getOutputPath(), full_dest_path);2388 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()) {
2354 try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename);2390 try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename);
2355 }2391 }
2356 if (self.pdb_dir) |pdb_dir| {2392 if (self.pdb_dir) |pdb_dir| {
...@@ -2615,11 +2651,13 @@ const VcpkgRootStatus = enum {...@@ -2615,11 +2651,13 @@ const VcpkgRootStatus = enum {
26152651
2616pub const VcpkgLinkage = std.builtin.LinkMode;2652pub const VcpkgLinkage = std.builtin.LinkMode;
26172653
2618pub const InstallDir = enum {2654pub const InstallDir = union(enum) {
2619 Prefix,2655 Prefix: void,
2620 Lib,2656 Lib: void,
2621 Bin,2657 Bin: void,
2622 Header,2658 Header: void,
2659 /// A path relative to the prefix
2660 Custom: []const u8,
2623};2661};
26242662
2625pub const InstalledFile = struct {2663pub const InstalledFile = struct {
lib/std/builtin.zig-1
...@@ -317,7 +317,6 @@ pub const TypeInfo = union(enum) {...@@ -317,7 +317,6 @@ pub const TypeInfo = union(enum) {
317 /// therefore must be kept in sync with the compiler implementation.317 /// therefore must be kept in sync with the compiler implementation.
318 pub const UnionField = struct {318 pub const UnionField = struct {
319 name: []const u8,319 name: []const u8,
320 enum_field: ?EnumField,
321 field_type: type,320 field_type: type,
322 };321 };
323322
lib/std/c.zig+11-4
...@@ -132,8 +132,6 @@ pub usingnamespace switch (builtin.os.tag) {...@@ -132,8 +132,6 @@ pub usingnamespace switch (builtin.os.tag) {
132 },132 },
133};133};
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;
137pub extern "c" fn rmdir(path: [*:0]const u8) c_int;135pub extern "c" fn rmdir(path: [*:0]const u8) c_int;
138pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8;136pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8;
139pub extern "c" fn sysctl(name: [*]const c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;137pub 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) {...@@ -237,8 +235,15 @@ pub usingnamespace switch (builtin.os.tag) {
237235
238pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;236pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
239pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;237pub 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;238
241pub extern "c" fn setuid(uid: c_uint) c_int;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
243pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;248pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;
244pub extern "c" fn malloc(usize) ?*c_void;249pub extern "c" fn malloc(usize) ?*c_void;
...@@ -335,3 +340,5 @@ pub extern "c" fn sync() void;...@@ -335,3 +340,5 @@ pub extern "c" fn sync() void;
335pub extern "c" fn syncfs(fd: c_int) c_int;340pub extern "c" fn syncfs(fd: c_int) c_int;
336pub extern "c" fn fsync(fd: c_int) c_int;341pub extern "c" fn fsync(fd: c_int) c_int;
337pub extern "c" fn fdatasync(fd: c_int) c_int;342pub 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 {...@@ -35,6 +35,15 @@ pub const onetimeauth = struct {
35 pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;35 pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
36};36};
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
38/// Core functions, that should rarely be used directly by applications.47/// Core functions, that should rarely be used directly by applications.
39pub const core = struct {48pub const core = struct {
40 pub const aes = @import("crypto/aes.zig");49 pub const aes = @import("crypto/aes.zig");
...@@ -70,6 +79,20 @@ const std = @import("std.zig");...@@ -70,6 +79,20 @@ const std = @import("std.zig");
70pub const randomBytes = std.os.getrandom;79pub const randomBytes = std.os.getrandom;
7180
72test "crypto" {81test "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
73 _ = @import("crypto/aes.zig");96 _ = @import("crypto/aes.zig");
74 _ = @import("crypto/blake2.zig");97 _ = @import("crypto/blake2.zig");
75 _ = @import("crypto/blake3.zig");98 _ = @import("crypto/blake3.zig");
...@@ -77,6 +100,7 @@ test "crypto" {...@@ -77,6 +100,7 @@ test "crypto" {
77 _ = @import("crypto/gimli.zig");100 _ = @import("crypto/gimli.zig");
78 _ = @import("crypto/hmac.zig");101 _ = @import("crypto/hmac.zig");
79 _ = @import("crypto/md5.zig");102 _ = @import("crypto/md5.zig");
103 _ = @import("crypto/pbkdf2.zig");
80 _ = @import("crypto/poly1305.zig");104 _ = @import("crypto/poly1305.zig");
81 _ = @import("crypto/sha1.zig");105 _ = @import("crypto/sha1.zig");
82 _ = @import("crypto/sha2.zig");106 _ = @import("crypto/sha2.zig");
lib/std/crypto/benchmark.zig+2-2
...@@ -5,8 +5,8 @@...@@ -5,8 +5,8 @@
5// and substantial portions of the software.5// and substantial portions of the software.
6// zig run benchmark.zig --release-fast --override-lib-dir ..6// zig run benchmark.zig --release-fast --override-lib-dir ..
77
8const builtin = @import("builtin");8const std = @import("../std.zig");
9const std = @import("std");9const builtin = std.builtin;
10const mem = std.mem;10const mem = std.mem;
11const time = std.time;11const time = std.time;
12const Timer = time.Timer;12const Timer = time.Timer;
lib/std/crypto/blake2.zig+4-4
...@@ -3,10 +3,10 @@...@@ -3,10 +3,10 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const mem = @import("../mem.zig");6const std = @import("../std.zig");
7const builtin = @import("builtin");7const mem = std.mem;
8const debug = @import("../debug.zig");8const math = std.math;
9const math = @import("../math.zig");9const debug = std.debug;
10const htest = @import("test.zig");10const htest = @import("test.zig");
1111
12const RoundParam = struct {12const RoundParam = struct {
lib/std/crypto/chacha20.zig-2
...@@ -7,10 +7,8 @@...@@ -7,10 +7,8 @@
77
8const std = @import("../std.zig");8const std = @import("../std.zig");
9const mem = std.mem;9const mem = std.mem;
10const endian = std.endian;
11const assert = std.debug.assert;10const assert = std.debug.assert;
12const testing = std.testing;11const testing = std.testing;
13const builtin = @import("builtin");
14const maxInt = std.math.maxInt;12const maxInt = std.math.maxInt;
15const Poly1305 = std.crypto.onetimeauth.Poly1305;13const Poly1305 = std.crypto.onetimeauth.Poly1305;
1614
lib/std/crypto/md5.zig+4-6
...@@ -3,12 +3,10 @@...@@ -3,12 +3,10 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const mem = @import("../mem.zig");6const std = @import("../std.zig");
7const math = @import("../math.zig");7const mem = std.mem;
8const endian = @import("../endian.zig");8const math = std.math;
9const builtin = @import("builtin");9const debug = std.debug;
10const debug = @import("../debug.zig");
11const fmt = @import("../fmt.zig");
1210
13const RoundParam = struct {11const RoundParam = struct {
14 a: usize,12 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 @@...@@ -3,7 +3,7 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("std");6const std = @import("../std.zig");
7const mem = std.mem;7const mem = std.mem;
88
9pub const Poly1305 = struct {9pub const Poly1305 = struct {
lib/std/crypto/sha1.zig+4-5
...@@ -3,11 +3,10 @@...@@ -3,11 +3,10 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const mem = @import("../mem.zig");6const std = @import("../std.zig");
7const math = @import("../math.zig");7const mem = std.mem;
8const endian = @import("../endian.zig");8const math = std.math;
9const debug = @import("../debug.zig");9const debug = std.debug;
10const builtin = @import("builtin");
1110
12const RoundParam = struct {11const RoundParam = struct {
13 a: usize,12 a: usize,
lib/std/crypto/sha2.zig+4-5
...@@ -3,11 +3,10 @@...@@ -3,11 +3,10 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const mem = @import("../mem.zig");6const std = @import("../std.zig");
7const math = @import("../math.zig");7const mem = std.mem;
8const endian = @import("../endian.zig");8const math = std.math;
9const debug = @import("../debug.zig");9const debug = std.debug;
10const builtin = @import("builtin");
11const htest = @import("test.zig");10const htest = @import("test.zig");
1211
13/////////////////////12/////////////////////
lib/std/crypto/sha3.zig+4-5
...@@ -3,11 +3,10 @@...@@ -3,11 +3,10 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const mem = @import("../mem.zig");6const std = @import("../std.zig");
7const math = @import("../math.zig");7const mem = std.mem;
8const endian = @import("../endian.zig");8const math = std.math;
9const debug = @import("../debug.zig");9const debug = std.debug;
10const builtin = @import("builtin");
11const htest = @import("test.zig");10const htest = @import("test.zig");
1211
13pub const Sha3_224 = Keccak(224, 0x06);12pub 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)...@@ -218,8 +218,9 @@ fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize)
218 }218 }
219219
220 /// Return an authentication tag for the current state220 /// Return an authentication tag for the current state
221 /// Assumes `out` is less than or equal to `mac_length`.
221 pub fn final(self: *Self, out: []u8) void {222 pub fn final(self: *Self, out: []u8) void {
222 std.debug.assert(out.len >= mac_length);223 std.debug.assert(out.len <= mac_length);
223 mem.writeIntLittle(T, out[0..mac_length], self.state.final(self.buf[0..self.buf_len]));224 mem.writeIntLittle(T, out[0..mac_length], self.state.final(self.buf[0..self.buf_len]));
224 }225 }
225226
lib/std/crypto/test.zig-1
...@@ -5,7 +5,6 @@...@@ -5,7 +5,6 @@
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("../std.zig");6const std = @import("../std.zig");
7const testing = std.testing;7const testing = std.testing;
8const mem = std.mem;
9const fmt = std.fmt;8const fmt = std.fmt;
109
11// Hash using the specified hasher `H` asserting `expected == H(input)`.10// Hash using the specified hasher `H` asserting `expected == H(input)`.
lib/std/elf.zig+1-1
...@@ -471,7 +471,7 @@ pub const SectionHeaderIterator = struct {...@@ -471,7 +471,7 @@ pub const SectionHeaderIterator = struct {
471471
472 if (self.elf_header.is_64) {472 if (self.elf_header.is_64) {
473 var shdr: Elf64_Shdr = undefined;473 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;
475 try preadNoEof(self.file, mem.asBytes(&shdr), offset);475 try preadNoEof(self.file, mem.asBytes(&shdr), offset);
476476
477 // ELF endianness matches native endianness.477 // ELF endianness matches native endianness.
lib/std/event/loop.zig+2-1
...@@ -112,7 +112,8 @@ pub const Loop = struct {...@@ -112,7 +112,8 @@ pub const Loop = struct {
112 /// have the correct pointer value.112 /// have the correct pointer value.
113 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765113 /// https://github.com/ziglang/zig/issues/2761 and https://github.com/ziglang/zig/issues/2765
114 pub fn init(self: *Loop) !void {114 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)) {
116 return self.initSingleThreaded();117 return self.initSingleThreaded();
117 } else {118 } else {
118 return self.initMultiThreaded();119 return self.initMultiThreaded();
lib/std/fmt.zig+57-84
...@@ -22,7 +22,7 @@ pub const Alignment = enum {...@@ -22,7 +22,7 @@ pub const Alignment = enum {
22pub const FormatOptions = struct {22pub const FormatOptions = struct {
23 precision: ?usize = null,23 precision: ?usize = null,
24 width: ?usize = null,24 width: ?usize = null,
25 alignment: Alignment = .Left,25 alignment: Alignment = .Right,
26 fill: u8 = ' ',26 fill: u8 = ' ',
27};27};
2828
...@@ -327,7 +327,7 @@ pub fn formatType(...@@ -327,7 +327,7 @@ pub fn formatType(
327 max_depth: usize,327 max_depth: usize,
328) @TypeOf(writer).Error!void {328) @TypeOf(writer).Error!void {
329 if (comptime std.mem.eql(u8, fmt, "*")) {329 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))));
331 try writer.writeAll("@");331 try writer.writeAll("@");
332 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);332 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
333 return;333 return;
...@@ -399,7 +399,7 @@ pub fn formatType(...@@ -399,7 +399,7 @@ pub fn formatType(
399 try writer.writeAll(@tagName(@as(UnionTagType, value)));399 try writer.writeAll(@tagName(@as(UnionTagType, value)));
400 try writer.writeAll(" = ");400 try writer.writeAll(" = ");
401 inline for (info.fields) |u_field| {401 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)) {
403 try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);403 try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);
404 }404 }
405 }405 }
...@@ -631,26 +631,22 @@ pub fn formatBuf(...@@ -631,26 +631,22 @@ pub fn formatBuf(
631 writer: anytype,631 writer: anytype,
632) !void {632) !void {
633 const width = options.width orelse buf.len;633 const width = options.width orelse buf.len;
634 var padding = if (width > buf.len) (width - buf.len) else 0;634 const padding = if (width > buf.len) (width - buf.len) else 0;
635 const pad_byte = [1]u8{options.fill};635
636 switch (options.alignment) {636 switch (options.alignment) {
637 .Left => {637 .Left => {
638 try writer.writeAll(buf);638 try writer.writeAll(buf);
639 while (padding > 0) : (padding -= 1) {639 try writer.writeByteNTimes(options.fill, padding);
640 try writer.writeAll(&pad_byte);
641 }
642 },640 },
643 .Center => {641 .Center => {
644 const padl = padding / 2;642 const left_padding = padding / 2;
645 var i: usize = 0;643 const right_padding = (padding + 1) / 2;
646 while (i < padl) : (i += 1) try writer.writeAll(&pad_byte);644 try writer.writeByteNTimes(options.fill, left_padding);
647 try writer.writeAll(buf);645 try writer.writeAll(buf);
648 while (i < padding) : (i += 1) try writer.writeAll(&pad_byte);646 try writer.writeByteNTimes(options.fill, right_padding);
649 },647 },
650 .Right => {648 .Right => {
651 while (padding > 0) : (padding -= 1) {649 try writer.writeByteNTimes(options.fill, padding);
652 try writer.writeAll(&pad_byte);
653 }
654 try writer.writeAll(buf);650 try writer.writeAll(buf);
655 },651 },
656 }652 }
...@@ -941,61 +937,27 @@ pub fn formatInt(...@@ -941,61 +937,27 @@ pub fn formatInt(
941 options: FormatOptions,937 options: FormatOptions,
942 writer: anytype,938 writer: anytype,
943) !void {939) !void {
940 assert(base >= 2);
941
944 const int_value = if (@TypeOf(value) == comptime_int) blk: {942 const int_value = if (@TypeOf(value) == comptime_int) blk: {
945 const Int = math.IntFittingRange(value, value);943 const Int = math.IntFittingRange(value, value);
946 break :blk @as(Int, value);944 break :blk @as(Int, value);
947 } else945 } else
948 value;946 value;
949947
950 if (@typeInfo(@TypeOf(int_value)).Int.is_signed) {948 const value_info = @typeInfo(@TypeOf(int_value)).Int;
951 return formatIntSigned(int_value, base, uppercase, options, writer);
952 } else {
953 return formatIntUnsigned(int_value, base, uppercase, options, writer);
954 }
955}
956949
957fn formatIntSigned(950 // The type must have the same size as `base` or be wider in order for the
958 value: anytype,951 // division to work
959 base: u8,952 const min_int_bits = comptime math.max(value_info.bits, 8);
960 uppercase: bool,953 const MinInt = std.meta.Int(false, min_int_bits);
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}
983954
984fn formatIntUnsigned(955 const abs_value = math.absCast(int_value);
985 value: anytype,956 // The worst case in terms of space needed is base 2, plus 1 for the sign
986 base: u8,957 var buf: [1 + math.max(value_info.bits, 1)]u8 = undefined;
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;
998958
959 var a: MinInt = abs_value;
960 var index: usize = buf.len;
999 while (true) {961 while (true) {
1000 const digit = a % base;962 const digit = a % base;
1001 index -= 1;963 index -= 1;
...@@ -1004,25 +966,21 @@ fn formatIntUnsigned(...@@ -1004,25 +966,21 @@ fn formatIntUnsigned(
1004 if (a == 0) break;966 if (a == 0) break;
1005 }967 }
1006968
1007 const digits_buf = buf[index..];969 if (value_info.is_signed) {
1008 const width = options.width orelse 0;970 if (value < 0) {
1009 const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;971 // Negative integer
1010972 index -= 1;
1011 if (padding > index) {973 buf[index] = '-';
1012 const zero_byte: u8 = options.fill;974 } else if (options.width == null or options.width.? == 0) {
1013 var leftover_padding = padding - index;975 // Positive integer, omit the plus sign
1014 while (true) {976 } else {
1015 try writer.writeAll(@as(*const [1]u8, &zero_byte)[0..]);977 // Positive integer
1016 leftover_padding -= 1;978 index -= 1;
1017 if (leftover_padding == 0) break;979 buf[index] = '+';
1018 }980 }
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);
1025 }981 }
982
983 return formatBuf(buf[index..], options, writer);
1026}984}
1027985
1028pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize {986pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize {
...@@ -1246,6 +1204,10 @@ test "optional" {...@@ -1246,6 +1204,10 @@ test "optional" {
1246 const value: ?i32 = null;1204 const value: ?i32 = null;
1247 try testFmt("optional: null\n", "optional: {}\n", .{value});1205 try testFmt("optional: null\n", "optional: {}\n", .{value});
1248 }1206 }
1207 {
1208 const value = @intToPtr(?*i32, 0xf000d000);
1209 try testFmt("optional: *i32@f000d000\n", "optional: {*}\n", .{value});
1210 }
1249}1211}
12501212
1251test "error" {1213test "error" {
...@@ -1283,7 +1245,17 @@ test "int.specifier" {...@@ -1283,7 +1245,17 @@ test "int.specifier" {
12831245
1284test "int.padded" {1246test "int.padded" {
1285 try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)});1247 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)});
1287}1259}
12881260
1289test "buffer" {1261test "buffer" {
...@@ -1329,7 +1301,7 @@ test "slice" {...@@ -1329,7 +1301,7 @@ test "slice" {
1329 try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value});1301 try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value});
1330 }1302 }
13311303
1332 try testFmt("buf: Test \n", "buf: {s:5}\n", .{"Test"});1304 try testFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"});
1333 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});1305 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"});
1334}1306}
13351307
...@@ -1362,7 +1334,7 @@ test "cstr" {...@@ -1362,7 +1334,7 @@ test "cstr" {
1362 .{@ptrCast([*c]const u8, "Test C")},1334 .{@ptrCast([*c]const u8, "Test C")},
1363 );1335 );
1364 try testFmt(1336 try testFmt(
1365 "cstr: Test C \n",1337 "cstr: Test C\n",
1366 "cstr: {s:10}\n",1338 "cstr: {s:10}\n",
1367 .{@ptrCast([*c]const u8, "Test C")},1339 .{@ptrCast([*c]const u8, "Test C")},
1368 );1340 );
...@@ -1805,7 +1777,7 @@ test "vector" {...@@ -1805,7 +1777,7 @@ test "vector" {
18051777
1806 try testFmt("{ true, false, true, false }", "{}", .{vbool});1778 try testFmt("{ true, false, true, false }", "{}", .{vbool});
1807 try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64});1779 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});
1809 try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});1781 try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64});
1810 try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64});1782 try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64});
1811 try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64});1783 try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64});
...@@ -1818,15 +1790,16 @@ test "enum-literal" {...@@ -1818,15 +1790,16 @@ test "enum-literal" {
18181790
1819test "padding" {1791test "padding" {
1820 try testFmt("Simple", "{}", .{"Simple"});1792 try testFmt("Simple", "{}", .{"Simple"});
1821 try testFmt("true ", "{:10}", .{true});1793 try testFmt(" true", "{:10}", .{true});
1822 try testFmt(" true", "{:>10}", .{true});1794 try testFmt(" true", "{:>10}", .{true});
1823 try testFmt("======true", "{:=>10}", .{true});1795 try testFmt("======true", "{:=>10}", .{true});
1824 try testFmt("true======", "{:=<10}", .{true});1796 try testFmt("true======", "{:=<10}", .{true});
1825 try testFmt(" true ", "{:^10}", .{true});1797 try testFmt(" true ", "{:^10}", .{true});
1826 try testFmt("===true===", "{:=^10}", .{true});1798 try testFmt("===true===", "{:=^10}", .{true});
1827 try testFmt("Minimum width", "{:18} width", .{"Minimum"});1799 try testFmt(" Minimum width", "{:18} width", .{"Minimum"});
1828 try testFmt("==================Filled", "{:=>24}", .{"Filled"});1800 try testFmt("==================Filled", "{:=>24}", .{"Filled"});
1829 try testFmt(" Centered ", "{:^24}", .{"Centered"});1801 try testFmt(" Centered ", "{:^24}", .{"Centered"});
1802 try testFmt("-", "{:-^1}", .{""});
1830}1803}
18311804
1832test "decimal float padding" {1805test "decimal float padding" {
lib/std/fs.zig+61-5
...@@ -21,10 +21,6 @@ pub const wasi = @import("fs/wasi.zig");...@@ -21,10 +21,6 @@ pub const wasi = @import("fs/wasi.zig");
2121
22// TODO audit these APIs with respect to Dir and absolute paths22// 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;
28pub const realpath = os.realpath;24pub const realpath = os.realpath;
29pub const realpathZ = os.realpathZ;25pub const realpathZ = os.realpathZ;
30pub const realpathC = @compileError("deprecated: renamed to realpathZ");26pub const realpathC = @compileError("deprecated: renamed to realpathZ");
...@@ -90,7 +86,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -90,7 +86,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
90 base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);86 base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
9187
92 if (cwd().symLink(existing_path, tmp_path, .{})) {88 if (cwd().symLink(existing_path, tmp_path, .{})) {
93 return rename(tmp_path, new_path);89 return cwd().rename(tmp_path, new_path);
94 } else |err| switch (err) {90 } else |err| switch (err) {
95 error.PathAlreadyExists => continue,91 error.PathAlreadyExists => continue,
96 else => return err, // TODO zig should know this set does not include PathAlreadyExists92 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 {...@@ -255,6 +251,45 @@ pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void {
255 return os.rmdirW(dir_path);251 return os.rmdirW(dir_path);
256}252}
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
258pub const Dir = struct {293pub const Dir = struct {
259 fd: os.fd_t,294 fd: os.fd_t,
260295
...@@ -1338,6 +1373,27 @@ pub const Dir = struct {...@@ -1338,6 +1373,27 @@ pub const Dir = struct {
1338 };1373 };
1339 }1374 }
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
1341 /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.1397 /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
1342 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent1398 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
1343 /// one; the latter case is known as a dangling link.1399 /// 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 {...@@ -728,7 +728,7 @@ pub const File = struct {
728 }728 }
729 var i: usize = 0;729 var i: usize = 0;
730 while (i < trailers.len) {730 while (i < trailers.len) {
731 while (amt >= headers[i].iov_len) {731 while (amt >= trailers[i].iov_len) {
732 amt -= trailers[i].iov_len;732 amt -= trailers[i].iov_len;
733 i += 1;733 i += 1;
734 if (i >= trailers.len) return;734 if (i >= trailers.len) return;
...@@ -740,14 +740,16 @@ pub const File = struct {...@@ -740,14 +740,16 @@ pub const File = struct {
740 }740 }
741741
742 pub const Reader = io.Reader(File, ReadError, read);742 pub const Reader = io.Reader(File, ReadError, read);
743
743 /// Deprecated: use `Reader`744 /// Deprecated: use `Reader`
744 pub const InStream = Reader;745 pub const InStream = Reader;
745746
746 pub fn reader(file: File) io.Reader(File, ReadError, read) {747 pub fn reader(file: File) Reader {
747 return .{ .context = file };748 return .{ .context = file };
748 }749 }
750
749 /// Deprecated: use `reader`751 /// Deprecated: use `reader`
750 pub fn inStream(file: File) io.InStream(File, ReadError, read) {752 pub fn inStream(file: File) Reader {
751 return .{ .context = file };753 return .{ .context = file };
752 }754 }
753755
lib/std/fs/test.zig+161
...@@ -274,6 +274,167 @@ test "file operations on directories" {...@@ -274,6 +274,167 @@ test "file operations on directories" {
274 dir.close();274 dir.close();
275}275}
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
277test "openSelfExe" {438test "openSelfExe" {
278 if (builtin.os.tag == .wasi) return error.SkipZigTest;439 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 {...@@ -139,9 +139,8 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
139 const tag = meta.activeTag(key);139 const tag = meta.activeTag(key);
140 const s = hash(hasher, tag, strat);140 const s = hash(hasher, tag, strat);
141 inline for (info.fields) |field| {141 inline for (info.fields) |field| {
142 const enum_field = field.enum_field.?;142 if (@field(tag_type, field.name) == tag) {
143 if (enum_field.value == @enumToInt(tag)) {143 hash(hasher, @field(key, field.name), strat);
144 hash(hasher, @field(key, enum_field.name), strat);
145 // TODO use a labelled break when it does not crash the compiler. cf #2908144 // TODO use a labelled break when it does not crash the compiler. cf #2908
146 // break :blk;145 // break :blk;
147 return;146 return;
lib/std/hash/crc.zig+1-4
...@@ -71,10 +71,7 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {...@@ -71,10 +71,7 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {
71 const p = input[i .. i + 8];71 const p = input[i .. i + 8];
7272
73 // Unrolling this way gives ~50Mb/s increase73 // Unrolling this way gives ~50Mb/s increase
74 self.crc ^= (@as(u32, p[0]) << 0);74 self.crc ^= std.mem.readIntLittle(u32, p[0..4]);
75 self.crc ^= (@as(u32, p[1]) << 8);
76 self.crc ^= (@as(u32, p[2]) << 16);
77 self.crc ^= (@as(u32, p[3]) << 24);
7875
79 self.crc =76 self.crc =
80 lookup_tables[0][p[7]] ^77 lookup_tables[0][p[7]] ^
lib/std/hash_map.zig+1-1
...@@ -113,7 +113,7 @@ pub fn HashMap(...@@ -113,7 +113,7 @@ pub fn HashMap(
113 return self.unmanaged.clearAndFree(self.allocator);113 return self.unmanaged.clearAndFree(self.allocator);
114 }114 }
115115
116 pub fn count(self: Self) usize {116 pub fn count(self: Self) Size {
117 return self.unmanaged.count();117 return self.unmanaged.count();
118 }118 }
119119
lib/std/heap.zig+1-1
...@@ -489,7 +489,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {...@@ -489,7 +489,7 @@ pub const HeapAllocator = switch (builtin.os.tag) {
489 const full_len = os.windows.kernel32.HeapSize(heap_handle, 0, ptr);489 const full_len = os.windows.kernel32.HeapSize(heap_handle, 0, ptr);
490 assert(full_len != std.math.maxInt(usize));490 assert(full_len != std.math.maxInt(usize));
491 assert(full_len >= amt);491 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);
493 };493 };
494 const buf = @intToPtr([*]u8, aligned_addr)[0..return_len];494 const buf = @intToPtr([*]u8, aligned_addr)[0..return_len];
495 getRecordPtr(buf).* = root_addr;495 getRecordPtr(buf).* = root_addr;
lib/std/heap/arena_allocator.zig+23-1
...@@ -26,7 +26,7 @@ pub const ArenaAllocator = struct {...@@ -26,7 +26,7 @@ pub const ArenaAllocator = struct {
26 return .{26 return .{
27 .allocator = Allocator{27 .allocator = Allocator{
28 .allocFn = alloc,28 .allocFn = alloc,
29 .resizeFn = Allocator.noResize,29 .resizeFn = resize,
30 },30 },
31 .child_allocator = child_allocator,31 .child_allocator = child_allocator,
32 .state = self,32 .state = self,
...@@ -84,4 +84,26 @@ pub const ArenaAllocator = struct {...@@ -84,4 +84,26 @@ pub const ArenaAllocator = struct {
84 return result;84 return result;
85 }85 }
86 }86 }
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 }
87};109};
lib/std/io/serialization.zig+2-2
...@@ -156,7 +156,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,...@@ -156,7 +156,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
156 const tag = try self.deserializeInt(TagInt);156 const tag = try self.deserializeInt(TagInt);
157157
158 inline for (info.fields) |field_info| {158 inline for (info.fields) |field_info| {
159 if (field_info.enum_field.?.value == tag) {159 if (@enumToInt(@field(TagType, field_info.name)) == tag) {
160 const name = field_info.name;160 const name = field_info.name;
161 const FieldType = field_info.field_type;161 const FieldType = field_info.field_type;
162 ptr.* = @unionInit(C, name, undefined);162 ptr.* = @unionInit(C, name, undefined);
...@@ -320,7 +320,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co...@@ -320,7 +320,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
320 // value, but @field requires a comptime value. Our alternative320 // value, but @field requires a comptime value. Our alternative
321 // is to check each field for a match321 // is to check each field for a match
322 inline for (info.fields) |field_info| {322 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) {
324 const name = field_info.name;324 const name = field_info.name;
325 const FieldType = field_info.field_type;325 const FieldType = field_info.field_type;
326 try self.serialize(@field(value, name));326 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 {...@@ -1613,7 +1613,7 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
1613 .Union => |unionInfo| {1613 .Union => |unionInfo| {
1614 if (unionInfo.tag_type) |UnionTagType| {1614 if (unionInfo.tag_type) |UnionTagType| {
1615 inline for (unionInfo.fields) |u_field| {1615 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)) {
1617 parseFree(u_field.field_type, @field(value, u_field.name), options);1617 parseFree(u_field.field_type, @field(value, u_field.name), options);
1618 break;1618 break;
1619 }1619 }
...@@ -2458,7 +2458,7 @@ pub fn stringify(...@@ -2458,7 +2458,7 @@ pub fn stringify(
2458 const info = @typeInfo(T).Union;2458 const info = @typeInfo(T).Union;
2459 if (info.tag_type) |UnionTagType| {2459 if (info.tag_type) |UnionTagType| {
2460 inline for (info.fields) |u_field| {2460 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)) {
2462 return try stringify(@field(value, u_field.name), options, out_stream);2462 return try stringify(@field(value, u_field.name), options, out_stream);
2463 }2463 }
2464 }2464 }
lib/std/macho.zig+110-7
...@@ -647,6 +647,32 @@ pub const nlist_64 = extern struct {...@@ -647,6 +647,32 @@ pub const nlist_64 = extern struct {
647 n_value: u64,647 n_value: u64,
648};648};
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
650/// After MacOS X 10.1 when a new load command is added that is required to be676/// After MacOS X 10.1 when a new load command is added that is required to be
651/// understood by the dynamic linker for the image to execute properly the677/// understood by the dynamic linker for the image to execute properly the
652/// LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic678/// 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;...@@ -1086,13 +1112,58 @@ pub const N_ECOML = 0xe8;
1086/// second stab entry with length information1112/// second stab entry with length information
1087pub const N_LENG = 0xfe;1113pub const N_LENG = 0xfe;
10881114
1089/// If a segment contains any sections marked with S_ATTR_DEBUG then all1115// For the two types of symbol pointers sections and the symbol stubs section
1090/// sections in that segment must have this attribute. No section other than1116// they have indirect symbol table entries. For each of the entries in the
1091/// a section marked with this attribute may reference the contents of this1117// section the indirect symbol table entries, in corresponding order in the
1092/// section. A section with this attribute may contain no symbols and must have1118// indirect symbol table, start at the index stored in the reserved1 field
1093/// a section type S_REGULAR. The static linker will not copy section contents1119// of the section structure. Since the indirect symbol table entries
1094/// from sections with this attribute into its output file. These sections1120// correspond to the entries in the section the number of indirect symbol table
1095/// generally contain DWARF debugging info.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
1096/// a debug section1167/// a debug section
1097pub const S_ATTR_DEBUG = 0x02000000;1168pub const S_ATTR_DEBUG = 0x02000000;
10981169
...@@ -1154,3 +1225,35 @@ pub const VM_PROT_WRITE: vm_prot_t = 0x2;...@@ -1154,3 +1225,35 @@ pub const VM_PROT_WRITE: vm_prot_t = 0x2;
11541225
1155/// VM execute permission1226/// VM execute permission
1156pub const VM_PROT_EXECUTE: vm_prot_t = 0x4;1227pub 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 {...@@ -465,10 +465,13 @@ pub fn TagPayloadType(comptime U: type, tag: @TagType(U)) type {
465 testing.expect(trait.is(.Union)(U));465 testing.expect(trait.is(.Union)(U));
466466
467 const info = @typeInfo(U).Union;467 const info = @typeInfo(U).Union;
468 const tag_info = @typeInfo(@TagType(U)).Enum;
468469
469 inline for (info.fields) |field_info| {470 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;
471 }473 }
474
472 unreachable;475 unreachable;
473}476}
474477
...@@ -504,15 +507,14 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool {...@@ -504,15 +507,14 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool {
504 }507 }
505 },508 },
506 .Union => |info| {509 .Union => |info| {
507 if (info.tag_type) |_| {510 if (info.tag_type) |Tag| {
508 const tag_a = activeTag(a);511 const tag_a = activeTag(a);
509 const tag_b = activeTag(b);512 const tag_b = activeTag(b);
510 if (tag_a != tag_b) return false;513 if (tag_a != tag_b) return false;
511514
512 inline for (info.fields) |field_info| {515 inline for (info.fields) |field_info| {
513 const enum_field = field_info.enum_field.?;516 if (@field(Tag, field_info.name) == tag_a) {
514 if (enum_field.value == @enumToInt(tag_a)) {517 return eql(@field(a, field_info.name), @field(b, field_info.name));
515 return eql(@field(a, enum_field.name), @field(b, enum_field.name));
516 }518 }
517 }519 }
518 return false;520 return false;
...@@ -715,7 +717,7 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -715,7 +717,7 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
715 },717 },
716 .Optional => |opt| {718 .Optional => |opt| {
717 if (@typeInfo(opt.child) == .Pointer) {719 if (@typeInfo(opt.child) == .Pointer) {
718 return @ptrCast(DestType, @alignCast(dest_ptr, target));720 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
719 }721 }
720 },722 },
721 else => {},723 else => {},
...@@ -723,23 +725,24 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -723,23 +725,24 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
723 },725 },
724 .Optional => |dest_opt| {726 .Optional => |dest_opt| {
725 if (@typeInfo(dest_opt.child) == .Pointer) {727 if (@typeInfo(dest_opt.child) == .Pointer) {
728 const dest_ptr = @typeInfo(dest_opt.child).Pointer;
726 switch (@typeInfo(TargetType)) {729 switch (@typeInfo(TargetType)) {
727 .Int, .ComptimeInt => {730 .Int, .ComptimeInt => {
728 return @intToPtr(DestType, target);731 return @intToPtr(DestType, target);
729 },732 },
730 .Pointer => {733 .Pointer => {
731 return @ptrCast(DestType, @alignCast(@alignOf(dest_opt.child.Child), target));734 return @ptrCast(DestType, @alignCast(dest_ptr.alignment, target));
732 },735 },
733 .Optional => |target_opt| {736 .Optional => |target_opt| {
734 if (@typeInfo(target_opt.child) == .Pointer) {737 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));
736 }739 }
737 },740 },
738 else => {},741 else => {},
739 }742 }
740 }743 }
741 },744 },
742 .Enum, .EnumLiteral => {745 .Enum => {
743 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {746 if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
744 return @intToEnum(DestType, target);747 return @intToEnum(DestType, target);
745 }748 }
...@@ -747,15 +750,18 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {...@@ -747,15 +750,18 @@ pub fn cast(comptime DestType: type, target: anytype) DestType {
747 .Int, .ComptimeInt => {750 .Int, .ComptimeInt => {
748 switch (@typeInfo(TargetType)) {751 switch (@typeInfo(TargetType)) {
749 .Pointer => {752 .Pointer => {
750 return @as(DestType, @ptrToInt(target));753 return @intCast(DestType, @ptrToInt(target));
751 },754 },
752 .Optional => |opt| {755 .Optional => |opt| {
753 if (@typeInfo(opt.child) == .Pointer) {756 if (@typeInfo(opt.child) == .Pointer) {
754 return @as(DestType, @ptrToInt(target));757 return @intCast(DestType, @ptrToInt(target));
755 }758 }
756 },759 },
757 .Enum, .EnumLiteral => {760 .Enum => {
758 return @as(DestType, @enumToInt(target));761 return @intCast(DestType, @enumToInt(target));
762 },
763 .Int, .ComptimeInt => {
764 return @intCast(DestType, target);
759 },765 },
760 else => {},766 else => {},
761 }767 }
...@@ -774,10 +780,49 @@ test "std.meta.cast" {...@@ -774,10 +780,49 @@ test "std.meta.cast" {
774780
775 var i = @as(i64, 10);781 var i = @as(i64, 10);
776782
777 testing.expect(cast(?*c_void, 0) == @intToPtr(?*c_void, 0));
778 testing.expect(cast(*u8, 16) == @intToPtr(*u8, 16));783 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);
782 testing.expect(cast(*u64, &i).* == @as(u64, 10));784 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);
783}828}
lib/std/os.zig+52-4
...@@ -320,6 +320,7 @@ pub const ReadError = error{...@@ -320,6 +320,7 @@ pub const ReadError = error{
320/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`320/// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
321/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as321/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
322/// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.322/// 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.
323/// For POSIX the limit is `math.maxInt(isize)`.324/// For POSIX the limit is `math.maxInt(isize)`.
324pub fn read(fd: fd_t, buf: []u8) ReadError!usize {325pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
325 if (builtin.os.tag == .windows) {326 if (builtin.os.tag == .windows) {
...@@ -353,6 +354,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {...@@ -353,6 +354,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
353 // Prevents EINVAL.354 // Prevents EINVAL.
354 const max_count = switch (std.Target.current.os.tag) {355 const max_count = switch (std.Target.current.os.tag) {
355 .linux => 0x7ffff000,356 .linux => 0x7ffff000,
357 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
356 else => math.maxInt(isize),358 else => math.maxInt(isize),
357 };359 };
358 const adjusted_len = math.min(max_count, buf.len);360 const adjusted_len = math.min(max_count, buf.len);
...@@ -693,6 +695,7 @@ pub const WriteError = error{...@@ -693,6 +695,7 @@ pub const WriteError = error{
693/// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000`695/// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000`
694/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as696/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
695/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.697/// 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.
696/// The corresponding POSIX limit is `math.maxInt(isize)`.699/// The corresponding POSIX limit is `math.maxInt(isize)`.
697pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {700pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
698 if (builtin.os.tag == .windows) {701 if (builtin.os.tag == .windows) {
...@@ -726,6 +729,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {...@@ -726,6 +729,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
726729
727 const max_count = switch (std.Target.current.os.tag) {730 const max_count = switch (std.Target.current.os.tag) {
728 .linux => 0x7ffff000,731 .linux => 0x7ffff000,
732 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
729 else => math.maxInt(isize),733 else => math.maxInt(isize),
730 };734 };
731 const adjusted_len = math.min(max_count, bytes.len);735 const adjusted_len = math.min(max_count, bytes.len);
...@@ -851,6 +855,7 @@ pub const PWriteError = WriteError || error{Unseekable};...@@ -851,6 +855,7 @@ pub const PWriteError = WriteError || error{Unseekable};
851/// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000`855/// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000`
852/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as856/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
853/// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.857/// 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.
854/// The corresponding POSIX limit is `math.maxInt(isize)`.859/// The corresponding POSIX limit is `math.maxInt(isize)`.
855pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {860pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
856 if (std.Target.current.os.tag == .windows) {861 if (std.Target.current.os.tag == .windows) {
...@@ -888,6 +893,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {...@@ -888,6 +893,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
888 // Prevent EINVAL.893 // Prevent EINVAL.
889 const max_count = switch (std.Target.current.os.tag) {894 const max_count = switch (std.Target.current.os.tag) {
890 .linux => 0x7ffff000,895 .linux => 0x7ffff000,
896 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
891 else => math.maxInt(isize),897 else => math.maxInt(isize),
892 };898 };
893 const adjusted_len = math.min(max_count, bytes.len);899 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...@@ -1884,7 +1890,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError
1884 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });1890 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
1885}1891}
18861892
1887const RenameError = error{1893pub const RenameError = error{
1888 /// In WASI, this error may occur when the file descriptor does1894 /// In WASI, this error may occur when the file descriptor does
1889 /// not hold the required rights to rename a resource by path relative to it.1895 /// not hold the required rights to rename a resource by path relative to it.
1890 AccessDenied,1896 AccessDenied,
...@@ -2101,6 +2107,7 @@ pub fn renameatW(...@@ -2101,6 +2107,7 @@ pub fn renameatW(
2101 .ACCESS_DENIED => return error.AccessDenied,2107 .ACCESS_DENIED => return error.AccessDenied,
2102 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,2108 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
2103 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,2109 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
2110 .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
2104 else => return windows.unexpectedStatus(rc),2111 else => return windows.unexpectedStatus(rc),
2105 }2112 }
2106}2113}
...@@ -2515,9 +2522,9 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read...@@ -2515,9 +2522,9 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read
2515pub const SetEidError = error{2522pub const SetEidError = error{
2516 InvalidUserId,2523 InvalidUserId,
2517 PermissionDenied,2524 PermissionDenied,
2518};2525} || UnexpectedError;
25192526
2520pub const SetIdError = error{ResourceLimitReached} || SetEidError || UnexpectedError;2527pub const SetIdError = error{ResourceLimitReached} || SetEidError;
25212528
2522pub fn setuid(uid: uid_t) SetIdError!void {2529pub fn setuid(uid: uid_t) SetIdError!void {
2523 switch (errno(system.setuid(uid))) {2530 switch (errno(system.setuid(uid))) {
...@@ -3084,7 +3091,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con...@@ -3084,7 +3091,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
3084 .WSAECONNREFUSED => return error.ConnectionRefused,3091 .WSAECONNREFUSED => return error.ConnectionRefused,
3085 .WSAETIMEDOUT => return error.ConnectionTimedOut,3092 .WSAETIMEDOUT => return error.ConnectionTimedOut,
3086 .WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well?3093 .WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well?
3087 , .WSAENETUNREACH => return error.NetworkUnreachable,3094 , .WSAENETUNREACH => return error.NetworkUnreachable,
3088 .WSAEFAULT => unreachable,3095 .WSAEFAULT => unreachable,
3089 .WSAEINVAL => unreachable,3096 .WSAEINVAL => unreachable,
3090 .WSAEISCONN => unreachable,3097 .WSAEISCONN => unreachable,
...@@ -4711,6 +4718,7 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize {...@@ -4711,6 +4718,7 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize {
4711/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`4718/// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
4712/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as4719/// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
4713/// well as stuffing the errno codes into the last `4096` values. This is cited on the `sendfile` man page.4720/// 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.
4714/// The corresponding POSIX limit on this is `math.maxInt(isize)`.4722/// The corresponding POSIX limit on this is `math.maxInt(isize)`.
4715pub fn sendfile(4723pub fn sendfile(
4716 out_fd: fd_t,4724 out_fd: fd_t,
...@@ -4733,6 +4741,7 @@ pub fn sendfile(...@@ -4733,6 +4741,7 @@ pub fn sendfile(
4733 });4741 });
4734 const max_count = switch (std.Target.current.os.tag) {4742 const max_count = switch (std.Target.current.os.tag) {
4735 .linux => 0x7ffff000,4743 .linux => 0x7ffff000,
4744 .macosx, .ios, .watchos, .tvos => math.maxInt(i32),
4736 else => math.maxInt(size_t),4745 else => math.maxInt(size_t),
4737 };4746 };
47384747
...@@ -5418,3 +5427,42 @@ pub fn fdatasync(fd: fd_t) SyncError!void {...@@ -5418,3 +5427,42 @@ pub fn fdatasync(fd: fd_t) SyncError!void {
5418 else => |err| return std.os.unexpectedErrno(err),5427 else => |err| return std.os.unexpectedErrno(err),
5419 }5428 }
5420}5429}
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) {...@@ -20,10 +20,13 @@ pub usingnamespace switch (builtin.arch) {
20 .arm => @import("linux/arm-eabi.zig"),20 .arm => @import("linux/arm-eabi.zig"),
21 .riscv64 => @import("linux/riscv64.zig"),21 .riscv64 => @import("linux/riscv64.zig"),
22 .mips, .mipsel => @import("linux/mips.zig"),22 .mips, .mipsel => @import("linux/mips.zig"),
23 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),
23 else => struct {},24 else => struct {},
24};25};
2526
26pub usingnamespace @import("linux/netlink.zig");27pub usingnamespace @import("linux/netlink.zig");
28pub usingnamespace @import("linux/prctl.zig");
29pub usingnamespace @import("linux/securebits.zig");
2730
28const is_mips = builtin.arch.isMIPS();31const is_mips = builtin.arch.isMIPS();
2932
...@@ -1590,6 +1593,123 @@ pub const RR_A = 1;...@@ -1590,6 +1593,123 @@ pub const RR_A = 1;
1590pub const RR_CNAME = 5;1593pub const RR_CNAME = 5;
1591pub const RR_AAAA = 28;1594pub 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
1593pub const nfds_t = usize;1713pub const nfds_t = usize;
1594pub const pollfd = extern struct {1714pub const pollfd = extern struct {
1595 fd: fd_t,1715 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) {...@@ -25,6 +25,7 @@ pub usingnamespace switch (builtin.arch) {
25 .arm => @import("linux/arm-eabi.zig"),25 .arm => @import("linux/arm-eabi.zig"),
26 .riscv64 => @import("linux/riscv64.zig"),26 .riscv64 => @import("linux/riscv64.zig"),
27 .mips, .mipsel => @import("linux/mips.zig"),27 .mips, .mipsel => @import("linux/mips.zig"),
28 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),
28 else => struct {},29 else => struct {},
29};30};
30pub usingnamespace @import("bits.zig");31pub usingnamespace @import("bits.zig");
...@@ -1258,6 +1259,10 @@ pub fn fdatasync(fd: fd_t) usize {...@@ -1258,6 +1259,10 @@ pub fn fdatasync(fd: fd_t) usize {
1258 return syscall1(.fdatasync, @bitCast(usize, @as(isize, fd)));1259 return syscall1(.fdatasync, @bitCast(usize, @as(isize, fd)));
1259}1260}
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
1261test "" {1266test "" {
1262 if (builtin.os.tag == .linux) {1267 if (builtin.os.tag == .linux) {
1263 _ = @import("linux/test.zig");1268 _ = @import("linux/test.zig");
lib/std/os/linux/bpf.zig+761-68
...@@ -3,9 +3,16 @@...@@ -3,9 +3,16 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.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 copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6usingnamespace std.os;6usingnamespace std.os.linux;
7const std = @import("../../std.zig");7const std = @import("../../std.zig");
8const errno = getErrno;
9const unexpectedErrno = std.os.unexpectedErrno;
8const expectEqual = std.testing.expectEqual;10const 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
10// instruction classes17// instruction classes
11pub const LD = 0x00;18pub const LD = 0x00;
...@@ -62,6 +69,7 @@ pub const MAXINSNS = 4096;...@@ -62,6 +69,7 @@ pub const MAXINSNS = 4096;
62// instruction classes69// instruction classes
63/// jmp mode in word width70/// jmp mode in word width
64pub const JMP32 = 0x06;71pub const JMP32 = 0x06;
72
65/// alu mode in double word width73/// alu mode in double word width
66pub const ALU64 = 0x07;74pub const ALU64 = 0x07;
6775
...@@ -72,14 +80,17 @@ pub const XADD = 0xc0;...@@ -72,14 +80,17 @@ pub const XADD = 0xc0;
72// alu/jmp fields80// alu/jmp fields
73/// mov reg to reg81/// mov reg to reg
74pub const MOV = 0xb0;82pub const MOV = 0xb0;
83
75/// sign extending arithmetic shift right */84/// sign extending arithmetic shift right */
76pub const ARSH = 0xc0;85pub const ARSH = 0xc0;
7786
78// change endianness of a register87// change endianness of a register
79/// flags for endianness conversion:88/// flags for endianness conversion:
80pub const END = 0xd0;89pub const END = 0xd0;
90
81/// convert to little-endian */91/// convert to little-endian */
82pub const TO_LE = 0x00;92pub const TO_LE = 0x00;
93
83/// convert to big-endian94/// convert to big-endian
84pub const TO_BE = 0x08;95pub const TO_BE = 0x08;
85pub const FROM_LE = TO_LE;96pub const FROM_LE = TO_LE;
...@@ -88,29 +99,39 @@ pub const FROM_BE = TO_BE;...@@ -88,29 +99,39 @@ pub const FROM_BE = TO_BE;
88// jmp encodings99// jmp encodings
89/// jump != *100/// jump != *
90pub const JNE = 0x50;101pub const JNE = 0x50;
102
91/// LT is unsigned, '<'103/// LT is unsigned, '<'
92pub const JLT = 0xa0;104pub const JLT = 0xa0;
105
93/// LE is unsigned, '<=' *106/// LE is unsigned, '<=' *
94pub const JLE = 0xb0;107pub const JLE = 0xb0;
108
95/// SGT is signed '>', GT in x86109/// SGT is signed '>', GT in x86
96pub const JSGT = 0x60;110pub const JSGT = 0x60;
111
97/// SGE is signed '>=', GE in x86112/// SGE is signed '>=', GE in x86
98pub const JSGE = 0x70;113pub const JSGE = 0x70;
114
99/// SLT is signed, '<'115/// SLT is signed, '<'
100pub const JSLT = 0xc0;116pub const JSLT = 0xc0;
117
101/// SLE is signed, '<='118/// SLE is signed, '<='
102pub const JSLE = 0xd0;119pub const JSLE = 0xd0;
120
103/// function call121/// function call
104pub const CALL = 0x80;122pub const CALL = 0x80;
123
105/// function return124/// function return
106pub const EXIT = 0x90;125pub const EXIT = 0x90;
107126
108/// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the127/// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the
109/// program in this cgroup yields to sub-cgroup program.128/// program in this cgroup yields to sub-cgroup program.
110pub const F_ALLOW_OVERRIDE = 0x1;129pub const F_ALLOW_OVERRIDE = 0x1;
130
111/// Flag for prog_attach command. If a sub-cgroup installs some bpf program,131/// Flag for prog_attach command. If a sub-cgroup installs some bpf program,
112/// that cgroup program gets run in addition to the program in this cgroup.132/// that cgroup program gets run in addition to the program in this cgroup.
113pub const F_ALLOW_MULTI = 0x2;133pub const F_ALLOW_MULTI = 0x2;
134
114/// Flag for prog_attach command.135/// Flag for prog_attach command.
115pub const F_REPLACE = 0x4;136pub const F_REPLACE = 0x4;
116137
...@@ -164,47 +185,61 @@ pub const PSEUDO_CALL = 1;...@@ -164,47 +185,61 @@ pub const PSEUDO_CALL = 1;
164185
165/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing186/// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing
166pub const ANY = 0;187pub const ANY = 0;
188
167/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist189/// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist
168pub const NOEXIST = 1;190pub const NOEXIST = 1;
191
169/// flag for BPF_MAP_UPDATE_ELEM command. update existing element192/// flag for BPF_MAP_UPDATE_ELEM command. update existing element
170pub const EXIST = 2;193pub const EXIST = 2;
194
171/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update195/// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update
172pub const F_LOCK = 4;196pub const F_LOCK = 4;
173197
174/// flag for BPF_MAP_CREATE command */198/// flag for BPF_MAP_CREATE command */
175pub const BPF_F_NO_PREALLOC = 0x1;199pub const BPF_F_NO_PREALLOC = 0x1;
200
176/// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in201/// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in
177/// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can202/// the BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list which can
178/// scale and perform better. Note, the LRU nodes (including free nodes) cannot203/// scale and perform better. Note, the LRU nodes (including free nodes) cannot
179/// be moved across different LRU lists.204/// be moved across different LRU lists.
180pub const BPF_F_NO_COMMON_LRU = 0x2;205pub const BPF_F_NO_COMMON_LRU = 0x2;
206
181/// flag for BPF_MAP_CREATE command. Specify numa node during map creation207/// flag for BPF_MAP_CREATE command. Specify numa node during map creation
182pub const BPF_F_NUMA_NODE = 0x4;208pub const BPF_F_NUMA_NODE = 0x4;
209
183/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from210/// flag for BPF_MAP_CREATE command. Flags for BPF object read access from
184/// syscall side211/// syscall side
185pub const BPF_F_RDONLY = 0x8;212pub const BPF_F_RDONLY = 0x8;
213
186/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from214/// flag for BPF_MAP_CREATE command. Flags for BPF object write access from
187/// syscall side215/// syscall side
188pub const BPF_F_WRONLY = 0x10;216pub const BPF_F_WRONLY = 0x10;
217
189/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset218/// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset
190/// instead of pointer219/// instead of pointer
191pub const BPF_F_STACK_BUILD_ID = 0x20;220pub const BPF_F_STACK_BUILD_ID = 0x20;
221
192/// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This222/// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This
193/// should only be used for testing.223/// should only be used for testing.
194pub const BPF_F_ZERO_SEED = 0x40;224pub const BPF_F_ZERO_SEED = 0x40;
225
195/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program226/// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program
196/// side.227/// side.
197pub const BPF_F_RDONLY_PROG = 0x80;228pub const BPF_F_RDONLY_PROG = 0x80;
229
198/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program230/// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program
199/// side.231/// side.
200pub const BPF_F_WRONLY_PROG = 0x100;232pub const BPF_F_WRONLY_PROG = 0x100;
233
201/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted234/// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted
202/// socket235/// socket
203pub const BPF_F_CLONE = 0x200;236pub const BPF_F_CLONE = 0x200;
237
204/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map238/// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map
205pub const BPF_F_MMAPABLE = 0x400;239pub const BPF_F_MMAPABLE = 0x400;
206240
207/// These values correspond to "syscalls" within the BPF program's environment241/// These values correspond to "syscalls" within the BPF program's environment,
242/// each one is documented in std.os.linux.BPF.kern
208pub const Helper = enum(i32) {243pub const Helper = enum(i32) {
209 unspec,244 unspec,
210 map_lookup_elem,245 map_lookup_elem,
...@@ -325,9 +360,34 @@ pub const Helper = enum(i32) {...@@ -325,9 +360,34 @@ pub const Helper = enum(i32) {
325 tcp_send_ack,360 tcp_send_ack,
326 send_signal_thread,361 send_signal_thread,
327 jiffies64,362 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,
328 _,386 _,
329};387};
330388
389// TODO: determine that this is the expected bit layout for both little and big
390// endian systems
331/// a single BPF instruction391/// a single BPF instruction
332pub const Insn = packed struct {392pub const Insn = packed struct {
333 code: u8,393 code: u8,
...@@ -340,19 +400,30 @@ pub const Insn = packed struct {...@@ -340,19 +400,30 @@ pub const Insn = packed struct {
340 /// frame400 /// frame
341 pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };401 pub const Reg = packed enum(u4) { r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 };
342 const Source = packed enum(u1) { reg, imm };402 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
343 const AluOp = packed enum(u8) {413 const AluOp = packed enum(u8) {
344 add = ADD,414 add = ADD,
345 sub = SUB,415 sub = SUB,
346 mul = MUL,416 mul = MUL,
347 div = DIV,417 div = DIV,
348 op_or = OR,418 alu_or = OR,
349 op_and = AND,419 alu_and = AND,
350 lsh = LSH,420 lsh = LSH,
351 rsh = RSH,421 rsh = RSH,
352 neg = NEG,422 neg = NEG,
353 mod = MOD,423 mod = MOD,
354 xor = XOR,424 xor = XOR,
355 mov = MOV,425 mov = MOV,
426 arsh = ARSH,
356 };427 };
357428
358 pub const Size = packed enum(u8) {429 pub const Size = packed enum(u8) {
...@@ -368,6 +439,13 @@ pub const Insn = packed struct {...@@ -368,6 +439,13 @@ pub const Insn = packed struct {
368 jgt = JGT,439 jgt = JGT,
369 jge = JGE,440 jge = JGE,
370 jset = JSET,441 jset = JSET,
442 jlt = JLT,
443 jle = JLE,
444 jne = JNE,
445 jsgt = JSGT,
446 jsge = JSGE,
447 jslt = JSLT,
448 jsle = JSLE,
371 };449 };
372450
373 const ImmOrReg = union(Source) {451 const ImmOrReg = union(Source) {
...@@ -419,22 +497,100 @@ pub const Insn = packed struct {...@@ -419,22 +497,100 @@ pub const Insn = packed struct {
419 return alu(64, .add, dst, src);497 return alu(64, .add, dst, src);
420 }498 }
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
422 fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {544 fn jmp(op: JmpOp, dst: Reg, src: anytype, off: i16) Insn {
423 return imm_reg(JMP | @enumToInt(op), dst, src, off);545 return imm_reg(JMP | @enumToInt(op), dst, src, off);
424 }546 }
425547
548 pub fn ja(off: i16) Insn {
549 return jmp(.ja, .r0, 0, off);
550 }
551
426 pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {552 pub fn jeq(dst: Reg, src: anytype, off: i16) Insn {
427 return jmp(.jeq, dst, src, off);553 return jmp(.jeq, dst, src, off);
428 }554 }
429555
430 pub fn stx_mem(size: Size, dst: Reg, src: Reg, off: i16) Insn {556 pub fn jgt(dst: Reg, src: anytype, off: i16) Insn {
431 return Insn{557 return jmp(.jgt, dst, src, off);
432 .code = STX | @enumToInt(size) | MEM,558 }
433 .dst = @enumToInt(dst),559
434 .src = @enumToInt(src),560 pub fn jge(dst: Reg, src: anytype, off: i16) Insn {
435 .off = off,561 return jmp(.jge, dst, src, off);
436 .imm = 0,562 }
437 };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);
438 }594 }
439595
440 pub fn xadd(dst: Reg, src: Reg) Insn {596 pub fn xadd(dst: Reg, src: Reg) Insn {
...@@ -447,17 +603,34 @@ pub const Insn = packed struct {...@@ -447,17 +603,34 @@ pub const Insn = packed struct {
447 };603 };
448 }604 }
449605
450 /// direct packet access, R0 = *(uint *)(skb->data + imm32)606 fn ld(mode: Mode, size: Size, dst: Reg, src: Reg, imm: i32) Insn {
451 pub fn ld_abs(size: Size, imm: i32) Insn {
452 return Insn{607 return Insn{
453 .code = LD | @enumToInt(size) | ABS,608 .code = @enumToInt(mode) | @enumToInt(size) | LD,
454 .dst = 0,609 .dst = @enumToInt(dst),
455 .src = 0,610 .src = @enumToInt(src),
456 .off = 0,611 .off = 0,
457 .imm = imm,612 .imm = imm,
458 };613 };
459 }614 }
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
461 fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn {634 fn ld_imm_impl1(dst: Reg, src: Reg, imm: u64) Insn {
462 return Insn{635 return Insn{
463 .code = LD | DW | IMM,636 .code = LD | DW | IMM,
...@@ -478,6 +651,14 @@ pub const Insn = packed struct {...@@ -478,6 +651,14 @@ pub const Insn = packed struct {
478 };651 };
479 }652 }
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
481 pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn {662 pub fn ld_map_fd1(dst: Reg, map_fd: fd_t) Insn {
482 return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd));663 return ld_imm_impl1(dst, @intToEnum(Reg, PSEUDO_MAP_FD), @intCast(u64, map_fd));
483 }664 }
...@@ -486,6 +667,53 @@ pub const Insn = packed struct {...@@ -486,6 +667,53 @@ pub const Insn = packed struct {
486 return ld_imm_impl2(@intCast(u64, map_fd));667 return ld_imm_impl2(@intCast(u64, map_fd));
487 }668 }
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
489 pub fn call(helper: Helper) Insn {717 pub fn call(helper: Helper) Insn {
490 return Insn{718 return Insn{
491 .code = JMP | CALL,719 .code = JMP | CALL,
...@@ -508,95 +736,242 @@ pub const Insn = packed struct {...@@ -508,95 +736,242 @@ pub const Insn = packed struct {
508 }736 }
509};737};
510738
511fn expect_insn(insn: Insn, val: u64) void {
512 expectEqual(@bitCast(u64, insn), val);
513}
514
515test "insn bitsize" {739test "insn bitsize" {
516 expectEqual(@bitSizeOf(Insn), 64);740 expectEqual(@bitSizeOf(Insn), 64);
517}741}
518742
519// mov instructions743fn expect_opcode(code: u8, insn: Insn) void {
520test "mov imm" {744 expectEqual(code, insn.code);
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);
531}745}
532746
533// ld instructions747// The opcodes were grabbed from https://github.com/iovisor/bpf-docs/blob/master/eBPF.md
534test "ld_abs" {748test "opcodes" {
535 expect_insn(Insn.ld_abs(.byte, 42), 0x0000002a00000030);749 // instructions that have a name that end with 1 or 2 are consecutive for
536}750 // loading 64-bit immediates (imm is only 32 bits wide)
537751
538test "ld_map_fd" {752 // alu instructions
539 expect_insn(Insn.ld_map_fd1(.r1, 42), 0x0000002a00001118);753 expect_opcode(0x07, Insn.add(.r1, 0));
540 expect_insn(Insn.ld_map_fd2(42), 0x0000000000000000);754 expect_opcode(0x0f, Insn.add(.r1, .r2));
541}755 expect_opcode(0x17, Insn.sub(.r1, 0));
542756 expect_opcode(0x1f, Insn.sub(.r1, .r2));
543// st instructions757 expect_opcode(0x27, Insn.mul(.r1, 0));
544test "stx_mem" {758 expect_opcode(0x2f, Insn.mul(.r1, .r2));
545 expect_insn(Insn.stx_mem(.word, .r10, .r0, -4), 0x00000000fffc0a63);759 expect_opcode(0x37, Insn.div(.r1, 0));
546}760 expect_opcode(0x3f, Insn.div(.r1, .r2));
547761 expect_opcode(0x47, Insn.alu_or(.r1, 0));
548test "xadd" {762 expect_opcode(0x4f, Insn.alu_or(.r1, .r2));
549 expect_insn(Insn.xadd(.r0, .r1), 0x00000000000010db);763 expect_opcode(0x57, Insn.alu_and(.r1, 0));
550}764 expect_opcode(0x5f, Insn.alu_and(.r1, .r2));
551765 expect_opcode(0x67, Insn.lsh(.r1, 0));
552// jmp instructions766 expect_opcode(0x6f, Insn.lsh(.r1, .r2));
553test "jeq imm" {767 expect_opcode(0x77, Insn.rsh(.r1, 0));
554 expect_insn(Insn.jeq(.r0, 0, 2), 0x0000000000020015);768 expect_opcode(0x7f, Insn.rsh(.r1, .r2));
555}769 expect_opcode(0x87, Insn.neg(.r1));
556770 expect_opcode(0x97, Insn.mod(.r1, 0));
557// other instructions771 expect_opcode(0x9f, Insn.mod(.r1, .r2));
558test "call" {772 expect_opcode(0xa7, Insn.xor(.r1, 0));
559 expect_insn(Insn.call(.map_lookup_elem), 0x0000000100000085);773 expect_opcode(0xaf, Insn.xor(.r1, .r2));
560}774 expect_opcode(0xb7, Insn.mov(.r1, 0));
561775 expect_opcode(0xbf, Insn.mov(.r1, .r2));
562test "exit" {776 expect_opcode(0xc7, Insn.arsh(.r1, 0));
563 expect_insn(Insn.exit(), 0x0000000000000095);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());
564}855}
565856
566pub const Cmd = extern enum(usize) {857pub 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
567 map_create,863 map_create,
864
865 /// Look up an element by key in a specified map and return its value.
866 ///
867 /// uses MapElemAttr
568 map_lookup_elem,868 map_lookup_elem,
869
870 /// Create or update an element (key/value pair) in a specified map.
871 ///
872 /// uses MapElemAttr
569 map_update_elem,873 map_update_elem,
874
875 /// Look up and delete an element by key in a specified map.
876 ///
877 /// uses MapElemAttr
570 map_delete_elem,878 map_delete_elem,
879
880 /// Look up an element by key in a specified map and return the key of the
881 /// next element.
571 map_get_next_key,882 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
572 prog_load,889 prog_load,
890
891 /// Pin a map or eBPF program to a path within the minimal BPF filesystem
892 ///
893 /// uses ObjAttr
573 obj_pin,894 obj_pin,
895
896 /// Get the file descriptor of a BPF object pinned to a certain path
897 ///
898 /// uses ObjAttr
574 obj_get,899 obj_get,
900
901 /// uses ProgAttachAttr
575 prog_attach,902 prog_attach,
903
904 /// uses ProgAttachAttr
576 prog_detach,905 prog_detach,
906
907 /// uses TestRunAttr
577 prog_test_run,908 prog_test_run,
909
910 /// uses GetIdAttr
578 prog_get_next_id,911 prog_get_next_id,
912
913 /// uses GetIdAttr
579 map_get_next_id,914 map_get_next_id,
915
916 /// uses GetIdAttr
580 prog_get_fd_by_id,917 prog_get_fd_by_id,
918
919 /// uses GetIdAttr
581 map_get_fd_by_id,920 map_get_fd_by_id,
921
922 /// uses InfoAttr
582 obj_get_info_by_fd,923 obj_get_info_by_fd,
924
925 /// uses QueryAttr
583 prog_query,926 prog_query,
927
928 /// uses RawTracepointAttr
584 raw_tracepoint_open,929 raw_tracepoint_open,
930
931 /// uses BtfLoadAttr
585 btf_load,932 btf_load,
933
934 /// uses GetIdAttr
586 btf_get_fd_by_id,935 btf_get_fd_by_id,
936
937 /// uses TaskFdQueryAttr
587 task_fd_query,938 task_fd_query,
939
940 /// uses MapElemAttr
588 map_lookup_and_delete_elem,941 map_lookup_and_delete_elem,
589 map_freeze,942 map_freeze,
943
944 /// uses GetIdAttr
590 btf_get_next_id,945 btf_get_next_id,
946
947 /// uses MapBatchAttr
591 map_lookup_batch,948 map_lookup_batch,
949
950 /// uses MapBatchAttr
592 map_lookup_and_delete_batch,951 map_lookup_and_delete_batch,
952
953 /// uses MapBatchAttr
593 map_update_batch,954 map_update_batch,
955
956 /// uses MapBatchAttr
594 map_delete_batch,957 map_delete_batch,
958
959 /// uses LinkCreateAttr
595 link_create,960 link_create,
961
962 /// uses LinkUpdateAttr
596 link_update,963 link_update,
964
965 /// uses GetIdAttr
597 link_get_fd_by_id,966 link_get_fd_by_id,
967
968 /// uses GetIdAttr
598 link_get_next_id,969 link_get_next_id,
970
971 /// uses EnableStatsAttr
599 enable_stats,972 enable_stats,
973
974 /// uses IterCreateAttr
600 iter_create,975 iter_create,
601 link_detach,976 link_detach,
602 _,977 _,
...@@ -630,42 +1005,138 @@ pub const MapType = extern enum(u32) {...@@ -630,42 +1005,138 @@ pub const MapType = extern enum(u32) {
630 sk_storage,1005 sk_storage,
631 devmap_hash,1006 devmap_hash,
632 struct_ops,1007 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
633 ringbuf,1042 ringbuf,
1043
634 _,1044 _,
635};1045};
6361046
637pub const ProgType = extern enum(u32) {1047pub const ProgType = extern enum(u32) {
638 unspec,1048 unspec,
1049
1050 /// context type: __sk_buff
639 socket_filter,1051 socket_filter,
1052
1053 /// context type: bpf_user_pt_regs_t
640 kprobe,1054 kprobe,
1055
1056 /// context type: __sk_buff
641 sched_cls,1057 sched_cls,
1058
1059 /// context type: __sk_buff
642 sched_act,1060 sched_act,
1061
1062 /// context type: u64
643 tracepoint,1063 tracepoint,
1064
1065 /// context type: xdp_md
644 xdp,1066 xdp,
1067
1068 /// context type: bpf_perf_event_data
645 perf_event,1069 perf_event,
1070
1071 /// context type: __sk_buff
646 cgroup_skb,1072 cgroup_skb,
1073
1074 /// context type: bpf_sock
647 cgroup_sock,1075 cgroup_sock,
1076
1077 /// context type: __sk_buff
648 lwt_in,1078 lwt_in,
1079
1080 /// context type: __sk_buff
649 lwt_out,1081 lwt_out,
1082
1083 /// context type: __sk_buff
650 lwt_xmit,1084 lwt_xmit,
1085
1086 /// context type: bpf_sock_ops
651 sock_ops,1087 sock_ops,
1088
1089 /// context type: __sk_buff
652 sk_skb,1090 sk_skb,
1091
1092 /// context type: bpf_cgroup_dev_ctx
653 cgroup_device,1093 cgroup_device,
1094
1095 /// context type: sk_msg_md
654 sk_msg,1096 sk_msg,
1097
1098 /// context type: bpf_raw_tracepoint_args
655 raw_tracepoint,1099 raw_tracepoint,
1100
1101 /// context type: bpf_sock_addr
656 cgroup_sock_addr,1102 cgroup_sock_addr,
1103
1104 /// context type: __sk_buff
657 lwt_seg6local,1105 lwt_seg6local,
1106
1107 /// context type: u32
658 lirc_mode2,1108 lirc_mode2,
1109
1110 /// context type: sk_reuseport_md
659 sk_reuseport,1111 sk_reuseport,
1112
1113 /// context type: __sk_buff
660 flow_dissector,1114 flow_dissector,
1115
1116 /// context type: bpf_sysctl
661 cgroup_sysctl,1117 cgroup_sysctl,
1118
1119 /// context type: bpf_raw_tracepoint_args
662 raw_tracepoint_writable,1120 raw_tracepoint_writable,
1121
1122 /// context type: bpf_sockopt
663 cgroup_sockopt,1123 cgroup_sockopt,
1124
1125 /// context type: void *
664 tracing,1126 tracing,
1127
1128 /// context type: void *
665 struct_ops,1129 struct_ops,
1130
1131 /// context type: void *
666 ext,1132 ext,
1133
1134 /// context type: void *
667 lsm,1135 lsm,
1136
1137 /// context type: bpf_sk_lookup
668 sk_lookup,1138 sk_lookup,
1139 _,
669};1140};
6701141
671pub const AttachType = extern enum(u32) {1142pub const AttachType = extern enum(u32) {
...@@ -715,27 +1186,38 @@ const obj_name_len = 16;...@@ -715,27 +1186,38 @@ const obj_name_len = 16;
715pub const MapCreateAttr = extern struct {1186pub const MapCreateAttr = extern struct {
716 /// one of MapType1187 /// one of MapType
717 map_type: u32,1188 map_type: u32,
1189
718 /// size of key in bytes1190 /// size of key in bytes
719 key_size: u32,1191 key_size: u32,
1192
720 /// size of value in bytes1193 /// size of value in bytes
721 value_size: u32,1194 value_size: u32,
1195
722 /// max number of entries in a map1196 /// max number of entries in a map
723 max_entries: u32,1197 max_entries: u32,
1198
724 /// .map_create related flags1199 /// .map_create related flags
725 map_flags: u32,1200 map_flags: u32,
1201
726 /// fd pointing to the inner map1202 /// fd pointing to the inner map
727 inner_map_fd: fd_t,1203 inner_map_fd: fd_t,
1204
728 /// numa node (effective only if MapCreateFlags.numa_node is set)1205 /// numa node (effective only if MapCreateFlags.numa_node is set)
729 numa_node: u32,1206 numa_node: u32,
730 map_name: [obj_name_len]u8,1207 map_name: [obj_name_len]u8,
1208
731 /// ifindex of netdev to create on1209 /// ifindex of netdev to create on
732 map_ifindex: u32,1210 map_ifindex: u32,
1211
733 /// fd pointing to a BTF type data1212 /// fd pointing to a BTF type data
734 btf_fd: fd_t,1213 btf_fd: fd_t,
1214
735 /// BTF type_id of the key1215 /// BTF type_id of the key
736 btf_key_type_id: u32,1216 btf_key_type_id: u32,
1217
737 /// BTF type_id of the value1218 /// BTF type_id of the value
738 bpf_value_type_id: u32,1219 bpf_value_type_id: u32,
1220
739 /// BTF type_id of a kernel struct stored as the map value1221 /// BTF type_id of a kernel struct stored as the map value
740 btf_vmlinux_value_type_id: u32,1222 btf_vmlinux_value_type_id: u32,
741};1223};
...@@ -755,10 +1237,12 @@ pub const MapElemAttr = extern struct {...@@ -755,10 +1237,12 @@ pub const MapElemAttr = extern struct {
755pub const MapBatchAttr = extern struct {1237pub const MapBatchAttr = extern struct {
756 /// start batch, NULL to start from beginning1238 /// start batch, NULL to start from beginning
757 in_batch: u64,1239 in_batch: u64,
1240
758 /// output: next start batch1241 /// output: next start batch
759 out_batch: u64,1242 out_batch: u64,
760 keys: u64,1243 keys: u64,
761 values: u64,1244 values: u64,
1245
762 /// input/output:1246 /// input/output:
763 /// input: # of key/value elements1247 /// input: # of key/value elements
764 /// output: # of filled elements1248 /// output: # of filled elements
...@@ -775,35 +1259,49 @@ pub const ProgLoadAttr = extern struct {...@@ -775,35 +1259,49 @@ pub const ProgLoadAttr = extern struct {
775 insn_cnt: u32,1259 insn_cnt: u32,
776 insns: u64,1260 insns: u64,
777 license: u64,1261 license: u64,
1262
778 /// verbosity level of verifier1263 /// verbosity level of verifier
779 log_level: u32,1264 log_level: u32,
1265
780 /// size of user buffer1266 /// size of user buffer
781 log_size: u32,1267 log_size: u32,
1268
782 /// user supplied buffer1269 /// user supplied buffer
783 log_buf: u64,1270 log_buf: u64,
1271
784 /// not used1272 /// not used
785 kern_version: u32,1273 kern_version: u32,
786 prog_flags: u32,1274 prog_flags: u32,
787 prog_name: [obj_name_len]u8,1275 prog_name: [obj_name_len]u8,
788 /// ifindex of netdev to prep for. For some prog types expected attach1276
789 /// type must be known at load time to verify attach type specific parts1277 /// ifindex of netdev to prep for.
790 /// of prog (context accesses, allowed helpers, etc).
791 prog_ifindex: u32,1278 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).
792 expected_attach_type: u32,1283 expected_attach_type: u32,
1284
793 /// fd pointing to BTF type data1285 /// fd pointing to BTF type data
794 prog_btf_fd: fd_t,1286 prog_btf_fd: fd_t,
1287
795 /// userspace bpf_func_info size1288 /// userspace bpf_func_info size
796 func_info_rec_size: u32,1289 func_info_rec_size: u32,
797 func_info: u64,1290 func_info: u64,
1291
798 /// number of bpf_func_info records1292 /// number of bpf_func_info records
799 func_info_cnt: u32,1293 func_info_cnt: u32,
1294
800 /// userspace bpf_line_info size1295 /// userspace bpf_line_info size
801 line_info_rec_size: u32,1296 line_info_rec_size: u32,
802 line_info: u64,1297 line_info: u64,
1298
803 /// number of bpf_line_info records1299 /// number of bpf_line_info records
804 line_info_cnt: u32,1300 line_info_cnt: u32,
1301
805 /// in-kernel BTF type id to attach to1302 /// in-kernel BTF type id to attach to
806 attact_btf_id: u32,1303 attact_btf_id: u32,
1304
807 /// 0 to attach to vmlinux1305 /// 0 to attach to vmlinux
808 attach_prog_id: u32,1306 attach_prog_id: u32,
809};1307};
...@@ -819,29 +1317,36 @@ pub const ObjAttr = extern struct {...@@ -819,29 +1317,36 @@ pub const ObjAttr = extern struct {
819pub const ProgAttachAttr = extern struct {1317pub const ProgAttachAttr = extern struct {
820 /// container object to attach to1318 /// container object to attach to
821 target_fd: fd_t,1319 target_fd: fd_t,
1320
822 /// eBPF program to attach1321 /// eBPF program to attach
823 attach_bpf_fd: fd_t,1322 attach_bpf_fd: fd_t,
1323
824 attach_type: u32,1324 attach_type: u32,
825 attach_flags: u32,1325 attach_flags: u32,
1326
826 // TODO: BPF_F_REPLACE flags1327 // TODO: BPF_F_REPLACE flags
827 /// previously attached eBPF program to replace if .replace is used1328 /// previously attached eBPF program to replace if .replace is used
828 replace_bpf_fd: fd_t,1329 replace_bpf_fd: fd_t,
829};1330};
8301331
831/// struct used by Cmd.prog_test_run command1332/// struct used by Cmd.prog_test_run command
832pub const TestAttr = extern struct {1333pub const TestRunAttr = extern struct {
833 prog_fd: fd_t,1334 prog_fd: fd_t,
834 retval: u32,1335 retval: u32,
1336
835 /// input: len of data_in1337 /// input: len of data_in
836 data_size_in: u32,1338 data_size_in: u32,
1339
837 /// input/output: len of data_out. returns ENOSPC if data_out is too small.1340 /// input/output: len of data_out. returns ENOSPC if data_out is too small.
838 data_size_out: u32,1341 data_size_out: u32,
839 data_in: u64,1342 data_in: u64,
840 data_out: u64,1343 data_out: u64,
841 repeat: u32,1344 repeat: u32,
842 duration: u32,1345 duration: u32,
1346
843 /// input: len of ctx_in1347 /// input: len of ctx_in
844 ctx_size_in: u32,1348 ctx_size_in: u32,
1349
845 /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small.1350 /// input/output: len of ctx_out. returns ENOSPC if ctx_out is too small.
846 ctx_size_out: u32,1351 ctx_size_out: u32,
847 ctx_in: u64,1352 ctx_in: u64,
...@@ -894,26 +1399,35 @@ pub const BtfLoadAttr = extern struct {...@@ -894,26 +1399,35 @@ pub const BtfLoadAttr = extern struct {
894 btf_log_level: u32,1399 btf_log_level: u32,
895};1400};
8961401
1402/// struct used by Cmd.task_fd_query
897pub const TaskFdQueryAttr = extern struct {1403pub const TaskFdQueryAttr = extern struct {
898 /// input: pid1404 /// input: pid
899 pid: pid_t,1405 pid: pid_t,
1406
900 /// input: fd1407 /// input: fd
901 fd: fd_t,1408 fd: fd_t,
1409
902 /// input: flags1410 /// input: flags
903 flags: u32,1411 flags: u32,
1412
904 /// input/output: buf len1413 /// input/output: buf len
905 buf_len: u32,1414 buf_len: u32,
1415
906 /// input/output:1416 /// input/output:
907 /// tp_name for tracepoint1417 /// tp_name for tracepoint
908 /// symbol for kprobe1418 /// symbol for kprobe
909 /// filename for uprobe1419 /// filename for uprobe
910 buf: u64,1420 buf: u64,
1421
911 /// output: prod_id1422 /// output: prod_id
912 prog_id: u32,1423 prog_id: u32,
1424
913 /// output: BPF_FD_TYPE1425 /// output: BPF_FD_TYPE
914 fd_type: u32,1426 fd_type: u32,
1427
915 /// output: probe_offset1428 /// output: probe_offset
916 probe_offset: u64,1429 probe_offset: u64,
1430
917 /// output: probe_addr1431 /// output: probe_addr
918 probe_addr: u64,1432 probe_addr: u64,
919};1433};
...@@ -922,9 +1436,11 @@ pub const TaskFdQueryAttr = extern struct {...@@ -922,9 +1436,11 @@ pub const TaskFdQueryAttr = extern struct {
922pub const LinkCreateAttr = extern struct {1436pub const LinkCreateAttr = extern struct {
923 /// eBPF program to attach1437 /// eBPF program to attach
924 prog_fd: fd_t,1438 prog_fd: fd_t,
1439
925 /// object to attach to1440 /// object to attach to
926 target_fd: fd_t,1441 target_fd: fd_t,
927 attach_type: u32,1442 attach_type: u32,
1443
928 /// extra flags1444 /// extra flags
929 flags: u32,1445 flags: u32,
930};1446};
...@@ -932,10 +1448,13 @@ pub const LinkCreateAttr = extern struct {...@@ -932,10 +1448,13 @@ pub const LinkCreateAttr = extern struct {
932/// struct used by Cmd.link_update command1448/// struct used by Cmd.link_update command
933pub const LinkUpdateAttr = extern struct {1449pub const LinkUpdateAttr = extern struct {
934 link_fd: fd_t,1450 link_fd: fd_t,
1451
935 /// new program to update link with1452 /// new program to update link with
936 new_prog_fd: fd_t,1453 new_prog_fd: fd_t,
1454
937 /// extra flags1455 /// extra flags
938 flags: u32,1456 flags: u32,
1457
939 /// expected link's program fd, it is specified only if BPF_F_REPLACE is1458 /// expected link's program fd, it is specified only if BPF_F_REPLACE is
940 /// set in flags1459 /// set in flags
941 old_prog_fd: fd_t,1460 old_prog_fd: fd_t,
...@@ -952,6 +1471,7 @@ pub const IterCreateAttr = extern struct {...@@ -952,6 +1471,7 @@ pub const IterCreateAttr = extern struct {
952 flags: u32,1471 flags: u32,
953};1472};
9541473
1474/// Mega struct that is passed to the bpf() syscall
955pub const Attr = extern union {1475pub const Attr = extern union {
956 map_create: MapCreateAttr,1476 map_create: MapCreateAttr,
957 map_elem: MapElemAttr,1477 map_elem: MapElemAttr,
...@@ -971,3 +1491,176 @@ pub const Attr = extern union {...@@ -971,3 +1491,176 @@ pub const Attr = extern union {
971 enable_stats: EnableStatsAttr,1491 enable_stats: EnableStatsAttr,
972 iter_create: IterCreateAttr,1492 iter_create: IterCreateAttr,
973};1493};
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 {...@@ -53,7 +53,7 @@ const TLSVariant = enum {
53};53};
5454
55const tls_variant = switch (builtin.arch) {55const 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,
57 .x86_64, .i386 => TLSVariant.VariantII,57 .x86_64, .i386 => TLSVariant.VariantII,
58 else => @compileError("undefined tls_variant for this architecture"),58 else => @compileError("undefined tls_variant for this architecture"),
59};59};
...@@ -77,12 +77,12 @@ const tls_tp_points_past_tcb = switch (builtin.arch) {...@@ -77,12 +77,12 @@ const tls_tp_points_past_tcb = switch (builtin.arch) {
77// make the generated code more efficient77// make the generated code more efficient
7878
79const tls_tp_offset = switch (builtin.arch) {79const tls_tp_offset = switch (builtin.arch) {
80 .mips, .mipsel => 0x7000,80 .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x7000,
81 else => 0,81 else => 0,
82};82};
8383
84const tls_dtv_offset = switch (builtin.arch) {84const tls_dtv_offset = switch (builtin.arch) {
85 .mips, .mipsel => 0x8000,85 .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x8000,
86 .riscv32, .riscv64 => 0x800,86 .riscv32, .riscv64 => 0x800,
87 else => 0,87 else => 0,
88};88};
...@@ -165,6 +165,13 @@ pub fn setThreadPointer(addr: usize) void {...@@ -165,6 +165,13 @@ pub fn setThreadPointer(addr: usize) void {
165 const rc = std.os.linux.syscall1(.set_thread_area, addr);165 const rc = std.os.linux.syscall1(.set_thread_area, addr);
166 assert(rc == 0);166 assert(rc == 0);
167 },167 },
168 .powerpc, .powerpc64, .powerpc64le => {
169 asm volatile (
170 \\ mr 13, %[addr]
171 :
172 : [addr] "r" (addr)
173 );
174 },
168 else => @compileError("Unsupported architecture"),175 else => @compileError("Unsupported architecture"),
169 }176 }
170}177}
lib/std/os/windows.zig+2-1
...@@ -828,7 +828,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -828,7 +828,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
828 }828 }
829}829}
830830
831pub const MoveFileError = error{Unexpected};831pub const MoveFileError = error{ FileNotFound, Unexpected };
832832
833pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {833pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
834 const old_path_w = try sliceToPrefixedFileW(old_path);834 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...@@ -839,6 +839,7 @@ pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) Move
839pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {839pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void {
840 if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {840 if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
841 switch (kernel32.GetLastError()) {841 switch (kernel32.GetLastError()) {
842 .FILE_NOT_FOUND => return error.FileNotFound,
842 else => |err| return unexpectedError(err),843 else => |err| return unexpectedError(err),
843 }844 }
844 }845 }
lib/std/priority_queue.zig+10-1
...@@ -195,7 +195,7 @@ pub fn PriorityQueue(comptime T: type) type {...@@ -195,7 +195,7 @@ pub fn PriorityQueue(comptime T: type) type {
195 count: usize,195 count: usize,
196196
197 pub fn next(it: *Iterator) ?T {197 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;
199 const out = it.count;199 const out = it.count;
200 it.count += 1;200 it.count += 1;
201 return it.queue.items[out];201 return it.queue.items[out];
...@@ -428,3 +428,12 @@ test "std.PriorityQueue: remove at index" {...@@ -428,3 +428,12 @@ test "std.PriorityQueue: remove at index" {
428 expectEqual(queue.remove(), 3);428 expectEqual(queue.remove(), 3);
429 expectEqual(queue.removeOrNull(), null);429 expectEqual(queue.removeOrNull(), null);
430}430}
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 {...@@ -593,8 +593,10 @@ pub fn getUserInfo(name: []const u8) !UserInfo {
593/// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else593/// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
594/// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.594/// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
595pub fn posixGetUserInfo(name: []const u8) !UserInfo {595pub fn posixGetUserInfo(name: []const u8) !UserInfo {
596 var reader = try io.Reader.open("/etc/passwd", null);596 const file = try std.fs.openFileAbsolute("/etc/passwd", .{});
597 defer reader.close();597 defer file.close();
598
599 const reader = file.reader();
598600
599 const State = enum {601 const State = enum {
600 Start,602 Start,
...@@ -650,8 +652,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {...@@ -650,8 +652,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
650 '0'...'9' => byte - '0',652 '0'...'9' => byte - '0',
651 else => return error.CorruptPasswordFile,653 else => return error.CorruptPasswordFile,
652 };654 };
653 if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile;655 if (@mulWithOverflow(u32, uid, 10, &uid)) return error.CorruptPasswordFile;
654 if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile;656 if (@addWithOverflow(u32, uid, digit, &uid)) return error.CorruptPasswordFile;
655 },657 },
656 },658 },
657 .ReadGroupId => switch (byte) {659 .ReadGroupId => switch (byte) {
...@@ -666,8 +668,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {...@@ -666,8 +668,8 @@ pub fn posixGetUserInfo(name: []const u8) !UserInfo {
666 '0'...'9' => byte - '0',668 '0'...'9' => byte - '0',
667 else => return error.CorruptPasswordFile,669 else => return error.CorruptPasswordFile,
668 };670 };
669 if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile;671 if (@mulWithOverflow(u32, gid, 10, &gid)) return error.CorruptPasswordFile;
670 if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile;672 if (@addWithOverflow(u32, gid, digit, &gid)) return error.CorruptPasswordFile;
671 },673 },
672 },674 },
673 }675 }
lib/std/special/c.zig+55
...@@ -394,6 +394,61 @@ fn clone() callconv(.Naked) void {...@@ -394,6 +394,61 @@ fn clone() callconv(.Naked) void {
394 \\ syscall394 \\ syscall
395 );395 );
396 },396 },
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
397 else => @compileError("Implement clone() for this arch."),452 else => @compileError("Implement clone() for this arch."),
398 }453 }
399}454}
lib/std/start.zig+15
...@@ -121,6 +121,21 @@ fn _start() callconv(.Naked) noreturn {...@@ -121,6 +121,21 @@ fn _start() callconv(.Naked) noreturn {
121 : [argc] "=r" (-> [*]usize)121 : [argc] "=r" (-> [*]usize)
122 );122 );
123 },123 },
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 },
124 else => @compileError("unsupported arch"),139 else => @compileError("unsupported arch"),
125 }140 }
126 // If LLVM inlines stack variables into _start, they will overwrite141 // 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");...@@ -50,6 +50,7 @@ pub const builtin = @import("builtin.zig");
50pub const c = @import("c.zig");50pub const c = @import("c.zig");
51pub const cache_hash = @import("cache_hash.zig");51pub const cache_hash = @import("cache_hash.zig");
52pub const coff = @import("coff.zig");52pub const coff = @import("coff.zig");
53pub const compress = @import("compress.zig");
53pub const crypto = @import("crypto.zig");54pub const crypto = @import("crypto.zig");
54pub const cstr = @import("cstr.zig");55pub const cstr = @import("cstr.zig");
55pub const debug = @import("debug.zig");56pub const debug = @import("debug.zig");
src-self-hosted/libc_installation.zig+9-8
...@@ -9,6 +9,8 @@ const is_darwin = Target.current.isDarwin();...@@ -9,6 +9,8 @@ const is_darwin = Target.current.isDarwin();
9const is_windows = Target.current.os.tag == .windows;9const is_windows = Target.current.os.tag == .windows;
10const is_gnu = Target.current.isGnu();10const is_gnu = Target.current.isGnu();
1111
12const log = std.log.scoped(.libc_installation);
13
12usingnamespace @import("windows_sdk.zig");14usingnamespace @import("windows_sdk.zig");
1315
14/// See the render function implementation for documentation of the fields.16/// See the render function implementation for documentation of the fields.
...@@ -37,7 +39,6 @@ pub const LibCInstallation = struct {...@@ -37,7 +39,6 @@ pub const LibCInstallation = struct {
37 pub fn parse(39 pub fn parse(
38 allocator: *Allocator,40 allocator: *Allocator,
39 libc_file: []const u8,41 libc_file: []const u8,
40 stderr: anytype,
41 ) !LibCInstallation {42 ) !LibCInstallation {
42 var self: LibCInstallation = .{};43 var self: LibCInstallation = .{};
4344
...@@ -62,7 +63,7 @@ pub const LibCInstallation = struct {...@@ -62,7 +63,7 @@ pub const LibCInstallation = struct {
62 if (line.len == 0 or line[0] == '#') continue;63 if (line.len == 0 or line[0] == '#') continue;
63 var line_it = std.mem.split(line, "=");64 var line_it = std.mem.split(line, "=");
64 const name = line_it.next() orelse {65 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", .{});
66 return error.ParseError;67 return error.ParseError;
67 };68 };
68 const value = line_it.rest();69 const value = line_it.rest();
...@@ -81,31 +82,31 @@ pub const LibCInstallation = struct {...@@ -81,31 +82,31 @@ pub const LibCInstallation = struct {
81 }82 }
82 inline for (fields) |field, i| {83 inline for (fields) |field, i| {
83 if (!found_keys[i].found) {84 if (!found_keys[i].found) {
84 try stderr.print("missing field: {}\n", .{field.name});85 log.err("missing field: {}\n", .{field.name});
85 return error.ParseError;86 return error.ParseError;
86 }87 }
87 }88 }
88 if (self.include_dir == null) {89 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", .{});
90 return error.ParseError;91 return error.ParseError;
91 }92 }
92 if (self.sys_include_dir == null) {93 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", .{});
94 return error.ParseError;95 return error.ParseError;
95 }96 }
96 if (self.crt_dir == null and !is_darwin) {97 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)});
98 return error.ParseError;99 return error.ParseError;
99 }100 }
100 if (self.msvc_lib_dir == null and is_windows and !is_gnu) {101 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", .{
102 @tagName(Target.current.os.tag),103 @tagName(Target.current.os.tag),
103 @tagName(Target.current.abi),104 @tagName(Target.current.abi),
104 });105 });
105 return error.ParseError;106 return error.ParseError;
106 }107 }
107 if (self.kernel32_lib_dir == null and is_windows and !is_gnu) {108 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", .{
109 @tagName(Target.current.os.tag),110 @tagName(Target.current.os.tag),
110 @tagName(Target.current.abi),111 @tagName(Target.current.abi),
111 });112 });
src-self-hosted/link/MachO.zig+24-16
...@@ -32,6 +32,20 @@ const LoadCommand = union(enum) {...@@ -32,6 +32,20 @@ const LoadCommand = union(enum) {
32 .Dysymtab => |x| x.cmdsize,32 .Dysymtab => |x| x.cmdsize,
33 };33 };
34 }34 }
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 }
35};49};
3650
37base: File,51base: File,
...@@ -258,8 +272,7 @@ pub fn flush(self: *MachO, module: *Module) !void {...@@ -258,8 +272,7 @@ pub fn flush(self: *MachO, module: *Module) !void {
258272
259 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);273 var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
260 for (self.load_commands.items) |cmd| {274 for (self.load_commands.items) |cmd| {
261 const cmd_to_write = [1]@TypeOf(cmd){cmd};275 try cmd.write(&self.base.file.?, last_cmd_offset);
262 try self.base.file.?.pwriteAll(mem.sliceAsBytes(cmd_to_write[0..1]), last_cmd_offset);
263 last_cmd_offset += cmd.cmdsize();276 last_cmd_offset += cmd.cmdsize();
264 }277 }
265 const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);278 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 {...@@ -346,19 +359,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
346 .n_desc = 0,359 .n_desc = 0,
347 .n_value = addr,360 .n_value = addr,
348 };361 };
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);
351 try self.writeSymbol(decl.link.macho.symbol_table_index.?);366 try self.writeSymbol(decl.link.macho.symbol_table_index.?);
352367
353 const text_section = self.sections.items[self.text_section_index.?];368 const text_section = self.sections.items[self.text_section_index.?];
354 const section_offset = symbol.n_value - text_section.addr;369 const section_offset = symbol.n_value - text_section.addr;
355 const file_offset = text_section.offset + section_offset;370 const file_offset = text_section.offset + section_offset;
356 log.debug("file_offset 0x{x}\n", .{file_offset});371 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.373 try self.base.file.?.pwriteAll(code, file_offset);
360 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
361 return self.updateDeclExports(module, decl, decl_exports);
362}374}
363375
364pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}376pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
...@@ -374,7 +386,7 @@ pub fn updateDeclExports(...@@ -374,7 +386,7 @@ pub fn updateDeclExports(
374386
375 if (decl.link.macho.symbol_table_index == null) return;387 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.?];
378 // TODO implement390 // TODO implement
379 if (exports.len == 0) return;391 if (exports.len == 0) return;
380392
...@@ -488,9 +500,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -488,9 +500,8 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
488 const addr = blk: {500 const addr = blk: {
489 if (self.last_text_block) |last| {501 if (self.last_text_block) |last| {
490 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];502 const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
491 const ideal_capacity = last.size * alloc_num / alloc_den;503 const end_addr = last_symbol.n_value + last.size;
492 const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;504 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
493 const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
494 block_placement = last;505 block_placement = last;
495 break :blk new_start_addr;506 break :blk new_start_addr;
496 } else {507 } else {
...@@ -504,10 +515,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,...@@ -504,10 +515,7 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
504 const text_capacity = self.allocatedSize(text_section.offset);515 const text_capacity = self.allocatedSize(text_section.offset);
505 const needed_size = (addr + new_block_size) - text_section.addr;516 const needed_size = (addr + new_block_size) - text_section.addr;
506 log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });517 log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });
507518 assert(needed_size <= text_capacity); // TODO handle growth
508 if (needed_size > text_capacity) {
509 // TODO handle growth
510 }
511519
512 self.last_text_block = text_block;520 self.last_text_block = text_block;
513 text_section.size = needed_size;521 text_section.size = needed_size;
...@@ -659,7 +667,7 @@ fn writeSymbol(self: *MachO, index: usize) !void {...@@ -659,7 +667,7 @@ fn writeSymbol(self: *MachO, index: usize) !void {
659 defer tracy.end();667 defer tracy.end();
660668
661 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;669 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]};
663 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;671 const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
664 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });672 log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
665 try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);673 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 {...@@ -598,12 +598,9 @@ const Stage2LibCInstallation = extern struct {
598598
599// ABI warning599// ABI warning
600export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {600export 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();
603 const libc_file = mem.spanZ(libc_file_z);601 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) {
605 error.ParseError => return .SemanticAnalyzeFail,603 error.ParseError => return .SemanticAnalyzeFail,
606 error.DiskQuota => return .DiskQuota,
607 error.FileTooBig => return .FileTooBig,604 error.FileTooBig => return .FileTooBig,
608 error.InputOutput => return .FileSystem,605 error.InputOutput => return .FileSystem,
609 error.NoSpaceLeft => return .NoSpaceLeft,606 error.NoSpaceLeft => return .NoSpaceLeft,
...@@ -612,7 +609,6 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [...@@ -612,7 +609,6 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
612 error.SystemResources => return .SystemResources,609 error.SystemResources => return .SystemResources,
613 error.OperationAborted => return .OperationAborted,610 error.OperationAborted => return .OperationAborted,
614 error.WouldBlock => unreachable,611 error.WouldBlock => unreachable,
615 error.NotOpenForWriting => unreachable,
616 error.NotOpenForReading => unreachable,612 error.NotOpenForReading => unreachable,
617 error.Unexpected => return .Unexpected,613 error.Unexpected => return .Unexpected,
618 error.IsDir => return .IsDir,614 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 {...@@ -2032,7 +2032,7 @@ fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
2032 // Handle the remaining escapes Zig doesn't support by turning them2032 // Handle the remaining escapes Zig doesn't support by turning them
2033 // into their respective hex representation2033 // into their respective hex representation
2034 else => if (std.ascii.isCntrl(c))2034 else => if (std.ascii.isCntrl(c))
2035 std.fmt.bufPrint(char_buf, "\\x{x:0<2}", .{c}) catch unreachable2035 std.fmt.bufPrint(char_buf, "\\x{x:0>2}", .{c}) catch unreachable
2036 else2036 else
2037 std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable,2037 std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable,
2038 };2038 };
...@@ -5881,7 +5881,7 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N...@@ -5881,7 +5881,7 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
5881 },5881 },
5882 .Identifier => {5882 .Identifier => {
5883 const mangled_name = scope.getAlias(slice);5883 const mangled_name = scope.getAlias(slice);
5884 return transCreateNodeIdentifier(c, mangled_name);5884 return transCreateNodeIdentifier(c, checkForBuiltinTypedef(mangled_name) orelse mangled_name);
5885 },5885 },
5886 .LParen => {5886 .LParen => {
5887 const inner_node = try parseCExpr(c, m, scope);5887 const inner_node = try parseCExpr(c, m, scope);
...@@ -5899,6 +5899,10 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N...@@ -5899,6 +5899,10 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
5899 saw_l_paren = true;5899 saw_l_paren = true;
5900 _ = m.next();5900 _ = m.next();
5901 },5901 },
5902 // (type)sizeof(x)
5903 .Keyword_sizeof,
5904 // (type)alignof(x)
5905 .Keyword_alignof,
5902 // (type)identifier5906 // (type)identifier
5903 .Identifier => {},5907 .Identifier => {},
5904 // (type)integer5908 // (type)integer
...@@ -6309,6 +6313,48 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast....@@ -6309,6 +6313,48 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.
6309 node.rhs = try parseCPrefixOpExpr(c, m, scope);6313 node.rhs = try parseCPrefixOpExpr(c, m, scope);
6310 return &node.base;6314 return &node.base;
6311 },6315 },
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 },
6312 else => {6358 else => {
6313 m.i -= 1;6359 m.i -= 1;
6314 return try parseCSuffixOpExpr(c, m, scope);6360 return try parseCSuffixOpExpr(c, m, scope);
src/all_types.hpp+1
...@@ -2265,6 +2265,7 @@ struct CodeGen {...@@ -2265,6 +2265,7 @@ struct CodeGen {
22652265
2266 Stage2LibCInstallation *libc;2266 Stage2LibCInstallation *libc;
22672267
2268 bool is_versioned;
2268 size_t version_major;2269 size_t version_major;
2269 size_t version_minor;2270 size_t version_minor;
2270 size_t version_patch;2271 size_t version_patch;
src/analyze.cpp+156-125
...@@ -1003,7 +1003,8 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {...@@ -1003,7 +1003,8 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
1003 g->zig_target->arch == ZigLLVM_x86_64 ||1003 g->zig_target->arch == ZigLLVM_x86_64 ||
1004 target_is_arm(g->zig_target) ||1004 target_is_arm(g->zig_target) ||
1005 target_is_riscv(g->zig_target) ||1005 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))
1007 {1008 {
1008 X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);1009 X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
1009 return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval;1010 return abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval;
...@@ -2372,7 +2373,10 @@ static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) {...@@ -2372,7 +2373,10 @@ static Error resolve_union_alignment(CodeGen *g, ZigType *union_type) {
2372 if (field->gen_index == UINT32_MAX)2373 if (field->gen_index == UINT32_MAX)
2373 continue;2374 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 }
2376 if (align_expr != nullptr) {2380 if (align_expr != nullptr) {
2377 if (!analyze_const_align(g, &union_type->data.unionation.decls_scope->base, align_expr,2381 if (!analyze_const_align(g, &union_type->data.unionation.decls_scope->base, align_expr,
2378 &field->align))2382 &field->align))
...@@ -2468,9 +2472,6 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) {...@@ -2468,9 +2472,6 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) {
24682472
2469 AstNode *decl_node = union_type->data.unionation.decl_node;2473 AstNode *decl_node = union_type->data.unionation.decl_node;
24702474
2471
2472 assert(decl_node->type == NodeTypeContainerDecl);
2473
2474 uint32_t field_count = union_type->data.unionation.src_field_count;2475 uint32_t field_count = union_type->data.unionation.src_field_count;
2475 TypeUnionField *most_aligned_union_member = union_type->data.unionation.most_aligned_union_member;2476 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) {...@@ -2603,16 +2604,16 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
2603 if (decl_node->type == NodeTypeContainerDecl) {2604 if (decl_node->type == NodeTypeContainerDecl) {
2604 assert(!enum_type->data.enumeration.fields);2605 assert(!enum_type->data.enumeration.fields);
2605 field_count = (uint32_t)decl_node->data.container_decl.fields.length;2606 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 }
2614 } else {2607 } 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;
2616 }2617 }
26172618
2618 Scope *scope = &enum_type->data.enumeration.decls_scope->base;2619 Scope *scope = &enum_type->data.enumeration.decls_scope->base;
...@@ -3055,7 +3056,6 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {...@@ -3055,7 +3056,6 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
3055 return ErrorNone;3056 return ErrorNone;
30563057
3057 AstNode *decl_node = union_type->data.unionation.decl_node;3058 AstNode *decl_node = union_type->data.unionation.decl_node;
3058 assert(decl_node->type == NodeTypeContainerDecl);
30593059
3060 if (union_type->data.unionation.resolve_loop_flag_zero_bits) {3060 if (union_type->data.unionation.resolve_loop_flag_zero_bits) {
3061 if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {3061 if (union_type->data.unionation.resolve_status != ResolveStatusInvalid) {
...@@ -3069,30 +3069,51 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {...@@ -3069,30 +3069,51 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
30693069
3070 union_type->data.unionation.resolve_loop_flag_zero_bits = true;3070 union_type->data.unionation.resolve_loop_flag_zero_bits = true;
30713071
3072 assert(union_type->data.unionation.fields == nullptr);3072 uint32_t field_count;
3073 uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;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
3074 if (field_count == 0) {3084 if (field_count == 0) {
3075 add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));3085 add_node_error(g, decl_node, buf_sprintf("unions must have 1 or more fields"));
3076 union_type->data.unionation.src_field_count = field_count;3086 union_type->data.unionation.src_field_count = field_count;
3077 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3087 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3078 return ErrorSemanticAnalyzeFail;3088 return ErrorSemanticAnalyzeFail;
3079 }3089 }
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
3084 Scope *scope = &union_type->data.unionation.decls_scope->base;3091 Scope *scope = &union_type->data.unionation.decls_scope->base;
30853092
3086 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};3093 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
30873094
3088 AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr;3095 bool is_auto_enum; // union(enum) or union(enum(expr))
3089 union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum ||3096 bool is_explicit_enum; // union(expr)
3090 enum_type_node != nullptr;3097 AstNode *enum_type_node; // expr in union(enum(expr)) or union(expr)
3091 bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);3098 if (decl_node->type == NodeTypeContainerDecl) {
3092 bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr) && !(g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease);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);
3093 ZigType *tag_type;3113 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);
3095 bool *covered_enum_fields;3115 bool *covered_enum_fields;
3116 bool *is_zero_bits = heap::c_allocator.allocate<bool>(field_count);
3096 ZigLLVMDIEnumerator **di_enumerators;3117 ZigLLVMDIEnumerator **di_enumerators;
3097 if (create_enum_type) {3118 if (create_enum_type) {
3098 occupied_tag_values.init(field_count);3119 occupied_tag_values.init(field_count);
...@@ -3134,87 +3155,96 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {...@@ -3134,87 +3155,96 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
3134 tag_type->data.enumeration.fields_by_name.init(field_count);3155 tag_type->data.enumeration.fields_by_name.init(field_count);
3135 tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope;3156 tag_type->data.enumeration.decls_scope = union_type->data.unionation.decls_scope;
3136 } else if (enum_type_node != nullptr) {3157 } else if (enum_type_node != nullptr) {
3137 ZigType *enum_type = analyze_type_expr(g, scope, enum_type_node);3158 tag_type = analyze_type_expr(g, scope, enum_type_node);
3138 if (type_is_invalid(enum_type)) {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)) {
3139 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3168 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3140 return ErrorSemanticAnalyzeFail;3169 return ErrorSemanticAnalyzeFail;
3141 }3170 }
3142 if (enum_type->id != ZigTypeIdEnum) {3171 if (tag_type->id != ZigTypeIdEnum) {
3143 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3172 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3144 add_node_error(g, enum_type_node,3173 add_node_error(g, enum_type_node != nullptr ? enum_type_node : decl_node,
3145 buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));3174 buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&tag_type->name)));
3146 return ErrorSemanticAnalyzeFail;3175 return ErrorSemanticAnalyzeFail;
3147 }3176 }
3148 if ((err = type_resolve(g, enum_type, ResolveStatusAlignmentKnown))) {3177 if ((err = type_resolve(g, tag_type, ResolveStatusAlignmentKnown))) {
3149 assert(g->errors.length != 0);3178 assert(g->errors.length != 0);
3150 return err;3179 return err;
3151 }3180 }
3152 tag_type = enum_type;3181 covered_enum_fields = heap::c_allocator.allocate<bool>(tag_type->data.enumeration.src_field_count);
3153 covered_enum_fields = heap::c_allocator.allocate<bool>(enum_type->data.enumeration.src_field_count);
3154 } else {
3155 tag_type = nullptr;
3156 }3182 }
3157 union_type->data.unionation.tag_type = tag_type;3183 union_type->data.unionation.tag_type = tag_type;
31583184
3159 uint32_t gen_field_index = 0;
3160 for (uint32_t i = 0; i < field_count; i += 1) {3185 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;
3163 TypeUnionField *union_field = &union_type->data.unionation.fields[i];3186 TypeUnionField *union_field = &union_type->data.unionation.fields[i];
3164 union_field->name = field_node->data.struct_field.name;3187 if (decl_node->type == NodeTypeContainerDecl) {
3165 union_field->decl_node = field_node;3188 AstNode *field_node = decl_node->data.container_decl.fields.at(i);
3166 union_field->gen_index = UINT32_MAX;3189 union_field->name = field_node->data.struct_field.name;
31673190 union_field->decl_node = field_node;
3168 auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);3191 union_field->gen_index = UINT32_MAX;
3169 if (field_entry != nullptr) {3192 is_zero_bits[i] = false;
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 }
31763193
3177 bool field_is_zero_bits;3194 auto field_entry = union_type->data.unionation.fields_by_name.put_unique(union_field->name, union_field);
3178 if (field_node->data.struct_field.type == nullptr) {3195 if (field_entry != nullptr) {
3179 if (decl_node->data.container_decl.auto_enum ||3196 ErrorMsg *msg = add_node_error(g, union_field->decl_node,
3180 decl_node->data.container_decl.init_arg_expr != nullptr)3197 buf_sprintf("duplicate union field: '%s'", buf_ptr(union_field->name)));
3181 {3198 add_error_note(g, msg, field_entry->value->decl_node, buf_sprintf("other field here"));
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"));
3186 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3199 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3187 return ErrorSemanticAnalyzeFail;3200 return ErrorSemanticAnalyzeFail;
3188 }3201 }
3189 } else {3202
3190 ZigValue *field_type_val = analyze_const_value(g, scope,3203 if (field_node->data.struct_field.type == nullptr) {
3191 field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);3204 if (is_auto_enum || is_explicit_enum) {
3192 if (type_is_invalid(field_type_val->type)) {3205 union_field->type_entry = g->builtin_types.entry_void;
3193 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3206 is_zero_bits[i] = true;
3194 return ErrorSemanticAnalyzeFail;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;
3195 }3221 }
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) {
3201 bool field_is_opaque_type;3231 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))) {
3203 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3233 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3204 return ErrorSemanticAnalyzeFail;3234 return ErrorSemanticAnalyzeFail;
3205 }3235 }
3206 if (field_is_opaque_type) {3236 if (field_is_opaque_type) {
3207 add_node_error(g, field_node,3237 add_node_error(g, union_field->decl_node,
3208 buf_create_from_str(3238 buf_create_from_str(
3209 "opaque types have unknown size and therefore cannot be directly embedded in unions"));3239 "opaque types have unknown size and therefore cannot be directly embedded in unions"));
3210 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3240 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3211 return ErrorSemanticAnalyzeFail;3241 return ErrorSemanticAnalyzeFail;
3212 }3242 }
32133243
3214 switch (type_val_resolve_requires_comptime(g, field_type_val)) {3244 switch (type_val_resolve_requires_comptime(g, union_field->type_val)) {
3215 case ReqCompTimeInvalid:3245 case ReqCompTimeInvalid:
3216 if (g->trace_err != nullptr) {3246 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,
3218 buf_create_from_str("while checking this field"));3248 buf_create_from_str("while checking this field"));
3219 }3249 }
3220 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3250 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
...@@ -3226,29 +3256,25 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {...@@ -3226,29 +3256,25 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
3226 break;3256 break;
3227 }3257 }
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]))) {
3230 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3260 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3231 return ErrorSemanticAnalyzeFail;3261 return ErrorSemanticAnalyzeFail;
3232 }3262 }
3233 }3263 }
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
3241 if (create_enum_type) {3265 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);
3243 union_field->enum_field = &tag_type->data.enumeration.fields[i];3267 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;
3245 union_field->enum_field->decl_index = i;3269 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
3248 auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field);3272 auto prev_entry = tag_type->data.enumeration.fields_by_name.put_unique(union_field->enum_field->name, union_field->enum_field);
3249 assert(prev_entry == nullptr); // caught by union de-duplicator above3273 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
3252 // In this first pass we resolve explicit tag values.3278 // In this first pass we resolve explicit tag values.
3253 // In a second pass we will fill in the unspecified ones.3279 // In a second pass we will fill in the unspecified ones.
3254 if (tag_value != nullptr) {3280 if (tag_value != nullptr) {
...@@ -3276,11 +3302,11 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {...@@ -3276,11 +3302,11 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
3276 return ErrorSemanticAnalyzeFail;3302 return ErrorSemanticAnalyzeFail;
3277 }3303 }
3278 }3304 }
3279 } else if (enum_type_node != nullptr) {3305 } else if (tag_type != nullptr) {
3280 union_field->enum_field = find_enum_type_field(tag_type, field_name);3306 union_field->enum_field = find_enum_type_field(tag_type, union_field->name);
3281 if (union_field->enum_field == nullptr) {3307 if (union_field->enum_field == nullptr) {
3282 ErrorMsg *msg = add_node_error(g, field_node,3308 ErrorMsg *msg = add_node_error(g, union_field->decl_node,
3283 buf_sprintf("enum field not found: '%s'", buf_ptr(field_name)));3309 buf_sprintf("enum field not found: '%s'", buf_ptr(union_field->name)));
3284 add_error_note(g, msg, tag_type->data.enumeration.decl_node,3310 add_error_note(g, msg, tag_type->data.enumeration.decl_node,
3285 buf_sprintf("enum declared here"));3311 buf_sprintf("enum declared here"));
3286 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3312 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
...@@ -3289,21 +3315,23 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {...@@ -3289,21 +3315,23 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
3289 covered_enum_fields[union_field->enum_field->decl_index] = true;3315 covered_enum_fields[union_field->enum_field->decl_index] = true;
3290 } else {3316 } else {
3291 union_field->enum_field = heap::c_allocator.create<TypeEnumField>();3317 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;
3293 union_field->enum_field->decl_index = i;3319 union_field->enum_field->decl_index = i;
3294 bigint_init_unsigned(&union_field->enum_field->value, i);3320 bigint_init_unsigned(&union_field->enum_field->value, i);
3295 }3321 }
3296 assert(union_field->enum_field != nullptr);3322 assert(union_field->enum_field != nullptr);
3323 }
32973324
3298 if (field_is_zero_bits)3325 uint32_t gen_field_index = 0;
3299 continue;3326 for (uint32_t i = 0; i < field_count; i += 1) {
33003327 TypeUnionField *union_field = &union_type->data.unionation.fields[i];
3301 union_field->gen_index = gen_field_index;3328 if (!is_zero_bits[i]) {
3302 gen_field_index += 1;3329 union_field->gen_index = gen_field_index;
3330 gen_field_index += 1;
3331 }
3303 }3332 }
33043333
3305 bool src_have_tag = decl_node->data.container_decl.auto_enum ||3334 bool src_have_tag = is_auto_enum || is_explicit_enum;
3306 decl_node->data.container_decl.init_arg_expr != nullptr;
33073335
3308 if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) {3336 if (src_have_tag && union_type->data.unionation.layout != ContainerLayoutAuto) {
3309 const char *qual_str;3337 const char *qual_str;
...@@ -3317,8 +3345,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {...@@ -3317,8 +3345,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
3317 qual_str = "extern";3345 qual_str = "extern";
3318 break;3346 break;
3319 }3347 }
3320 AstNode *source_node = (decl_node->data.container_decl.init_arg_expr != nullptr) ?3348 AstNode *source_node = enum_type_node != nullptr ? enum_type_node : decl_node;
3321 decl_node->data.container_decl.init_arg_expr : decl_node;
3322 add_node_error(g, source_node,3349 add_node_error(g, source_node,
3323 buf_sprintf("%s union does not support enum tag type", qual_str));3350 buf_sprintf("%s union does not support enum tag type", qual_str));
3324 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3351 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
...@@ -3326,43 +3353,47 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {...@@ -3326,43 +3353,47 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
3326 }3353 }
33273354
3328 if (create_enum_type) {3355 if (create_enum_type) {
3329 // Now iterate again and populate the unspecified tag values3356 if (decl_node->type == NodeTypeContainerDecl) {
3330 uint32_t next_maybe_unoccupied_index = 0;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) {3360 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);3361 AstNode *field_node = decl_node->data.container_decl.fields.at(field_i);
3334 TypeUnionField *union_field = &union_type->data.unionation.fields[field_i];3362 TypeUnionField *union_field = &union_type->data.unionation.fields[field_i];
3335 AstNode *tag_value = field_node->data.struct_field.value;3363 AstNode *tag_value = field_node->data.struct_field.value;
33363364
3337 if (tag_value == nullptr) {3365 if (tag_value == nullptr) {
3338 if (occupied_tag_values.size() == 0) {3366 if (occupied_tag_values.size() == 0) {
3339 bigint_init_unsigned(&union_field->enum_field->value, next_maybe_unoccupied_index);3367 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);
3345 next_maybe_unoccupied_index += 1;3368 next_maybe_unoccupied_index += 1;
3346 auto entry = occupied_tag_values.put_unique(proposed_value, field_node);3369 } else {
3347 if (entry != nullptr) {3370 BigInt proposed_value;
3348 continue;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;
3349 }3379 }
3350 break;3380 bigint_init_bigint(&union_field->enum_field->value, &proposed_value);
3351 }3381 }
3352 bigint_init_bigint(&union_field->enum_field->value, &proposed_value);
3353 }3382 }
3354 }3383 }
3355 }3384 }
3356 } else if (enum_type_node != nullptr) {3385 } else if (tag_type != nullptr) {
3357 for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {3386 for (uint32_t i = 0; i < tag_type->data.enumeration.src_field_count; i += 1) {
3358 TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];3387 TypeEnumField *enum_field = &tag_type->data.enumeration.fields[i];
3359 if (!covered_enum_fields[i]) {3388 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);
3362 ErrorMsg *msg = add_node_error(g, decl_node,3389 ErrorMsg *msg = add_node_error(g, decl_node,
3363 buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name)));3390 buf_sprintf("enum field missing: '%s'", buf_ptr(enum_field->name)));
3364 add_error_note(g, msg, field_node,3391 if (decl_node->type == NodeTypeContainerDecl) {
3365 buf_sprintf("declared here"));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 }
3366 union_type->data.unionation.resolve_status = ResolveStatusInvalid;3397 union_type->data.unionation.resolve_status = ResolveStatusInvalid;
3367 }3398 }
3368 }3399 }
...@@ -8350,7 +8381,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS...@@ -8350,7 +8381,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
8350 ZigLLVMDIFile *di_file;8381 ZigLLVMDIFile *di_file;
8351 ZigLLVMDIScope *di_scope;8382 ZigLLVMDIScope *di_scope;
8352 unsigned line;8383 unsigned line;
8353 if (decl_node != nullptr && !struct_type->data.structure.created_by_at_type) {8384 if (decl_node != nullptr) {
8354 Scope *scope = &struct_type->data.structure.decls_scope->base;8385 Scope *scope = &struct_type->data.structure.decls_scope->base;
8355 ZigType *import = get_scope_import(scope);8386 ZigType *import = get_scope_import(scope);
8356 di_file = import->data.structure.root_struct->di_file;8387 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...@@ -8713,7 +8744,7 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta
87138744
8714 uint64_t store_size_in_bits = union_field->type_entry->size_in_bits;8745 uint64_t store_size_in_bits = union_field->type_entry->size_in_bits;
8715 uint64_t abi_align_in_bits = 8*union_field->type_entry->abi_align;8746 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;
8717 union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,8748 union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
8718 ZigLLVMTypeToScope(union_type->llvm_di_type), buf_ptr(union_field->enum_field->name),8749 ZigLLVMTypeToScope(union_type->llvm_di_type), buf_ptr(union_field->enum_field->name),
8719 import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1),8750 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) {...@@ -91,7 +91,8 @@ void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix) {
91 g->test_name_prefix = prefix;91 g->test_name_prefix = prefix;
92}92}
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;
95 g->version_major = major;96 g->version_major = major;
96 g->version_minor = minor;97 g->version_minor = minor;
97 g->version_patch = patch;98 g->version_patch = patch;
...@@ -10824,6 +10825,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {...@@ -10824,6 +10825,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
10824 cache_bool(ch, g->emit_bin);10825 cache_bool(ch, g->emit_bin);
10825 cache_bool(ch, g->emit_llvm_ir);10826 cache_bool(ch, g->emit_llvm_ir);
10826 cache_bool(ch, g->emit_asm);10827 cache_bool(ch, g->emit_asm);
10828 cache_bool(ch, g->is_versioned);
10827 cache_usize(ch, g->version_major);10829 cache_usize(ch, g->version_major);
10828 cache_usize(ch, g->version_minor);10830 cache_usize(ch, g->version_minor);
10829 cache_usize(ch, g->version_patch);10831 cache_usize(ch, g->version_patch);
...@@ -10894,7 +10896,7 @@ static void resolve_out_paths(CodeGen *g) {...@@ -10894,7 +10896,7 @@ static void resolve_out_paths(CodeGen *g) {
10894 buf_resize(out_basename, 0);10896 buf_resize(out_basename, 0);
10895 buf_append_str(out_basename, target_lib_file_prefix(g->zig_target));10897 buf_append_str(out_basename, target_lib_file_prefix(g->zig_target));
10896 buf_append_buf(out_basename, g->root_out_name);10898 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,
10898 g->version_major, g->version_minor, g->version_patch));10900 g->version_major, g->version_minor, g->version_patch));
10899 break;10901 break;
10900 }10902 }
src/codegen.hpp+1-1
...@@ -38,7 +38,7 @@ void codegen_set_rdynamic(CodeGen *g, bool rdynamic);...@@ -38,7 +38,7 @@ void codegen_set_rdynamic(CodeGen *g, bool rdynamic);
38void codegen_set_linker_script(CodeGen *g, const char *linker_script);38void codegen_set_linker_script(CodeGen *g, const char *linker_script);
39void codegen_set_test_filter(CodeGen *g, Buf *filter);39void codegen_set_test_filter(CodeGen *g, Buf *filter);
40void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix);40void 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);
42void codegen_add_time_event(CodeGen *g, const char *name);42void codegen_add_time_event(CodeGen *g, const char *name);
43void codegen_print_timing_report(CodeGen *g, FILE *f);43void codegen_print_timing_report(CodeGen *g, FILE *f);
44void codegen_link(CodeGen *g);44void 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...@@ -335,7 +335,7 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
335 bool is_ld = (strcmp(lib->name, "ld") == 0);335 bool is_ld = (strcmp(lib->name, "ld") == 0);
336336
337 CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node);337 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);
339 child_gen->is_dynamic = true;339 child_gen->is_dynamic = true;
340 child_gen->is_dummy_so = true;340 child_gen->is_dummy_so = true;
341 child_gen->version_script_path = map_file_path;341 child_gen->version_script_path = map_file_path;
src/ir.cpp+108-21
...@@ -64,6 +64,7 @@ enum ConstCastResultId {...@@ -64,6 +64,7 @@ enum ConstCastResultId {
64 ConstCastResultIdPointerChild,64 ConstCastResultIdPointerChild,
65 ConstCastResultIdSliceChild,65 ConstCastResultIdSliceChild,
66 ConstCastResultIdOptionalChild,66 ConstCastResultIdOptionalChild,
67 ConstCastResultIdOptionalShape,
67 ConstCastResultIdErrorUnionPayload,68 ConstCastResultIdErrorUnionPayload,
68 ConstCastResultIdErrorUnionErrorSet,69 ConstCastResultIdErrorUnionErrorSet,
69 ConstCastResultIdFnAlign,70 ConstCastResultIdFnAlign,
...@@ -11947,8 +11948,22 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -11947,8 +11948,22 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
11947 }11948 }
11948 }11949 }
1194911950
11950 // maybe11951 // optional types
11951 if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdOptional) {11952 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 }
11952 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type,11967 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type,
11953 actual_type->data.maybe.child_type, source_node, wanted_is_mutable);11968 actual_type->data.maybe.child_type, source_node, wanted_is_mutable);
11954 if (child.id == ConstCastResultIdInvalid)11969 if (child.id == ConstCastResultIdInvalid)
...@@ -14550,6 +14565,13 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa...@@ -14550,6 +14565,13 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
14550 report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg);14565 report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg);
14551 break;14566 break;
14552 }14567 }
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 }
14553 case ConstCastResultIdErrorUnionErrorSet: {14575 case ConstCastResultIdErrorUnionErrorSet: {
14554 ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,14576 ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
14555 buf_sprintf("error set '%s' cannot cast into error set '%s'",14577 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...@@ -25425,8 +25447,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2542525447
25426 init_const_slice(ira->codegen, fields[2], union_field_array, 0, union_field_count, false);25448 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
25430 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {25450 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
25431 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];25451 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
25432 ZigValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index];25452 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...@@ -25434,20 +25454,10 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
25434 union_field_val->special = ConstValSpecialStatic;25454 union_field_val->special = ConstValSpecialStatic;
25435 union_field_val->type = type_info_union_field_type;25455 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);
25438 inner_fields[1]->special = ConstValSpecialStatic;25458 inner_fields[1]->special = ConstValSpecialStatic;
25439 inner_fields[1]->type = get_optional_type(ira->codegen, type_info_enum_field_type);25459 inner_fields[1]->type = ira->codegen->builtin_types.entry_type;
2544025460 inner_fields[1]->data.x_type = union_field->type_entry;
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;
2545125461
25452 ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee;25462 ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee;
25453 init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true);25463 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...@@ -26103,7 +26113,8 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26103 entry->data.structure.layout = layout;26113 entry->data.structure.layout = layout;
26104 entry->data.structure.special = is_tuple ? StructSpecialInferredTuple : StructSpecialNone;26114 entry->data.structure.special = is_tuple ? StructSpecialInferredTuple : StructSpecialNone;
26105 entry->data.structure.created_by_at_type = true;26115 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
26108 assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);26119 assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
26109 assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0);26120 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...@@ -26227,13 +26238,89 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
26227 return ira->codegen->invalid_inst_gen->value->type;26238 return ira->codegen->invalid_inst_gen->value->type;
26228 field->value = *field_int_value;26239 field->value = *field_int_value;
26229 }26240 }
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 }
26231 return entry;26322 return entry;
26232 }26323 }
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;
26237 case ZigTypeIdFn:26324 case ZigTypeIdFn:
26238 case ZigTypeIdBoundFn:26325 case ZigTypeIdBoundFn:
26239 ir_add_error(ira, source_instr, buf_sprintf(26326 ir_add_error(ira, source_instr, buf_sprintf(
src/main.cpp+7-1
...@@ -416,6 +416,7 @@ static int main0(int argc, char **argv) {...@@ -416,6 +416,7 @@ static int main0(int argc, char **argv) {
416 const char *test_filter = nullptr;416 const char *test_filter = nullptr;
417 const char *test_name_prefix = nullptr;417 const char *test_name_prefix = nullptr;
418 bool test_evented_io = false;418 bool test_evented_io = false;
419 bool is_versioned = false;
419 size_t ver_major = 0;420 size_t ver_major = 0;
420 size_t ver_minor = 0;421 size_t ver_minor = 0;
421 size_t ver_patch = 0;422 size_t ver_patch = 0;
...@@ -870,6 +871,7 @@ static int main0(int argc, char **argv) {...@@ -870,6 +871,7 @@ static int main0(int argc, char **argv) {
870 fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));871 fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
871 return EXIT_FAILURE;872 return EXIT_FAILURE;
872 }873 }
874 is_versioned = true;
873 ver_major = atoi(buf_ptr(linker_args.at(i)));875 ver_major = atoi(buf_ptr(linker_args.at(i)));
874 } else if (buf_eql_str(arg, "--minor-image-version")) {876 } else if (buf_eql_str(arg, "--minor-image-version")) {
875 i += 1;877 i += 1;
...@@ -877,6 +879,7 @@ static int main0(int argc, char **argv) {...@@ -877,6 +879,7 @@ static int main0(int argc, char **argv) {
877 fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));879 fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
878 return EXIT_FAILURE;880 return EXIT_FAILURE;
879 }881 }
882 is_versioned = true;
880 ver_minor = atoi(buf_ptr(linker_args.at(i)));883 ver_minor = atoi(buf_ptr(linker_args.at(i)));
881 } else if (buf_eql_str(arg, "--stack")) {884 } else if (buf_eql_str(arg, "--stack")) {
882 i += 1;885 i += 1;
...@@ -1228,10 +1231,13 @@ static int main0(int argc, char **argv) {...@@ -1228,10 +1231,13 @@ static int main0(int argc, char **argv) {
1228 } else if (strcmp(arg, "--test-name-prefix") == 0) {1231 } else if (strcmp(arg, "--test-name-prefix") == 0) {
1229 test_name_prefix = argv[i];1232 test_name_prefix = argv[i];
1230 } else if (strcmp(arg, "--ver-major") == 0) {1233 } else if (strcmp(arg, "--ver-major") == 0) {
1234 is_versioned = true;
1231 ver_major = atoi(argv[i]);1235 ver_major = atoi(argv[i]);
1232 } else if (strcmp(arg, "--ver-minor") == 0) {1236 } else if (strcmp(arg, "--ver-minor") == 0) {
1237 is_versioned = true;
1233 ver_minor = atoi(argv[i]);1238 ver_minor = atoi(argv[i]);
1234 } else if (strcmp(arg, "--ver-patch") == 0) {1239 } else if (strcmp(arg, "--ver-patch") == 0) {
1240 is_versioned = true;
1235 ver_patch = atoi(argv[i]);1241 ver_patch = atoi(argv[i]);
1236 } else if (strcmp(arg, "--test-cmd") == 0) {1242 } else if (strcmp(arg, "--test-cmd") == 0) {
1237 test_exec_args.append(argv[i]);1243 test_exec_args.append(argv[i]);
...@@ -1590,7 +1596,7 @@ static int main0(int argc, char **argv) {...@@ -1590,7 +1596,7 @@ static int main0(int argc, char **argv) {
1590 g->emit_llvm_ir = emit_llvm_ir;1596 g->emit_llvm_ir = emit_llvm_ir;
15911597
1592 codegen_set_out_name(g, buf_out_name);1598 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);
1594 g->want_single_threaded = want_single_threaded;1600 g->want_single_threaded = want_single_threaded;
1595 codegen_set_linker_script(g, linker_script);1601 codegen_set_linker_script(g, linker_script);
1596 g->version_script_path = version_script; 1602 g->version_script_path = version_script;
src/target.cpp+21-8
...@@ -779,7 +779,7 @@ const char *target_lib_file_prefix(const ZigTarget *target) {...@@ -779,7 +779,7 @@ const char *target_lib_file_prefix(const ZigTarget *target) {
779 }779 }
780}780}
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,
783 size_t version_major, size_t version_minor, size_t version_patch)783 size_t version_major, size_t version_minor, size_t version_patch)
784{784{
785 if (target_is_wasm(target)) {785 if (target_is_wasm(target)) {
...@@ -799,11 +799,19 @@ const char *target_lib_file_ext(const ZigTarget *target, bool is_static,...@@ -799,11 +799,19 @@ const char *target_lib_file_ext(const ZigTarget *target, bool is_static,
799 if (is_static) {799 if (is_static) {
800 return ".a";800 return ".a";
801 } else if (target_os_is_darwin(target->os)) {801 } else if (target_os_is_darwin(target->os)) {
802 return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",802 if (is_versioned) {
803 version_major, version_minor, version_patch));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 }
804 } else {808 } else {
805 return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,809 if (is_versioned) {
806 version_major, version_minor, version_patch));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 }
807 }815 }
808 }816 }
809}817}
...@@ -853,6 +861,9 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {...@@ -853,6 +861,9 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
853 case ZigLLVM_riscv32:861 case ZigLLVM_riscv32:
854 case ZigLLVM_riscv64:862 case ZigLLVM_riscv64:
855 case ZigLLVM_mipsel:863 case ZigLLVM_mipsel:
864 case ZigLLVM_ppc:
865 case ZigLLVM_ppc64:
866 case ZigLLVM_ppc64le:
856 return "sp";867 return "sp";
857868
858 case ZigLLVM_wasm32:869 case ZigLLVM_wasm32:
...@@ -879,7 +890,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {...@@ -879,7 +890,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
879 case ZigLLVM_msp430:890 case ZigLLVM_msp430:
880 case ZigLLVM_nvptx:891 case ZigLLVM_nvptx:
881 case ZigLLVM_nvptx64:892 case ZigLLVM_nvptx64:
882 case ZigLLVM_ppc64le:
883 case ZigLLVM_r600:893 case ZigLLVM_r600:
884 case ZigLLVM_renderscript32:894 case ZigLLVM_renderscript32:
885 case ZigLLVM_renderscript64:895 case ZigLLVM_renderscript64:
...@@ -893,8 +903,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {...@@ -893,8 +903,6 @@ const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) {
893 case ZigLLVM_tce:903 case ZigLLVM_tce:
894 case ZigLLVM_tcele:904 case ZigLLVM_tcele:
895 case ZigLLVM_xcore:905 case ZigLLVM_xcore:
896 case ZigLLVM_ppc:
897 case ZigLLVM_ppc64:
898 case ZigLLVM_ve:906 case ZigLLVM_ve:
899 zig_panic("TODO populate this table with stack pointer register name for this CPU architecture");907 zig_panic("TODO populate this table with stack pointer register name for this CPU architecture");
900 }908 }
...@@ -1325,6 +1333,11 @@ bool target_is_mips(const ZigTarget *target) {...@@ -1325,6 +1333,11 @@ bool target_is_mips(const ZigTarget *target) {
1325 target->arch == ZigLLVM_mips64 || target->arch == ZigLLVM_mips64el;1333 target->arch == ZigLLVM_mips64 || target->arch == ZigLLVM_mips64el;
1326}1334}
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
1328unsigned target_fn_align(const ZigTarget *target) {1341unsigned target_fn_align(const ZigTarget *target) {
1329 return 16;1342 return 16;
1330}1343}
src/target.hpp+2-1
...@@ -87,7 +87,7 @@ const char *target_asm_file_ext(const ZigTarget *target);...@@ -87,7 +87,7 @@ const char *target_asm_file_ext(const ZigTarget *target);
87const char *target_llvm_ir_file_ext(const ZigTarget *target);87const char *target_llvm_ir_file_ext(const ZigTarget *target);
88const char *target_exe_file_ext(const ZigTarget *target);88const char *target_exe_file_ext(const ZigTarget *target);
89const char *target_lib_file_prefix(const ZigTarget *target);89const 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,
91 size_t version_major, size_t version_minor, size_t version_patch);91 size_t version_major, size_t version_minor, size_t version_patch);
9292
93bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target);93bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target);
...@@ -95,6 +95,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type);...@@ -95,6 +95,7 @@ ZigLLVM_OSType get_llvm_os_type(Os os_type);
9595
96bool target_is_arm(const ZigTarget *target);96bool target_is_arm(const ZigTarget *target);
97bool target_is_mips(const ZigTarget *target);97bool target_is_mips(const ZigTarget *target);
98bool target_is_ppc(const ZigTarget *target);
98bool target_allows_addr_zero(const ZigTarget *target);99bool target_allows_addr_zero(const ZigTarget *target);
99bool target_has_valgrind_support(const ZigTarget *target);100bool target_has_valgrind_support(const ZigTarget *target);
100bool target_os_is_darwin(Os os);101bool target_os_is_darwin(Os os);
test/compile_errors.zig+130-1
...@@ -10,6 +10,135 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -10,6 +10,135 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
10 "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'",10 "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'",
11 });11 });
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
13 cases.add("@Type with undefined",142 cases.add("@Type with undefined",
14 \\comptime {143 \\comptime {
15 \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } });144 \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } });
...@@ -7419,7 +7548,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -7419,7 +7548,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
7419 });7548 });
74207549
7421 cases.add( // fixed bug #20327550 cases.add( // fixed bug #2032
7422 "compile diagnostic string for top level decl type",7551 "compile diagnostic string for top level decl type",
7423 \\export fn entry() void {7552 \\export fn entry() void {
7424 \\ var foo: u32 = @This(){};7553 \\ var foo: u32 = @This(){};
7425 \\}7554 \\}
test/stack_traces.zig+3-3
...@@ -282,10 +282,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -282,10 +282,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
282 \\source.zig:10:8: [address] in main (test)282 \\source.zig:10:8: [address] in main (test)
283 \\ foo();283 \\ foo();
284 \\ ^284 \\ ^
285 \\start.zig:254:29: [address] in std.start.posixCallMainAndExit (test)285 \\start.zig:269:29: [address] in std.start.posixCallMainAndExit (test)
286 \\ return root.main();286 \\ return root.main();
287 \\ ^287 \\ ^
288 \\start.zig:128:5: [address] in std.start._start (test)288 \\start.zig:143:5: [address] in std.start._start (test)
289 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});289 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
290 \\ ^290 \\ ^
291 \\291 \\
...@@ -294,7 +294,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -294,7 +294,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
294 switch (std.Target.current.cpu.arch) {294 switch (std.Target.current.cpu.arch) {
295 .aarch64 => "", // TODO disabled; results in segfault295 .aarch64 => "", // TODO disabled; results in segfault
296 else => 296 else =>
297 \\start.zig:128:5: [address] in std.start._start (test)297 \\start.zig:143:5: [address] in std.start._start (test)
298 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});298 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
299 \\ ^299 \\ ^
300 \\300 \\
test/stage1/behavior/cast.zig+5
...@@ -849,3 +849,8 @@ test "comptime float casts" {...@@ -849,3 +849,8 @@ test "comptime float casts" {
849 expect(b == 2);849 expect(b == 2);
850 expect(@TypeOf(b) == comptime_int);850 expect(@TypeOf(b) == comptime_int);
851}851}
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 {...@@ -6,4 +6,7 @@ typedef struct Color {
6 unsigned char a;6 unsigned char a;
7} Color;7} Color;
8#define CLITERAL(type) (type)8#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 @@...@@ -1,12 +1,18 @@
1const expect = @import("std").testing.expect;1const expect = @import("std").testing.expect;
2const expectEqual = @import("std").testing.expectEqual;
23
3const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h"));4const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h"));
45
5test "initializer list expression" {6test "initializer list expression" {
6 @import("std").testing.expectEqual(h.Color{7 expectEqual(h.Color{
7 .r = 200,8 .r = 200,
8 .g = 200,9 .g = 200,
9 .b = 200,10 .b = 200,
10 .a = 255,11 .a = 255,
11 }, h.LIGHTGRAY);12 }, h.LIGHTGRAY);
12}13}
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" {...@@ -313,3 +313,106 @@ test "Type.Enum" {
313 testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b));313 testing.expectEqual(@as(u32, 5), @enumToInt(Bar.b));
314 testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6)));314 testing.expectEqual(@as(u32, 6), @enumToInt(@intToEnum(Bar, 6)));
315}315}
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 {...@@ -198,8 +198,6 @@ fn testUnion() void {
198 expect(typeinfo_info.Union.layout == .Auto);198 expect(typeinfo_info.Union.layout == .Auto);
199 expect(typeinfo_info.Union.tag_type.? == TypeId);199 expect(typeinfo_info.Union.tag_type.? == TypeId);
200 expect(typeinfo_info.Union.fields.len == 25);200 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);
203 expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));201 expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
204 expect(typeinfo_info.Union.decls.len == 21);202 expect(typeinfo_info.Union.decls.len == 21);
205203
...@@ -213,7 +211,6 @@ fn testUnion() void {...@@ -213,7 +211,6 @@ fn testUnion() void {
213 expect(notag_union_info.Union.tag_type == null);211 expect(notag_union_info.Union.tag_type == null);
214 expect(notag_union_info.Union.layout == .Auto);212 expect(notag_union_info.Union.layout == .Auto);
215 expect(notag_union_info.Union.fields.len == 2);213 expect(notag_union_info.Union.fields.len == 2);
216 expect(notag_union_info.Union.fields[0].enum_field == null);
217 expect(notag_union_info.Union.fields[1].field_type == u32);214 expect(notag_union_info.Union.fields[1].field_type == u32);
218215
219 const TestExternUnion = extern union {216 const TestExternUnion = extern union {
...@@ -223,7 +220,6 @@ fn testUnion() void {...@@ -223,7 +220,6 @@ fn testUnion() void {
223 const extern_union_info = @typeInfo(TestExternUnion);220 const extern_union_info = @typeInfo(TestExternUnion);
224 expect(extern_union_info.Union.layout == .Extern);221 expect(extern_union_info.Union.layout == .Extern);
225 expect(extern_union_info.Union.tag_type == null);222 expect(extern_union_info.Union.tag_type == null);
226 expect(extern_union_info.Union.fields[0].enum_field == null);
227 expect(extern_union_info.Union.fields[0].field_type == *c_void);223 expect(extern_union_info.Union.fields[0].field_type == *c_void);
228}224}
229225
test/translate_c.zig+3
...@@ -2761,12 +2761,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2761,12 +2761,15 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2761 cases.add("macro cast",2761 cases.add("macro cast",
2762 \\#define FOO(bar) baz((void *)(baz))2762 \\#define FOO(bar) baz((void *)(baz))
2763 \\#define BAR (void*) a2763 \\#define BAR (void*) a
2764 \\#define BAZ (uint32_t)(2)
2764 , &[_][]const u8{2765 , &[_][]const u8{
2765 \\pub inline fn FOO(bar: anytype) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {2766 \\pub inline fn FOO(bar: anytype) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {
2766 \\ return baz((@import("std").meta.cast(?*c_void, baz)));2767 \\ return baz((@import("std").meta.cast(?*c_void, baz)));
2767 \\}2768 \\}
2768 ,2769 ,
2769 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));2770 \\pub const BAR = (@import("std").meta.cast(?*c_void, a));
2771 ,
2772 \\pub const BAZ = (@import("std").meta.cast(u32, 2));
2770 });2773 });
27712774
2772 cases.add("macro with cast to unsigned short, long, and long long",2775 cases.add("macro with cast to unsigned short, long, and long long",