| author | |
| committer | |
| log | e2d1f9874df2a9221aaa9ec55bd2974b70601f64 |
| tree | eff7919b0717e193aa53b70fcee862d6f33deddb |
| parent | 52b8239a22aa37fe3914427cd4e2905231769e59 |
| parent | 58ee5f4e61cd9b7a9ba65798e2214efa3753a733 |
86 files changed, 5961 insertions(+), 510 deletions(-)
.builds/freebsd.yml+2-2| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | image: freebsd/latest | 1 | image: freebsd/latest |
| 2 | secrets: | 2 | secrets: |
| 3 | - 6c60aaee-92e7-4e7d-812c-114817689b4d | 3 | - 51bfddf5-86a6-4e01-8576-358c72a4a0a4 |
| 4 | - dd0bd962-7664-4d3e-b0f3-41c9ee96b8b8 | 4 | - 5cfede76-914e-4071-893e-e5e2e6ae3cea |
| 5 | sources: | 5 | sources: |
| 6 | - https://github.com/ziglang/zig | 6 | - https://github.com/ziglang/zig |
| 7 | tasks: | 7 | tasks: |
README.md+18| ... | @@ -68,3 +68,21 @@ make install | ... | @@ -68,3 +68,21 @@ make install |
| 68 | ##### Windows | 68 | ##### Windows |
| 69 | 69 | ||
| 70 | See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows | 70 | See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows |
| 71 | |||
| 72 | ## License | ||
| 73 | |||
| 74 | The ultimate goal of the Zig project is to serve users. As a first-order | ||
| 75 | effect, this means users of the compiler, helping programmers to write better | ||
| 76 | code. Even more important, however, are the end users. | ||
| 77 | |||
| 78 | Zig is intended to be used to help end users accomplish their goals. For | ||
| 79 | example, 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 | ||
| 81 | shameful to utilize Zig to exploit people instead of benefit them. | ||
| 82 | |||
| 83 | However, such problems are best solved with social norms, not with software | ||
| 84 | licenses. Any attempt to complicate the software license of Zig would risk | ||
| 85 | compromising the value Zig provides to users. | ||
| 86 | |||
| 87 | Therefore, Zig is available under the MIT (Expat) License, and comes with a | ||
| 88 | humble 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 | }); |
| 128 | 135 | ||
| 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% |
| 24 | cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b | 24 | cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b |
| 25 | msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b | 25 | msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b |
| 26 | 26 | ||
| 27 | "%ZIGINSTALLDIR%\bin\zig.exe" build test -Dskip-compile-errors || exit /b | 27 | "%ZIGINSTALLDIR%\bin\zig.exe" build test -Dskip-non-native -Dskip-compile-errors || exit /b |
| 28 | 28 | ||
| 29 | set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" | 29 | set "PATH=%CD:~0,2%\msys64\usr\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem" |
| 30 | SET "MSYSTEM=MINGW64" | 30 | SET "MSYSTEM=MINGW64" |
ci/srht/freebsd_script+4-6| ... | @@ -28,11 +28,8 @@ make $JOBS install | ... | @@ -28,11 +28,8 @@ make $JOBS install |
| 28 | 28 | ||
| 29 | release/bin/zig build test-fmt | 29 | release/bin/zig build test-fmt |
| 30 | release/bin/zig build test-behavior | 30 | release/bin/zig build test-behavior |
| 31 | 31 | # 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 | |||
| 36 | release/bin/zig build test-compiler-rt | 33 | release/bin/zig build test-compiler-rt |
| 37 | release/bin/zig build test-compare-output | 34 | release/bin/zig build test-compare-output |
| 38 | release/bin/zig build test-standalone | 35 | release/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 |
| 44 | release/bin/zig build test-run-translated-c | 41 | release/bin/zig build test-run-translated-c |
| 45 | # TODO disabled until we are shipping self-hosted | 42 | # TODO disabled until we are shipping self-hosted |
| 46 | #release/bin/zig build test-gen-h | 43 | #release/bin/zig build test-gen-h |
| 47 | release/bin/zig build test-compile-errors | 44 | # TODO disabled to save time and hit that 45 minute limit |
| 45 | #release/bin/zig build test-compile-errors | ||
| 48 | release/bin/zig build docs | 46 | release/bin/zig build docs |
| 49 | 47 | ||
| 50 | if [ -f ~/.s3cfg ]; then | 48 | if [ -f ~/.s3cfg ]; then |
ci/srht/on_master_success+2-2| ... | @@ -23,7 +23,7 @@ packages: | ... | @@ -23,7 +23,7 @@ packages: |
| 23 | - jq | 23 | - jq |
| 24 | - xz | 24 | - xz |
| 25 | secrets: | 25 | secrets: |
| 26 | - 6c60aaee-92e7-4e7d-812c-114817689b4d | 26 | - 51bfddf5-86a6-4e01-8576-358c72a4a0a4 |
| 27 | sources: | 27 | sources: |
| 28 | - https://github.com/ziglang/zig | 28 | - https://github.com/ziglang/zig |
| 29 | tasks: | 29 | tasks: |
| ... | @@ -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 to | 9731 | <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 like | 9732 | 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 | } |
| 260 | 260 | ||
| 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 | } |
| 265 | 270 | ||
| 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 | } |
| 340 | 345 | ||
| 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 | } |
| 348 | 355 | ||
| ... | @@ -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, |
| 1214 | 1223 | ||
| 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, |
| 1217 | 1228 | ||
| ... | @@ -1268,33 +1279,41 @@ pub const LibExeObjStep = struct { | ... | @@ -1268,33 +1279,41 @@ pub const LibExeObjStep = struct { |
| 1268 | Test, | 1279 | Test, |
| 1269 | }; | 1280 | }; |
| 1270 | 1281 | ||
| 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 | } |
| 1276 | 1295 | ||
| 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 | } |
| 1282 | 1301 | ||
| 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 | } |
| 1288 | 1307 | ||
| 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 | } |
| 1294 | 1313 | ||
| 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 | } |
| 1300 | 1319 | ||
| ... | @@ -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"); |
| 2007 | 2039 | ||
| 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; |
| 2038 | 2070 | ||
| 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; | ||
| 2042 | 2075 | ||
| 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; |
| 2045 | 2078 | ||
| 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 | } |
| 2287 | 2321 | ||
| 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 { |
| 2329 | 2363 | ||
| 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 { |
| 2350 | 2386 | ||
| 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 { |
| 2615 | 2651 | ||
| 2616 | pub const VcpkgLinkage = std.builtin.LinkMode; | 2652 | pub const VcpkgLinkage = std.builtin.LinkMode; |
| 2617 | 2653 | ||
| 2618 | pub const InstallDir = enum { | 2654 | pub 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 | }; |
| 2624 | 2662 | ||
| 2625 | pub const InstalledFile = struct { | 2663 | pub 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 | }; |
| 323 | 322 |
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 | }; |
| 134 | 134 | ||
| 135 | pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; | ||
| 136 | pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; | ||
| 137 | pub extern "c" fn rmdir(path: [*:0]const u8) c_int; | 135 | pub extern "c" fn rmdir(path: [*:0]const u8) c_int; |
| 138 | pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8; | 136 | pub extern "c" fn getenv(name: [*:0]const u8) ?[*:0]u8; |
| 139 | pub extern "c" fn sysctl(name: [*]const c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int; | 137 | pub 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) { |
| 237 | 235 | ||
| 238 | pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; | 236 | pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; |
| 239 | pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; | 237 | pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; |
| 240 | pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; | 238 | |
| 241 | pub extern "c" fn setuid(uid: c_uint) c_int; | 239 | pub extern "c" fn setuid(uid: uid_t) c_int; |
| 240 | pub extern "c" fn setgid(gid: gid_t) c_int; | ||
| 241 | pub extern "c" fn seteuid(euid: uid_t) c_int; | ||
| 242 | pub extern "c" fn setegid(egid: gid_t) c_int; | ||
| 243 | pub extern "c" fn setreuid(ruid: uid_t, euid: uid_t) c_int; | ||
| 244 | pub extern "c" fn setregid(rgid: gid_t, egid: gid_t) c_int; | ||
| 245 | pub extern "c" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) c_int; | ||
| 246 | pub extern "c" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) c_int; | ||
| 242 | 247 | ||
| 243 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; | 248 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; |
| 244 | pub extern "c" fn malloc(usize) ?*c_void; | 249 | pub 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; |
| 335 | pub extern "c" fn syncfs(fd: c_int) c_int; | 340 | pub extern "c" fn syncfs(fd: c_int) c_int; |
| 336 | pub extern "c" fn fsync(fd: c_int) c_int; | 341 | pub extern "c" fn fsync(fd: c_int) c_int; |
| 337 | pub extern "c" fn fdatasync(fd: c_int) c_int; | 342 | pub extern "c" fn fdatasync(fd: c_int) c_int; |
| 343 | |||
| 344 | pub 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. | ||
| 6 | const std = @import("std.zig"); | ||
| 7 | |||
| 8 | pub const deflate = @import("compress/deflate.zig"); | ||
| 9 | pub const gzip = @import("compress/gzip.zig"); | ||
| 10 | pub const zlib = @import("compress/zlib.zig"); | ||
| 11 | |||
| 12 | test "" { | ||
| 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 | |||
| 11 | const std = @import("std"); | ||
| 12 | const io = std.io; | ||
| 13 | const math = std.math; | ||
| 14 | const mem = std.mem; | ||
| 15 | |||
| 16 | const assert = std.debug.assert; | ||
| 17 | |||
| 18 | const MAXBITS = 15; | ||
| 19 | const MAXLCODES = 286; | ||
| 20 | const MAXDCODES = 30; | ||
| 21 | const MAXCODES = MAXLCODES + MAXDCODES; | ||
| 22 | const 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. | ||
| 27 | const PREFIX_LUT_BITS = 9; | ||
| 28 | |||
| 29 | const 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 | ||
| 130 | fn 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 | |||
| 139 | pub 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 | |||
| 633 | pub 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 | |||
| 9 | const std = @import("std"); | ||
| 10 | const io = std.io; | ||
| 11 | const fs = std.fs; | ||
| 12 | const testing = std.testing; | ||
| 13 | const mem = std.mem; | ||
| 14 | const deflate = std.compress.deflate; | ||
| 15 | |||
| 16 | // Flags for the FLG field in the header | ||
| 17 | const FTEXT = 1 << 0; | ||
| 18 | const FHCRC = 1 << 1; | ||
| 19 | const FEXTRA = 1 << 2; | ||
| 20 | const FNAME = 1 << 3; | ||
| 21 | const FCOMMENT = 1 << 4; | ||
| 22 | |||
| 23 | pub 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 | |||
| 158 | pub fn gzipStream(allocator: *mem.Allocator, reader: anytype) !GzipStream(@TypeOf(reader)) { | ||
| 159 | return GzipStream(@TypeOf(reader)).init(allocator, reader); | ||
| 160 | } | ||
| 161 | |||
| 162 | fn 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. | ||
| 179 | pub 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 | ||
| 192 | test "compressed data" { | ||
| 193 | try testReader( | ||
| 194 | @embedFile("rfc1952.txt.gz"), | ||
| 195 | "164ef0897b4cbec63abf1b57f069f3599bd0fb7c72c2a4dee21bd7e03ec9af67", | ||
| 196 | ); | ||
| 197 | } | ||
| 198 | |||
| 199 | test "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 | |||
| 7 | Network Working Group P. Deutsch | ||
| 8 | Request for Comments: 1951 Aladdin Enterprises | ||
| 9 | Category: Informational May 1996 | ||
| 10 | |||
| 11 | |||
| 12 | DEFLATE Compressed Data Format Specification version 1.3 | ||
| 13 | |||
| 14 | Status 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 | |||
| 20 | IESG Note: | ||
| 21 | |||
| 22 | The IESG takes no position on the validity of any Intellectual | ||
| 23 | Property Rights statements contained in this document. | ||
| 24 | |||
| 25 | Notices | ||
| 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 | |||
| 40 | Abstract | ||
| 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 | |||
| 58 | Deutsch Informational [Page 1] | ||
| 59 | |||
| 60 | RFC 1951 DEFLATE Compressed Data Format Specification May 1996 | ||
| 61 | |||
| 62 | |||
| 63 | Table 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 | |||
| 92 | 1. 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 | |||
| 114 | Deutsch Informational [Page 2] | ||
| 115 | |||
| 116 | RFC 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 | |||
| 170 | Deutsch Informational [Page 3] | ||
| 171 | |||
| 172 | RFC 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 | |||
| 187 | 2. 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 | |||
| 226 | Deutsch Informational [Page 4] | ||
| 227 | |||
| 228 | RFC 1951 DEFLATE Compressed Data Format Specification May 1996 | ||
| 229 | |||
| 230 | |||
| 231 | 3. 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 | |||
| 282 | Deutsch Informational [Page 5] | ||
| 283 | |||
| 284 | RFC 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 | |||
| 338 | Deutsch Informational [Page 6] | ||
| 339 | |||
| 340 | RFC 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 | |||
| 394 | Deutsch Informational [Page 7] | ||
| 395 | |||
| 396 | RFC 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 | |||
| 450 | Deutsch Informational [Page 8] | ||
| 451 | |||
| 452 | RFC 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 | |||
| 506 | Deutsch Informational [Page 9] | ||
| 507 | |||
| 508 | RFC 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 | |||
| 562 | Deutsch Informational [Page 10] | ||
| 563 | |||
| 564 | RFC 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 | |||
| 618 | Deutsch Informational [Page 11] | ||
| 619 | |||
| 620 | RFC 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 | |||
| 674 | Deutsch Informational [Page 12] | ||
| 675 | |||
| 676 | RFC 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 | |||
| 730 | Deutsch Informational [Page 13] | ||
| 731 | |||
| 732 | RFC 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 | |||
| 773 | 4. 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 | |||
| 786 | Deutsch Informational [Page 14] | ||
| 787 | |||
| 788 | RFC 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 | |||
| 842 | Deutsch Informational [Page 15] | ||
| 843 | |||
| 844 | RFC 1951 DEFLATE Compressed Data Format Specification May 1996 | ||
| 845 | |||
| 846 | |||
| 847 | 5. 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 | |||
| 869 | 6. 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 | |||
| 881 | 7. 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 | |||
| 887 | 8. 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 | |||
| 898 | Deutsch Informational [Page 16] | ||
| 899 | |||
| 900 | RFC 1951 DEFLATE Compressed Data Format Specification May 1996 | ||
| 901 | |||
| 902 | |||
| 903 | 9. 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 | |||
| 954 | Deutsch 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 | |||
| 9 | const std = @import("std"); | ||
| 10 | const io = std.io; | ||
| 11 | const fs = std.fs; | ||
| 12 | const testing = std.testing; | ||
| 13 | const mem = std.mem; | ||
| 14 | const deflate = std.compress.deflate; | ||
| 15 | |||
| 16 | pub 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 | |||
| 95 | pub fn zlibStream(allocator: *mem.Allocator, reader: anytype) !ZlibStream(@TypeOf(reader)) { | ||
| 96 | return ZlibStream(@TypeOf(reader)).init(allocator, reader); | ||
| 97 | } | ||
| 98 | |||
| 99 | fn 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. | ||
| 116 | pub 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 | ||
| 129 | test "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 | |||
| 147 | test "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 | }; |
| 37 | 37 | ||
| 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. | ||
| 43 | pub 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. |
| 39 | pub const core = struct { | 48 | pub 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"); |
| 70 | pub const randomBytes = std.os.getrandom; | 79 | pub const randomBytes = std.os.getrandom; |
| 71 | 80 | ||
| 72 | test "crypto" { | 81 | test "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 .. |
| 7 | 7 | ||
| 8 | const builtin = @import("builtin"); | 8 | const std = @import("../std.zig"); |
| 9 | const std = @import("std"); | 9 | const builtin = std.builtin; |
| 10 | const mem = std.mem; | 10 | const mem = std.mem; |
| 11 | const time = std.time; | 11 | const time = std.time; |
| 12 | const Timer = time.Timer; | 12 | const 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 copies | 4 | // 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. |
| 6 | const mem = @import("../mem.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const builtin = @import("builtin"); | 7 | const mem = std.mem; |
| 8 | const debug = @import("../debug.zig"); | 8 | const math = std.math; |
| 9 | const math = @import("../math.zig"); | 9 | const debug = std.debug; |
| 10 | const htest = @import("test.zig"); | 10 | const htest = @import("test.zig"); |
| 11 | 11 | ||
| 12 | const RoundParam = struct { | 12 | const RoundParam = struct { |
lib/std/crypto/chacha20.zig-2| ... | @@ -7,10 +7,8 @@ | ... | @@ -7,10 +7,8 @@ |
| 7 | 7 | ||
| 8 | const std = @import("../std.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const mem = std.mem; | 9 | const mem = std.mem; |
| 10 | const endian = std.endian; | ||
| 11 | const assert = std.debug.assert; | 10 | const assert = std.debug.assert; |
| 12 | const testing = std.testing; | 11 | const testing = std.testing; |
| 13 | const builtin = @import("builtin"); | ||
| 14 | const maxInt = std.math.maxInt; | 12 | const maxInt = std.math.maxInt; |
| 15 | const Poly1305 = std.crypto.onetimeauth.Poly1305; | 13 | const Poly1305 = std.crypto.onetimeauth.Poly1305; |
| 16 | 14 |
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 copies | 4 | // 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. |
| 6 | const mem = @import("../mem.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = @import("../math.zig"); | 7 | const mem = std.mem; |
| 8 | const endian = @import("../endian.zig"); | 8 | const math = std.math; |
| 9 | const builtin = @import("builtin"); | 9 | const debug = std.debug; |
| 10 | const debug = @import("../debug.zig"); | ||
| 11 | const fmt = @import("../fmt.zig"); | ||
| 12 | 10 | ||
| 13 | const RoundParam = struct { | 11 | const 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 | |||
| 7 | const std = @import("std"); | ||
| 8 | const mem = std.mem; | ||
| 9 | const 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 | |||
| 39 | pub 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`. | ||
| 65 | pub 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 | |||
| 164 | const htest = @import("test.zig"); | ||
| 165 | const HmacSha1 = std.crypto.auth.hmac.HmacSha1; | ||
| 166 | |||
| 167 | // RFC 6070 PBKDF2 HMAC-SHA1 Test Vectors | ||
| 168 | test "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 | |||
| 183 | test "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 | |||
| 198 | test "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 | |||
| 213 | test "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 | |||
| 233 | test "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 | |||
| 248 | test "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 | |||
| 263 | test "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 copies | 4 | // 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. |
| 6 | const std = @import("std"); | 6 | const std = @import("../std.zig"); |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | 8 | ||
| 9 | pub const Poly1305 = struct { | 9 | pub 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 copies | 4 | // 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. |
| 6 | const mem = @import("../mem.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = @import("../math.zig"); | 7 | const mem = std.mem; |
| 8 | const endian = @import("../endian.zig"); | 8 | const math = std.math; |
| 9 | const debug = @import("../debug.zig"); | 9 | const debug = std.debug; |
| 10 | const builtin = @import("builtin"); | ||
| 11 | 10 | ||
| 12 | const RoundParam = struct { | 11 | const 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 copies | 4 | // 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. |
| 6 | const mem = @import("../mem.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = @import("../math.zig"); | 7 | const mem = std.mem; |
| 8 | const endian = @import("../endian.zig"); | 8 | const math = std.math; |
| 9 | const debug = @import("../debug.zig"); | 9 | const debug = std.debug; |
| 10 | const builtin = @import("builtin"); | ||
| 11 | const htest = @import("test.zig"); | 10 | const htest = @import("test.zig"); |
| 12 | 11 | ||
| 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 copies | 4 | // 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. |
| 6 | const mem = @import("../mem.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = @import("../math.zig"); | 7 | const mem = std.mem; |
| 8 | const endian = @import("../endian.zig"); | 8 | const math = std.math; |
| 9 | const debug = @import("../debug.zig"); | 9 | const debug = std.debug; |
| 10 | const builtin = @import("builtin"); | ||
| 11 | const htest = @import("test.zig"); | 10 | const htest = @import("test.zig"); |
| 12 | 11 | ||
| 13 | pub const Sha3_224 = Keccak(224, 0x06); | 12 | pub 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 | } |
| 219 | 219 | ||
| 220 | /// Return an authentication tag for the current state | 220 | /// 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 | } |
| 225 | 226 |
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. |
| 6 | const std = @import("../std.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const testing = std.testing; | 7 | const testing = std.testing; |
| 8 | const mem = std.mem; | ||
| 9 | const fmt = std.fmt; | 8 | const fmt = std.fmt; |
| 10 | 9 | ||
| 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 { |
| 471 | 471 | ||
| 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); |
| 476 | 476 | ||
| 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/2765 | 113 | /// 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 { |
| 22 | pub const FormatOptions = struct { | 22 | pub 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 | }; |
| 28 | 28 | ||
| ... | @@ -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 | } else | 945 | } else |
| 948 | value; | 946 | value; |
| 949 | 947 | ||
| 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 | } | ||
| 956 | 949 | ||
| 957 | fn 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 | } | ||
| 983 | 954 | ||
| 984 | fn 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; | ||
| 998 | 958 | ||
| 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 | } |
| 1006 | 968 | ||
| 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 |
| 1010 | 972 | 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 | } |
| 1027 | 985 | ||
| 1028 | pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, options: FormatOptions) usize { | 986 | pub 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 | } |
| 1250 | 1212 | ||
| 1251 | test "error" { | 1213 | test "error" { |
| ... | @@ -1283,7 +1245,17 @@ test "int.specifier" { | ... | @@ -1283,7 +1245,17 @@ test "int.specifier" { |
| 1283 | 1245 | ||
| 1284 | test "int.padded" { | 1246 | test "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 | } |
| 1288 | 1260 | ||
| 1289 | test "buffer" { | 1261 | test "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 | } |
| 1331 | 1303 | ||
| 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 | } |
| 1335 | 1307 | ||
| ... | @@ -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" { |
| 1805 | 1777 | ||
| 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" { |
| 1818 | 1790 | ||
| 1819 | test "padding" { | 1791 | test "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 | } |
| 1831 | 1804 | ||
| 1832 | test "decimal float padding" { | 1805 | test "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"); |
| 21 | 21 | ||
| 22 | // TODO audit these APIs with respect to Dir and absolute paths | 22 | // TODO audit these APIs with respect to Dir and absolute paths |
| 23 | 23 | ||
| 24 | pub const rename = os.rename; | ||
| 25 | pub const renameZ = os.renameZ; | ||
| 26 | pub const renameC = @compileError("deprecated: renamed to renameZ"); | ||
| 27 | pub const renameW = os.renameW; | ||
| 28 | pub const realpath = os.realpath; | 24 | pub const realpath = os.realpath; |
| 29 | pub const realpathZ = os.realpathZ; | 25 | pub const realpathZ = os.realpathZ; |
| 30 | pub const realpathC = @compileError("deprecated: renamed to realpathZ"); | 26 | pub 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); |
| 91 | 87 | ||
| 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 PathAlreadyExists | 92 | 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 | } |
| 257 | 253 | ||
| 254 | pub const renameC = @compileError("deprecated: use renameZ, dir.renameZ, or renameAbsoluteZ"); | ||
| 255 | |||
| 256 | /// Same as `Dir.rename` except the paths are absolute. | ||
| 257 | pub 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. | ||
| 264 | pub 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. | ||
| 271 | pub 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` | ||
| 278 | pub 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. | ||
| 283 | pub 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. | ||
| 289 | pub 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 | |||
| 258 | pub const Dir = struct { | 293 | pub const Dir = struct { |
| 259 | fd: os.fd_t, | 294 | fd: os.fd_t, |
| 260 | 295 | ||
| ... | @@ -1338,6 +1373,27 @@ pub const Dir = struct { | ... | @@ -1338,6 +1373,27 @@ pub const Dir = struct { |
| 1338 | }; | 1373 | }; |
| 1339 | } | 1374 | } |
| 1340 | 1375 | ||
| 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 nonexistent | 1398 | /// 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 | } |
| 741 | 741 | ||
| 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; |
| 745 | 746 | ||
| 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 | } |
| 753 | 755 |
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 | } |
| 276 | 276 | ||
| 277 | test "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 | |||
| 308 | test "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 | |||
| 351 | test "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 | |||
| 365 | test "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 | |||
| 385 | test "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 | |||
| 277 | test "openSelfExe" { | 438 | test "openSelfExe" { |
| 278 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 439 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 279 | 440 |
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 #2908 | 144 | // 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]; |
| 72 | 72 | ||
| 73 | // Unrolling this way gives ~50Mb/s increase | 73 | // 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); | ||
| 78 | 75 | ||
| 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 | } |
| 115 | 115 | ||
| 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 | } |
| 119 | 119 |
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); |
| 157 | 157 | ||
| 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 alternative | 320 | // value, but @field requires a comptime value. Our alternative |
| 321 | // is to check each field for a match | 321 | // 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 | }; |
| 649 | 649 | ||
| 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. | ||
| 656 | pub 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 be | 676 | /// 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 the | 677 | /// 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 dynamic | 678 | /// 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 information | 1112 | /// second stab entry with length information |
| 1087 | pub const N_LENG = 0xfe; | 1113 | pub const N_LENG = 0xfe; |
| 1088 | 1114 | ||
| 1089 | /// If a segment contains any sections marked with S_ATTR_DEBUG then all | 1115 | // 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 than | 1116 | // 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 this | 1117 | // section the indirect symbol table entries, in corresponding order in the |
| 1092 | /// section. A section with this attribute may contain no symbols and must have | 1118 | // 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 contents | 1119 | // of the section structure. Since the indirect symbol table entries |
| 1094 | /// from sections with this attribute into its output file. These sections | 1120 | // 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 | ||
| 1127 | pub const S_NON_LAZY_SYMBOL_POINTERS = 0x6; | ||
| 1128 | |||
| 1129 | /// section with only lazy symbol pointers | ||
| 1130 | pub const S_LAZY_SYMBOL_POINTERS = 0x7; | ||
| 1131 | |||
| 1132 | /// section with only symbol stubs, byte size of stub in the reserved2 field | ||
| 1133 | pub const S_SYMBOL_STUBS = 0x8; | ||
| 1134 | |||
| 1135 | /// section with only function pointers for initialization | ||
| 1136 | pub const S_MOD_INIT_FUNC_POINTERS = 0x9; | ||
| 1137 | |||
| 1138 | /// section with only function pointers for termination | ||
| 1139 | pub const S_MOD_TERM_FUNC_POINTERS = 0xa; | ||
| 1140 | |||
| 1141 | /// section contains symbols that are to be coalesced | ||
| 1142 | pub const S_COALESCED = 0xb; | ||
| 1143 | |||
| 1144 | /// zero fill on demand section (that can be larger than 4 gigabytes) | ||
| 1145 | pub const S_GB_ZEROFILL = 0xc; | ||
| 1146 | |||
| 1147 | /// section with only pairs of function pointers for interposing | ||
| 1148 | pub const S_INTERPOSING = 0xd; | ||
| 1149 | |||
| 1150 | /// section with only 16 byte literals | ||
| 1151 | pub const S_16BYTE_LITERALS = 0xe; | ||
| 1152 | |||
| 1153 | /// section contains DTrace Object Format | ||
| 1154 | pub const S_DTRACE_DOF = 0xf; | ||
| 1155 | |||
| 1156 | /// section with only lazy symbol pointers to lazy loaded dylibs | ||
| 1157 | pub 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 section | 1167 | /// a debug section |
| 1097 | pub const S_ATTR_DEBUG = 0x02000000; | 1168 | pub const S_ATTR_DEBUG = 0x02000000; |
| 1098 | 1169 | ||
| ... | @@ -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; |
| 1154 | 1225 | ||
| 1155 | /// VM execute permission | 1226 | /// VM execute permission |
| 1156 | pub const VM_PROT_EXECUTE: vm_prot_t = 0x4; | 1227 | pub const VM_PROT_EXECUTE: vm_prot_t = 0x4; |
| 1228 | |||
| 1229 | pub 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)); |
| 466 | 466 | ||
| 467 | const info = @typeInfo(U).Union; | 467 | const info = @typeInfo(U).Union; |
| 468 | const tag_info = @typeInfo(@TagType(U)).Enum; | ||
| 468 | 469 | ||
| 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 | } |
| 474 | 477 | ||
| ... | @@ -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; |
| 511 | 514 | ||
| 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" { |
| 774 | 780 | ||
| 775 | var i = @as(i64, 10); | 781 | var i = @as(i64, 10); |
| 776 | 782 | ||
| 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. | ||
| 801 | pub 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 | |||
| 818 | test "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, as | 321 | /// 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)`. |
| 324 | pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | 325 | pub 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, as | 696 | /// 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)`. |
| 697 | pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { | 700 | pub 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 { |
| 726 | 729 | ||
| 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, as | 856 | /// 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)`. |
| 855 | pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { | 860 | pub 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 | } |
| 1886 | 1892 | ||
| 1887 | const RenameError = error{ | 1893 | pub const RenameError = error{ |
| 1888 | /// In WASI, this error may occur when the file descriptor does | 1894 | /// 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 |
| 2515 | pub const SetEidError = error{ | 2522 | pub const SetEidError = error{ |
| 2516 | InvalidUserId, | 2523 | InvalidUserId, |
| 2517 | PermissionDenied, | 2524 | PermissionDenied, |
| 2518 | }; | 2525 | } || UnexpectedError; |
| 2519 | 2526 | ||
| 2520 | pub const SetIdError = error{ResourceLimitReached} || SetEidError || UnexpectedError; | 2527 | pub const SetIdError = error{ResourceLimitReached} || SetEidError; |
| 2521 | 2528 | ||
| 2522 | pub fn setuid(uid: uid_t) SetIdError!void { | 2529 | pub 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, as | 4719 | /// 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)`. |
| 4715 | pub fn sendfile( | 4723 | pub 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 | }; |
| 4738 | 4747 | ||
| ... | @@ -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 | |||
| 5431 | pub 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 | |||
| 5446 | pub 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 | }; |
| 25 | 26 | ||
| 26 | pub usingnamespace @import("linux/netlink.zig"); | 27 | pub usingnamespace @import("linux/netlink.zig"); |
| 28 | pub usingnamespace @import("linux/prctl.zig"); | ||
| 29 | pub usingnamespace @import("linux/securebits.zig"); | ||
| 27 | 30 | ||
| 28 | const is_mips = builtin.arch.isMIPS(); | 31 | const is_mips = builtin.arch.isMIPS(); |
| 29 | 32 | ||
| ... | @@ -1590,6 +1593,123 @@ pub const RR_A = 1; | ... | @@ -1590,6 +1593,123 @@ pub const RR_A = 1; |
| 1590 | pub const RR_CNAME = 5; | 1593 | pub const RR_CNAME = 5; |
| 1591 | pub const RR_AAAA = 28; | 1594 | pub const RR_AAAA = 28; |
| 1592 | 1595 | ||
| 1596 | /// Turn off Nagle's algorithm | ||
| 1597 | pub const TCP_NODELAY = 1; | ||
| 1598 | /// Limit MSS | ||
| 1599 | pub const TCP_MAXSEG = 2; | ||
| 1600 | /// Never send partially complete segments. | ||
| 1601 | pub const TCP_CORK = 3; | ||
| 1602 | /// Start keeplives after this period, in seconds | ||
| 1603 | pub const TCP_KEEPIDLE = 4; | ||
| 1604 | /// Interval between keepalives | ||
| 1605 | pub const TCP_KEEPINTVL = 5; | ||
| 1606 | /// Number of keepalives before death | ||
| 1607 | pub const TCP_KEEPCNT = 6; | ||
| 1608 | /// Number of SYN retransmits | ||
| 1609 | pub const TCP_SYNCNT = 7; | ||
| 1610 | /// Life time of orphaned FIN-WAIT-2 state | ||
| 1611 | pub const TCP_LINGER2 = 8; | ||
| 1612 | /// Wake up listener only when data arrive | ||
| 1613 | pub const TCP_DEFER_ACCEPT = 9; | ||
| 1614 | /// Bound advertised window | ||
| 1615 | pub const TCP_WINDOW_CLAMP = 10; | ||
| 1616 | /// Information about this connection. | ||
| 1617 | pub const TCP_INFO = 11; | ||
| 1618 | /// Block/reenable quick acks | ||
| 1619 | pub const TCP_QUICKACK = 12; | ||
| 1620 | /// Congestion control algorithm | ||
| 1621 | pub const TCP_CONGESTION = 13; | ||
| 1622 | /// TCP MD5 Signature (RFC2385) | ||
| 1623 | pub const TCP_MD5SIG = 14; | ||
| 1624 | /// Use linear timeouts for thin streams | ||
| 1625 | pub const TCP_THIN_LINEAR_TIMEOUTS = 16; | ||
| 1626 | /// Fast retrans. after 1 dupack | ||
| 1627 | pub const TCP_THIN_DUPACK = 17; | ||
| 1628 | /// How long for loss retry before timeout | ||
| 1629 | pub const TCP_USER_TIMEOUT = 18; | ||
| 1630 | /// TCP sock is under repair right now | ||
| 1631 | pub const TCP_REPAIR = 19; | ||
| 1632 | pub const TCP_REPAIR_QUEUE = 20; | ||
| 1633 | pub const TCP_QUEUE_SEQ = 21; | ||
| 1634 | pub const TCP_REPAIR_OPTIONS = 22; | ||
| 1635 | /// Enable FastOpen on listeners | ||
| 1636 | pub const TCP_FASTOPEN = 23; | ||
| 1637 | pub const TCP_TIMESTAMP = 24; | ||
| 1638 | /// limit number of unsent bytes in write queue | ||
| 1639 | pub const TCP_NOTSENT_LOWAT = 25; | ||
| 1640 | /// Get Congestion Control (optional) info | ||
| 1641 | pub const TCP_CC_INFO = 26; | ||
| 1642 | /// Record SYN headers for new connections | ||
| 1643 | pub const TCP_SAVE_SYN = 27; | ||
| 1644 | /// Get SYN headers recorded for connection | ||
| 1645 | pub const TCP_SAVED_SYN = 28; | ||
| 1646 | /// Get/set window parameters | ||
| 1647 | pub const TCP_REPAIR_WINDOW = 29; | ||
| 1648 | /// Attempt FastOpen with connect | ||
| 1649 | pub const TCP_FASTOPEN_CONNECT = 30; | ||
| 1650 | /// Attach a ULP to a TCP connection | ||
| 1651 | pub const TCP_ULP = 31; | ||
| 1652 | /// TCP MD5 Signature with extensions | ||
| 1653 | pub const TCP_MD5SIG_EXT = 32; | ||
| 1654 | /// Set the key for Fast Open (cookie) | ||
| 1655 | pub const TCP_FASTOPEN_KEY = 33; | ||
| 1656 | /// Enable TFO without a TFO cookie | ||
| 1657 | pub const TCP_FASTOPEN_NO_COOKIE = 34; | ||
| 1658 | pub const TCP_ZEROCOPY_RECEIVE = 35; | ||
| 1659 | /// Notify bytes available to read as a cmsg on read | ||
| 1660 | pub const TCP_INQ = 36; | ||
| 1661 | pub const TCP_CM_INQ = TCP_INQ; | ||
| 1662 | /// delay outgoing packets by XX usec | ||
| 1663 | pub const TCP_TX_DELAY = 37; | ||
| 1664 | |||
| 1665 | pub const TCP_REPAIR_ON = 1; | ||
| 1666 | pub const TCP_REPAIR_OFF = 0; | ||
| 1667 | /// Turn off without window probes | ||
| 1668 | pub const TCP_REPAIR_OFF_NO_WP = -1; | ||
| 1669 | |||
| 1670 | pub const tcp_repair_opt = extern struct { | ||
| 1671 | opt_code: u32, | ||
| 1672 | opt_val: u32, | ||
| 1673 | }; | ||
| 1674 | |||
| 1675 | pub 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 | |||
| 1683 | pub 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 | ||
| 1691 | pub 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 | ||
| 1703 | pub const TCPI_OPT_TIMESTAMPS = 1; | ||
| 1704 | pub const TCPI_OPT_SACK = 2; | ||
| 1705 | pub const TCPI_OPT_WSCALE = 4; | ||
| 1706 | /// ECN was negociated at TCP session init | ||
| 1707 | pub const TCPI_OPT_ECN = 8; | ||
| 1708 | /// we received at least one packet with ECT | ||
| 1709 | pub const TCPI_OPT_ECN_SEEN = 16; | ||
| 1710 | /// SYN-ACK acked data in SYN sent or rcvd | ||
| 1711 | pub const TCPI_OPT_SYN_DATA = 32; | ||
| 1712 | |||
| 1593 | pub const nfds_t = usize; | 1713 | pub const nfds_t = usize; |
| 1594 | pub const pollfd = extern struct { | 1714 | pub 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 @@ | ||
| 1 | const std = @import("../../../std.zig"); | ||
| 2 | const linux = std.os.linux; | ||
| 3 | const socklen_t = linux.socklen_t; | ||
| 4 | const iovec = linux.iovec; | ||
| 5 | const iovec_const = linux.iovec_const; | ||
| 6 | const uid_t = linux.uid_t; | ||
| 7 | const gid_t = linux.gid_t; | ||
| 8 | const pid_t = linux.pid_t; | ||
| 9 | const stack_t = linux.stack_t; | ||
| 10 | const sigset_t = linux.sigset_t; | ||
| 11 | pub 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 | |||
| 407 | pub const O_CREAT = 0o100; | ||
| 408 | pub const O_EXCL = 0o200; | ||
| 409 | pub const O_NOCTTY = 0o400; | ||
| 410 | pub const O_TRUNC = 0o1000; | ||
| 411 | pub const O_APPEND = 0o2000; | ||
| 412 | pub const O_NONBLOCK = 0o4000; | ||
| 413 | pub const O_DSYNC = 0o10000; | ||
| 414 | pub const O_SYNC = 0o4010000; | ||
| 415 | pub const O_RSYNC = 0o4010000; | ||
| 416 | pub const O_DIRECTORY = 0o40000; | ||
| 417 | pub const O_NOFOLLOW = 0o100000; | ||
| 418 | pub const O_CLOEXEC = 0o2000000; | ||
| 419 | |||
| 420 | pub const O_ASYNC = 0o20000; | ||
| 421 | pub const O_DIRECT = 0o400000; | ||
| 422 | pub const O_LARGEFILE = 0o200000; | ||
| 423 | pub const O_NOATIME = 0o1000000; | ||
| 424 | pub const O_PATH = 0o10000000; | ||
| 425 | pub const O_TMPFILE = 0o20200000; | ||
| 426 | pub const O_NDELAY = O_NONBLOCK; | ||
| 427 | |||
| 428 | pub const F_DUPFD = 0; | ||
| 429 | pub const F_GETFD = 1; | ||
| 430 | pub const F_SETFD = 2; | ||
| 431 | pub const F_GETFL = 3; | ||
| 432 | pub const F_SETFL = 4; | ||
| 433 | |||
| 434 | pub const F_SETOWN = 8; | ||
| 435 | pub const F_GETOWN = 9; | ||
| 436 | pub const F_SETSIG = 10; | ||
| 437 | pub const F_GETSIG = 11; | ||
| 438 | |||
| 439 | pub const F_GETLK = 5; | ||
| 440 | pub const F_SETLK = 6; | ||
| 441 | pub const F_SETLKW = 7; | ||
| 442 | |||
| 443 | pub const F_RDLCK = 0; | ||
| 444 | pub const F_WRLCK = 1; | ||
| 445 | pub const F_UNLCK = 2; | ||
| 446 | |||
| 447 | pub const LOCK_SH = 1; | ||
| 448 | pub const LOCK_EX = 2; | ||
| 449 | pub const LOCK_UN = 8; | ||
| 450 | pub const LOCK_NB = 4; | ||
| 451 | |||
| 452 | pub const F_SETOWN_EX = 15; | ||
| 453 | pub const F_GETOWN_EX = 16; | ||
| 454 | |||
| 455 | pub const F_GETOWNER_UIDS = 17; | ||
| 456 | |||
| 457 | /// stack-like segment | ||
| 458 | pub const MAP_GROWSDOWN = 0x0100; | ||
| 459 | |||
| 460 | /// ETXTBSY | ||
| 461 | pub const MAP_DENYWRITE = 0x0800; | ||
| 462 | |||
| 463 | /// mark it as an executable | ||
| 464 | pub const MAP_EXECUTABLE = 0x1000; | ||
| 465 | |||
| 466 | /// pages are locked | ||
| 467 | pub const MAP_LOCKED = 0x0080; | ||
| 468 | |||
| 469 | /// don't check for reservations | ||
| 470 | pub const MAP_NORESERVE = 0x0040; | ||
| 471 | |||
| 472 | pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; | ||
| 473 | pub const VDSO_CGT_VER = "LINUX_2.6.15"; | ||
| 474 | |||
| 475 | pub 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 | |||
| 484 | pub 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 | |||
| 494 | pub 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 | |||
| 504 | pub const blksize_t = i64; | ||
| 505 | pub const nlink_t = u64; | ||
| 506 | pub const time_t = i64; | ||
| 507 | pub const mode_t = u32; | ||
| 508 | pub const off_t = i64; | ||
| 509 | pub const ino_t = u64; | ||
| 510 | pub const dev_t = u64; | ||
| 511 | pub 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. | ||
| 519 | pub 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 | |||
| 548 | pub const timespec = extern struct { | ||
| 549 | tv_sec: time_t, | ||
| 550 | tv_nsec: isize, | ||
| 551 | }; | ||
| 552 | |||
| 553 | pub const timeval = extern struct { | ||
| 554 | tv_sec: isize, | ||
| 555 | tv_usec: isize, | ||
| 556 | }; | ||
| 557 | |||
| 558 | pub const timezone = extern struct { | ||
| 559 | tz_minuteswest: i32, | ||
| 560 | tz_dsttime: i32, | ||
| 561 | }; | ||
| 562 | |||
| 563 | pub const greg_t = u64; | ||
| 564 | pub const gregset_t = [48]greg_t; | ||
| 565 | pub 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. | ||
| 571 | pub 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 | }; | ||
| 579 | pub const vrregset_t = vrregset; | ||
| 580 | |||
| 581 | pub 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 | |||
| 594 | pub 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 | |||
| 602 | pub 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 | |||
| 7 | pub const PR_SET_PDEATHSIG = 1; | ||
| 8 | pub const PR_GET_PDEATHSIG = 2; | ||
| 9 | |||
| 10 | pub const PR_GET_DUMPABLE = 3; | ||
| 11 | pub const PR_SET_DUMPABLE = 4; | ||
| 12 | |||
| 13 | pub const PR_GET_UNALIGN = 5; | ||
| 14 | pub const PR_SET_UNALIGN = 6; | ||
| 15 | pub const PR_UNALIGN_NOPRINT = 1; | ||
| 16 | pub const PR_UNALIGN_SIGBUS = 2; | ||
| 17 | |||
| 18 | pub const PR_GET_KEEPCAPS = 7; | ||
| 19 | pub const PR_SET_KEEPCAPS = 8; | ||
| 20 | |||
| 21 | pub const PR_GET_FPEMU = 9; | ||
| 22 | pub const PR_SET_FPEMU = 10; | ||
| 23 | pub const PR_FPEMU_NOPRINT = 1; | ||
| 24 | pub const PR_FPEMU_SIGFPE = 2; | ||
| 25 | |||
| 26 | pub const PR_GET_FPEXC = 11; | ||
| 27 | pub const PR_SET_FPEXC = 12; | ||
| 28 | pub const PR_FP_EXC_SW_ENABLE = 0x80; | ||
| 29 | pub const PR_FP_EXC_DIV = 0x010000; | ||
| 30 | pub const PR_FP_EXC_OVF = 0x020000; | ||
| 31 | pub const PR_FP_EXC_UND = 0x040000; | ||
| 32 | pub const PR_FP_EXC_RES = 0x080000; | ||
| 33 | pub const PR_FP_EXC_INV = 0x100000; | ||
| 34 | pub const PR_FP_EXC_DISABLED = 0; | ||
| 35 | pub const PR_FP_EXC_NONRECOV = 1; | ||
| 36 | pub const PR_FP_EXC_ASYNC = 2; | ||
| 37 | pub const PR_FP_EXC_PRECISE = 3; | ||
| 38 | |||
| 39 | pub const PR_GET_TIMING = 13; | ||
| 40 | pub const PR_SET_TIMING = 14; | ||
| 41 | pub const PR_TIMING_STATISTICAL = 0; | ||
| 42 | pub const PR_TIMING_TIMESTAMP = 1; | ||
| 43 | |||
| 44 | pub const PR_SET_NAME = 15; | ||
| 45 | pub const PR_GET_NAME = 16; | ||
| 46 | |||
| 47 | pub const PR_GET_ENDIAN = 19; | ||
| 48 | pub const PR_SET_ENDIAN = 20; | ||
| 49 | pub const PR_ENDIAN_BIG = 0; | ||
| 50 | pub const PR_ENDIAN_LITTLE = 1; | ||
| 51 | pub const PR_ENDIAN_PPC_LITTLE = 2; | ||
| 52 | |||
| 53 | pub const PR_GET_SECCOMP = 21; | ||
| 54 | pub const PR_SET_SECCOMP = 22; | ||
| 55 | |||
| 56 | pub const PR_CAPBSET_READ = 23; | ||
| 57 | pub const PR_CAPBSET_DROP = 24; | ||
| 58 | |||
| 59 | pub const PR_GET_TSC = 25; | ||
| 60 | pub const PR_SET_TSC = 26; | ||
| 61 | pub const PR_TSC_ENABLE = 1; | ||
| 62 | pub const PR_TSC_SIGSEGV = 2; | ||
| 63 | |||
| 64 | pub const PR_GET_SECUREBITS = 27; | ||
| 65 | pub const PR_SET_SECUREBITS = 28; | ||
| 66 | |||
| 67 | pub const PR_SET_TIMERSLACK = 29; | ||
| 68 | pub const PR_GET_TIMERSLACK = 30; | ||
| 69 | |||
| 70 | pub const PR_TASK_PERF_EVENTS_DISABLE = 31; | ||
| 71 | pub const PR_TASK_PERF_EVENTS_ENABLE = 32; | ||
| 72 | |||
| 73 | pub const PR_MCE_KILL = 33; | ||
| 74 | pub const PR_MCE_KILL_CLEAR = 0; | ||
| 75 | pub const PR_MCE_KILL_SET = 1; | ||
| 76 | |||
| 77 | pub const PR_MCE_KILL_LATE = 0; | ||
| 78 | pub const PR_MCE_KILL_EARLY = 1; | ||
| 79 | pub const PR_MCE_KILL_DEFAULT = 2; | ||
| 80 | |||
| 81 | pub const PR_MCE_KILL_GET = 34; | ||
| 82 | |||
| 83 | pub const PR_SET_MM = 35; | ||
| 84 | pub const PR_SET_MM_START_CODE = 1; | ||
| 85 | pub const PR_SET_MM_END_CODE = 2; | ||
| 86 | pub const PR_SET_MM_START_DATA = 3; | ||
| 87 | pub const PR_SET_MM_END_DATA = 4; | ||
| 88 | pub const PR_SET_MM_START_STACK = 5; | ||
| 89 | pub const PR_SET_MM_START_BRK = 6; | ||
| 90 | pub const PR_SET_MM_BRK = 7; | ||
| 91 | pub const PR_SET_MM_ARG_START = 8; | ||
| 92 | pub const PR_SET_MM_ARG_END = 9; | ||
| 93 | pub const PR_SET_MM_ENV_START = 10; | ||
| 94 | pub const PR_SET_MM_ENV_END = 11; | ||
| 95 | pub const PR_SET_MM_AUXV = 12; | ||
| 96 | pub const PR_SET_MM_EXE_FILE = 13; | ||
| 97 | pub const PR_SET_MM_MAP = 14; | ||
| 98 | pub const PR_SET_MM_MAP_SIZE = 15; | ||
| 99 | |||
| 100 | pub 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 | |||
| 117 | pub const PR_SET_PTRACER = 0x59616d61; | ||
| 118 | pub const PR_SET_PTRACER_ANY = std.math.maxInt(c_ulong); | ||
| 119 | |||
| 120 | pub const PR_SET_CHILD_SUBREAPER = 36; | ||
| 121 | pub const PR_GET_CHILD_SUBREAPER = 37; | ||
| 122 | |||
| 123 | pub const PR_SET_NO_NEW_PRIVS = 38; | ||
| 124 | pub const PR_GET_NO_NEW_PRIVS = 39; | ||
| 125 | |||
| 126 | pub const PR_GET_TID_ADDRESS = 40; | ||
| 127 | |||
| 128 | pub const PR_SET_THP_DISABLE = 41; | ||
| 129 | pub const PR_GET_THP_DISABLE = 42; | ||
| 130 | |||
| 131 | pub const PR_MPX_ENABLE_MANAGEMENT = 43; | ||
| 132 | pub const PR_MPX_DISABLE_MANAGEMENT = 44; | ||
| 133 | |||
| 134 | pub const PR_SET_FP_MODE = 45; | ||
| 135 | pub const PR_GET_FP_MODE = 46; | ||
| 136 | pub const PR_FP_MODE_FR = 1 << 0; | ||
| 137 | pub const PR_FP_MODE_FRE = 1 << 1; | ||
| 138 | |||
| 139 | pub const PR_CAP_AMBIENT = 47; | ||
| 140 | pub const PR_CAP_AMBIENT_IS_SET = 1; | ||
| 141 | pub const PR_CAP_AMBIENT_RAISE = 2; | ||
| 142 | pub const PR_CAP_AMBIENT_LOWER = 3; | ||
| 143 | pub const PR_CAP_AMBIENT_CLEAR_ALL = 4; | ||
| 144 | |||
| 145 | pub const PR_SVE_SET_VL = 50; | ||
| 146 | pub const PR_SVE_SET_VL_ONEXEC = 1 << 18; | ||
| 147 | pub const PR_SVE_GET_VL = 51; | ||
| 148 | pub const PR_SVE_VL_LEN_MASK = 0xffff; | ||
| 149 | pub const PR_SVE_VL_INHERIT = 1 << 17; | ||
| 150 | |||
| 151 | pub const PR_GET_SPECULATION_CTRL = 52; | ||
| 152 | pub const PR_SET_SPECULATION_CTRL = 53; | ||
| 153 | pub const PR_SPEC_STORE_BYPASS = 0; | ||
| 154 | pub const PR_SPEC_NOT_AFFECTED = 0; | ||
| 155 | pub const PR_SPEC_PRCTL = 1 << 0; | ||
| 156 | pub const PR_SPEC_ENABLE = 1 << 1; | ||
| 157 | pub const PR_SPEC_DISABLE = 1 << 2; | ||
| 158 | pub 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 | |||
| 7 | fn issecure_mask(comptime x: comptime_int) comptime_int { | ||
| 8 | return 1 << x; | ||
| 9 | } | ||
| 10 | |||
| 11 | pub const SECUREBITS_DEFAULT = 0x00000000; | ||
| 12 | |||
| 13 | pub const SECURE_NOROOT = 0; | ||
| 14 | pub const SECURE_NOROOT_LOCKED = 1; | ||
| 15 | |||
| 16 | pub const SECBIT_NOROOT = issecure_mask(SECURE_NOROOT); | ||
| 17 | pub const SECBIT_NOROOT_LOCKED = issecure_mask(SECURE_NOROOT_LOCKED); | ||
| 18 | |||
| 19 | pub const SECURE_NO_SETUID_FIXUP = 2; | ||
| 20 | pub const SECURE_NO_SETUID_FIXUP_LOCKED = 3; | ||
| 21 | |||
| 22 | pub const SECBIT_NO_SETUID_FIXUP = issecure_mask(SECURE_NO_SETUID_FIXUP); | ||
| 23 | pub const SECBIT_NO_SETUID_FIXUP_LOCKED = issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED); | ||
| 24 | |||
| 25 | pub const SECURE_KEEP_CAPS = 4; | ||
| 26 | pub const SECURE_KEEP_CAPS_LOCKED = 5; | ||
| 27 | |||
| 28 | pub const SECBIT_KEEP_CAPS = issecure_mask(SECURE_KEEP_CAPS); | ||
| 29 | pub const SECBIT_KEEP_CAPS_LOCKED = issecure_mask(SECURE_KEEP_CAPS_LOCKED); | ||
| 30 | |||
| 31 | pub const SECURE_NO_CAP_AMBIENT_RAISE = 6; | ||
| 32 | pub const SECURE_NO_CAP_AMBIENT_RAISE_LOCKED = 7; | ||
| 33 | |||
| 34 | pub const SECBIT_NO_CAP_AMBIENT_RAISE = issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE); | ||
| 35 | pub const SECBIT_NO_CAP_AMBIENT_RAISE_LOCKED = issecure_mask(SECURE_NO_CAP_AMBIENT_RAISE_LOCKED); | ||
| 36 | |||
| 37 | pub 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); | ||
| 41 | pub 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 | }; |
| 30 | pub usingnamespace @import("bits.zig"); | 31 | pub 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 | } |
| 1260 | 1261 | ||
| 1262 | pub 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 | |||
| 1261 | test "" { | 1266 | test "" { |
| 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 copies | 4 | // 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. |
| 6 | usingnamespace std.os; | 6 | usingnamespace std.os.linux; |
| 7 | const std = @import("../../std.zig"); | 7 | const std = @import("../../std.zig"); |
| 8 | const errno = getErrno; | ||
| 9 | const unexpectedErrno = std.os.unexpectedErrno; | ||
| 8 | const expectEqual = std.testing.expectEqual; | 10 | const expectEqual = std.testing.expectEqual; |
| 11 | const expectError = std.testing.expectError; | ||
| 12 | const expect = std.testing.expect; | ||
| 13 | |||
| 14 | pub const btf = @import("bpf/btf.zig"); | ||
| 15 | pub const kern = @import("bpf/kern.zig"); | ||
| 9 | 16 | ||
| 10 | // instruction classes | 17 | // instruction classes |
| 11 | pub const LD = 0x00; | 18 | pub const LD = 0x00; |
| ... | @@ -62,6 +69,7 @@ pub const MAXINSNS = 4096; | ... | @@ -62,6 +69,7 @@ pub const MAXINSNS = 4096; |
| 62 | // instruction classes | 69 | // instruction classes |
| 63 | /// jmp mode in word width | 70 | /// jmp mode in word width |
| 64 | pub const JMP32 = 0x06; | 71 | pub const JMP32 = 0x06; |
| 72 | |||
| 65 | /// alu mode in double word width | 73 | /// alu mode in double word width |
| 66 | pub const ALU64 = 0x07; | 74 | pub const ALU64 = 0x07; |
| 67 | 75 | ||
| ... | @@ -72,14 +80,17 @@ pub const XADD = 0xc0; | ... | @@ -72,14 +80,17 @@ pub const XADD = 0xc0; |
| 72 | // alu/jmp fields | 80 | // alu/jmp fields |
| 73 | /// mov reg to reg | 81 | /// mov reg to reg |
| 74 | pub const MOV = 0xb0; | 82 | pub const MOV = 0xb0; |
| 83 | |||
| 75 | /// sign extending arithmetic shift right */ | 84 | /// sign extending arithmetic shift right */ |
| 76 | pub const ARSH = 0xc0; | 85 | pub const ARSH = 0xc0; |
| 77 | 86 | ||
| 78 | // change endianness of a register | 87 | // change endianness of a register |
| 79 | /// flags for endianness conversion: | 88 | /// flags for endianness conversion: |
| 80 | pub const END = 0xd0; | 89 | pub const END = 0xd0; |
| 90 | |||
| 81 | /// convert to little-endian */ | 91 | /// convert to little-endian */ |
| 82 | pub const TO_LE = 0x00; | 92 | pub const TO_LE = 0x00; |
| 93 | |||
| 83 | /// convert to big-endian | 94 | /// convert to big-endian |
| 84 | pub const TO_BE = 0x08; | 95 | pub const TO_BE = 0x08; |
| 85 | pub const FROM_LE = TO_LE; | 96 | pub 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 encodings | 99 | // jmp encodings |
| 89 | /// jump != * | 100 | /// jump != * |
| 90 | pub const JNE = 0x50; | 101 | pub const JNE = 0x50; |
| 102 | |||
| 91 | /// LT is unsigned, '<' | 103 | /// LT is unsigned, '<' |
| 92 | pub const JLT = 0xa0; | 104 | pub const JLT = 0xa0; |
| 105 | |||
| 93 | /// LE is unsigned, '<=' * | 106 | /// LE is unsigned, '<=' * |
| 94 | pub const JLE = 0xb0; | 107 | pub const JLE = 0xb0; |
| 108 | |||
| 95 | /// SGT is signed '>', GT in x86 | 109 | /// SGT is signed '>', GT in x86 |
| 96 | pub const JSGT = 0x60; | 110 | pub const JSGT = 0x60; |
| 111 | |||
| 97 | /// SGE is signed '>=', GE in x86 | 112 | /// SGE is signed '>=', GE in x86 |
| 98 | pub const JSGE = 0x70; | 113 | pub const JSGE = 0x70; |
| 114 | |||
| 99 | /// SLT is signed, '<' | 115 | /// SLT is signed, '<' |
| 100 | pub const JSLT = 0xc0; | 116 | pub const JSLT = 0xc0; |
| 117 | |||
| 101 | /// SLE is signed, '<=' | 118 | /// SLE is signed, '<=' |
| 102 | pub const JSLE = 0xd0; | 119 | pub const JSLE = 0xd0; |
| 120 | |||
| 103 | /// function call | 121 | /// function call |
| 104 | pub const CALL = 0x80; | 122 | pub const CALL = 0x80; |
| 123 | |||
| 105 | /// function return | 124 | /// function return |
| 106 | pub const EXIT = 0x90; | 125 | pub const EXIT = 0x90; |
| 107 | 126 | ||
| 108 | /// Flag for prog_attach command. If a sub-cgroup installs some bpf program, the | 127 | /// 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. |
| 110 | pub const F_ALLOW_OVERRIDE = 0x1; | 129 | pub 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. |
| 113 | pub const F_ALLOW_MULTI = 0x2; | 133 | pub const F_ALLOW_MULTI = 0x2; |
| 134 | |||
| 114 | /// Flag for prog_attach command. | 135 | /// Flag for prog_attach command. |
| 115 | pub const F_REPLACE = 0x4; | 136 | pub const F_REPLACE = 0x4; |
| 116 | 137 | ||
| ... | @@ -164,47 +185,61 @@ pub const PSEUDO_CALL = 1; | ... | @@ -164,47 +185,61 @@ pub const PSEUDO_CALL = 1; |
| 164 | 185 | ||
| 165 | /// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing | 186 | /// flag for BPF_MAP_UPDATE_ELEM command. create new element or update existing |
| 166 | pub const ANY = 0; | 187 | pub const ANY = 0; |
| 188 | |||
| 167 | /// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist | 189 | /// flag for BPF_MAP_UPDATE_ELEM command. create new element if it didn't exist |
| 168 | pub const NOEXIST = 1; | 190 | pub const NOEXIST = 1; |
| 191 | |||
| 169 | /// flag for BPF_MAP_UPDATE_ELEM command. update existing element | 192 | /// flag for BPF_MAP_UPDATE_ELEM command. update existing element |
| 170 | pub const EXIST = 2; | 193 | pub const EXIST = 2; |
| 194 | |||
| 171 | /// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update | 195 | /// flag for BPF_MAP_UPDATE_ELEM command. spin_lock-ed map_lookup/map_update |
| 172 | pub const F_LOCK = 4; | 196 | pub const F_LOCK = 4; |
| 173 | 197 | ||
| 174 | /// flag for BPF_MAP_CREATE command */ | 198 | /// flag for BPF_MAP_CREATE command */ |
| 175 | pub const BPF_F_NO_PREALLOC = 0x1; | 199 | pub const BPF_F_NO_PREALLOC = 0x1; |
| 200 | |||
| 176 | /// flag for BPF_MAP_CREATE command. Instead of having one common LRU list in | 201 | /// 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 can | 202 | /// 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) cannot | 203 | /// 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. |
| 180 | pub const BPF_F_NO_COMMON_LRU = 0x2; | 205 | pub const BPF_F_NO_COMMON_LRU = 0x2; |
| 206 | |||
| 181 | /// flag for BPF_MAP_CREATE command. Specify numa node during map creation | 207 | /// flag for BPF_MAP_CREATE command. Specify numa node during map creation |
| 182 | pub const BPF_F_NUMA_NODE = 0x4; | 208 | pub const BPF_F_NUMA_NODE = 0x4; |
| 209 | |||
| 183 | /// flag for BPF_MAP_CREATE command. Flags for BPF object read access from | 210 | /// flag for BPF_MAP_CREATE command. Flags for BPF object read access from |
| 184 | /// syscall side | 211 | /// syscall side |
| 185 | pub const BPF_F_RDONLY = 0x8; | 212 | pub const BPF_F_RDONLY = 0x8; |
| 213 | |||
| 186 | /// flag for BPF_MAP_CREATE command. Flags for BPF object write access from | 214 | /// flag for BPF_MAP_CREATE command. Flags for BPF object write access from |
| 187 | /// syscall side | 215 | /// syscall side |
| 188 | pub const BPF_F_WRONLY = 0x10; | 216 | pub const BPF_F_WRONLY = 0x10; |
| 217 | |||
| 189 | /// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset | 218 | /// flag for BPF_MAP_CREATE command. Flag for stack_map, store build_id+offset |
| 190 | /// instead of pointer | 219 | /// instead of pointer |
| 191 | pub const BPF_F_STACK_BUILD_ID = 0x20; | 220 | pub const BPF_F_STACK_BUILD_ID = 0x20; |
| 221 | |||
| 192 | /// flag for BPF_MAP_CREATE command. Zero-initialize hash function seed. This | 222 | /// 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. |
| 194 | pub const BPF_F_ZERO_SEED = 0x40; | 224 | pub const BPF_F_ZERO_SEED = 0x40; |
| 225 | |||
| 195 | /// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program | 226 | /// flag for BPF_MAP_CREATE command Flags for accessing BPF object from program |
| 196 | /// side. | 227 | /// side. |
| 197 | pub const BPF_F_RDONLY_PROG = 0x80; | 228 | pub const BPF_F_RDONLY_PROG = 0x80; |
| 229 | |||
| 198 | /// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program | 230 | /// flag for BPF_MAP_CREATE command. Flags for accessing BPF object from program |
| 199 | /// side. | 231 | /// side. |
| 200 | pub const BPF_F_WRONLY_PROG = 0x100; | 232 | pub const BPF_F_WRONLY_PROG = 0x100; |
| 233 | |||
| 201 | /// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted | 234 | /// flag for BPF_MAP_CREATE command. Clone map from listener for newly accepted |
| 202 | /// socket | 235 | /// socket |
| 203 | pub const BPF_F_CLONE = 0x200; | 236 | pub const BPF_F_CLONE = 0x200; |
| 237 | |||
| 204 | /// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map | 238 | /// flag for BPF_MAP_CREATE command. Enable memory-mapping BPF map |
| 205 | pub const BPF_F_MMAPABLE = 0x400; | 239 | pub const BPF_F_MMAPABLE = 0x400; |
| 206 | 240 | ||
| 207 | /// These values correspond to "syscalls" within the BPF program's environment | 241 | /// These values correspond to "syscalls" within the BPF program's environment, |
| 242 | /// each one is documented in std.os.linux.BPF.kern | ||
| 208 | pub const Helper = enum(i32) { | 243 | pub 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 | }; |
| 330 | 388 | ||
| 389 | // TODO: determine that this is the expected bit layout for both little and big | ||
| 390 | // endian systems | ||
| 331 | /// a single BPF instruction | 391 | /// a single BPF instruction |
| 332 | pub const Insn = packed struct { | 392 | pub 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 | /// frame | 400 | /// 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 | }; |
| 357 | 428 | ||
| 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 | }; |
| 372 | 450 | ||
| 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 | } |
| 421 | 499 | ||
| 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 | } |
| 425 | 547 | ||
| 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 | } |
| 429 | 555 | ||
| 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 | } |
| 439 | 595 | ||
| 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 | } |
| 449 | 605 | ||
| 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 | } |
| 460 | 615 | ||
| 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 | } |
| 480 | 653 | ||
| 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 | } |
| 488 | 669 | ||
| 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 | }; |
| 510 | 738 | ||
| 511 | fn expect_insn(insn: Insn, val: u64) void { | ||
| 512 | expectEqual(@bitCast(u64, insn), val); | ||
| 513 | } | ||
| 514 | |||
| 515 | test "insn bitsize" { | 739 | test "insn bitsize" { |
| 516 | expectEqual(@bitSizeOf(Insn), 64); | 740 | expectEqual(@bitSizeOf(Insn), 64); |
| 517 | } | 741 | } |
| 518 | 742 | ||
| 519 | // mov instructions | 743 | fn expect_opcode(code: u8, insn: Insn) void { |
| 520 | test "mov imm" { | 744 | expectEqual(code, insn.code); |
| 521 | expect_insn(Insn.mov(.r1, 1), 0x00000001000001b7); | ||
| 522 | } | ||
| 523 | |||
| 524 | test "mov reg" { | ||
| 525 | expect_insn(Insn.mov(.r6, .r1), 0x00000000000016bf); | ||
| 526 | } | ||
| 527 | |||
| 528 | // alu instructions | ||
| 529 | test "add imm" { | ||
| 530 | expect_insn(Insn.add(.r2, -4), 0xfffffffc00000207); | ||
| 531 | } | 745 | } |
| 532 | 746 | ||
| 533 | // ld instructions | 747 | // The opcodes were grabbed from https://github.com/iovisor/bpf-docs/blob/master/eBPF.md |
| 534 | test "ld_abs" { | 748 | test "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) |
| 537 | 751 | ||
| 538 | test "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)); |
| 542 | 756 | expect_opcode(0x1f, Insn.sub(.r1, .r2)); | |
| 543 | // st instructions | 757 | expect_opcode(0x27, Insn.mul(.r1, 0)); |
| 544 | test "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)); |
| 547 | 761 | expect_opcode(0x47, Insn.alu_or(.r1, 0)); | |
| 548 | test "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)); |
| 551 | 765 | expect_opcode(0x67, Insn.lsh(.r1, 0)); | |
| 552 | // jmp instructions | 766 | expect_opcode(0x6f, Insn.lsh(.r1, .r2)); |
| 553 | test "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)); |
| 556 | 770 | expect_opcode(0x97, Insn.mod(.r1, 0)); | |
| 557 | // other instructions | 771 | expect_opcode(0x9f, Insn.mod(.r1, .r2)); |
| 558 | test "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)); |
| 561 | 775 | expect_opcode(0xbf, Insn.mov(.r1, .r2)); | |
| 562 | test "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 | } |
| 565 | 856 | ||
| 566 | pub const Cmd = extern enum(usize) { | 857 | pub 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 | }; |
| 636 | 1046 | ||
| 637 | pub const ProgType = extern enum(u32) { | 1047 | pub 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 | }; |
| 670 | 1141 | ||
| 671 | pub const AttachType = extern enum(u32) { | 1142 | pub const AttachType = extern enum(u32) { |
| ... | @@ -715,27 +1186,38 @@ const obj_name_len = 16; | ... | @@ -715,27 +1186,38 @@ const obj_name_len = 16; |
| 715 | pub const MapCreateAttr = extern struct { | 1186 | pub const MapCreateAttr = extern struct { |
| 716 | /// one of MapType | 1187 | /// one of MapType |
| 717 | map_type: u32, | 1188 | map_type: u32, |
| 1189 | |||
| 718 | /// size of key in bytes | 1190 | /// size of key in bytes |
| 719 | key_size: u32, | 1191 | key_size: u32, |
| 1192 | |||
| 720 | /// size of value in bytes | 1193 | /// size of value in bytes |
| 721 | value_size: u32, | 1194 | value_size: u32, |
| 1195 | |||
| 722 | /// max number of entries in a map | 1196 | /// max number of entries in a map |
| 723 | max_entries: u32, | 1197 | max_entries: u32, |
| 1198 | |||
| 724 | /// .map_create related flags | 1199 | /// .map_create related flags |
| 725 | map_flags: u32, | 1200 | map_flags: u32, |
| 1201 | |||
| 726 | /// fd pointing to the inner map | 1202 | /// 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 on | 1209 | /// ifindex of netdev to create on |
| 732 | map_ifindex: u32, | 1210 | map_ifindex: u32, |
| 1211 | |||
| 733 | /// fd pointing to a BTF type data | 1212 | /// fd pointing to a BTF type data |
| 734 | btf_fd: fd_t, | 1213 | btf_fd: fd_t, |
| 1214 | |||
| 735 | /// BTF type_id of the key | 1215 | /// 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 value | 1218 | /// 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 value | 1221 | /// 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 { |
| 755 | pub const MapBatchAttr = extern struct { | 1237 | pub const MapBatchAttr = extern struct { |
| 756 | /// start batch, NULL to start from beginning | 1238 | /// start batch, NULL to start from beginning |
| 757 | in_batch: u64, | 1239 | in_batch: u64, |
| 1240 | |||
| 758 | /// output: next start batch | 1241 | /// 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 elements | 1247 | /// input: # of key/value elements |
| 764 | /// output: # of filled elements | 1248 | /// 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 verifier | 1263 | /// verbosity level of verifier |
| 779 | log_level: u32, | 1264 | log_level: u32, |
| 1265 | |||
| 780 | /// size of user buffer | 1266 | /// size of user buffer |
| 781 | log_size: u32, | 1267 | log_size: u32, |
| 1268 | |||
| 782 | /// user supplied buffer | 1269 | /// user supplied buffer |
| 783 | log_buf: u64, | 1270 | log_buf: u64, |
| 1271 | |||
| 784 | /// not used | 1272 | /// 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 attach | 1276 | |
| 789 | /// type must be known at load time to verify attach type specific parts | 1277 | /// 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 data | 1285 | /// fd pointing to BTF type data |
| 794 | prog_btf_fd: fd_t, | 1286 | prog_btf_fd: fd_t, |
| 1287 | |||
| 795 | /// userspace bpf_func_info size | 1288 | /// 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 records | 1292 | /// number of bpf_func_info records |
| 799 | func_info_cnt: u32, | 1293 | func_info_cnt: u32, |
| 1294 | |||
| 800 | /// userspace bpf_line_info size | 1295 | /// 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 records | 1299 | /// 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 to | 1302 | /// in-kernel BTF type id to attach to |
| 806 | attact_btf_id: u32, | 1303 | attact_btf_id: u32, |
| 1304 | |||
| 807 | /// 0 to attach to vmlinux | 1305 | /// 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 { |
| 819 | pub const ProgAttachAttr = extern struct { | 1317 | pub const ProgAttachAttr = extern struct { |
| 820 | /// container object to attach to | 1318 | /// container object to attach to |
| 821 | target_fd: fd_t, | 1319 | target_fd: fd_t, |
| 1320 | |||
| 822 | /// eBPF program to attach | 1321 | /// 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 flags | 1327 | // TODO: BPF_F_REPLACE flags |
| 827 | /// previously attached eBPF program to replace if .replace is used | 1328 | /// previously attached eBPF program to replace if .replace is used |
| 828 | replace_bpf_fd: fd_t, | 1329 | replace_bpf_fd: fd_t, |
| 829 | }; | 1330 | }; |
| 830 | 1331 | ||
| 831 | /// struct used by Cmd.prog_test_run command | 1332 | /// struct used by Cmd.prog_test_run command |
| 832 | pub const TestAttr = extern struct { | 1333 | pub 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_in | 1337 | /// 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_in | 1347 | /// 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 | }; |
| 896 | 1401 | ||
| 1402 | /// struct used by Cmd.task_fd_query | ||
| 897 | pub const TaskFdQueryAttr = extern struct { | 1403 | pub const TaskFdQueryAttr = extern struct { |
| 898 | /// input: pid | 1404 | /// input: pid |
| 899 | pid: pid_t, | 1405 | pid: pid_t, |
| 1406 | |||
| 900 | /// input: fd | 1407 | /// input: fd |
| 901 | fd: fd_t, | 1408 | fd: fd_t, |
| 1409 | |||
| 902 | /// input: flags | 1410 | /// input: flags |
| 903 | flags: u32, | 1411 | flags: u32, |
| 1412 | |||
| 904 | /// input/output: buf len | 1413 | /// input/output: buf len |
| 905 | buf_len: u32, | 1414 | buf_len: u32, |
| 1415 | |||
| 906 | /// input/output: | 1416 | /// input/output: |
| 907 | /// tp_name for tracepoint | 1417 | /// tp_name for tracepoint |
| 908 | /// symbol for kprobe | 1418 | /// symbol for kprobe |
| 909 | /// filename for uprobe | 1419 | /// filename for uprobe |
| 910 | buf: u64, | 1420 | buf: u64, |
| 1421 | |||
| 911 | /// output: prod_id | 1422 | /// output: prod_id |
| 912 | prog_id: u32, | 1423 | prog_id: u32, |
| 1424 | |||
| 913 | /// output: BPF_FD_TYPE | 1425 | /// output: BPF_FD_TYPE |
| 914 | fd_type: u32, | 1426 | fd_type: u32, |
| 1427 | |||
| 915 | /// output: probe_offset | 1428 | /// output: probe_offset |
| 916 | probe_offset: u64, | 1429 | probe_offset: u64, |
| 1430 | |||
| 917 | /// output: probe_addr | 1431 | /// 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 { |
| 922 | pub const LinkCreateAttr = extern struct { | 1436 | pub const LinkCreateAttr = extern struct { |
| 923 | /// eBPF program to attach | 1437 | /// eBPF program to attach |
| 924 | prog_fd: fd_t, | 1438 | prog_fd: fd_t, |
| 1439 | |||
| 925 | /// object to attach to | 1440 | /// 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 flags | 1444 | /// 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 command | 1448 | /// struct used by Cmd.link_update command |
| 933 | pub const LinkUpdateAttr = extern struct { | 1449 | pub const LinkUpdateAttr = extern struct { |
| 934 | link_fd: fd_t, | 1450 | link_fd: fd_t, |
| 1451 | |||
| 935 | /// new program to update link with | 1452 | /// new program to update link with |
| 936 | new_prog_fd: fd_t, | 1453 | new_prog_fd: fd_t, |
| 1454 | |||
| 937 | /// extra flags | 1455 | /// extra flags |
| 938 | flags: u32, | 1456 | flags: u32, |
| 1457 | |||
| 939 | /// expected link's program fd, it is specified only if BPF_F_REPLACE is | 1458 | /// expected link's program fd, it is specified only if BPF_F_REPLACE is |
| 940 | /// set in flags | 1459 | /// 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 | }; |
| 954 | 1473 | ||
| 1474 | /// Mega struct that is passed to the bpf() syscall | ||
| 955 | pub const Attr = extern union { | 1475 | pub 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 | |||
| 1495 | pub const Log = struct { | ||
| 1496 | level: u32, | ||
| 1497 | buf: []u8, | ||
| 1498 | }; | ||
| 1499 | |||
| 1500 | pub 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 | |||
| 1520 | test "map_create" { | ||
| 1521 | const map = try map_create(.hash, 4, 4, 32); | ||
| 1522 | defer std.os.close(map); | ||
| 1523 | } | ||
| 1524 | |||
| 1525 | pub 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 | |||
| 1546 | pub 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 | |||
| 1569 | pub 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 | |||
| 1589 | test "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 | |||
| 1617 | pub 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 | |||
| 1651 | test "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. | ||
| 6 | const magic = 0xeb9f; | ||
| 7 | const version = 1; | ||
| 8 | |||
| 9 | pub const ext = @import("ext.zig"); | ||
| 10 | |||
| 11 | /// All offsets are in bytes relative to the end of this header | ||
| 12 | pub 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 | ||
| 32 | pub const max_type = 0xfffff; | ||
| 33 | |||
| 34 | /// Max offset into string section | ||
| 35 | pub const max_name_offset = 0xffffff; | ||
| 36 | |||
| 37 | /// Max number of struct/union/enum member of func args | ||
| 38 | pub const max_vlen = 0xffff; | ||
| 39 | |||
| 40 | pub 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 | ||
| 63 | pub 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 | ||
| 83 | pub 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 | |||
| 94 | test "IntInfo is 32 bits" { | ||
| 95 | std.testing.expectEqual(@bitSizeOf(IntInfo), 32); | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Enum kind is followed by this struct | ||
| 99 | pub const Enum = packed struct { | ||
| 100 | name_off: u32, | ||
| 101 | val: i32, | ||
| 102 | }; | ||
| 103 | |||
| 104 | /// Array kind is followd by this struct | ||
| 105 | pub 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 | ||
| 113 | pub 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 | ||
| 127 | pub const Param = packed struct { | ||
| 128 | name_off: u32, | ||
| 129 | typ: u32, | ||
| 130 | }; | ||
| 131 | |||
| 132 | pub const VarLinkage = enum { | ||
| 133 | static, | ||
| 134 | global_allocated, | ||
| 135 | global_extern, | ||
| 136 | }; | ||
| 137 | |||
| 138 | pub 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 | ||
| 146 | pub 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. | ||
| 152 | pub 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. | ||
| 6 | pub 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 | |||
| 19 | pub 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. | ||
| 6 | const 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' | ||
| 14 | pub const map_lookup_elem = @intToPtr(fn (map: *const kern.MapDef, key: ?*const c_void) ?*c_void, 1); | ||
| 15 | pub const map_update_elem = @intToPtr(fn (map: *const kern.MapDef, key: ?*const c_void, value: ?*const c_void, flags: u64) c_long, 2); | ||
| 16 | pub const map_delete_elem = @intToPtr(fn (map: *const kern.MapDef, key: ?*const c_void) c_long, 3); | ||
| 17 | pub const probe_read = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 4); | ||
| 18 | pub const ktime_get_ns = @intToPtr(fn () u64, 5); | ||
| 19 | pub const trace_printk = @intToPtr(fn (fmt: [*:0]const u8, fmt_size: u32, arg1: u64, arg2: u64, arg3: u64) c_long, 6); | ||
| 20 | pub const get_prandom_u32 = @intToPtr(fn () u32, 7); | ||
| 21 | pub const get_smp_processor_id = @intToPtr(fn () u32, 8); | ||
| 22 | pub const skb_store_bytes = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, from: ?*const c_void, len: u32, flags: u64) c_long, 9); | ||
| 23 | pub const l3_csum_replace = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, from: u64, to: u64, size: u64) c_long, 10); | ||
| 24 | pub const l4_csum_replace = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, from: u64, to: u64, flags: u64) c_long, 11); | ||
| 25 | pub const tail_call = @intToPtr(fn (ctx: ?*c_void, prog_array_map: *const kern.MapDef, index: u32) c_long, 12); | ||
| 26 | pub const clone_redirect = @intToPtr(fn (skb: *kern.SkBuff, ifindex: u32, flags: u64) c_long, 13); | ||
| 27 | pub const get_current_pid_tgid = @intToPtr(fn () u64, 14); | ||
| 28 | pub const get_current_uid_gid = @intToPtr(fn () u64, 15); | ||
| 29 | pub const get_current_comm = @intToPtr(fn (buf: ?*c_void, size_of_buf: u32) c_long, 16); | ||
| 30 | pub const get_cgroup_classid = @intToPtr(fn (skb: *kern.SkBuff) u32, 17); | ||
| 31 | // Note vlan_proto is big endian | ||
| 32 | pub const skb_vlan_push = @intToPtr(fn (skb: *kern.SkBuff, vlan_proto: u16, vlan_tci: u16) c_long, 18); | ||
| 33 | pub const skb_vlan_pop = @intToPtr(fn (skb: *kern.SkBuff) c_long, 19); | ||
| 34 | pub const skb_get_tunnel_key = @intToPtr(fn (skb: *kern.SkBuff, key: *kern.TunnelKey, size: u32, flags: u64) c_long, 20); | ||
| 35 | pub const skb_set_tunnel_key = @intToPtr(fn (skb: *kern.SkBuff, key: *kern.TunnelKey, size: u32, flags: u64) c_long, 21); | ||
| 36 | pub const perf_event_read = @intToPtr(fn (map: *const kern.MapDef, flags: u64) u64, 22); | ||
| 37 | pub const redirect = @intToPtr(fn (ifindex: u32, flags: u64) c_long, 23); | ||
| 38 | pub const get_route_realm = @intToPtr(fn (skb: *kern.SkBuff) u32, 24); | ||
| 39 | pub const perf_event_output = @intToPtr(fn (ctx: ?*c_void, map: *const kern.MapDef, flags: u64, data: ?*c_void, size: u64) c_long, 25); | ||
| 40 | pub const skb_load_bytes = @intToPtr(fn (skb: ?*c_void, offset: u32, to: ?*c_void, len: u32) c_long, 26); | ||
| 41 | pub const get_stackid = @intToPtr(fn (ctx: ?*c_void, map: *const kern.MapDef, flags: u64) c_long, 27); | ||
| 42 | // from and to point to __be32 | ||
| 43 | pub const csum_diff = @intToPtr(fn (from: *u32, from_size: u32, to: *u32, to_size: u32, seed: u32) i64, 28); | ||
| 44 | pub const skb_get_tunnel_opt = @intToPtr(fn (skb: *kern.SkBuff, opt: ?*c_void, size: u32) c_long, 29); | ||
| 45 | pub const skb_set_tunnel_opt = @intToPtr(fn (skb: *kern.SkBuff, opt: ?*c_void, size: u32) c_long, 30); | ||
| 46 | // proto is __be16 | ||
| 47 | pub const skb_change_proto = @intToPtr(fn (skb: *kern.SkBuff, proto: u16, flags: u64) c_long, 31); | ||
| 48 | pub const skb_change_type = @intToPtr(fn (skb: *kern.SkBuff, skb_type: u32) c_long, 32); | ||
| 49 | pub const skb_under_cgroup = @intToPtr(fn (skb: *kern.SkBuff, map: ?*const c_void, index: u32) c_long, 33); | ||
| 50 | pub const get_hash_recalc = @intToPtr(fn (skb: *kern.SkBuff) u32, 34); | ||
| 51 | pub const get_current_task = @intToPtr(fn () u64, 35); | ||
| 52 | pub const probe_write_user = @intToPtr(fn (dst: ?*c_void, src: ?*const c_void, len: u32) c_long, 36); | ||
| 53 | pub const current_task_under_cgroup = @intToPtr(fn (map: *const kern.MapDef, index: u32) c_long, 37); | ||
| 54 | pub const skb_change_tail = @intToPtr(fn (skb: *kern.SkBuff, len: u32, flags: u64) c_long, 38); | ||
| 55 | pub const skb_pull_data = @intToPtr(fn (skb: *kern.SkBuff, len: u32) c_long, 39); | ||
| 56 | pub const csum_update = @intToPtr(fn (skb: *kern.SkBuff, csum: u32) i64, 40); | ||
| 57 | pub const set_hash_invalid = @intToPtr(fn (skb: *kern.SkBuff) void, 41); | ||
| 58 | pub const get_numa_node_id = @intToPtr(fn () c_long, 42); | ||
| 59 | pub const skb_change_head = @intToPtr(fn (skb: *kern.SkBuff, len: u32, flags: u64) c_long, 43); | ||
| 60 | pub const xdp_adjust_head = @intToPtr(fn (xdp_md: *kern.XdpMd, delta: c_int) c_long, 44); | ||
| 61 | pub const probe_read_str = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 45); | ||
| 62 | pub const get_socket_cookie = @intToPtr(fn (ctx: ?*c_void) u64, 46); | ||
| 63 | pub const get_socket_uid = @intToPtr(fn (skb: *kern.SkBuff) u32, 47); | ||
| 64 | pub const set_hash = @intToPtr(fn (skb: *kern.SkBuff, hash: u32) c_long, 48); | ||
| 65 | pub const setsockopt = @intToPtr(fn (bpf_socket: *kern.SockOps, level: c_int, optname: c_int, optval: ?*c_void, optlen: c_int) c_long, 49); | ||
| 66 | pub const skb_adjust_room = @intToPtr(fn (skb: *kern.SkBuff, len_diff: i32, mode: u32, flags: u64) c_long, 50); | ||
| 67 | pub const redirect_map = @intToPtr(fn (map: *const kern.MapDef, key: u32, flags: u64) c_long, 51); | ||
| 68 | pub const sk_redirect_map = @intToPtr(fn (skb: *kern.SkBuff, map: *const kern.MapDef, key: u32, flags: u64) c_long, 52); | ||
| 69 | pub const sock_map_update = @intToPtr(fn (skops: *kern.SockOps, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 53); | ||
| 70 | pub const xdp_adjust_meta = @intToPtr(fn (xdp_md: *kern.XdpMd, delta: c_int) c_long, 54); | ||
| 71 | pub const perf_event_read_value = @intToPtr(fn (map: *const kern.MapDef, flags: u64, buf: *kern.PerfEventValue, buf_size: u32) c_long, 55); | ||
| 72 | pub const perf_prog_read_value = @intToPtr(fn (ctx: *kern.PerfEventData, buf: *kern.PerfEventValue, buf_size: u32) c_long, 56); | ||
| 73 | pub const getsockopt = @intToPtr(fn (bpf_socket: ?*c_void, level: c_int, optname: c_int, optval: ?*c_void, optlen: c_int) c_long, 57); | ||
| 74 | pub const override_return = @intToPtr(fn (regs: *PtRegs, rc: u64) c_long, 58); | ||
| 75 | pub const sock_ops_cb_flags_set = @intToPtr(fn (bpf_sock: *kern.SockOps, argval: c_int) c_long, 59); | ||
| 76 | pub const msg_redirect_map = @intToPtr(fn (msg: *kern.SkMsgMd, map: *const kern.MapDef, key: u32, flags: u64) c_long, 60); | ||
| 77 | pub const msg_apply_bytes = @intToPtr(fn (msg: *kern.SkMsgMd, bytes: u32) c_long, 61); | ||
| 78 | pub const msg_cork_bytes = @intToPtr(fn (msg: *kern.SkMsgMd, bytes: u32) c_long, 62); | ||
| 79 | pub const msg_pull_data = @intToPtr(fn (msg: *kern.SkMsgMd, start: u32, end: u32, flags: u64) c_long, 63); | ||
| 80 | pub const bind = @intToPtr(fn (ctx: *kern.BpfSockAddr, addr: *kern.SockAddr, addr_len: c_int) c_long, 64); | ||
| 81 | pub const xdp_adjust_tail = @intToPtr(fn (xdp_md: *kern.XdpMd, delta: c_int) c_long, 65); | ||
| 82 | pub const skb_get_xfrm_state = @intToPtr(fn (skb: *kern.SkBuff, index: u32, xfrm_state: *kern.XfrmState, size: u32, flags: u64) c_long, 66); | ||
| 83 | pub const get_stack = @intToPtr(fn (ctx: ?*c_void, buf: ?*c_void, size: u32, flags: u64) c_long, 67); | ||
| 84 | pub const skb_load_bytes_relative = @intToPtr(fn (skb: ?*const c_void, offset: u32, to: ?*c_void, len: u32, start_header: u32) c_long, 68); | ||
| 85 | pub const fib_lookup = @intToPtr(fn (ctx: ?*c_void, params: *kern.FibLookup, plen: c_int, flags: u32) c_long, 69); | ||
| 86 | pub const sock_hash_update = @intToPtr(fn (skops: *kern.SockOps, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 70); | ||
| 87 | pub const msg_redirect_hash = @intToPtr(fn (msg: *kern.SkMsgMd, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 71); | ||
| 88 | pub const sk_redirect_hash = @intToPtr(fn (skb: *kern.SkBuff, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 72); | ||
| 89 | pub const lwt_push_encap = @intToPtr(fn (skb: *kern.SkBuff, typ: u32, hdr: ?*c_void, len: u32) c_long, 73); | ||
| 90 | pub const lwt_seg6_store_bytes = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, from: ?*const c_void, len: u32) c_long, 74); | ||
| 91 | pub const lwt_seg6_adjust_srh = @intToPtr(fn (skb: *kern.SkBuff, offset: u32, delta: i32) c_long, 75); | ||
| 92 | pub const lwt_seg6_action = @intToPtr(fn (skb: *kern.SkBuff, action: u32, param: ?*c_void, param_len: u32) c_long, 76); | ||
| 93 | pub const rc_repeat = @intToPtr(fn (ctx: ?*c_void) c_long, 77); | ||
| 94 | pub const rc_keydown = @intToPtr(fn (ctx: ?*c_void, protocol: u32, scancode: u64, toggle: u32) c_long, 78); | ||
| 95 | pub const skb_cgroup_id = @intToPtr(fn (skb: *kern.SkBuff) u64, 79); | ||
| 96 | pub const get_current_cgroup_id = @intToPtr(fn () u64, 80); | ||
| 97 | pub const get_local_storage = @intToPtr(fn (map: ?*c_void, flags: u64) ?*c_void, 81); | ||
| 98 | pub const sk_select_reuseport = @intToPtr(fn (reuse: *kern.SkReusePortMd, map: *const kern.MapDef, key: ?*c_void, flags: u64) c_long, 82); | ||
| 99 | pub const skb_ancestor_cgroup_id = @intToPtr(fn (skb: *kern.SkBuff, ancestor_level: c_int) u64, 83); | ||
| 100 | pub const sk_lookup_tcp = @intToPtr(fn (ctx: ?*c_void, tuple: *kern.SockTuple, tuple_size: u32, netns: u64, flags: u64) ?*kern.Sock, 84); | ||
| 101 | pub const sk_lookup_udp = @intToPtr(fn (ctx: ?*c_void, tuple: *kern.SockTuple, tuple_size: u32, netns: u64, flags: u64) ?*kern.Sock, 85); | ||
| 102 | pub const sk_release = @intToPtr(fn (sock: *kern.Sock) c_long, 86); | ||
| 103 | pub const map_push_elem = @intToPtr(fn (map: *const kern.MapDef, value: ?*const c_void, flags: u64) c_long, 87); | ||
| 104 | pub const map_pop_elem = @intToPtr(fn (map: *const kern.MapDef, value: ?*c_void) c_long, 88); | ||
| 105 | pub const map_peek_elem = @intToPtr(fn (map: *const kern.MapDef, value: ?*c_void) c_long, 89); | ||
| 106 | pub const msg_push_data = @intToPtr(fn (msg: *kern.SkMsgMd, start: u32, len: u32, flags: u64) c_long, 90); | ||
| 107 | pub const msg_pop_data = @intToPtr(fn (msg: *kern.SkMsgMd, start: u32, len: u32, flags: u64) c_long, 91); | ||
| 108 | pub const rc_pointer_rel = @intToPtr(fn (ctx: ?*c_void, rel_x: i32, rel_y: i32) c_long, 92); | ||
| 109 | pub const spin_lock = @intToPtr(fn (lock: *kern.SpinLock) c_long, 93); | ||
| 110 | pub const spin_unlock = @intToPtr(fn (lock: *kern.SpinLock) c_long, 94); | ||
| 111 | pub const sk_fullsock = @intToPtr(fn (sk: *kern.Sock) ?*SkFullSock, 95); | ||
| 112 | pub const tcp_sock = @intToPtr(fn (sk: *kern.Sock) ?*kern.TcpSock, 96); | ||
| 113 | pub const skb_ecn_set_ce = @intToPtr(fn (skb: *kern.SkBuff) c_long, 97); | ||
| 114 | pub const get_listener_sock = @intToPtr(fn (sk: *kern.Sock) ?*kern.Sock, 98); | ||
| 115 | pub const skc_lookup_tcp = @intToPtr(fn (ctx: ?*c_void, tuple: *kern.SockTuple, tuple_size: u32, netns: u64, flags: u64) ?*kern.Sock, 99); | ||
| 116 | pub const tcp_check_syncookie = @intToPtr(fn (sk: *kern.Sock, iph: ?*c_void, iph_len: u32, th: *TcpHdr, th_len: u32) c_long, 100); | ||
| 117 | pub const sysctl_get_name = @intToPtr(fn (ctx: *kern.SysCtl, buf: ?*u8, buf_len: c_ulong, flags: u64) c_long, 101); | ||
| 118 | pub const sysctl_get_current_value = @intToPtr(fn (ctx: *kern.SysCtl, buf: ?*u8, buf_len: c_ulong) c_long, 102); | ||
| 119 | pub const sysctl_get_new_value = @intToPtr(fn (ctx: *kern.SysCtl, buf: ?*u8, buf_len: c_ulong) c_long, 103); | ||
| 120 | pub const sysctl_set_new_value = @intToPtr(fn (ctx: *kern.SysCtl, buf: ?*const u8, buf_len: c_ulong) c_long, 104); | ||
| 121 | pub const strtol = @intToPtr(fn (buf: *const u8, buf_len: c_ulong, flags: u64, res: *c_long) c_long, 105); | ||
| 122 | pub const strtoul = @intToPtr(fn (buf: *const u8, buf_len: c_ulong, flags: u64, res: *c_ulong) c_long, 106); | ||
| 123 | pub const sk_storage_get = @intToPtr(fn (map: *const kern.MapDef, sk: *kern.Sock, value: ?*c_void, flags: u64) ?*c_void, 107); | ||
| 124 | pub const sk_storage_delete = @intToPtr(fn (map: *const kern.MapDef, sk: *kern.Sock) c_long, 108); | ||
| 125 | pub const send_signal = @intToPtr(fn (sig: u32) c_long, 109); | ||
| 126 | pub const tcp_gen_syncookie = @intToPtr(fn (sk: *kern.Sock, iph: ?*c_void, iph_len: u32, th: *TcpHdr, th_len: u32) i64, 110); | ||
| 127 | pub const skb_output = @intToPtr(fn (ctx: ?*c_void, map: *const kern.MapDef, flags: u64, data: ?*c_void, size: u64) c_long, 111); | ||
| 128 | pub const probe_read_user = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 112); | ||
| 129 | pub const probe_read_kernel = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 113); | ||
| 130 | pub const probe_read_user_str = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 114); | ||
| 131 | pub const probe_read_kernel_str = @intToPtr(fn (dst: ?*c_void, size: u32, unsafe_ptr: ?*const c_void) c_long, 115); | ||
| 132 | pub const tcp_send_ack = @intToPtr(fn (tp: ?*c_void, rcv_nxt: u32) c_long, 116); | ||
| 133 | pub const send_signal_thread = @intToPtr(fn (sig: u32) c_long, 117); | ||
| 134 | pub const jiffies64 = @intToPtr(fn () u64, 118); | ||
| 135 | pub const read_branch_records = @intToPtr(fn (ctx: *kern.PerfEventData, buf: ?*c_void, size: u32, flags: u64) c_long, 119); | ||
| 136 | pub const get_ns_current_pid_tgid = @intToPtr(fn (dev: u64, ino: u64, nsdata: *kern.PidNsInfo, size: u32) c_long, 120); | ||
| 137 | pub const xdp_output = @intToPtr(fn (ctx: ?*c_void, map: *const kern.MapDef, flags: u64, data: ?*c_void, size: u64) c_long, 121); | ||
| 138 | pub const get_netns_cookie = @intToPtr(fn (ctx: ?*c_void) u64, 122); | ||
| 139 | pub const get_current_ancestor_cgroup_id = @intToPtr(fn (ancestor_level: c_int) u64, 123); | ||
| 140 | pub const sk_assign = @intToPtr(fn (skb: *kern.SkBuff, sk: *kern.Sock, flags: u64) c_long, 124); | ||
| 141 | pub const ktime_get_boot_ns = @intToPtr(fn () u64, 125); | ||
| 142 | pub const seq_printf = @intToPtr(fn (m: *kern.SeqFile, fmt: ?*const u8, fmt_size: u32, data: ?*const c_void, data_len: u32) c_long, 126); | ||
| 143 | pub const seq_write = @intToPtr(fn (m: *kern.SeqFile, data: ?*const u8, len: u32) c_long, 127); | ||
| 144 | pub const sk_cgroup_id = @intToPtr(fn (sk: *kern.BpfSock) u64, 128); | ||
| 145 | pub const sk_ancestor_cgroup_id = @intToPtr(fn (sk: *kern.BpfSock, ancestor_level: c_long) u64, 129); | ||
| 146 | pub const ringbuf_output = @intToPtr(fn (ringbuf: ?*c_void, data: ?*c_void, size: u64, flags: u64) ?*c_void, 130); | ||
| 147 | pub const ringbuf_reserve = @intToPtr(fn (ringbuf: ?*c_void, size: u64, flags: u64) ?*c_void, 131); | ||
| 148 | pub const ringbuf_submit = @intToPtr(fn (data: ?*c_void, flags: u64) void, 132); | ||
| 149 | pub const ringbuf_discard = @intToPtr(fn (data: ?*c_void, flags: u64) void, 133); | ||
| 150 | pub const ringbuf_query = @intToPtr(fn (ringbuf: ?*c_void, flags: u64) u64, 134); | ||
| 151 | pub const csum_level = @intToPtr(fn (skb: *kern.SkBuff, level: u64) c_long, 134); | ||
| 152 | pub const skc_to_tcp6_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.Tcp6Sock, 135); | ||
| 153 | pub const skc_to_tcp_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.TcpSock, 136); | ||
| 154 | pub const skc_to_tcp_timewait_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.TcpTimewaitSock, 137); | ||
| 155 | pub const skc_to_tcp_request_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.TcpRequestSock, 138); | ||
| 156 | pub const skc_to_udp6_sock = @intToPtr(fn (sk: ?*c_void) ?*kern.Udp6Sock, 139); | ||
| 157 | pub 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. | ||
| 6 | const std = @import("../../../std.zig"); | ||
| 7 | |||
| 8 | const in_bpf_program = switch (std.builtin.arch) { | ||
| 9 | .bpfel, .bpfeb => true, | ||
| 10 | else => false, | ||
| 11 | }; | ||
| 12 | |||
| 13 | pub const helpers = if (in_bpf_program) @import("helpers.zig") else struct {}; | ||
| 14 | |||
| 15 | pub const BpfSock = @Type(.Opaque); | ||
| 16 | pub const BpfSockAddr = @Type(.Opaque); | ||
| 17 | pub const FibLookup = @Type(.Opaque); | ||
| 18 | pub const MapDef = @Type(.Opaque); | ||
| 19 | pub const PerfEventData = @Type(.Opaque); | ||
| 20 | pub const PerfEventValue = @Type(.Opaque); | ||
| 21 | pub const PidNsInfo = @Type(.Opaque); | ||
| 22 | pub const SeqFile = @Type(.Opaque); | ||
| 23 | pub const SkBuff = @Type(.Opaque); | ||
| 24 | pub const SkMsgMd = @Type(.Opaque); | ||
| 25 | pub const SkReusePortMd = @Type(.Opaque); | ||
| 26 | pub const Sock = @Type(.Opaque); | ||
| 27 | pub const SockAddr = @Type(.Opaque); | ||
| 28 | pub const SockOps = @Type(.Opaque); | ||
| 29 | pub const SockTuple = @Type(.Opaque); | ||
| 30 | pub const SpinLock = @Type(.Opaque); | ||
| 31 | pub const SysCtl = @Type(.Opaque); | ||
| 32 | pub const Tcp6Sock = @Type(.Opaque); | ||
| 33 | pub const TcpRequestSock = @Type(.Opaque); | ||
| 34 | pub const TcpSock = @Type(.Opaque); | ||
| 35 | pub const TcpTimewaitSock = @Type(.Opaque); | ||
| 36 | pub const TunnelKey = @Type(.Opaque); | ||
| 37 | pub const Udp6Sock = @Type(.Opaque); | ||
| 38 | pub const XdpMd = @Type(.Opaque); | ||
| 39 | pub const XfrmState = @Type(.Opaque); | ||
lib/std/os/linux/powerpc64.zig created+127| ... | @@ -0,0 +1,127 @@ | ||
| 1 | usingnamespace @import("../bits.zig"); | ||
| 2 | |||
| 3 | pub 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 | |||
| 15 | pub 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 | |||
| 28 | pub 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 | |||
| 42 | pub 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 | |||
| 57 | pub 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 | |||
| 73 | pub 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 | |||
| 90 | pub 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. | ||
| 117 | pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; | ||
| 118 | |||
| 119 | pub const restore = restore_rt; | ||
| 120 | |||
| 121 | pub 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 | }; |
| 54 | 54 | ||
| 55 | const tls_variant = switch (builtin.arch) { | 55 | const 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 efficient | 77 | // make the generated code more efficient |
| 78 | 78 | ||
| 79 | const tls_tp_offset = switch (builtin.arch) { | 79 | const 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 | }; |
| 83 | 83 | ||
| 84 | const tls_dtv_offset = switch (builtin.arch) { | 84 | const 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 | } |
| 830 | 830 | ||
| 831 | pub const MoveFileError = error{Unexpected}; | 831 | pub const MoveFileError = error{ FileNotFound, Unexpected }; |
| 832 | 832 | ||
| 833 | pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void { | 833 | pub 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 |
| 839 | pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DWORD) MoveFileError!void { | 839 | pub 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, |
| 196 | 196 | ||
| 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 | |||
| 432 | test "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 else | 593 | /// 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`. |
| 595 | pub fn posixGetUserInfo(name: []const u8) !UserInfo { | 595 | pub 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(); | ||
| 598 | 600 | ||
| 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 | \\ syscall | 394 | \\ 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 overwrite | 141 | // 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"); |
| 50 | pub const c = @import("c.zig"); | 50 | pub const c = @import("c.zig"); |
| 51 | pub const cache_hash = @import("cache_hash.zig"); | 51 | pub const cache_hash = @import("cache_hash.zig"); |
| 52 | pub const coff = @import("coff.zig"); | 52 | pub const coff = @import("coff.zig"); |
| 53 | pub const compress = @import("compress.zig"); | ||
| 53 | pub const crypto = @import("crypto.zig"); | 54 | pub const crypto = @import("crypto.zig"); |
| 54 | pub const cstr = @import("cstr.zig"); | 55 | pub const cstr = @import("cstr.zig"); |
| 55 | pub const debug = @import("debug.zig"); | 56 | pub 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(); |
| 9 | const is_windows = Target.current.os.tag == .windows; | 9 | const is_windows = Target.current.os.tag == .windows; |
| 10 | const is_gnu = Target.current.isGnu(); | 10 | const is_gnu = Target.current.isGnu(); |
| 11 | 11 | ||
| 12 | const log = std.log.scoped(.libc_installation); | ||
| 13 | |||
| 12 | usingnamespace @import("windows_sdk.zig"); | 14 | usingnamespace @import("windows_sdk.zig"); |
| 13 | 15 | ||
| 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 = .{}; |
| 43 | 44 | ||
| ... | @@ -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 | }; |
| 36 | 50 | ||
| 37 | base: File, | 51 | base: 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 { |
| 258 | 272 | ||
| 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; | ||
| 350 | 362 | ||
| 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.?); |
| 352 | 367 | ||
| 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); | ||
| 358 | 372 | ||
| 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 | } |
| 363 | 375 | ||
| 364 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} | 376 | pub 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( |
| 374 | 386 | ||
| 375 | if (decl.link.macho.symbol_table_index == null) return; | 387 | if (decl.link.macho.symbol_table_index == null) return; |
| 376 | 388 | ||
| 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 implement | 390 | // TODO implement |
| 379 | if (exports.len == 0) return; | 391 | if (exports.len == 0) return; |
| 380 | 392 | ||
| ... | @@ -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 }); |
| 507 | 518 | assert(needed_size <= text_capacity); // TODO handle growth | |
| 508 | if (needed_size > text_capacity) { | ||
| 509 | // TODO handle growth | ||
| 510 | } | ||
| 511 | 519 | ||
| 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(); |
| 660 | 668 | ||
| 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 { |
| 598 | 598 | ||
| 599 | // ABI warning | 599 | // ABI warning |
| 600 | export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error { | 600 | export 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 them | 2032 | // Handle the remaining escapes Zig doesn't support by turning them |
| 2033 | // into their respective hex representation | 2033 | // 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 unreachable | 2035 | std.fmt.bufPrint(char_buf, "\\x{x:0>2}", .{c}) catch unreachable |
| 2036 | else | 2036 | 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)identifier | 5906 | // (type)identifier |
| 5903 | .Identifier => {}, | 5907 | .Identifier => {}, |
| 5904 | // (type)integer | 5908 | // (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 { |
| 2265 | 2265 | ||
| 2266 | Stage2LibCInstallation *libc; | 2266 | Stage2LibCInstallation *libc; |
| 2267 | 2267 | ||
| 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; |
| 2374 | 2375 | ||
| 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) { |
| 2468 | 2472 | ||
| 2469 | AstNode *decl_node = union_type->data.unionation.decl_node; | 2473 | AstNode *decl_node = union_type->data.unionation.decl_node; |
| 2470 | 2474 | ||
| 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; |
| 2476 | 2477 | ||
| ... | @@ -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 | } |
| 2617 | 2618 | ||
| 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; |
| 3056 | 3057 | ||
| 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); | ||
| 3059 | 3059 | ||
| 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) { |
| 3069 | 3069 | ||
| 3070 | union_type->data.unionation.resolve_loop_flag_zero_bits = true; | 3070 | union_type->data.unionation.resolve_loop_flag_zero_bits = true; |
| 3071 | 3071 | ||
| 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); | ||
| 3083 | 3090 | ||
| 3084 | Scope *scope = &union_type->data.unionation.decls_scope->base; | 3091 | Scope *scope = &union_type->data.unionation.decls_scope->base; |
| 3085 | 3092 | ||
| 3086 | HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {}; | 3093 | HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {}; |
| 3087 | 3094 | ||
| 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; |
| 3158 | 3184 | ||
| 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; |
| 3167 | 3190 | 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 | } | ||
| 3176 | 3193 | ||
| 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; | ||
| 3200 | 3222 | ||
| 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 | } |
| 3213 | 3243 | ||
| 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 | } |
| 3228 | 3258 | ||
| 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 | } |
| 3234 | 3264 | ||
| 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; |
| 3247 | 3271 | ||
| 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 above | 3273 | assert(prev_entry == nullptr); // caught by union de-duplicator above |
| 3250 | 3274 | ||
| 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 | } | ||
| 3297 | 3324 | ||
| 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) { |
| 3300 | 3327 | 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 | } |
| 3304 | 3333 | ||
| 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; | ||
| 3307 | 3335 | ||
| 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 | } |
| 3327 | 3354 | ||
| 3328 | if (create_enum_type) { | 3355 | if (create_enum_type) { |
| 3329 | // Now iterate again and populate the unspecified tag values | 3356 | 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; | ||
| 3331 | 3359 | ||
| 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; |
| 3336 | 3364 | ||
| 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 |
| 8713 | 8744 | ||
| 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 | } |
| 93 | 93 | ||
| 94 | void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch) { | 94 | void 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); |
| 38 | void codegen_set_linker_script(CodeGen *g, const char *linker_script); | 38 | void codegen_set_linker_script(CodeGen *g, const char *linker_script); |
| 39 | void codegen_set_test_filter(CodeGen *g, Buf *filter); | 39 | void codegen_set_test_filter(CodeGen *g, Buf *filter); |
| 40 | void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); | 40 | void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); |
| 41 | void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch); | 41 | void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch); |
| 42 | void codegen_add_time_event(CodeGen *g, const char *name); | 42 | void codegen_add_time_event(CodeGen *g, const char *name); |
| 43 | void codegen_print_timing_report(CodeGen *g, FILE *f); | 43 | void codegen_print_timing_report(CodeGen *g, FILE *f); |
| 44 | void codegen_link(CodeGen *g); | 44 | void 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); |
| 336 | 336 | ||
| 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 | } |
| 11949 | 11950 | ||
| 11950 | // maybe | 11951 | // 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 |
| 25425 | 25447 | ||
| 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); |
| 25427 | 25449 | ||
| 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; |
| 25436 | 25456 | ||
| 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; |
| 25440 | 25460 | 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; | ||
| 25451 | 25461 | ||
| 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); | ||
| 26107 | 26118 | ||
| 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; | ||
| 26230 | 26267 | ||
| 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; |
| 1591 | 1597 | ||
| 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 | } |
| 781 | 781 | ||
| 782 | const char *target_lib_file_ext(const ZigTarget *target, bool is_static, | 782 | const 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"; |
| 857 | 868 | ||
| 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 | } |
| 1327 | 1335 | ||
| 1336 | bool target_is_ppc(const ZigTarget *target) { | ||
| 1337 | return target->arch == ZigLLVM_ppc || target->arch == ZigLLVM_ppc64 || | ||
| 1338 | target->arch == ZigLLVM_ppc64le; | ||
| 1339 | } | ||
| 1340 | |||
| 1328 | unsigned target_fn_align(const ZigTarget *target) { | 1341 | unsigned 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); |
| 87 | const char *target_llvm_ir_file_ext(const ZigTarget *target); | 87 | const char *target_llvm_ir_file_ext(const ZigTarget *target); |
| 88 | const char *target_exe_file_ext(const ZigTarget *target); | 88 | const char *target_exe_file_ext(const ZigTarget *target); |
| 89 | const char *target_lib_file_prefix(const ZigTarget *target); | 89 | const char *target_lib_file_prefix(const ZigTarget *target); |
| 90 | const char *target_lib_file_ext(const ZigTarget *target, bool is_static, | 90 | const 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); |
| 92 | 92 | ||
| 93 | bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target); | 93 | bool 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); |
| 95 | 95 | ||
| 96 | bool target_is_arm(const ZigTarget *target); | 96 | bool target_is_arm(const ZigTarget *target); |
| 97 | bool target_is_mips(const ZigTarget *target); | 97 | bool target_is_mips(const ZigTarget *target); |
| 98 | bool target_is_ppc(const ZigTarget *target); | ||
| 98 | bool target_allows_addr_zero(const ZigTarget *target); | 99 | bool target_allows_addr_zero(const ZigTarget *target); |
| 99 | bool target_has_valgrind_support(const ZigTarget *target); | 100 | bool target_has_valgrind_support(const ZigTarget *target); |
| 100 | bool target_os_is_darwin(Os os); | 101 | bool 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 | }); |
| 12 | 12 | ||
| 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 | }); |
| 7420 | 7549 | ||
| 7421 | cases.add( // fixed bug #2032 | 7550 | 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 segfault | 295 | .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 | |||
| 853 | test "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 @@ |
| 1 | const expect = @import("std").testing.expect; | 1 | const expect = @import("std").testing.expect; |
| 2 | const expectEqual = @import("std").testing.expectEqual; | ||
| 2 | 3 | ||
| 3 | const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h")); | 4 | const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h")); |
| 4 | 5 | ||
| 5 | test "initializer list expression" { | 6 | test "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 | |||
| 15 | test "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 | |||
| 317 | test "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 | |||
| 378 | test "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 | |||
| 404 | test "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); |
| 205 | 203 | ||
| ... | @@ -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); |
| 218 | 215 | ||
| 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 | } |
| 229 | 225 |
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*) a | 2763 | \\#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 | }); |
| 2771 | 2774 | ||
| 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", |