diff options
Diffstat (limited to 'lib')
39 files changed, 3349 insertions, 2672 deletions
diff --git a/lib/docs/wasm/markdown/Parser.zig b/lib/docs/wasm/markdown/Parser.zig index 7f463224be..fe4fc2f9b0 100644 --- a/lib/docs/wasm/markdown/Parser.zig +++ b/lib/docs/wasm/markdown/Parser.zig @@ -1540,7 +1540,7 @@ const InlineParser = struct { iter.pos += 1; return .{ .text = replacement }; }; - const is_valid = iter.pos + cp_len < iter.content.len and + const is_valid = iter.pos + cp_len <= iter.content.len and std.unicode.utf8ValidateSlice(iter.content[iter.pos..][0..cp_len]); const cp_encoded = if (is_valid) iter.content[iter.pos..][0..cp_len] diff --git a/lib/init/build.zig b/lib/init/build.zig index e513acdf25..d53cbbb982 100644 --- a/lib/init/build.zig +++ b/lib/init/build.zig @@ -19,7 +19,7 @@ pub fn build(b: *std.Build) void { .name = "$", // In this case the main source file is merely a path, however, in more // complicated build scripts, this could be a generated file. - .root_source_file = .{ .path = "src/root.zig" }, + .root_source_file = b.path("src/root.zig"), .target = target, .optimize = optimize, }); @@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "$", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); @@ -67,7 +67,7 @@ pub fn build(b: *std.Build) void { // Creates a step for unit testing. This only builds the test executable // but does not run it. const lib_unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/root.zig" }, + .root_source_file = b.path("src/root.zig"), .target = target, .optimize = optimize, }); @@ -75,7 +75,7 @@ pub fn build(b: *std.Build) void { const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const exe_unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); diff --git a/lib/init/build.zig.zon b/lib/init/build.zig.zon index dc9d138ba5..50da5587b1 100644 --- a/lib/init/build.zig.zon +++ b/lib/init/build.zig.zon @@ -37,6 +37,11 @@ // // build root. In this case the package's hash is irrelevant and therefore not // // computed. This field and `url` are mutually exclusive. // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, //}, }, diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 2c644ce4b0..a3d091858a 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -607,17 +607,17 @@ pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: var h_list = [_][]const u8{ self.install_path, "include" }; if (dir_list.lib_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; + if (fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; lib_list[1] = dir; } if (dir_list.exe_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; + if (fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; exe_list[1] = dir; } if (dir_list.include_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; + if (fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; h_list[1] = dir; } @@ -858,7 +858,7 @@ pub const TestOptions = struct { /// deprecated: use `.filters = &.{filter}` instead of `.filter = filter`. filter: ?[]const u8 = null, filters: []const []const u8 = &.{}, - test_runner: ?[]const u8 = null, + test_runner: ?LazyPath = null, link_libc: ?bool = null, single_threaded: ?bool = null, pic: ?bool = null, @@ -1546,22 +1546,22 @@ pub fn addInstallArtifact( } ///`dest_rel_path` is relative to prefix path -pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step); +pub fn installFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + b.getInstallStep().dependOn(&b.addInstallFileWithDir(b.path(src_path), .prefix, dest_rel_path).step); } -pub fn installDirectory(self: *Build, options: Step.InstallDir.Options) void { - self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); +pub fn installDirectory(b: *Build, options: Step.InstallDir.Options) void { + b.getInstallStep().dependOn(&b.addInstallDirectory(options).step); } ///`dest_rel_path` is relative to bin path -pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step); +pub fn installBinFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + b.getInstallStep().dependOn(&b.addInstallFileWithDir(b.path(src_path), .bin, dest_rel_path).step); } ///`dest_rel_path` is relative to lib path -pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); +pub fn installLibFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + b.getInstallStep().dependOn(&b.addInstallFileWithDir(b.path(src_path), .lib, dest_rel_path).step); } pub fn addObjCopy(b: *Build, source: LazyPath, options: Step.ObjCopy.Options) *Step.ObjCopy { @@ -1635,6 +1635,22 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void { src_file.close(); } +/// References a file or directory relative to the source root. +pub fn path(b: *Build, sub_path: []const u8) LazyPath { + if (fs.path.isAbsolute(sub_path)) { + std.debug.panic("sub_path is expected to be relative to the build root, but was this absolute path: '{s}'. It is best avoid absolute paths, but if you must, it is supported by LazyPath.cwd_relative", .{ + sub_path, + }); + } + return .{ .src_path = .{ + .owner = b, + .sub_path = sub_path, + } }; +} + +/// This is low-level implementation details of the build system, not meant to +/// be called by users' build scripts. Even in the build system itself it is a +/// code smell to call this function. pub fn pathFromRoot(b: *Build, p: []const u8) []u8 { return fs.path.resolve(b.allocator, &.{ b.build_root.path orelse ".", p }) catch @panic("OOM"); } @@ -1674,10 +1690,9 @@ pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []con return name; } var it = mem.tokenizeScalar(u8, PATH, fs.path.delimiter); - while (it.next()) |path| { + while (it.next()) |p| { const full_path = self.pathJoin(&.{ - path, - self.fmt("{s}{s}", .{ name, exe_extension }), + p, self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1687,10 +1702,9 @@ pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []con if (fs.path.isAbsolute(name)) { return name; } - for (paths) |path| { + for (paths) |p| { const full_path = self.pathJoin(&.{ - path, - self.fmt("{s}{s}", .{ name, exe_extension }), + p, self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1771,7 +1785,7 @@ pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) .bin => self.exe_dir, .lib => self.lib_dir, .header => self.h_dir, - .custom => |path| self.pathJoin(&.{ self.install_path, path }), + .custom => |p| self.pathJoin(&.{ self.install_path, p }), }; return fs.path.resolve( self.allocator, @@ -2032,7 +2046,7 @@ fn dependencyInner( const build_root: std.Build.Cache.Directory = .{ .path = build_root_string, - .handle = std.fs.cwd().openDir(build_root_string, .{}) catch |err| { + .handle = fs.cwd().openDir(build_root_string, .{}) catch |err| { std.debug.print("unable to open '{s}': {s}\n", .{ build_root_string, @errorName(err), }); @@ -2093,9 +2107,9 @@ pub const GeneratedFile = struct { // so that we can join it with another path (e.g. build root, cache root, etc.) // // dirname("") should still be null, because we can't go up any further. -fn dirnameAllowEmpty(path: []const u8) ?[]const u8 { - return fs.path.dirname(path) orelse { - if (fs.path.isAbsolute(path) or path.len == 0) return null; +fn dirnameAllowEmpty(full_path: []const u8) ?[]const u8 { + return fs.path.dirname(full_path) orelse { + if (fs.path.isAbsolute(full_path) or full_path.len == 0) return null; return ""; }; @@ -2117,11 +2131,15 @@ test dirnameAllowEmpty { /// A reference to an existing or future path. pub const LazyPath = union(enum) { - /// A source file path relative to build root. - /// This should not be an absolute path, but in an older iteration of the zig build - /// system API, it was allowed to be absolute. Absolute paths should use `cwd_relative`. + /// Deprecated; use the `path` function instead. path: []const u8, + /// A source file path relative to build root. + src_path: struct { + owner: *std.Build, + sub_path: []const u8, + }, + /// A file that is generated by an interface. Those files usually are /// not available until built by a build step. generated: *const GeneratedFile, @@ -2150,11 +2168,10 @@ pub const LazyPath = union(enum) { sub_path: []const u8, }, - /// Returns a new file source that will have a relative path to the build root guaranteed. - /// Asserts the parameter is not an absolute path. - pub fn relative(path: []const u8) LazyPath { - std.debug.assert(!std.fs.path.isAbsolute(path)); - return LazyPath{ .path = path }; + /// Deprecated. Call `path` instead. + pub fn relative(p: []const u8) LazyPath { + std.log.warn("deprecated. call std.Build.path instead", .{}); + return .{ .path = p }; } /// Returns a lazy path referring to the directory containing this path. @@ -2168,13 +2185,16 @@ pub const LazyPath = union(enum) { return switch (self) { .generated => |gen| .{ .generated_dirname = .{ .generated = gen, .up = 0 } }, .generated_dirname => |gen| .{ .generated_dirname = .{ .generated = gen.generated, .up = gen.up + 1 } }, + .src_path => |sp| .{ .src_path = .{ + .owner = sp.owner, + .sub_path = dirnameAllowEmpty(sp.sub_path) orelse { + dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {}; + @panic("misconfigured build script"); + }, + } }, .path => |p| .{ .path = dirnameAllowEmpty(p) orelse { - dumpBadDirnameHelp(null, null, - \\dirname() attempted to traverse outside the build root. - \\This is not allowed. - \\ - , .{}) catch {}; + dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {}; @panic("misconfigured build script"); }, }, @@ -2195,7 +2215,6 @@ pub const LazyPath = union(enum) { } else { dumpBadDirnameHelp(null, null, \\dirname() attempted to traverse outside the current working directory. - \\This is not allowed. \\ , .{}) catch {}; @panic("misconfigured build script"); @@ -2207,7 +2226,6 @@ pub const LazyPath = union(enum) { .sub_path = dirnameAllowEmpty(dep.sub_path) orelse { dumpBadDirnameHelp(null, null, \\dirname() attempted to traverse outside the dependency root. - \\This is not allowed. \\ , .{}) catch {}; @panic("misconfigured build script"); @@ -2220,7 +2238,8 @@ pub const LazyPath = union(enum) { /// Either returns the path or `"generated"`. pub fn getDisplayName(self: LazyPath) []const u8 { return switch (self) { - .path, .cwd_relative => self.path, + .src_path => |sp| sp.sub_path, + .path, .cwd_relative => |p| p, .generated => "generated", .generated_dirname => "generated", .dependency => "dependency", @@ -2230,7 +2249,7 @@ pub const LazyPath = union(enum) { /// Adds dependencies this file source implies to the given step. pub fn addStepDependencies(self: LazyPath, other_step: *Step) void { switch (self) { - .path, .cwd_relative, .dependency => {}, + .src_path, .path, .cwd_relative, .dependency => {}, .generated => |gen| other_step.dependOn(gen.step), .generated_dirname => |gen| other_step.dependOn(gen.generated.step), } @@ -2250,6 +2269,7 @@ pub const LazyPath = union(enum) { pub fn getPath2(self: LazyPath, src_builder: *Build, asking_step: ?*Step) []const u8 { switch (self) { .path => |p| return src_builder.pathFromRoot(p), + .src_path => |sp| return sp.owner.pathFromRoot(sp.sub_path), .cwd_relative => |p| return src_builder.pathFromCwd(p), .generated => |gen| return gen.path orelse { std.debug.getStderrMutex().lock(); @@ -2262,13 +2282,13 @@ pub const LazyPath = union(enum) { (src_builder.cache_root.join(src_builder.allocator, &.{"."}) catch @panic("OOM")); const gen_step = gen.generated.step; - var path = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step); + var p = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step); var i: usize = 0; while (i <= gen.up) : (i += 1) { // path is absolute. // dirname will return null only if we're at root. // Typically, we'll stop well before that at the cache root. - path = fs.path.dirname(path) orelse { + p = fs.path.dirname(p) orelse { dumpBadDirnameHelp(gen_step, asking_step, \\dirname() reached root. \\No more directories left to go up. @@ -2277,7 +2297,7 @@ pub const LazyPath = union(enum) { @panic("misconfigured build script"); }; - if (mem.eql(u8, path, cache_root_path) and i < gen.up) { + if (mem.eql(u8, p, cache_root_path) and i < gen.up) { // If we hit the cache root and there's still more to go, // the script attempted to go too far. dumpBadDirnameHelp(gen_step, asking_step, @@ -2288,7 +2308,7 @@ pub const LazyPath = union(enum) { @panic("misconfigured build script"); } } - return path; + return p; }, .dependency => |dep| { return dep.dependency.builder.pathJoin(&[_][]const u8{ @@ -2299,9 +2319,16 @@ pub const LazyPath = union(enum) { } } - /// Duplicates the file source for a given builder. + /// Copies the internal strings. + /// + /// The `b` parameter is only used for its allocator. All *Build instances + /// share the same allocator. pub fn dupe(self: LazyPath, b: *Build) LazyPath { return switch (self) { + .src_path => |sp| .{ .src_path = .{ + .owner = sp.owner, + .sub_path = sp.owner.dupePath(sp.sub_path), + } }, .path => |p| .{ .path = b.dupePath(p) }, .cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) }, .generated => |gen| .{ .generated = gen }, diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index 02386de430..d19f0a781e 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -451,7 +451,7 @@ pub fn linkFramework(m: *Module, name: []const u8, options: LinkFrameworkOptions pub const AddCSourceFilesOptions = struct { /// When provided, `files` are relative to `root` rather than the /// package that owns the `Compile` step. - root: LazyPath = .{ .path = "" }, + root: ?LazyPath = null, files: []const []const u8, flags: []const []const u8 = &.{}, }; @@ -472,7 +472,7 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void { const c_source_files = allocator.create(CSourceFiles) catch @panic("OOM"); c_source_files.* = .{ - .root = options.root, + .root = options.root orelse b.path(""), .files = b.dupeStrings(options.files), .flags = b.dupeStrings(options.flags), }; @@ -573,17 +573,6 @@ pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void { pub fn addRPath(m: *Module, directory_path: LazyPath) void { const b = m.owner; - switch (directory_path) { - .path, .cwd_relative => |path| { - // TODO: remove this check after people upgrade and stop expecting it to work - if (std.mem.startsWith(u8, path, "@executable_path") or - std.mem.startsWith(u8, path, "@loader_path")) - { - @panic("this function is for adding directory paths. It does not support special rpaths. use addRPathSpecial for that."); - } - }, - else => {}, - } m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM"); addLazyPathDependenciesOnly(m, directory_path); } diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index c39a487649..f8f0fcd584 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -247,15 +247,27 @@ const ComputeCompareExpected = struct { const Check = struct { kind: Kind, + payload: Payload, + data: std.ArrayList(u8), actions: std.ArrayList(Action), fn create(allocator: Allocator, kind: Kind) Check { return .{ .kind = kind, + .payload = .{ .none = {} }, + .data = std.ArrayList(u8).init(allocator), .actions = std.ArrayList(Action).init(allocator), }; } + fn dumpSection(allocator: Allocator, name: [:0]const u8) Check { + var check = Check.create(allocator, .dump_section); + const off: u32 = @intCast(check.data.items.len); + check.data.writer().print("{s}\x00", .{name}) catch @panic("OOM"); + check.payload = .{ .dump_section = off }; + return check; + } + fn extract(self: *Check, phrase: SearchPhrase) void { self.actions.append(.{ .tag = .extract, @@ -305,6 +317,13 @@ const Check = struct { dyld_lazy_bind, exports, compute_compare, + dump_section, + }; + + const Payload = union { + none: void, + /// Null-delimited string in the 'data' buffer. + dump_section: u32, }; }; @@ -513,6 +532,11 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void { self.checkExact(label); } +pub fn dumpSection(self: *CheckObject, name: [:0]const u8) void { + const new_check = Check.dumpSection(self.step.owner.allocator, name); + self.checks.append(new_check) catch @panic("OOM"); +} + /// Creates a new standalone, singular check which allows running simple binary operations /// on the extracted variables. It will then compare the reduced program with the value of /// the expected variable. @@ -564,13 +588,44 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } const output = switch (self.obj_format) { - .macho => try MachODumper.parseAndDump(step, chk.kind, contents), - .elf => try ElfDumper.parseAndDump(step, chk.kind, contents), + .macho => try MachODumper.parseAndDump(step, chk, contents), + .elf => try ElfDumper.parseAndDump(step, chk, contents), .coff => return step.fail("TODO coff parser", .{}), - .wasm => try WasmDumper.parseAndDump(step, chk.kind, contents), + .wasm => try WasmDumper.parseAndDump(step, chk, contents), else => unreachable, }; + // Depending on whether we requested dumping section verbatim or not, + // we either format message string with escaped codes, or not to aid debugging + // the failed test. + const fmtMessageString = struct { + fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(formatMessageString) { + return .{ .data = .{ + .kind = kind, + .msg = msg, + } }; + } + + const Ctx = struct { + kind: Check.Kind, + msg: []const u8, + }; + + fn formatMessageString( + ctx: Ctx, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + switch (ctx.kind) { + .dump_section => try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}), + else => try writer.writeAll(ctx.msg), + } + } + }.fmtMessageString; + var it = mem.tokenizeAny(u8, output, "\r\n"); for (chk.actions.items) |act| { switch (act.tag) { @@ -585,7 +640,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -600,7 +655,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -614,7 +669,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does contain it: ======== \\{s} \\=================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -629,7 +684,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ act.phrase.resolve(b, step), fmtMessageString(chk.kind, output) }); } }, @@ -660,7 +715,7 @@ const MachODumper = struct { } }; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -731,7 +786,7 @@ const MachODumper = struct { } } - switch (kind) { + switch (check.kind) { .headers => { try dumpHeader(hdr, writer); @@ -764,7 +819,7 @@ const MachODumper = struct { if (dyld_info_lc == null) return step.fail("no dyld info found", .{}); const lc = dyld_info_lc.?; - switch (kind) { + switch (check.kind) { .dyld_rebase => if (lc.rebase_size > 0) { const data = bytes[lc.rebase_off..][0..lc.rebase_size]; try writer.writeAll(dyld_rebase_label ++ "\n"); @@ -805,7 +860,7 @@ const MachODumper = struct { return step.fail("no exports data found", .{}); }, - else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(kind)}), + else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -1633,14 +1688,14 @@ const ElfDumper = struct { const dynamic_section_label = "dynamic section"; const archive_symtab_label = "archive symbol table"; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { - return parseAndDumpArchive(step, kind, bytes) catch |err| switch (err) { - error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, kind, bytes), + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { + return parseAndDumpArchive(step, check, bytes) catch |err| switch (err) { + error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, check, bytes), else => |e| return e, }; } - fn parseAndDumpArchive(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -1702,13 +1757,13 @@ const ElfDumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .archive_symtab => if (ctx.symtab.items.len > 0) { try ctx.dumpSymtab(writer); } else return step.fail("no archive symbol table found", .{}), else => if (ctx.objects.items.len > 0) { - try ctx.dumpObjects(step, kind, writer); + try ctx.dumpObjects(step, check, writer); } else return step.fail("empty archive", .{}), } @@ -1785,10 +1840,10 @@ const ElfDumper = struct { } } - fn dumpObjects(ctx: ArchiveContext, step: *Step, kind: Check.Kind, writer: anytype) !void { + fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void { for (ctx.objects.items) |object| { try writer.print("object {s}\n", .{object.name}); - const output = try parseAndDumpObject(step, kind, ctx.data[object.off..][0..object.len]); + const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]); defer ctx.gpa.free(output); try writer.print("{s}\n", .{output}); } @@ -1806,7 +1861,7 @@ const ElfDumper = struct { }; }; - fn parseAndDumpObject(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -1859,7 +1914,7 @@ const ElfDumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .headers => { try ctx.dumpHeader(writer); try ctx.dumpShdrs(writer); @@ -1878,7 +1933,13 @@ const ElfDumper = struct { try ctx.dumpDynamicSection(shndx, writer); } else return step.fail("no .dynamic section found", .{}), - else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(kind)}), + .dump_section => { + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); + const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name}); + try ctx.dumpSection(shndx, writer); + }, + + else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -2176,6 +2237,11 @@ const ElfDumper = struct { } } + fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { + const data = ctx.getSectionContents(shndx); + try writer.print("{s}", .{data}); + } + inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { const shdr = ctx.shdrs[shndx]; return getString(ctx.shstrtab, shdr.sh_name); @@ -2300,7 +2366,7 @@ const ElfDumper = struct { const WasmDumper = struct { const symtab_label = "symbols"; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var fbs = std.io.fixedBufferStream(bytes); const reader = fbs.reader(); @@ -2317,7 +2383,7 @@ const WasmDumper = struct { errdefer output.deinit(); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .headers => { while (reader.readByte()) |current_byte| { const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { @@ -2330,7 +2396,7 @@ const WasmDumper = struct { } else |_| {} // reached end of stream }, - else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(kind)}), + else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -2364,7 +2430,7 @@ const WasmDumper = struct { => { const entries = try std.leb.readULEB128(u32, reader); try writer.print("\nentries {d}\n", .{entries}); - try dumpSection(step, section, data[fbs.pos..], entries, writer); + try parseSection(step, section, data[fbs.pos..], entries, writer); }, .custom => { const name_length = try std.leb.readULEB128(u32, reader); @@ -2393,7 +2459,7 @@ const WasmDumper = struct { } } - fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { + fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { var fbs = std.io.fixedBufferStream(data); const reader = fbs.reader(); diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 4a5364176a..c36fd8c4ca 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -55,7 +55,7 @@ global_base: ?u64 = null, zig_lib_dir: ?LazyPath, exec_cmd_args: ?[]const ?[]const u8, filters: []const []const u8, -test_runner: ?[]const u8, +test_runner: ?LazyPath, test_server_mode: bool, wasi_exec_model: ?std.builtin.WasiExecModel = null, @@ -236,7 +236,7 @@ pub const Options = struct { version: ?std.SemanticVersion = null, max_rss: usize = 0, filters: []const []const u8 = &.{}, - test_runner: ?[]const u8 = null, + test_runner: ?LazyPath = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, @@ -264,20 +264,8 @@ pub const HeaderInstallation = union(enum) { dest_rel_path: []const u8, pub fn dupe(self: File, b: *std.Build) File { - // 'path' lazy paths are relative to the build root of some step, inferred from the step - // in which they are used. This means that we can't dupe such paths, because they may - // come from dependencies with their own build roots and duping the paths as is might - // cause the build script to search for the file relative to the wrong root. - // As a temporary workaround, we convert build root-relative paths to absolute paths. - // If/when the build-root relative paths are updated to encode which build root they are - // relative to, this workaround should be removed. - const duped_source: LazyPath = switch (self.source) { - .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) }, - else => self.source.dupe(b), - }; - return .{ - .source = duped_source, + .source = self.source.dupe(b), .dest_rel_path = b.dupePath(self.dest_rel_path), }; } @@ -305,20 +293,8 @@ pub const HeaderInstallation = union(enum) { }; pub fn dupe(self: Directory, b: *std.Build) Directory { - // 'path' lazy paths are relative to the build root of some step, inferred from the step - // in which they are used. This means that we can't dupe such paths, because they may - // come from dependencies with their own build roots and duping the paths as is might - // cause the build script to search for the file relative to the wrong root. - // As a temporary workaround, we convert build root-relative paths to absolute paths. - // If/when the build-root relative paths are updated to encode which build root they are - // relative to, this workaround should be removed. - const duped_source: LazyPath = switch (self.source) { - .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) }, - else => self.source.dupe(b), - }; - return .{ - .source = duped_source, + .source = self.source.dupe(b), .dest_rel_path = b.dupePath(self.dest_rel_path), .options = self.options.dupe(b), }; @@ -402,7 +378,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .zig_lib_dir = null, .exec_cmd_args = null, .filters = options.filters, - .test_runner = options.test_runner, + .test_runner = null, .test_server_mode = options.test_runner == null, .rdynamic = false, .installed_path = null, @@ -429,6 +405,11 @@ pub fn create(owner: *std.Build, options: Options) *Compile { lp.addStepDependencies(&self.step); } + if (options.test_runner) |lp| { + self.test_runner = lp.dupe(self.step.owner); + lp.addStepDependencies(&self.step); + } + // Only the PE/COFF format has a Resource Table which is where the manifest // gets embedded, so for any other target the manifest file is just ignored. if (target.ofmt == .coff) { @@ -1402,7 +1383,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.test_runner) |test_runner| { try zig_args.append("--test-runner"); - try zig_args.append(b.pathFromRoot(test_runner)); + try zig_args.append(test_runner.getPath(b)); } for (b.debug_log_scopes) |log_scope| { diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index 46631cac24..e547f8082e 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -58,6 +58,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader { if (options.style.getPath()) |s| default_include_path: { const sub_path = switch (s) { + .src_path => |sp| sp.sub_path, .path => |path| path, .generated, .generated_dirname => break :default_include_path, .cwd_relative => |sub_path| sub_path, @@ -192,13 +193,21 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .autoconf => |file_source| { try output.appendSlice(c_generated_line); const src_path = file_source.getPath(b); - const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); + const contents = std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes) catch |err| { + return step.fail("unable to read autoconf input file '{s}': {s}", .{ + src_path, @errorName(err), + }); + }; try render_autoconf(step, contents, &output, self.values, src_path); }, .cmake => |file_source| { try output.appendSlice(c_generated_line); const src_path = file_source.getPath(b); - const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); + const contents = std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes) catch |err| { + return step.fail("unable to read cmake input file '{s}': {s}", .{ + src_path, @errorName(err), + }); + }; try render_cmake(step, contents, &output, self.values, src_path); }, .blank => { diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 842442e37b..55cde46308 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -2740,6 +2740,13 @@ pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool { return true; } + if (target.os.tag == .haiku) { + if (eqlIgnoreCase(ignore_case, name, "root")) + return true; + if (eqlIgnoreCase(ignore_case, name, "network")) + return true; + } + return false; } diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index cbd3d42741..ee0c602125 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -1,156 +1,157 @@ //! Uniform Resource Identifier (URI) parsing roughly adhering to <https://tools.ietf.org/html/rfc3986>. //! Does not do perfect grammar and character class checking, but should be robust against URIs in the wild. -const Uri = @This(); -const std = @import("std.zig"); -const testing = std.testing; -const Allocator = std.mem.Allocator; - scheme: []const u8, -user: ?[]const u8 = null, -password: ?[]const u8 = null, -host: ?[]const u8 = null, +user: ?Component = null, +password: ?Component = null, +host: ?Component = null, port: ?u16 = null, -path: []const u8, -query: ?[]const u8 = null, -fragment: ?[]const u8 = null, - -/// Applies URI encoding and replaces all reserved characters with their respective %XX code. -pub fn escapeString(allocator: Allocator, input: []const u8) error{OutOfMemory}![]u8 { - return escapeStringWithFn(allocator, input, isUnreserved); -} - -pub fn escapePath(allocator: Allocator, input: []const u8) error{OutOfMemory}![]u8 { - return escapeStringWithFn(allocator, input, isPathChar); -} - -pub fn escapeQuery(allocator: Allocator, input: []const u8) error{OutOfMemory}![]u8 { - return escapeStringWithFn(allocator, input, isQueryChar); -} - -pub fn writeEscapedString(writer: anytype, input: []const u8) !void { - return writeEscapedStringWithFn(writer, input, isUnreserved); -} - -pub fn writeEscapedPath(writer: anytype, input: []const u8) !void { - return writeEscapedStringWithFn(writer, input, isPathChar); -} - -pub fn writeEscapedQuery(writer: anytype, input: []const u8) !void { - return writeEscapedStringWithFn(writer, input, isQueryChar); -} - -pub fn escapeStringWithFn(allocator: Allocator, input: []const u8, comptime keepUnescaped: fn (c: u8) bool) Allocator.Error![]u8 { - var outsize: usize = 0; - for (input) |c| { - outsize += if (keepUnescaped(c)) @as(usize, 1) else 3; +path: Component = Component.empty, +query: ?Component = null, +fragment: ?Component = null, + +pub const Component = union(enum) { + /// Invalid characters in this component must be percent encoded + /// before being printed as part of a URI. + raw: []const u8, + /// This component is already percent-encoded, it can be printed + /// directly as part of a URI. + percent_encoded: []const u8, + + pub const empty: Component = .{ .percent_encoded = "" }; + + pub fn isEmpty(component: Component) bool { + return switch (component) { + .raw, .percent_encoded => |string| string.len == 0, + }; } - var output = try allocator.alloc(u8, outsize); - var outptr: usize = 0; - for (input) |c| { - if (keepUnescaped(c)) { - output[outptr] = c; - outptr += 1; - } else { - var buf: [2]u8 = undefined; - _ = std.fmt.bufPrint(&buf, "{X:0>2}", .{c}) catch unreachable; - - output[outptr + 0] = '%'; - output[outptr + 1] = buf[0]; - output[outptr + 2] = buf[1]; - outptr += 3; - } + /// Allocates the result with `arena` only if needed, so the result should not be freed. + pub fn toRawMaybeAlloc( + component: Component, + arena: std.mem.Allocator, + ) std.mem.Allocator.Error![]const u8 { + return switch (component) { + .raw => |raw| raw, + .percent_encoded => |percent_encoded| if (std.mem.indexOfScalar(u8, percent_encoded, '%')) |_| + try std.fmt.allocPrint(arena, "{raw}", .{component}) + else + percent_encoded, + }; } - return output; -} -pub fn writeEscapedStringWithFn(writer: anytype, input: []const u8, comptime keepUnescaped: fn (c: u8) bool) @TypeOf(writer).Error!void { - for (input) |c| { - if (keepUnescaped(c)) { - try writer.writeByte(c); - } else { - try writer.print("%{X:0>2}", .{c}); - } + pub fn format( + component: Component, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (fmt_str.len == 0) { + try writer.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{ + @tagName(component), + std.zig.fmtEscapes(switch (component) { + .raw, .percent_encoded => |string| string, + }), + }); + } else if (comptime std.mem.eql(u8, fmt_str, "raw")) switch (component) { + .raw => |raw| try writer.writeAll(raw), + .percent_encoded => |percent_encoded| { + var start: usize = 0; + var index: usize = 0; + while (std.mem.indexOfScalarPos(u8, percent_encoded, index, '%')) |percent| { + index = percent + 1; + if (percent_encoded.len - index < 2) continue; + const percent_encoded_char = + std.fmt.parseInt(u8, percent_encoded[index..][0..2], 16) catch continue; + try writer.print("{s}{c}", .{ + percent_encoded[start..percent], + percent_encoded_char, + }); + start = percent + 3; + index = percent + 3; + } + try writer.writeAll(percent_encoded[start..]); + }, + } else if (comptime std.mem.eql(u8, fmt_str, "%")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isUnreserved), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "user")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isUserChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "password")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isPasswordChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "host")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isHostChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "path")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isPathChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "query")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isQueryChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "fragment")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isFragmentChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else @compileError("invalid format string '" ++ fmt_str ++ "'"); } -} -/// Parses a URI string and unescapes all %XX where XX is a valid hex number. Otherwise, verbatim copies -/// them to the output. -pub fn unescapeString(allocator: Allocator, input: []const u8) error{OutOfMemory}![]u8 { - var outsize: usize = 0; - var inptr: usize = 0; - while (inptr < input.len) { - if (input[inptr] == '%') { - inptr += 1; - if (inptr + 2 <= input.len) { - _ = std.fmt.parseInt(u8, input[inptr..][0..2], 16) catch { - outsize += 3; - inptr += 2; - continue; - }; - inptr += 2; - outsize += 1; - } else { - outsize += 1; - } - } else { - inptr += 1; - outsize += 1; + pub fn percentEncode( + writer: anytype, + raw: []const u8, + comptime isValidChar: fn (u8) bool, + ) @TypeOf(writer).Error!void { + var start: usize = 0; + for (raw, 0..) |char, index| { + if (isValidChar(char)) continue; + try writer.print("{s}%{X:0>2}", .{ raw[start..index], char }); + start = index + 1; } + try writer.writeAll(raw[start..]); } +}; - var output = try allocator.alloc(u8, outsize); - var outptr: usize = 0; - inptr = 0; - while (inptr < input.len) { - if (input[inptr] == '%') { - inptr += 1; - if (inptr + 2 <= input.len) { - const value = std.fmt.parseInt(u8, input[inptr..][0..2], 16) catch { - output[outptr + 0] = input[inptr + 0]; - output[outptr + 1] = input[inptr + 1]; - inptr += 2; - outptr += 2; +/// Percent decodes all %XX where XX is a valid hex number. +/// `output` may alias `input` if `output.ptr <= input.ptr`. +/// Mutates and returns a subslice of `output`. +pub fn percentDecodeBackwards(output: []u8, input: []const u8) []u8 { + var input_index = input.len; + var output_index = output.len; + while (input_index > 0) { + if (input_index >= 3) { + const maybe_percent_encoded = input[input_index - 3 ..][0..3]; + if (maybe_percent_encoded[0] == '%') { + if (std.fmt.parseInt(u8, maybe_percent_encoded[1..], 16)) |percent_encoded_char| { + input_index -= maybe_percent_encoded.len; + output_index -= 1; + output[output_index] = percent_encoded_char; continue; - }; - - output[outptr] = value; - - inptr += 2; - outptr += 1; - } else { - output[outptr] = input[inptr - 1]; - outptr += 1; + } else |_| {} } - } else { - output[outptr] = input[inptr]; - inptr += 1; - outptr += 1; } + input_index -= 1; + output_index -= 1; + output[output_index] = input[input_index]; } - return output; + return output[output_index..]; +} + +/// Percent decodes all %XX where XX is a valid hex number. +/// Mutates and returns a subslice of `buffer`. +pub fn percentDecodeInPlace(buffer: []u8) []u8 { + return percentDecodeBackwards(buffer, buffer); } pub const ParseError = error{ UnexpectedCharacter, InvalidFormat, InvalidPort }; /// Parses the URI or returns an error. This function is not compliant, but is required to parse /// some forms of URIs in the wild, such as HTTP Location headers. -/// The return value will contain unescaped strings pointing into the -/// original `text`. Each component that is provided, will be non-`null`. -pub fn parseWithoutScheme(text: []const u8) ParseError!Uri { +/// The return value will contain strings pointing into the original `text`. +/// Each component that is provided, will be non-`null`. +pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri { var reader = SliceReader{ .slice = text }; - var uri = Uri{ - .scheme = "", - .user = null, - .password = null, - .host = null, - .port = null, - .path = "", // path is always set, but empty by default. - .query = null, - .fragment = null, - }; + var uri: Uri = .{ .scheme = scheme, .path = undefined }; if (reader.peekPrefix("//")) a: { // authority part std.debug.assert(reader.get().? == '/'); @@ -167,12 +168,12 @@ pub fn parseWithoutScheme(text: []const u8) ParseError!Uri { const user_info = authority[0..index]; if (std.mem.indexOf(u8, user_info, ":")) |idx| { - uri.user = user_info[0..idx]; + uri.user = .{ .percent_encoded = user_info[0..idx] }; if (idx < user_info.len - 1) { // empty password is also "no password" - uri.password = user_info[idx + 1 ..]; + uri.password = .{ .percent_encoded = user_info[idx + 1 ..] }; } } else { - uri.user = user_info; + uri.user = .{ .percent_encoded = user_info }; uri.password = null; } } @@ -205,19 +206,19 @@ pub fn parseWithoutScheme(text: []const u8) ParseError!Uri { } if (start_of_host >= end_of_host) return error.InvalidFormat; - uri.host = authority[start_of_host..end_of_host]; + uri.host = .{ .percent_encoded = authority[start_of_host..end_of_host] }; } - uri.path = reader.readUntil(isPathSeparator); + uri.path = .{ .percent_encoded = reader.readUntil(isPathSeparator) }; if ((reader.peek() orelse 0) == '?') { // query part std.debug.assert(reader.get().? == '?'); - uri.query = reader.readUntil(isQuerySeparator); + uri.query = .{ .percent_encoded = reader.readUntil(isQuerySeparator) }; } if ((reader.peek() orelse 0) == '#') { // fragment part std.debug.assert(reader.get().? == '#'); - uri.fragment = reader.readUntilEof(); + uri.fragment = .{ .percent_encoded = reader.readUntilEof() }; } return uri; @@ -242,8 +243,8 @@ pub const WriteToStreamOptions = struct { /// When true, include the fragment part of the URI. Ignored when `path` is false. fragment: bool = false, - /// When true, do not escape any part of the URI. - raw: bool = false, + /// When true, include the port part of the URI. Ignored when `port` is null. + port: bool = true, }; pub fn writeToStream( @@ -252,80 +253,53 @@ pub fn writeToStream( writer: anytype, ) @TypeOf(writer).Error!void { if (options.scheme) { - try writer.writeAll(uri.scheme); - try writer.writeAll(":"); - + try writer.print("{s}:", .{uri.scheme}); if (options.authority and uri.host != null) { try writer.writeAll("//"); } } - if (options.authority) { if (options.authentication and uri.host != null) { if (uri.user) |user| { - try writer.writeAll(user); + try writer.print("{user}", .{user}); if (uri.password) |password| { - try writer.writeAll(":"); - try writer.writeAll(password); + try writer.print(":{password}", .{password}); } - try writer.writeAll("@"); + try writer.writeByte('@'); } } - if (uri.host) |host| { - try writer.writeAll(host); - - if (uri.port) |port| { - try writer.writeAll(":"); - try std.fmt.formatInt(port, 10, .lower, .{}, writer); + try writer.print("{host}", .{host}); + if (options.port) { + if (uri.port) |port| try writer.print(":{d}", .{port}); } } } - if (options.path) { - if (uri.path.len == 0) { - try writer.writeAll("/"); - } else if (options.raw) { - try writer.writeAll(uri.path); - } else { - try writeEscapedPath(writer, uri.path); + try writer.print("{path}", .{ + if (uri.path.isEmpty()) Uri.Component{ .percent_encoded = "/" } else uri.path, + }); + if (options.query) { + if (uri.query) |query| try writer.print("?{query}", .{query}); + } + if (options.fragment) { + if (uri.fragment) |fragment| try writer.print("#{fragment}", .{fragment}); } - - if (options.query) if (uri.query) |q| { - try writer.writeAll("?"); - if (options.raw) { - try writer.writeAll(q); - } else { - try writeEscapedQuery(writer, q); - } - }; - - if (options.fragment) if (uri.fragment) |f| { - try writer.writeAll("#"); - if (options.raw) { - try writer.writeAll(f); - } else { - try writeEscapedQuery(writer, f); - } - }; } } pub fn format( uri: Uri, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - _ = options; - - const scheme = comptime std.mem.indexOf(u8, fmt, ";") != null or fmt.len == 0; - const authentication = comptime std.mem.indexOf(u8, fmt, "@") != null or fmt.len == 0; - const authority = comptime std.mem.indexOf(u8, fmt, "+") != null or fmt.len == 0; - const path = comptime std.mem.indexOf(u8, fmt, "/") != null or fmt.len == 0; - const query = comptime std.mem.indexOf(u8, fmt, "?") != null or fmt.len == 0; - const fragment = comptime std.mem.indexOf(u8, fmt, "#") != null or fmt.len == 0; - const raw = comptime std.mem.indexOf(u8, fmt, "r") != null or fmt.len == 0; + const scheme = comptime std.mem.indexOfScalar(u8, fmt_str, ';') != null or fmt_str.len == 0; + const authentication = comptime std.mem.indexOfScalar(u8, fmt_str, '@') != null or fmt_str.len == 0; + const authority = comptime std.mem.indexOfScalar(u8, fmt_str, '+') != null or fmt_str.len == 0; + const path = comptime std.mem.indexOfScalar(u8, fmt_str, '/') != null or fmt_str.len == 0; + const query = comptime std.mem.indexOfScalar(u8, fmt_str, '?') != null or fmt_str.len == 0; + const fragment = comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null or fmt_str.len == 0; return writeToStream(uri, .{ .scheme = scheme, @@ -334,12 +308,11 @@ pub fn format( .path = path, .query = query, .fragment = fragment, - .raw = raw, }, writer); } /// Parses the URI or returns an error. -/// The return value will contain unescaped strings pointing into the +/// The return value will contain strings pointing into the /// original `text`. Each component that is provided, will be non-`null`. pub fn parse(text: []const u8) ParseError!Uri { var reader: SliceReader = .{ .slice = text }; @@ -353,42 +326,32 @@ pub fn parse(text: []const u8) ParseError!Uri { return error.InvalidFormat; } - var uri = try parseWithoutScheme(reader.readUntilEof()); - uri.scheme = scheme; - - return uri; + return parseAfterScheme(scheme, reader.readUntilEof()); } -pub const ResolveInplaceError = ParseError || error{OutOfMemory}; +pub const ResolveInPlaceError = ParseError || error{NoSpaceLeft}; /// Resolves a URI against a base URI, conforming to RFC 3986, Section 5. -/// Copies `new` to the beginning of `aux_buf`, allowing the slices to overlap, +/// Copies `new` to the beginning of `aux_buf.*`, allowing the slices to overlap, /// then parses `new` as a URI, and then resolves the path in place. /// If a merge needs to take place, the newly constructed path will be stored -/// in `aux_buf` just after the copied `new`. -pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplaceError!Uri { - std.mem.copyForwards(u8, aux_buf, new); +/// in `aux_buf.*` just after the copied `new`, and `aux_buf.*` will be modified +/// to only contain the remaining unused space. +pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: *[]u8) ResolveInPlaceError!Uri { + std.mem.copyForwards(u8, aux_buf.*, new); // At this point, new is an invalid pointer. - const new_mut = aux_buf[0..new.len]; - - const new_parsed, const has_scheme = p: { - break :p .{ - parse(new_mut) catch |first_err| { - break :p .{ - parseWithoutScheme(new_mut) catch return first_err, - false, - }; - }, - true, - }; - }; + const new_mut = aux_buf.*[0..new.len]; + aux_buf.* = aux_buf.*[new.len..]; + const new_parsed = parse(new_mut) catch |err| + (parseAfterScheme("", new_mut) catch return err); // As you can see above, `new_mut` is not a const pointer. - const new_path: []u8 = @constCast(new_parsed.path); + const new_path: []u8 = @constCast(new_parsed.path.percent_encoded); - if (has_scheme) return .{ + if (new_parsed.scheme.len > 0) return .{ .scheme = new_parsed.scheme, .user = new_parsed.user, + .password = new_parsed.password, .host = new_parsed.host, .port = new_parsed.port, .path = remove_dot_segments(new_path), @@ -399,6 +362,7 @@ pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplace if (new_parsed.host) |host| return .{ .scheme = base.scheme, .user = new_parsed.user, + .password = new_parsed.password, .host = host, .port = new_parsed.port, .path = remove_dot_segments(new_path), @@ -406,28 +370,21 @@ pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplace .fragment = new_parsed.fragment, }; - const path, const query = b: { - if (new_path.len == 0) - break :b .{ - base.path, - new_parsed.query orelse base.query, - }; - - if (new_path[0] == '/') - break :b .{ - remove_dot_segments(new_path), - new_parsed.query, - }; - - break :b .{ - try merge_paths(base.path, new_path, aux_buf[new_mut.len..]), - new_parsed.query, - }; + const path, const query = if (new_path.len == 0) .{ + base.path, + new_parsed.query orelse base.query, + } else if (new_path[0] == '/') .{ + remove_dot_segments(new_path), + new_parsed.query, + } else .{ + try merge_paths(base.path, new_path, aux_buf), + new_parsed.query, }; return .{ .scheme = base.scheme, .user = base.user, + .password = base.password, .host = base.host, .port = base.port, .path = path, @@ -437,7 +394,7 @@ pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplace } /// In-place implementation of RFC 3986, Section 5.2.4. -fn remove_dot_segments(path: []u8) []u8 { +fn remove_dot_segments(path: []u8) Component { var in_i: usize = 0; var out_i: usize = 0; while (in_i < path.len) { @@ -476,28 +433,28 @@ fn remove_dot_segments(path: []u8) []u8 { } } } - return path[0..out_i]; + return .{ .percent_encoded = path[0..out_i] }; } test remove_dot_segments { { var buffer = "/a/b/c/./../../g".*; - try std.testing.expectEqualStrings("/a/g", remove_dot_segments(&buffer)); + try std.testing.expectEqualStrings("/a/g", remove_dot_segments(&buffer).percent_encoded); } } /// 5.2.3. Merge Paths -fn merge_paths(base: []const u8, new: []u8, aux: []u8) error{OutOfMemory}![]u8 { - if (aux.len < base.len + 1 + new.len) return error.OutOfMemory; - if (base.len == 0) { - aux[0] = '/'; - @memcpy(aux[1..][0..new.len], new); - return remove_dot_segments(aux[0 .. new.len + 1]); +fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component { + var aux = std.io.fixedBufferStream(aux_buf.*); + if (!base.isEmpty()) { + try aux.writer().print("{path}", .{base}); + aux.pos = std.mem.lastIndexOfScalar(u8, aux.getWritten(), '/') orelse + return remove_dot_segments(new); } - const pos = std.mem.lastIndexOfScalar(u8, base, '/') orelse return remove_dot_segments(new); - @memcpy(aux[0 .. pos + 1], base[0 .. pos + 1]); - @memcpy(aux[pos + 1 ..][0..new.len], new); - return remove_dot_segments(aux[0 .. pos + 1 + new.len]); + try aux.writer().print("/{s}", .{new}); + const merged_path = remove_dot_segments(aux.getWritten()); + aux_buf.* = aux_buf.*[merged_path.percent_encoded.len..]; + return merged_path; } const SliceReader = struct { @@ -561,13 +518,6 @@ fn isSchemeChar(c: u8) bool { }; } -fn isAuthoritySeparator(c: u8) bool { - return switch (c) { - '/', '?', '#' => true, - else => false, - }; -} - /// reserved = gen-delims / sub-delims fn isReserved(c: u8) bool { return isGenLimit(c) or isSubLimit(c); @@ -598,19 +548,40 @@ fn isUnreserved(c: u8) bool { }; } -fn isPathSeparator(c: u8) bool { - return switch (c) { - '?', '#' => true, - else => false, - }; +fn isUserChar(c: u8) bool { + return isUnreserved(c) or isSubLimit(c); +} + +fn isPasswordChar(c: u8) bool { + return isUserChar(c) or c == ':'; +} + +fn isHostChar(c: u8) bool { + return isPasswordChar(c) or c == '[' or c == ']'; } fn isPathChar(c: u8) bool { - return isUnreserved(c) or isSubLimit(c) or c == '/' or c == ':' or c == '@'; + return isUserChar(c) or c == '/' or c == ':' or c == '@'; } fn isQueryChar(c: u8) bool { - return isPathChar(c) or c == '?' or c == '%'; + return isPathChar(c) or c == '?'; +} + +const isFragmentChar = isQueryChar; + +fn isAuthoritySeparator(c: u8) bool { + return switch (c) { + '/', '?', '#' => true, + else => false, + }; +} + +fn isPathSeparator(c: u8) bool { + return switch (c) { + '?', '#' => true, + else => false, + }; } fn isQuerySeparator(c: u8) bool { @@ -623,92 +594,92 @@ fn isQuerySeparator(c: u8) bool { test "basic" { const parsed = try parse("https://ziglang.org/download"); try testing.expectEqualStrings("https", parsed.scheme); - try testing.expectEqualStrings("ziglang.org", parsed.host orelse return error.UnexpectedNull); - try testing.expectEqualStrings("/download", parsed.path); + try testing.expectEqualStrings("ziglang.org", parsed.host.?.percent_encoded); + try testing.expectEqualStrings("/download", parsed.path.percent_encoded); try testing.expectEqual(@as(?u16, null), parsed.port); } test "with port" { const parsed = try parse("http://example:1337/"); try testing.expectEqualStrings("http", parsed.scheme); - try testing.expectEqualStrings("example", parsed.host orelse return error.UnexpectedNull); - try testing.expectEqualStrings("/", parsed.path); + try testing.expectEqualStrings("example", parsed.host.?.percent_encoded); + try testing.expectEqualStrings("/", parsed.path.percent_encoded); try testing.expectEqual(@as(?u16, 1337), parsed.port); } test "should fail gracefully" { - try std.testing.expectEqual(@as(ParseError!Uri, error.InvalidFormat), parse("foobar://")); + try std.testing.expectError(error.InvalidFormat, parse("foobar://")); } test "file" { const parsed = try parse("file:///"); - try std.testing.expectEqualSlices(u8, "file", parsed.scheme); - try std.testing.expectEqual(@as(?[]const u8, null), parsed.host); - try std.testing.expectEqualSlices(u8, "/", parsed.path); + try std.testing.expectEqualStrings("file", parsed.scheme); + try std.testing.expectEqual(@as(?Component, null), parsed.host); + try std.testing.expectEqualStrings("/", parsed.path.percent_encoded); const parsed2 = try parse("file:///an/absolute/path/to/something"); - try std.testing.expectEqualSlices(u8, "file", parsed2.scheme); - try std.testing.expectEqual(@as(?[]const u8, null), parsed2.host); - try std.testing.expectEqualSlices(u8, "/an/absolute/path/to/something", parsed2.path); + try std.testing.expectEqualStrings("file", parsed2.scheme); + try std.testing.expectEqual(@as(?Component, null), parsed2.host); + try std.testing.expectEqualStrings("/an/absolute/path/to/something", parsed2.path.percent_encoded); const parsed3 = try parse("file://localhost/an/absolute/path/to/another/thing/"); - try std.testing.expectEqualSlices(u8, "file", parsed3.scheme); - try std.testing.expectEqualSlices(u8, "localhost", parsed3.host.?); - try std.testing.expectEqualSlices(u8, "/an/absolute/path/to/another/thing/", parsed3.path); + try std.testing.expectEqualStrings("file", parsed3.scheme); + try std.testing.expectEqualStrings("localhost", parsed3.host.?.percent_encoded); + try std.testing.expectEqualStrings("/an/absolute/path/to/another/thing/", parsed3.path.percent_encoded); } test "scheme" { - try std.testing.expectEqualSlices(u8, "http", (try parse("http:_")).scheme); - try std.testing.expectEqualSlices(u8, "scheme-mee", (try parse("scheme-mee:_")).scheme); - try std.testing.expectEqualSlices(u8, "a.b.c", (try parse("a.b.c:_")).scheme); - try std.testing.expectEqualSlices(u8, "ab+", (try parse("ab+:_")).scheme); - try std.testing.expectEqualSlices(u8, "X+++", (try parse("X+++:_")).scheme); - try std.testing.expectEqualSlices(u8, "Y+-.", (try parse("Y+-.:_")).scheme); + try std.testing.expectEqualStrings("http", (try parse("http:_")).scheme); + try std.testing.expectEqualStrings("scheme-mee", (try parse("scheme-mee:_")).scheme); + try std.testing.expectEqualStrings("a.b.c", (try parse("a.b.c:_")).scheme); + try std.testing.expectEqualStrings("ab+", (try parse("ab+:_")).scheme); + try std.testing.expectEqualStrings("X+++", (try parse("X+++:_")).scheme); + try std.testing.expectEqualStrings("Y+-.", (try parse("Y+-.:_")).scheme); } test "authority" { - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://hostname")).host.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://hostname")).host.?.percent_encoded); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://userinfo@hostname")).host.?); - try std.testing.expectEqualSlices(u8, "userinfo", (try parse("scheme://userinfo@hostname")).user.?); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://userinfo@hostname")).password); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://userinfo@")).host); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://userinfo@hostname")).host.?.percent_encoded); + try std.testing.expectEqualStrings("userinfo", (try parse("scheme://userinfo@hostname")).user.?.percent_encoded); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://userinfo@hostname")).password); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://userinfo@")).host); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://user:password@hostname")).host.?); - try std.testing.expectEqualSlices(u8, "user", (try parse("scheme://user:password@hostname")).user.?); - try std.testing.expectEqualSlices(u8, "password", (try parse("scheme://user:password@hostname")).password.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://user:password@hostname")).host.?.percent_encoded); + try std.testing.expectEqualStrings("user", (try parse("scheme://user:password@hostname")).user.?.percent_encoded); + try std.testing.expectEqualStrings("password", (try parse("scheme://user:password@hostname")).password.?.percent_encoded); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://hostname:0")).host.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://hostname:0")).host.?.percent_encoded); try std.testing.expectEqual(@as(u16, 1234), (try parse("scheme://hostname:1234")).port.?); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://userinfo@hostname:1234")).host.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://userinfo@hostname:1234")).host.?.percent_encoded); try std.testing.expectEqual(@as(u16, 1234), (try parse("scheme://userinfo@hostname:1234")).port.?); - try std.testing.expectEqualSlices(u8, "userinfo", (try parse("scheme://userinfo@hostname:1234")).user.?); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://userinfo@hostname:1234")).password); + try std.testing.expectEqualStrings("userinfo", (try parse("scheme://userinfo@hostname:1234")).user.?.percent_encoded); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://userinfo@hostname:1234")).password); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://user:password@hostname:1234")).host.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://user:password@hostname:1234")).host.?.percent_encoded); try std.testing.expectEqual(@as(u16, 1234), (try parse("scheme://user:password@hostname:1234")).port.?); - try std.testing.expectEqualSlices(u8, "user", (try parse("scheme://user:password@hostname:1234")).user.?); - try std.testing.expectEqualSlices(u8, "password", (try parse("scheme://user:password@hostname:1234")).password.?); + try std.testing.expectEqualStrings("user", (try parse("scheme://user:password@hostname:1234")).user.?.percent_encoded); + try std.testing.expectEqualStrings("password", (try parse("scheme://user:password@hostname:1234")).password.?.percent_encoded); } test "authority.password" { - try std.testing.expectEqualSlices(u8, "username", (try parse("scheme://username@a")).user.?); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://username@a")).password); + try std.testing.expectEqualStrings("username", (try parse("scheme://username@a")).user.?.percent_encoded); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://username@a")).password); - try std.testing.expectEqualSlices(u8, "username", (try parse("scheme://username:@a")).user.?); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://username:@a")).password); + try std.testing.expectEqualStrings("username", (try parse("scheme://username:@a")).user.?.percent_encoded); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://username:@a")).password); - try std.testing.expectEqualSlices(u8, "username", (try parse("scheme://username:password@a")).user.?); - try std.testing.expectEqualSlices(u8, "password", (try parse("scheme://username:password@a")).password.?); + try std.testing.expectEqualStrings("username", (try parse("scheme://username:password@a")).user.?.percent_encoded); + try std.testing.expectEqualStrings("password", (try parse("scheme://username:password@a")).password.?.percent_encoded); - try std.testing.expectEqualSlices(u8, "username", (try parse("scheme://username::@a")).user.?); - try std.testing.expectEqualSlices(u8, ":", (try parse("scheme://username::@a")).password.?); + try std.testing.expectEqualStrings("username", (try parse("scheme://username::@a")).user.?.percent_encoded); + try std.testing.expectEqualStrings(":", (try parse("scheme://username::@a")).password.?.percent_encoded); } fn testAuthorityHost(comptime hostlist: anytype) !void { inline for (hostlist) |hostname| { - try std.testing.expectEqualSlices(u8, hostname, (try parse("scheme://" ++ hostname)).host.?); + try std.testing.expectEqualStrings(hostname, (try parse("scheme://" ++ hostname)).host.?.percent_encoded); } } @@ -761,11 +732,11 @@ test "RFC example 1" { .scheme = uri[0..3], .user = null, .password = null, - .host = uri[6..17], + .host = .{ .percent_encoded = uri[6..17] }, .port = 8042, - .path = uri[22..33], - .query = uri[34..45], - .fragment = uri[46..50], + .path = .{ .percent_encoded = uri[22..33] }, + .query = .{ .percent_encoded = uri[34..45] }, + .fragment = .{ .percent_encoded = uri[46..50] }, }, try parse(uri)); } @@ -777,7 +748,7 @@ test "RFC example 2" { .password = null, .host = null, .port = null, - .path = uri[4..], + .path = .{ .percent_encoded = uri[4..] }, .query = null, .fragment = null, }, try parse(uri)); @@ -838,55 +809,60 @@ test "Special test" { _ = try parse("https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be&t=0"); } -test "URI escaping" { - const input = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; - const expected = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad"; +test "URI percent encoding" { + try std.testing.expectFmt( + "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad", + "{%}", + .{Component{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }}, + ); +} - const actual = try escapeString(std.testing.allocator, input); - defer std.testing.allocator.free(actual); +test "URI percent decoding" { + { + const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; + var input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad".*; - try std.testing.expectEqualSlices(u8, expected, actual); -} + try std.testing.expectFmt(expected, "{raw}", .{Component{ .percent_encoded = &input }}); + + var output: [expected.len]u8 = undefined; + try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); + + try std.testing.expectEqualStrings(expected, percentDecodeInPlace(&input)); + } -test "URI unescaping" { - const input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad"; - const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; + { + const expected = "/abc%"; + var input = expected.*; - const actual = try unescapeString(std.testing.allocator, input); - defer std.testing.allocator.free(actual); + try std.testing.expectFmt(expected, "{raw}", .{Component{ .percent_encoded = &input }}); - try std.testing.expectEqualSlices(u8, expected, actual); + var output: [expected.len]u8 = undefined; + try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); - const decoded = try unescapeString(std.testing.allocator, "/abc%"); - defer std.testing.allocator.free(decoded); - try std.testing.expectEqualStrings("/abc%", decoded); + try std.testing.expectEqualStrings(expected, percentDecodeInPlace(&input)); + } } -test "URI query escaping" { +test "URI query encoding" { const address = "https://objects.githubusercontent.com/?response-content-type=application%2Foctet-stream"; const parsed = try Uri.parse(address); - // format the URI to escape it - const formatted_uri = try std.fmt.allocPrint(std.testing.allocator, "{/?}", .{parsed}); - defer std.testing.allocator.free(formatted_uri); - try std.testing.expectEqualStrings("/?response-content-type=application%2Foctet-stream", formatted_uri); + // format the URI to percent encode it + try std.testing.expectFmt("/?response-content-type=application%2Foctet-stream", "{/?}", .{parsed}); } test "format" { - const uri = Uri{ + const uri: Uri = .{ .scheme = "file", .user = null, .password = null, .host = null, .port = null, - .path = "/foo/bar/baz", + .path = .{ .raw = "/foo/bar/baz" }, .query = null, .fragment = null, }; - var buf = std.ArrayList(u8).init(std.testing.allocator); - defer buf.deinit(); - try buf.writer().print("{;/?#}", .{uri}); - try std.testing.expectEqualSlices(u8, "file:/foo/bar/baz", buf.items); + try std.testing.expectFmt("file:/foo/bar/baz", "{;/?#}", .{uri}); } test "URI malformed input" { @@ -894,3 +870,7 @@ test "URI malformed input" { try std.testing.expectError(error.InvalidFormat, std.Uri.parse("http://]@[")); try std.testing.expectError(error.InvalidFormat, std.Uri.parse("http://lo]s\x85hc@[/8\x10?0Q")); } + +const std = @import("std.zig"); +const testing = std.testing; +const Uri = @This(); diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index d75dd3bf00..cd2e4bc296 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -5,28 +5,18 @@ const maxInt = std.math.maxInt; const iovec = std.posix.iovec; const iovec_const = std.posix.iovec_const; -extern "c" fn _errnop() *c_int; +pub extern "root" fn find_directory(which: directory_which, volume: i32, createIt: bool, path_ptr: [*]u8, length: i32) u64; -pub const _errno = _errnop; - -pub extern "c" fn find_directory(which: directory_which, volume: i32, createIt: bool, path_ptr: [*]u8, length: i32) u64; - -pub extern "c" fn find_thread(thread_name: ?*anyopaque) i32; +pub extern "root" fn find_thread(thread_name: ?*anyopaque) i32; -pub extern "c" fn get_system_info(system_info: *system_info) usize; +pub extern "root" fn get_system_info(system_info: *system_info) usize; -pub extern "c" fn _get_team_info(team: c_int, team_info: *team_info, size: usize) i32; +pub extern "root" fn _get_team_info(team: i32, team_info: *team_info, size: usize) i32; -pub extern "c" fn _get_next_area_info(team: c_int, cookie: *i64, area_info: *area_info, size: usize) i32; +pub extern "root" fn _get_next_area_info(team: i32, cookie: *i64, area_info: *area_info, size: usize) i32; // TODO revisit if abi changes or better option becomes apparent -pub extern "c" fn _get_next_image_info(team: c_int, cookie: *i32, image_info: *image_info, size: usize) i32; - -pub extern "c" fn _kern_read_dir(fd: c_int, buf_ptr: [*]u8, nbytes: usize, maxcount: u32) usize; - -pub extern "c" fn _kern_read_stat(fd: c_int, path_ptr: [*]u8, traverse_link: bool, st: *Stat, stat_size: i32) usize; - -pub extern "c" fn _kern_get_current_team() i32; +pub extern "root" fn _get_next_image_info(team: i32, cookie: *i32, image_info: *image_info, size: usize) i32; pub const sem_t = extern struct { type: i32, @@ -45,7 +35,7 @@ pub const pthread_attr_t = extern struct { __stack_address: ?*anyopaque, }; -pub const EAI = enum(c_int) { +pub const EAI = enum(i32) { /// address family for hostname not supported ADDRFAMILY = 1, @@ -99,11 +89,7 @@ pub const AI = struct { pub const AI_NUMERICSERV = AI.NUMERICSERV; -pub const fd_t = c_int; -pub const pid_t = c_int; -pub const uid_t = u32; -pub const gid_t = u32; -pub const mode_t = c_uint; +pub const fd_t = i32; pub const socklen_t = u32; @@ -129,8 +115,8 @@ pub const dl_phdr_info = extern struct { }; pub const Flock = extern struct { - type: c_short, - whence: c_short, + type: i16, + whence: i16, start: off_t, len: off_t, pid: pid_t, @@ -159,54 +145,6 @@ pub const msghdr = extern struct { msg_flags: i32, }; -pub const off_t = i64; -pub const ino_t = u64; - -pub const Stat = extern struct { - dev: i32, - ino: u64, - mode: u32, - nlink: i32, - uid: i32, - gid: i32, - size: i64, - rdev: i32, - blksize: i32, - atim: timespec, - mtim: timespec, - ctim: timespec, - crtim: timespec, - st_type: u32, - blocks: i64, - - pub fn atime(self: @This()) timespec { - return self.atim; - } - pub fn mtime(self: @This()) timespec { - return self.mtim; - } - pub fn ctime(self: @This()) timespec { - return self.ctim; - } - pub fn birthtime(self: @This()) timespec { - return self.crtim; - } -}; - -pub const timespec = extern struct { - tv_sec: isize, - tv_nsec: isize, -}; - -pub const dirent = extern struct { - dev: i32, - pdev: i32, - ino: i64, - pino: i64, - reclen: u16, - name: [256]u8, -}; - pub const B_OS_NAME_LENGTH = 32; // OS.h pub const area_info = extern struct { @@ -354,17 +292,6 @@ pub const PROT = struct { pub const NONE = 0x00; }; -pub const CLOCK = struct { - /// system-wide monotonic clock (aka system time) - pub const MONOTONIC = 0; - /// system-wide real time clock - pub const REALTIME = -1; - /// clock measuring the used CPU time of the current process - pub const PROCESS_CPUTIME_ID = -2; - /// clock measuring the used CPU time of the current thread - pub const THREAD_CPUTIME_ID = -3; -}; - pub const MSF = struct { pub const ASYNC = 1; pub const INVALIDATE = 2; @@ -404,159 +331,526 @@ pub const W = struct { } }; -// /system/develop/headers/posix/poll.h +// access function +pub const F_OK = 0; // test for existence of file +pub const X_OK = 1; // test for execute or search permission +pub const W_OK = 2; // test for write permission +pub const R_OK = 4; // test for read permission -pub const nfds_t = usize; +pub const F = struct { + pub const DUPFD = 0x0001; + pub const GETFD = 0x0002; + pub const SETFD = 0x0004; + pub const GETFL = 0x0008; + pub const SETFL = 0x0010; -pub const pollfd = extern struct { - fd: i32, - events: i16, - revents: i16, -}; + pub const GETLK = 0x0020; + pub const SETLK = 0x0080; + pub const SETLKW = 0x0100; + pub const DUPFD_CLOEXEC = 0x0200; -pub const POLL = struct { - /// any readable data available - pub const IN = 0x0001; - /// file descriptor is writeable - pub const OUT = 0x0002; - pub const RDNORM = IN; - pub const WRNORM = OUT; - /// priority readable data - pub const RDBAND = 0x0008; - /// priority data can be written - pub const WRBAND = 0x0010; - /// high priority readable data - pub const PRI = 0x0020; + pub const RDLCK = 0x0040; + pub const UNLCK = 0x0200; + pub const WRLCK = 0x0400; +}; - /// errors pending - pub const ERR = 0x0004; - /// disconnected - pub const HUP = 0x0080; - /// invalid file descriptor - pub const NVAL = 0x1000; +pub const LOCK = struct { + pub const SH = 0x01; + pub const EX = 0x02; + pub const NB = 0x04; + pub const UN = 0x08; }; -// /system/develop/headers/posix/signal.h +pub const FD_CLOEXEC = 1; -pub const sigset_t = u64; -pub const empty_sigset: sigset_t = 0; -pub const filled_sigset = ~@as(sigset_t, 0); +pub const SEEK = struct { + pub const SET = 0; + pub const CUR = 1; + pub const END = 2; +}; -pub const SIG = struct { - pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); - pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); - pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); +pub const SOCK = struct { + pub const STREAM = 1; + pub const DGRAM = 2; + pub const RAW = 3; + pub const SEQPACKET = 5; - pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(3); + /// WARNING: this flag is not supported by windows socket functions directly, + /// it is only supported by std.os.socket. Be sure that this value does + /// not share any bits with any of the `SOCK` values. + pub const CLOEXEC = 0x10000; + /// WARNING: this flag is not supported by windows socket functions directly, + /// it is only supported by std.os.socket. Be sure that this value does + /// not share any bits with any of the `SOCK` values. + pub const NONBLOCK = 0x20000; +}; - pub const HUP = 1; - pub const INT = 2; - pub const QUIT = 3; - pub const ILL = 4; - pub const CHLD = 5; - pub const ABRT = 6; - pub const IOT = ABRT; - pub const PIPE = 7; - pub const FPE = 8; - pub const KILL = 9; - pub const STOP = 10; - pub const SEGV = 11; - pub const CONT = 12; - pub const TSTP = 13; - pub const ALRM = 14; - pub const TERM = 15; - pub const TTIN = 16; - pub const TTOU = 17; - pub const USR1 = 18; - pub const USR2 = 19; - pub const WINCH = 20; - pub const KILLTHR = 21; - pub const TRAP = 22; - pub const POLL = 23; - pub const PROF = 24; - pub const SYS = 25; - pub const URG = 26; - pub const VTALRM = 27; - pub const XCPU = 28; - pub const XFSZ = 29; - pub const BUS = 30; - pub const RESERVED1 = 31; - pub const RESERVED2 = 32; +pub const SO = struct { + pub const ACCEPTCONN = 0x00000001; + pub const BROADCAST = 0x00000002; + pub const DEBUG = 0x00000004; + pub const DONTROUTE = 0x00000008; + pub const KEEPALIVE = 0x00000010; + pub const OOBINLINE = 0x00000020; + pub const REUSEADDR = 0x00000040; + pub const REUSEPORT = 0x00000080; + pub const USELOOPBACK = 0x00000100; + pub const LINGER = 0x00000200; - pub const BLOCK = 1; - pub const UNBLOCK = 2; - pub const SETMASK = 3; + pub const SNDBUF = 0x40000001; + pub const SNDLOWAT = 0x40000002; + pub const SNDTIMEO = 0x40000003; + pub const RCVBUF = 0x40000004; + pub const RCVLOWAT = 0x40000005; + pub const RCVTIMEO = 0x40000006; + pub const ERROR = 0x40000007; + pub const TYPE = 0x40000008; + pub const NONBLOCK = 0x40000009; + pub const BINDTODEVICE = 0x4000000a; + pub const PEERCRED = 0x4000000b; }; -pub const siginfo_t = extern struct { - signo: c_int, - code: c_int, - errno: c_int, +pub const SOL = struct { + pub const SOCKET = -1; +}; - pid: pid_t, - uid: uid_t, - addr: *allowzero anyopaque, +pub const PF = struct { + pub const UNSPEC = AF.UNSPEC; + pub const INET = AF.INET; + pub const ROUTE = AF.ROUTE; + pub const LINK = AF.LINK; + pub const INET6 = AF.INET6; + pub const LOCAL = AF.LOCAL; + pub const UNIX = AF.UNIX; + pub const BLUETOOTH = AF.BLUETOOTH; }; -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = extern struct { - pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; - pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.C) void; +pub const AF = struct { + pub const UNSPEC = 0; + pub const INET = 1; + pub const APPLETALK = 2; + pub const ROUTE = 3; + pub const LINK = 4; + pub const INET6 = 5; + pub const DLI = 6; + pub const IPX = 7; + pub const NOTIFY = 8; + pub const LOCAL = 9; + pub const UNIX = LOCAL; + pub const BLUETOOTH = 10; + pub const MAX = 11; +}; - /// signal handler - handler: extern union { - handler: handler_fn, - sigaction: sigaction_fn, - }, +pub const DT = struct {}; - /// signal mask to apply - mask: sigset_t, +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; - /// see signal options - flags: c_int, +/// delete event from kq +pub const EV_DELETE = 0x0002; - /// will be passed to the signal handler, BeOS extension - userdata: *allowzero anyopaque = undefined, +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +pub const EVFILT_READ = -1; +pub const EVFILT_WRITE = -2; + +/// attached to aio requests +pub const EVFILT_AIO = -3; + +/// attached to vnodes +pub const EVFILT_VNODE = -4; + +/// attached to struct proc +pub const EVFILT_PROC = -5; + +/// attached to struct proc +pub const EVFILT_SIGNAL = -6; + +/// timers +pub const EVFILT_TIMER = -7; + +/// Process descriptors +pub const EVFILT_PROCDESC = -8; + +/// Filesystem events +pub const EVFILT_FS = -9; + +pub const EVFILT_LIO = -10; + +/// User events +pub const EVFILT_USER = -11; + +/// Sendfile events +pub const EVFILT_SENDFILE = -12; + +pub const EVFILT_EMPTY = -13; + +pub const T = struct { + pub const CGETA = 0x8000; + pub const CSETA = 0x8001; + pub const CSETAF = 0x8002; + pub const CSETAW = 0x8003; + pub const CWAITEVENT = 0x8004; + pub const CSBRK = 0x8005; + pub const CFLSH = 0x8006; + pub const CXONC = 0x8007; + pub const CQUERYCONNECTED = 0x8008; + pub const CGETBITS = 0x8009; + pub const CSETDTR = 0x8010; + pub const CSETRTS = 0x8011; + pub const IOCGWINSZ = 0x8012; + pub const IOCSWINSZ = 0x8013; + pub const CVTIME = 0x8014; + pub const IOCGPGRP = 0x8015; + pub const IOCSPGRP = 0x8016; + pub const IOCSCTTY = 0x8017; + pub const IOCMGET = 0x8018; + pub const IOCMSET = 0x8019; + pub const IOCSBRK = 0x8020; + pub const IOCCBRK = 0x8021; + pub const IOCMBIS = 0x8022; + pub const IOCMBIC = 0x8023; + pub const IOCGSID = 0x8024; + + pub const FIONREAD = 0xbe000001; + pub const FIONBIO = 0xbe000000; }; -pub const SA = struct { - pub const NOCLDSTOP = 0x01; - pub const NOCLDWAIT = 0x02; - pub const RESETHAND = 0x04; - pub const NODEFER = 0x08; - pub const RESTART = 0x10; - pub const ONSTACK = 0x20; - pub const SIGINFO = 0x40; - pub const NOMASK = NODEFER; - pub const STACK = ONSTACK; - pub const ONESHOT = RESETHAND; +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, }; -pub const SS = struct { - pub const ONSTACK = 0x1; - pub const DISABLE = 0x2; +pub const S = struct { + pub const IFMT = 0o170000; + pub const IFSOCK = 0o140000; + pub const IFLNK = 0o120000; + pub const IFREG = 0o100000; + pub const IFBLK = 0o060000; + pub const IFDIR = 0o040000; + pub const IFCHR = 0o020000; + pub const IFIFO = 0o010000; + pub const INDEX_DIR = 0o4000000000; + + pub const IUMSK = 0o7777; + pub const ISUID = 0o4000; + pub const ISGID = 0o2000; + pub const ISVTX = 0o1000; + pub const IRWXU = 0o700; + pub const IRUSR = 0o400; + pub const IWUSR = 0o200; + pub const IXUSR = 0o100; + pub const IRWXG = 0o070; + pub const IRGRP = 0o040; + pub const IWGRP = 0o020; + pub const IXGRP = 0o010; + pub const IRWXO = 0o007; + pub const IROTH = 0o004; + pub const IWOTH = 0o002; + pub const IXOTH = 0o001; + + pub fn ISREG(m: u32) bool { + return m & IFMT == IFREG; + } + + pub fn ISLNK(m: u32) bool { + return m & IFMT == IFLNK; + } + + pub fn ISBLK(m: u32) bool { + return m & IFMT == IFBLK; + } + + pub fn ISDIR(m: u32) bool { + return m & IFMT == IFDIR; + } + + pub fn ISCHR(m: u32) bool { + return m & IFMT == IFCHR; + } + + pub fn ISFIFO(m: u32) bool { + return m & IFMT == IFIFO; + } + + pub fn ISSOCK(m: u32) bool { + return m & IFMT == IFSOCK; + } + + pub fn ISINDEX(m: u32) bool { + return m & INDEX_DIR == INDEX_DIR; + } }; -pub const MINSIGSTKSZ = 8192; -pub const SIGSTKSZ = 16384; +pub const HOST_NAME_MAX = 255; -pub const stack_t = extern struct { - sp: [*]u8, - size: isize, +pub const addrinfo = extern struct { flags: i32, + family: i32, + socktype: i32, + protocol: i32, + addrlen: socklen_t, + canonname: ?[*:0]u8, + addr: ?*sockaddr, + next: ?*addrinfo, }; -pub const NSIG = 65; +pub const IPPROTO = struct { + pub const IP = 0; + pub const HOPOPTS = 0; + pub const ICMP = 1; + pub const IGMP = 2; + pub const TCP = 6; + pub const UDP = 17; + pub const IPV6 = 41; + pub const ROUTING = 43; + pub const FRAGMENT = 44; + pub const ESP = 50; + pub const AH = 51; + pub const ICMPV6 = 58; + pub const NONE = 59; + pub const DSTOPTS = 60; + pub const ETHERIP = 97; + pub const RAW = 255; + pub const MAX = 256; +}; -pub const mcontext_t = vregs; +pub const rlimit_resource = enum(i32) { + CORE = 0, + CPU = 1, + DATA = 2, + FSIZE = 3, + NOFILE = 4, + STACK = 5, + AS = 6, + NOVMON = 7, + _, +}; -pub const ucontext_t = extern struct { - link: ?*ucontext_t, - sigmask: sigset_t, - stack: stack_t, - mcontext: mcontext_t, +pub const rlim_t = i64; + +pub const RLIM = struct { + /// No limit + pub const INFINITY: rlim_t = (1 << 63) - 1; + + pub const SAVED_MAX = INFINITY; + pub const SAVED_CUR = INFINITY; +}; + +pub const rlimit = extern struct { + /// Soft limit + cur: rlim_t, + /// Hard limit + max: rlim_t, +}; + +pub const SHUT = struct { + pub const RD = 0; + pub const WR = 1; + pub const RDWR = 2; +}; + +// TODO fill out if needed +pub const directory_which = enum(i32) { + B_USER_SETTINGS_DIRECTORY = 0xbbe, + + _, +}; + +pub const MSG_NOSIGNAL = 0x0800; + +// /system/develop/headers/os/kernel/OS.h + +pub const area_id = i32; +pub const port_id = i32; +pub const sem_id = i32; +pub const team_id = i32; +pub const thread_id = i32; + +// /system/develop/headers/os/support/Errors.h + +pub const E = enum(i32) { + pub const B_GENERAL_ERROR_BASE: i32 = std.math.minInt(i32); + pub const B_OS_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x1000; + pub const B_APP_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x2000; + pub const B_INTERFACE_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x3000; + pub const B_MEDIA_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x4000; + pub const B_TRANSLATION_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x4800; + pub const B_MIDI_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x5000; + pub const B_STORAGE_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x6000; + pub const B_POSIX_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x7000; + pub const B_MAIL_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x8000; + pub const B_PRINT_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x9000; + pub const B_DEVICE_ERROR_BASE = B_GENERAL_ERROR_BASE + 0xa000; + + pub const B_ERRORS_END = B_GENERAL_ERROR_BASE + 0xffff; + + pub const B_NO_MEMORY = B_GENERAL_ERROR_BASE + 0; + pub const B_IO_ERROR = B_GENERAL_ERROR_BASE + 1; + pub const B_PERMISSION_DENIED = B_GENERAL_ERROR_BASE + 2; + pub const B_BAD_INDEX = B_GENERAL_ERROR_BASE + 3; + pub const B_BAD_TYPE = B_GENERAL_ERROR_BASE + 4; + pub const B_BAD_VALUE = B_GENERAL_ERROR_BASE + 5; + pub const B_MISMATCHED_VALUES = B_GENERAL_ERROR_BASE + 6; + pub const B_NAME_NOT_FOUND = B_GENERAL_ERROR_BASE + 7; + pub const B_NAME_IN_USE = B_GENERAL_ERROR_BASE + 8; + pub const B_TIMED_OUT = B_GENERAL_ERROR_BASE + 9; + pub const B_INTERRUPTED = B_GENERAL_ERROR_BASE + 10; + pub const B_WOULD_BLOCK = B_GENERAL_ERROR_BASE + 11; + pub const B_CANCELED = B_GENERAL_ERROR_BASE + 12; + pub const B_NO_INIT = B_GENERAL_ERROR_BASE + 13; + pub const B_NOT_INITIALIZED = B_GENERAL_ERROR_BASE + 13; + pub const B_BUSY = B_GENERAL_ERROR_BASE + 14; + pub const B_NOT_ALLOWED = B_GENERAL_ERROR_BASE + 15; + pub const B_BAD_DATA = B_GENERAL_ERROR_BASE + 16; + pub const B_DONT_DO_THAT = B_GENERAL_ERROR_BASE + 17; + + pub const B_BAD_IMAGE_ID = B_OS_ERROR_BASE + 0x300; + pub const B_BAD_ADDRESS = B_OS_ERROR_BASE + 0x301; + pub const B_NOT_AN_EXECUTABLE = B_OS_ERROR_BASE + 0x302; + pub const B_MISSING_LIBRARY = B_OS_ERROR_BASE + 0x303; + pub const B_MISSING_SYMBOL = B_OS_ERROR_BASE + 0x304; + pub const B_UNKNOWN_EXECUTABLE = B_OS_ERROR_BASE + 0x305; + pub const B_LEGACY_EXECUTABLE = B_OS_ERROR_BASE + 0x306; + + pub const B_FILE_ERROR = B_STORAGE_ERROR_BASE + 0; + pub const B_FILE_EXISTS = B_STORAGE_ERROR_BASE + 2; + pub const B_ENTRY_NOT_FOUND = B_STORAGE_ERROR_BASE + 3; + pub const B_NAME_TOO_LONG = B_STORAGE_ERROR_BASE + 4; + pub const B_NOT_A_DIRECTORY = B_STORAGE_ERROR_BASE + 5; + pub const B_DIRECTORY_NOT_EMPTY = B_STORAGE_ERROR_BASE + 6; + pub const B_DEVICE_FULL = B_STORAGE_ERROR_BASE + 7; + pub const B_READ_ONLY_DEVICE = B_STORAGE_ERROR_BASE + 8; + pub const B_IS_A_DIRECTORY = B_STORAGE_ERROR_BASE + 9; + pub const B_NO_MORE_FDS = B_STORAGE_ERROR_BASE + 10; + pub const B_CROSS_DEVICE_LINK = B_STORAGE_ERROR_BASE + 11; + pub const B_LINK_LIMIT = B_STORAGE_ERROR_BASE + 12; + pub const B_BUSTED_PIPE = B_STORAGE_ERROR_BASE + 13; + pub const B_UNSUPPORTED = B_STORAGE_ERROR_BASE + 14; + pub const B_PARTITION_TOO_SMALL = B_STORAGE_ERROR_BASE + 15; + pub const B_PARTIAL_READ = B_STORAGE_ERROR_BASE + 16; + pub const B_PARTIAL_WRITE = B_STORAGE_ERROR_BASE + 17; + + SUCCESS = 0, + + @"2BIG" = B_POSIX_ERROR_BASE + 1, + CHILD = B_POSIX_ERROR_BASE + 2, + DEADLK = B_POSIX_ERROR_BASE + 3, + FBIG = B_POSIX_ERROR_BASE + 4, + MLINK = B_POSIX_ERROR_BASE + 5, + NFILE = B_POSIX_ERROR_BASE + 6, + NODEV = B_POSIX_ERROR_BASE + 7, + NOLCK = B_POSIX_ERROR_BASE + 8, + NOSYS = B_POSIX_ERROR_BASE + 9, + NOTTY = B_POSIX_ERROR_BASE + 10, + NXIO = B_POSIX_ERROR_BASE + 11, + SPIPE = B_POSIX_ERROR_BASE + 12, + SRCH = B_POSIX_ERROR_BASE + 13, + FPOS = B_POSIX_ERROR_BASE + 14, + SIGPARM = B_POSIX_ERROR_BASE + 15, + DOM = B_POSIX_ERROR_BASE + 16, + RANGE = B_POSIX_ERROR_BASE + 17, + PROTOTYPE = B_POSIX_ERROR_BASE + 18, + PROTONOSUPPORT = B_POSIX_ERROR_BASE + 19, + PFNOSUPPORT = B_POSIX_ERROR_BASE + 20, + AFNOSUPPORT = B_POSIX_ERROR_BASE + 21, + ADDRINUSE = B_POSIX_ERROR_BASE + 22, + ADDRNOTAVAIL = B_POSIX_ERROR_BASE + 23, + NETDOWN = B_POSIX_ERROR_BASE + 24, + NETUNREACH = B_POSIX_ERROR_BASE + 25, + NETRESET = B_POSIX_ERROR_BASE + 26, + CONNABORTED = B_POSIX_ERROR_BASE + 27, + CONNRESET = B_POSIX_ERROR_BASE + 28, + ISCONN = B_POSIX_ERROR_BASE + 29, + NOTCONN = B_POSIX_ERROR_BASE + 30, + SHUTDOWN = B_POSIX_ERROR_BASE + 31, + CONNREFUSED = B_POSIX_ERROR_BASE + 32, + HOSTUNREACH = B_POSIX_ERROR_BASE + 33, + NOPROTOOPT = B_POSIX_ERROR_BASE + 34, + NOBUFS = B_POSIX_ERROR_BASE + 35, + INPROGRESS = B_POSIX_ERROR_BASE + 36, + ALREADY = B_POSIX_ERROR_BASE + 37, + ILSEQ = B_POSIX_ERROR_BASE + 38, + NOMSG = B_POSIX_ERROR_BASE + 39, + STALE = B_POSIX_ERROR_BASE + 40, + OVERFLOW = B_POSIX_ERROR_BASE + 41, + MSGSIZE = B_POSIX_ERROR_BASE + 42, + OPNOTSUPP = B_POSIX_ERROR_BASE + 43, + NOTSOCK = B_POSIX_ERROR_BASE + 44, + HOSTDOWN = B_POSIX_ERROR_BASE + 45, + BADMSG = B_POSIX_ERROR_BASE + 46, + CANCELED = B_POSIX_ERROR_BASE + 47, + DESTADDRREQ = B_POSIX_ERROR_BASE + 48, + DQUOT = B_POSIX_ERROR_BASE + 49, + IDRM = B_POSIX_ERROR_BASE + 50, + MULTIHOP = B_POSIX_ERROR_BASE + 51, + NODATA = B_POSIX_ERROR_BASE + 52, + NOLINK = B_POSIX_ERROR_BASE + 53, + NOSR = B_POSIX_ERROR_BASE + 54, + NOSTR = B_POSIX_ERROR_BASE + 55, + NOTSUP = B_POSIX_ERROR_BASE + 56, + PROTO = B_POSIX_ERROR_BASE + 57, + TIME = B_POSIX_ERROR_BASE + 58, + TXTBSY = B_POSIX_ERROR_BASE + 59, + NOATTR = B_POSIX_ERROR_BASE + 60, + NOTRECOVERABLE = B_POSIX_ERROR_BASE + 61, + OWNERDEAD = B_POSIX_ERROR_BASE + 62, + + NOMEM = B_NO_MEMORY, + + ACCES = B_PERMISSION_DENIED, + INTR = B_INTERRUPTED, + IO = B_IO_ERROR, + BUSY = B_BUSY, + FAULT = B_BAD_ADDRESS, + TIMEDOUT = B_TIMED_OUT, + /// Also used for WOULDBLOCK + AGAIN = B_WOULD_BLOCK, + BADF = B_FILE_ERROR, + EXIST = B_FILE_EXISTS, + INVAL = B_BAD_VALUE, + NAMETOOLONG = B_NAME_TOO_LONG, + NOENT = B_ENTRY_NOT_FOUND, + PERM = B_NOT_ALLOWED, + NOTDIR = B_NOT_A_DIRECTORY, + ISDIR = B_IS_A_DIRECTORY, + NOTEMPTY = B_DIRECTORY_NOT_EMPTY, + NOSPC = B_DEVICE_FULL, + ROFS = B_READ_ONLY_DEVICE, + MFILE = B_NO_MORE_FDS, + XDEV = B_CROSS_DEVICE_LINK, + LOOP = B_LINK_LIMIT, + NOEXEC = B_NOT_AN_EXECUTABLE, + PIPE = B_BUSTED_PIPE, + + _, }; +// /system/develop/headers/os/support/SupportDefs.h + +pub const status_t = i32; + // /system/develop/headers/posix/arch/*/signal.h pub const vregs = switch (builtin.cpu.arch) { @@ -822,445 +1116,278 @@ pub const vregs = switch (builtin.cpu.arch) { else => void, }; -// access function -pub const F_OK = 0; // test for existence of file -pub const X_OK = 1; // test for execute or search permission -pub const W_OK = 2; // test for write permission -pub const R_OK = 4; // test for read permission - -pub const F = struct { - pub const DUPFD = 0x0001; - pub const GETFD = 0x0002; - pub const SETFD = 0x0004; - pub const GETFL = 0x0008; - pub const SETFL = 0x0010; - - pub const GETLK = 0x0020; - pub const SETLK = 0x0080; - pub const SETLKW = 0x0100; - pub const DUPFD_CLOEXEC = 0x0200; - - pub const RDLCK = 0x0040; - pub const UNLCK = 0x0200; - pub const WRLCK = 0x0400; -}; - -pub const LOCK = struct { - pub const SH = 0x01; - pub const EX = 0x02; - pub const NB = 0x04; - pub const UN = 0x08; -}; - -pub const FD_CLOEXEC = 1; - -pub const SEEK = struct { - pub const SET = 0; - pub const CUR = 1; - pub const END = 2; +// /system/develop/headers/posix/dirent.h + +pub const DirEnt = extern struct { + /// device + dev: dev_t, + /// parent device (only for queries) + pdev: dev_t, + /// inode number + ino: ino_t, + /// parent inode (only for queries) + pino: ino_t, + /// length of this record, not the name + reclen: u16, + /// name of the entry (null byte terminated) + name: [0]u8, + pub fn getName(dirent: *const DirEnt) [*:0]const u8 { + return @ptrCast(&dirent.name); + } }; -pub const SOCK = struct { - pub const STREAM = 1; - pub const DGRAM = 2; - pub const RAW = 3; - pub const SEQPACKET = 5; +// /system/develop/headers/posix/errno.h - /// WARNING: this flag is not supported by windows socket functions directly, - /// it is only supported by std.os.socket. Be sure that this value does - /// not share any bits with any of the `SOCK` values. - pub const CLOEXEC = 0x10000; - /// WARNING: this flag is not supported by windows socket functions directly, - /// it is only supported by std.os.socket. Be sure that this value does - /// not share any bits with any of the `SOCK` values. - pub const NONBLOCK = 0x20000; -}; +extern "root" fn _errnop() *i32; +pub const _errno = _errnop; -pub const SO = struct { - pub const ACCEPTCONN = 0x00000001; - pub const BROADCAST = 0x00000002; - pub const DEBUG = 0x00000004; - pub const DONTROUTE = 0x00000008; - pub const KEEPALIVE = 0x00000010; - pub const OOBINLINE = 0x00000020; - pub const REUSEADDR = 0x00000040; - pub const REUSEPORT = 0x00000080; - pub const USELOOPBACK = 0x00000100; - pub const LINGER = 0x00000200; +// /system/develop/headers/posix/poll.h - pub const SNDBUF = 0x40000001; - pub const SNDLOWAT = 0x40000002; - pub const SNDTIMEO = 0x40000003; - pub const RCVBUF = 0x40000004; - pub const RCVLOWAT = 0x40000005; - pub const RCVTIMEO = 0x40000006; - pub const ERROR = 0x40000007; - pub const TYPE = 0x40000008; - pub const NONBLOCK = 0x40000009; - pub const BINDTODEVICE = 0x4000000a; - pub const PEERCRED = 0x4000000b; -}; +pub const nfds_t = usize; -pub const SOL = struct { - pub const SOCKET = -1; +pub const pollfd = extern struct { + fd: i32, + events: i16, + revents: i16, }; -pub const PF = struct { - pub const UNSPEC = AF.UNSPEC; - pub const INET = AF.INET; - pub const ROUTE = AF.ROUTE; - pub const LINK = AF.LINK; - pub const INET6 = AF.INET6; - pub const LOCAL = AF.LOCAL; - pub const UNIX = AF.UNIX; - pub const BLUETOOTH = AF.BLUETOOTH; -}; +pub const POLL = struct { + /// any readable data available + pub const IN = 0x0001; + /// file descriptor is writeable + pub const OUT = 0x0002; + pub const RDNORM = IN; + pub const WRNORM = OUT; + /// priority readable data + pub const RDBAND = 0x0008; + /// priority data can be written + pub const WRBAND = 0x0010; + /// high priority readable data + pub const PRI = 0x0020; -pub const AF = struct { - pub const UNSPEC = 0; - pub const INET = 1; - pub const APPLETALK = 2; - pub const ROUTE = 3; - pub const LINK = 4; - pub const INET6 = 5; - pub const DLI = 6; - pub const IPX = 7; - pub const NOTIFY = 8; - pub const LOCAL = 9; - pub const UNIX = LOCAL; - pub const BLUETOOTH = 10; - pub const MAX = 11; + /// errors pending + pub const ERR = 0x0004; + /// disconnected + pub const HUP = 0x0080; + /// invalid file descriptor + pub const NVAL = 0x1000; }; -pub const DT = struct {}; - -/// add event to kq (implies enable) -pub const EV_ADD = 0x0001; - -/// delete event from kq -pub const EV_DELETE = 0x0002; - -/// enable event -pub const EV_ENABLE = 0x0004; - -/// disable event (not reported) -pub const EV_DISABLE = 0x0008; - -/// only report one occurrence -pub const EV_ONESHOT = 0x0010; - -/// clear event state after reporting -pub const EV_CLEAR = 0x0020; - -/// force immediate event output -/// ... with or without EV_ERROR -/// ... use KEVENT_FLAG_ERROR_EVENTS -/// on syscalls supporting flags -pub const EV_RECEIPT = 0x0040; - -/// disable event after reporting -pub const EV_DISPATCH = 0x0080; - -pub const EVFILT_READ = -1; -pub const EVFILT_WRITE = -2; - -/// attached to aio requests -pub const EVFILT_AIO = -3; +// /system/develop/headers/posix/signal.h -/// attached to vnodes -pub const EVFILT_VNODE = -4; +pub const sigset_t = u64; +pub const empty_sigset: sigset_t = 0; +pub const filled_sigset = ~@as(sigset_t, 0); -/// attached to struct proc -pub const EVFILT_PROC = -5; +pub const SIG = struct { + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); -/// attached to struct proc -pub const EVFILT_SIGNAL = -6; + pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(3); -/// timers -pub const EVFILT_TIMER = -7; + pub const HUP = 1; + pub const INT = 2; + pub const QUIT = 3; + pub const ILL = 4; + pub const CHLD = 5; + pub const ABRT = 6; + pub const IOT = ABRT; + pub const PIPE = 7; + pub const FPE = 8; + pub const KILL = 9; + pub const STOP = 10; + pub const SEGV = 11; + pub const CONT = 12; + pub const TSTP = 13; + pub const ALRM = 14; + pub const TERM = 15; + pub const TTIN = 16; + pub const TTOU = 17; + pub const USR1 = 18; + pub const USR2 = 19; + pub const WINCH = 20; + pub const KILLTHR = 21; + pub const TRAP = 22; + pub const POLL = 23; + pub const PROF = 24; + pub const SYS = 25; + pub const URG = 26; + pub const VTALRM = 27; + pub const XCPU = 28; + pub const XFSZ = 29; + pub const BUS = 30; + pub const RESERVED1 = 31; + pub const RESERVED2 = 32; -/// Process descriptors -pub const EVFILT_PROCDESC = -8; + pub const BLOCK = 1; + pub const UNBLOCK = 2; + pub const SETMASK = 3; +}; -/// Filesystem events -pub const EVFILT_FS = -9; +pub const siginfo_t = extern struct { + signo: i32, + code: i32, + errno: i32, -pub const EVFILT_LIO = -10; + pid: pid_t, + uid: uid_t, + addr: *allowzero anyopaque, +}; -/// User events -pub const EVFILT_USER = -11; +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = extern struct { + pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; + pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.C) void; -/// Sendfile events -pub const EVFILT_SENDFILE = -12; + /// signal handler + handler: extern union { + handler: handler_fn, + sigaction: sigaction_fn, + }, -pub const EVFILT_EMPTY = -13; + /// signal mask to apply + mask: sigset_t, -pub const T = struct { - pub const CGETA = 0x8000; - pub const CSETA = 0x8001; - pub const CSETAF = 0x8002; - pub const CSETAW = 0x8003; - pub const CWAITEVENT = 0x8004; - pub const CSBRK = 0x8005; - pub const CFLSH = 0x8006; - pub const CXONC = 0x8007; - pub const CQUERYCONNECTED = 0x8008; - pub const CGETBITS = 0x8009; - pub const CSETDTR = 0x8010; - pub const CSETRTS = 0x8011; - pub const IOCGWINSZ = 0x8012; - pub const IOCSWINSZ = 0x8013; - pub const CVTIME = 0x8014; - pub const IOCGPGRP = 0x8015; - pub const IOCSPGRP = 0x8016; - pub const IOCSCTTY = 0x8017; - pub const IOCMGET = 0x8018; - pub const IOCMSET = 0x8019; - pub const IOCSBRK = 0x8020; - pub const IOCCBRK = 0x8021; - pub const IOCMBIS = 0x8022; - pub const IOCMBIC = 0x8023; - pub const IOCGSID = 0x8024; + /// see signal options + flags: i32, - pub const FIONREAD = 0xbe000001; - pub const FIONBIO = 0xbe000000; + /// will be passed to the signal handler, BeOS extension + userdata: *allowzero anyopaque = undefined, }; -pub const winsize = extern struct { - ws_row: u16, - ws_col: u16, - ws_xpixel: u16, - ws_ypixel: u16, +pub const SA = struct { + pub const NOCLDSTOP = 0x01; + pub const NOCLDWAIT = 0x02; + pub const RESETHAND = 0x04; + pub const NODEFER = 0x08; + pub const RESTART = 0x10; + pub const ONSTACK = 0x20; + pub const SIGINFO = 0x40; + pub const NOMASK = NODEFER; + pub const STACK = ONSTACK; + pub const ONESHOT = RESETHAND; }; -const B_POSIX_ERROR_BASE = -2147454976; +pub const SS = struct { + pub const ONSTACK = 0x1; + pub const DISABLE = 0x2; +}; -pub const E = enum(i32) { - @"2BIG" = B_POSIX_ERROR_BASE + 1, - CHILD = B_POSIX_ERROR_BASE + 2, - DEADLK = B_POSIX_ERROR_BASE + 3, - FBIG = B_POSIX_ERROR_BASE + 4, - MLINK = B_POSIX_ERROR_BASE + 5, - NFILE = B_POSIX_ERROR_BASE + 6, - NODEV = B_POSIX_ERROR_BASE + 7, - NOLCK = B_POSIX_ERROR_BASE + 8, - NOSYS = B_POSIX_ERROR_BASE + 9, - NOTTY = B_POSIX_ERROR_BASE + 10, - NXIO = B_POSIX_ERROR_BASE + 11, - SPIPE = B_POSIX_ERROR_BASE + 12, - SRCH = B_POSIX_ERROR_BASE + 13, - FPOS = B_POSIX_ERROR_BASE + 14, - SIGPARM = B_POSIX_ERROR_BASE + 15, - DOM = B_POSIX_ERROR_BASE + 16, - RANGE = B_POSIX_ERROR_BASE + 17, - PROTOTYPE = B_POSIX_ERROR_BASE + 18, - PROTONOSUPPORT = B_POSIX_ERROR_BASE + 19, - PFNOSUPPORT = B_POSIX_ERROR_BASE + 20, - AFNOSUPPORT = B_POSIX_ERROR_BASE + 21, - ADDRINUSE = B_POSIX_ERROR_BASE + 22, - ADDRNOTAVAIL = B_POSIX_ERROR_BASE + 23, - NETDOWN = B_POSIX_ERROR_BASE + 24, - NETUNREACH = B_POSIX_ERROR_BASE + 25, - NETRESET = B_POSIX_ERROR_BASE + 26, - CONNABORTED = B_POSIX_ERROR_BASE + 27, - CONNRESET = B_POSIX_ERROR_BASE + 28, - ISCONN = B_POSIX_ERROR_BASE + 29, - NOTCONN = B_POSIX_ERROR_BASE + 30, - SHUTDOWN = B_POSIX_ERROR_BASE + 31, - CONNREFUSED = B_POSIX_ERROR_BASE + 32, - HOSTUNREACH = B_POSIX_ERROR_BASE + 33, - NOPROTOOPT = B_POSIX_ERROR_BASE + 34, - NOBUFS = B_POSIX_ERROR_BASE + 35, - INPROGRESS = B_POSIX_ERROR_BASE + 36, - ALREADY = B_POSIX_ERROR_BASE + 37, - ILSEQ = B_POSIX_ERROR_BASE + 38, - NOMSG = B_POSIX_ERROR_BASE + 39, - STALE = B_POSIX_ERROR_BASE + 40, - OVERFLOW = B_POSIX_ERROR_BASE + 41, - MSGSIZE = B_POSIX_ERROR_BASE + 42, - OPNOTSUPP = B_POSIX_ERROR_BASE + 43, - NOTSOCK = B_POSIX_ERROR_BASE + 44, - HOSTDOWN = B_POSIX_ERROR_BASE + 45, - BADMSG = B_POSIX_ERROR_BASE + 46, - CANCELED = B_POSIX_ERROR_BASE + 47, - DESTADDRREQ = B_POSIX_ERROR_BASE + 48, - DQUOT = B_POSIX_ERROR_BASE + 49, - IDRM = B_POSIX_ERROR_BASE + 50, - MULTIHOP = B_POSIX_ERROR_BASE + 51, - NODATA = B_POSIX_ERROR_BASE + 52, - NOLINK = B_POSIX_ERROR_BASE + 53, - NOSR = B_POSIX_ERROR_BASE + 54, - NOSTR = B_POSIX_ERROR_BASE + 55, - NOTSUP = B_POSIX_ERROR_BASE + 56, - PROTO = B_POSIX_ERROR_BASE + 57, - TIME = B_POSIX_ERROR_BASE + 58, - TXTBSY = B_POSIX_ERROR_BASE + 59, - NOATTR = B_POSIX_ERROR_BASE + 60, - NOTRECOVERABLE = B_POSIX_ERROR_BASE + 61, - OWNERDEAD = B_POSIX_ERROR_BASE + 62, +pub const MINSIGSTKSZ = 8192; +pub const SIGSTKSZ = 16384; - ACCES = -0x7ffffffe, // Permission denied - INTR = -0x7ffffff6, // Interrupted system call - IO = -0x7fffffff, // Input/output error - BUSY = -0x7ffffff2, // Device busy - FAULT = -0x7fffecff, // Bad address - TIMEDOUT = -2147483639, // Operation timed out - AGAIN = -0x7ffffff5, - BADF = -0x7fffa000, // Bad file descriptor - EXIST = -0x7fff9ffe, // File exists - INVAL = -0x7ffffffb, // Invalid argument - NAMETOOLONG = -2147459068, // File name too long - NOENT = -0x7fff9ffd, // No such file or directory - PERM = -0x7ffffff1, // Operation not permitted - NOTDIR = -0x7fff9ffb, // Not a directory - ISDIR = -0x7fff9ff7, // Is a directory - NOTEMPTY = -2147459066, // Directory not empty - NOSPC = -0x7fff9ff9, // No space left on device - ROFS = -0x7fff9ff8, // Read-only filesystem - MFILE = -0x7fff9ff6, // Too many open files - XDEV = -0x7fff9ff5, // Cross-device link - NOEXEC = -0x7fffecfe, // Exec format error - PIPE = -0x7fff9ff3, // Broken pipe - NOMEM = -0x80000000, // Cannot allocate memory - LOOP = -2147459060, // Too many levels of symbolic links - SUCCESS = 0, - _, +pub const stack_t = extern struct { + sp: [*]u8, + size: isize, + flags: i32, }; -pub const S = struct { - pub const IFMT = 0o170000; - pub const IFSOCK = 0o140000; - pub const IFLNK = 0o120000; - pub const IFREG = 0o100000; - pub const IFBLK = 0o060000; - pub const IFDIR = 0o040000; - pub const IFCHR = 0o020000; - pub const IFIFO = 0o010000; - pub const INDEX_DIR = 0o4000000000; - - pub const IUMSK = 0o7777; - pub const ISUID = 0o4000; - pub const ISGID = 0o2000; - pub const ISVTX = 0o1000; - pub const IRWXU = 0o700; - pub const IRUSR = 0o400; - pub const IWUSR = 0o200; - pub const IXUSR = 0o100; - pub const IRWXG = 0o070; - pub const IRGRP = 0o040; - pub const IWGRP = 0o020; - pub const IXGRP = 0o010; - pub const IRWXO = 0o007; - pub const IROTH = 0o004; - pub const IWOTH = 0o002; - pub const IXOTH = 0o001; +pub const NSIG = 65; - pub fn ISREG(m: u32) bool { - return m & IFMT == IFREG; - } +pub const mcontext_t = vregs; - pub fn ISLNK(m: u32) bool { - return m & IFMT == IFLNK; - } +pub const ucontext_t = extern struct { + link: ?*ucontext_t, + sigmask: sigset_t, + stack: stack_t, + mcontext: mcontext_t, +}; - pub fn ISBLK(m: u32) bool { - return m & IFMT == IFBLK; - } +// /system/develop/headers/posix/sys/stat.h - pub fn ISDIR(m: u32) bool { - return m & IFMT == IFDIR; - } +pub const Stat = extern struct { + dev: dev_t, + ino: ino_t, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + size: off_t, + rdev: dev_t, + blksize: blksize_t, + atim: timespec, + mtim: timespec, + ctim: timespec, + crtim: timespec, + type: u32, + blocks: blkcnt_t, - pub fn ISCHR(m: u32) bool { - return m & IFMT == IFCHR; + pub fn atime(self: @This()) timespec { + return self.atim; } - - pub fn ISFIFO(m: u32) bool { - return m & IFMT == IFIFO; + pub fn mtime(self: @This()) timespec { + return self.mtim; } - - pub fn ISSOCK(m: u32) bool { - return m & IFMT == IFSOCK; + pub fn ctime(self: @This()) timespec { + return self.ctim; } - - pub fn ISINDEX(m: u32) bool { - return m & INDEX_DIR == INDEX_DIR; + pub fn birthtime(self: @This()) timespec { + return self.crtim; } }; -pub const HOST_NAME_MAX = 255; +// /system/develop/headers/posix/sys/types.h -pub const addrinfo = extern struct { - flags: i32, - family: i32, - socktype: i32, - protocol: i32, - addrlen: socklen_t, - canonname: ?[*:0]u8, - addr: ?*sockaddr, - next: ?*addrinfo, -}; +pub const blkcnt_t = i64; +pub const blksize_t = i32; +pub const fsblkcnt_t = i64; +pub const fsfilcnt_t = i64; +pub const off_t = i64; +pub const ino_t = i64; +pub const cnt_t = i32; +pub const dev_t = i32; +pub const pid_t = i32; +pub const id_t = i32; -pub const IPPROTO = struct { - pub const IP = 0; - pub const HOPOPTS = 0; - pub const ICMP = 1; - pub const IGMP = 2; - pub const TCP = 6; - pub const UDP = 17; - pub const IPV6 = 41; - pub const ROUTING = 43; - pub const FRAGMENT = 44; - pub const ESP = 50; - pub const AH = 51; - pub const ICMPV6 = 58; - pub const NONE = 59; - pub const DSTOPTS = 60; - pub const ETHERIP = 97; - pub const RAW = 255; - pub const MAX = 256; -}; +pub const uid_t = u32; +pub const gid_t = u32; +pub const mode_t = u32; +pub const umode_t = u32; +pub const nlink_t = i32; -pub const rlimit_resource = enum(c_int) { - CORE = 0, - CPU = 1, - DATA = 2, - FSIZE = 3, - NOFILE = 4, - STACK = 5, - AS = 6, - NOVMON = 7, - _, -}; +pub const clockid_t = i32; +pub const timer_t = *opaque {}; -pub const rlim_t = i64; +// /system/develop/headers/posix/time.h -pub const RLIM = struct { - /// No limit - pub const INFINITY: rlim_t = (1 << 63) - 1; +pub const clock_t = i32; +pub const suseconds_t = i32; +pub const useconds_t = u32; - pub const SAVED_MAX = INFINITY; - pub const SAVED_CUR = INFINITY; -}; +pub const time_t = isize; -pub const rlimit = extern struct { - /// Soft limit - cur: rlim_t, - /// Hard limit - max: rlim_t, -}; +pub const CLOCKS_PER_SEC = 1_000_000; +pub const CLK_TCK = CLOCKS_PER_SEC; +pub const TIME_UTC = 1; -pub const SHUT = struct { - pub const RD = 0; - pub const WR = 1; - pub const RDWR = 2; +pub const CLOCK = struct { + /// system-wide monotonic clock (aka system time) + pub const MONOTONIC: clockid_t = 0; + /// system-wide real time clock + pub const REALTIME: clockid_t = -1; + /// clock measuring the used CPU time of the current process + pub const PROCESS_CPUTIME_ID: clockid_t = -2; + /// clock measuring the used CPU time of the current thread + pub const THREAD_CPUTIME_ID: clockid_t = -3; }; -// TODO fill out if needed -pub const directory_which = enum(c_int) { - B_USER_SETTINGS_DIRECTORY = 0xbbe, +pub const timespec = extern struct { + /// seconds + tv_sec: time_t, + /// and nanoseconds + tv_nsec: isize, +}; - _, +pub const itimerspec = extern struct { + interval: timespec, + value: timespec, }; -pub const MSG_NOSIGNAL = 0x0800; +// /system/develop/headers/private/system/syscalls.h + +pub extern "root" fn _kern_get_current_team() team_id; +pub extern "root" fn _kern_open_dir(fd: fd_t, path: [*:0]const u8) fd_t; +pub extern "root" fn _kern_read_dir(fd: fd_t, buffer: [*]u8, bufferSize: usize, maxCount: u32) isize; +pub extern "root" fn _kern_rewind_dir(fd: fd_t) status_t; +pub extern "root" fn _kern_read_stat(fd: fd_t, path: [*:0]const u8, traverseLink: bool, stat: *Stat, statSize: usize) status_t; diff --git a/lib/std/crypto/Certificate.zig b/lib/std/crypto/Certificate.zig index c3ac3e22aa..b1cc4fc095 100644 --- a/lib/std/crypto/Certificate.zig +++ b/lib/std/crypto/Certificate.zig @@ -772,7 +772,7 @@ fn verifyRsa( Hash.hash(message, &msg_hashed, .{}); switch (modulus.len) { - inline 128, 256, 512 => |modulus_len| { + inline 128, 256, 384, 512 => |modulus_len| { const ps_len = modulus_len - (hash_der.len + msg_hashed.len) - 3; const em: [modulus_len]u8 = [2]u8{ 0, 1 } ++ diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 7e118afb26..3e979175d8 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -131,7 +131,12 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c return throughput; } -const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }}; +const signatures = [_]Crypto{ + Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }, + Crypto{ .ty = crypto.sign.ecdsa.EcdsaP256Sha256, .name = "ecdsa-p256" }, + Crypto{ .ty = crypto.sign.ecdsa.EcdsaP384Sha384, .name = "ecdsa-p384" }, + Crypto{ .ty = crypto.sign.ecdsa.EcdsaSecp256k1Sha256, .name = "ecdsa-secp256k1" }, +}; pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 { const msg = [_]u8{0} ** 64; diff --git a/lib/std/crypto/ecdsa.zig b/lib/std/crypto/ecdsa.zig index 70362470c3..b2751ce20a 100644 --- a/lib/std/crypto/ecdsa.zig +++ b/lib/std/crypto/ecdsa.zig @@ -4,6 +4,7 @@ const crypto = std.crypto; const fmt = std.fmt; const io = std.io; const mem = std.mem; +const sha3 = crypto.hash.sha3; const testing = std.testing; const EncodingError = crypto.errors.EncodingError; @@ -26,7 +27,11 @@ pub const EcdsaSecp256k1Sha256oSha256 = Ecdsa(crypto.ecc.Secp256k1, crypto.hash. /// Elliptic Curve Digital Signature Algorithm (ECDSA). pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { - const Hmac = crypto.auth.hmac.Hmac(Hash); + const Prf = switch (Hash) { + sha3.Shake128 => sha3.KMac128, + sha3.Shake256 => sha3.KMac256, + else => crypto.auth.hmac.Hmac(Hash), + }; return struct { /// Length (in bytes) of optional random bytes, for non-deterministic signatures. @@ -350,22 +355,22 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { if (noise) |n| @memcpy(m_z, &n); @memcpy(m_x, &secret_key); @memcpy(m_h, &h); - Hmac.create(&k, &m, &k); - Hmac.create(m_v, m_v, &k); + Prf.create(&k, &m, &k); + Prf.create(m_v, m_v, &k); m_i.* = 0x01; - Hmac.create(&k, &m, &k); - Hmac.create(m_v, m_v, &k); + Prf.create(&k, &m, &k); + Prf.create(m_v, m_v, &k); while (true) { var t_off: usize = 0; while (t_off < t.len) : (t_off += m_v.len) { const t_end = @min(t_off + m_v.len, t.len); - Hmac.create(m_v, m_v, &k); + Prf.create(m_v, m_v, &k); @memcpy(t[t_off..t_end], m_v[0 .. t_end - t_off]); } if (Curve.scalar.Scalar.fromBytes(t, .big)) |s| return s else |_| {} m_i.* = 0x00; - Hmac.create(&k, m[0 .. m_v.len + 1], &k); - Hmac.create(m_v, m_v, &k); + Prf.create(&k, m[0 .. m_v.len + 1], &k); + Prf.create(m_v, m_v, &k); } } }; diff --git a/lib/std/crypto/keccak_p.zig b/lib/std/crypto/keccak_p.zig index d04373269e..352172a1f6 100644 --- a/lib/std/crypto/keccak_p.zig +++ b/lib/std/crypto/keccak_p.zig @@ -195,7 +195,7 @@ pub fn KeccakF(comptime f: u11) type { } /// A generic Keccak-P state. -pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, comptime rounds: u5) type { +pub fn State(comptime f: u11, comptime capacity: u11, comptime rounds: u5) type { comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid state size comptime assert(capacity < f and capacity % 8 == 0); // invalid capacity size @@ -207,6 +207,9 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti /// Keccak does not have any options. pub const Options = struct {}; + /// The input delimiter. + delim: u8, + offset: usize = 0, buf: [rate]u8 = undefined, @@ -238,10 +241,28 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti } } + /// Initialize the state from a slice of bytes. + pub fn init(bytes: [f / 8]u8) Self { + return .{ .st = KeccakF(f).init(bytes) }; + } + + /// Permute the state + pub fn permute(self: *Self) void { + self.st.permuteR(rounds); + self.offset = 0; + } + + /// Align the input to the rate boundary. + pub fn fillBlock(self: *Self) void { + self.st.addBytes(self.buf[0..self.offset]); + self.st.permuteR(rounds); + self.offset = 0; + } + /// Mark the end of the input. pub fn pad(self: *Self) void { self.st.addBytes(self.buf[0..self.offset]); - self.st.addByte(delim, self.offset); + self.st.addByte(self.delim, self.offset); self.st.addByte(0x80, rate - 1); self.st.permuteR(rounds); self.offset = 0; diff --git a/lib/std/crypto/pcurves/p256/p256_64.zig b/lib/std/crypto/pcurves/p256/p256_64.zig index e8dbaead33..f3d38ca3e6 100644 --- a/lib/std/crypto/pcurves/p256/p256_64.zig +++ b/lib/std/crypto/pcurves/p256/p256_64.zig @@ -73,12 +73,9 @@ pub const NonMontgomeryDomainFieldElement = [4]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -95,12 +92,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/p256/p256_scalar_64.zig b/lib/std/crypto/pcurves/p256/p256_scalar_64.zig index 152c2b8787..736a3ea8b7 100644 --- a/lib/std/crypto/pcurves/p256/p256_scalar_64.zig +++ b/lib/std/crypto/pcurves/p256/p256_scalar_64.zig @@ -73,12 +73,9 @@ pub const NonMontgomeryDomainFieldElement = [4]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -95,12 +92,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/p384/p384_64.zig b/lib/std/crypto/pcurves/p384/p384_64.zig index f25a7d65b5..e1419e7c81 100644 --- a/lib/std/crypto/pcurves/p384/p384_64.zig +++ b/lib/std/crypto/pcurves/p384/p384_64.zig @@ -42,12 +42,9 @@ pub const NonMontgomeryDomainFieldElement = [6]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -64,12 +61,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/p384/p384_scalar_64.zig b/lib/std/crypto/pcurves/p384/p384_scalar_64.zig index fc787ba7b9..68a0a0ca2f 100644 --- a/lib/std/crypto/pcurves/p384/p384_scalar_64.zig +++ b/lib/std/crypto/pcurves/p384/p384_scalar_64.zig @@ -42,12 +42,9 @@ pub const NonMontgomeryDomainFieldElement = [6]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -64,12 +61,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig b/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig index ae3e97c619..1c69b90eea 100644 --- a/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig +++ b/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig @@ -42,12 +42,9 @@ pub const NonMontgomeryDomainFieldElement = [4]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -64,12 +61,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig b/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig index 12c833bb33..97bf5f0a45 100644 --- a/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig +++ b/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig @@ -42,12 +42,9 @@ pub const NonMontgomeryDomainFieldElement = [4]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -64,12 +61,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index 8fa76a48bf..e75dcb7b79 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -18,11 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); pub const Shake128 = Shake(128); pub const Shake256 = Shake(256); +pub const CShake128 = CShake(128, null); +pub const CShake256 = CShake(256, null); + +pub const KMac128 = KMac(128); +pub const KMac256 = KMac(256); + +pub const TupleHash128 = TupleHash(128); +pub const TupleHash256 = TupleHash(256); + /// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. /// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. /// The delimiter is 0x1f by default, but can be changed for context-separation. /// For a protocol that uses both KangarooTwelve and TurboSHAKE128, it is recommended to avoid using 0x06, 0x07 or 0x0b for the delimiter. -pub fn TurboShake128(comptime delim: ?u7) type { +pub fn TurboShake128(delim: ?u7) type { return TurboShake(128, delim); } @@ -34,27 +43,26 @@ pub fn TurboShake256(comptime delim: ?u7) type { } /// A generic Keccak hash function. -pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type { +pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim: u8, comptime rounds: u5) type { comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length - const State = KeccakState(f, output_bits * 2, delim, rounds); + const State = KeccakState(f, output_bits * 2, rounds); return struct { const Self = @This(); - st: State = .{}, + st: State, /// The output length, in bytes. pub const digest_length = output_bits / 8; /// The block length, or rate, in bytes. pub const block_length = State.rate; - /// Keccak does not have any options. - pub const Options = struct {}; + /// The delimiter can be overwritten in the options. + pub const Options = struct { delim: u8 = default_delim }; /// Initialize a Keccak hash function. pub fn init(options: Options) Self { - _ = options; - return Self{}; + return Self{ .st = .{ .delim = options.delim } }; } /// Hash a slice of bytes. @@ -105,29 +113,28 @@ pub fn TurboShake(comptime security_level: u11, comptime delim: ?u7) type { return ShakeLike(security_level, d, 12); } -fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: u5) type { +fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { const f = 1600; - const State = KeccakState(f, security_level * 2, delim, rounds); + const State = KeccakState(f, security_level * 2, rounds); return struct { const Self = @This(); - st: State = .{}, + st: State, buf: [State.rate]u8 = undefined, offset: usize = 0, padded: bool = false, /// The recommended output length, in bytes. - pub const digest_length = security_level / 2; + pub const digest_length = security_level / 8 * 2; /// The block length, or rate, in bytes. pub const block_length = State.rate; - /// Keccak does not have any options. - pub const Options = struct {}; + /// The delimiter can be overwritten in the options. + pub const Options = struct { delim: u8 = default_delim }; /// Initialize a SHAKE extensible hash function. pub fn init(options: Options) Self { - _ = options; - return Self{}; + return Self{ .st = .{ .delim = options.delim } }; } /// Hash a slice of bytes. @@ -182,6 +189,11 @@ fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: self.st.st.clear(0, State.rate); } + /// Align the input to a block boundary. + pub fn fillBlock(self: *Self) void { + self.st.fillBlock(); + } + pub const Error = error{}; pub const Writer = std.io.Writer(*Self, Error, write); @@ -196,6 +208,338 @@ fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: }; } +/// The cSHAKE extendable output hash function. +/// cSHAKE is similar to SHAKE, but in addition to the input message, it also takes an optional context (aka customization string). +pub fn CShake(comptime security_level: u11, comptime fname: ?[]const u8) type { + return CShakeLike(security_level, 0x04, 24, fname); +} + +fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5, comptime fname: ?[]const u8) type { + return struct { + const Shaker = ShakeLike(security_level, default_delim, rounds); + shaker: Shaker, + + /// The recommended output length, in bytes. + pub const digest_length = Shaker.digest_length; + /// The block length, or rate, in bytes. + pub const block_length = Shaker.block_length; + + /// cSHAKE options can include a context string. + pub const Options = struct { context: ?[]const u8 = null }; + + const Self = @This(); + + /// Initialize a SHAKE extensible hash function. + pub fn init(options: Options) Self { + if (fname == null and options.context == null) { + return Self{ .shaker = Shaker.init(.{ .delim = 0x1f }) }; + } + var shaker = Shaker.init(.{}); + comptime assert(Shaker.block_length % 8 == 0); + const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8); + shaker.update(encoded_rate_len.slice()); + const encoded_zero = comptime NistLengthEncoding.encode(.left, 0); + if (fname) |name| { + const encoded_fname_len = comptime NistLengthEncoding.encode(.left, name.len); + const encoded_fname = comptime encoded_fname_len.slice() ++ name; + shaker.update(encoded_fname); + } else { + shaker.update(encoded_zero.slice()); + } + if (options.context) |context| { + const encoded_context_len = NistLengthEncoding.encode(.left, context.len); + shaker.update(encoded_context_len.slice()); + shaker.update(context); + } else { + shaker.update(encoded_zero.slice()); + } + shaker.st.fillBlock(); + return Self{ .shaker = shaker }; + } + + /// Hash a slice of bytes. + /// `out` can be any length. + pub fn hash(bytes: []const u8, out: []u8, options: Options) void { + var st = Self.init(options); + st.update(bytes); + st.squeeze(out); + } + + /// Absorb a slice of bytes into the state. + pub fn update(self: *Self, bytes: []const u8) void { + self.shaker.update(bytes); + } + + /// Squeeze a slice of bytes from the state. + /// `out` can be any length, and the function can be called multiple times. + pub fn squeeze(self: *Self, out: []u8) void { + self.shaker.squeeze(out); + } + + /// Return the hash of the absorbed bytes. + /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead). + pub fn final(self: *Self, out: []u8) void { + self.shaker.final(out); + } + + /// Align the input to a block boundary. + pub fn fillBlock(self: *Self) void { + self.shaker.fillBlock(); + } + + pub const Error = error{}; + pub const Writer = std.io.Writer(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + self.update(bytes); + return bytes.len; + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + }; +} + +/// The KMAC extendable output authentication function. +/// KMAC is a keyed version of the cSHAKE function, with an optional context. +/// It can be used as an SHA-3 based alternative to HMAC, as well as a generic keyed XoF (extendable output function). +pub fn KMac(comptime security_level: u11) type { + return KMacLike(security_level, 0x04, 24); +} + +fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { + const CShaker = CShakeLike(security_level, default_delim, rounds, "KMAC"); + + return struct { + const Self = @This(); + + /// The recommended output length, in bytes. + pub const mac_length = CShaker.digest_length; + /// The minimum output length, in bytes. + pub const mac_length_min = 4; + /// The recommended key length, in bytes. + pub const key_length = security_level / 8; + /// The minimum key length, in bytes. + pub const key_length_min = 0; + /// The block length, or rate, in bytes. + pub const block_length = CShaker.block_length; + + cshaker: CShaker, + xof_mode: bool = false, + + /// KMAC options can include a context string. + pub const Options = struct { + context: ?[]const u8 = null, + }; + + /// Initialize a state for the KMAC function, with an optional context and an arbitrary-long key. + /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message. + /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. + pub fn initWithOptions(key: []const u8, options: Options) Self { + var cshaker = CShaker.init(.{ .context = options.context }); + const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8); + cshaker.update(encoded_rate_len.slice()); + const encoded_key_len = NistLengthEncoding.encode(.left, key.len); + cshaker.update(encoded_key_len.slice()); + cshaker.update(key); + cshaker.fillBlock(); + return Self{ + .cshaker = cshaker, + }; + } + + /// Initialize a state for the KMAC function. + /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message. + /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. + pub fn init(key: []const u8) Self { + return initWithOptions(key, .{}); + } + + /// Add data to the state. + pub fn update(self: *Self, b: []const u8) void { + self.cshaker.update(b); + } + + /// Return an authentication tag for the current state. + pub fn final(self: *Self, out: []u8) void { + const encoded_out_len = NistLengthEncoding.encode(.right, out.len); + self.update(encoded_out_len.slice()); + self.cshaker.final(out); + } + + /// Squeeze a slice of bytes from the state. + /// `out` can be any length, and the function can be called multiple times. + pub fn squeeze(self: *Self, out: []u8) void { + if (!self.xof_mode) { + const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0); + self.update(encoded_out_len.slice()); + self.xof_mode = true; + } + self.cshaker.squeeze(out); + } + + /// Return an authentication tag for a message and a key, with an optional context. + pub fn createWithOptions(out: []u8, msg: []const u8, key: []const u8, options: Options) void { + var ctx = Self.initWithOptions(key, options); + ctx.update(msg); + ctx.final(out); + } + + /// Return an authentication tag for a message and a key. + pub fn create(out: []u8, msg: []const u8, key: []const u8) void { + var ctx = Self.init(key); + ctx.update(msg); + ctx.final(out); + } + + pub const Error = error{}; + pub const Writer = std.io.Writer(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + self.update(bytes); + return bytes.len; + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + }; +} + +/// The TupleHash extendable output hash function, with domain-separated inputs. +/// TupleHash is a secure hash function with a variable output length, based on the cSHAKE function. +/// It is designed for unambiguously hashing tuples of data. +/// +/// With most hash functions, calling `update("A")` followed by `update("B")`is identical to `update("AB")`. +/// With TupleHash, this is not the case: `update("A"); update("B")` is different from `update("AB")`. +/// +/// Any number of inputs can be hashed, and the output depends on individual inputs and their order. +pub fn TupleHash(comptime security_level: u11) type { + return TupleHashLike(security_level, 0x04, 24); +} + +fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { + const CShaker = CShakeLike(security_level, default_delim, rounds, "TupleHash"); + + return struct { + const Self = @This(); + + /// The output length, in bytes. + pub const digest_length = CShaker.digest_length; + /// The block length, or rate, in bytes. + pub const block_length = CShaker.block_length; + + cshaker: CShaker, + xof_mode: bool = false, + + /// TupleHash options can include a context string. + pub const Options = struct { + context: ?[]const u8 = null, + }; + + /// Initialize a state for the TupleHash function, with an optional context. + /// If the context is going to be reused, the structure can be initialized once, and cloned for each message. + /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. + /// + /// A key can be optionally added to the context to create a keyed TupleHash function, similar to KMAC. + pub fn initWithOptions(options: Options) Self { + const cshaker = CShaker.init(.{ .context = options.context }); + return Self{ + .cshaker = cshaker, + }; + } + + /// Initialize a state for the MAC function. + pub fn init() Self { + return initWithOptions(.{}); + } + + /// Add data to the state, separated from previous updates. + pub fn update(self: *Self, b: []const u8) void { + const encoded_b_len = NistLengthEncoding.encode(.left, b.len); + self.cshaker.update(encoded_b_len.slice()); + self.cshaker.update(b); + } + + /// Return an authentication tag for the current state. + pub fn final(self: *Self, out: []u8) void { + const encoded_out_len = NistLengthEncoding.encode(.right, out.len); + self.cshaker.update(encoded_out_len.slice()); + self.cshaker.final(out); + } + + /// Align the input to a block boundary. + pub fn fillBlock(self: *Self) void { + self.cshaker.fillBlock(); + } + + /// Squeeze a slice of bytes from the state. + /// `out` can be any length, and the function can be called multiple times. + pub fn squeeze(self: *Self, out: []u8) void { + if (!self.xof_mode) { + const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0); + self.update(encoded_out_len.slice()); + self.xof_mode = true; + } + self.cshaker.squeeze(out); + } + + pub const Error = error{}; + pub const Writer = std.io.Writer(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + self.update(bytes); + return bytes.len; + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + }; +} + +/// The NIST SP 800-185 encoded length format. +pub const NistLengthEncoding = enum { + left, + right, + + /// A length encoded according to NIST SP 800-185. + pub const Length = struct { + /// The size of the encoded value, in bytes. + len: usize = 0, + /// A buffer to store the encoded length. + buf: [@sizeOf(usize) + 1]u8 = undefined, + + /// Return the encoded length as a slice. + pub fn slice(self: *const Length) []const u8 { + return self.buf[0..self.len]; + } + }; + + /// Encode a length according to NIST SP 800-185. + pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length { + const len_bits = @bitSizeOf(@TypeOf(len)) - @clz(len) + 3; + const len_bytes = std.math.divCeil(usize, len_bits, 8) catch unreachable; + + var res = Length{ .len = len_bytes + 1 }; + if (encoding == .right) { + res.buf[len_bytes] = @intCast(len_bytes); + } + const end = if (encoding == .right) len_bytes - 1 else len_bytes; + res.buf[end] = @truncate(len << 3); + var len_ = len >> 5; + for (1..len_bytes) |i| { + res.buf[end - i] = @truncate(len_); + len_ >>= 8; + } + if (encoding == .left) { + res.buf[0] = @intCast(len_bytes); + } + return res; + } +}; + const htest = @import("test.zig"); test "sha3-224 single" { @@ -397,3 +741,88 @@ test "SHA-3 with streaming" { h.final(&out); try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); } + +test "cSHAKE-128 with no context nor function name" { + var out: [32]u8 = undefined; + CShake128.hash("hello123", &out, .{}); + try htest.assertEqual("1b85861510bc4d8e467d6f8a92270533cbaa7ba5e06c2d2a502854bac468b8b9", &out); +} + +test "cSHAKE-128 with context" { + var out: [32]u8 = undefined; + CShake128.hash("hello123", &out, .{ .context = "custom" }); + try htest.assertEqual("7509fa13a6bd3e38ad5c6fac042142c233996e40ebffc86c276f108b3b19cc6a", &out); +} + +test "cSHAKE-128 with context and function" { + var out: [32]u8 = undefined; + CShake(128, "function").hash("hello123", &out, .{ .context = "custom" }); + try htest.assertEqual("ad7f4d7db2d96587fcd5047c65d37c368f5366e3afac60bb9b66b0bb95dfb675", &out); +} + +test "cSHAKE-256" { + var out: [32]u8 = undefined; + CShake256.hash("hello123", &out, .{ .context = "custom" }); + try htest.assertEqual("dabe027eb1a6cbe3a0542d0560eb4e6b39146dd72ae1bf89c970a61bd93b1813", &out); +} + +test "KMAC-128 with empty key and message" { + var out: [KMac128.mac_length]u8 = undefined; + const key = ""; + KMac128.create(&out, "", key); + try htest.assertEqual("5c135c615152fb4d9784dd1155f9b6034e013fd77165c327dfa4d36701983ef7", &out); +} + +test "KMAC-128" { + var out: [KMac128.mac_length]u8 = undefined; + const key = "A KMAC secret key"; + KMac128.create(&out, "hello123", key); + try htest.assertEqual("1fa1c0d761129a83f9a4299ca137674de8373a3cc437799ae4c129e651627f8e", &out); +} + +test "KMAC-128 with a customization string" { + var out: [KMac128.mac_length]u8 = undefined; + const key = "A KMAC secret key"; + KMac128.createWithOptions(&out, "hello123", key, .{ .context = "custom" }); + try htest.assertEqual("c58c6d42dc00a27dfa8e7e08f8c9307cecb5d662ddb11b6c36057fc2e0e068ba", &out); +} + +test "KMACXOF-128" { + const key = "A KMAC secret key"; + var xof = KMac128.init(key); + xof.update("hello123"); + var out: [50]u8 = undefined; + xof.squeeze(&out); + try htest.assertEqual("628c2fb870d294b3673ac82d9f0d651aae6a5bb8084ea8cd8343cb888d075b9053173200a71f301141069c3c0322527981f7", &out); + xof.squeeze(&out); + try htest.assertEqual("7b638e178cfdac5727a4ea7694efaa967a65a1d0034501855acff506b4158d187d5a18d668e67b43f2abf61144b20ed4c09f", &out); +} + +test "KMACXOF-256" { + const key = "A KMAC secret key"; + var xof = KMac256.init(key); + xof.update("hello123"); + var out: [50]u8 = undefined; + xof.squeeze(&out); + try htest.assertEqual("23fc644bc2655ba6fde7b7c11f2804f22e8d8c6bd7db856268bf3370ce2362703f6c7e91916a1b8c116e60edfbcb25613054", &out); + xof.squeeze(&out); + try htest.assertEqual("ff97251020ff255ee65a1c1f5f78ebe904f61211c39f973f82fbce2b196b9f51c2cb12afe51549a0f1eaf7954e657ba11af3", &out); +} + +test "TupleHash-128" { + var st = TupleHash128.init(); + st.update("hello"); + st.update("123"); + var out: [32]u8 = undefined; + st.final(&out); + try htest.assertEqual("3938d49ade8ec0f0c305ac63497b2d2e8b2f650714f9667cc41816b1c11ffd20", &out); +} + +test "TupleHash-256" { + var st = TupleHash256.init(); + st.update("hello"); + st.update("123"); + var out: [64]u8 = undefined; + st.final(&out); + try htest.assertEqual("2dca563c2882f2ba4f46a441a4c5e13fb97150d1436fe99c7e4e43a2d20d0f1cd3d38483bde4a966930606dfa6c61c4ca6400aeedfb474d1bf0d7f6a70968289", &out); +} diff --git a/lib/std/debug.zig b/lib/std/debug.zig index d1d6201b80..eaa5ca9ff7 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -406,7 +406,7 @@ pub fn assert(ok: bool) void { pub fn panic(comptime format: []const u8, args: anytype) noreturn { @setCold(true); - panicExtra(null, null, format, args); + panicExtra(@errorReturnTrace(), @returnAddress(), format, args); } /// `panicExtra` is useful when you want to print out an `@errorReturnTrace` @@ -661,7 +661,7 @@ pub const StackIterator = struct { fn isValidMemory(address: usize) bool { // We are unable to determine validity of memory for freestanding targets - if (native_os == .freestanding) return true; + if (native_os == .freestanding or native_os == .uefi) return true; const aligned_address = address & ~@as(usize, @intCast((mem.page_size - 1))); if (aligned_address == 0) return false; diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 74d91239f8..b6222f942f 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -220,71 +220,83 @@ pub const Iterator = switch (native_os) { }, .haiku => struct { dir: Dir, - buf: [1024]u8, // TODO align(@alignOf(posix.dirent64)), + buf: [@sizeOf(DirEnt) + posix.PATH_MAX]u8 align(@alignOf(DirEnt)), + offset: usize, index: usize, end_index: usize, first_iter: bool, const Self = @This(); + const DirEnt = posix.system.DirEnt; pub const Error = IteratorError; /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. pub fn next(self: *Self) Error!?Entry { - start_over: while (true) { - // TODO: find a better max - const HAIKU_MAX_COUNT = 10000; + while (true) { if (self.index >= self.end_index) { if (self.first_iter) { - posix.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions + switch (@as(posix.E, @enumFromInt(posix.system._kern_rewind_dir(self.dir.fd)))) { + .SUCCESS => {}, + .BADF => unreachable, // Dir is invalid + .FAULT => unreachable, + .NOTDIR => unreachable, + .INVAL => unreachable, + .ACCES => return error.AccessDenied, + .PERM => return error.AccessDenied, + else => |err| return posix.unexpectedErrno(err), + } self.first_iter = false; } const rc = posix.system._kern_read_dir( self.dir.fd, &self.buf, self.buf.len, - HAIKU_MAX_COUNT, + self.buf.len / @sizeOf(DirEnt), ); if (rc == 0) return null; if (rc < 0) { - switch (posix.errno(rc)) { - .BADF => unreachable, // Dir is invalid or was opened without iteration ability + switch (@as(posix.E, @enumFromInt(rc))) { + .BADF => unreachable, // Dir is invalid .FAULT => unreachable, .NOTDIR => unreachable, .INVAL => unreachable, + .OVERFLOW => unreachable, + .ACCES => return error.AccessDenied, + .PERM => return error.AccessDenied, else => |err| return posix.unexpectedErrno(err), } } + self.offset = 0; self.index = 0; - self.end_index = @as(usize, @intCast(rc)); - } - const haiku_entry = @as(*align(1) posix.system.dirent, @ptrCast(&self.buf[self.index])); - const next_index = self.index + haiku_entry.reclen; - self.index = next_index; - const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&haiku_entry.name)), 0); - - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or (haiku_entry.ino == 0)) { - continue :start_over; + self.end_index = @intCast(rc); } + const dirent: *DirEnt = @ptrCast(@alignCast(&self.buf[self.offset])); + self.offset += dirent.reclen; + self.index += 1; + const name = mem.span(dirent.getName()); + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or dirent.ino == 0) continue; var stat_info: posix.Stat = undefined; - const rc = posix.system._kern_read_stat( + switch (@as(posix.E, @enumFromInt(posix.system._kern_read_stat( self.dir.fd, - &haiku_entry.name, + name, false, &stat_info, 0, - ); - if (rc != 0) { - switch (posix.errno(rc)) { - .SUCCESS => {}, - .BADF => unreachable, // Dir is invalid or was opened without iteration ability - .FAULT => unreachable, - .NOTDIR => unreachable, - .INVAL => unreachable, - else => |err| return posix.unexpectedErrno(err), - } + )))) { + .SUCCESS => {}, + .INVAL => unreachable, + .BADF => unreachable, // Dir is invalid + .NOMEM => return error.SystemResources, + .ACCES => return error.AccessDenied, + .PERM => return error.AccessDenied, + .FAULT => unreachable, + .NAMETOOLONG => unreachable, + .LOOP => unreachable, + .NOENT => continue, + else => |err| return posix.unexpectedErrno(err), } const statmode = stat_info.mode & posix.S.IFMT; @@ -315,7 +327,7 @@ pub const Iterator = switch (native_os) { dir: Dir, // The if guard is solely there to prevent compile errors from missing `linux.dirent64` // definition when compiling for other OSes. It doesn't do anything when compiling for Linux. - buf: [1024]u8 align(if (native_os != .linux) 1 else @alignOf(linux.dirent64)), + buf: [1024]u8 align(@alignOf(linux.dirent64)), index: usize, end_index: usize, first_iter: bool, @@ -599,8 +611,16 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { .buf = undefined, .first_iter = first_iter_start_value, }, - .linux, .haiku => return Iterator{ + .linux => return Iterator{ + .dir = self, + .index = 0, + .end_index = 0, + .buf = undefined, + .first_iter = first_iter_start_value, + }, + .haiku => return Iterator{ .dir = self, + .offset = 0, .index = 0, .end_index = 0, .buf = undefined, @@ -626,16 +646,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { } pub const Walker = struct { - stack: std.ArrayList(StackItem), - name_buffer: std.ArrayList(u8), + stack: std.ArrayListUnmanaged(StackItem), + name_buffer: std.ArrayListUnmanaged(u8), + allocator: Allocator, - pub const WalkerEntry = struct { + pub const Entry = struct { /// The containing directory. This can be used to operate directly on `basename` /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. /// The directory remains open until `next` or `deinit` is called. dir: Dir, - basename: []const u8, - path: []const u8, + basename: [:0]const u8, + path: [:0]const u8, kind: Dir.Entry.Kind, }; @@ -647,7 +668,8 @@ pub const Walker = struct { /// After each call to this function, and on deinit(), the memory returned /// from this function becomes invalid. A copy must be made in order to keep /// a reference to the path. - pub fn next(self: *Walker) !?WalkerEntry { + pub fn next(self: *Walker) !?Walker.Entry { + const gpa = self.allocator; while (self.stack.items.len != 0) { // `top` and `containing` become invalid after appending to `self.stack` var top = &self.stack.items[self.stack.items.len - 1]; @@ -666,10 +688,12 @@ pub const Walker = struct { }) |base| { self.name_buffer.shrinkRetainingCapacity(dirname_len); if (self.name_buffer.items.len != 0) { - try self.name_buffer.append(fs.path.sep); + try self.name_buffer.append(gpa, fs.path.sep); dirname_len += 1; } - try self.name_buffer.appendSlice(base.name); + try self.name_buffer.ensureUnusedCapacity(gpa, base.name.len + 1); + self.name_buffer.appendSliceAssumeCapacity(base.name); + self.name_buffer.appendAssumeCapacity(0); if (base.kind == .directory) { var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { error.NameTooLong => unreachable, // no path sep in base.name @@ -677,18 +701,18 @@ pub const Walker = struct { }; { errdefer new_dir.close(); - try self.stack.append(StackItem{ + try self.stack.append(gpa, .{ .iter = new_dir.iterateAssumeFirstIteration(), - .dirname_len = self.name_buffer.items.len, + .dirname_len = self.name_buffer.items.len - 1, }); top = &self.stack.items[self.stack.items.len - 1]; containing = &self.stack.items[self.stack.items.len - 2]; } } - return WalkerEntry{ + return .{ .dir = containing.iter.dir, - .basename = self.name_buffer.items[dirname_len..], - .path = self.name_buffer.items, + .basename = self.name_buffer.items[dirname_len .. self.name_buffer.items.len - 1 :0], + .path = self.name_buffer.items[0 .. self.name_buffer.items.len - 1 :0], .kind = base.kind, }; } else { @@ -702,37 +726,39 @@ pub const Walker = struct { } pub fn deinit(self: *Walker) void { + const gpa = self.allocator; // Close any remaining directories except the initial one (which is always at index 0) if (self.stack.items.len > 1) { for (self.stack.items[1..]) |*item| { item.iter.dir.close(); } } - self.stack.deinit(); - self.name_buffer.deinit(); + self.stack.deinit(gpa); + self.name_buffer.deinit(gpa); } }; /// Recursively iterates over a directory. +/// /// `self` must have been opened with `OpenDirOptions{.iterate = true}`. -/// Must call `Walker.deinit` when done. +/// +/// `Walker.deinit` releases allocated memory and directory handles. +/// /// The order of returned file system entries is undefined. +/// /// `self` will not be closed after walking it. -pub fn walk(self: Dir, allocator: Allocator) !Walker { - var name_buffer = std.ArrayList(u8).init(allocator); - errdefer name_buffer.deinit(); - - var stack = std.ArrayList(Walker.StackItem).init(allocator); - errdefer stack.deinit(); +pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker { + var stack: std.ArrayListUnmanaged(Walker.StackItem) = .{}; - try stack.append(Walker.StackItem{ + try stack.append(allocator, .{ .iter = self.iterate(), .dirname_len = 0, }); - return Walker{ + return .{ .stack = stack, - .name_buffer = name_buffer, + .name_buffer = .{}, + .allocator = allocator, }; } @@ -1429,6 +1455,27 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) Open .wasi => { return openDir(self, mem.sliceTo(sub_path_c, 0), args); }, + .haiku => { + const rc = posix.system._kern_open_dir(self.fd, sub_path_c); + if (rc >= 0) return .{ .fd = rc }; + switch (@as(posix.E, @enumFromInt(rc))) { + .FAULT => unreachable, + .INVAL => unreachable, + .BADF => unreachable, + .ACCES => return error.AccessDenied, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOTDIR => return error.NotDir, + .PERM => return error.AccessDenied, + .BUSY => return error.DeviceBusy, + else => |err| return posix.unexpectedErrno(err), + } + }, else => { var symlink_flags: posix.O = .{ .ACCMODE = .RDONLY, diff --git a/lib/std/hash/crc.zig b/lib/std/hash/crc.zig index 0ac4de761e..732c03e721 100644 --- a/lib/std/hash/crc.zig +++ b/lib/std/hash/crc.zig @@ -1,285 +1,911 @@ -// There are two implementations of CRC32 implemented with the following key characteristics: -// -// - Crc32WithPoly uses 8Kb of tables but is ~10x faster than the small method. -// -// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is -// still moderately fast just slow relative to the slicing approach. - -const std = @import("std"); -const builtin = @import("builtin"); -const debug = std.debug; -const testing = std.testing; - -pub usingnamespace @import("crc/catalog.zig"); - -pub fn Algorithm(comptime W: type) type { - return struct { - polynomial: W, - initial: W, - reflect_input: bool, - reflect_output: bool, - xor_output: W, - }; -} +//! This file is auto-generated by tools/update_crc_catalog.zig. -pub fn Crc(comptime W: type, comptime algorithm: Algorithm(W)) type { - return struct { - const Self = @This(); - const I = if (@bitSizeOf(W) < 8) u8 else W; - const lookup_table = blk: { - @setEvalBranchQuota(2500); - - const poly = if (algorithm.reflect_input) - @bitReverse(@as(I, algorithm.polynomial)) >> (@bitSizeOf(I) - @bitSizeOf(W)) - else - @as(I, algorithm.polynomial) << (@bitSizeOf(I) - @bitSizeOf(W)); - - var table: [256]I = undefined; - for (&table, 0..) |*e, i| { - var crc: I = i; - if (algorithm.reflect_input) { - var j: usize = 0; - while (j < 8) : (j += 1) { - crc = (crc >> 1) ^ ((crc & 1) * poly); - } - } else { - crc <<= @bitSizeOf(I) - 8; - var j: usize = 0; - while (j < 8) : (j += 1) { - crc = (crc << 1) ^ (((crc >> (@bitSizeOf(I) - 1)) & 1) * poly); - } - } - e.* = crc; - } - break :blk table; - }; - - crc: I, - - pub fn init() Self { - const initial = if (algorithm.reflect_input) - @bitReverse(@as(I, algorithm.initial)) >> (@bitSizeOf(I) - @bitSizeOf(W)) - else - @as(I, algorithm.initial) << (@bitSizeOf(I) - @bitSizeOf(W)); - return Self{ .crc = initial }; - } - - inline fn tableEntry(index: I) I { - return lookup_table[@as(u8, @intCast(index & 0xFF))]; - } - - pub fn update(self: *Self, bytes: []const u8) void { - var i: usize = 0; - if (@bitSizeOf(I) <= 8) { - while (i < bytes.len) : (i += 1) { - self.crc = tableEntry(self.crc ^ bytes[i]); - } - } else if (algorithm.reflect_input) { - while (i < bytes.len) : (i += 1) { - const table_index = self.crc ^ bytes[i]; - self.crc = tableEntry(table_index) ^ (self.crc >> 8); - } - } else { - while (i < bytes.len) : (i += 1) { - const table_index = (self.crc >> (@bitSizeOf(I) - 8)) ^ bytes[i]; - self.crc = tableEntry(table_index) ^ (self.crc << 8); - } - } - } - - pub fn final(self: Self) W { - var c = self.crc; - if (algorithm.reflect_input != algorithm.reflect_output) { - c = @bitReverse(c); - } - if (!algorithm.reflect_output) { - c >>= @bitSizeOf(I) - @bitSizeOf(W); - } - return @as(W, @intCast(c ^ algorithm.xor_output)); - } - - pub fn hash(bytes: []const u8) W { - var c = Self.init(); - c.update(bytes); - return c.final(); - } - }; -} +const impl = @import("crc/impl.zig"); -pub const Polynomial = enum(u32) { - IEEE = 0xedb88320, - Castagnoli = 0x82f63b78, - Koopman = 0xeb31d82e, - _, -}; +pub const Crc = impl.Crc; +pub const Polynomial = impl.Polynomial; +pub const Crc32WithPoly = impl.Crc32WithPoly; +pub const Crc32SmallWithPoly = impl.Crc32SmallWithPoly; // IEEE is by far the most common CRC and so is aliased by default. pub const Crc32 = Crc32WithPoly(.IEEE); -// slicing-by-8 crc32 implementation. -pub fn Crc32WithPoly(comptime poly: Polynomial) type { - return struct { - const Self = @This(); - const lookup_tables = block: { - @setEvalBranchQuota(20000); - var tables: [8][256]u32 = undefined; - - for (&tables[0], 0..) |*e, i| { - var crc = @as(u32, @intCast(i)); - var j: usize = 0; - while (j < 8) : (j += 1) { - if (crc & 1 == 1) { - crc = (crc >> 1) ^ @intFromEnum(poly); - } else { - crc = (crc >> 1); - } - } - e.* = crc; - } - - var i: usize = 0; - while (i < 256) : (i += 1) { - var crc = tables[0][i]; - var j: usize = 1; - while (j < 8) : (j += 1) { - const index: u8 = @truncate(crc); - crc = tables[0][index] ^ (crc >> 8); - tables[j][i] = crc; - } - } - - break :block tables; - }; - - crc: u32, - - pub fn init() Self { - return Self{ .crc = 0xffffffff }; - } - - pub fn update(self: *Self, input: []const u8) void { - var i: usize = 0; - while (i + 8 <= input.len) : (i += 8) { - const p = input[i..][0..8]; - - // Unrolling this way gives ~50Mb/s increase - self.crc ^= std.mem.readInt(u32, p[0..4], .little); - - self.crc = - lookup_tables[0][p[7]] ^ - lookup_tables[1][p[6]] ^ - lookup_tables[2][p[5]] ^ - lookup_tables[3][p[4]] ^ - lookup_tables[4][@as(u8, @truncate(self.crc >> 24))] ^ - lookup_tables[5][@as(u8, @truncate(self.crc >> 16))] ^ - lookup_tables[6][@as(u8, @truncate(self.crc >> 8))] ^ - lookup_tables[7][@as(u8, @truncate(self.crc >> 0))]; - } - - while (i < input.len) : (i += 1) { - const index = @as(u8, @truncate(self.crc)) ^ input[i]; - self.crc = (self.crc >> 8) ^ lookup_tables[0][index]; - } - } - - pub fn final(self: *Self) u32 { - return ~self.crc; - } - - pub fn hash(input: []const u8) u32 { - var c = Self.init(); - c.update(input); - return c.final(); - } - }; -} - -const verify = @import("verify.zig"); - -test "crc32 ieee" { - const Crc32Ieee = Crc32WithPoly(.IEEE); - - try testing.expect(Crc32Ieee.hash("") == 0x00000000); - try testing.expect(Crc32Ieee.hash("a") == 0xe8b7be43); - try testing.expect(Crc32Ieee.hash("abc") == 0x352441c2); -} - -test "crc32 castagnoli" { - const Crc32Castagnoli = Crc32WithPoly(.Castagnoli); - - try testing.expect(Crc32Castagnoli.hash("") == 0x00000000); - try testing.expect(Crc32Castagnoli.hash("a") == 0xc1d04330); - try testing.expect(Crc32Castagnoli.hash("abc") == 0x364b3fb7); -} - -test "crc32 iterative" { - try verify.iterativeApi(Crc32WithPoly(.IEEE)); -} - -// half-byte lookup table implementation. -pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type { - return struct { - const Self = @This(); - const lookup_table = block: { - var table: [16]u32 = undefined; - - for (&table, 0..) |*e, i| { - var crc = @as(u32, @intCast(i * 16)); - var j: usize = 0; - while (j < 8) : (j += 1) { - if (crc & 1 == 1) { - crc = (crc >> 1) ^ @intFromEnum(poly); - } else { - crc = (crc >> 1); - } - } - e.* = crc; - } - - break :block table; - }; - - crc: u32, - - pub fn init() Self { - return Self{ .crc = 0xffffffff }; - } - - pub fn update(self: *Self, input: []const u8) void { - for (input) |b| { - self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 0)))] ^ (self.crc >> 4); - self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 4)))] ^ (self.crc >> 4); - } - } - - pub fn final(self: *Self) u32 { - return ~self.crc; - } - - pub fn hash(input: []const u8) u32 { - var c = Self.init(); - c.update(input); - return c.final(); - } - }; -} - -test "small crc32 iterative" { - try verify.iterativeApi(Crc32SmallWithPoly(.IEEE)); +test { + _ = @import("crc/test.zig"); } -test "small crc32 ieee" { - const Crc32Ieee = Crc32SmallWithPoly(.IEEE); - - try testing.expect(Crc32Ieee.hash("") == 0x00000000); - try testing.expect(Crc32Ieee.hash("a") == 0xe8b7be43); - try testing.expect(Crc32Ieee.hash("abc") == 0x352441c2); -} - -test "small crc32 castagnoli" { - const Crc32Castagnoli = Crc32SmallWithPoly(.Castagnoli); - - try testing.expect(Crc32Castagnoli.hash("") == 0x00000000); - try testing.expect(Crc32Castagnoli.hash("a") == 0xc1d04330); - try testing.expect(Crc32Castagnoli.hash("abc") == 0x364b3fb7); -} +pub const Crc3Gsm = Crc(u3, .{ + .polynomial = 0x3, + .initial = 0x0, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x7, +}); + +pub const Crc3Rohc = Crc(u3, .{ + .polynomial = 0x3, + .initial = 0x7, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0, +}); + +pub const Crc4G704 = Crc(u4, .{ + .polynomial = 0x3, + .initial = 0x0, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0, +}); + +pub const Crc4Interlaken = Crc(u4, .{ + .polynomial = 0x3, + .initial = 0xf, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xf, +}); + +pub const Crc5EpcC1g2 = Crc(u5, .{ + .polynomial = 0x09, + .initial = 0x09, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc5G704 = Crc(u5, .{ + .polynomial = 0x15, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc5Usb = Crc(u5, .{ + .polynomial = 0x05, + .initial = 0x1f, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x1f, +}); + +pub const Crc6Cdma2000A = Crc(u6, .{ + .polynomial = 0x27, + .initial = 0x3f, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc6Cdma2000B = Crc(u6, .{ + .polynomial = 0x07, + .initial = 0x3f, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc6Darc = Crc(u6, .{ + .polynomial = 0x19, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc6G704 = Crc(u6, .{ + .polynomial = 0x03, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc6Gsm = Crc(u6, .{ + .polynomial = 0x2f, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x3f, +}); + +pub const Crc7Mmc = Crc(u7, .{ + .polynomial = 0x09, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc7Rohc = Crc(u7, .{ + .polynomial = 0x4f, + .initial = 0x7f, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc7Umts = Crc(u7, .{ + .polynomial = 0x45, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Autosar = Crc(u8, .{ + .polynomial = 0x2f, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xff, +}); + +pub const Crc8Bluetooth = Crc(u8, .{ + .polynomial = 0xa7, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8Cdma2000 = Crc(u8, .{ + .polynomial = 0x9b, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Darc = Crc(u8, .{ + .polynomial = 0x39, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8DvbS2 = Crc(u8, .{ + .polynomial = 0xd5, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8GsmA = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8GsmB = Crc(u8, .{ + .polynomial = 0x49, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xff, +}); + +pub const Crc8Hitag = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8I4321 = Crc(u8, .{ + .polynomial = 0x07, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x55, +}); + +pub const Crc8ICode = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xfd, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Lte = Crc(u8, .{ + .polynomial = 0x9b, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8MaximDow = Crc(u8, .{ + .polynomial = 0x31, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8MifareMad = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xc7, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Nrsc5 = Crc(u8, .{ + .polynomial = 0x31, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Opensafety = Crc(u8, .{ + .polynomial = 0x2f, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Rohc = Crc(u8, .{ + .polynomial = 0x07, + .initial = 0xff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8SaeJ1850 = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xff, +}); + +pub const Crc8Smbus = Crc(u8, .{ + .polynomial = 0x07, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Tech3250 = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8Wcdma = Crc(u8, .{ + .polynomial = 0x9b, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc10Atm = Crc(u10, .{ + .polynomial = 0x233, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc10Cdma2000 = Crc(u10, .{ + .polynomial = 0x3d9, + .initial = 0x3ff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc10Gsm = Crc(u10, .{ + .polynomial = 0x175, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x3ff, +}); + +pub const Crc11Flexray = Crc(u11, .{ + .polynomial = 0x385, + .initial = 0x01a, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc11Umts = Crc(u11, .{ + .polynomial = 0x307, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc12Cdma2000 = Crc(u12, .{ + .polynomial = 0xf13, + .initial = 0xfff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc12Dect = Crc(u12, .{ + .polynomial = 0x80f, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc12Gsm = Crc(u12, .{ + .polynomial = 0xd31, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xfff, +}); + +pub const Crc12Umts = Crc(u12, .{ + .polynomial = 0x80f, + .initial = 0x000, + .reflect_input = false, + .reflect_output = true, + .xor_output = 0x000, +}); + +pub const Crc13Bbc = Crc(u13, .{ + .polynomial = 0x1cf5, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc14Darc = Crc(u14, .{ + .polynomial = 0x0805, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc14Gsm = Crc(u14, .{ + .polynomial = 0x202d, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x3fff, +}); + +pub const Crc15Can = Crc(u15, .{ + .polynomial = 0x4599, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc15Mpt1327 = Crc(u15, .{ + .polynomial = 0x6815, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0001, +}); + +pub const Crc16Arc = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Cdma2000 = Crc(u16, .{ + .polynomial = 0xc867, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Cms = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Dds110 = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0x800d, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16DectR = Crc(u16, .{ + .polynomial = 0x0589, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0001, +}); + +pub const Crc16DectX = Crc(u16, .{ + .polynomial = 0x0589, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Dnp = Crc(u16, .{ + .polynomial = 0x3d65, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffff, +}); + +pub const Crc16En13757 = Crc(u16, .{ + .polynomial = 0x3d65, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffff, +}); + +pub const Crc16Genibus = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffff, +}); + +pub const Crc16Gsm = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffff, +}); + +pub const Crc16Ibm3740 = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16IbmSdlc = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffff, +}); + +pub const Crc16IsoIec144433A = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xc6c6, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Kermit = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Lj1200 = Crc(u16, .{ + .polynomial = 0x6f63, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16M17 = Crc(u16, .{ + .polynomial = 0x5935, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16MaximDow = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffff, +}); + +pub const Crc16Mcrf4xx = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Modbus = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Nrsc5 = Crc(u16, .{ + .polynomial = 0x080b, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16OpensafetyA = Crc(u16, .{ + .polynomial = 0x5935, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16OpensafetyB = Crc(u16, .{ + .polynomial = 0x755b, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Profibus = Crc(u16, .{ + .polynomial = 0x1dcf, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffff, +}); + +pub const Crc16Riello = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xb2aa, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16SpiFujitsu = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x1d0f, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16T10Dif = Crc(u16, .{ + .polynomial = 0x8bb7, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Teledisk = Crc(u16, .{ + .polynomial = 0xa097, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Tms37157 = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x89ec, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Umts = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Usb = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffff, +}); + +pub const Crc16Xmodem = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc17CanFd = Crc(u17, .{ + .polynomial = 0x1685b, + .initial = 0x00000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00000, +}); + +pub const Crc21CanFd = Crc(u21, .{ + .polynomial = 0x102899, + .initial = 0x000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24Ble = Crc(u24, .{ + .polynomial = 0x00065b, + .initial = 0x555555, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x000000, +}); + +pub const Crc24FlexrayA = Crc(u24, .{ + .polynomial = 0x5d6dcb, + .initial = 0xfedcba, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24FlexrayB = Crc(u24, .{ + .polynomial = 0x5d6dcb, + .initial = 0xabcdef, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24Interlaken = Crc(u24, .{ + .polynomial = 0x328b63, + .initial = 0xffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffff, +}); + +pub const Crc24LteA = Crc(u24, .{ + .polynomial = 0x864cfb, + .initial = 0x000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24LteB = Crc(u24, .{ + .polynomial = 0x800063, + .initial = 0x000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24Openpgp = Crc(u24, .{ + .polynomial = 0x864cfb, + .initial = 0xb704ce, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24Os9 = Crc(u24, .{ + .polynomial = 0x800063, + .initial = 0xffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffff, +}); + +pub const Crc30Cdma = Crc(u30, .{ + .polynomial = 0x2030b9c7, + .initial = 0x3fffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x3fffffff, +}); + +pub const Crc31Philips = Crc(u31, .{ + .polynomial = 0x04c11db7, + .initial = 0x7fffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x7fffffff, +}); + +pub const Crc32Aixm = Crc(u32, .{ + .polynomial = 0x814141ab, + .initial = 0x00000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00000000, +}); + +pub const Crc32Autosar = Crc(u32, .{ + .polynomial = 0xf4acfb13, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffff, +}); + +pub const Crc32Base91D = Crc(u32, .{ + .polynomial = 0xa833982b, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffff, +}); + +pub const Crc32Bzip2 = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0xffffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffffff, +}); + +pub const Crc32CdRomEdc = Crc(u32, .{ + .polynomial = 0x8001801b, + .initial = 0x00000000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00000000, +}); + +pub const Crc32Cksum = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0x00000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffffff, +}); + +pub const Crc32Iscsi = Crc(u32, .{ + .polynomial = 0x1edc6f41, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffff, +}); + +pub const Crc32IsoHdlc = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffff, +}); + +pub const Crc32Jamcrc = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00000000, +}); + +pub const Crc32Mef = Crc(u32, .{ + .polynomial = 0x741b8cd7, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00000000, +}); + +pub const Crc32Mpeg2 = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0xffffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00000000, +}); + +pub const Crc32Xfer = Crc(u32, .{ + .polynomial = 0x000000af, + .initial = 0x00000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00000000, +}); + +pub const Crc40Gsm = Crc(u40, .{ + .polynomial = 0x0004820009, + .initial = 0x0000000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffffffff, +}); + +pub const Crc64Ecma182 = Crc(u64, .{ + .polynomial = 0x42f0e1eba9ea3693, + .initial = 0x0000000000000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000000000000000, +}); + +pub const Crc64GoIso = Crc(u64, .{ + .polynomial = 0x000000000000001b, + .initial = 0xffffffffffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffffffffffff, +}); + +pub const Crc64Ms = Crc(u64, .{ + .polynomial = 0x259c84cba6426349, + .initial = 0xffffffffffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000000000000000, +}); + +pub const Crc64Redis = Crc(u64, .{ + .polynomial = 0xad93d23594c935a9, + .initial = 0x0000000000000000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000000000000000, +}); + +pub const Crc64We = Crc(u64, .{ + .polynomial = 0x42f0e1eba9ea3693, + .initial = 0xffffffffffffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffffffffffffff, +}); + +pub const Crc64Xz = Crc(u64, .{ + .polynomial = 0x42f0e1eba9ea3693, + .initial = 0xffffffffffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffffffffffff, +}); + +pub const Crc82Darc = Crc(u82, .{ + .polynomial = 0x0308c0111011401440411, + .initial = 0x000000000000000000000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x000000000000000000000, +}); diff --git a/lib/std/hash/crc/catalog.zig b/lib/std/hash/crc/catalog.zig deleted file mode 100644 index ed08accce6..0000000000 --- a/lib/std/hash/crc/catalog.zig +++ /dev/null @@ -1,903 +0,0 @@ -//! This file is auto-generated by tools/update_crc_catalog.zig. - -const Crc = @import("../crc.zig").Crc; - -test { - _ = @import("catalog_test.zig"); -} - -pub const Crc3Gsm = Crc(u3, .{ - .polynomial = 0x3, - .initial = 0x0, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x7, -}); - -pub const Crc3Rohc = Crc(u3, .{ - .polynomial = 0x3, - .initial = 0x7, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0, -}); - -pub const Crc4G704 = Crc(u4, .{ - .polynomial = 0x3, - .initial = 0x0, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0, -}); - -pub const Crc4Interlaken = Crc(u4, .{ - .polynomial = 0x3, - .initial = 0xf, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xf, -}); - -pub const Crc5EpcC1g2 = Crc(u5, .{ - .polynomial = 0x09, - .initial = 0x09, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc5G704 = Crc(u5, .{ - .polynomial = 0x15, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc5Usb = Crc(u5, .{ - .polynomial = 0x05, - .initial = 0x1f, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x1f, -}); - -pub const Crc6Cdma2000A = Crc(u6, .{ - .polynomial = 0x27, - .initial = 0x3f, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc6Cdma2000B = Crc(u6, .{ - .polynomial = 0x07, - .initial = 0x3f, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc6Darc = Crc(u6, .{ - .polynomial = 0x19, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc6G704 = Crc(u6, .{ - .polynomial = 0x03, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc6Gsm = Crc(u6, .{ - .polynomial = 0x2f, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x3f, -}); - -pub const Crc7Mmc = Crc(u7, .{ - .polynomial = 0x09, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc7Rohc = Crc(u7, .{ - .polynomial = 0x4f, - .initial = 0x7f, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc7Umts = Crc(u7, .{ - .polynomial = 0x45, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Autosar = Crc(u8, .{ - .polynomial = 0x2f, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xff, -}); - -pub const Crc8Bluetooth = Crc(u8, .{ - .polynomial = 0xa7, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8Cdma2000 = Crc(u8, .{ - .polynomial = 0x9b, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Darc = Crc(u8, .{ - .polynomial = 0x39, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8DvbS2 = Crc(u8, .{ - .polynomial = 0xd5, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8GsmA = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8GsmB = Crc(u8, .{ - .polynomial = 0x49, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xff, -}); - -pub const Crc8Hitag = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8I4321 = Crc(u8, .{ - .polynomial = 0x07, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x55, -}); - -pub const Crc8ICode = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xfd, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Lte = Crc(u8, .{ - .polynomial = 0x9b, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8MaximDow = Crc(u8, .{ - .polynomial = 0x31, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8MifareMad = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xc7, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Nrsc5 = Crc(u8, .{ - .polynomial = 0x31, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Opensafety = Crc(u8, .{ - .polynomial = 0x2f, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Rohc = Crc(u8, .{ - .polynomial = 0x07, - .initial = 0xff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8SaeJ1850 = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xff, -}); - -pub const Crc8Smbus = Crc(u8, .{ - .polynomial = 0x07, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Tech3250 = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8Wcdma = Crc(u8, .{ - .polynomial = 0x9b, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc10Atm = Crc(u10, .{ - .polynomial = 0x233, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc10Cdma2000 = Crc(u10, .{ - .polynomial = 0x3d9, - .initial = 0x3ff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc10Gsm = Crc(u10, .{ - .polynomial = 0x175, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x3ff, -}); - -pub const Crc11Flexray = Crc(u11, .{ - .polynomial = 0x385, - .initial = 0x01a, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc11Umts = Crc(u11, .{ - .polynomial = 0x307, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc12Cdma2000 = Crc(u12, .{ - .polynomial = 0xf13, - .initial = 0xfff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc12Dect = Crc(u12, .{ - .polynomial = 0x80f, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc12Gsm = Crc(u12, .{ - .polynomial = 0xd31, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xfff, -}); - -pub const Crc12Umts = Crc(u12, .{ - .polynomial = 0x80f, - .initial = 0x000, - .reflect_input = false, - .reflect_output = true, - .xor_output = 0x000, -}); - -pub const Crc13Bbc = Crc(u13, .{ - .polynomial = 0x1cf5, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc14Darc = Crc(u14, .{ - .polynomial = 0x0805, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc14Gsm = Crc(u14, .{ - .polynomial = 0x202d, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x3fff, -}); - -pub const Crc15Can = Crc(u15, .{ - .polynomial = 0x4599, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc15Mpt1327 = Crc(u15, .{ - .polynomial = 0x6815, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0001, -}); - -pub const Crc16Arc = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Cdma2000 = Crc(u16, .{ - .polynomial = 0xc867, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Cms = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Dds110 = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0x800d, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16DectR = Crc(u16, .{ - .polynomial = 0x0589, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0001, -}); - -pub const Crc16DectX = Crc(u16, .{ - .polynomial = 0x0589, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Dnp = Crc(u16, .{ - .polynomial = 0x3d65, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffff, -}); - -pub const Crc16En13757 = Crc(u16, .{ - .polynomial = 0x3d65, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffff, -}); - -pub const Crc16Genibus = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffff, -}); - -pub const Crc16Gsm = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffff, -}); - -pub const Crc16Ibm3740 = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16IbmSdlc = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffff, -}); - -pub const Crc16IsoIec144433A = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xc6c6, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Kermit = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Lj1200 = Crc(u16, .{ - .polynomial = 0x6f63, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16M17 = Crc(u16, .{ - .polynomial = 0x5935, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16MaximDow = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffff, -}); - -pub const Crc16Mcrf4xx = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Modbus = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Nrsc5 = Crc(u16, .{ - .polynomial = 0x080b, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16OpensafetyA = Crc(u16, .{ - .polynomial = 0x5935, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16OpensafetyB = Crc(u16, .{ - .polynomial = 0x755b, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Profibus = Crc(u16, .{ - .polynomial = 0x1dcf, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffff, -}); - -pub const Crc16Riello = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xb2aa, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16SpiFujitsu = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x1d0f, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16T10Dif = Crc(u16, .{ - .polynomial = 0x8bb7, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Teledisk = Crc(u16, .{ - .polynomial = 0xa097, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Tms37157 = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x89ec, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Umts = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Usb = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffff, -}); - -pub const Crc16Xmodem = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc17CanFd = Crc(u17, .{ - .polynomial = 0x1685b, - .initial = 0x00000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00000, -}); - -pub const Crc21CanFd = Crc(u21, .{ - .polynomial = 0x102899, - .initial = 0x000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24Ble = Crc(u24, .{ - .polynomial = 0x00065b, - .initial = 0x555555, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x000000, -}); - -pub const Crc24FlexrayA = Crc(u24, .{ - .polynomial = 0x5d6dcb, - .initial = 0xfedcba, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24FlexrayB = Crc(u24, .{ - .polynomial = 0x5d6dcb, - .initial = 0xabcdef, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24Interlaken = Crc(u24, .{ - .polynomial = 0x328b63, - .initial = 0xffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffff, -}); - -pub const Crc24LteA = Crc(u24, .{ - .polynomial = 0x864cfb, - .initial = 0x000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24LteB = Crc(u24, .{ - .polynomial = 0x800063, - .initial = 0x000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24Openpgp = Crc(u24, .{ - .polynomial = 0x864cfb, - .initial = 0xb704ce, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24Os9 = Crc(u24, .{ - .polynomial = 0x800063, - .initial = 0xffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffff, -}); - -pub const Crc30Cdma = Crc(u30, .{ - .polynomial = 0x2030b9c7, - .initial = 0x3fffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x3fffffff, -}); - -pub const Crc31Philips = Crc(u31, .{ - .polynomial = 0x04c11db7, - .initial = 0x7fffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x7fffffff, -}); - -pub const Crc32Aixm = Crc(u32, .{ - .polynomial = 0x814141ab, - .initial = 0x00000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00000000, -}); - -pub const Crc32Autosar = Crc(u32, .{ - .polynomial = 0xf4acfb13, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffff, -}); - -pub const Crc32Base91D = Crc(u32, .{ - .polynomial = 0xa833982b, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffff, -}); - -pub const Crc32Bzip2 = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0xffffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffffff, -}); - -pub const Crc32CdRomEdc = Crc(u32, .{ - .polynomial = 0x8001801b, - .initial = 0x00000000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00000000, -}); - -pub const Crc32Cksum = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0x00000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffffff, -}); - -pub const Crc32Iscsi = Crc(u32, .{ - .polynomial = 0x1edc6f41, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffff, -}); - -pub const Crc32IsoHdlc = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffff, -}); - -pub const Crc32Jamcrc = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00000000, -}); - -pub const Crc32Mef = Crc(u32, .{ - .polynomial = 0x741b8cd7, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00000000, -}); - -pub const Crc32Mpeg2 = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0xffffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00000000, -}); - -pub const Crc32Xfer = Crc(u32, .{ - .polynomial = 0x000000af, - .initial = 0x00000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00000000, -}); - -pub const Crc40Gsm = Crc(u40, .{ - .polynomial = 0x0004820009, - .initial = 0x0000000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffffffff, -}); - -pub const Crc64Ecma182 = Crc(u64, .{ - .polynomial = 0x42f0e1eba9ea3693, - .initial = 0x0000000000000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000000000000000, -}); - -pub const Crc64GoIso = Crc(u64, .{ - .polynomial = 0x000000000000001b, - .initial = 0xffffffffffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffffffffffff, -}); - -pub const Crc64Ms = Crc(u64, .{ - .polynomial = 0x259c84cba6426349, - .initial = 0xffffffffffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000000000000000, -}); - -pub const Crc64Redis = Crc(u64, .{ - .polynomial = 0xad93d23594c935a9, - .initial = 0x0000000000000000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000000000000000, -}); - -pub const Crc64We = Crc(u64, .{ - .polynomial = 0x42f0e1eba9ea3693, - .initial = 0xffffffffffffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffffffffffffff, -}); - -pub const Crc64Xz = Crc(u64, .{ - .polynomial = 0x42f0e1eba9ea3693, - .initial = 0xffffffffffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffffffffffff, -}); - -pub const Crc82Darc = Crc(u82, .{ - .polynomial = 0x0308c0111011401440411, - .initial = 0x000000000000000000000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x000000000000000000000, -}); diff --git a/lib/std/hash/crc/impl.zig b/lib/std/hash/crc/impl.zig new file mode 100644 index 0000000000..f0fd5ac14e --- /dev/null +++ b/lib/std/hash/crc/impl.zig @@ -0,0 +1,241 @@ +// There is a generic CRC implementation "Crc()" which can be paramterized via +// the Algorithm struct for a plethora of uses, along with two implementations +// of CRC32 implemented with the following key characteristics: +// +// - Crc32WithPoly uses 8Kb of tables but is ~10x faster than the small method. +// +// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is +// still moderately fast just slow relative to the slicing approach. +// +// The primary interface for all of the standard CRC algorithms is the +// generated file "crc.zig", which uses the implementation code here to define +// many standard CRCs. + +const std = @import("std"); + +pub fn Algorithm(comptime W: type) type { + return struct { + polynomial: W, + initial: W, + reflect_input: bool, + reflect_output: bool, + xor_output: W, + }; +} + +pub fn Crc(comptime W: type, comptime algorithm: Algorithm(W)) type { + return struct { + const Self = @This(); + const I = if (@bitSizeOf(W) < 8) u8 else W; + const lookup_table = blk: { + @setEvalBranchQuota(2500); + + const poly = if (algorithm.reflect_input) + @bitReverse(@as(I, algorithm.polynomial)) >> (@bitSizeOf(I) - @bitSizeOf(W)) + else + @as(I, algorithm.polynomial) << (@bitSizeOf(I) - @bitSizeOf(W)); + + var table: [256]I = undefined; + for (&table, 0..) |*e, i| { + var crc: I = i; + if (algorithm.reflect_input) { + var j: usize = 0; + while (j < 8) : (j += 1) { + crc = (crc >> 1) ^ ((crc & 1) * poly); + } + } else { + crc <<= @bitSizeOf(I) - 8; + var j: usize = 0; + while (j < 8) : (j += 1) { + crc = (crc << 1) ^ (((crc >> (@bitSizeOf(I) - 1)) & 1) * poly); + } + } + e.* = crc; + } + break :blk table; + }; + + crc: I, + + pub fn init() Self { + const initial = if (algorithm.reflect_input) + @bitReverse(@as(I, algorithm.initial)) >> (@bitSizeOf(I) - @bitSizeOf(W)) + else + @as(I, algorithm.initial) << (@bitSizeOf(I) - @bitSizeOf(W)); + return Self{ .crc = initial }; + } + + inline fn tableEntry(index: I) I { + return lookup_table[@as(u8, @intCast(index & 0xFF))]; + } + + pub fn update(self: *Self, bytes: []const u8) void { + var i: usize = 0; + if (@bitSizeOf(I) <= 8) { + while (i < bytes.len) : (i += 1) { + self.crc = tableEntry(self.crc ^ bytes[i]); + } + } else if (algorithm.reflect_input) { + while (i < bytes.len) : (i += 1) { + const table_index = self.crc ^ bytes[i]; + self.crc = tableEntry(table_index) ^ (self.crc >> 8); + } + } else { + while (i < bytes.len) : (i += 1) { + const table_index = (self.crc >> (@bitSizeOf(I) - 8)) ^ bytes[i]; + self.crc = tableEntry(table_index) ^ (self.crc << 8); + } + } + } + + pub fn final(self: Self) W { + var c = self.crc; + if (algorithm.reflect_input != algorithm.reflect_output) { + c = @bitReverse(c); + } + if (!algorithm.reflect_output) { + c >>= @bitSizeOf(I) - @bitSizeOf(W); + } + return @as(W, @intCast(c ^ algorithm.xor_output)); + } + + pub fn hash(bytes: []const u8) W { + var c = Self.init(); + c.update(bytes); + return c.final(); + } + }; +} + +pub const Polynomial = enum(u32) { + IEEE = 0xedb88320, + Castagnoli = 0x82f63b78, + Koopman = 0xeb31d82e, + _, +}; + +// slicing-by-8 crc32 implementation. +pub fn Crc32WithPoly(comptime poly: Polynomial) type { + return struct { + const Self = @This(); + const lookup_tables = block: { + @setEvalBranchQuota(20000); + var tables: [8][256]u32 = undefined; + + for (&tables[0], 0..) |*e, i| { + var crc = @as(u32, @intCast(i)); + var j: usize = 0; + while (j < 8) : (j += 1) { + if (crc & 1 == 1) { + crc = (crc >> 1) ^ @intFromEnum(poly); + } else { + crc = (crc >> 1); + } + } + e.* = crc; + } + + var i: usize = 0; + while (i < 256) : (i += 1) { + var crc = tables[0][i]; + var j: usize = 1; + while (j < 8) : (j += 1) { + const index: u8 = @truncate(crc); + crc = tables[0][index] ^ (crc >> 8); + tables[j][i] = crc; + } + } + + break :block tables; + }; + + crc: u32, + + pub fn init() Self { + return Self{ .crc = 0xffffffff }; + } + + pub fn update(self: *Self, input: []const u8) void { + var i: usize = 0; + while (i + 8 <= input.len) : (i += 8) { + const p = input[i..][0..8]; + + // Unrolling this way gives ~50Mb/s increase + self.crc ^= std.mem.readInt(u32, p[0..4], .little); + + self.crc = + lookup_tables[0][p[7]] ^ + lookup_tables[1][p[6]] ^ + lookup_tables[2][p[5]] ^ + lookup_tables[3][p[4]] ^ + lookup_tables[4][@as(u8, @truncate(self.crc >> 24))] ^ + lookup_tables[5][@as(u8, @truncate(self.crc >> 16))] ^ + lookup_tables[6][@as(u8, @truncate(self.crc >> 8))] ^ + lookup_tables[7][@as(u8, @truncate(self.crc >> 0))]; + } + + while (i < input.len) : (i += 1) { + const index = @as(u8, @truncate(self.crc)) ^ input[i]; + self.crc = (self.crc >> 8) ^ lookup_tables[0][index]; + } + } + + pub fn final(self: *Self) u32 { + return ~self.crc; + } + + pub fn hash(input: []const u8) u32 { + var c = Self.init(); + c.update(input); + return c.final(); + } + }; +} + +// half-byte lookup table implementation. +pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type { + return struct { + const Self = @This(); + const lookup_table = block: { + var table: [16]u32 = undefined; + + for (&table, 0..) |*e, i| { + var crc = @as(u32, @intCast(i * 16)); + var j: usize = 0; + while (j < 8) : (j += 1) { + if (crc & 1 == 1) { + crc = (crc >> 1) ^ @intFromEnum(poly); + } else { + crc = (crc >> 1); + } + } + e.* = crc; + } + + break :block table; + }; + + crc: u32, + + pub fn init() Self { + return Self{ .crc = 0xffffffff }; + } + + pub fn update(self: *Self, input: []const u8) void { + for (input) |b| { + self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 0)))] ^ (self.crc >> 4); + self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 4)))] ^ (self.crc >> 4); + } + } + + pub fn final(self: *Self) u32 { + return ~self.crc; + } + + pub fn hash(input: []const u8) u32 { + var c = Self.init(); + c.update(input); + return c.final(); + } + }; +} diff --git a/lib/std/hash/crc/catalog_test.zig b/lib/std/hash/crc/test.zig index ab89745ca8..c9cabf6463 100644 --- a/lib/std/hash/crc/catalog_test.zig +++ b/lib/std/hash/crc/test.zig @@ -2,10 +2,29 @@ const std = @import("std"); const testing = std.testing; -const catalog = @import("catalog.zig"); +const verify = @import("../verify.zig"); +const crc = @import("../crc.zig"); + +test "crc32 ieee" { + inline for ([2]type{ crc.Crc32WithPoly(.IEEE), crc.Crc32SmallWithPoly(.IEEE) }) |ieee| { + try testing.expect(ieee.hash("") == 0x00000000); + try testing.expect(ieee.hash("a") == 0xe8b7be43); + try testing.expect(ieee.hash("abc") == 0x352441c2); + try verify.iterativeApi(ieee); + } +} + +test "crc32 castagnoli" { + inline for ([2]type{ crc.Crc32WithPoly(.Castagnoli), crc.Crc32SmallWithPoly(.Castagnoli) }) |casta| { + try testing.expect(casta.hash("") == 0x00000000); + try testing.expect(casta.hash("a") == 0xc1d04330); + try testing.expect(casta.hash("abc") == 0x364b3fb7); + try verify.iterativeApi(casta); + } +} test "CRC-3/GSM" { - const Crc3Gsm = catalog.Crc3Gsm; + const Crc3Gsm = crc.Crc3Gsm; try testing.expectEqual(@as(u3, 0x4), Crc3Gsm.hash("123456789")); @@ -16,7 +35,7 @@ test "CRC-3/GSM" { } test "CRC-3/ROHC" { - const Crc3Rohc = catalog.Crc3Rohc; + const Crc3Rohc = crc.Crc3Rohc; try testing.expectEqual(@as(u3, 0x6), Crc3Rohc.hash("123456789")); @@ -27,7 +46,7 @@ test "CRC-3/ROHC" { } test "CRC-4/G-704" { - const Crc4G704 = catalog.Crc4G704; + const Crc4G704 = crc.Crc4G704; try testing.expectEqual(@as(u4, 0x7), Crc4G704.hash("123456789")); @@ -38,7 +57,7 @@ test "CRC-4/G-704" { } test "CRC-4/INTERLAKEN" { - const Crc4Interlaken = catalog.Crc4Interlaken; + const Crc4Interlaken = crc.Crc4Interlaken; try testing.expectEqual(@as(u4, 0xb), Crc4Interlaken.hash("123456789")); @@ -49,7 +68,7 @@ test "CRC-4/INTERLAKEN" { } test "CRC-5/EPC-C1G2" { - const Crc5EpcC1g2 = catalog.Crc5EpcC1g2; + const Crc5EpcC1g2 = crc.Crc5EpcC1g2; try testing.expectEqual(@as(u5, 0x00), Crc5EpcC1g2.hash("123456789")); @@ -60,7 +79,7 @@ test "CRC-5/EPC-C1G2" { } test "CRC-5/G-704" { - const Crc5G704 = catalog.Crc5G704; + const Crc5G704 = crc.Crc5G704; try testing.expectEqual(@as(u5, 0x07), Crc5G704.hash("123456789")); @@ -71,7 +90,7 @@ test "CRC-5/G-704" { } test "CRC-5/USB" { - const Crc5Usb = catalog.Crc5Usb; + const Crc5Usb = crc.Crc5Usb; try testing.expectEqual(@as(u5, 0x19), Crc5Usb.hash("123456789")); @@ -82,7 +101,7 @@ test "CRC-5/USB" { } test "CRC-6/CDMA2000-A" { - const Crc6Cdma2000A = catalog.Crc6Cdma2000A; + const Crc6Cdma2000A = crc.Crc6Cdma2000A; try testing.expectEqual(@as(u6, 0x0d), Crc6Cdma2000A.hash("123456789")); @@ -93,7 +112,7 @@ test "CRC-6/CDMA2000-A" { } test "CRC-6/CDMA2000-B" { - const Crc6Cdma2000B = catalog.Crc6Cdma2000B; + const Crc6Cdma2000B = crc.Crc6Cdma2000B; try testing.expectEqual(@as(u6, 0x3b), Crc6Cdma2000B.hash("123456789")); @@ -104,7 +123,7 @@ test "CRC-6/CDMA2000-B" { } test "CRC-6/DARC" { - const Crc6Darc = catalog.Crc6Darc; + const Crc6Darc = crc.Crc6Darc; try testing.expectEqual(@as(u6, 0x26), Crc6Darc.hash("123456789")); @@ -115,7 +134,7 @@ test "CRC-6/DARC" { } test "CRC-6/G-704" { - const Crc6G704 = catalog.Crc6G704; + const Crc6G704 = crc.Crc6G704; try testing.expectEqual(@as(u6, 0x06), Crc6G704.hash("123456789")); @@ -126,7 +145,7 @@ test "CRC-6/G-704" { } test "CRC-6/GSM" { - const Crc6Gsm = catalog.Crc6Gsm; + const Crc6Gsm = crc.Crc6Gsm; try testing.expectEqual(@as(u6, 0x13), Crc6Gsm.hash("123456789")); @@ -137,7 +156,7 @@ test "CRC-6/GSM" { } test "CRC-7/MMC" { - const Crc7Mmc = catalog.Crc7Mmc; + const Crc7Mmc = crc.Crc7Mmc; try testing.expectEqual(@as(u7, 0x75), Crc7Mmc.hash("123456789")); @@ -148,7 +167,7 @@ test "CRC-7/MMC" { } test "CRC-7/ROHC" { - const Crc7Rohc = catalog.Crc7Rohc; + const Crc7Rohc = crc.Crc7Rohc; try testing.expectEqual(@as(u7, 0x53), Crc7Rohc.hash("123456789")); @@ -159,7 +178,7 @@ test "CRC-7/ROHC" { } test "CRC-7/UMTS" { - const Crc7Umts = catalog.Crc7Umts; + const Crc7Umts = crc.Crc7Umts; try testing.expectEqual(@as(u7, 0x61), Crc7Umts.hash("123456789")); @@ -170,7 +189,7 @@ test "CRC-7/UMTS" { } test "CRC-8/AUTOSAR" { - const Crc8Autosar = catalog.Crc8Autosar; + const Crc8Autosar = crc.Crc8Autosar; try testing.expectEqual(@as(u8, 0xdf), Crc8Autosar.hash("123456789")); @@ -181,7 +200,7 @@ test "CRC-8/AUTOSAR" { } test "CRC-8/BLUETOOTH" { - const Crc8Bluetooth = catalog.Crc8Bluetooth; + const Crc8Bluetooth = crc.Crc8Bluetooth; try testing.expectEqual(@as(u8, 0x26), Crc8Bluetooth.hash("123456789")); @@ -192,7 +211,7 @@ test "CRC-8/BLUETOOTH" { } test "CRC-8/CDMA2000" { - const Crc8Cdma2000 = catalog.Crc8Cdma2000; + const Crc8Cdma2000 = crc.Crc8Cdma2000; try testing.expectEqual(@as(u8, 0xda), Crc8Cdma2000.hash("123456789")); @@ -203,7 +222,7 @@ test "CRC-8/CDMA2000" { } test "CRC-8/DARC" { - const Crc8Darc = catalog.Crc8Darc; + const Crc8Darc = crc.Crc8Darc; try testing.expectEqual(@as(u8, 0x15), Crc8Darc.hash("123456789")); @@ -214,7 +233,7 @@ test "CRC-8/DARC" { } test "CRC-8/DVB-S2" { - const Crc8DvbS2 = catalog.Crc8DvbS2; + const Crc8DvbS2 = crc.Crc8DvbS2; try testing.expectEqual(@as(u8, 0xbc), Crc8DvbS2.hash("123456789")); @@ -225,7 +244,7 @@ test "CRC-8/DVB-S2" { } test "CRC-8/GSM-A" { - const Crc8GsmA = catalog.Crc8GsmA; + const Crc8GsmA = crc.Crc8GsmA; try testing.expectEqual(@as(u8, 0x37), Crc8GsmA.hash("123456789")); @@ -236,7 +255,7 @@ test "CRC-8/GSM-A" { } test "CRC-8/GSM-B" { - const Crc8GsmB = catalog.Crc8GsmB; + const Crc8GsmB = crc.Crc8GsmB; try testing.expectEqual(@as(u8, 0x94), Crc8GsmB.hash("123456789")); @@ -247,7 +266,7 @@ test "CRC-8/GSM-B" { } test "CRC-8/HITAG" { - const Crc8Hitag = catalog.Crc8Hitag; + const Crc8Hitag = crc.Crc8Hitag; try testing.expectEqual(@as(u8, 0xb4), Crc8Hitag.hash("123456789")); @@ -258,7 +277,7 @@ test "CRC-8/HITAG" { } test "CRC-8/I-432-1" { - const Crc8I4321 = catalog.Crc8I4321; + const Crc8I4321 = crc.Crc8I4321; try testing.expectEqual(@as(u8, 0xa1), Crc8I4321.hash("123456789")); @@ -269,7 +288,7 @@ test "CRC-8/I-432-1" { } test "CRC-8/I-CODE" { - const Crc8ICode = catalog.Crc8ICode; + const Crc8ICode = crc.Crc8ICode; try testing.expectEqual(@as(u8, 0x7e), Crc8ICode.hash("123456789")); @@ -280,7 +299,7 @@ test "CRC-8/I-CODE" { } test "CRC-8/LTE" { - const Crc8Lte = catalog.Crc8Lte; + const Crc8Lte = crc.Crc8Lte; try testing.expectEqual(@as(u8, 0xea), Crc8Lte.hash("123456789")); @@ -291,7 +310,7 @@ test "CRC-8/LTE" { } test "CRC-8/MAXIM-DOW" { - const Crc8MaximDow = catalog.Crc8MaximDow; + const Crc8MaximDow = crc.Crc8MaximDow; try testing.expectEqual(@as(u8, 0xa1), Crc8MaximDow.hash("123456789")); @@ -302,7 +321,7 @@ test "CRC-8/MAXIM-DOW" { } test "CRC-8/MIFARE-MAD" { - const Crc8MifareMad = catalog.Crc8MifareMad; + const Crc8MifareMad = crc.Crc8MifareMad; try testing.expectEqual(@as(u8, 0x99), Crc8MifareMad.hash("123456789")); @@ -313,7 +332,7 @@ test "CRC-8/MIFARE-MAD" { } test "CRC-8/NRSC-5" { - const Crc8Nrsc5 = catalog.Crc8Nrsc5; + const Crc8Nrsc5 = crc.Crc8Nrsc5; try testing.expectEqual(@as(u8, 0xf7), Crc8Nrsc5.hash("123456789")); @@ -324,7 +343,7 @@ test "CRC-8/NRSC-5" { } test "CRC-8/OPENSAFETY" { - const Crc8Opensafety = catalog.Crc8Opensafety; + const Crc8Opensafety = crc.Crc8Opensafety; try testing.expectEqual(@as(u8, 0x3e), Crc8Opensafety.hash("123456789")); @@ -335,7 +354,7 @@ test "CRC-8/OPENSAFETY" { } test "CRC-8/ROHC" { - const Crc8Rohc = catalog.Crc8Rohc; + const Crc8Rohc = crc.Crc8Rohc; try testing.expectEqual(@as(u8, 0xd0), Crc8Rohc.hash("123456789")); @@ -346,7 +365,7 @@ test "CRC-8/ROHC" { } test "CRC-8/SAE-J1850" { - const Crc8SaeJ1850 = catalog.Crc8SaeJ1850; + const Crc8SaeJ1850 = crc.Crc8SaeJ1850; try testing.expectEqual(@as(u8, 0x4b), Crc8SaeJ1850.hash("123456789")); @@ -357,7 +376,7 @@ test "CRC-8/SAE-J1850" { } test "CRC-8/SMBUS" { - const Crc8Smbus = catalog.Crc8Smbus; + const Crc8Smbus = crc.Crc8Smbus; try testing.expectEqual(@as(u8, 0xf4), Crc8Smbus.hash("123456789")); @@ -368,7 +387,7 @@ test "CRC-8/SMBUS" { } test "CRC-8/TECH-3250" { - const Crc8Tech3250 = catalog.Crc8Tech3250; + const Crc8Tech3250 = crc.Crc8Tech3250; try testing.expectEqual(@as(u8, 0x97), Crc8Tech3250.hash("123456789")); @@ -379,7 +398,7 @@ test "CRC-8/TECH-3250" { } test "CRC-8/WCDMA" { - const Crc8Wcdma = catalog.Crc8Wcdma; + const Crc8Wcdma = crc.Crc8Wcdma; try testing.expectEqual(@as(u8, 0x25), Crc8Wcdma.hash("123456789")); @@ -390,7 +409,7 @@ test "CRC-8/WCDMA" { } test "CRC-10/ATM" { - const Crc10Atm = catalog.Crc10Atm; + const Crc10Atm = crc.Crc10Atm; try testing.expectEqual(@as(u10, 0x199), Crc10Atm.hash("123456789")); @@ -401,7 +420,7 @@ test "CRC-10/ATM" { } test "CRC-10/CDMA2000" { - const Crc10Cdma2000 = catalog.Crc10Cdma2000; + const Crc10Cdma2000 = crc.Crc10Cdma2000; try testing.expectEqual(@as(u10, 0x233), Crc10Cdma2000.hash("123456789")); @@ -412,7 +431,7 @@ test "CRC-10/CDMA2000" { } test "CRC-10/GSM" { - const Crc10Gsm = catalog.Crc10Gsm; + const Crc10Gsm = crc.Crc10Gsm; try testing.expectEqual(@as(u10, 0x12a), Crc10Gsm.hash("123456789")); @@ -423,7 +442,7 @@ test "CRC-10/GSM" { } test "CRC-11/FLEXRAY" { - const Crc11Flexray = catalog.Crc11Flexray; + const Crc11Flexray = crc.Crc11Flexray; try testing.expectEqual(@as(u11, 0x5a3), Crc11Flexray.hash("123456789")); @@ -434,7 +453,7 @@ test "CRC-11/FLEXRAY" { } test "CRC-11/UMTS" { - const Crc11Umts = catalog.Crc11Umts; + const Crc11Umts = crc.Crc11Umts; try testing.expectEqual(@as(u11, 0x061), Crc11Umts.hash("123456789")); @@ -445,7 +464,7 @@ test "CRC-11/UMTS" { } test "CRC-12/CDMA2000" { - const Crc12Cdma2000 = catalog.Crc12Cdma2000; + const Crc12Cdma2000 = crc.Crc12Cdma2000; try testing.expectEqual(@as(u12, 0xd4d), Crc12Cdma2000.hash("123456789")); @@ -456,7 +475,7 @@ test "CRC-12/CDMA2000" { } test "CRC-12/DECT" { - const Crc12Dect = catalog.Crc12Dect; + const Crc12Dect = crc.Crc12Dect; try testing.expectEqual(@as(u12, 0xf5b), Crc12Dect.hash("123456789")); @@ -467,7 +486,7 @@ test "CRC-12/DECT" { } test "CRC-12/GSM" { - const Crc12Gsm = catalog.Crc12Gsm; + const Crc12Gsm = crc.Crc12Gsm; try testing.expectEqual(@as(u12, 0xb34), Crc12Gsm.hash("123456789")); @@ -478,7 +497,7 @@ test "CRC-12/GSM" { } test "CRC-12/UMTS" { - const Crc12Umts = catalog.Crc12Umts; + const Crc12Umts = crc.Crc12Umts; try testing.expectEqual(@as(u12, 0xdaf), Crc12Umts.hash("123456789")); @@ -489,7 +508,7 @@ test "CRC-12/UMTS" { } test "CRC-13/BBC" { - const Crc13Bbc = catalog.Crc13Bbc; + const Crc13Bbc = crc.Crc13Bbc; try testing.expectEqual(@as(u13, 0x04fa), Crc13Bbc.hash("123456789")); @@ -500,7 +519,7 @@ test "CRC-13/BBC" { } test "CRC-14/DARC" { - const Crc14Darc = catalog.Crc14Darc; + const Crc14Darc = crc.Crc14Darc; try testing.expectEqual(@as(u14, 0x082d), Crc14Darc.hash("123456789")); @@ -511,7 +530,7 @@ test "CRC-14/DARC" { } test "CRC-14/GSM" { - const Crc14Gsm = catalog.Crc14Gsm; + const Crc14Gsm = crc.Crc14Gsm; try testing.expectEqual(@as(u14, 0x30ae), Crc14Gsm.hash("123456789")); @@ -522,7 +541,7 @@ test "CRC-14/GSM" { } test "CRC-15/CAN" { - const Crc15Can = catalog.Crc15Can; + const Crc15Can = crc.Crc15Can; try testing.expectEqual(@as(u15, 0x059e), Crc15Can.hash("123456789")); @@ -533,7 +552,7 @@ test "CRC-15/CAN" { } test "CRC-15/MPT1327" { - const Crc15Mpt1327 = catalog.Crc15Mpt1327; + const Crc15Mpt1327 = crc.Crc15Mpt1327; try testing.expectEqual(@as(u15, 0x2566), Crc15Mpt1327.hash("123456789")); @@ -544,7 +563,7 @@ test "CRC-15/MPT1327" { } test "CRC-16/ARC" { - const Crc16Arc = catalog.Crc16Arc; + const Crc16Arc = crc.Crc16Arc; try testing.expectEqual(@as(u16, 0xbb3d), Crc16Arc.hash("123456789")); @@ -555,7 +574,7 @@ test "CRC-16/ARC" { } test "CRC-16/CDMA2000" { - const Crc16Cdma2000 = catalog.Crc16Cdma2000; + const Crc16Cdma2000 = crc.Crc16Cdma2000; try testing.expectEqual(@as(u16, 0x4c06), Crc16Cdma2000.hash("123456789")); @@ -566,7 +585,7 @@ test "CRC-16/CDMA2000" { } test "CRC-16/CMS" { - const Crc16Cms = catalog.Crc16Cms; + const Crc16Cms = crc.Crc16Cms; try testing.expectEqual(@as(u16, 0xaee7), Crc16Cms.hash("123456789")); @@ -577,7 +596,7 @@ test "CRC-16/CMS" { } test "CRC-16/DDS-110" { - const Crc16Dds110 = catalog.Crc16Dds110; + const Crc16Dds110 = crc.Crc16Dds110; try testing.expectEqual(@as(u16, 0x9ecf), Crc16Dds110.hash("123456789")); @@ -588,7 +607,7 @@ test "CRC-16/DDS-110" { } test "CRC-16/DECT-R" { - const Crc16DectR = catalog.Crc16DectR; + const Crc16DectR = crc.Crc16DectR; try testing.expectEqual(@as(u16, 0x007e), Crc16DectR.hash("123456789")); @@ -599,7 +618,7 @@ test "CRC-16/DECT-R" { } test "CRC-16/DECT-X" { - const Crc16DectX = catalog.Crc16DectX; + const Crc16DectX = crc.Crc16DectX; try testing.expectEqual(@as(u16, 0x007f), Crc16DectX.hash("123456789")); @@ -610,7 +629,7 @@ test "CRC-16/DECT-X" { } test "CRC-16/DNP" { - const Crc16Dnp = catalog.Crc16Dnp; + const Crc16Dnp = crc.Crc16Dnp; try testing.expectEqual(@as(u16, 0xea82), Crc16Dnp.hash("123456789")); @@ -621,7 +640,7 @@ test "CRC-16/DNP" { } test "CRC-16/EN-13757" { - const Crc16En13757 = catalog.Crc16En13757; + const Crc16En13757 = crc.Crc16En13757; try testing.expectEqual(@as(u16, 0xc2b7), Crc16En13757.hash("123456789")); @@ -632,7 +651,7 @@ test "CRC-16/EN-13757" { } test "CRC-16/GENIBUS" { - const Crc16Genibus = catalog.Crc16Genibus; + const Crc16Genibus = crc.Crc16Genibus; try testing.expectEqual(@as(u16, 0xd64e), Crc16Genibus.hash("123456789")); @@ -643,7 +662,7 @@ test "CRC-16/GENIBUS" { } test "CRC-16/GSM" { - const Crc16Gsm = catalog.Crc16Gsm; + const Crc16Gsm = crc.Crc16Gsm; try testing.expectEqual(@as(u16, 0xce3c), Crc16Gsm.hash("123456789")); @@ -654,7 +673,7 @@ test "CRC-16/GSM" { } test "CRC-16/IBM-3740" { - const Crc16Ibm3740 = catalog.Crc16Ibm3740; + const Crc16Ibm3740 = crc.Crc16Ibm3740; try testing.expectEqual(@as(u16, 0x29b1), Crc16Ibm3740.hash("123456789")); @@ -665,7 +684,7 @@ test "CRC-16/IBM-3740" { } test "CRC-16/IBM-SDLC" { - const Crc16IbmSdlc = catalog.Crc16IbmSdlc; + const Crc16IbmSdlc = crc.Crc16IbmSdlc; try testing.expectEqual(@as(u16, 0x906e), Crc16IbmSdlc.hash("123456789")); @@ -676,7 +695,7 @@ test "CRC-16/IBM-SDLC" { } test "CRC-16/ISO-IEC-14443-3-A" { - const Crc16IsoIec144433A = catalog.Crc16IsoIec144433A; + const Crc16IsoIec144433A = crc.Crc16IsoIec144433A; try testing.expectEqual(@as(u16, 0xbf05), Crc16IsoIec144433A.hash("123456789")); @@ -687,7 +706,7 @@ test "CRC-16/ISO-IEC-14443-3-A" { } test "CRC-16/KERMIT" { - const Crc16Kermit = catalog.Crc16Kermit; + const Crc16Kermit = crc.Crc16Kermit; try testing.expectEqual(@as(u16, 0x2189), Crc16Kermit.hash("123456789")); @@ -698,7 +717,7 @@ test "CRC-16/KERMIT" { } test "CRC-16/LJ1200" { - const Crc16Lj1200 = catalog.Crc16Lj1200; + const Crc16Lj1200 = crc.Crc16Lj1200; try testing.expectEqual(@as(u16, 0xbdf4), Crc16Lj1200.hash("123456789")); @@ -709,7 +728,7 @@ test "CRC-16/LJ1200" { } test "CRC-16/M17" { - const Crc16M17 = catalog.Crc16M17; + const Crc16M17 = crc.Crc16M17; try testing.expectEqual(@as(u16, 0x772b), Crc16M17.hash("123456789")); @@ -720,7 +739,7 @@ test "CRC-16/M17" { } test "CRC-16/MAXIM-DOW" { - const Crc16MaximDow = catalog.Crc16MaximDow; + const Crc16MaximDow = crc.Crc16MaximDow; try testing.expectEqual(@as(u16, 0x44c2), Crc16MaximDow.hash("123456789")); @@ -731,7 +750,7 @@ test "CRC-16/MAXIM-DOW" { } test "CRC-16/MCRF4XX" { - const Crc16Mcrf4xx = catalog.Crc16Mcrf4xx; + const Crc16Mcrf4xx = crc.Crc16Mcrf4xx; try testing.expectEqual(@as(u16, 0x6f91), Crc16Mcrf4xx.hash("123456789")); @@ -742,7 +761,7 @@ test "CRC-16/MCRF4XX" { } test "CRC-16/MODBUS" { - const Crc16Modbus = catalog.Crc16Modbus; + const Crc16Modbus = crc.Crc16Modbus; try testing.expectEqual(@as(u16, 0x4b37), Crc16Modbus.hash("123456789")); @@ -753,7 +772,7 @@ test "CRC-16/MODBUS" { } test "CRC-16/NRSC-5" { - const Crc16Nrsc5 = catalog.Crc16Nrsc5; + const Crc16Nrsc5 = crc.Crc16Nrsc5; try testing.expectEqual(@as(u16, 0xa066), Crc16Nrsc5.hash("123456789")); @@ -764,7 +783,7 @@ test "CRC-16/NRSC-5" { } test "CRC-16/OPENSAFETY-A" { - const Crc16OpensafetyA = catalog.Crc16OpensafetyA; + const Crc16OpensafetyA = crc.Crc16OpensafetyA; try testing.expectEqual(@as(u16, 0x5d38), Crc16OpensafetyA.hash("123456789")); @@ -775,7 +794,7 @@ test "CRC-16/OPENSAFETY-A" { } test "CRC-16/OPENSAFETY-B" { - const Crc16OpensafetyB = catalog.Crc16OpensafetyB; + const Crc16OpensafetyB = crc.Crc16OpensafetyB; try testing.expectEqual(@as(u16, 0x20fe), Crc16OpensafetyB.hash("123456789")); @@ -786,7 +805,7 @@ test "CRC-16/OPENSAFETY-B" { } test "CRC-16/PROFIBUS" { - const Crc16Profibus = catalog.Crc16Profibus; + const Crc16Profibus = crc.Crc16Profibus; try testing.expectEqual(@as(u16, 0xa819), Crc16Profibus.hash("123456789")); @@ -797,7 +816,7 @@ test "CRC-16/PROFIBUS" { } test "CRC-16/RIELLO" { - const Crc16Riello = catalog.Crc16Riello; + const Crc16Riello = crc.Crc16Riello; try testing.expectEqual(@as(u16, 0x63d0), Crc16Riello.hash("123456789")); @@ -808,7 +827,7 @@ test "CRC-16/RIELLO" { } test "CRC-16/SPI-FUJITSU" { - const Crc16SpiFujitsu = catalog.Crc16SpiFujitsu; + const Crc16SpiFujitsu = crc.Crc16SpiFujitsu; try testing.expectEqual(@as(u16, 0xe5cc), Crc16SpiFujitsu.hash("123456789")); @@ -819,7 +838,7 @@ test "CRC-16/SPI-FUJITSU" { } test "CRC-16/T10-DIF" { - const Crc16T10Dif = catalog.Crc16T10Dif; + const Crc16T10Dif = crc.Crc16T10Dif; try testing.expectEqual(@as(u16, 0xd0db), Crc16T10Dif.hash("123456789")); @@ -830,7 +849,7 @@ test "CRC-16/T10-DIF" { } test "CRC-16/TELEDISK" { - const Crc16Teledisk = catalog.Crc16Teledisk; + const Crc16Teledisk = crc.Crc16Teledisk; try testing.expectEqual(@as(u16, 0x0fb3), Crc16Teledisk.hash("123456789")); @@ -841,7 +860,7 @@ test "CRC-16/TELEDISK" { } test "CRC-16/TMS37157" { - const Crc16Tms37157 = catalog.Crc16Tms37157; + const Crc16Tms37157 = crc.Crc16Tms37157; try testing.expectEqual(@as(u16, 0x26b1), Crc16Tms37157.hash("123456789")); @@ -852,7 +871,7 @@ test "CRC-16/TMS37157" { } test "CRC-16/UMTS" { - const Crc16Umts = catalog.Crc16Umts; + const Crc16Umts = crc.Crc16Umts; try testing.expectEqual(@as(u16, 0xfee8), Crc16Umts.hash("123456789")); @@ -863,7 +882,7 @@ test "CRC-16/UMTS" { } test "CRC-16/USB" { - const Crc16Usb = catalog.Crc16Usb; + const Crc16Usb = crc.Crc16Usb; try testing.expectEqual(@as(u16, 0xb4c8), Crc16Usb.hash("123456789")); @@ -874,7 +893,7 @@ test "CRC-16/USB" { } test "CRC-16/XMODEM" { - const Crc16Xmodem = catalog.Crc16Xmodem; + const Crc16Xmodem = crc.Crc16Xmodem; try testing.expectEqual(@as(u16, 0x31c3), Crc16Xmodem.hash("123456789")); @@ -885,7 +904,7 @@ test "CRC-16/XMODEM" { } test "CRC-17/CAN-FD" { - const Crc17CanFd = catalog.Crc17CanFd; + const Crc17CanFd = crc.Crc17CanFd; try testing.expectEqual(@as(u17, 0x04f03), Crc17CanFd.hash("123456789")); @@ -896,7 +915,7 @@ test "CRC-17/CAN-FD" { } test "CRC-21/CAN-FD" { - const Crc21CanFd = catalog.Crc21CanFd; + const Crc21CanFd = crc.Crc21CanFd; try testing.expectEqual(@as(u21, 0x0ed841), Crc21CanFd.hash("123456789")); @@ -907,7 +926,7 @@ test "CRC-21/CAN-FD" { } test "CRC-24/BLE" { - const Crc24Ble = catalog.Crc24Ble; + const Crc24Ble = crc.Crc24Ble; try testing.expectEqual(@as(u24, 0xc25a56), Crc24Ble.hash("123456789")); @@ -918,7 +937,7 @@ test "CRC-24/BLE" { } test "CRC-24/FLEXRAY-A" { - const Crc24FlexrayA = catalog.Crc24FlexrayA; + const Crc24FlexrayA = crc.Crc24FlexrayA; try testing.expectEqual(@as(u24, 0x7979bd), Crc24FlexrayA.hash("123456789")); @@ -929,7 +948,7 @@ test "CRC-24/FLEXRAY-A" { } test "CRC-24/FLEXRAY-B" { - const Crc24FlexrayB = catalog.Crc24FlexrayB; + const Crc24FlexrayB = crc.Crc24FlexrayB; try testing.expectEqual(@as(u24, 0x1f23b8), Crc24FlexrayB.hash("123456789")); @@ -940,7 +959,7 @@ test "CRC-24/FLEXRAY-B" { } test "CRC-24/INTERLAKEN" { - const Crc24Interlaken = catalog.Crc24Interlaken; + const Crc24Interlaken = crc.Crc24Interlaken; try testing.expectEqual(@as(u24, 0xb4f3e6), Crc24Interlaken.hash("123456789")); @@ -951,7 +970,7 @@ test "CRC-24/INTERLAKEN" { } test "CRC-24/LTE-A" { - const Crc24LteA = catalog.Crc24LteA; + const Crc24LteA = crc.Crc24LteA; try testing.expectEqual(@as(u24, 0xcde703), Crc24LteA.hash("123456789")); @@ -962,7 +981,7 @@ test "CRC-24/LTE-A" { } test "CRC-24/LTE-B" { - const Crc24LteB = catalog.Crc24LteB; + const Crc24LteB = crc.Crc24LteB; try testing.expectEqual(@as(u24, 0x23ef52), Crc24LteB.hash("123456789")); @@ -973,7 +992,7 @@ test "CRC-24/LTE-B" { } test "CRC-24/OPENPGP" { - const Crc24Openpgp = catalog.Crc24Openpgp; + const Crc24Openpgp = crc.Crc24Openpgp; try testing.expectEqual(@as(u24, 0x21cf02), Crc24Openpgp.hash("123456789")); @@ -984,7 +1003,7 @@ test "CRC-24/OPENPGP" { } test "CRC-24/OS-9" { - const Crc24Os9 = catalog.Crc24Os9; + const Crc24Os9 = crc.Crc24Os9; try testing.expectEqual(@as(u24, 0x200fa5), Crc24Os9.hash("123456789")); @@ -995,7 +1014,7 @@ test "CRC-24/OS-9" { } test "CRC-30/CDMA" { - const Crc30Cdma = catalog.Crc30Cdma; + const Crc30Cdma = crc.Crc30Cdma; try testing.expectEqual(@as(u30, 0x04c34abf), Crc30Cdma.hash("123456789")); @@ -1006,7 +1025,7 @@ test "CRC-30/CDMA" { } test "CRC-31/PHILIPS" { - const Crc31Philips = catalog.Crc31Philips; + const Crc31Philips = crc.Crc31Philips; try testing.expectEqual(@as(u31, 0x0ce9e46c), Crc31Philips.hash("123456789")); @@ -1017,7 +1036,7 @@ test "CRC-31/PHILIPS" { } test "CRC-32/AIXM" { - const Crc32Aixm = catalog.Crc32Aixm; + const Crc32Aixm = crc.Crc32Aixm; try testing.expectEqual(@as(u32, 0x3010bf7f), Crc32Aixm.hash("123456789")); @@ -1028,7 +1047,7 @@ test "CRC-32/AIXM" { } test "CRC-32/AUTOSAR" { - const Crc32Autosar = catalog.Crc32Autosar; + const Crc32Autosar = crc.Crc32Autosar; try testing.expectEqual(@as(u32, 0x1697d06a), Crc32Autosar.hash("123456789")); @@ -1039,7 +1058,7 @@ test "CRC-32/AUTOSAR" { } test "CRC-32/BASE91-D" { - const Crc32Base91D = catalog.Crc32Base91D; + const Crc32Base91D = crc.Crc32Base91D; try testing.expectEqual(@as(u32, 0x87315576), Crc32Base91D.hash("123456789")); @@ -1050,7 +1069,7 @@ test "CRC-32/BASE91-D" { } test "CRC-32/BZIP2" { - const Crc32Bzip2 = catalog.Crc32Bzip2; + const Crc32Bzip2 = crc.Crc32Bzip2; try testing.expectEqual(@as(u32, 0xfc891918), Crc32Bzip2.hash("123456789")); @@ -1061,7 +1080,7 @@ test "CRC-32/BZIP2" { } test "CRC-32/CD-ROM-EDC" { - const Crc32CdRomEdc = catalog.Crc32CdRomEdc; + const Crc32CdRomEdc = crc.Crc32CdRomEdc; try testing.expectEqual(@as(u32, 0x6ec2edc4), Crc32CdRomEdc.hash("123456789")); @@ -1072,7 +1091,7 @@ test "CRC-32/CD-ROM-EDC" { } test "CRC-32/CKSUM" { - const Crc32Cksum = catalog.Crc32Cksum; + const Crc32Cksum = crc.Crc32Cksum; try testing.expectEqual(@as(u32, 0x765e7680), Crc32Cksum.hash("123456789")); @@ -1083,7 +1102,7 @@ test "CRC-32/CKSUM" { } test "CRC-32/ISCSI" { - const Crc32Iscsi = catalog.Crc32Iscsi; + const Crc32Iscsi = crc.Crc32Iscsi; try testing.expectEqual(@as(u32, 0xe3069283), Crc32Iscsi.hash("123456789")); @@ -1094,7 +1113,7 @@ test "CRC-32/ISCSI" { } test "CRC-32/ISO-HDLC" { - const Crc32IsoHdlc = catalog.Crc32IsoHdlc; + const Crc32IsoHdlc = crc.Crc32IsoHdlc; try testing.expectEqual(@as(u32, 0xcbf43926), Crc32IsoHdlc.hash("123456789")); @@ -1105,7 +1124,7 @@ test "CRC-32/ISO-HDLC" { } test "CRC-32/JAMCRC" { - const Crc32Jamcrc = catalog.Crc32Jamcrc; + const Crc32Jamcrc = crc.Crc32Jamcrc; try testing.expectEqual(@as(u32, 0x340bc6d9), Crc32Jamcrc.hash("123456789")); @@ -1116,7 +1135,7 @@ test "CRC-32/JAMCRC" { } test "CRC-32/MEF" { - const Crc32Mef = catalog.Crc32Mef; + const Crc32Mef = crc.Crc32Mef; try testing.expectEqual(@as(u32, 0xd2c22f51), Crc32Mef.hash("123456789")); @@ -1127,7 +1146,7 @@ test "CRC-32/MEF" { } test "CRC-32/MPEG-2" { - const Crc32Mpeg2 = catalog.Crc32Mpeg2; + const Crc32Mpeg2 = crc.Crc32Mpeg2; try testing.expectEqual(@as(u32, 0x0376e6e7), Crc32Mpeg2.hash("123456789")); @@ -1138,7 +1157,7 @@ test "CRC-32/MPEG-2" { } test "CRC-32/XFER" { - const Crc32Xfer = catalog.Crc32Xfer; + const Crc32Xfer = crc.Crc32Xfer; try testing.expectEqual(@as(u32, 0xbd0be338), Crc32Xfer.hash("123456789")); @@ -1149,7 +1168,7 @@ test "CRC-32/XFER" { } test "CRC-40/GSM" { - const Crc40Gsm = catalog.Crc40Gsm; + const Crc40Gsm = crc.Crc40Gsm; try testing.expectEqual(@as(u40, 0xd4164fc646), Crc40Gsm.hash("123456789")); @@ -1160,7 +1179,7 @@ test "CRC-40/GSM" { } test "CRC-64/ECMA-182" { - const Crc64Ecma182 = catalog.Crc64Ecma182; + const Crc64Ecma182 = crc.Crc64Ecma182; try testing.expectEqual(@as(u64, 0x6c40df5f0b497347), Crc64Ecma182.hash("123456789")); @@ -1171,7 +1190,7 @@ test "CRC-64/ECMA-182" { } test "CRC-64/GO-ISO" { - const Crc64GoIso = catalog.Crc64GoIso; + const Crc64GoIso = crc.Crc64GoIso; try testing.expectEqual(@as(u64, 0xb90956c775a41001), Crc64GoIso.hash("123456789")); @@ -1182,7 +1201,7 @@ test "CRC-64/GO-ISO" { } test "CRC-64/MS" { - const Crc64Ms = catalog.Crc64Ms; + const Crc64Ms = crc.Crc64Ms; try testing.expectEqual(@as(u64, 0x75d4b74f024eceea), Crc64Ms.hash("123456789")); @@ -1193,7 +1212,7 @@ test "CRC-64/MS" { } test "CRC-64/REDIS" { - const Crc64Redis = catalog.Crc64Redis; + const Crc64Redis = crc.Crc64Redis; try testing.expectEqual(@as(u64, 0xe9c6d914c4b8d9ca), Crc64Redis.hash("123456789")); @@ -1204,7 +1223,7 @@ test "CRC-64/REDIS" { } test "CRC-64/WE" { - const Crc64We = catalog.Crc64We; + const Crc64We = crc.Crc64We; try testing.expectEqual(@as(u64, 0x62ec59e3f1a4f00a), Crc64We.hash("123456789")); @@ -1215,7 +1234,7 @@ test "CRC-64/WE" { } test "CRC-64/XZ" { - const Crc64Xz = catalog.Crc64Xz; + const Crc64Xz = crc.Crc64Xz; try testing.expectEqual(@as(u64, 0x995dc9bbdf1939fa), Crc64Xz.hash("123456789")); @@ -1226,7 +1245,7 @@ test "CRC-64/XZ" { } test "CRC-82/DARC" { - const Crc82Darc = catalog.Crc82Darc; + const Crc82Darc = crc.Crc82Darc; try testing.expectEqual(@as(u82, 0x09ea83f625023801fd612), Crc82Darc.hash("123456789")); diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 2cec73f281..837bdc63c7 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -771,17 +771,41 @@ pub const Request = struct { req.client.connection_pool.release(req.client.allocator, req.connection.?); req.connection = null; - const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUrlScheme; + var server_header = std.heap.FixedBufferAllocator.init(req.response.parser.header_bytes_buffer); + defer req.response.parser.header_bytes_buffer = server_header.buffer[server_header.end_index..]; + const protocol, const valid_uri = try validateUri(uri, server_header.allocator()); + + const new_host = valid_uri.host.?.raw; + const prev_host = req.uri.host.?.raw; + const keep_privileged_headers = + std.ascii.eqlIgnoreCase(valid_uri.scheme, req.uri.scheme) and + std.ascii.endsWithIgnoreCase(new_host, prev_host) and + (new_host.len == prev_host.len or new_host[new_host.len - prev_host.len - 1] == '.'); + if (!keep_privileged_headers) { + // When redirecting to a different domain, strip privileged headers. + req.privileged_headers = &.{}; + } - const port: u16 = uri.port orelse switch (protocol) { - .plain => 80, - .tls => 443, - }; + if (switch (req.response.status) { + .see_other => true, + .moved_permanently, .found => req.method == .POST, + else => false, + }) { + // A redirect to a GET must change the method and remove the body. + req.method = .GET; + req.transfer_encoding = .none; + req.headers.content_type = .omit; + } - const host = uri.host orelse return error.UriMissingHost; + if (req.transfer_encoding != .none) { + // The request body has already been sent. The request is + // still in a valid state, but the redirect must be handled + // manually. + return error.RedirectRequiresResend; + } - req.uri = uri; - req.connection = try req.client.connect(host, port, protocol); + req.uri = valid_uri; + req.connection = try req.client.connect(new_host, uriPort(valid_uri, protocol), protocol); req.redirect_behavior.subtractOne(); req.response.parser.reset(); @@ -796,13 +820,8 @@ pub const Request = struct { pub const SendError = Connection.WriteError || error{ InvalidContentLength, UnsupportedTransferEncoding }; - pub const SendOptions = struct { - /// Specifies that the uri is already escaped. - raw_uri: bool = false, - }; - /// Send the HTTP request headers to the server. - pub fn send(req: *Request, options: SendOptions) SendError!void { + pub fn send(req: *Request) SendError!void { if (!req.method.requestHasBody() and req.transfer_encoding != .none) return error.UnsupportedTransferEncoding; @@ -821,7 +840,6 @@ pub const Request = struct { .authority = connection.proxied, .path = true, .query = true, - .raw = options.raw_uri, }, w); } try w.writeByte(' '); @@ -1038,55 +1056,19 @@ pub const Request = struct { const location = req.response.location orelse return error.HttpRedirectLocationMissing; - // This mutates the beginning of header_buffer and uses that - // for the backing memory of the returned new_uri. - const header_buffer = req.response.parser.header_bytes_buffer; - const new_uri = req.uri.resolve_inplace(location, header_buffer) catch - return error.HttpRedirectLocationInvalid; - - // The new URI references the beginning of header_bytes_buffer memory. - // That memory will be kept, but everything after it will be - // reused by the subsequent request. In other words, - // header_bytes_buffer must be large enough to store all - // redirect locations as well as the final request header. - const path_end = new_uri.path.ptr + new_uri.path.len; - // https://github.com/ziglang/zig/issues/1738 - const path_offset = @intFromPtr(path_end) - @intFromPtr(header_buffer.ptr); - const end_offset = @max(path_offset, location.len); - req.response.parser.header_bytes_buffer = header_buffer[end_offset..]; - - const is_same_domain_or_subdomain = - std.ascii.endsWithIgnoreCase(new_uri.host.?, req.uri.host.?) and - (new_uri.host.?.len == req.uri.host.?.len or - new_uri.host.?[new_uri.host.?.len - req.uri.host.?.len - 1] == '.'); - - if (new_uri.host == null or !is_same_domain_or_subdomain or - !std.ascii.eqlIgnoreCase(new_uri.scheme, req.uri.scheme)) - { - // When redirecting to a different domain, strip privileged headers. - req.privileged_headers = &.{}; - } - - if (switch (req.response.status) { - .see_other => true, - .moved_permanently, .found => req.method == .POST, - else => false, - }) { - // A redirect to a GET must change the method and remove the body. - req.method = .GET; - req.transfer_encoding = .none; - req.headers.content_type = .omit; - } - - if (req.transfer_encoding != .none) { - // The request body has already been sent. The request is - // still in a valid state, but the redirect must be handled - // manually. - return error.RedirectRequiresResend; - } - - try req.redirect(new_uri); - try req.send(.{}); + // This mutates the beginning of header_bytes_buffer and uses that + // for the backing memory of the returned Uri. + try req.redirect(req.uri.resolve_inplace( + location, + &req.response.parser.header_bytes_buffer, + ) catch |err| switch (err) { + error.UnexpectedCharacter, + error.InvalidFormat, + error.InvalidPort, + => return error.HttpRedirectLocationInvalid, + error.NoSpaceLeft => return error.HttpHeadersOversize, + }); + try req.send(); } else { req.response.skip = false; if (!req.response.parser.done) { @@ -1264,30 +1246,25 @@ fn createProxyFromEnvVar(arena: Allocator, env_var_names: []const []const u8) !? }; } else return null; - const uri = Uri.parse(content) catch try Uri.parseWithoutScheme(content); - - const protocol = if (uri.scheme.len == 0) - .plain // No scheme, assume http:// - else - protocol_map.get(uri.scheme) orelse return null; // Unknown scheme, ignore - - const host = uri.host orelse return error.HttpProxyMissingHost; + const uri = Uri.parse(content) catch try Uri.parseAfterScheme("http", content); + const protocol, const valid_uri = validateUri(uri, arena) catch |err| switch (err) { + error.UnsupportedUriScheme => return null, + error.UriMissingHost => return error.HttpProxyMissingHost, + error.OutOfMemory => |e| return e, + }; - const authorization: ?[]const u8 = if (uri.user != null or uri.password != null) a: { - const authorization = try arena.alloc(u8, basic_authorization.valueLengthFromUri(uri)); - assert(basic_authorization.value(uri, authorization).len == authorization.len); + const authorization: ?[]const u8 = if (valid_uri.user != null or valid_uri.password != null) a: { + const authorization = try arena.alloc(u8, basic_authorization.valueLengthFromUri(valid_uri)); + assert(basic_authorization.value(valid_uri, authorization).len == authorization.len); break :a authorization; } else null; const proxy = try arena.create(Proxy); proxy.* = .{ .protocol = protocol, - .host = host, + .host = valid_uri.host.?.raw, .authorization = authorization, - .port = uri.port orelse switch (protocol) { - .plain => 80, - .tls => 443, - }, + .port = uriPort(valid_uri, protocol), .supports_connect = true, }; return proxy; @@ -1305,24 +1282,26 @@ pub const basic_authorization = struct { } pub fn valueLengthFromUri(uri: Uri) usize { - return valueLength( - if (uri.user) |user| user.len else 0, - if (uri.password) |password| password.len else 0, - ); + var stream = std.io.countingWriter(std.io.null_writer); + try stream.writer().print("{user}", .{uri.user orelse Uri.Component.empty}); + const user_len = stream.bytes_written; + stream.bytes_written = 0; + try stream.writer().print("{password}", .{uri.password orelse Uri.Component.empty}); + const password_len = stream.bytes_written; + return valueLength(@intCast(user_len), @intCast(password_len)); } pub fn value(uri: Uri, out: []u8) []u8 { - assert(uri.user == null or uri.user.?.len <= max_user_len); - assert(uri.password == null or uri.password.?.len <= max_password_len); - - @memcpy(out[0..prefix.len], prefix); - var buf: [max_user_len + ":".len + max_password_len]u8 = undefined; - const unencoded = std.fmt.bufPrint(&buf, "{s}:{s}", .{ - uri.user orelse "", uri.password orelse "", - }) catch unreachable; - const base64 = std.base64.standard.Encoder.encode(out[prefix.len..], unencoded); + var stream = std.io.fixedBufferStream(&buf); + stream.writer().print("{user}", .{uri.user orelse Uri.Component.empty}) catch + unreachable; + assert(stream.pos <= max_user_len); + stream.writer().print(":{password}", .{uri.password orelse Uri.Component.empty}) catch + unreachable; + @memcpy(out[0..prefix.len], prefix); + const base64 = std.base64.standard.Encoder.encode(out[prefix.len..], stream.getWritten()); return out[0 .. prefix.len + base64.len]; } }; @@ -1337,8 +1316,7 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec .host = host, .port = port, .protocol = protocol, - })) |node| - return node; + })) |node| return node; if (disable_tls and protocol == .tls) return error.TlsInitializationFailed; @@ -1449,19 +1427,12 @@ pub fn connectTunnel( client.connection_pool.release(client.allocator, conn); } - const uri: Uri = .{ + var buffer: [8096]u8 = undefined; + var req = client.open(.CONNECT, .{ .scheme = "http", - .user = null, - .password = null, - .host = tunnel_host, + .host = .{ .raw = tunnel_host }, .port = tunnel_port, - .path = "", - .query = null, - .fragment = null, - }; - - var buffer: [8096]u8 = undefined; - var req = client.open(.CONNECT, uri, .{ + }, .{ .redirect_behavior = .unhandled, .connection = conn, .server_header_buffer = &buffer, @@ -1471,7 +1442,7 @@ pub fn connectTunnel( }; defer req.deinit(); - req.send(.{ .raw_uri = true }) catch |err| break :tunnel err; + req.send() catch |err| break :tunnel err; req.wait() catch |err| break :tunnel err; if (req.response.status.class() == .server_error) { @@ -1500,7 +1471,7 @@ pub fn connectTunnel( } // Prevents a dependency loop in open() -const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUrlScheme, ConnectionRefused }; +const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUriScheme, ConnectionRefused }; pub const ConnectError = ConnectErrorPartial || RequestError; /// Connect to `host:port` using the specified protocol. This will reuse a @@ -1548,7 +1519,7 @@ pub fn connect( pub const RequestError = ConnectTcpError || ConnectErrorPartial || Request.SendError || std.fmt.ParseIntError || Connection.WriteError || error{ // TODO: file a zig fmt issue for this bad indentation - UnsupportedUrlScheme, + UnsupportedUriScheme, UriMissingHost, CertificateBundleLoadFailure, @@ -1598,12 +1569,28 @@ pub const RequestOptions = struct { privileged_headers: []const http.Header = &.{}, }; -pub const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{ - .{ "http", .plain }, - .{ "ws", .plain }, - .{ "https", .tls }, - .{ "wss", .tls }, -}); +fn validateUri(uri: Uri, arena: Allocator) !struct { Connection.Protocol, Uri } { + const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{ + .{ "http", .plain }, + .{ "ws", .plain }, + .{ "https", .tls }, + .{ "wss", .tls }, + }); + const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUriScheme; + var valid_uri = uri; + // The host is always going to be needed as a raw string for hostname resolution anyway. + valid_uri.host = .{ + .raw = try (uri.host orelse return error.UriMissingHost).toRawMaybeAlloc(arena), + }; + return .{ protocol, valid_uri }; +} + +fn uriPort(uri: Uri, protocol: Connection.Protocol) u16 { + return uri.port orelse switch (protocol) { + .plain => 80, + .tls => 443, + }; +} /// Open a connection to the host specified by `uri` and prepare to send a HTTP request. /// @@ -1633,14 +1620,8 @@ pub fn open( } } - const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUrlScheme; - - const port: u16 = uri.port orelse switch (protocol) { - .plain => 80, - .tls => 443, - }; - - const host = uri.host orelse return error.UriMissingHost; + var server_header = std.heap.FixedBufferAllocator.init(options.server_header_buffer); + const protocol, const valid_uri = try validateUri(uri, server_header.allocator()); if (protocol == .tls and @atomicLoad(bool, &client.next_https_rescan_certs, .acquire)) { if (disable_tls) unreachable; @@ -1649,15 +1630,17 @@ pub fn open( defer client.ca_bundle_mutex.unlock(); if (client.next_https_rescan_certs) { - client.ca_bundle.rescan(client.allocator) catch return error.CertificateBundleLoadFailure; + client.ca_bundle.rescan(client.allocator) catch + return error.CertificateBundleLoadFailure; @atomicStore(bool, &client.next_https_rescan_certs, false, .release); } } - const conn = options.connection orelse try client.connect(host, port, protocol); + const conn = options.connection orelse + try client.connect(valid_uri.host.?.raw, uriPort(valid_uri, protocol), protocol); var req: Request = .{ - .uri = uri, + .uri = valid_uri, .client = client, .connection = conn, .keep_alive = options.keep_alive, @@ -1671,7 +1654,7 @@ pub fn open( .status = undefined, .reason = undefined, .keep_alive = undefined, - .parser = proto.HeadersParser.init(options.server_header_buffer), + .parser = proto.HeadersParser.init(server_header.buffer[server_header.end_index..]), }, .headers = options.headers, .extra_headers = options.extra_headers, @@ -1751,7 +1734,7 @@ pub fn fetch(client: *Client, options: FetchOptions) !FetchResult { if (options.payload) |payload| req.transfer_encoding = .{ .content_length = payload.len }; - try req.send(.{ .raw_uri = options.raw_uri }); + try req.send(); if (options.payload) |payload| try req.writeAll(payload); diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig index e2aa810d58..caeed0e1ea 100644 --- a/lib/std/http/test.zig +++ b/lib/std/http/test.zig @@ -64,7 +64,7 @@ test "trailers" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -474,6 +474,15 @@ test "general client/server API coverage" { .{ .name = "location", .value = "/redirect/3" }, }, }); + } else if (mem.eql(u8, request.head.target, "/redirect/5")) { + try request.respond("Hello, Redirected!\n", .{ + .status = .found, + .extra_headers = &.{ + .{ .name = "location", .value = "/%2525" }, + }, + }); + } else if (mem.eql(u8, request.head.target, "/%2525")) { + try request.respond("Encoded redirect successful!\n", .{}); } else if (mem.eql(u8, request.head.target, "/redirect/invalid")) { const invalid_port = try getUnusedTcpPort(); const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}", .{invalid_port}); @@ -529,7 +538,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -554,7 +563,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192 * 1024); @@ -578,7 +587,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -604,7 +613,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -629,7 +638,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -656,7 +665,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -684,7 +693,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); try std.testing.expectEqual(.ok, req.response.status); @@ -725,7 +734,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -749,7 +758,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -773,7 +782,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -797,13 +806,34 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); req.wait() catch |err| switch (err) { error.TooManyHttpRedirects => {}, else => return err, }; } + { // redirect to encoded url + const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/redirect/5", .{port}); + defer gpa.free(location); + const uri = try std.Uri.parse(location); + + log.info("{s}", .{location}); + var server_header_buffer: [1024]u8 = undefined; + var req = try client.open(.GET, uri, .{ + .server_header_buffer = &server_header_buffer, + }); + defer req.deinit(); + + try req.send(); + try req.wait(); + + const body = try req.reader().readAllAlloc(gpa, 8192); + defer gpa.free(body); + + try expectEqualStrings("Encoded redirect successful!\n", body); + } + // connection has been kept alive try expect(client.http_proxy != null or client.connection_pool.free_len == 1); @@ -819,7 +849,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); const result = req.wait(); // a proxy without an upstream is likely to return a 5xx status. @@ -913,16 +943,16 @@ test "Server streams both reading and writing" { var server_header_buffer: [555]u8 = undefined; var req = try client.open(.POST, .{ .scheme = "http", - .host = "127.0.0.1", + .host = .{ .raw = "127.0.0.1" }, .port = test_server.port(), - .path = "/", + .path = .{ .percent_encoded = "/" }, }, .{ .server_header_buffer = &server_header_buffer, }); defer req.deinit(); req.transfer_encoding = .chunked; - try req.send(.{}); + try req.send(); try req.wait(); try req.writeAll("one "); @@ -956,7 +986,7 @@ fn echoTests(client: *http.Client, port: u16) !void { req.transfer_encoding = .{ .content_length = 14 }; - try req.send(.{}); + try req.send(); try req.writeAll("Hello, "); try req.writeAll("World!\n"); try req.finish(); @@ -990,7 +1020,7 @@ fn echoTests(client: *http.Client, port: u16) !void { req.transfer_encoding = .chunked; - try req.send(.{}); + try req.send(); try req.writeAll("Hello, "); try req.writeAll("World!\n"); try req.finish(); @@ -1044,7 +1074,7 @@ fn echoTests(client: *http.Client, port: u16) !void { req.transfer_encoding = .chunked; - try req.send(.{}); + try req.send(); try req.writeAll("Hello, "); try req.writeAll("World!\n"); try req.finish(); @@ -1075,7 +1105,7 @@ fn echoTests(client: *http.Client, port: u16) !void { req.transfer_encoding = .chunked; - try req.send(.{}); + try req.send(); try req.wait(); try expectEqual(.expectation_failed, req.response.status); } @@ -1180,7 +1210,7 @@ test "redirect to different connection" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); diff --git a/lib/std/io.zig b/lib/std/io.zig index 9f0f444a83..ab89114000 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -413,7 +413,7 @@ pub const StreamSource = @import("io/stream_source.zig").StreamSource; pub const tty = @import("io/tty.zig"); /// A Writer that doesn't write to anything. -pub const null_writer = @as(NullWriter, .{ .context = {} }); +pub const null_writer: NullWriter = .{ .context = {} }; const NullWriter = Writer(void, error{}, dummyWrite); fn dummyWrite(context: void, data: []const u8) error{}!usize { diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index b564d9a99b..93ad1ccbe2 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -3776,19 +3776,19 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { const limb_shift = shift / limb_bits; const interior_limb_shift = @as(Log2Limb, @truncate(shift)); - var carry: Limb = 0; var i: usize = 0; while (i < a.len - limb_shift) : (i += 1) { - const src_i = a.len - i - 1; - const dst_i = src_i - limb_shift; + const dst_i = i; + const src_i = dst_i + limb_shift; const src_digit = a[src_i]; - r[dst_i] = carry | (src_digit >> interior_limb_shift); - carry = @call(.always_inline, math.shl, .{ + const src_digit_next = if (src_i + 1 < a.len) a[src_i + 1] else 0; + const carry = @call(.always_inline, math.shl, .{ Limb, - src_digit, + src_digit_next, limb_bits - @as(Limb, @intCast(interior_limb_shift)), }); + r[dst_i] = carry | (src_digit >> interior_limb_shift); } } diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index f9d7543c2b..624bdc0b83 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -2019,6 +2019,19 @@ test "shift-right multi" { try a.shiftRight(&a, 63); try a.shiftRight(&a, 2); try testing.expect(a.eqlZero()); + + try a.set(0xffff0000eeee1111dddd2222cccc3333000000000000000000000); + try a.shiftRight(&a, 84); + const string = try a.toString( + testing.allocator, + 16, + .lower, + ); + defer testing.allocator.free(string); + try std.testing.expectEqualStrings( + string, + "ffff0000eeee1111dddd2222cccc3333", + ); } test "shift-left single" { diff --git a/lib/std/once.zig b/lib/std/once.zig index 2f6ee709e2..ee3a8b7a35 100644 --- a/lib/std/once.zig +++ b/lib/std/once.zig @@ -7,6 +7,7 @@ pub fn once(comptime f: fn () void) Once(f) { } /// An object that executes the function `f` just once. +/// It is undefined behavior if `f` re-enters the same Once instance. pub fn Once(comptime f: fn () void) type { return struct { done: bool = false, @@ -51,15 +52,18 @@ test "Once executes its function just once" { global_once.call(); } else { var threads: [10]std.Thread = undefined; - defer for (threads) |handle| handle.join(); + var thread_count: usize = 0; + defer for (threads[0..thread_count]) |handle| handle.join(); for (&threads) |*handle| { handle.* = try std.Thread.spawn(.{}, struct { fn thread_fn(x: u8) void { _ = x; global_once.call(); + if (global_number != 1) @panic("memory ordering bug"); } }.thread_fn, .{0}); + thread_count += 1; } } diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 1020bef4b7..1847ceb8a1 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -839,7 +839,7 @@ test "sigaction" { const S = struct { var handler_called_count: u32 = 0; - fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) void { + fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.C) void { _ = ctx_ptr; // Check that we received the correct signal. switch (native_os) { diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 2977bc16cc..9dc5bb4a53 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -30,7 +30,7 @@ pub const Diagnostics = struct { errors: std.ArrayListUnmanaged(Error) = .{}, root_entries: usize = 0, - root_dir: ?[]const u8 = null, + root_dir: []const u8 = "", pub const Error = union(enum) { unable_to_create_sym_link: struct { @@ -55,10 +55,8 @@ pub const Diagnostics = struct { d.root_dir = try d.allocator.dupe(u8, root_dir); return; } - if (d.root_dir) |r| { - d.allocator.free(r); - d.root_dir = null; - } + d.allocator.free(d.root_dir); + d.root_dir = ""; } } @@ -103,10 +101,7 @@ pub const Diagnostics = struct { } } d.errors.deinit(d.allocator); - if (d.root_dir) |r| { - d.allocator.free(r); - d.root_dir = null; - } + d.allocator.free(d.root_dir); d.* = undefined; } }; @@ -1060,7 +1055,7 @@ test "pipeToFileSystem root_dir" { }; // there is no root_dir - try testing.expect(diagnostics.root_dir == null); + try testing.expectEqual(0, diagnostics.root_dir.len); try testing.expectEqual(3, diagnostics.root_entries); } @@ -1082,7 +1077,7 @@ test "pipeToFileSystem root_dir" { }; // root_dir found - try testing.expectEqualStrings("example", diagnostics.root_dir.?); + try testing.expectEqualStrings("example", diagnostics.root_dir); try testing.expectEqual(1, diagnostics.root_entries); } } diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index db082b7f8e..64e8a1c805 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -106,12 +106,8 @@ pub const NullTerminatedString = enum(u32) { /// Given an index into `string_bytes` returns the null-terminated string found there. pub fn nullTerminatedString(code: Zir, index: NullTerminatedString) [:0]const u8 { - const start = @intFromEnum(index); - var end: u32 = start; - while (code.string_bytes[end] != 0) { - end += 1; - } - return code.string_bytes[start..end :0]; + const slice = code.string_bytes[@intFromEnum(index)..]; + return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0]; } pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index c9fe14a1c6..af0986e0b8 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -988,9 +988,13 @@ fn detectAbiAndDynamicLinker( // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead, // doing the same logic recursively in case it finds another shebang line. - // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a - // reasonably reliable path to start with. - var file_name: []const u8 = "/usr/bin/env"; + var file_name: []const u8 = switch (os.tag) { + // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a + // reasonably reliable path to start with. + else => "/usr/bin/env", + // Haiku does not have a /usr root directory. + .haiku => "/bin/env", + }; // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) var buffer: [258]u8 = undefined; while (true) { diff --git a/lib/std/zig/system/NativePaths.zig b/lib/std/zig/system/NativePaths.zig index 9d9ab22812..2a50e27b0c 100644 --- a/lib/std/zig/system/NativePaths.zig +++ b/lib/std/zig/system/NativePaths.zig @@ -105,6 +105,13 @@ pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths { return self; } + if (builtin.os.tag == .haiku) { + try self.addLibDir("/system/non-packaged/lib"); + try self.addLibDir("/system/develop/lib"); + try self.addLibDir("/system/lib"); + return self; + } + if (builtin.os.tag != .windows and builtin.os.tag != .wasi) { const triple = try native_target.linuxTriple(arena); |
