From 02bc6356d72f34fdf4c0669bbcad20427f4d8991 Mon Sep 17 00:00:00 2001 From: Krzysztof Wolicki Der Teufel Date: Sat, 4 Feb 2023 14:02:39 +0100 Subject: [PATCH 001/122] autodoc: main.js cleanup and formatting --- lib/docs/main.js | 162 +++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 83 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index fae39c5fba..2e88c04814 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -106,7 +106,7 @@ const NAV_MODES = { // empty array means refers to the package itself declNames: [], // these will be all types, except the last one may be a type or a decl - declObjs: [], + declObjs: [], // (a, b, c, d) comptime call; result is the value the docs refer to callName: null, }; @@ -200,7 +200,7 @@ const NAV_MODES = { case NAV_MODES.GUIDES: document.title = "[G] " + curNav.activeGuide + suffix; return; - } + } } function isDecl(x) { @@ -401,7 +401,7 @@ const NAV_MODES = { domGuideSwitch.classList.add("active"); domApiSwitch.classList.remove("active"); domDocs.classList.add("hidden"); - domGuides.classList.remove("hidden"); + domGuides.classList.remove("hidden"); domApiMenu.classList.add("hidden"); // sidebar guides list @@ -422,7 +422,7 @@ const NAV_MODES = { if (list.length > 0) { domGuidesMenu.classList.remove("hidden"); } - + // main content const activeGuide = zigAnalysis.guides[curNav.activeGuide]; if (activeGuide == undefined) { @@ -454,7 +454,7 @@ const NAV_MODES = { Happy writing! `); - } else { + } else { domGuides.innerHTML = markdown(activeGuide); } } @@ -467,7 +467,7 @@ const NAV_MODES = { domDocs.classList.remove("hidden"); domApiMenu.classList.remove("hidden"); domGuidesMenu.classList.add("hidden"); - + domStatus.classList.add("hidden"); domFnProto.classList.add("hidden"); domSectParams.classList.add("hidden"); @@ -537,9 +537,9 @@ const NAV_MODES = { currentType = childDecl; curNav.declObjs.push(currentType); } - - - + + + window.x = currentType; renderNav(); @@ -586,15 +586,15 @@ const NAV_MODES = { switch (curNav.mode) { case NAV_MODES.API: case NAV_MODES.API_INTERNAL: - return renderApi(); + return renderApi(); case NAV_MODES.GUIDES: - return renderGuides(); + return renderGuides(); default: - throw "?"; - } + throw "?"; + } } - + function renderDocTest(decl) { if (!decl.decltest) return; const astNode = getAstNode(decl.decltest); @@ -651,7 +651,7 @@ const NAV_MODES = { wantLink: true, fnDecl, }); - + domFnSourceLink.innerHTML = "[src]"; let docsSource = null; @@ -895,7 +895,7 @@ const NAV_MODES = { function navLink(pkgNames, declNames, callName) { let base = curNav.mode; - + if (pkgNames.length === 0 && declNames.length === 0) { return base; } else if (declNames.length === 0 && callName == null) { @@ -919,18 +919,18 @@ const NAV_MODES = { function findDeclNavLink(declName) { if (curNav.declObjs.length == 0) return null; - const curFile = getAstNode(curNav.declObjs[curNav.declObjs.length-1].src).file; - - for (let i = curNav.declObjs.length -1; i >= 0; i--) { - const curDecl = curNav.declObjs[i]; - const curDeclName = curNav.declNames[i-1]; - if (curDeclName == declName) { - const declPath = curNav.declNames.slice(0,i); - return navLink(curNav.pkgNames, declPath); - } + const curFile = getAstNode(curNav.declObjs[curNav.declObjs.length - 1].src).file; - if (findSubDecl(curDecl, declName) != null) { - const declPath = curNav.declNames.slice(0,i).concat([declName]); + for (let i = curNav.declObjs.length - 1; i >= 0; i--) { + const curDecl = curNav.declObjs[i]; + const curDeclName = curNav.declNames[i - 1]; + if (curDeclName == declName) { + const declPath = curNav.declNames.slice(0, i); + return navLink(curNav.pkgNames, declPath); + } + + if (findSubDecl(curDecl, declName) != null) { + const declPath = curNav.declNames.slice(0, i).concat([declName]); return navLink(curNav.pkgNames, declPath); } } @@ -1362,10 +1362,6 @@ const NAV_MODES = { payloadHtml += "truncate"; break; } - case "align_cast": { - payloadHtml += "alignCast"; - break; - } case "has_decl": { payloadHtml += "hasDecl"; break; @@ -1696,15 +1692,15 @@ const NAV_MODES = { } case "declRef": { const name = getDecl(expr.declRef).name; - + if (opts.wantHtml) { let payloadHtml = ""; if (opts.wantLink) { - payloadHtml += ''; + payloadHtml += ''; } payloadHtml += '' + - name + + name + ""; if (opts.wantLink) payloadHtml += ""; return payloadHtml; @@ -1724,12 +1720,12 @@ const NAV_MODES = { if ("string" in expr.refPath[i]) { component = expr.refPath[i].string; } else { - component = exprName(expr.refPath[i], {...opts, wantLink: false}); + component = exprName(expr.refPath[i], { ...opts, wantLink: false }); if (opts.wantLink && "declRef" in expr.refPath[i]) { url += "." + getDecl(expr.refPath[i].declRef).name; - component = '' + + component = '' + component + - ""; + ""; } } name += "." + component; @@ -1788,7 +1784,7 @@ const NAV_MODES = { name = "struct { "; } } - if (structObj.fields.length > 1 && opts.wantHtml) {name += "
";} + if (structObj.fields.length > 1 && opts.wantHtml) { name += "
"; } let indent = ""; if (structObj.fields.length > 1 && opts.wantHtml) { indent = "    " @@ -1804,7 +1800,7 @@ const NAV_MODES = { field_end += " "; } - for(let i = 0; i < structObj.fields.length; i += 1) { + for (let i = 0; i < structObj.fields.length; i += 1) { let fieldNode = getAstNode(structNode.fields[i]); let fieldName = fieldNode.name; let html = indent; @@ -1813,11 +1809,11 @@ const NAV_MODES = { } let fieldTypeExpr = structObj.fields[i]; - if(!structObj.is_tuple) { + if (!structObj.is_tuple) { html += ": "; } - html += exprName(fieldTypeExpr, {...opts, indent: indent}); + html += exprName(fieldTypeExpr, { ...opts, indent: indent }); html += field_end; @@ -1846,7 +1842,7 @@ const NAV_MODES = { if (enumObj.nonexhaustive) { fields_len += 1; } - if (fields_len > 1 && opts.wantHtml) {name += "
";} + if (fields_len > 1 && opts.wantHtml) { name += "
"; } let indent = ""; if (fields_len > 1) { if (opts.wantHtml) { @@ -1864,10 +1860,10 @@ const NAV_MODES = { } else { field_end += " "; } - for(let i = 0; i < enumNode.fields.length; i += 1) { + for (let i = 0; i < enumNode.fields.length; i += 1) { let fieldNode = getAstNode(enumNode.fields[i]); let fieldName = fieldNode.name; - let html = indent + escapeHtml(fieldName); + let html = indent + escapeHtml(fieldName); html += field_end; @@ -1891,16 +1887,16 @@ const NAV_MODES = { name = "union"; } if (unionObj.auto_tag) { - if (opts.wantHtml) { - name += " (enum"; - } else { - name += " (enum"; - } - if (unionObj.tag) { - name += "(" + exprName(unionObj.tag, opts) + "))"; - } else { - name += ")"; - } + if (opts.wantHtml) { + name += " (enum"; + } else { + name += " (enum"; + } + if (unionObj.tag) { + name += "(" + exprName(unionObj.tag, opts) + "))"; + } else { + name += ")"; + } } else if (unionObj.tag) { name += " (" + exprName(unionObj.tag, opts) + ")"; } @@ -1922,7 +1918,7 @@ const NAV_MODES = { } else { field_end += " "; } - for(let i = 0; i < unionObj.fields.length; i += 1) { + for (let i = 0; i < unionObj.fields.length; i += 1) { let fieldNode = getAstNode(unionNode.fields[i]); let fieldName = fieldNode.name; let html = indent + escapeHtml(fieldName); @@ -1930,7 +1926,7 @@ const NAV_MODES = { let fieldTypeExpr = unionObj.fields[i]; html += ": "; - html += exprName(fieldTypeExpr, {...opts, indent: indent}); + html += exprName(fieldTypeExpr, { ...opts, indent: indent }); html += field_end; @@ -2159,7 +2155,7 @@ const NAV_MODES = { opts.fnDecl = null; opts.linkFnNameDecl = null; let payloadHtml = ""; - if (opts.addParensIfFnSignature && fnObj.src == 0){ + if (opts.addParensIfFnSignature && fnObj.src == 0) { payloadHtml += "("; } if (opts.wantHtml) { @@ -2175,7 +2171,7 @@ const NAV_MODES = { if (linkFnNameDecl) { payloadHtml += '' + - escapeHtml(fnDecl.name) + + escapeHtml(fnDecl.name) + ""; } else { payloadHtml += escapeHtml(fnDecl.name); @@ -2194,7 +2190,7 @@ const NAV_MODES = { fields = fnNode.fields; isVarArgs = fnNode.varArgs; } - + for (let i = 0; i < fnObj.params.length; i += 1) { if (i != 0) { payloadHtml += ", "; @@ -2247,13 +2243,13 @@ const NAV_MODES = { } else if ("typeOf" in value) { payloadHtml += exprName(value, opts); } else if ("typeOf_peer" in value) { - payloadHtml += exprName(value, opts); + payloadHtml += exprName(value, opts); } else if ("declRef" in value) { - payloadHtml += exprName(value, opts); + payloadHtml += exprName(value, opts); } else if ("call" in value) { - payloadHtml += exprName(value, opts); + payloadHtml += exprName(value, opts); } else if ("refPath" in value) { - payloadHtml += exprName(value, opts); + payloadHtml += exprName(value, opts); } else if ("type" in value) { payloadHtml += exprName(value, opts); //payloadHtml += '' + name + ""; @@ -2297,7 +2293,7 @@ const NAV_MODES = { } if (fnObj.ret != null) { payloadHtml += exprName(fnObj.ret, { - ...opts, + ...opts, addParensIfFnSignature: true, }); } else if (opts.wantHtml) { @@ -2306,7 +2302,7 @@ const NAV_MODES = { payloadHtml += "anytype"; } - if (opts.addParensIfFnSignature && fnObj.src == 0){ + if (opts.addParensIfFnSignature && fnObj.src == 0) { payloadHtml += ")"; } return payloadHtml; @@ -2349,7 +2345,7 @@ const NAV_MODES = { ) { name = "std"; } else { - name = exprName({ type: typeObj }, {wantHtml: false, wantLink: false}); + name = exprName({ type: typeObj }, { wantHtml: false, wantLink: false }); } if (name != null && name != "") { domHdrName.innerText = @@ -2640,10 +2636,10 @@ const NAV_MODES = { } function sourceFileLink(decl) { - const srcNode = getAstNode(decl.src); - return sourceFileUrlTemplate. - replace("{{file}}", zigAnalysis.files[srcNode.file]). - replace("{{line}}", srcNode.line + 1); + const srcNode = getAstNode(decl.src); + return sourceFileUrlTemplate. + replace("{{file}}", zigAnalysis.files[srcNode.file]). + replace("{{line}}", srcNode.line + 1); } function renderContainer(container) { @@ -2779,8 +2775,8 @@ const NAV_MODES = { if (short != docs) { short = markdown(short); var long = markdown(docs); - tdDesc.innerHTML = - "
" + short + "
" + "
" + long + "
"; + tdDesc.innerHTML = + "
" + short + "
" + "
" + long + "
"; } else { tdDesc.innerHTML = markdown(short); @@ -2814,10 +2810,10 @@ const NAV_MODES = { html += ' = ' + fieldName + ""; } else { let fieldTypeExpr = container.fields[i]; - if(container.kind ==! typeKinds.Struct || !container.is_tuple) { + if (container.kind !== typeKinds.Struct || !container.is_tuple) { html += ": "; } - html += exprName(fieldTypeExpr, {wantHtml:true, wantLink:true}); + html += exprName(fieldTypeExpr, { wantHtml: true, wantLink: true }); let tsn = typeShorthandName(fieldTypeExpr); if (tsn) { html += " (" + tsn + ")"; @@ -3003,8 +2999,8 @@ const NAV_MODES = { throw new Error("No type 'type' found"); } - - function updateCurNav() { + + function updateCurNav() { curNav = { mode: NAV_MODES.API, pkgNames: [], @@ -3017,7 +3013,7 @@ const NAV_MODES = { const mode = location.hash.substring(0, 3); let query = location.hash.substring(3); - + const DEFAULT_HASH = NAV_MODES.API + zigAnalysis.packages[zigAnalysis.rootPkg].name; switch (mode) { case NAV_MODES.API: @@ -3033,7 +3029,7 @@ const NAV_MODES = { nonSearchPart = query.substring(0, qpos); curNavSearch = decodeURIComponent(query.substring(qpos + 1)); } - + let parts = nonSearchPart.split(":"); if (parts[0] == "") { location.hash = DEFAULT_HASH; @@ -3055,14 +3051,14 @@ const NAV_MODES = { curNav.mode = mode; curNav.activeGuide = query; - + return; default: location.hash = DEFAULT_HASH; return; } - } - + } + function onHashChange() { updateCurNav(); if (domSearch.value !== curNavSearch) { @@ -3099,7 +3095,7 @@ const NAV_MODES = { if (!callee.generic_ret) return null; resolvedGenericRet = resolveValue({ expr: callee.generic_ret }); } - + if ("type" in resolvedGenericRet.expr) { parentType = getType(resolvedGenericRet.expr.type); } @@ -3244,7 +3240,7 @@ const NAV_MODES = { }); } - function shortDesc(docs){ + function shortDesc(docs) { const trimmed_docs = docs.trim(); let index = trimmed_docs.indexOf("\n\n"); let cut = false; From 14bf20daeb784fb65e7d5789af13694a272a7142 Mon Sep 17 00:00:00 2001 From: Krzysztof Wolicki Der Teufel Date: Fri, 10 Feb 2023 14:31:26 +0100 Subject: [PATCH 002/122] autodoc: anonymous struct type indentation fix --- lib/docs/main.js | 4 ++-- src/Autodoc.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 2e88c04814..45dc6ed2c1 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1789,7 +1789,7 @@ const NAV_MODES = { if (structObj.fields.length > 1 && opts.wantHtml) { indent = "    " } - if (opts.indent) { + if (opts.indent && structObj.fields.length > 1) { indent = opts.indent + indent; } let structNode = getAstNode(structObj.src); @@ -1819,7 +1819,7 @@ const NAV_MODES = { name += html; } - if (opts.indent) { + if (opts.indent && structObj.fields.length > 1) { name += opts.indent; } name += "}"; diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 0c2c39bbcc..f945a463c0 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -3628,7 +3628,7 @@ fn tryResolveRefPath( } if (self.pending_ref_paths.get(&path[path.len - 1])) |waiter_list| { - // It's important to de-register oureslves as pending before + // It's important to de-register ourselves as pending before // attempting to resolve any other decl. _ = self.pending_ref_paths.remove(&path[path.len - 1]); From e359308b2bf30f6fe5403ddfddcf7925f6ab1f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Thu, 16 Feb 2023 06:00:57 +0900 Subject: [PATCH 003/122] autodoc: fix md list markers matching Both original Markdown and CommonMark require at least one space or tab between the list marker and any following content. Following this allows starting a line with a negative number or one with a decimal point. --- lib/docs/main.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index 7a27f9db4f..feb6a96e50 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -3319,14 +3319,16 @@ const NAV_MODES = { } else if (line.text.startsWith("#")) { line.type = "h1"; line.text = line.text.substr(1); - } else if (line.text.startsWith("-")) { - line.type = "ul"; - line.text = line.text.substr(1); - } else if (line.text.match(/^\d+\..*$/)) { - // if line starts with {number}{dot} - const match = line.text.match(/(\d+)\./); + } else if (line.text.match(/^-[ \t]+.*$/)) { + // line starts with a hyphen, followed by spaces or tabs + const match = line.text.match(/^-[ \t]+/); line.type = "ul"; line.text = line.text.substr(match[0].length); + } else if (line.text.match(/^\d+\.[ \t]+.*$/)) { + // line starts with {number}{dot}{spaces or tabs} + const match = line.text.match(/(\d+)\.[ \t]+/); + line.type = "ol"; + line.text = line.text.substr(match[0].length); line.ordered_number = Number(match[1].length); } else if (line.text == "```") { line.type = "skip"; @@ -4067,4 +4069,4 @@ function toggleExpand(event) { if (!parent.open && parent.getBoundingClientRect().top < 0) { parent.parentElement.parentElement.scrollIntoView(true); } -} \ No newline at end of file +} From b56d4f215061e57dd2f5470c14df0c27b810c8ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Thu, 16 Feb 2023 06:01:47 +0900 Subject: [PATCH 004/122] autodoc: render ordered lists as such --- lib/docs/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/main.js b/lib/docs/main.js index feb6a96e50..579fc79442 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -3538,7 +3538,7 @@ const NAV_MODES = { case "ul": case "ol": if ( - !previousLineIs("ul", line_no) || + !previousLineIs(line.type, line_no) || getPreviousLineIndent(line_no) < line.indent ) { html += "<" + line.type + ">\n"; @@ -3547,7 +3547,7 @@ const NAV_MODES = { html += "
  • " + markdownInlines(line.text) + "
  • \n"; if ( - !nextLineIs("ul", line_no) || + !nextLineIs(line.type, line_no) || getNextLineIndent(line_no) < line.indent ) { html += "\n"; From c02ced4d346656abefa4cccfbdebf5dd27b326e5 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 2 Jul 2022 09:40:24 -0600 Subject: [PATCH 005/122] ignore SIGPIPE by default --- lib/std/os.zig | 45 +++++++++++++++++++++++++++++++++++++++++++++ lib/std/start.zig | 1 + 2 files changed, 46 insertions(+) diff --git a/lib/std/os.zig b/lib/std/os.zig index c5eeb34b1c..eb4dd82164 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -7056,3 +7056,48 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { else => |err| return unexpectedErrno(err), }; } + +/// Whether or not the current target support SIGPIPE +pub const have_sigpipe_support = switch (builtin.os.tag) { + .linux, + .macos, + .netbsd, + .solaris, + .freebsd, + .openbsd, + => true, + else => false, +}; + +pub const keep_sigpipe: bool = if (@hasDecl(root, "keep_sigpipe")) + root.keep_sigpipe +else + false; + +/// This function will tell the kernel to ignore SIGPIPE rather than terminate +/// the process. This function is automatically called in `start.zig` before +/// `main`. This behavior can be disabled by adding this to your root module: +/// +/// pub const keep_sigpipe = true; +/// +/// SIGPIPE is triggered when a process attempts to write to a broken pipe. +/// By default, SIGPIPE will terminate the process without giving the program +/// an opportunity to handle the situation. Unlike a segfault, it doesn't +/// trigger the panic handler so all the developer sees is that the program +/// terminated with no indication as to why. +/// +/// By telling the kernel to instead ignore SIGPIPE, writes to broken pipes +/// will return the EPIPE error (error.BrokenPipe) and the program can handle +/// it like any other error. +pub fn maybeIgnoreSigpipe() void { + if (have_sigpipe_support and !keep_sigpipe) { + const act = Sigaction{ + .handler = .{ .sigaction = SIG.IGN }, + .mask = empty_sigset, + .flags = SA.SIGINFO, + }; + sigaction(SIG.PIPE, &act, null) catch |err| std.debug.panic("ignore SIGPIPE failed with '{s}'" ++ + ", add `pub const keep_sigpipe = true;` to your root module" ++ + " or adjust have_sigpipe_support in std/os.zig", .{@errorName(err)}); + } +} diff --git a/lib/std/start.zig b/lib/std/start.zig index ea221d1539..6edebde122 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -496,6 +496,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { std.os.environ = envp; std.debug.maybeEnableSegfaultHandler(); + std.os.maybeIgnoreSigpipe(); return initEventLoopAndCallMain(); } From 0a8fe34b11f7a44fd7f744bf4332353a5e7bfcdf Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 2 Jul 2022 10:36:39 -0600 Subject: [PATCH 006/122] add test to ignore sigpipe --- lib/std/Build/EmulatableRunStep.zig | 4 +- lib/std/Build/RunStep.zig | 83 ++++++++++++++------- lib/std/os.zig | 13 ++-- test/link/macho/dead_strip_dylibs/build.zig | 2 +- test/src/compare_output.zig | 2 +- test/standalone.zig | 3 + test/standalone/sigpipe/breakpipe.zig | 21 ++++++ test/standalone/sigpipe/build.zig | 35 +++++++++ 8 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 test/standalone/sigpipe/breakpipe.zig create mode 100644 test/standalone/sigpipe/build.zig diff --git a/lib/std/Build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig index 5517f7f9aa..d4b5238524 100644 --- a/lib/std/Build/EmulatableRunStep.zig +++ b/lib/std/Build/EmulatableRunStep.zig @@ -26,7 +26,7 @@ builder: *std.Build, exe: *CompileStep, /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution -expected_exit_code: ?u8 = 0, +expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, /// Override this field to modify the environment env_map: ?*EnvMap, @@ -131,7 +131,7 @@ fn make(step: *Step) !void { try RunStep.runCommand( argv_list.items, self.builder, - self.expected_exit_code, + self.expected_term, self.stdout_action, self.stderr_action, .Inherit, diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 5bc271409a..0ef78dfdeb 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -35,7 +35,7 @@ stderr_action: StdIoAction = .inherit, stdin_behavior: std.ChildProcess.StdIo = .Inherit, /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution -expected_exit_code: ?u8 = 0, +expected_term: ?std.ChildProcess.Term = .{ .Exited = 0 }, /// Print the command before running it print: bool, @@ -289,7 +289,7 @@ fn make(step: *Step) !void { try runCommand( argv_list.items, self.builder, - self.expected_exit_code, + self.expected_term, self.stdout_action, self.stderr_action, self.stdin_behavior, @@ -303,10 +303,55 @@ fn make(step: *Step) !void { } } +fn formatTerm( + term: ?std.ChildProcess.Term, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, +) !void { + _ = fmt; + _ = options; + if (term) |t| switch (t) { + .Exited => |code| try writer.print("exited with code {}", .{code}), + .Signal => |sig| try writer.print("terminated with signal {}", .{sig}), + .Stopped => |sig| try writer.print("stopped with signal {}", .{sig}), + .Unknown => |code| try writer.print("terminated for unknown reason with code {}", .{code}), + } else { + try writer.writeAll("exited with any code"); + } +} +fn fmtTerm(term: ?std.ChildProcess.Term) std.fmt.Formatter(formatTerm) { + return .{ .data = term }; +} + +fn termMatches(expected: ?std.ChildProcess.Term, actual: std.ChildProcess.Term) bool { + return if (expected) |e| switch (e) { + .Exited => |expected_code| switch (actual) { + .Exited => |actual_code| expected_code == actual_code, + else => false, + }, + .Signal => |expected_sig| switch (actual) { + .Signal => |actual_sig| expected_sig == actual_sig, + else => false, + }, + .Stopped => |expected_sig| switch (actual) { + .Stopped => |actual_sig| expected_sig == actual_sig, + else => false, + }, + .Unknown => |expected_code| switch (actual) { + .Unknown => |actual_code| expected_code == actual_code, + else => false, + }, + } else switch (actual) { + .Exited => true, + else => false, + }; +} + pub fn runCommand( argv: []const []const u8, builder: *std.Build, - expected_exit_code: ?u8, + expected_term: ?std.ChildProcess.Term, stdout_action: StdIoAction, stderr_action: StdIoAction, stdin_behavior: std.ChildProcess.StdIo, @@ -368,32 +413,14 @@ pub fn runCommand( return err; }; - switch (term) { - .Exited => |code| blk: { - const expected_code = expected_exit_code orelse break :blk; - - if (code != expected_code) { - if (builder.prominent_compile_errors) { - std.debug.print("Run step exited with error code {} (expected {})\n", .{ - code, - expected_code, - }); - } else { - std.debug.print("The following command exited with error code {} (expected {}):\n", .{ - code, - expected_code, - }); - printCmd(cwd, argv); - } - - return error.UnexpectedExitCode; - } - }, - else => { - std.debug.print("The following command terminated unexpectedly:\n", .{}); + if (!termMatches(expected_term, term)) { + if (builder.prominent_compile_errors) { + std.debug.print("Run step {} (expected {})\n", .{ fmtTerm(term), fmtTerm(expected_term) }); + } else { + std.debug.print("The following command {} (expected {}):\n", .{ fmtTerm(term), fmtTerm(expected_term) }); printCmd(cwd, argv); - return error.UncleanExit; - }, + } + return error.UnexpectedExit; } switch (stderr_action) { diff --git a/lib/std/os.zig b/lib/std/os.zig index eb4dd82164..9752df7f16 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -7074,6 +7074,8 @@ pub const keep_sigpipe: bool = if (@hasDecl(root, "keep_sigpipe")) else false; +fn noopSigHandler(_: c_int) callconv(.C) void {} + /// This function will tell the kernel to ignore SIGPIPE rather than terminate /// the process. This function is automatically called in `start.zig` before /// `main`. This behavior can be disabled by adding this to your root module: @@ -7092,12 +7094,13 @@ else pub fn maybeIgnoreSigpipe() void { if (have_sigpipe_support and !keep_sigpipe) { const act = Sigaction{ - .handler = .{ .sigaction = SIG.IGN }, + // We set handler to a noop function instead of SIG.IGN so we don't leak our + // signal disposition to a child process + .handler = .{ .handler = noopSigHandler }, .mask = empty_sigset, - .flags = SA.SIGINFO, + .flags = 0, }; - sigaction(SIG.PIPE, &act, null) catch |err| std.debug.panic("ignore SIGPIPE failed with '{s}'" ++ - ", add `pub const keep_sigpipe = true;` to your root module" ++ - " or adjust have_sigpipe_support in std/os.zig", .{@errorName(err)}); + sigaction(SIG.PIPE, &act, null) catch |err| + std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)}); } } diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig index 8b62cec6e6..af2f5cf0dc 100644 --- a/test/link/macho/dead_strip_dylibs/build.zig +++ b/test/link/macho/dead_strip_dylibs/build.zig @@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void { exe.dead_strip_dylibs = true; const run_cmd = exe.run(); - run_cmd.expected_exit_code = @bitCast(u8, @as(i8, -2)); // should fail + run_cmd.expected_term = .{ .Exited = @bitCast(u8, @as(i8, -2)) }; // should fail test_step.dependOn(&run_cmd.step); } } diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig index edd48321c9..3bda3bdacd 100644 --- a/test/src/compare_output.zig +++ b/test/src/compare_output.zig @@ -168,7 +168,7 @@ pub const CompareOutputContext = struct { run.addArgs(case.cli_args); run.stderr_action = .ignore; run.stdout_action = .ignore; - run.expected_exit_code = 126; + run.expected_term = .{ .Exited = 126 }; self.step.dependOn(&run.step); }, diff --git a/test/standalone.zig b/test/standalone.zig index 81eb1b0042..ed0d2c2d30 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -84,6 +84,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/pie/build.zig", .{}); } cases.addBuildFile("test/standalone/issue_12706/build.zig", .{}); + if (std.os.have_sigpipe_support) { + cases.addBuildFile("test/standalone/sigpipe/build.zig", .{}); + } // Ensure the development tools are buildable. Alphabetically sorted. // No need to build `tools/spirv/grammar.zig`. diff --git a/test/standalone/sigpipe/breakpipe.zig b/test/standalone/sigpipe/breakpipe.zig new file mode 100644 index 0000000000..6498f5b2eb --- /dev/null +++ b/test/standalone/sigpipe/breakpipe.zig @@ -0,0 +1,21 @@ +const std = @import("std"); +const build_options = @import("build_options"); + +pub usingnamespace if (build_options.keep_sigpipe) struct { + pub const keep_sigpipe = true; +} else struct { + // intentionally not setting keep_sigpipe to ensure the default behavior is equivalent to false +}; + +pub fn main() !void { + const pipe = try std.os.pipe(); + std.os.close(pipe[0]); + _ = std.os.write(pipe[1], "a") catch |err| switch (err) { + error.BrokenPipe => { + try std.io.getStdOut().writer().writeAll("BrokenPipe\n"); + std.os.exit(123); + }, + else => |e| return e, + }; + unreachable; +} diff --git a/test/standalone/sigpipe/build.zig b/test/standalone/sigpipe/build.zig new file mode 100644 index 0000000000..763df5fe46 --- /dev/null +++ b/test/standalone/sigpipe/build.zig @@ -0,0 +1,35 @@ +const std = @import("std"); +const os = std.os; + +pub fn build(b: *std.build.Builder) !void { + const test_step = b.step("test", "Run the tests"); + + // This test runs "breakpipe" as a child process and that process + // depends on inheriting a SIGPIPE disposition of "default". + { + const act = os.Sigaction{ + .handler = .{ .handler = os.SIG.DFL }, + .mask = os.empty_sigset, + .flags = 0, + }; + try os.sigaction(os.SIG.PIPE, &act, null); + } + + for ([_]bool{ false, true }) |keep_sigpipe| { + const options = b.addOptions(); + options.addOption(bool, "keep_sigpipe", keep_sigpipe); + const exe = b.addExecutable(.{ + .name = "breakpipe", + .root_source_file = .{ .path = "breakpipe.zig" }, + }); + exe.addOptions("build_options", options); + const run = exe.run(); + if (keep_sigpipe) { + run.expected_term = .{ .Signal = std.os.SIG.PIPE }; + } else { + run.stdout_action = .{ .expect_exact = "BrokenPipe\n" }; + run.expected_term = .{ .Exited = 123 }; + } + test_step.dependOn(&run.step); + } +} From dafefe9c9d3ffd484915ead0474c7e772f1dfcfb Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 18 Feb 2023 11:46:24 -0700 Subject: [PATCH 007/122] use std_options for keep_sigpipe and existence of SIG.PIPE to check for support --- lib/std/os.zig | 34 ++------------------------- lib/std/std.zig | 16 +++++++++++++ test/standalone/sigpipe/breakpipe.zig | 2 +- 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 9752df7f16..bd6719ec8f 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -7057,42 +7057,12 @@ pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec { }; } -/// Whether or not the current target support SIGPIPE -pub const have_sigpipe_support = switch (builtin.os.tag) { - .linux, - .macos, - .netbsd, - .solaris, - .freebsd, - .openbsd, - => true, - else => false, -}; - -pub const keep_sigpipe: bool = if (@hasDecl(root, "keep_sigpipe")) - root.keep_sigpipe -else - false; +pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE"); fn noopSigHandler(_: c_int) callconv(.C) void {} -/// This function will tell the kernel to ignore SIGPIPE rather than terminate -/// the process. This function is automatically called in `start.zig` before -/// `main`. This behavior can be disabled by adding this to your root module: -/// -/// pub const keep_sigpipe = true; -/// -/// SIGPIPE is triggered when a process attempts to write to a broken pipe. -/// By default, SIGPIPE will terminate the process without giving the program -/// an opportunity to handle the situation. Unlike a segfault, it doesn't -/// trigger the panic handler so all the developer sees is that the program -/// terminated with no indication as to why. -/// -/// By telling the kernel to instead ignore SIGPIPE, writes to broken pipes -/// will return the EPIPE error (error.BrokenPipe) and the program can handle -/// it like any other error. pub fn maybeIgnoreSigpipe() void { - if (have_sigpipe_support and !keep_sigpipe) { + if (have_sigpipe_support and !std.options.keep_sigpipe) { const act = Sigaction{ // We set handler to a noop function instead of SIG.IGN so we don't leak our // signal disposition to a child process diff --git a/lib/std/std.zig b/lib/std/std.zig index e02be2ebaf..5b0963ba20 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -167,6 +167,22 @@ pub const options = struct { options_override.crypto_always_getrandom else false; + + /// By default Zig disables SIGPIPE by setting a "no-op" handler for it. Set this option + /// to `true` to prevent that. + /// + /// Note that we use a "no-op" handler instead of SIG_IGN because it will not be inherited by + /// any child process. + /// + /// SIGPIPE is triggered when a process attempts to write to a broken pipe. By default, SIGPIPE + /// will terminate the process instead of exiting. It doesn't trigger the panic handler so in many + /// cases it's unclear why the process was terminated. By capturing SIGPIPE instead, functions that + /// write to broken pipes will return the EPIPE error (error.BrokenPipe) and the program can handle + /// it like any other error. + pub const keep_sigpipe: bool = if (@hasDecl(options_override, "keep_sigpipe")) + options_override.keep_sigpipe + else + false; }; // This forces the start.zig file to be imported, and the comptime logic inside that diff --git a/test/standalone/sigpipe/breakpipe.zig b/test/standalone/sigpipe/breakpipe.zig index 6498f5b2eb..3623451db5 100644 --- a/test/standalone/sigpipe/breakpipe.zig +++ b/test/standalone/sigpipe/breakpipe.zig @@ -1,7 +1,7 @@ const std = @import("std"); const build_options = @import("build_options"); -pub usingnamespace if (build_options.keep_sigpipe) struct { +pub const std_options = if (build_options.keep_sigpipe) struct { pub const keep_sigpipe = true; } else struct { // intentionally not setting keep_sigpipe to ensure the default behavior is equivalent to false From f10950526ea781ee2d15df74398527420cca13a1 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 18 Feb 2023 22:05:09 +0200 Subject: [PATCH 008/122] implement `writeToMemory`/`readFromMemory` for pointers --- src/Sema.zig | 51 +++++++++++-------- src/arch/wasm/CodeGen.zig | 4 +- src/codegen.zig | 2 +- src/value.zig | 49 ++++++++++++++---- ...tCast_same_size_but_bit_count_mismatch.zig | 2 +- ...h_different_sizes_inside_an_expression.zig | 2 +- 6 files changed, 74 insertions(+), 36 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index fcdb1ce518..41e5fdc20e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2529,7 +2529,7 @@ fn coerceResultPtr( _ = try block.addBinOp(.store, new_ptr, null_inst); return Air.Inst.Ref.void_value; } - return sema.bitCast(block, ptr_ty, new_ptr, src); + return sema.bitCast(block, ptr_ty, new_ptr, src, null); } const trash_inst = trash_block.instructions.pop(); @@ -2545,7 +2545,7 @@ fn coerceResultPtr( if (try sema.resolveDefinedValue(block, src, new_ptr)) |ptr_val| { new_ptr = try sema.addConstant(ptr_operand_ty, ptr_val); } else { - new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src); + new_ptr = try sema.bitCast(block, ptr_operand_ty, new_ptr, src, null); } }, .wrap_optional => { @@ -9655,7 +9655,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .Vector, => {}, } - return sema.bitCast(block, dest_ty, operand, operand_src); + return sema.bitCast(block, dest_ty, operand, inst_data.src(), operand_src); } fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -9888,7 +9888,7 @@ fn zirSwitchCapture( switch (operand_ty.zigTypeTag()) { .ErrorSet => if (block.switch_else_err_ty) |some| { - return sema.bitCast(block, some, operand, operand_src); + return sema.bitCast(block, some, operand, operand_src, null); } else { try block.addUnreachable(false); return Air.Inst.Ref.unreachable_value; @@ -9988,14 +9988,14 @@ fn zirSwitchCapture( Module.ErrorSet.sortNames(&names); const else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); - return sema.bitCast(block, else_error_ty, operand, operand_src); + return sema.bitCast(block, else_error_ty, operand, operand_src, null); } else { const item_ref = try sema.resolveInst(items[0]); // Previous switch validation ensured this will succeed const item_val = sema.resolveConstValue(block, .unneeded, item_ref, "") catch unreachable; const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); - return sema.bitCast(block, item_ty, operand, operand_src); + return sema.bitCast(block, item_ty, operand, operand_src, null); } }, else => { @@ -19953,7 +19953,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else is_aligned; try sema.addSafetyCheck(block, ok, .incorrect_alignment); } - return sema.bitCast(block, dest_ty, ptr, ptr_src); + return sema.bitCast(block, dest_ty, ptr, ptr_src, null); } fn zirBitCount( @@ -24141,8 +24141,9 @@ fn unionFieldVal( return sema.addConstant(field.ty, tag_and_val.val); } else { const old_ty = union_ty.unionFieldType(tag_and_val.tag, sema.mod); - const new_val = try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0); - return sema.addConstant(field.ty, new_val); + if (try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0)) |new_val| { + return sema.addConstant(field.ty, new_val); + } } }, } @@ -26514,8 +26515,12 @@ fn storePtrVal( const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(target)); const buffer = try sema.gpa.alloc(u8, abi_size); defer sema.gpa.free(buffer); - reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer); - operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]); + reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, sema.mod, buffer) catch |err| switch (err) { + error.ReinterpretDeclRef => unreachable, + }; + operand_val.writeToMemory(operand_ty, sema.mod, buffer[reinterpret.byte_offset..]) catch |err| switch (err) { + error.ReinterpretDeclRef => unreachable, + }; const arena = mut_kit.beginArena(sema.mod); defer mut_kit.finishArena(sema.mod); @@ -27398,6 +27403,7 @@ fn bitCast( dest_ty_unresolved: Type, inst: Air.Inst.Ref, inst_src: LazySrcLoc, + operand_src: ?LazySrcLoc, ) CompileError!Air.Inst.Ref { const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved); try sema.resolveTypeLayout(dest_ty); @@ -27419,10 +27425,11 @@ fn bitCast( } if (try sema.resolveMaybeUndefVal(inst)) |val| { - const result_val = try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0); - return sema.addConstant(dest_ty, result_val); + if (try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0)) |result_val| { + return sema.addConstant(dest_ty, result_val); + } } - try sema.requireRuntimeBlock(block, inst_src, null); + try sema.requireRuntimeBlock(block, inst_src, operand_src); return block.addBitCast(dest_ty, inst); } @@ -27434,7 +27441,7 @@ fn bitCastVal( old_ty: Type, new_ty: Type, buffer_offset: usize, -) !Value { +) !?Value { const target = sema.mod.getTarget(); if (old_ty.eql(new_ty, sema.mod)) return val; @@ -27443,8 +27450,10 @@ fn bitCastVal( const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target)); const buffer = try sema.gpa.alloc(u8, abi_size); defer sema.gpa.free(buffer); - val.writeToMemory(old_ty, sema.mod, buffer); - return Value.readFromMemory(new_ty, sema.mod, buffer[buffer_offset..], sema.arena); + val.writeToMemory(old_ty, sema.mod, buffer) catch |err| switch (err) { + error.ReinterpretDeclRef => return null, + }; + return try Value.readFromMemory(new_ty, sema.mod, buffer[buffer_offset..], sema.arena); } fn coerceArrayPtrToSlice( @@ -27551,7 +27560,7 @@ fn coerceCompatiblePtrs( } else is_non_zero; try sema.addSafetyCheck(block, ok, .cast_to_null); } - return sema.bitCast(block, dest_ty, inst, inst_src); + return sema.bitCast(block, dest_ty, inst, inst_src, null); } fn coerceEnumToUnion( @@ -28291,7 +28300,7 @@ fn analyzeRef( try sema.storePtr(block, src, alloc, operand); // TODO: Replace with sema.coerce when that supports adding pointer constness. - return sema.bitCast(block, ptr_type, alloc, src); + return sema.bitCast(block, ptr_type, alloc, src, null); } fn analyzeLoad( @@ -32327,11 +32336,11 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value // Try the smaller bit-cast first, since that's more efficient than using the larger `parent` if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty)) - return DerefResult{ .val = try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0) }; + return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load }; // If that fails, try to bit-cast from the largest parent value with a well-defined layout if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty)) - return DerefResult{ .val = try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset) }; + return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load }; if (deref.ty_without_well_defined_layout) |bad_ty| { // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 53dc28626c..b229a67e70 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2896,7 +2896,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { const struct_obj = ty.castTag(.@"struct").?.data; assert(struct_obj.layout == .Packed); var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer - val.writeToPackedMemory(ty, func.bin_file.base.options.module.?, &buf, 0); + val.writeToPackedMemory(ty, func.bin_file.base.options.module.?, &buf, 0) catch unreachable; var payload: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = std.mem.readIntLittle(u64, &buf), @@ -2907,7 +2907,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { .Vector => { assert(determineSimdStoreStrategy(ty, target) == .direct); var buf: [16]u8 = undefined; - val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf); + val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf) catch unreachable; return func.storeSimdImmd(buf); }, else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}), diff --git a/src/codegen.zig b/src/codegen.zig index 9eea1c667d..df7ceff1f0 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -527,7 +527,7 @@ pub fn generateSymbol( .fail => |em| return Result{ .fail = em }, } } else { - field_val.writeToPackedMemory(field_ty, mod, code.items[current_pos..], bits); + field_val.writeToPackedMemory(field_ty, mod, code.items[current_pos..], bits) catch unreachable; } bits += @intCast(u16, field_ty.bitSize(target)); } diff --git a/src/value.zig b/src/value.zig index 0d80bf7927..5646a837ad 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1249,11 +1249,22 @@ pub const Value = extern union { }; } + fn isDeclRef(val: Value) bool { + var check = val; + while (true) switch (check.tag()) { + .variable, .decl_ref, .decl_ref_mut, .comptime_field_ptr => return true, + .field_ptr => check = check.castTag(.field_ptr).?.data.container_ptr, + .elem_ptr => check = check.castTag(.elem_ptr).?.data.array_ptr, + .eu_payload_ptr, .opt_payload_ptr => check = check.cast(Value.Payload.PayloadPtr).?.data.container_ptr, + else => return false, + }; + } + /// Write a Value's contents to `buffer`. /// /// Asserts that buffer.len >= ty.abiSize(). The buffer is allowed to extend past /// the end of the value in memory. - pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) void { + pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{ReinterpretDeclRef}!void { const target = mod.getTarget(); const endian = target.cpu.arch.endian(); if (val.isUndef()) { @@ -1309,7 +1320,7 @@ pub const Value = extern union { var buf_off: usize = 0; while (elem_i < len) : (elem_i += 1) { const elem_val = val.elemValueBuffer(mod, elem_i, &elem_value_buf); - elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]); + try elem_val.writeToMemory(elem_ty, mod, buffer[buf_off..]); buf_off += elem_size; } }, @@ -1317,7 +1328,7 @@ pub const Value = extern union { // We use byte_count instead of abi_size here, so that any padding bytes // follow the data bytes, on both big- and little-endian systems. const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; - writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); + return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); }, .Struct => switch (ty.containerLayout()) { .Auto => unreachable, // Sema is supposed to have emitted a compile error already @@ -1326,12 +1337,12 @@ pub const Value = extern union { const field_vals = val.castTag(.aggregate).?.data; for (fields, 0..) |field, i| { const off = @intCast(usize, ty.structFieldOffset(i, target)); - writeToMemory(field_vals[i], field.ty, mod, buffer[off..]); + try writeToMemory(field_vals[i], field.ty, mod, buffer[off..]); } }, .Packed => { const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; - writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); + return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); }, }, .ErrorSet => { @@ -1345,9 +1356,14 @@ pub const Value = extern union { .Extern => @panic("TODO implement writeToMemory for extern unions"), .Packed => { const byte_count = (@intCast(usize, ty.bitSize(target)) + 7) / 8; - writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); + return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); }, }, + .Pointer => { + assert(!ty.isSlice()); // No well defined layout. + if (val.isDeclRef()) return error.ReinterpretDeclRef; + return val.writeToMemory(Type.usize, mod, buffer); + }, else => @panic("TODO implement writeToMemory for more types"), } } @@ -1356,7 +1372,7 @@ pub const Value = extern union { /// /// Both the start and the end of the provided buffer must be tight, since /// big-endian packed memory layouts start at the end of the buffer. - pub fn writeToPackedMemory(val: Value, ty: Type, mod: *Module, buffer: []u8, bit_offset: usize) void { + pub fn writeToPackedMemory(val: Value, ty: Type, mod: *Module, buffer: []u8, bit_offset: usize) error{ReinterpretDeclRef}!void { const target = mod.getTarget(); const endian = target.cpu.arch.endian(); if (val.isUndef()) { @@ -1420,7 +1436,7 @@ pub const Value = extern union { // On big-endian systems, LLVM reverses the element order of vectors by default const tgt_elem_i = if (endian == .Big) len - elem_i - 1 else elem_i; const elem_val = val.elemValueBuffer(mod, tgt_elem_i, &elem_value_buf); - elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits); + try elem_val.writeToPackedMemory(elem_ty, mod, buffer, bit_offset + bits); bits += elem_bit_size; } }, @@ -1433,7 +1449,7 @@ pub const Value = extern union { const field_vals = val.castTag(.aggregate).?.data; for (fields, 0..) |field, i| { const field_bits = @intCast(u16, field.ty.bitSize(target)); - field_vals[i].writeToPackedMemory(field.ty, mod, buffer, bit_offset + bits); + try field_vals[i].writeToPackedMemory(field.ty, mod, buffer, bit_offset + bits); bits += field_bits; } }, @@ -1446,9 +1462,14 @@ pub const Value = extern union { const field_type = ty.unionFields().values()[field_index.?].ty; const field_val = val.fieldValue(field_type, field_index.?); - field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); + return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset); }, }, + .Pointer => { + assert(!ty.isSlice()); // No well defined layout. + if (val.isDeclRef()) return error.ReinterpretDeclRef; + return val.writeToPackedMemory(Type.usize, mod, buffer, bit_offset); + }, else => @panic("TODO implement writeToPackedMemory for more types"), } } @@ -1553,6 +1574,10 @@ pub const Value = extern union { }; return Value.initPayload(&payload.base); }, + .Pointer => { + assert(!ty.isSlice()); // No well defined layout. + return readFromMemory(Type.usize, mod, buffer, arena); + }, else => @panic("TODO implement readFromMemory for more types"), } } @@ -1640,6 +1665,10 @@ pub const Value = extern union { return Tag.aggregate.create(arena, field_vals); }, }, + .Pointer => { + assert(!ty.isSlice()); // No well defined layout. + return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); + }, else => @panic("TODO implement readFromPackedMemory for more types"), } } diff --git a/test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig b/test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig index f67a5d139f..2f7bd9c9bc 100644 --- a/test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig +++ b/test/cases/compile_errors/bitCast_same_size_but_bit_count_mismatch.zig @@ -7,4 +7,4 @@ export fn entry(byte: u8) void { // backend=stage2 // target=native // -// :2:29: error: @bitCast size mismatch: destination type 'u7' has 7 bits but source type 'u8' has 8 bits +// :2:16: error: @bitCast size mismatch: destination type 'u7' has 7 bits but source type 'u8' has 8 bits diff --git a/test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig b/test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig index 8951eee5c0..bf87ba8bc5 100644 --- a/test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig +++ b/test/cases/compile_errors/bitCast_with_different_sizes_inside_an_expression.zig @@ -7,4 +7,4 @@ export fn entry() void { // backend=stage2 // target=native // -// :2:29: error: @bitCast size mismatch: destination type 'u8' has 8 bits but source type 'f32' has 32 bits +// :2:16: error: @bitCast size mismatch: destination type 'u8' has 8 bits but source type 'f32' has 32 bits From 476bdc8b0b02cbd09f6a856aa7dc548dea565109 Mon Sep 17 00:00:00 2001 From: Jan Philipp Hafer Date: Sat, 11 Feb 2023 15:16:44 +0100 Subject: [PATCH 009/122] compiler_rt: restructure compiler_rt.zig according to README.md Justifications - compiler_rt base routines are almost finished, so make 1:1 mapping of code and documentation. - Make adjustments to code + documentation simpler to prevent technical or documentation debt. --- lib/compiler_rt.zig | 280 ++++++++++++++++++++------------------ lib/compiler_rt/README.md | 2 +- 2 files changed, 146 insertions(+), 136 deletions(-) diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index feeb8dfb38..b3fcd6cb80 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -3,64 +3,35 @@ const builtin = @import("builtin"); pub const panic = @import("compiler_rt/common.zig").panic; comptime { - _ = @import("compiler_rt/addf3.zig"); - _ = @import("compiler_rt/addhf3.zig"); - _ = @import("compiler_rt/addsf3.zig"); - _ = @import("compiler_rt/adddf3.zig"); - _ = @import("compiler_rt/addtf3.zig"); - _ = @import("compiler_rt/addxf3.zig"); + // Integer routines + _ = @import("compiler_rt/count0bits.zig"); + _ = @import("compiler_rt/parity.zig"); + _ = @import("compiler_rt/popcount.zig"); + _ = @import("compiler_rt/bswap.zig"); + _ = @import("compiler_rt/cmp.zig"); - _ = @import("compiler_rt/subhf3.zig"); - _ = @import("compiler_rt/subsf3.zig"); - _ = @import("compiler_rt/subdf3.zig"); - _ = @import("compiler_rt/subtf3.zig"); - _ = @import("compiler_rt/subxf3.zig"); + _ = @import("compiler_rt/shift.zig"); + _ = @import("compiler_rt/negXi2.zig"); + _ = @import("compiler_rt/int.zig"); + _ = @import("compiler_rt/muldi3.zig"); + _ = @import("compiler_rt/multi3.zig"); + _ = @import("compiler_rt/divti3.zig"); + _ = @import("compiler_rt/udivti3.zig"); + _ = @import("compiler_rt/modti3.zig"); + _ = @import("compiler_rt/umodti3.zig"); - _ = @import("compiler_rt/mulf3.zig"); - _ = @import("compiler_rt/mulhf3.zig"); - _ = @import("compiler_rt/mulsf3.zig"); - _ = @import("compiler_rt/muldf3.zig"); - _ = @import("compiler_rt/multf3.zig"); - _ = @import("compiler_rt/mulxf3.zig"); + _ = @import("compiler_rt/absv.zig"); + _ = @import("compiler_rt/absvsi2.zig"); + _ = @import("compiler_rt/absvdi2.zig"); + _ = @import("compiler_rt/absvti2.zig"); + _ = @import("compiler_rt/negv.zig"); - _ = @import("compiler_rt/powiXf2.zig"); - _ = @import("compiler_rt/mulc3.zig"); - _ = @import("compiler_rt/mulhc3.zig"); - _ = @import("compiler_rt/mulsc3.zig"); - _ = @import("compiler_rt/muldc3.zig"); - _ = @import("compiler_rt/mulxc3.zig"); - _ = @import("compiler_rt/multc3.zig"); - - _ = @import("compiler_rt/divc3.zig"); - _ = @import("compiler_rt/divhc3.zig"); - _ = @import("compiler_rt/divsc3.zig"); - _ = @import("compiler_rt/divdc3.zig"); - _ = @import("compiler_rt/divxc3.zig"); - _ = @import("compiler_rt/divtc3.zig"); - - _ = @import("compiler_rt/neghf2.zig"); - _ = @import("compiler_rt/negsf2.zig"); - _ = @import("compiler_rt/negdf2.zig"); - _ = @import("compiler_rt/negtf2.zig"); - _ = @import("compiler_rt/negxf2.zig"); - - _ = @import("compiler_rt/comparef.zig"); - _ = @import("compiler_rt/cmphf2.zig"); - _ = @import("compiler_rt/cmpsf2.zig"); - _ = @import("compiler_rt/cmpdf2.zig"); - _ = @import("compiler_rt/cmptf2.zig"); - _ = @import("compiler_rt/cmpxf2.zig"); - _ = @import("compiler_rt/gehf2.zig"); - _ = @import("compiler_rt/gesf2.zig"); - _ = @import("compiler_rt/gedf2.zig"); - _ = @import("compiler_rt/gexf2.zig"); - _ = @import("compiler_rt/getf2.zig"); - _ = @import("compiler_rt/unordhf2.zig"); - _ = @import("compiler_rt/unordsf2.zig"); - _ = @import("compiler_rt/unorddf2.zig"); - _ = @import("compiler_rt/unordxf2.zig"); - _ = @import("compiler_rt/unordtf2.zig"); + _ = @import("compiler_rt/addo.zig"); + _ = @import("compiler_rt/subo.zig"); + _ = @import("compiler_rt/mulo.zig"); + // Float routines + // conversion _ = @import("compiler_rt/extendf.zig"); _ = @import("compiler_rt/extendhfsf2.zig"); _ = @import("compiler_rt/extendhfdf2.zig"); @@ -85,70 +56,6 @@ comptime { _ = @import("compiler_rt/trunctfdf2.zig"); _ = @import("compiler_rt/trunctfxf2.zig"); - _ = @import("compiler_rt/divhf3.zig"); - _ = @import("compiler_rt/divsf3.zig"); - _ = @import("compiler_rt/divdf3.zig"); - _ = @import("compiler_rt/divxf3.zig"); - _ = @import("compiler_rt/divtf3.zig"); - _ = @import("compiler_rt/sin.zig"); - _ = @import("compiler_rt/cos.zig"); - _ = @import("compiler_rt/sincos.zig"); - _ = @import("compiler_rt/ceil.zig"); - _ = @import("compiler_rt/exp.zig"); - _ = @import("compiler_rt/exp2.zig"); - _ = @import("compiler_rt/fabs.zig"); - _ = @import("compiler_rt/floor.zig"); - _ = @import("compiler_rt/fma.zig"); - _ = @import("compiler_rt/fmax.zig"); - _ = @import("compiler_rt/fmin.zig"); - _ = @import("compiler_rt/fmod.zig"); - _ = @import("compiler_rt/log.zig"); - _ = @import("compiler_rt/log10.zig"); - _ = @import("compiler_rt/log2.zig"); - _ = @import("compiler_rt/round.zig"); - _ = @import("compiler_rt/sqrt.zig"); - _ = @import("compiler_rt/tan.zig"); - _ = @import("compiler_rt/trunc.zig"); - _ = @import("compiler_rt/divti3.zig"); - _ = @import("compiler_rt/modti3.zig"); - _ = @import("compiler_rt/multi3.zig"); - _ = @import("compiler_rt/udivti3.zig"); - _ = @import("compiler_rt/udivmodei4.zig"); - _ = @import("compiler_rt/udivmodti4.zig"); - _ = @import("compiler_rt/umodti3.zig"); - - _ = @import("compiler_rt/int_to_float.zig"); - _ = @import("compiler_rt/floatsihf.zig"); - _ = @import("compiler_rt/floatsisf.zig"); - _ = @import("compiler_rt/floatsidf.zig"); - _ = @import("compiler_rt/floatsitf.zig"); - _ = @import("compiler_rt/floatsixf.zig"); - _ = @import("compiler_rt/floatdihf.zig"); - _ = @import("compiler_rt/floatdisf.zig"); - _ = @import("compiler_rt/floatdidf.zig"); - _ = @import("compiler_rt/floatditf.zig"); - _ = @import("compiler_rt/floatdixf.zig"); - _ = @import("compiler_rt/floattihf.zig"); - _ = @import("compiler_rt/floattisf.zig"); - _ = @import("compiler_rt/floattidf.zig"); - _ = @import("compiler_rt/floattitf.zig"); - _ = @import("compiler_rt/floattixf.zig"); - _ = @import("compiler_rt/floatundihf.zig"); - _ = @import("compiler_rt/floatundisf.zig"); - _ = @import("compiler_rt/floatundidf.zig"); - _ = @import("compiler_rt/floatunditf.zig"); - _ = @import("compiler_rt/floatundixf.zig"); - _ = @import("compiler_rt/floatunsihf.zig"); - _ = @import("compiler_rt/floatunsisf.zig"); - _ = @import("compiler_rt/floatunsidf.zig"); - _ = @import("compiler_rt/floatunsitf.zig"); - _ = @import("compiler_rt/floatunsixf.zig"); - _ = @import("compiler_rt/floatuntihf.zig"); - _ = @import("compiler_rt/floatuntisf.zig"); - _ = @import("compiler_rt/floatuntidf.zig"); - _ = @import("compiler_rt/floatuntitf.zig"); - _ = @import("compiler_rt/floatuntixf.zig"); - _ = @import("compiler_rt/float_to_int.zig"); _ = @import("compiler_rt/fixhfsi.zig"); _ = @import("compiler_rt/fixhfdi.zig"); @@ -181,28 +88,131 @@ comptime { _ = @import("compiler_rt/fixunsxfdi.zig"); _ = @import("compiler_rt/fixunsxfti.zig"); - _ = @import("compiler_rt/count0bits.zig"); - _ = @import("compiler_rt/parity.zig"); - _ = @import("compiler_rt/popcount.zig"); - _ = @import("compiler_rt/bswap.zig"); - _ = @import("compiler_rt/int.zig"); - _ = @import("compiler_rt/shift.zig"); + _ = @import("compiler_rt/int_to_float.zig"); + _ = @import("compiler_rt/floatsihf.zig"); + _ = @import("compiler_rt/floatsisf.zig"); + _ = @import("compiler_rt/floatsidf.zig"); + _ = @import("compiler_rt/floatsitf.zig"); + _ = @import("compiler_rt/floatsixf.zig"); + _ = @import("compiler_rt/floatdihf.zig"); + _ = @import("compiler_rt/floatdisf.zig"); + _ = @import("compiler_rt/floatdidf.zig"); + _ = @import("compiler_rt/floatditf.zig"); + _ = @import("compiler_rt/floatdixf.zig"); + _ = @import("compiler_rt/floattihf.zig"); + _ = @import("compiler_rt/floattisf.zig"); + _ = @import("compiler_rt/floattidf.zig"); + _ = @import("compiler_rt/floattitf.zig"); + _ = @import("compiler_rt/floattixf.zig"); + _ = @import("compiler_rt/floatundihf.zig"); + _ = @import("compiler_rt/floatundisf.zig"); + _ = @import("compiler_rt/floatundidf.zig"); + _ = @import("compiler_rt/floatunditf.zig"); + _ = @import("compiler_rt/floatundixf.zig"); + _ = @import("compiler_rt/floatunsihf.zig"); + _ = @import("compiler_rt/floatunsisf.zig"); + _ = @import("compiler_rt/floatunsidf.zig"); + _ = @import("compiler_rt/floatunsitf.zig"); + _ = @import("compiler_rt/floatunsixf.zig"); + _ = @import("compiler_rt/floatuntihf.zig"); + _ = @import("compiler_rt/floatuntisf.zig"); + _ = @import("compiler_rt/floatuntidf.zig"); + _ = @import("compiler_rt/floatuntitf.zig"); + _ = @import("compiler_rt/floatuntixf.zig"); - _ = @import("compiler_rt/negXi2.zig"); + // comparison + _ = @import("compiler_rt/comparef.zig"); + _ = @import("compiler_rt/cmphf2.zig"); + _ = @import("compiler_rt/cmpsf2.zig"); + _ = @import("compiler_rt/cmpdf2.zig"); + _ = @import("compiler_rt/cmptf2.zig"); + _ = @import("compiler_rt/cmpxf2.zig"); + _ = @import("compiler_rt/unordhf2.zig"); + _ = @import("compiler_rt/unordsf2.zig"); + _ = @import("compiler_rt/unorddf2.zig"); + _ = @import("compiler_rt/unordxf2.zig"); + _ = @import("compiler_rt/unordtf2.zig"); + _ = @import("compiler_rt/gehf2.zig"); + _ = @import("compiler_rt/gesf2.zig"); + _ = @import("compiler_rt/gedf2.zig"); + _ = @import("compiler_rt/gexf2.zig"); + _ = @import("compiler_rt/getf2.zig"); - _ = @import("compiler_rt/muldi3.zig"); + // arithmetic + _ = @import("compiler_rt/addf3.zig"); + _ = @import("compiler_rt/addhf3.zig"); + _ = @import("compiler_rt/addsf3.zig"); + _ = @import("compiler_rt/adddf3.zig"); + _ = @import("compiler_rt/addtf3.zig"); + _ = @import("compiler_rt/addxf3.zig"); - _ = @import("compiler_rt/absv.zig"); - _ = @import("compiler_rt/absvsi2.zig"); - _ = @import("compiler_rt/absvdi2.zig"); - _ = @import("compiler_rt/absvti2.zig"); + _ = @import("compiler_rt/subhf3.zig"); + _ = @import("compiler_rt/subsf3.zig"); + _ = @import("compiler_rt/subdf3.zig"); + _ = @import("compiler_rt/subtf3.zig"); + _ = @import("compiler_rt/subxf3.zig"); - _ = @import("compiler_rt/negv.zig"); - _ = @import("compiler_rt/addo.zig"); - _ = @import("compiler_rt/subo.zig"); - _ = @import("compiler_rt/mulo.zig"); - _ = @import("compiler_rt/cmp.zig"); + _ = @import("compiler_rt/mulf3.zig"); + _ = @import("compiler_rt/mulhf3.zig"); + _ = @import("compiler_rt/mulsf3.zig"); + _ = @import("compiler_rt/muldf3.zig"); + _ = @import("compiler_rt/multf3.zig"); + _ = @import("compiler_rt/mulxf3.zig"); + _ = @import("compiler_rt/divhf3.zig"); + _ = @import("compiler_rt/divsf3.zig"); + _ = @import("compiler_rt/divdf3.zig"); + _ = @import("compiler_rt/divxf3.zig"); + _ = @import("compiler_rt/divtf3.zig"); + + _ = @import("compiler_rt/neghf2.zig"); + _ = @import("compiler_rt/negsf2.zig"); + _ = @import("compiler_rt/negdf2.zig"); + _ = @import("compiler_rt/negtf2.zig"); + _ = @import("compiler_rt/negxf2.zig"); + + // other + _ = @import("compiler_rt/powiXf2.zig"); + _ = @import("compiler_rt/mulc3.zig"); + _ = @import("compiler_rt/mulhc3.zig"); + _ = @import("compiler_rt/mulsc3.zig"); + _ = @import("compiler_rt/muldc3.zig"); + _ = @import("compiler_rt/mulxc3.zig"); + _ = @import("compiler_rt/multc3.zig"); + + _ = @import("compiler_rt/divc3.zig"); + _ = @import("compiler_rt/divhc3.zig"); + _ = @import("compiler_rt/divsc3.zig"); + _ = @import("compiler_rt/divdc3.zig"); + _ = @import("compiler_rt/divxc3.zig"); + _ = @import("compiler_rt/divtc3.zig"); + + // Math routines. Alphabetically sorted. + _ = @import("compiler_rt/ceil.zig"); + _ = @import("compiler_rt/cos.zig"); + _ = @import("compiler_rt/exp.zig"); + _ = @import("compiler_rt/exp2.zig"); + _ = @import("compiler_rt/fabs.zig"); + _ = @import("compiler_rt/floor.zig"); + _ = @import("compiler_rt/fma.zig"); + _ = @import("compiler_rt/fmax.zig"); + _ = @import("compiler_rt/fmin.zig"); + _ = @import("compiler_rt/fmod.zig"); + _ = @import("compiler_rt/log.zig"); + _ = @import("compiler_rt/log10.zig"); + _ = @import("compiler_rt/log2.zig"); + _ = @import("compiler_rt/round.zig"); + _ = @import("compiler_rt/sin.zig"); + _ = @import("compiler_rt/sincos.zig"); + _ = @import("compiler_rt/sqrt.zig"); + _ = @import("compiler_rt/tan.zig"); + _ = @import("compiler_rt/trunc.zig"); + + // BigInt. Alphabetically sorted. + _ = @import("compiler_rt/udivmodei4.zig"); + _ = @import("compiler_rt/udivmodti4.zig"); + + // extra _ = @import("compiler_rt/os_version_check.zig"); _ = @import("compiler_rt/emutls.zig"); _ = @import("compiler_rt/arm.zig"); diff --git a/lib/compiler_rt/README.md b/lib/compiler_rt/README.md index 0590c33fde..fb581daf2a 100644 --- a/lib/compiler_rt/README.md +++ b/lib/compiler_rt/README.md @@ -331,7 +331,7 @@ Integer and Float Operations | ✓ | __negdf2 | f64 | ∅ | f64 | .. | | ✓ | __negtf2 | f128 | ∅ | f128 | .. | | ✓ | __negxf2 | f80 | ∅ | f80 | .. | -| | | | | | **Floating point raised to integer power** | +| | | | | | **Other** | | ✓ | __powihf2 | f16 | i32 | f16 | `a ^ b` | | ✓ | __powisf2 | f32 | i32 | f32 | .. | | ✓ | __powidf2 | f64 | i32 | f64 | .. | From 19984d8751894608dc3cd2271c8e2e40c476d5df Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sat, 21 Jan 2023 02:49:14 +1100 Subject: [PATCH 010/122] std.hash: add XxHash64 and XxHash32 --- lib/std/hash.zig | 5 + lib/std/hash/xxhash.zig | 268 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 lib/std/hash/xxhash.zig diff --git a/lib/std/hash.zig b/lib/std/hash.zig index 2680a8e263..8e92b4c9de 100644 --- a/lib/std/hash.zig +++ b/lib/std/hash.zig @@ -32,6 +32,10 @@ pub const CityHash64 = cityhash.CityHash64; const wyhash = @import("hash/wyhash.zig"); pub const Wyhash = wyhash.Wyhash; +const xxhash = @import("hash/xxhash.zig"); +pub const XxHash64 = xxhash.XxHash64; +pub const XxHash32 = xxhash.XxHash32; + test "hash" { _ = adler; _ = auto_hash; @@ -40,4 +44,5 @@ test "hash" { _ = murmur; _ = cityhash; _ = wyhash; + _ = xxhash; } diff --git a/lib/std/hash/xxhash.zig b/lib/std/hash/xxhash.zig new file mode 100644 index 0000000000..633bbbfad2 --- /dev/null +++ b/lib/std/hash/xxhash.zig @@ -0,0 +1,268 @@ +const std = @import("std"); +const mem = std.mem; +const expectEqual = std.testing.expectEqual; + +inline fn rotl(comptime count: comptime_int, value: anytype) @TypeOf(value) { + return (value << count) | (value >> (@bitSizeOf(@TypeOf(value)) - count)); +} + +pub const XxHash64 = struct { + acc1: u64, + acc2: u64, + acc3: u64, + acc4: u64, + + seed: u64, + buf: [32]u8, + buf_len: usize, + byte_count: usize, + + const prime_1 = 0x9E3779B185EBCA87; // 0b1001111000110111011110011011000110000101111010111100101010000111 + const prime_2 = 0xC2B2AE3D27D4EB4F; // 0b1100001010110010101011100011110100100111110101001110101101001111 + const prime_3 = 0x165667B19E3779F9; // 0b0001011001010110011001111011000110011110001101110111100111111001 + const prime_4 = 0x85EBCA77C2B2AE63; // 0b1000010111101011110010100111011111000010101100101010111001100011 + const prime_5 = 0x27D4EB2F165667C5; // 0b0010011111010100111010110010111100010110010101100110011111000101 + + pub fn init(seed: u64) XxHash64 { + return XxHash64{ + .seed = seed, + .acc1 = seed +% prime_1 +% prime_2, + .acc2 = seed +% prime_2, + .acc3 = seed, + .acc4 = seed -% prime_1, + .buf = undefined, + .buf_len = 0, + .byte_count = 0, + }; + } + + pub fn update(self: *XxHash64, input: []const u8) void { + if (input.len < 32 - self.buf_len) { + mem.copy(u8, self.buf[self.buf_len..], input); + self.buf_len += input.len; + return; + } + + var i: usize = 0; + + if (self.buf_len > 0) { + i = 32 - self.buf_len; + mem.copy(u8, self.buf[self.buf_len..], input[0..i]); + self.processStripe(&self.buf); + self.buf_len = 0; + } + + while (i + 32 <= input.len) : (i += 32) { + self.processStripe(input[i..][0..32]); + } + + const remaining_bytes = input[i..]; + mem.copy(u8, &self.buf, remaining_bytes); + self.buf_len = remaining_bytes.len; + } + + inline fn processStripe(self: *XxHash64, buf: *const [32]u8) void { + self.acc1 = round(self.acc1, mem.readIntLittle(u64, buf[0..8])); + self.acc2 = round(self.acc2, mem.readIntLittle(u64, buf[8..16])); + self.acc3 = round(self.acc3, mem.readIntLittle(u64, buf[16..24])); + self.acc4 = round(self.acc4, mem.readIntLittle(u64, buf[24..32])); + self.byte_count += 32; + } + + inline fn round(acc: u64, lane: u64) u64 { + const a = acc +% (lane *% prime_2); + const b = rotl(31, a); + return b *% prime_1; + } + + pub fn final(self: *XxHash64) u64 { + var acc: u64 = undefined; + + if (self.byte_count < 32) { + acc = self.seed +% prime_5; + } else { + acc = rotl(1, self.acc1) +% rotl(7, self.acc2) +% rotl(12, self.acc3) +% rotl(18, self.acc4); + acc = mergeAccumulator(acc, self.acc1); + acc = mergeAccumulator(acc, self.acc2); + acc = mergeAccumulator(acc, self.acc3); + acc = mergeAccumulator(acc, self.acc4); + } + + acc = acc +% @as(u64, self.byte_count) +% @as(u64, self.buf_len); + + var pos: usize = 0; + while (pos + 8 <= self.buf_len) : (pos += 8) { + const lane = mem.readIntLittle(u64, self.buf[pos..][0..8]); + acc ^= round(0, lane); + acc = rotl(27, acc) *% prime_1; + acc +%= prime_4; + } + + if (pos + 4 <= self.buf_len) { + const lane = @as(u64, mem.readIntLittle(u32, self.buf[pos..][0..4])); + acc ^= lane *% prime_1; + acc = rotl(23, acc) *% prime_2; + acc +%= prime_3; + pos += 4; + } + + while (pos < self.buf_len) : (pos += 1) { + const lane = @as(u64, self.buf[pos]); + acc ^= lane *% prime_5; + acc = rotl(11, acc) *% prime_1; + } + + acc ^= acc >> 33; + acc *%= prime_2; + acc ^= acc >> 29; + acc *%= prime_3; + acc ^= acc >> 32; + + return acc; + } + + inline fn mergeAccumulator(acc: u64, other: u64) u64 { + const a = acc ^ round(0, other); + const b = a *% prime_1; + return b +% prime_4; + } + + pub fn hash(input: []const u8) u64 { + var hasher = XxHash64.init(0); + hasher.update(input); + return hasher.final(); + } +}; + +pub const XxHash32 = struct { + acc1: u32, + acc2: u32, + acc3: u32, + acc4: u32, + + seed: u32, + buf: [16]u8, + buf_len: usize, + byte_count: usize, + + const prime_1 = 0x9E3779B1; // 0b10011110001101110111100110110001 + const prime_2 = 0x85EBCA77; // 0b10000101111010111100101001110111 + const prime_3 = 0xC2B2AE3D; // 0b11000010101100101010111000111101 + const prime_4 = 0x27D4EB2F; // 0b00100111110101001110101100101111 + const prime_5 = 0x165667B1; // 0b00010110010101100110011110110001 + + pub fn init(seed: u32) XxHash32 { + return XxHash32{ + .seed = seed, + .acc1 = seed +% prime_1 +% prime_2, + .acc2 = seed +% prime_2, + .acc3 = seed, + .acc4 = seed -% prime_1, + .buf = undefined, + .buf_len = 0, + .byte_count = 0, + }; + } + + pub fn update(self: *XxHash32, input: []const u8) void { + if (input.len < 16 - self.buf_len) { + mem.copy(u8, self.buf[self.buf_len..], input); + self.buf_len += input.len; + return; + } + + var i: usize = 0; + + if (self.buf_len > 0) { + i = 16 - self.buf_len; + mem.copy(u8, self.buf[self.buf_len..], input[0..i]); + self.processStripe(&self.buf); + self.buf_len = 0; + } + + while (i + 16 <= input.len) : (i += 16) { + self.processStripe(input[i..][0..16]); + } + + const remaining_bytes = input[i..]; + mem.copy(u8, &self.buf, remaining_bytes); + self.buf_len = remaining_bytes.len; + } + + inline fn processStripe(self: *XxHash32, buf: *const [16]u8) void { + self.acc1 = round(self.acc1, mem.readIntLittle(u32, buf[0..4])); + self.acc2 = round(self.acc2, mem.readIntLittle(u32, buf[4..8])); + self.acc3 = round(self.acc3, mem.readIntLittle(u32, buf[8..12])); + self.acc4 = round(self.acc4, mem.readIntLittle(u32, buf[12..16])); + self.byte_count += 16; + } + + inline fn round(acc: u32, lane: u32) u32 { + const a = acc +% (lane *% prime_2); + const b = rotl(13, a); + return b *% prime_1; + } + + pub fn final(self: *XxHash32) u32 { + var acc: u32 = undefined; + + if (self.byte_count < 16) { + acc = self.seed +% prime_5; + } else { + acc = rotl(1, self.acc1) +% rotl(7, self.acc2) +% rotl(12, self.acc3) +% rotl(18, self.acc4); + } + + acc = acc +% @intCast(u32, self.byte_count) +% @intCast(u32, self.buf_len); + + var pos: usize = 0; + while (pos + 4 <= self.buf_len) : (pos += 4) { + const lane = mem.readIntLittle(u32, self.buf[pos..][0..4]); + acc +%= lane *% prime_3; + acc = rotl(17, acc) *% prime_4; + } + + while (pos < self.buf_len) : (pos += 1) { + const lane = @as(u32, self.buf[pos]); + acc +%= lane *% prime_5; + acc = rotl(11, acc) *% prime_1; + } + + acc ^= acc >> 15; + acc *%= prime_2; + acc ^= acc >> 13; + acc *%= prime_3; + acc ^= acc >> 16; + + return acc; + } + + pub fn hash(input: []const u8) u32 { + var hasher = XxHash32.init(0); + hasher.update(input); + return hasher.final(); + } +}; + +test "xxhash64" { + const hash = XxHash64.hash; + + try expectEqual(hash(""), 0xef46db3751d8e999); + try expectEqual(hash("a"), 0xd24ec4f1a98c6e5b); + try expectEqual(hash("abc"), 0x44bc2cf5ad770999); + try expectEqual(hash("message digest"), 0x066ed728fceeb3be); + try expectEqual(hash("abcdefghijklmnopqrstuvwxyz"), 0xcfe1f278fa89835c); + try expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0xaaa46907d3047814); + try expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0xe04a477f19ee145d); +} + +test "xxhash32" { + const hash = XxHash32.hash; + + try expectEqual(hash(""), 0x02cc5d05); + try expectEqual(hash("a"), 0x550d7456); + try expectEqual(hash("abc"), 0x32d153ff); + try expectEqual(hash("message digest"), 0x7c948494); + try expectEqual(hash("abcdefghijklmnopqrstuvwxyz"), 0x63a14d5f); + try expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x9c285e64); + try expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x9c05f475); +} From 61cb5143872ec2f3ae9c2942a03e18968bf27761 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sat, 21 Jan 2023 19:10:47 +1100 Subject: [PATCH 011/122] std.compress: add zstandard decompressor --- build.zig | 3 + lib/std/compress.zig | 2 + lib/std/compress/testdata/rfc8478.txt | 3027 ++++++++++++++++++ lib/std/compress/testdata/rfc8478.txt.zst.19 | Bin 0 -> 22211 bytes lib/std/compress/testdata/rfc8478.txt.zst.3 | Bin 0 -> 25639 bytes lib/std/compress/zstandard.zig | 22 + lib/std/compress/zstandard/decompress.zig | 1249 ++++++++ lib/std/compress/zstandard/types.zig | 394 +++ 8 files changed, 4697 insertions(+) create mode 100644 lib/std/compress/testdata/rfc8478.txt create mode 100644 lib/std/compress/testdata/rfc8478.txt.zst.19 create mode 100644 lib/std/compress/testdata/rfc8478.txt.zst.3 create mode 100644 lib/std/compress/zstandard.zig create mode 100644 lib/std/compress/zstandard/decompress.zig create mode 100644 lib/std/compress/zstandard/types.zig diff --git a/build.zig b/build.zig index faf14cc405..f75efeb8b4 100644 --- a/build.zig +++ b/build.zig @@ -113,8 +113,11 @@ pub fn build(b: *std.Build) !void { ".gz", ".z.0", ".z.9", + ".zstd.3", + ".zstd.19", "rfc1951.txt", "rfc1952.txt", + "rfc8478.txt", // exclude files from lib/std/compress/deflate/testdata ".expect", ".expect-noinput", diff --git a/lib/std/compress.zig b/lib/std/compress.zig index 9af1b30259..02e17474a1 100644 --- a/lib/std/compress.zig +++ b/lib/std/compress.zig @@ -6,6 +6,7 @@ pub const lzma = @import("compress/lzma.zig"); pub const lzma2 = @import("compress/lzma2.zig"); pub const xz = @import("compress/xz.zig"); pub const zlib = @import("compress/zlib.zig"); +pub const zstandard = @import("compress/zstandard.zig"); pub fn HashedReader( comptime ReaderType: anytype, @@ -44,4 +45,5 @@ test { _ = lzma2; _ = xz; _ = zlib; + _ = zstandard; } diff --git a/lib/std/compress/testdata/rfc8478.txt b/lib/std/compress/testdata/rfc8478.txt new file mode 100644 index 0000000000..e4ac22a302 --- /dev/null +++ b/lib/std/compress/testdata/rfc8478.txt @@ -0,0 +1,3027 @@ + + + + + + +Internet Engineering Task Force (IETF) Y. Collet +Request for Comments: 8478 M. Kucherawy, Ed. +Category: Informational Facebook +ISSN: 2070-1721 October 2018 + + + Zstandard Compression and the application/zstd Media Type + +Abstract + + Zstandard, or "zstd" (pronounced "zee standard"), is a data + compression mechanism. This document describes the mechanism and + registers a media type and content encoding to be used when + transporting zstd-compressed content via Multipurpose Internet Mail + Extensions (MIME). + + Despite use of the word "standard" as part of its name, readers are + advised that this document is not an Internet Standards Track + specification; it is being published for informational purposes only. + +Status of This Memo + + This document is not an Internet Standards Track specification; it is + published for informational purposes. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Not all documents + approved by the IESG are candidates for any level of Internet + Standard; see Section 2 of RFC 7841. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + https://www.rfc-editor.org/info/rfc8478. + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 1] + +RFC 8478 application/zstd October 2018 + + +Copyright Notice + + Copyright (c) 2018 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 2] + +RFC 8478 application/zstd October 2018 + + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4 + 2. Definitions . . . . . . . . . . . . . . . . . . . . . . . . . 4 + 3. Compression Algorithm . . . . . . . . . . . . . . . . . . . . 5 + 3.1. Frames . . . . . . . . . . . . . . . . . . . . . . . . . 6 + 3.1.1. Zstandard Frames . . . . . . . . . . . . . . . . . . 6 + 3.1.1.1. Frame Header . . . . . . . . . . . . . . . . . . 7 + 3.1.1.2. Blocks . . . . . . . . . . . . . . . . . . . . . 12 + 3.1.1.3. Compressed Blocks . . . . . . . . . . . . . . . . 14 + 3.1.1.4. Sequence Execution . . . . . . . . . . . . . . . 28 + 3.1.1.5. Repeat Offsets . . . . . . . . . . . . . . . . . 29 + 3.1.2. Skippable Frames . . . . . . . . . . . . . . . . . . 30 + 4. Entropy Encoding . . . . . . . . . . . . . . . . . . . . . . 30 + 4.1. FSE . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 + 4.1.1. FSE Table Description . . . . . . . . . . . . . . . . 31 + 4.2. Huffman Coding . . . . . . . . . . . . . . . . . . . . . 34 + 4.2.1. Huffman Tree Description . . . . . . . . . . . . . . 35 + 4.2.1.1. Huffman Tree Header . . . . . . . . . . . . . . . 36 + 4.2.1.2. FSE Compression of Huffman Weights . . . . . . . 37 + 4.2.1.3. Conversion from Weights to Huffman Prefix Codes . 38 + 4.2.2. Huffman-Coded Streams . . . . . . . . . . . . . . . . 39 + 5. Dictionary Format . . . . . . . . . . . . . . . . . . . . . . 40 + 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 42 + 6.1. The 'application/zstd' Media Type . . . . . . . . . . . . 42 + 6.2. Content Encoding . . . . . . . . . . . . . . . . . . . . 43 + 6.3. Dictionaries . . . . . . . . . . . . . . . . . . . . . . 43 + 7. Security Considerations . . . . . . . . . . . . . . . . . . . 43 + 8. Implementation Status . . . . . . . . . . . . . . . . . . . . 44 + 9. References . . . . . . . . . . . . . . . . . . . . . . . . . 45 + 9.1. Normative References . . . . . . . . . . . . . . . . . . 45 + 9.2. Informative References . . . . . . . . . . . . . . . . . 45 + Appendix A. Decoding Tables for Predefined Codes . . . . . . . . 46 + A.1. Literal Length Code Table . . . . . . . . . . . . . . . . 46 + A.2. Match Length Code Table . . . . . . . . . . . . . . . . . 49 + A.3. Offset Code Table . . . . . . . . . . . . . . . . . . . . 52 + Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 53 + Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 54 + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 3] + +RFC 8478 application/zstd October 2018 + + +1. Introduction + + Zstandard, or "zstd" (pronounced "zee standard"), is a data + compression mechanism, akin to gzip [RFC1952]. + + Despite use of the word "standard" as part of its name, readers are + advised that this document is not an Internet Standards Track + specification; it is being published for informational purposes only. + + This document describes the Zstandard format. Also, to enable the + transport of a data object compressed with Zstandard, this document + registers a media type that can be used to identify such content when + it is used in a payload encoded using Multipurpose Internet Mail + Extensions (MIME). + +2. Definitions + + Some terms used elsewhere in this document are defined here for + clarity. + + uncompressed: Describes an arbitrary set of bytes in their original + form, prior to being subjected to compression. + + compress, compression: The act of processing a set of bytes via the + compression mechanism described here. + + compressed: Describes the result of passing a set of bytes through + this mechanism. The original input has thus been compressed. + + decompress, decompression: The act of processing a set of bytes + through the inverse of the compression mechanism described here, + in an attempt to recover the original set of bytes prior to + compression. + + decompressed: Describes the result of passing a set of bytes through + the reverse of this mechanism. When this is successful, the + decompressed payload and the uncompressed payload are + indistinguishable. + + encode: The process of translating data from one form to another; + this may include compression or it may refer to other translations + done as part of this specification. + + decode: The reverse of "encode"; describes a process of reversing a + prior encoding to recover the original content. + + + + + + +Collet & Kucherawy Informational [Page 4] + +RFC 8478 application/zstd October 2018 + + + frame: Content compressed by Zstandard is transformed into a + Zstandard frame. Multiple frames can be appended into a single + file or stream. A frame is completely independent, has a defined + beginning and end, and has a set of parameters that tells the + decoder how to decompress it. + + block: A frame encapsulates one or multiple blocks. Each block + contains arbitrary content, which is described by its header, and + has a guaranteed maximum content size that depends upon frame + parameters. Unlike frames, each block depends on previous blocks + for proper decoding. However, each block can be decompressed + without waiting for its successor, allowing streaming operations. + + natural order: A sequence or ordering of objects or values that is + typical of that type of object or value. A set of unique + integers, for example, is in "natural order" if when progressing + from one element in the set or sequence to the next, there is + never a decrease in value. + + The naming convention for identifiers within the specification is + Mixed_Case_With_Underscores. Identifiers inside square brackets + indicate that the identifier is optional in the presented context. + +3. Compression Algorithm + + This section describes the Zstandard algorithm. + + The purpose of this document is to define a lossless compressed data + format that is a) independent of the CPU type, operating system, file + system, and character set and b) is suitable for file compression and + pipe and streaming compression, using the Zstandard algorithm. The + text of the specification assumes a basic background in programming + at the level of bits and other primitive data representations. + + The data can be produced or consumed, even for an arbitrarily long + sequentially presented input data stream, using only an a priori + bounded amount of intermediate storage, and hence can be used in data + communications. The format uses the Zstandard compression method, + and an optional xxHash-64 checksum method [XXHASH], for detection of + data corruption. + + The data format defined by this specification does not attempt to + allow random access to compressed data. + + Unless otherwise indicated below, a compliant compressor must produce + data sets that conform to the specifications presented here. + However, it does not need to support all options. + + + + +Collet & Kucherawy Informational [Page 5] + +RFC 8478 application/zstd October 2018 + + + A compliant decompressor must be able to decompress at least one + working set of parameters that conforms to the specifications + presented here. It may also ignore informative fields, such as the + checksum. Whenever it does not support a parameter defined in the + compressed stream, it must produce a non-ambiguous error code and + associated error message explaining which parameter is unsupported. + + This specification is intended for use by implementers of software to + compress data into Zstandard format and/or decompress data from + Zstandard format. The Zstandard format is supported by an open + source reference implementation, written in portable C, and available + at [ZSTD]. + +3.1. Frames + + Zstandard compressed data is made up of one or more frames. Each + frame is independent and can be decompressed independently of other + frames. The decompressed content of multiple concatenated frames is + the concatenation of each frame's decompressed content. + + There are two frame formats defined for Zstandard: Zstandard frames + and skippable frames. Zstandard frames contain compressed data, + while skippable frames contain custom user metadata. + +3.1.1. Zstandard Frames + + The structure of a single Zstandard frame is as follows: + + +--------------------+------------+ + | Magic_Number | 4 bytes | + +--------------------+------------+ + | Frame_Header | 2-14 bytes | + +--------------------+------------+ + | Data_Block | n bytes | + +--------------------+------------+ + | [More Data_Blocks] | | + +--------------------+------------+ + | [Content_Checksum] | 0-4 bytes | + +--------------------+------------+ + + Magic_Number: 4 bytes, little-endian format. Value: 0xFD2FB528. + + Frame_Header: 2 to 14 bytes, detailed in Section 3.1.1.1. + + Data_Block: Detailed in Section 3.1.1.2. This is where data + appears. + + + + + +Collet & Kucherawy Informational [Page 6] + +RFC 8478 application/zstd October 2018 + + + Content_Checksum: An optional 32-bit checksum, only present if + Content_Checksum_Flag is set. The content checksum is the result + of the XXH64() hash function [XXHASH] digesting the original + (decoded) data as input, and a seed of zero. The low 4 bytes of + the checksum are stored in little-endian format. + + The magic number was selected to be less probable to find at the + beginning of an arbitrary file. It avoids trivial patterns (0x00, + 0xFF, repeated bytes, increasing bytes, etc.), contains byte values + outside of ASCII range, and doesn't map into UTF-8 space, all of + which reduce the likelihood of its appearance at the top of a text + file. + +3.1.1.1. Frame Header + + The frame header has a variable size, with a minimum of 2 bytes and + up to 14 bytes depending on optional parameters. The structure of + Frame_Header is as follows: + + +-------------------------+-----------+ + | Frame_Header_Descriptor | 1 byte | + +-------------------------+-----------+ + | [Window_Descriptor] | 0-1 byte | + +-------------------------+-----------+ + | [Dictionary_ID] | 0-4 bytes | + +-------------------------+-----------+ + | [Frame_Content_Size] | 0-8 bytes | + +-------------------------+-----------+ + + + + + + + + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 7] + +RFC 8478 application/zstd October 2018 + + +3.1.1.1.1. Frame_Header_Descriptor + + The first header's byte is called the Frame_Header_Descriptor. It + describes which other fields are present. Decoding this byte is + enough to tell the size of Frame_Header. + + +------------+-------------------------+ + | Bit Number | Field Name | + +------------+-------------------------+ + | 7-6 | Frame_Content_Size_Flag | + +------------+-------------------------+ + | 5 | Single_Segment_Flag | + +------------+-------------------------+ + | 4 | (unused) | + +------------+-------------------------+ + | 3 | (reserved) | + +------------+-------------------------+ + | 2 | Content_Checksum_Flag | + +------------+-------------------------+ + | 1-0 | Dictionary_ID_Flag | + +------------+-------------------------+ + + In this table, bit 7 is the highest bit, while bit 0 is the lowest + one. + +3.1.1.1.1.1. Frame_Content_Size_Flag + + This is a 2-bit flag (equivalent to Frame_Header_Descriptor right- + shifted 6 bits) specifying whether Frame_Content_Size (the + decompressed data size) is provided within the header. Flag_Value + provides FCS_Field_Size, which is the number of bytes used by + Frame_Content_Size according to the following table: + + +----------------+--------+---+---+---+ + | Flag_Value | 0 | 1 | 2 | 3 | + +----------------+--------+---+---+---+ + | FCS_Field_Size | 0 or 1 | 2 | 4 | 8 | + +----------------+--------+---+---+---+ + + When Flag_Value is 0, FCS_Field_Size depends on Single_Segment_Flag: + If Single_Segment_Flag is set, FCS_Field_Size is 1. Otherwise, + FCS_Field_Size is 0; Frame_Content_Size is not provided. + + + + + + + + + +Collet & Kucherawy Informational [Page 8] + +RFC 8478 application/zstd October 2018 + + +3.1.1.1.1.2. Single_Segment_Flag + + If this flag is set, data must be regenerated within a single + continuous memory segment. + + In this case, Window_Descriptor byte is skipped, but + Frame_Content_Size is necessarily present. As a consequence, the + decoder must allocate a memory segment of size equal or larger than + Frame_Content_Size. + + In order to protect the decoder from unreasonable memory + requirements, a decoder is allowed to reject a compressed frame that + requests a memory size beyond the decoder's authorized range. + + For broader compatibility, decoders are recommended to support memory + sizes of at least 8 MB. This is only a recommendation; each decoder + is free to support higher or lower limits, depending on local + limitations. + +3.1.1.1.1.3. Unused Bit + + A decoder compliant with this specification version shall not + interpret this bit. It might be used in a future version, to signal + a property that is not mandatory to properly decode the frame. An + encoder compliant with this specification must set this bit to zero. + +3.1.1.1.1.4. Reserved Bit + + This bit is reserved for some future feature. Its value must be + zero. A decoder compliant with this specification version must + ensure it is not set. This bit may be used in a future revision, to + signal a feature that must be interpreted to decode the frame + correctly. + +3.1.1.1.1.5. Content_Checksum_Flag + + If this flag is set, a 32-bit Content_Checksum will be present at the + frame's end. See the description of Content_Checksum above. + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 9] + +RFC 8478 application/zstd October 2018 + + +3.1.1.1.1.6. Dictionary_ID_Flag + + This is a 2-bit flag (= Frame_Header_Descriptor & 0x3) indicating + whether a dictionary ID is provided within the header. It also + specifies the size of this field as DID_Field_Size: + + +----------------+---+---+---+---+ + | Flag_Value | 0 | 1 | 2 | 3 | + +----------------+---+---+---+---+ + | DID_Field_Size | 0 | 1 | 2 | 4 | + +----------------+---+---+---+---+ + +3.1.1.1.2. Window Descriptor + + This provides guarantees about the minimum memory buffer required to + decompress a frame. This information is important for decoders to + allocate enough memory. + + The Window_Descriptor byte is optional. When Single_Segment_Flag is + set, Window_Descriptor is not present. In this case, Window_Size is + Frame_Content_Size, which can be any value from 0 to 2^64-1 bytes (16 + ExaBytes). + + +------------+----------+----------+ + | Bit Number | 7-3 | 2-0 | + +------------+----------+----------+ + | Field Name | Exponent | Mantissa | + +------------+----------+----------+ + + The minimum memory buffer size is called Window_Size. It is + described by the following formulae: + + windowLog = 10 + Exponent; + windowBase = 1 << windowLog; + windowAdd = (windowBase / 8) * Mantissa; + Window_Size = windowBase + windowAdd; + + The minimum Window_Size is 1 KB. The maximum Window_Size is (1<<41) + + 7*(1<<38) bytes, which is 3.75 TB. + + In general, larger Window_Size values tend to improve the compression + ratio, but at the cost of increased memory usage. + + To properly decode compressed data, a decoder will need to allocate a + buffer of at least Window_Size bytes. + + + + + + +Collet & Kucherawy Informational [Page 10] + +RFC 8478 application/zstd October 2018 + + + In order to protect decoders from unreasonable memory requirements, a + decoder is allowed to reject a compressed frame that requests a + memory size beyond decoder's authorized range. + + For improved interoperability, it's recommended for decoders to + support values of Window_Size up to 8 MB and for encoders not to + generate frames requiring a Window_Size larger than 8 MB. It's + merely a recommendation though, and decoders are free to support + larger or lower limits, depending on local limitations. + +3.1.1.1.3. Dictionary_ID + + This is a variable size field, which contains the ID of the + dictionary required to properly decode the frame. This field is + optional. When it's not present, it's up to the decoder to know + which dictionary to use. + + Dictionary_ID field size is provided by DID_Field_Size. + DID_Field_Size is directly derived from the value of + Dictionary_ID_Flag. One byte can represent an ID 0-255; 2 bytes can + represent an ID 0-65535; 4 bytes can represent an ID 0-4294967295. + Format is little-endian. + + It is permitted to represent a small ID (for example, 13) with a + large 4-byte dictionary ID, even if it is less efficient. + + Within private environments, any dictionary ID can be used. However, + for frames and dictionaries distributed in public space, + Dictionary_ID must be attributed carefully. The following ranges are + reserved for use only with dictionaries that have been registered + with IANA (see Section 6.3): + + low range: <= 32767 + high range: >= (1 << 31) + + Any other value for Dictionary_ID can be used by private arrangement + between participants. + + Any payload presented for decompression that references an + unregistered reserved dictionary ID results in an error. + + + + + + + + + + + +Collet & Kucherawy Informational [Page 11] + +RFC 8478 application/zstd October 2018 + + +3.1.1.1.4. Frame Content Size + + This is the original (uncompressed) size. This information is + optional. Frame_Content_Size uses a variable number of bytes, + provided by FCS_Field_Size. FCS_Field_Size is provided by the value + of Frame_Content_Size_Flag. FCS_Field_Size can be equal to 0 (not + present), 1, 2, 4, or 8 bytes. + + +----------------+--------------+ + | FCS Field Size | Range | + +----------------+--------------+ + | 0 | unknown | + +----------------+--------------+ + | 1 | 0 - 255 | + +----------------+--------------+ + | 2 | 256 - 65791 | + +----------------+--------------+ + | 4 | 0 - 2^32 - 1 | + +----------------+--------------+ + | 8 | 0 - 2^64 - 1 | + +----------------+--------------+ + + Frame_Content_Size format is little-endian. When FCS_Field_Size is + 1, 4, or 8 bytes, the value is read directly. When FCS_Field_Size is + 2, the offset of 256 is added. It's allowed to represent a small + size (for example 18) using any compatible variant. + +3.1.1.2. Blocks + + After Magic_Number and Frame_Header, there are some number of blocks. + Each frame must have at least 1 block, but there is no upper limit on + the number of blocks per frame. + + The structure of a block is as follows: + + +--------------+---------------+ + | Block_Header | Block_Content | + +--------------+---------------+ + | 3 bytes | n bytes | + +--------------+---------------+ + + + + + + + + + + + +Collet & Kucherawy Informational [Page 12] + +RFC 8478 application/zstd October 2018 + + + Block_Header uses 3 bytes, written using little-endian convention. + It contains three fields: + + +------------+------------+------------+ + | Last_Block | Block_Type | Block_Size | + +------------+------------+------------+ + | bit 0 | bits 1-2 | bits 3-23 | + +------------+------------+------------+ + +3.1.1.2.1. Last_Block + + The lowest bit (Last_Block) signals whether this block is the last + one. The frame will end after this last block. It may be followed + by an optional Content_Checksum (see Section 3.1.1). + +3.1.1.2.2. Block_Type + + The next 2 bits represent the Block_Type. There are four block + types: + + +-----------+------------------+ + | Value | Block_Type | + +-----------+------------------+ + | 0 | Raw_Block | + +-----------+------------------+ + | 1 | RLE_Block | + +-----------+------------------+ + | 2 | Compressed_Block | + +-----------+------------------+ + | 3 | Reserved | + +-----------+------------------+ + + Raw_Block: This is an uncompressed block. Block_Content contains + Block_Size bytes. + + RLE_Block: This is a single byte, repeated Block_Size times. + Block_Content consists of a single byte. On the decompression + side, this byte must be repeated Block_Size times. + + Compressed_Block: This is a compressed block as described in + Section 3.1.1.3. Block_Size is the length of Block_Content, + namely the compressed data. The decompressed size is not known, + but its maximum possible value is guaranteed (see below). + + Reserved: This is not a block. This value cannot be used with the + current specification. If such a value is present, it is + considered to be corrupt data. + + + + +Collet & Kucherawy Informational [Page 13] + +RFC 8478 application/zstd October 2018 + + +3.1.1.2.3. Block_Size + + The upper 21 bits of Block_Header represent the Block_Size. + Block_Size is the size of the block excluding the header. A block + can contain any number of bytes (even zero), up to + Block_Maximum_Decompressed_Size, which is the smallest of: + + o Window_Size + + o 128 KB + + A Compressed_Block has the extra restriction that Block_Size is + always strictly less than the decompressed size. If this condition + cannot be respected, the block must be sent uncompressed instead + (i.e., treated as a Raw_Block). + +3.1.1.3. Compressed Blocks + + To decompress a compressed block, the compressed size must be + provided from the Block_Size field within Block_Header. + + A compressed block consists of two sections: a Literals + Section (Section 3.1.1.3.1) and a + Sequences_Section (Section 3.1.1.3.2). The results of the two + sections are then combined to produce the decompressed data in + Sequence Execution (Section 3.1.1.4). + + To decode a compressed block, the following elements are necessary: + + o Previous decoded data, up to a distance of Window_Size, or the + beginning of the Frame, whichever is smaller. Single_Segment_Flag + will be set in the latter case. + + o List of "recent offsets" from the previous Compressed_Block. + + o The previous Huffman tree, required by Treeless_Literals_Block + type. + + o Previous Finite State Entropy (FSE) decoding tables, required by + Repeat_Mode, for each symbol type (literals lengths, match + lengths, offsets). + + Note that decoding tables are not always from the previous + Compressed_Block: + + o Every decoding table can come from a dictionary. + + + + + +Collet & Kucherawy Informational [Page 14] + +RFC 8478 application/zstd October 2018 + + + o The Huffman tree comes from the previous + Compressed_Literals_Block. + +3.1.1.3.1. Literals_Section_Header + + All literals are regrouped in the first part of the block. They can + be decoded first and then copied during Sequence Execution (see + Section 3.1.1.4), or they can be decoded on the flow during Sequence + Execution. + + Literals can be stored uncompressed or compressed using Huffman + prefix codes. When compressed, an optional tree description can be + present, followed by 1 or 4 streams. + + +----------------------------+ + | Literals_Section_Header | + +----------------------------+ + | [Huffman_Tree_Description] | + +----------------------------+ + | [Jump_Table] | + +----------------------------+ + | Stream_1 | + +----------------------------+ + | [Stream_2] | + +----------------------------+ + | [Stream_3] | + +----------------------------+ + | [Stream_4] | + +----------------------------+ + +3.1.1.3.1.1. Literals_Section_Header + + This field describes how literals are packed. It's a byte-aligned + variable-size bit field, ranging from 1 to 5 bytes, using little- + endian convention. + + +---------------------+-----------+ + | Literals_Block_Type | 2 bits | + +---------------------+-----------+ + | Size_Format | 1-2 bits | + +---------------------+-----------+ + | Regenerated_Size | 5-20 bits | + +---------------------+-----------+ + | [Compressed_Size] | 0-18 bits | + +---------------------+-----------+ + + In this representation, bits at the top are the lowest bits. + + + + +Collet & Kucherawy Informational [Page 15] + +RFC 8478 application/zstd October 2018 + + + The Literals_Block_Type field uses the two lowest bits of the first + byte, describing four different block types: + + +---------------------------+-------+ + | Literals_Block_Type | Value | + +---------------------------+-------+ + | Raw_Literals_Block | 0 | + +---------------------------+-------+ + | RLE_Literals_Block | 1 | + +---------------------------+-------+ + | Compressed_Literals_Block | 2 | + +---------------------------+-------+ + | Treeless_Literals_Block | 3 | + +---------------------------+-------+ + + Raw_Literals_Block: Literals are stored uncompressed. + Literals_Section_Content is Regenerated_Size. + + RLE_Literals_Block: Literals consist of a single-byte value repeated + Regenerated_Size times. Literals_Section_Content is 1. + + Compressed_Literals_Block: This is a standard Huffman-compressed + block, starting with a Huffman tree description. See details + below. Literals_Section_Content is Compressed_Size. + + Treeless_Literals_Block: This is a Huffman-compressed block, using + the Huffman tree from the previous Compressed_Literals_Block, or a + dictionary if there is no previous Huffman-compressed literals + block. Huffman_Tree_Description will be skipped. Note that if + this mode is triggered without any previous Huffman-table in the + frame (or dictionary, per Section 5), it should be treated as data + corruption. Literals_Section_Content is Compressed_Size. + + The Size_Format is divided into two families: + + o For Raw_Literals_Block and RLE_Literals_Block, it's only necessary + to decode Regenerated_Size. There is no Compressed_Size field. + + o For Compressed_Block and Treeless_Literals_Block, it's required to + decode both Compressed_Size and Regenerated_Size (the decompressed + size). It's also necessary to decode the number of streams (1 or + 4). + + For values spanning several bytes, the convention is little endian. + + Size_Format for Raw_Literals_Block and RLE_Literals_Block uses 1 or 2 + bits. Its value is (Literals_Section_Header[0]>>2) & 0x3. + + + + +Collet & Kucherawy Informational [Page 16] + +RFC 8478 application/zstd October 2018 + + + Size_Format == 00 or 10: Size_Format uses 1 bit. Regenerated_Size + uses 5 bits (value 0-31). Literals_Section_Header uses 1 byte. + Regenerated_Size = Literal_Section_Header[0]>>3. + + Size_Format == 01: Size_Format uses 2 bits. Regenerated_Size uses + 12 bits (values 0-4095). Literals_Section_Header uses 2 bytes. + Regenerated_Size = (Literals_Section_Header[0]>>4) + + (Literals_Section_Header[1]<<4). + + Size_Format == 11: Size_Format uses 2 bits. Regenerated_Size uses + 20 bits (values 0-1048575). Literals_Section_Header uses 3 bytes. + Regenerated_Size = (Literals_Section_Header[0]>>4) + + (Literals_Section_Header[1]<<4) + (Literals_Section_Header[2]<<12) + + Only Stream_1 is present for these cases. Note that it is permitted + to represent a short value (for example, 13) using a long format, + even if it's less efficient. + + Size_Format for Compressed_Literals_Block and Treeless_Literals_Block + always uses 2 bits. + + Size_Format == 00: A single stream. Both Regenerated_Size and + Compressed_Size use 10 bits (values 0-1023). + Literals_Section_Header uses 3 bytes. + + Size_Format == 01: 4 streams. Both Regenerated_Size and + Compressed_Size use 10 bits (values 0-1023). + Literals_Section_Header uses 3 bytes. + + Size_Format == 10: 4 streams. Both Regenerated_Size and + Compressed_Size use 14 bits (values 0-16383). + Literals_Section_Header uses 4 bytes. + + Size_Format == 11: 4 streams. Both Regenerated_Size and + Compressed_Size use 18 bits (values 0-262143). + Literals_Section_Header uses 5 bytes. + + Both the Compressed_Size and Regenerated_Size fields follow little- + endian convention. Note that Compressed_Size includes the size of + the Huffman_Tree_Description when it is present. + +3.1.1.3.1.2. Raw_Literals_Block + + The data in Stream_1 is Regenerated_Size bytes long. It contains the + raw literals data to be used during Sequence Execution + (Section 3.1.1.3.2). + + + + + +Collet & Kucherawy Informational [Page 17] + +RFC 8478 application/zstd October 2018 + + +3.1.1.3.1.3. RLE_Literals_Block + + Stream_1 consists of a single byte that should be repeated + Regenerated_Size times to generate the decoded literals. + +3.1.1.3.1.4. Compressed_Literals_Block and Treeless_Literals_Block + + Both of these modes contain Huffman-encoded data. For + Treeless_Literals_Block, the Huffman table comes from the previously + compressed literals block, or from a dictionary; see Section 5. + +3.1.1.3.1.5. Huffman_Tree_Description + + This section is only present when the Literals_Block_Type type is + Compressed_Literals_Block (2). The format of + Huffman_Tree_Description can be found in Section 4.2.1. The size of + Huffman_Tree_Description is determined during the decoding process. + It must be used to determine where streams begin. + + Total_Streams_Size = Compressed_Size + - Huffman_Tree_Description_Size + +3.1.1.3.1.6. Jump_Table + + The Jump_Table is only present when there are 4 Huffman-coded + streams. + + (Reminder: Huffman-compressed data consists of either 1 or 4 Huffman- + coded streams.) + + If only 1 stream is present, it is a single bitstream occupying the + entire remaining portion of the literals block, encoded as described + within Section 4.2.2. + + If there are 4 streams, Literals_Section_Header only provides enough + information to know the decompressed and compressed sizes of all 4 + streams combined. The decompressed size of each stream is equal to + (Regenerated_Size+3)/4, except for the last stream, which may be up + to 3 bytes smaller, to reach a total decompressed size as specified + in Regenerated_Size. + + The compressed size of each stream is provided explicitly in the + Jump_Table. The Jump_Table is 6 bytes long and consists of three + 2-byte little-endian fields, describing the compressed sizes of the + first 3 streams. Stream4_Size is computed from Total_Streams_Size + minus sizes of other streams. + + + + + +Collet & Kucherawy Informational [Page 18] + +RFC 8478 application/zstd October 2018 + + + Stream4_Size = Total_Streams_Size - 6 + - Stream1_Size - Stream2_Size + - Stream3_Size + + Note that if Stream1_Size + Stream2_Size + Stream3_Size exceeds + Total_Streams_Size, the data are considered corrupted. + + Each of these 4 bitstreams is then decoded independently as a + Huffman-Coded stream, as described in Section 4.2.2. + +3.1.1.3.2. Sequences_Section + + A compressed block is a succession of sequences. A sequence is a + literal copy command, followed by a match copy command. A literal + copy command specifies a length. It is the number of bytes to be + copied (or extracted) from the Literals Section. A match copy + command specifies an offset and a length. + + When all sequences are decoded, if there are literals left in the + literals section, these bytes are added at the end of the block. + + This is described in more detail in Section 3.1.1.4. + + The Sequences_Section regroups all symbols required to decode + commands. There are three symbol types: literals lengths, offsets, + and match lengths. They are encoded together, interleaved, in a + single "bitstream". + + The Sequences_Section starts by a header, followed by optional + probability tables for each symbol type, followed by the bitstream. + + Sequences_Section_Header + [Literals_Length_Table] + [Offset_Table] + [Match_Length_Table] + bitStream + + To decode the Sequences_Section, it's necessary to know its size. + This size is deduced from the size of the Literals_Section: + Sequences_Section_Size = Block_Size - Literals_Section_Header - + Literals_Section_Content + + + + + + + + + + +Collet & Kucherawy Informational [Page 19] + +RFC 8478 application/zstd October 2018 + + +3.1.1.3.2.1. Sequences_Section_Header + + This header consists of two items: + + o Number_of_Sequences + + o Symbol_Compression_Modes + + Number_of_Sequences is a variable size field using between 1 and 3 + bytes. If the first byte is "byte0": + + o if (byte0 == 0): there are no sequences. The sequence section + stops here. Decompressed content is defined entirely as Literals + Section content. The FSE tables used in Repeat_Mode are not + updated. + + o if (byte0 < 128): Number_of_Sequences = byte0. Uses 1 byte. + + o if (byte0 < 255): Number_of_Sequences = ((byte0 - 128) << 8) + + byte1. Uses 2 bytes. + + o if (byte0 == 255): Number_of_Sequences = byte1 + (byte2 << 8) + + 0x7F00. Uses 3 bytes. + + Symbol_Compression_Modes is a single byte, defining the compression + mode of each symbol type. + + +-------------+----------------------+ + | Bit Number | Field Name | + +-------------+----------------------+ + | 7-6 | Literal_Lengths_Mode | + +-------------+----------------------+ + | 5-4 | Offsets_Mode | + +-------------+----------------------+ + | 3-2 | Match_Lengths_Mode | + +-------------+----------------------+ + | 1-0 | Reserved | + +-------------+----------------------+ + + The last field, Reserved, must be all zeroes. + + + + + + + + + + + +Collet & Kucherawy Informational [Page 20] + +RFC 8478 application/zstd October 2018 + + + Literals_Lengths_Mode, Offsets_Mode, and Match_Lengths_Mode define + the Compression_Mode of literals lengths, offsets, and match lengths + symbols, respectively. They follow the same enumeration: + + +-------+---------------------+ + | Value | Compression_Mode | + +-------+---------------------+ + | 0 | Predefined_Mode | + +-------+---------------------+ + | 1 | RLE_Mode | + +-------+---------------------+ + | 2 | FSE_Compressed_Mode | + +-------+---------------------+ + | 3 | Repeat_Mode | + +-------+---------------------+ + + Predefined_Mode: A predefined FSE (see Section 4.1) distribution + table is used, as defined in Section 3.1.1.3.2.2. No distribution + table will be present. + + RLE_Mode: The table description consists of a single byte, which + contains the symbol's value. This symbol will be used for all + sequences. + + FSE_Compressed_Mode: Standard FSE compression. A distribution table + will be present. The format of this distribution table is + described in Section 4.1.1. Note that the maximum allowed + accuracy log for literals length and match length tables is 9, and + the maximum accuracy log for the offsets table is 8. This mode + must not be used when only one symbol is present; RLE_Mode should + be used instead (although any other mode will work). + + Repeat_Mode: The table used in the previous Compressed_Block with + Number_Of_Sequences > 0 will be used again, or if this is the + first block, the table in the dictionary will be used. Note that + this includes RLE_Mode, so if Repeat_Mode follows RLE_Mode, the + same symbol will be repeated. It also includes Predefined_Mode, + in which case Repeat_Mode will have the same outcome as + Predefined_Mode. No distribution table will be present. If this + mode is used without any previous sequence table in the frame (or + dictionary; see Section 5) to repeat, this should be treated as + corruption. + + + + + + + + + +Collet & Kucherawy Informational [Page 21] + +RFC 8478 application/zstd October 2018 + + +3.1.1.3.2.1.1. Sequence Codes for Lengths and Offsets + + Each symbol is a code in its own context, which specifies Baseline + and Number_of_Bits to add. Codes are FSE compressed and interleaved + with raw additional bits in the same bitstream. + + Literals length codes are values ranging from 0 to 35 inclusive. + They define lengths from 0 to 131071 bytes. The literals length is + equal to the decoded Baseline plus the result of reading + Number_of_Bits bits from the bitstream, as a little-endian value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 22] + +RFC 8478 application/zstd October 2018 + + + +----------------------+----------+----------------+ + | Literals_Length_Code | Baseline | Number_of_Bits | + +----------------------+----------+----------------+ + | 0-15 | length | 0 | + +----------------------+----------+----------------+ + | 16 | 16 | 1 | + +----------------------+----------+----------------+ + | 17 | 18 | 1 | + +----------------------+----------+----------------+ + | 18 | 20 | 1 | + +----------------------+----------+----------------+ + | 19 | 22 | 1 | + +----------------------+----------+----------------+ + | 20 | 24 | 2 | + +----------------------+----------+----------------+ + | 21 | 28 | 2 | + +----------------------+----------+----------------+ + | 22 | 32 | 3 | + +----------------------+----------+----------------+ + | 23 | 40 | 3 | + +----------------------+----------+----------------+ + | 24 | 48 | 4 | + +----------------------+----------+----------------+ + | 25 | 64 | 6 | + +----------------------+----------+----------------+ + | 26 | 128 | 7 | + +----------------------+----------+----------------+ + | 27 | 256 | 8 | + +----------------------+----------+----------------+ + | 28 | 512 | 9 | + +----------------------+----------+----------------+ + | 29 | 1024 | 10 | + +----------------------+----------+----------------+ + | 30 | 2048 | 11 | + +----------------------+----------+----------------+ + | 31 | 4096 | 12 | + +----------------------+----------+----------------+ + | 32 | 8192 | 13 | + +----------------------+----------+----------------+ + | 33 | 16384 | 14 | + +----------------------+----------+----------------+ + | 34 | 32768 | 15 | + +----------------------+----------+----------------+ + | 35 | 65536 | 16 | + +----------------------+----------+----------------+ + + + + + + +Collet & Kucherawy Informational [Page 23] + +RFC 8478 application/zstd October 2018 + + + Match length codes are values ranging from 0 to 52 inclusive. They + define lengths from 3 to 131074 bytes. The match length is equal to + the decoded Baseline plus the result of reading Number_of_Bits bits + from the bitstream, as a little-endian value. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 24] + +RFC 8478 application/zstd October 2018 + + + +-------------------+-----------------------+----------------+ + | Match_Length_Code | Baseline | Number_of_Bits | + +-------------------+-----------------------+----------------+ + | 0-31 | Match_Length_Code + 3 | 0 | + +-------------------+-----------------------+----------------+ + | 32 | 35 | 1 | + +-------------------+-----------------------+----------------+ + | 33 | 37 | 1 | + +-------------------+-----------------------+----------------+ + | 34 | 39 | 1 | + +-------------------+-----------------------+----------------+ + | 35 | 41 | 1 | + +-------------------+-----------------------+----------------+ + | 36 | 43 | 2 | + +-------------------+-----------------------+----------------+ + | 37 | 47 | 2 | + +-------------------+-----------------------+----------------+ + | 38 | 51 | 3 | + +-------------------+-----------------------+----------------+ + | 39 | 59 | 3 | + +-------------------+-----------------------+----------------+ + | 40 | 67 | 4 | + +-------------------+-----------------------+----------------+ + | 41 | 83 | 4 | + +-------------------+-----------------------+----------------+ + | 42 | 99 | 5 | + +-------------------+-----------------------+----------------+ + | 43 | 131 | 7 | + +-------------------+-----------------------+----------------+ + | 44 | 259 | 8 | + +-------------------+-----------------------+----------------+ + | 45 | 515 | 9 | + +-------------------+-----------------------+----------------+ + | 46 | 1027 | 10 | + +-------------------+-----------------------+----------------+ + | 47 | 2051 | 11 | + +-------------------+-----------------------+----------------+ + | 48 | 4099 | 12 | + +-------------------+-----------------------+----------------+ + | 49 | 8195 | 13 | + +-------------------+-----------------------+----------------+ + | 50 | 16387 | 14 | + +-------------------+-----------------------+----------------+ + | 51 | 32771 | 15 | + +-------------------+-----------------------+----------------+ + | 52 | 65539 | 16 | + +-------------------+-----------------------+----------------+ + + + + +Collet & Kucherawy Informational [Page 25] + +RFC 8478 application/zstd October 2018 + + + Offset codes are values ranging from 0 to N. + + A decoder is free to limit its maximum supported value for N. + Support for values of at least 22 is recommended. At the time of + this writing, the reference decoder supports a maximum N value of 31. + + An offset code is also the number of additional bits to read in + little-endian fashion and can be translated into an Offset_Value + using the following formulas: + + Offset_Value = (1 << offsetCode) + readNBits(offsetCode); + if (Offset_Value > 3) Offset = Offset_Value - 3; + + This means that maximum Offset_Value is (2^(N+1))-1, supporting back- + reference distance up to (2^(N+1))-4, but it is limited by the + maximum back-reference distance (see Section 3.1.1.1.2). + + Offset_Value from 1 to 3 are special: they define "repeat codes". + This is described in more detail in Section 3.1.1.5. + +3.1.1.3.2.1.2. Decoding Sequences + + FSE bitstreams are read in reverse of the direction they are written. + In zstd, the compressor writes bits forward into a block, and the + decompressor must read the bitstream backwards. + + To find the start of the bitstream, it is therefore necessary to know + the offset of the last byte of the block, which can be found by + counting Block_Size bytes after the block header. + + After writing the last bit containing information, the compressor + writes a single 1 bit and then fills the byte with 0-7 zero bits of + padding. The last byte of the compressed bitstream cannot be zero + for that reason. + + When decompressing, the last byte containing the padding is the first + byte to read. The decompressor needs to skip 0-7 initial zero bits + until the first 1 bit occurs. Afterwards, the useful part of the + bitstream begins. + + FSE decoding requires a 'state' to be carried from symbol to symbol. + For more explanation on FSE decoding, see Section 4.1. + + For sequence decoding, a separate state keeps track of each literal + lengths, offsets, and match lengths symbols. Some FSE primitives are + also used. For more details on the operation of these primitives, + see Section 4.1. + + + + +Collet & Kucherawy Informational [Page 26] + +RFC 8478 application/zstd October 2018 + + + The bitstream starts with initial FSE state values, each using the + required number of bits in their respective accuracy, decoded + previously from their normalized distribution. It starts with + Literals_Length_State, followed by Offset_State, and finally + Match_Length_State. + + Note that all values are read backward, so the 'start' of the + bitstream is at the highest position in memory, immediately before + the last 1 bit for padding. + + After decoding the starting states, a single sequence is decoded + Number_Of_Sequences times. These sequences are decoded in order from + first to last. Since the compressor writes the bitstream in the + forward direction, this means the compressor must encode the + sequences starting with the last one and ending with the first. + + For each of the symbol types, the FSE state can be used to determine + the appropriate code. The code then defines the Baseline and + Number_of_Bits to read for each type. The description of the codes + for how to determine these values can be found in + Section 3.1.1.3.2.1. + + Decoding starts by reading the Number_of_Bits required to decode + offset. It does the same for Match_Length and then for + Literals_Length. This sequence is then used for Sequence Execution + (see Section 3.1.1.4). + + If it is not the last sequence in the block, the next operation is to + update states. Using the rules pre-calculated in the decoding + tables, Literals_Length_State is updated, followed by + Match_Length_State, and then Offset_State. See Section 4.1 for + details on how to update states from the bitstream. + + This operation will be repeated Number_of_Sequences times. At the + end, the bitstream shall be entirely consumed; otherwise, the + bitstream is considered corrupted. + +3.1.1.3.2.2. Default Distributions + + If Predefined_Mode is selected for a symbol type, its FSE decoding + table is generated from a predefined distribution table defined here. + For details on how to convert this distribution into a decoding + table, see Section 4.1. + + + + + + + + +Collet & Kucherawy Informational [Page 27] + +RFC 8478 application/zstd October 2018 + + +3.1.1.3.2.2.1. Literals Length + + The decoding table uses an accuracy log of 6 bits (64 states). + + short literalsLength_defaultDistribution[36] = + { 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, + -1,-1,-1,-1 + }; + +3.1.1.3.2.2.2. Match Length + + The decoding table uses an accuracy log of 6 bits (64 states). + + short matchLengths_defaultDistribution[53] = + { 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-1,-1, + -1,-1,-1,-1,-1 + }; + +3.1.1.3.2.2.3. Offset Codes + + The decoding table uses an accuracy log of 5 bits (32 states), and + supports a maximum N value of 28, allowing offset values up to + 536,870,908. + + If any sequence in the compressed block requires a larger offset than + this, it's not possible to use the default distribution to represent + it. + + short offsetCodes_defaultDistribution[29] = + { 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1,-1,-1,-1,-1,-1 + }; + +3.1.1.4. Sequence Execution + + Once literals and sequences have been decoded, they are combined to + produce the decoded content of a block. + + Each sequence consists of a tuple of (literals_length, offset_value, + match_length), decoded as described in the + Sequences_Section (Section 3.1.1.3.2). To execute a sequence, first + copy literals_length bytes from the decoded literals to the output. + + + + + + +Collet & Kucherawy Informational [Page 28] + +RFC 8478 application/zstd October 2018 + + + Then, match_length bytes are copied from previous decoded data. The + offset to copy from is determined by offset_value: + + o if Offset_Value > 3, then the offset is Offset_Value - 3; + + o if Offset_Value is from 1-3, the offset is a special repeat offset + value. See Section 3.1.1.5 for how the offset is determined in + this case. + + The offset is defined as from the current position (after copying the + literals), so an offset of 6 and a match length of 3 means that 3 + bytes should be copied from 6 bytes back. Note that all offsets + leading to previously decoded data must be smaller than Window_Size + defined in Frame_Header_Descriptor (Section 3.1.1.1.1). + +3.1.1.5. Repeat Offsets + + As seen above, the first three values define a repeated offset; we + will call them Repeated_Offset1, Repeated_Offset2, and + Repeated_Offset3. They are sorted in recency order, with + Repeated_Offset1 meaning "most recent one". + + If offset_value is 1, then the offset used is Repeated_Offset1, etc. + + There is one exception: When the current sequence's literals_length + is 0, repeated offsets are shifted by 1, so an offset_value of 1 + means Repeated_Offset2, an offset_value of 2 means Repeated_Offset3, + and an offset_value of 3 means Repeated_Offset1 - 1_byte. + + For the first block, the starting offset history is populated with + the following values: Repeated_Offset1 (1), Repeated_Offset2 (4), and + Repeated_Offset3 (8), unless a dictionary is used, in which case they + come from the dictionary. + + Then each block gets its starting offset history from the ending + values of the most recent Compressed_Block. Note that blocks that + are not Compressed_Block are skipped; they do not contribute to + offset history. + + The newest offset takes the lead in offset history, shifting others + back (up to its previous place if it was already present). This + means that when Repeated_Offset1 (most recent) is used, history is + unmodified. When Repeated_Offset2 is used, it is swapped with + Repeated_Offset1. If any other offset is used, it becomes + Repeated_Offset1, and the rest are shifted back by 1. + + + + + + +Collet & Kucherawy Informational [Page 29] + +RFC 8478 application/zstd October 2018 + + +3.1.2. Skippable Frames + + +--------------+------------+-----------+ + | Magic_Number | Frame_Size | User_Data | + +--------------+------------+-----------+ + | 4 bytes | 4 bytes | n bytes | + +--------------+------------+-----------+ + + Skippable frames allow the insertion of user-defined metadata into a + flow of concatenated frames. + + Skippable frames defined in this specification are compatible with + skippable frames in [LZ4]. + + From a compliant decoder perspective, skippable frames simply need to + be skipped, and their content ignored, resuming decoding after the + skippable frame. + + It should be noted that a skippable frame can be used to watermark a + stream of concatenated frames embedding any kind of tracking + information (even just a Universally Unique Identifier (UUID)). + Users wary of such possibility should scan the stream of concatenated + frames in an attempt to detect such frames for analysis or removal. + + The fields are: + + Magic_Number: 4 bytes, little-endian format. Value: 0x184D2A5?, + which means any value from 0x184D2A50 to 0x184D2A5F. All 16 + values are valid to identify a skippable frame. This + specification does not detail any specific tagging methods for + skippable frames. + + Frame_Size: This is the size, in bytes, of the following User_Data + (without including the magic number nor the size field itself). + This field is represented using 4 bytes, little-endian format, + unsigned 32 bits. This means User_Data can't be bigger than + (2^32-1) bytes. + + User_Data: This field can be anything. Data will just be skipped by + the decoder. + +4. Entropy Encoding + + Two types of entropy encoding are used by the Zstandard format: FSE + and Huffman coding. Huffman is used to compress literals, while FSE + is used for all other symbols (Literals_Length_Code, + Match_Length_Code, and offset codes) and to compress Huffman headers. + + + + +Collet & Kucherawy Informational [Page 30] + +RFC 8478 application/zstd October 2018 + + +4.1. FSE + + FSE, short for Finite State Entropy, is an entropy codec based on + [ANS]. FSE encoding/decoding involves a state that is carried over + between symbols, so decoding must be done in the opposite direction + as encoding. Therefore, all FSE bitstreams are read from end to + beginning. Note that the order of the bits in the stream is not + reversed; they are simply read in the reverse order from which they + were written. + + For additional details on FSE, see Finite State Entropy [FSE]. + + FSE decoding involves a decoding table that has a power of 2 size and + contains three elements: Symbol, Num_Bits, and Baseline. The base 2 + logarithm of the table size is its Accuracy_Log. An FSE state value + represents an index in this table. + + To obtain the initial state value, consume Accuracy_Log bits from the + stream as a little-endian value. The next symbol in the stream is + the Symbol indicated in the table for that state. To obtain the next + state value, the decoder should consume Num_Bits bits from the stream + as a little-endian value and add it to Baseline. + +4.1.1. FSE Table Description + + To decode FSE streams, it is necessary to construct the decoding + table. The Zstandard format encodes FSE table descriptions as + described here. + + An FSE distribution table describes the probabilities of all symbols + from 0 to the last present one (included) on a normalized scale of + (1 << Accuracy_Log). Note that there must be two or more symbols + with non-zero probability. + + A bitstream is read forward, in little-endian fashion. It is not + necessary to know its exact size, since the size will be discovered + and reported by the decoding process. The bitstream starts by + reporting on which scale it operates. If low4bits designates the + lowest 4 bits of the first byte, then Accuracy_Log = low4bits + 5. + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 31] + +RFC 8478 application/zstd October 2018 + + + This is followed by each symbol value, from 0 to the last present + one. The number of bits used by each field is variable and depends + on: + + Remaining probabilities + 1: For example, presuming an Accuracy_Log + of 8, and presuming 100 probabilities points have already been + distributed, the decoder may read any value from 0 to + (256 - 100 + 1) == 157, inclusive. Therefore, it must read + log2sup(157) == 8 bits. + + Value decoded: Small values use 1 fewer bit. For example, presuming + values from 0 to 157 (inclusive) are possible, 255 - 157 = 98 + values are remaining in an 8-bit field. The first 98 values + (hence from 0 to 97) use only 7 bits, and values from 98 to 157 + use 8 bits. This is achieved through this scheme: + + +------------+---------------+-----------+ + | Value Read | Value Decoded | Bits Used | + +------------+---------------+-----------+ + | 0 - 97 | 0 - 97 | 7 | + +------------+---------------+-----------+ + | 98 - 127 | 98 - 127 | 8 | + +------------+---------------+-----------+ + | 128 - 225 | 0 - 97 | 7 | + +------------+---------------+-----------+ + | 226 - 255 | 128 - 157 | 8 | + +------------+---------------+-----------+ + + Symbol probabilities are read one by one, in order. The probability + is obtained from Value decoded using the formula P = Value - 1. This + means the value 0 becomes the negative probability -1. This is a + special probability that means "less than 1". Its effect on the + distribution table is described below. For the purpose of + calculating total allocated probability points, it counts as 1. + + When a symbol has a probability of zero, it is followed by a 2-bit + repeat flag. This repeat flag tells how many probabilities of zeroes + follow the current one. It provides a number ranging from 0 to 3. + If it is a 3, another 2-bit repeat flag follows, and so on. + + When the last symbol reaches a cumulated total of + (1 << Accuracy_Log), decoding is complete. If the last symbol makes + the cumulated total go above (1 << Accuracy_Log), distribution is + considered corrupted. + + + + + + + +Collet & Kucherawy Informational [Page 32] + +RFC 8478 application/zstd October 2018 + + + Finally, the decoder can tell how many bytes were used in this + process and how many symbols are present. The bitstream consumes a + round number of bytes. Any remaining bit within the last byte is + simply unused. + + The distribution of normalized probabilities is enough to create a + unique decoding table. The table has a size of (1 << Accuracy_Log). + Each cell describes the symbol decoded and instructions to get the + next state. + + Symbols are scanned in their natural order for "less than 1" + probabilities as described above. Symbols with this probability are + being attributed a single cell, starting from the end of the table + and retreating. These symbols define a full state reset, reading + Accuracy_Log bits. + + All remaining symbols are allocated in their natural order. Starting + from symbol 0 and table position 0, each symbol gets allocated as + many cells as its probability. Cell allocation is spread, not + linear; each successor position follows this rule: + + position += (tableSize >> 1) + (tableSize >> 3) + 3; + position &= tableSize - 1; + + A position is skipped if it is already occupied by a "less than 1" + probability symbol. Position does not reset between symbols; it + simply iterates through each position in the table, switching to the + next symbol when enough states have been allocated to the current + one. + + The result is a list of state values. Each state will decode the + current symbol. + + To get the Number_of_Bits and Baseline required for the next state, + it is first necessary to sort all states in their natural order. The + lower states will need 1 more bit than higher ones. The process is + repeated for each symbol. + + For example, presuming a symbol has a probability of 5, it receives + five state values. States are sorted in natural order. The next + power of 2 is 8. The space of probabilities is divided into 8 equal + parts. Presuming the Accuracy_Log is 7, this defines 128 states, and + each share (divided by 8) is 16 in size. In order to reach 8, 8 - 5 + = 3 lowest states will count "double", doubling the number of shares + (32 in width), requiring 1 more bit in the process. + + + + + + +Collet & Kucherawy Informational [Page 33] + +RFC 8478 application/zstd October 2018 + + + Baseline is assigned starting from the higher states using fewer + bits, and proceeding naturally, then resuming at the first state, + each taking its allocated width from Baseline. + + +----------------+-------+-------+--------+------+-------+ + | state order | 0 | 1 | 2 | 3 | 4 | + +----------------+-------+-------+--------+------+-------+ + | width | 32 | 32 | 32 | 16 | 16 | + +----------------+-------+-------+--------+------+-------+ + | Number_of_Bits | 5 | 5 | 5 | 4 | 4 | + +----------------+-------+-------+--------+------+-------+ + | range number | 2 | 4 | 6 | 0 | 1 | + +----------------+-------+-------+--------+------+-------+ + | Baseline | 32 | 64 | 96 | 0 | 16 | + +----------------+-------+-------+--------+------+-------+ + | range | 32-63 | 64-95 | 96-127 | 0-15 | 16-31 | + +----------------+-------+-------+--------+------+-------+ + + The next state is determined from the current state by reading the + required Number_of_Bits and adding the specified Baseline. + + See Appendix A for the results of this process that are applied to + the default distributions. + +4.2. Huffman Coding + + Zstandard Huffman-coded streams are read backwards, similar to the + FSE bitstreams. Therefore, to find the start of the bitstream, it is + necessary to know the offset of the last byte of the Huffman-coded + stream. + + After writing the last bit containing information, the compressor + writes a single 1 bit and then fills the byte with 0-7 0 bits of + padding. The last byte of the compressed bitstream cannot be 0 for + that reason. + + When decompressing, the last byte containing the padding is the first + byte to read. The decompressor needs to skip 0-7 initial 0 bits and + the first 1 bit that occurs. Afterwards, the useful part of the + bitstream begins. + + The bitstream contains Huffman-coded symbols in little-endian order, + with the codes defined by the method below. + + + + + + + + +Collet & Kucherawy Informational [Page 34] + +RFC 8478 application/zstd October 2018 + + +4.2.1. Huffman Tree Description + + Prefix coding represents symbols from an a priori known alphabet by + bit sequences (codewords), one codeword for each symbol, in a manner + such that different symbols may be represented by bit sequences of + different lengths, but a parser can always parse an encoded string + unambiguously symbol by symbol. + + Given an alphabet with known symbol frequencies, the Huffman + algorithm allows the construction of an optimal prefix code using the + fewest bits of any possible prefix codes for that alphabet. + + The prefix code must not exceed a maximum code length. More bits + improve accuracy but yield a larger header size and require more + memory or more complex decoding operations. This specification + limits the maximum code length to 11 bits. + + All literal values from zero (included) to the last present one + (excluded) are represented by Weight with values from 0 to + Max_Number_of_Bits. Transformation from Weight to Number_of_Bits + follows this pseudocode: + + if Weight == 0 + Number_of_Bits = 0 + else + Number_of_Bits = Max_Number_of_Bits + 1 - Weight + + The last symbol's Weight is deduced from previously decoded ones, by + completing to the nearest power of 2. This power of 2 gives + Max_Number_of_Bits the depth of the current tree. + + For example, presume the following Huffman tree must be described: + + +---------------+----------------+ + | Literal Value | Number_of_Bits | + +---------------+----------------+ + | 0 | 1 | + +---------------+----------------+ + | 1 | 2 | + +---------------+----------------+ + | 2 | 3 | + +---------------+----------------+ + | 3 | 0 | + +---------------+----------------+ + | 4 | 4 | + +---------------+----------------+ + | 5 | 4 | + +---------------+----------------+ + + + +Collet & Kucherawy Informational [Page 35] + +RFC 8478 application/zstd October 2018 + + + The tree depth is 4, since its longest element uses 4 bits. (The + longest elements are those with the smallest frequencies.) Value 5 + will not be listed as it can be determined from the values for 0-4, + nor will values above 5 as they are all 0. Values from 0 to 4 will + be listed using Weight instead of Number_of_Bits. The pseudocode to + determine Weight is: + + if Number_of_Bits == 0 + Weight = 0 + else + Weight = Max_Number_of_Bits + 1 - Number_of_Bits + + It gives the following series of weights: + + +---------------+--------+ + | Literal Value | Weight | + +---------------+--------+ + | 0 | 4 | + +---------------+--------+ + | 1 | 3 | + +---------------+--------+ + | 2 | 2 | + +---------------+--------+ + | 3 | 0 | + +---------------+--------+ + | 4 | 1 | + +---------------+--------+ + + The decoder will do the inverse operation: having collected weights + of literals from 0 to 4, it knows the last literal, 5, is present + with a non-zero Weight. The Weight of 5 can be determined by + advancing to the next power of 2. The sum of 2^(Weight-1) (excluding + 0's) is 15. The nearest power of 2 is 16. Therefore, + Max_Number_of_Bits = 4 and Weight[5] = 16 - 15 = 1. + +4.2.1.1. Huffman Tree Header + + This is a single byte value (0-255), which describes how the series + of weights is encoded. + + headerByte < 128: The series of weights is compressed using FSE (see + below). The length of the FSE-compressed series is equal to + headerByte (0-127). + + + + + + + + +Collet & Kucherawy Informational [Page 36] + +RFC 8478 application/zstd October 2018 + + + headerByte >= 128: This is a direct representation, where each + Weight is written directly as a 4-bit field (0-15). They are + encoded forward, 2 weights to a byte with the first weight taking + the top 4 bits and the second taking the bottom 4; for example, + the following operations could be used to read the weights: + + Weight[0] = (Byte[0] >> 4) + Weight[1] = (Byte[0] & 0xf), + etc. + + The full representation occupies ceiling(Number_of_Symbols/2) + bytes, meaning it uses only full bytes even if Number_of_Symbols + is odd. Number_of_Symbols = headerByte - 127. Note that maximum + Number_of_Symbols is 255 - 127 = 128. If any literal has a value + over 128, raw header mode is not possible, and it is necessary to + use FSE compression. + +4.2.1.2. FSE Compression of Huffman Weights + + In this case, the series of Huffman weights is compressed using FSE + compression. It is a single bitstream with two interleaved states, + sharing a single distribution table. + + To decode an FSE bitstream, it is necessary to know its compressed + size. Compressed size is provided by headerByte. It's also + necessary to know its maximum possible decompressed size, which is + 255, since literal values span from 0 to 255, and the last symbol's + Weight is not represented. + + An FSE bitstream starts by a header, describing probabilities + distribution. It will create a decoding table. For a list of + Huffman weights, the maximum accuracy log is 6 bits. For more + details, see Section 4.1.1. + + The Huffman header compression uses two states, which share the same + FSE distribution table. The first state (State1) encodes the even- + numbered index symbols, and the second (State2) encodes the odd- + numbered index symbols. State1 is initialized first, and then + State2, and they take turns decoding a single symbol and updating + their state. For more details on these FSE operations, see + Section 4.1. + + The number of symbols to be decoded is determined by tracking the + bitStream overflow condition: If updating state after decoding a + symbol would require more bits than remain in the stream, it is + assumed that extra bits are zero. Then, symbols for each of the + final states are decoded and the process is complete. + + + + +Collet & Kucherawy Informational [Page 37] + +RFC 8478 application/zstd October 2018 + + +4.2.1.3. Conversion from Weights to Huffman Prefix Codes + + All present symbols will now have a Weight value. It is possible to + transform weights into Number_of_Bits, using this formula: + + if Weight > 0 + Number_of_Bits = Max_Number_of_Bits + 1 - Weight + else + Number_of_Bits = 0 + + Symbols are sorted by Weight. Within the same Weight, symbols keep + natural sequential order. Symbols with a Weight of zero are removed. + Then, starting from the lowest Weight, prefix codes are distributed + in sequential order. + + For example, assume the following list of weights has been decoded: + + +---------+--------+ + | Literal | Weight | + +---------+--------+ + | 0 | 4 | + +---------+--------+ + | 1 | 3 | + +---------+--------+ + | 2 | 2 | + +---------+--------+ + | 3 | 0 | + +---------+--------+ + | 4 | 1 | + +---------+--------+ + | 5 | 1 | + +---------+--------+ + + + + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 38] + +RFC 8478 application/zstd October 2018 + + + Sorting by weight and then the natural sequential order yields the + following distribution: + + +---------+--------+----------------+--------------+ + | Literal | Weight | Number_Of_Bits | Prefix Codes | + +---------+--------+----------------|--------------+ + | 3 | 0 | 0 | N/A | + +---------+--------+----------------|--------------+ + | 4 | 1 | 4 | 0000 | + +---------+--------+----------------|--------------+ + | 5 | 1 | 4 | 0001 | + +---------+--------+----------------|--------------+ + | 2 | 2 | 3 | 001 | + +---------+--------+----------------|--------------+ + | 1 | 3 | 2 | 01 | + +---------+--------+----------------|--------------+ + | 0 | 4 | 1 | 1 | + +---------+--------+----------------|--------------+ + +4.2.2. Huffman-Coded Streams + + Given a Huffman decoding table, it is possible to decode a Huffman- + coded stream. + + Each bitstream must be read backward, which starts from the end and + goes up to the beginning. Therefore, it is necessary to know the + size of each bitstream. + + It is also necessary to know exactly which bit is the last. This is + detected by a final bit flag: the highest bit of the last byte is a + final-bit-flag. Consequently, a last byte of 0 is not possible. And + the final-bit-flag itself is not part of the useful bitstream. + Hence, the last byte contains between 0 and 7 useful bits. + + Starting from the end, it is possible to read the bitstream in a + little-endian fashion, keeping track of already used bits. Since the + bitstream is encoded in reverse order, starting from the end, read + symbols in forward order. + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 39] + +RFC 8478 application/zstd October 2018 + + + For example, if the literal sequence "0145" was encoded using the + above prefix code, it would be encoded (in reverse order) as: + + +---------+----------+ + | Symbol | Encoding | + +---------+----------+ + | 5 | 0000 | + +---------+----------+ + | 4 | 0001 | + +---------+----------+ + | 1 | 01 | + +---------+----------+ + | 0 | 1 | + +---------+----------+ + | Padding | 00001 | + +---------+----------+ + + This results in the following 2-byte bitstream: + + 00010000 00001101 + + Here is an alternative representation with the symbol codes separated + by underscores: + + 0001_0000 00001_1_01 + + Reading the highest Max_Number_of_Bits bits, it's possible to compare + the extracted value to the decoding table, determining the symbol to + decode and number of bits to discard. + + The process continues reading up to the required number of symbols + per stream. If a bitstream is not entirely and exactly consumed, + hence reaching exactly its beginning position with all bits consumed, + the decoding process is considered faulty. + +5. Dictionary Format + + Zstandard is compatible with "raw content" dictionaries, free of any + format restriction, except that they must be at least 8 bytes. These + dictionaries function as if they were just the content part of a + formatted dictionary. + + However, dictionaries created by "zstd --train" in the reference + implementation follow a specific format, described here. + + Dictionaries are not included in the compressed content but rather + are provided out of band. That is, the Dictionary_ID identifies + which should be used, but this specification does not describe the + + + +Collet & Kucherawy Informational [Page 40] + +RFC 8478 application/zstd October 2018 + + + mechanism by which the dictionary is obtained prior to use during + compression or decompression. + + A dictionary has a size, defined either by a buffer limit or a file + size. The general format is: + + +--------------+---------------+----------------+---------+ + | Magic_Number | Dictionary_ID | Entropy_Tables | Content | + +--------------+---------------+----------------+---------+ + + Magic_Number: 4 bytes ID, value 0xEC30A437, little-endian format. + + Dictionary_ID: 4 bytes, stored in little-endian format. + Dictionary_ID can be any value, except 0 (which means no + Dictionary_ID). It is used by decoders to check if they use the + correct dictionary. If the frame is going to be distributed in a + private environment, any Dictionary_ID can be used. However, for + public distribution of compressed frames, the following ranges are + reserved and shall not be used: + + low range: <= 32767 + high range: >= (2^31) + + Entropy_Tables: Follow the same format as the tables in compressed + blocks. See the relevant FSE and Huffman sections for how to + decode these tables. They are stored in the following order: + Huffman table for literals, FSE table for offsets, FSE table for + match lengths, and FSE table for literals lengths. These tables + populate the Repeat Stats literals mode and Repeat distribution + mode for sequence decoding. It is finally followed by 3 offset + values, populating repeat offsets (instead of using {1,4,8}), + stored in order, 4-bytes little-endian each, for a total of 12 + bytes. Each repeat offset must have a value less than the + dictionary size. + + Content: The rest of the dictionary is its content. The content + acts as a "past" in front of data to be compressed or + decompressed, so it can be referenced in sequence commands. As + long as the amount of data decoded from this frame is less than or + equal to Window_Size, sequence commands may specify offsets longer + than the total length of decoded output so far to reference back + to the dictionary, even parts of the dictionary with offsets + larger than Window_Size. After the total output has surpassed + Window_Size, however, this is no longer allowed, and the + dictionary is no longer accessible. + + + + + + +Collet & Kucherawy Informational [Page 41] + +RFC 8478 application/zstd October 2018 + + +6. IANA Considerations + + IANA has made two registrations, as described below. + +6.1. The 'application/zstd' Media Type + + The 'application/zstd' media type identifies a block of data that is + compressed using zstd compression. The data is a stream of bytes as + described in this document. IANA has added the following to the + "Media Types" registry: + + Type name: application + + Subtype name: zstd + + Required parameters: N/A + + Optional parameters: N/A + + Encoding considerations: binary + + Security considerations: See Section 7 of RFC 8478 + + Interoperability considerations: N/A + + Published specification: RFC 8478 + + Applications that use this media type: anywhere data size is an + issue + + Additional information: + + Magic number(s): 4 bytes, little-endian format. + Value: 0xFD2FB528 + + File extension(s): zst + + Macintosh file type code(s): N/A + + For further information: See [ZSTD] + + Intended usage: common + + Restrictions on usage: N/A + + Author: Murray S. Kucherawy + + Change Controller: IETF + + + +Collet & Kucherawy Informational [Page 42] + +RFC 8478 application/zstd October 2018 + + + Provisional registration: no + +6.2. Content Encoding + + IANA has added the following entry to the "HTTP Content Coding + Registry" within the "Hypertext Transfer Protocol (HTTP) Parameters" + registry: + + Name: zstd + + Description: A stream of bytes compressed using the Zstandard + protocol + + Pointer to specification text: RFC 8478 + +6.3. Dictionaries + + Work in progress includes development of dictionaries that will + optimize compression and decompression of particular types of data. + Specification of such dictionaries for public use will necessitate + registration of a code point from the reserved range described in + Section 3.1.1.1.3 and its association with a specific dictionary. + + However, there are at present no such dictionaries published for + public use, so this document makes no immediate request of IANA to + create such a registry. + +7. Security Considerations + + Any data compression method involves the reduction of redundancy in + the data. Zstandard is no exception, and the usual precautions + apply. + + One should never compress a message whose content must remain secret + with a message generated by a third party. Such a compression can be + used to guess the content of the secret message through analysis of + entropy reduction. This was demonstrated in the Compression Ratio + Info-leak Made Easy (CRIME) attack [CRIME], for example. + + A decoder has to demonstrate capabilities to detect and prevent any + kind of data tampering in the compressed frame from triggering system + faults, such as reading or writing beyond allowed memory ranges. + This can be guaranteed by either the implementation language or + careful bound checkings. Of particular note is the encoding of + Number_of_Sequences values that cause the decoder to read into the + block header (and beyond), as well as the indication of a + Frame_Content_Size that is smaller than the actual decompressed data, + in an attempt to trigger a buffer overflow. It is highly recommended + + + +Collet & Kucherawy Informational [Page 43] + +RFC 8478 application/zstd October 2018 + + + to fuzz-test (i.e., provide invalid, unexpected, or random input and + verify safe operation of) decoder implementations to test and harden + their capability to detect bad frames and deal with them without any + adverse system side effect. + + An attacker may provide correctly formed compressed frames with + unreasonable memory requirements. A decoder must always control + memory requirements and enforce some (system-specific) limits in + order to protect memory usage from such scenarios. + + Compression can be optimized by training a dictionary on a variety of + related content payloads. This dictionary must then be available at + the decoder for decompression of the payload to be possible. While + this document does not specify how to acquire a dictionary for a + given compressed payload, it is worth noting that third-party + dictionaries may interact unexpectedly with a decoder, leading to + possible memory or other resource exhaustion attacks. We expect such + topics to be discussed in further detail in the Security + Considerations section of a forthcoming RFC for dictionary + acquisition and transmission, but highlight this issue now out of an + abundance of caution. + + As discussed in Section 3.1.2, it is possible to store arbitrary user + metadata in skippable frames. While such frames are ignored during + decompression of the data, they can be used as a watermark to track + the path of the compressed payload. + +8. Implementation Status + + Source code for a C language implementation of a Zstandard-compliant + library is available at [ZSTD-GITHUB]. This implementation is + considered to be the reference implementation and is production + ready; it implements the full range of the specification. It is + routinely tested against security hazards and widely deployed within + Facebook infrastructure. + + The reference version is optimized for speed and is highly portable. + It has been proven to run safely on multiple architectures (e.g., + x86, x64, ARM, MIPS, PowerPC, IA64) featuring 32- or 64-bit + addressing schemes, a little- or big-endian storage scheme, a number + of different operating systems (e.g., UNIX (including Linux, BSD, + OS-X, and Solaris) and Windows), and a number of compilers (e.g., + gcc, clang, visual, and icc). + + + + + + + + +Collet & Kucherawy Informational [Page 44] + +RFC 8478 application/zstd October 2018 + + +9. References + +9.1. Normative References + + [ZSTD] "Zstandard", . + +9.2. Informative References + + [ANS] Duda, J., "Asymmetric numeral systems: entropy coding + combining speed of Huffman coding with compression rate of + arithmetic coding", January 2014, + . + + [CRIME] "CRIME", June 2018, . + + [FSE] "FiniteStateEntropy", commit 6efa78a, June 2018, + . + + [LZ4] "LZ4 Frame Format Description", commit d03224b, January + 2018, . + + [RFC1952] Deutsch, P., "GZIP file format specification version 4.3", + RFC 1952, DOI 10.17487/RFC1952, May 1996, + . + + [XXHASH] "XXHASH Algorithm", . + + [ZSTD-GITHUB] + "zstd", commit 8514bd8, August 2018, + . + + + + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 45] + +RFC 8478 application/zstd October 2018 + + +Appendix A. Decoding Tables for Predefined Codes + + This appendix contains FSE decoding tables for the predefined literal + length, match length, and offset codes. The tables have been + constructed using the algorithm as given above in Section 4.1.1. The + tables here can be used as examples to crosscheck that an + implementation has built its decoding tables correctly. + +A.1. Literal Length Code Table + + +-------+--------+----------------+------+ + | State | Symbol | Number_Of_Bits | Base | + +-------+--------+----------------+------+ + | 0 | 0 | 0 | 0 | + +-------+--------+----------------+------+ + | 0 | 0 | 4 | 0 | + +-------+--------+----------------+------+ + | 1 | 0 | 4 | 16 | + +-------+--------+----------------+------+ + | 2 | 1 | 5 | 32 | + +-------+--------+----------------+------+ + | 3 | 3 | 5 | 0 | + +-------+--------+----------------+------+ + | 4 | 4 | 5 | 0 | + +-------+--------+----------------+------+ + | 5 | 6 | 5 | 0 | + +-------+--------+----------------+------+ + | 6 | 7 | 5 | 0 | + +-------+--------+----------------+------+ + | 7 | 9 | 5 | 0 | + +-------+--------+----------------+------+ + | 8 | 10 | 5 | 0 | + +-------+--------+----------------+------+ + | 9 | 12 | 5 | 0 | + +-------+--------+----------------+------+ + | 10 | 14 | 6 | 0 | + +-------+--------+----------------+------+ + | 11 | 16 | 5 | 0 | + +-------+--------+----------------+------+ + | 12 | 18 | 5 | 0 | + +-------+--------+----------------+------+ + | 13 | 19 | 5 | 0 | + +-------+--------+----------------+------+ + | 14 | 21 | 5 | 0 | + +-------+--------+----------------+------+ + | 15 | 22 | 5 | 0 | + +-------+--------+----------------+------+ + | 16 | 24 | 5 | 0 | + + + +Collet & Kucherawy Informational [Page 46] + +RFC 8478 application/zstd October 2018 + + + +-------+--------+----------------+------+ + | 17 | 25 | 5 | 32 | + +-------+--------+----------------+------+ + | 18 | 26 | 5 | 0 | + +-------+--------+----------------+------+ + | 19 | 27 | 6 | 0 | + +-------+--------+----------------+------+ + | 20 | 29 | 6 | 0 | + +-------+--------+----------------+------+ + | 21 | 31 | 6 | 0 | + +-------+--------+----------------+------+ + | 22 | 0 | 4 | 32 | + +-------+--------+----------------+------+ + | 23 | 1 | 4 | 0 | + +-------+--------+----------------+------+ + | 24 | 2 | 5 | 0 | + +-------+--------+----------------+------+ + | 25 | 4 | 5 | 32 | + +-------+--------+----------------+------+ + | 26 | 5 | 5 | 0 | + +-------+--------+----------------+------+ + | 27 | 7 | 5 | 32 | + +-------+--------+----------------+------+ + | 28 | 8 | 5 | 0 | + +-------+--------+----------------+------+ + | 29 | 10 | 5 | 32 | + +-------+--------+----------------+------+ + | 30 | 11 | 5 | 0 | + +-------+--------+----------------+------+ + | 31 | 13 | 6 | 0 | + +-------+--------+----------------+------+ + | 32 | 16 | 5 | 32 | + +-------+--------+----------------+------+ + | 33 | 17 | 5 | 0 | + +-------+--------+----------------+------+ + | 34 | 19 | 5 | 32 | + +-------+--------+----------------+------+ + | 35 | 20 | 5 | 0 | + +-------+--------+----------------+------+ + | 36 | 22 | 5 | 32 | + +-------+--------+----------------+------+ + | 37 | 23 | 5 | 0 | + +-------+--------+----------------+------+ + | 38 | 25 | 4 | 0 | + +-------+--------+----------------+------+ + | 39 | 25 | 4 | 16 | + +-------+--------+----------------+------+ + | 40 | 26 | 5 | 32 | + + + +Collet & Kucherawy Informational [Page 47] + +RFC 8478 application/zstd October 2018 + + + +-------+--------+----------------+------+ + | 41 | 28 | 6 | 0 | + +-------+--------+----------------+------+ + | 42 | 30 | 6 | 0 | + +-------+--------+----------------+------+ + | 43 | 0 | 4 | 48 | + +-------+--------+----------------+------+ + | 44 | 1 | 4 | 16 | + +-------+--------+----------------+------+ + | 45 | 2 | 5 | 32 | + +-------+--------+----------------+------+ + | 46 | 3 | 5 | 32 | + +-------+--------+----------------+------+ + | 47 | 5 | 5 | 32 | + +-------+--------+----------------+------+ + | 48 | 6 | 5 | 32 | + +-------+--------+----------------+------+ + | 49 | 8 | 5 | 32 | + +-------+--------+----------------+------+ + | 50 | 9 | 5 | 32 | + +-------+--------+----------------+------+ + | 51 | 11 | 5 | 32 | + +-------+--------+----------------+------+ + | 52 | 12 | 5 | 32 | + +-------+--------+----------------+------+ + | 53 | 15 | 6 | 0 | + +-------+--------+----------------+------+ + | 54 | 17 | 5 | 32 | + +-------+--------+----------------+------+ + | 55 | 18 | 5 | 32 | + +-------+--------+----------------+------+ + | 56 | 20 | 5 | 32 | + +-------+--------+----------------+------+ + | 57 | 21 | 5 | 32 | + +-------+--------+----------------+------+ + | 58 | 23 | 5 | 32 | + +-------+--------+----------------+------+ + | 59 | 24 | 5 | 32 | + +-------+--------+----------------+------+ + | 60 | 35 | 6 | 0 | + +-------+--------+----------------+------+ + | 61 | 34 | 6 | 0 | + +-------+--------+----------------+------+ + | 62 | 33 | 6 | 0 | + +-------+--------+----------------+------+ + | 63 | 32 | 6 | 0 | + +-------+--------+----------------+------+ + + + + +Collet & Kucherawy Informational [Page 48] + +RFC 8478 application/zstd October 2018 + + +A.2. Match Length Code Table + + +-------+--------+----------------+------+ + | State | Symbol | Number_Of_Bits | Base | + +-------+--------+----------------+------+ + | 0 | 0 | 0 | 0 | + +-------+--------+----------------+------+ + | 0 | 0 | 6 | 0 | + +-------+--------+----------------+------+ + | 1 | 1 | 4 | 0 | + +-------+--------+----------------+------+ + | 2 | 2 | 5 | 32 | + +-------+--------+----------------+------+ + | 3 | 3 | 5 | 0 | + +-------+--------+----------------+------+ + | 4 | 5 | 5 | 0 | + +-------+--------+----------------+------+ + | 5 | 6 | 5 | 0 | + +-------+--------+----------------+------+ + | 6 | 8 | 5 | 0 | + +-------+--------+----------------+------+ + | 7 | 10 | 6 | 0 | + +-------+--------+----------------+------+ + | 8 | 13 | 6 | 0 | + +-------+--------+----------------+------+ + | 9 | 16 | 6 | 0 | + +-------+--------+----------------+------+ + | 10 | 19 | 6 | 0 | + +-------+--------+----------------+------+ + | 11 | 22 | 6 | 0 | + +-------+--------+----------------+------+ + | 12 | 25 | 6 | 0 | + +-------+--------+----------------+------+ + | 13 | 28 | 6 | 0 | + +-------+--------+----------------+------+ + | 14 | 31 | 6 | 0 | + +-------+--------+----------------+------+ + | 15 | 33 | 6 | 0 | + +-------+--------+----------------+------+ + | 16 | 35 | 6 | 0 | + +-------+--------+----------------+------+ + | 17 | 37 | 6 | 0 | + +-------+--------+----------------+------+ + | 18 | 39 | 6 | 0 | + +-------+--------+----------------+------+ + | 19 | 41 | 6 | 0 | + +-------+--------+----------------+------+ + | 20 | 43 | 6 | 0 | + + + +Collet & Kucherawy Informational [Page 49] + +RFC 8478 application/zstd October 2018 + + + +-------+--------+----------------+------+ + | 21 | 45 | 6 | 0 | + +-------+--------+----------------+------+ + | 22 | 1 | 4 | 16 | + +-------+--------+----------------+------+ + | 23 | 2 | 4 | 0 | + +-------+--------+----------------+------+ + | 24 | 3 | 5 | 32 | + +-------+--------+----------------+------+ + | 25 | 4 | 5 | 0 | + +-------+--------+----------------+------+ + | 26 | 6 | 5 | 32 | + +-------+--------+----------------+------+ + | 27 | 7 | 5 | 0 | + +-------+--------+----------------+------+ + | 28 | 9 | 6 | 0 | + +-------+--------+----------------+------+ + | 29 | 12 | 6 | 0 | + +-------+--------+----------------+------+ + | 30 | 15 | 6 | 0 | + +-------+--------+----------------+------+ + | 31 | 18 | 6 | 0 | + +-------+--------+----------------+------+ + | 32 | 21 | 6 | 0 | + +-------+--------+----------------+------+ + | 33 | 24 | 6 | 0 | + +-------+--------+----------------+------+ + | 34 | 27 | 6 | 0 | + +-------+--------+----------------+------+ + | 35 | 30 | 6 | 0 | + +-------+--------+----------------+------+ + | 36 | 32 | 6 | 0 | + +-------+--------+----------------+------+ + | 37 | 34 | 6 | 0 | + +-------+--------+----------------+------+ + | 38 | 36 | 6 | 0 | + +-------+--------+----------------+------+ + | 39 | 38 | 6 | 0 | + +-------+--------+----------------+------+ + | 40 | 40 | 6 | 0 | + +-------+--------+----------------+------+ + | 41 | 42 | 6 | 0 | + +-------+--------+----------------+------+ + | 42 | 44 | 6 | 0 | + +-------+--------+----------------+------+ + | 43 | 1 | 4 | 32 | + +-------+--------+----------------+------+ + | 44 | 1 | 4 | 48 | + + + +Collet & Kucherawy Informational [Page 50] + +RFC 8478 application/zstd October 2018 + + + +-------+--------+----------------+------+ + | 45 | 2 | 4 | 16 | + +-------+--------+----------------+------+ + | 46 | 4 | 5 | 32 | + +-------+--------+----------------+------+ + | 47 | 5 | 5 | 32 | + +-------+--------+----------------+------+ + | 48 | 7 | 5 | 32 | + +-------+--------+----------------+------+ + | 49 | 8 | 5 | 32 | + +-------+--------+----------------+------+ + | 50 | 11 | 6 | 0 | + +-------+--------+----------------+------+ + | 51 | 14 | 6 | 0 | + +-------+--------+----------------+------+ + | 52 | 17 | 6 | 0 | + +-------+--------+----------------+------+ + | 53 | 20 | 6 | 0 | + +-------+--------+----------------+------+ + | 54 | 23 | 6 | 0 | + +-------+--------+----------------+------+ + | 55 | 26 | 6 | 0 | + +-------+--------+----------------+------+ + | 56 | 29 | 6 | 0 | + +-------+--------+----------------+------+ + | 57 | 52 | 6 | 0 | + +-------+--------+----------------+------+ + | 58 | 51 | 6 | 0 | + +-------+--------+----------------+------+ + | 59 | 50 | 6 | 0 | + +-------+--------+----------------+------+ + | 60 | 49 | 6 | 0 | + +-------+--------+----------------+------+ + | 61 | 48 | 6 | 0 | + +-------+--------+----------------+------+ + | 62 | 47 | 6 | 0 | + +-------+--------+----------------+------+ + | 63 | 46 | 6 | 0 | + +-------+--------+----------------+------+ + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 51] + +RFC 8478 application/zstd October 2018 + + +A.3. Offset Code Table + + +-------+--------+----------------+------+ + | State | Symbol | Number_Of_Bits | Base | + +-------+--------+----------------+------+ + | 0 | 0 | 0 | 0 | + +-------+--------+----------------+------+ + | 0 | 0 | 5 | 0 | + +-------+--------+----------------+------+ + | 1 | 6 | 4 | 0 | + +-------+--------+----------------+------+ + | 2 | 9 | 5 | 0 | + +-------+--------+----------------+------+ + | 3 | 15 | 5 | 0 | + +-------+--------+----------------+------+ + | 4 | 21 | 5 | 0 | + +-------+--------+----------------+------+ + | 5 | 3 | 5 | 0 | + +-------+--------+----------------+------+ + | 6 | 7 | 4 | 0 | + +-------+--------+----------------+------+ + | 7 | 12 | 5 | 0 | + +-------+--------+----------------+------+ + | 8 | 18 | 5 | 0 | + +-------+--------+----------------+------+ + | 9 | 23 | 5 | 0 | + +-------+--------+----------------+------+ + | 10 | 5 | 5 | 0 | + +-------+--------+----------------+------+ + | 11 | 8 | 4 | 0 | + +-------+--------+----------------+------+ + | 12 | 14 | 5 | 0 | + +-------+--------+----------------+------+ + | 13 | 20 | 5 | 0 | + +-------+--------+----------------+------+ + | 14 | 2 | 5 | 0 | + +-------+--------+----------------+------+ + | 15 | 7 | 4 | 16 | + +-------+--------+----------------+------+ + | 16 | 11 | 5 | 0 | + +-------+--------+----------------+------+ + | 17 | 17 | 5 | 0 | + +-------+--------+----------------+------+ + | 18 | 22 | 5 | 0 | + +-------+--------+----------------+------+ + | 19 | 4 | 5 | 0 | + +-------+--------+----------------+------+ + | 20 | 8 | 4 | 16 | + + + +Collet & Kucherawy Informational [Page 52] + +RFC 8478 application/zstd October 2018 + + + +-------+--------+----------------+------+ + | 21 | 13 | 5 | 0 | + +-------+--------+----------------+------+ + | 22 | 19 | 5 | 0 | + +-------+--------+----------------+------+ + | 23 | 1 | 5 | 0 | + +-------+--------+----------------+------+ + | 24 | 6 | 4 | 16 | + +-------+--------+----------------+------+ + | 25 | 10 | 5 | 0 | + +-------+--------+----------------+------+ + | 26 | 16 | 5 | 0 | + +-------+--------+----------------+------+ + | 27 | 28 | 5 | 0 | + +-------+--------+----------------+------+ + | 28 | 27 | 5 | 0 | + +-------+--------+----------------+------+ + | 29 | 26 | 5 | 0 | + +-------+--------+----------------+------+ + | 30 | 25 | 5 | 0 | + +-------+--------+----------------+------+ + | 31 | 24 | 5 | 0 | + +-------+--------+----------------+------+ + +Acknowledgments + + zstd was developed by Yann Collet. + + Bobo Bose-Kolanu, Felix Handte, Kyle Nekritz, Nick Terrell, and David + Schleimer provided helpful feedback during the development of this + document. + + + + + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 53] + +RFC 8478 application/zstd October 2018 + + +Authors' Addresses + + Yann Collet + Facebook + 1 Hacker Way + Menlo Park, CA 94025 + United States of America + + Email: cyan@fb.com + + + Murray S. Kucherawy (editor) + Facebook + 1 Hacker Way + Menlo Park, CA 94025 + United States of America + + Email: msk@fb.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Collet & Kucherawy Informational [Page 54] + diff --git a/lib/std/compress/testdata/rfc8478.txt.zst.19 b/lib/std/compress/testdata/rfc8478.txt.zst.19 new file mode 100644 index 0000000000000000000000000000000000000000..e0cf325af238aaf25c2d4ec05107381a28bd481f GIT binary patch literal 22211 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 Wx~H8l@BLdP@qcAfX|n-M&rFS~S5QX) literal 0 HcmV?d00001 diff --git a/lib/std/compress/testdata/rfc8478.txt.zst.3 b/lib/std/compress/testdata/rfc8478.txt.zst.3 new file mode 100644 index 0000000000000000000000000000000000000000..781601a8a2c9150aad00ee56d7997d2a96396404 GIT binary patch literal 25639 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 { + const header = try decodeZStandardHeader(src[4..], null); + return header.content_size; + }, + .skippable => return 0, + } +} + +pub fn frameType(src: []const u8) !frame.Kind { + const magic = readInt(u32, src[0..4]); + return if (magic == frame.ZStandard.magic_number) + .zstandard + else if (isSkippableMagic(magic)) + .skippable + else + error.BadMagic; +} + +const ReadWriteCount = struct { + read_count: usize, + write_count: usize, +}; + +pub fn decodeFrame(dest: []u8, src: []const u8, verify_checksum: bool) !ReadWriteCount { + return switch (try frameType(src)) { + .zstandard => decodeZStandardFrame(dest, src, verify_checksum), + .skippable => ReadWriteCount{ + .read_count = try skippableFrameSize(src[0..8]) + 8, + .write_count = 0, + }, + }; +} + +const DecodeState = struct { + repeat_offsets: [3]u32, + + offset: StateData(8), + match: StateData(9), + literal: StateData(9), + + offset_fse_buffer: []Table.Fse, + match_fse_buffer: []Table.Fse, + literal_fse_buffer: []Table.Fse, + + literal_written_count: usize, + + literal_stream_reader: ReverseBitReader(ReversedByteReader.Reader), + literal_stream_bytes: ReversedByteReader, + literal_stream_index: usize, + huffman_tree: Literals.HuffmanTree, + + fn StateData(comptime max_accuracy_log: comptime_int) type { + return struct { + state: State, + table: Table, + accuracy_log: u8, + + const State = std.meta.Int(.unsigned, max_accuracy_log); + }; + } + + fn readInitialState(self: *DecodeState, bit_reader: anytype) !void { + self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); + self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); + self.match.state = try bit_reader.readBitsNoEof(u9, self.match.accuracy_log); + log.debug("initial decoder state: literal = {d}, offset = {d} match = {d}", .{ + self.literal.state, + self.offset.state, + self.match.state, + }); + } + + fn updateRepeatOffset(self: *DecodeState, offset: u32) void { + std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[1]); + std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[2]); + self.repeat_offsets[0] = offset; + } + + fn useRepeatOffset(self: *DecodeState, index: usize) u32 { + if (index == 1) + std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[1]) + else if (index == 2) { + std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[2]); + std.mem.swap(u32, &self.repeat_offsets[1], &self.repeat_offsets[2]); + } + return self.repeat_offsets[0]; + } + + const DataType = enum { offset, match, literal }; + + fn updateState(self: *DecodeState, comptime choice: DataType, bit_reader: anytype) !void { + switch (@field(self, @tagName(choice)).table) { + .rle => {}, + .fse => |table| { + const data = table[@field(self, @tagName(choice)).state]; + const T = @TypeOf(@field(self, @tagName(choice))).State; + const bits_summand = try bit_reader.readBitsNoEof(T, data.bits); + const next_state = data.baseline + bits_summand; + @field(self, @tagName(choice)).state = @intCast(@TypeOf(@field(self, @tagName(choice))).State, next_state); + }, + } + } + + fn updateFseTable( + self: *DecodeState, + src: []const u8, + comptime choice: DataType, + mode: Sequences.Header.Mode, + first_compressed_block: bool, + ) !usize { + const field_name = @tagName(choice); + switch (mode) { + .predefined => { + @field(self, field_name).accuracy_log = @field(types.compressed_block.default_accuracy_log, field_name); + @field(self, field_name).table = @field(types.compressed_block, "predefined_" ++ field_name ++ "_fse_table"); + return 0; + }, + .rle => { + @field(self, field_name).accuracy_log = 0; + @field(self, field_name).table = .{ .rle = src[0] }; + return 1; + }, + .fse => { + var stream = std.io.fixedBufferStream(src); + var counting_reader = std.io.countingReader(stream.reader()); + var bit_reader = bitReader(counting_reader.reader()); + + const table_size = try decodeFseTable( + &bit_reader, + @field(types.compressed_block.table_symbol_count_max, field_name), + @field(types.compressed_block.table_accuracy_log_max, field_name), + @field(self, field_name ++ "_fse_buffer"), + ); + @field(self, field_name).table = .{ .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size] }; + @field(self, field_name).accuracy_log = std.math.log2_int_ceil(usize, table_size); + log.debug("decoded fse " ++ field_name ++ " table '{}'", .{ + std.fmt.fmtSliceHexUpper(src[0..counting_reader.bytes_read]), + }); + dumpFseTable(field_name, @field(self, field_name).table.fse); + return counting_reader.bytes_read; + }, + .repeat => return if (first_compressed_block) error.RepeatModeFirst else 0, + } + } + + const Sequence = struct { + literal_length: u32, + match_length: u32, + offset: u32, + }; + + fn nextSequence(self: *DecodeState, bit_reader: anytype) !Sequence { + const raw_code = self.getCode(.offset); + const offset_code = std.math.cast(u5, raw_code) orelse { + log.err("got offset code of {d}", .{raw_code}); + return error.OffsetCodeTooLarge; + }; + const offset_value = (@as(u32, 1) << offset_code) + try bit_reader.readBitsNoEof(u32, offset_code); + + const match_code = self.getCode(.match); + const match = types.compressed_block.match_length_code_table[match_code]; + const match_length = match[0] + try bit_reader.readBitsNoEof(u32, match[1]); + + const literal_code = self.getCode(.literal); + const literal = types.compressed_block.literals_length_code_table[literal_code]; + const literal_length = literal[0] + try bit_reader.readBitsNoEof(u32, literal[1]); + + const offset = if (offset_value > 3) offset: { + const offset = offset_value - 3; + self.updateRepeatOffset(offset); + break :offset offset; + } else offset: { + if (literal_length == 0) { + if (offset_value == 3) { + const offset = self.repeat_offsets[0] - 1; + self.updateRepeatOffset(offset); + break :offset offset; + } + break :offset self.useRepeatOffset(offset_value); + } + break :offset self.useRepeatOffset(offset_value - 1); + }; + + log.debug("sequence = ({d}, {d}, {d})", .{ literal_length, offset, match_length }); + return .{ + .literal_length = literal_length, + .match_length = match_length, + .offset = offset, + }; + } + + fn executeSequenceSlice(self: *DecodeState, dest: []u8, write_pos: usize, literals: Literals, sequence: Sequence) !void { + try self.decodeLiteralsInto(dest[write_pos..], literals, sequence.literal_length); + + // TODO: should we validate offset against max_window_size? + assert(sequence.offset <= write_pos + sequence.literal_length); + const copy_start = write_pos + sequence.literal_length - sequence.offset; + const copy_end = copy_start + sequence.match_length; + // NOTE: we ignore the usage message for std.mem.copy and copy with dest.ptr >= src.ptr + // to allow repeats + std.mem.copy(u8, dest[write_pos + sequence.literal_length ..], dest[copy_start..copy_end]); + } + + fn decodeSequenceSlice( + self: *DecodeState, + dest: []u8, + write_pos: usize, + literals: Literals, + bit_reader: anytype, + last_sequence: bool, + ) !usize { + const sequence = try self.nextSequence(bit_reader); + try self.executeSequenceSlice(dest, write_pos, literals, sequence); + log.debug("sequence decompressed into '{x}'", .{ + std.fmt.fmtSliceHexUpper(dest[write_pos .. write_pos + sequence.literal_length + sequence.match_length]), + }); + if (!last_sequence) { + try self.updateState(.literal, bit_reader); + try self.updateState(.match, bit_reader); + try self.updateState(.offset, bit_reader); + } + return sequence.match_length + sequence.literal_length; + } + + fn nextLiteralMultiStream(self: *DecodeState, literals: Literals) !void { + self.literal_stream_index += 1; + try self.initLiteralStream(literals.streams.four[self.literal_stream_index]); + } + + fn initLiteralStream(self: *DecodeState, bytes: []const u8) !void { + log.debug("initing literal stream: {}", .{std.fmt.fmtSliceHexUpper(bytes)}); + self.literal_stream_bytes = reversedByteReader(bytes); + self.literal_stream_reader = reverseBitReader(self.literal_stream_bytes.reader()); + while (0 == try self.literal_stream_reader.readBitsNoEof(u1, 1)) {} + } + + fn decodeLiteralsInto(self: *DecodeState, dest: []u8, literals: Literals, len: usize) !void { + if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; + switch (literals.header.block_type) { + .raw => { + const literal_data = literals.streams.one[self.literal_written_count .. self.literal_written_count + len]; + std.mem.copy(u8, dest, literal_data); + self.literal_written_count += len; + }, + .rle => { + var i: usize = 0; + while (i < len) : (i += 1) { + dest[i] = literals.streams.one[0]; + } + log.debug("rle: {}", .{std.fmt.fmtSliceHexUpper(dest[0..len])}); + self.literal_written_count += len; + }, + .compressed, .treeless => { + // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; + const huffman_tree = self.huffman_tree; + const max_bit_count = huffman_tree.max_bit_count; + const starting_bit_count = Literals.HuffmanTree.weightToBitCount( + huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, + max_bit_count, + ); + var bits_read: u4 = 0; + var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; + var bit_count_to_read: u4 = starting_bit_count; + var i: usize = 0; + while (i < len) : (i += 1) { + var prefix: u16 = 0; + while (true) { + const new_bits = self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch |err| + switch (err) { + error.EndOfStream => if (literals.streams == .four and self.literal_stream_index < 3) bits: { + try self.nextLiteralMultiStream(literals); + break :bits try self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read); + } else { + return error.UnexpectedEndOfLiteralStream; + }, + }; + prefix <<= bit_count_to_read; + prefix |= new_bits; + bits_read += bit_count_to_read; + const result = try huffman_tree.query(huffman_tree_index, prefix); + + switch (result) { + .symbol => |sym| { + dest[i] = sym; + bit_count_to_read = starting_bit_count; + bits_read = 0; + huffman_tree_index = huffman_tree.symbol_count_minus_one; + break; + }, + .index => |index| { + huffman_tree_index = index; + const bit_count = Literals.HuffmanTree.weightToBitCount( + huffman_tree.nodes[index].weight, + max_bit_count, + ); + bit_count_to_read = bit_count - bits_read; + }, + } + } + } + self.literal_written_count += len; + }, + } + } + + fn getCode(self: *DecodeState, comptime choice: DataType) u32 { + return switch (@field(self, @tagName(choice)).table) { + .rle => |value| value, + .fse => |table| table[@field(self, @tagName(choice)).state].symbol, + }; + } +}; + +const literal_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.literal; +const match_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; +const offset_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; + +pub fn decodeZStandardFrame(dest: []u8, src: []const u8, verify_checksum: bool) !ReadWriteCount { + assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); + var consumed_count: usize = 4; + + const frame_header = try decodeZStandardHeader(src[consumed_count..], &consumed_count); + + if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; + + const content_size = frame_header.content_size orelse return error.UnknownContentSizeUnsupported; + // const window_size = frameWindowSize(header) orelse return error.WindowSizeUnknown; + if (dest.len < content_size) return error.ContentTooLarge; + + const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; + var hash_state = if (should_compute_checksum) std.hash.XxHash64.init(0) else undefined; + + // TODO: block_maximum_size should be @min(1 << 17, window_size); + const written_count = try decodeFrameBlocks( + dest, + src[consumed_count..], + &consumed_count, + if (should_compute_checksum) &hash_state else null, + ); + + if (frame_header.descriptor.content_checksum_flag) { + const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); + consumed_count += 4; + if (verify_checksum) { + const hash = hash_state.final(); + const hash_low_bytes = hash & 0xFFFFFFFF; + if (checksum != hash_low_bytes) { + std.log.err("expected checksum {x}, got {x} (full hash {x})", .{ checksum, hash_low_bytes, hash }); + return error.ChecksumFailure; + } + } + } + return ReadWriteCount{ .read_count = consumed_count, .write_count = written_count }; +} + +pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, hash: ?*std.hash.XxHash64) !usize { + // These tables take 7680 bytes + var literal_fse_data: [literal_table_size_max]Table.Fse = undefined; + var match_fse_data: [match_table_size_max]Table.Fse = undefined; + var offset_fse_data: [offset_table_size_max]Table.Fse = undefined; + + var block_header = decodeBlockHeader(src[0..3]); + var bytes_read: usize = 3; + var decode_state = DecodeState{ + .repeat_offsets = .{ + types.compressed_block.start_repeated_offset_1, + types.compressed_block.start_repeated_offset_2, + types.compressed_block.start_repeated_offset_3, + }, + + .offset = undefined, + .match = undefined, + .literal = undefined, + + .literal_fse_buffer = &literal_fse_data, + .match_fse_buffer = &match_fse_data, + .offset_fse_buffer = &offset_fse_data, + + .literal_written_count = 0, + .literal_stream_reader = undefined, + .literal_stream_bytes = undefined, + .literal_stream_index = undefined, + .huffman_tree = undefined, + }; + var first_compressed_block = true; + var first_compressed_literals = true; + var written_count: usize = 0; + while (true) : ({ + block_header = decodeBlockHeader(src[bytes_read..][0..3]); + bytes_read += 3; + }) { + const written_size = try decodeBlock( + dest, + src[bytes_read..], + block_header, + &decode_state, + &first_compressed_block, + &first_compressed_literals, + &bytes_read, + written_count, + ); + if (hash) |hash_state| hash_state.update(dest[written_count .. written_count + written_size]); + written_count += written_size; + if (block_header.last_block) break; + } + consumed_count.* += bytes_read; + return written_count; +} + +pub fn decodeBlock( + dest: []u8, + src: []const u8, + block_header: frame.ZStandard.Block.Header, + decode_state: *DecodeState, + first_compressed_block: *bool, + first_compressed_literals: *bool, + consumed_count: *usize, + written_count: usize, +) !usize { + const block_maximum_size = 1 << 17; // 128KiB + const block_size = block_header.block_size; + if (block_maximum_size < block_size) return error.BlockSizeOverMaximum; + // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) + switch (block_header.block_type) { + .raw => { + log.debug("writing raw block - size {d}", .{block_size}); + const data = src[0..block_size]; + std.mem.copy(u8, dest[written_count..], data); + consumed_count.* += block_size; + return block_size; + }, + .rle => { + log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); + var write_pos: usize = written_count; + while (write_pos < block_size + written_count) : (write_pos += 1) { + dest[write_pos] = src[0]; + } + consumed_count.* += 1; + return block_size; + }, + .compressed => { + var bytes_read: usize = 0; + const literals = try decodeLiteralsSection(src, &bytes_read); + const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); + + if (first_compressed_literals.* and literals.header.block_type == .treeless) + return error.TreelessLiteralsFirst; + + if (literals.huffman_tree) |tree| { + decode_state.huffman_tree = tree; + first_compressed_literals.* = false; + } + + switch (literals.header.block_type) { + .raw, .rle => {}, + .compressed, .treeless => { + decode_state.literal_stream_index = 0; + switch (literals.streams) { + .one => |slice| try decode_state.initLiteralStream(slice), + .four => |streams| try decode_state.initLiteralStream(streams[0]), + } + }, + } + + if (sequences_header.sequence_count > 0) { + bytes_read += try decode_state.updateFseTable( + src[bytes_read..], + .literal, + sequences_header.literal_lengths, + first_compressed_block.*, + ); + + bytes_read += try decode_state.updateFseTable( + src[bytes_read..], + .offset, + sequences_header.offsets, + first_compressed_block.*, + ); + + bytes_read += try decode_state.updateFseTable( + src[bytes_read..], + .match, + sequences_header.match_lengths, + first_compressed_block.*, + ); + first_compressed_block.* = false; + } + + var bytes_written: usize = 0; + if (sequences_header.sequence_count > 0) { + const bit_stream_bytes = src[bytes_read..block_size]; + var reverse_byte_reader = reversedByteReader(bit_stream_bytes); + var bit_stream = reverseBitReader(reverse_byte_reader.reader()); + + while (0 == try bit_stream.readBitsNoEof(u1, 1)) {} + try decode_state.readInitialState(&bit_stream); + + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + log.debug("decoding sequence {d}", .{i}); + const decompressed_size = try decode_state.decodeSequenceSlice( + dest, + written_count + bytes_written, + literals, + &bit_stream, + i == sequences_header.sequence_count - 1, + ); + bytes_written += decompressed_size; + } + + bytes_read += bit_stream_bytes.len; + } + + if (decode_state.literal_written_count < literals.header.regenerated_size) { + log.debug("decoding remaining literals", .{}); + const len = literals.header.regenerated_size - decode_state.literal_written_count; + try decode_state.decodeLiteralsInto(dest[written_count + bytes_written ..], literals, len); + log.debug("remaining decoded literals at {d}: {}", .{ + written_count, + std.fmt.fmtSliceHexUpper(dest[written_count .. written_count + len]), + }); + bytes_written += len; + } + + decode_state.literal_written_count = 0; + assert(bytes_read == block_header.block_size); + consumed_count.* += bytes_read; + return bytes_written; + }, + .reserved => return error.FrameContainsReservedBlock, + } +} + +pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { + const magic = readInt(u32, src[0..4]); + assert(isSkippableMagic(magic)); + const frame_size = readInt(u32, src[4..8]); + return .{ + .magic_number = magic, + .frame_size = frame_size, + }; +} + +pub fn skippableFrameSize(src: *const [8]u8) !usize { + assert(isSkippableMagic(readInt(u32, src[0..4]))); + const frame_size = readInt(u32, src[4..8]); + return frame_size; +} + +pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { + if (header.window_descriptor) |descriptor| { + const exponent = (descriptor & 0b11111000) >> 3; + const mantissa = descriptor & 0b00000111; + const window_log = 10 + exponent; + const window_base = @as(u64, 1) << @intCast(u6, window_log); + const window_add = (window_base / 8) * mantissa; + return window_base + window_add; + } else return header.content_size; +} + +pub fn decodeZStandardHeader(src: []const u8, consumed_count: ?*usize) !frame.ZStandard.Header { + const descriptor = @bitCast(frame.ZStandard.Header.Descriptor, src[0]); + + if (descriptor.unused) return error.UnusedBitSet; + if (descriptor.reserved) return error.ReservedBitSet; + + var bytes_read_count: usize = 1; + + var window_descriptor: ?u8 = null; + if (!descriptor.single_segment_flag) { + window_descriptor = src[bytes_read_count]; + bytes_read_count += 1; + } + + var dictionary_id: ?u32 = null; + if (descriptor.dictionary_id_flag > 0) { + // if flag is 3 we field_size = 4, else field_size = flag + const field_size = (@as(u3, 1) << descriptor.dictionary_id_flag) >> 1; + dictionary_id = readVarInt(u32, src[bytes_read_count .. bytes_read_count + field_size]); + bytes_read_count += field_size; + } + + var content_size: ?u64 = null; + if (descriptor.single_segment_flag or descriptor.content_size_flag > 0) { + const field_size = @as(u4, 1) << descriptor.content_size_flag; + content_size = readVarInt(u64, src[bytes_read_count .. bytes_read_count + field_size]); + if (field_size == 2) content_size.? += 256; + bytes_read_count += field_size; + } + + if (consumed_count) |p| p.* += bytes_read_count; + + const header = frame.ZStandard.Header{ + .descriptor = descriptor, + .window_descriptor = window_descriptor, + .dictionary_id = dictionary_id, + .content_size = content_size, + }; + log.debug( + "decoded ZStandard frame header {x}: " ++ + "desc = (d={d},c={},r={},u={},s={},cs={d}), win_desc = {?x}, dict_id = {?x}, content_size = {?d}", + .{ + std.fmt.fmtSliceHexUpper(src[0..bytes_read_count]), + header.descriptor.dictionary_id_flag, + header.descriptor.content_checksum_flag, + header.descriptor.reserved, + header.descriptor.unused, + header.descriptor.single_segment_flag, + header.descriptor.content_size_flag, + header.window_descriptor, + header.dictionary_id, + header.content_size, + }, + ); + return header; +} + +pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { + const last_block = src[0] & 1 == 1; + const block_type = @intToEnum(frame.ZStandard.Block.Type, (src[0] & 0b110) >> 1); + const block_size = ((src[0] & 0b11111000) >> 3) + (@as(u21, src[1]) << 5) + (@as(u21, src[2]) << 13); + log.debug("decoded block header {}: last = {}, type = {s}, size = {d}", .{ + std.fmt.fmtSliceHexUpper(src), + last_block, + @tagName(block_type), + block_size, + }); + return .{ + .last_block = last_block, + .block_type = block_type, + .block_size = block_size, + }; +} + +pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !Literals { + // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) + var bytes_read: usize = 0; + const header = decodeLiteralsHeader(src, &bytes_read); + switch (header.block_type) { + .raw => { + const stream = src[bytes_read .. bytes_read + header.regenerated_size]; + consumed_count.* += header.regenerated_size + bytes_read; + return Literals{ + .header = header, + .huffman_tree = null, + .streams = .{ .one = stream }, + }; + }, + .rle => { + const stream = src[bytes_read .. bytes_read + 1]; + consumed_count.* += 1 + bytes_read; + return Literals{ + .header = header, + .huffman_tree = null, + .streams = .{ .one = stream }, + }; + }, + .compressed, .treeless => { + const huffman_tree_start = bytes_read; + const huffman_tree = if (header.block_type == .compressed) + try decodeHuffmanTree(src[bytes_read..], &bytes_read) + else + null; + const huffman_tree_size = bytes_read - huffman_tree_start; + const total_streams_size = @as(usize, header.compressed_size.?) - huffman_tree_size; + log.debug("huffman tree size = {}, total streams size = {}", .{ huffman_tree_size, total_streams_size }); + if (huffman_tree) |tree| dumpHuffmanTree(tree); + + if (header.size_format == 0) { + const stream = src[bytes_read .. bytes_read + total_streams_size]; + bytes_read += total_streams_size; + consumed_count.* += bytes_read; + return Literals{ + .header = header, + .huffman_tree = huffman_tree, + .streams = .{ .one = stream }, + }; + } + + const stream_data = src[bytes_read .. bytes_read + total_streams_size]; + + log.debug("jump table: {}", .{std.fmt.fmtSliceHexUpper(stream_data[0..6])}); + const stream_1_length = @as(usize, readInt(u16, stream_data[0..2])); + const stream_2_length = @as(usize, readInt(u16, stream_data[2..4])); + const stream_3_length = @as(usize, readInt(u16, stream_data[4..6])); + const stream_4_length = (total_streams_size - 6) - (stream_1_length + stream_2_length + stream_3_length); + + const stream_1_start = 6; + const stream_2_start = stream_1_start + stream_1_length; + const stream_3_start = stream_2_start + stream_2_length; + const stream_4_start = stream_3_start + stream_3_length; + + consumed_count.* += total_streams_size + bytes_read; + + return Literals{ + .header = header, + .huffman_tree = huffman_tree, + .streams = .{ .four = .{ + stream_data[stream_1_start .. stream_1_start + stream_1_length], + stream_data[stream_2_start .. stream_2_start + stream_2_length], + stream_data[stream_3_start .. stream_3_start + stream_3_length], + stream_data[stream_4_start .. stream_4_start + stream_4_length], + } }, + }; + }, + } +} + +fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !Literals.HuffmanTree { + var bytes_read: usize = 0; + bytes_read += 1; + const header = src[0]; + var symbol_count: usize = undefined; + var weights: [256]u4 = undefined; + var max_number_of_bits: u4 = undefined; + if (header < 128) { + // FSE compressed weigths + const compressed_size = header; + var stream = std.io.fixedBufferStream(src[1 .. compressed_size + 1]); + var counting_reader = std.io.countingReader(stream.reader()); + var bit_reader = bitReader(counting_reader.reader()); + + var entries: [1 << 6]Table.Fse = undefined; + const table_size = try decodeFseTable(&bit_reader, 256, 6, &entries); + const accuracy_log = std.math.log2_int_ceil(usize, table_size); + + var huff_data = src[1 + counting_reader.bytes_read .. compressed_size + 1]; + var huff_data_bytes = reversedByteReader(huff_data); + var huff_bits = reverseBitReader(huff_data_bytes.reader()); + while (0 == try huff_bits.readBitsNoEof(u1, 1)) {} + + dumpFseTable("huffman", entries[0..table_size]); + + var i: usize = 0; + var even_state: u32 = try huff_bits.readBitsNoEof(u32, accuracy_log); + var odd_state: u32 = try huff_bits.readBitsNoEof(u32, accuracy_log); + + while (i < 255) { + const even_data = entries[even_state]; + var read_bits: usize = 0; + const even_bits = try huff_bits.readBits(u32, even_data.bits, &read_bits); + weights[i] = @intCast(u4, even_data.symbol); + i += 1; + if (read_bits < even_data.bits) { + weights[i] = @intCast(u4, entries[odd_state].symbol); + log.debug("overflow condition: setting weights[{d}] = {d}", .{ i, weights[i] }); + i += 1; + break; + } + even_state = even_data.baseline + even_bits; + + read_bits = 0; + const odd_data = entries[odd_state]; + const odd_bits = try huff_bits.readBits(u32, odd_data.bits, &read_bits); + weights[i] = @intCast(u4, odd_data.symbol); + i += 1; + if (read_bits < odd_data.bits) { + if (i == 256) return error.MalformedHuffmanTree; + weights[i] = @intCast(u4, entries[even_state].symbol); + log.debug("overflow condition: setting weights[{d}] = {d}", .{ i, weights[i] }); + i += 1; + break; + } + odd_state = odd_data.baseline + odd_bits; + } else return error.MalformedHuffmanTree; + + symbol_count = i + 1; // stream contains all but the last symbol + bytes_read += compressed_size; + } else { + const encoded_symbol_count = header - 127; + symbol_count = encoded_symbol_count + 1; + log.debug("huffman tree symbol count = {d}", .{symbol_count}); + const weights_byte_count = (encoded_symbol_count + 1) / 2; + log.debug("decoding direct huffman tree: {}|{}", .{ + std.fmt.fmtSliceHexUpper(src[0..1]), + std.fmt.fmtSliceHexUpper(src[1 .. weights_byte_count + 1]), + }); + if (src.len < weights_byte_count) return error.MalformedHuffmanTree; + var i: usize = 0; + while (i < weights_byte_count) : (i += 1) { + weights[2 * i] = @intCast(u4, src[i + 1] >> 4); + weights[2 * i + 1] = @intCast(u4, src[i + 1] & 0xF); + log.debug("weights[{d}] = {d}", .{ 2 * i, weights[2 * i] }); + log.debug("weights[{d}] = {d}", .{ 2 * i + 1, weights[2 * i + 1] }); + } + bytes_read += weights_byte_count; + } + var weight_power_sum: u16 = 0; + for (weights[0 .. symbol_count - 1]) |value| { + if (value > 0) { + weight_power_sum += @as(u16, 1) << (value - 1); + } + } + log.debug("weight power sum = {d}", .{weight_power_sum}); + + // advance to next power of two (even if weight_power_sum is a power of 2) + max_number_of_bits = @intCast(u4, std.math.log2_int(u16, weight_power_sum) + 1); + const next_power_of_two = @as(u16, 1) << max_number_of_bits; + weights[symbol_count - 1] = @intCast(u4, std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1); + log.debug("weights[{d}] = {d}", .{ symbol_count - 1, weights[symbol_count - 1] }); + + var weight_sorted_prefixed_symbols: [256]Literals.HuffmanTree.PrefixedSymbol = undefined; + for (weight_sorted_prefixed_symbols[0..symbol_count]) |_, i| { + weight_sorted_prefixed_symbols[i] = .{ + .symbol = @intCast(u8, i), + .weight = undefined, + .prefix = undefined, + }; + } + + std.sort.sort( + Literals.HuffmanTree.PrefixedSymbol, + weight_sorted_prefixed_symbols[0..symbol_count], + weights, + lessThanByWeight, + ); + + var prefix: u16 = 0; + var prefixed_symbol_count: usize = 0; + var sorted_index: usize = 0; + while (sorted_index < symbol_count) { + var symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; + const weight = weights[symbol]; + if (weight == 0) { + sorted_index += 1; + continue; + } + + while (sorted_index < symbol_count) : ({ + sorted_index += 1; + prefixed_symbol_count += 1; + prefix += 1; + }) { + symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; + if (weights[symbol] != weight) { + prefix = ((prefix - 1) >> (weights[symbol] - weight)) + 1; + break; + } + weight_sorted_prefixed_symbols[prefixed_symbol_count].symbol = symbol; + weight_sorted_prefixed_symbols[prefixed_symbol_count].prefix = prefix; + weight_sorted_prefixed_symbols[prefixed_symbol_count].weight = weight; + } + } + consumed_count.* += bytes_read; + const tree = Literals.HuffmanTree{ + .max_bit_count = max_number_of_bits, + .symbol_count_minus_one = @intCast(u8, prefixed_symbol_count - 1), + .nodes = weight_sorted_prefixed_symbols, + }; + log.debug("decoded huffman tree {}:", .{std.fmt.fmtSliceHexUpper(src[0..bytes_read])}); + return tree; +} + +fn lessThanByWeight( + weights: [256]u4, + lhs: Literals.HuffmanTree.PrefixedSymbol, + rhs: Literals.HuffmanTree.PrefixedSymbol, +) bool { + // NOTE: this function relies on the use of a stable sorting algorithm, + // otherwise a special case of if (weights[lhs] == weights[rhs]) return lhs < rhs; + // should be added + return weights[lhs.symbol] < weights[rhs.symbol]; +} + +pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) Literals.Header { + // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) + const start = consumed_count.*; + const byte0 = src[0]; + const block_type = @intToEnum(Literals.BlockType, byte0 & 0b11); + const size_format = @intCast(u2, (byte0 & 0b1100) >> 2); + var regenerated_size: u20 = undefined; + var compressed_size: ?u18 = null; + switch (block_type) { + .raw, .rle => { + switch (size_format) { + 0, 2 => { + regenerated_size = byte0 >> 3; + consumed_count.* += 1; + }, + 1 => { + regenerated_size = (byte0 >> 4) + + (@as(u20, src[consumed_count.* + 1]) << 4); + consumed_count.* += 2; + }, + 3 => { + regenerated_size = (byte0 >> 4) + + (@as(u20, src[consumed_count.* + 1]) << 4) + + (@as(u20, src[consumed_count.* + 2]) << 12); + consumed_count.* += 3; + }, + } + }, + .compressed, .treeless => { + const byte1 = src[1]; + const byte2 = src[2]; + switch (size_format) { + 0, 1 => { + regenerated_size = (byte0 >> 4) + ((@as(u20, byte1) & 0b00111111) << 4); + compressed_size = ((byte1 & 0b11000000) >> 6) + (@as(u18, byte2) << 2); + consumed_count.* += 3; + }, + 2 => { + const byte3 = src[3]; + regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00000011) << 12); + compressed_size = ((byte2 & 0b11111100) >> 2) + (@as(u18, byte3) << 6); + consumed_count.* += 4; + }, + 3 => { + const byte3 = src[3]; + const byte4 = src[4]; + regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00111111) << 12); + compressed_size = ((byte2 & 0b11000000) >> 6) + (@as(u18, byte3) << 2) + (@as(u18, byte4) << 10); + consumed_count.* += 5; + }, + } + }, + } + log.debug( + "decoded literals section header '{}': type = {s}, size_format = {}, regen_size = {d}, compressed size = {?d}", + .{ + std.fmt.fmtSliceHexUpper(src[0 .. consumed_count.* - start]), + @tagName(block_type), + size_format, + regenerated_size, + compressed_size, + }, + ); + return Literals.Header{ + .block_type = block_type, + .size_format = size_format, + .regenerated_size = regenerated_size, + .compressed_size = compressed_size, + }; +} + +fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences.Header { + var sequence_count: u24 = undefined; + + var bytes_read: usize = 0; + const byte0 = src[0]; + if (byte0 == 0) { + bytes_read += 1; + log.debug("decoded sequences header '{}': sequence count = 0", .{std.fmt.fmtSliceHexUpper(src[0..bytes_read])}); + consumed_count.* += bytes_read; + return Sequences.Header{ + .sequence_count = 0, + .offsets = undefined, + .match_lengths = undefined, + .literal_lengths = undefined, + }; + } else if (byte0 < 128) { + sequence_count = byte0; + bytes_read += 1; + } else if (byte0 < 255) { + sequence_count = (@as(u24, (byte0 - 128)) << 8) + src[1]; + bytes_read += 2; + } else { + sequence_count = src[1] + (@as(u24, src[2]) << 8) + 0x7F00; + bytes_read += 3; + } + + const compression_modes = src[bytes_read]; + bytes_read += 1; + + consumed_count.* += bytes_read; + const matches_mode = @intToEnum(Sequences.Header.Mode, (compression_modes & 0b00001100) >> 2); + const offsets_mode = @intToEnum(Sequences.Header.Mode, (compression_modes & 0b00110000) >> 4); + const literal_mode = @intToEnum(Sequences.Header.Mode, (compression_modes & 0b11000000) >> 6); + log.debug("decoded sequences header '{}': (sc={d},o={s},m={s},l={s})", .{ + std.fmt.fmtSliceHexUpper(src[0..bytes_read]), + sequence_count, + @tagName(offsets_mode), + @tagName(matches_mode), + @tagName(literal_mode), + }); + if (compression_modes & 0b11 != 0) return error.ReservedBitSet; + + return Sequences.Header{ + .sequence_count = sequence_count, + .offsets = offsets_mode, + .match_lengths = matches_mode, + .literal_lengths = literal_mode, + }; +} + +fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { + const total_probability = @intCast(u16, entries.len); + const accuracy_log = std.math.log2_int(u16, total_probability); + assert(total_probability <= 1 << 9); + + var less_than_one_count: usize = 0; + for (values) |value, i| { + if (value == 0) { + entries[entries.len - 1 - less_than_one_count] = Table.Fse{ + .symbol = @intCast(u8, i), + .baseline = 0, + .bits = accuracy_log, + }; + less_than_one_count += 1; + } + } + + var position: usize = 0; + var temp_states: [1 << 9]u16 = undefined; + for (values) |value, symbol| { + if (value == 0 or value == 1) continue; + const probability = value - 1; + + const state_share_dividend = try std.math.ceilPowerOfTwo(u16, probability); + const share_size = @divExact(total_probability, state_share_dividend); + const double_state_count = state_share_dividend - probability; + const single_state_count = probability - double_state_count; + const share_size_log = std.math.log2_int(u16, share_size); + + var i: u16 = 0; + while (i < probability) : (i += 1) { + temp_states[i] = @intCast(u16, position); + position += (entries.len >> 1) + (entries.len >> 3) + 3; + position &= entries.len - 1; + while (position >= entries.len - less_than_one_count) { + position += (entries.len >> 1) + (entries.len >> 3) + 3; + position &= entries.len - 1; + } + } + std.sort.sort(u16, temp_states[0..probability], {}, std.sort.asc(u16)); + i = 0; + while (i < probability) : (i += 1) { + entries[temp_states[i]] = if (i < double_state_count) Table.Fse{ + .symbol = @intCast(u8, symbol), + .bits = share_size_log + 1, + .baseline = single_state_count * share_size + i * 2 * share_size, + } else Table.Fse{ + .symbol = @intCast(u8, symbol), + .bits = share_size_log, + .baseline = (i - double_state_count) * share_size, + }; + } + } +} + +fn decodeFseTable( + bit_reader: anytype, + expected_symbol_count: usize, + max_accuracy_log: u4, + entries: []Table.Fse, +) !usize { + log.debug("decoding fse table {d} {d}", .{ max_accuracy_log, expected_symbol_count }); + + const accuracy_log_biased = try bit_reader.readBitsNoEof(u4, 4); + log.debug("accuracy_log_biased = {d}", .{accuracy_log_biased}); + if (accuracy_log_biased > max_accuracy_log -| 5) return error.MalformedAccuracyLog; + const accuracy_log = accuracy_log_biased + 5; + + var values: [256]u16 = undefined; + var value_count: usize = 0; + + const total_probability = @as(u16, 1) << accuracy_log; + log.debug("total probability = {d}", .{total_probability}); + var accumulated_probability: u16 = 0; + + while (accumulated_probability < total_probability) { + // WARNING: The RFC in poorly worded, and would suggest std.math.log2_int_ceil is correct here, + // but power of two (remaining probabilities + 1) need max bits set to 1 more. + const max_bits = @intCast(u4, std.math.log2_int(u16, total_probability - accumulated_probability + 1)) + 1; + const small = try bit_reader.readBitsNoEof(u16, max_bits - 1); + + const cutoff = (@as(u16, 1) << max_bits) - 1 - (total_probability - accumulated_probability + 1); + + const value = if (small < cutoff) + small + else value: { + const value_read = small + (try bit_reader.readBitsNoEof(u16, 1) << (max_bits - 1)); + break :value if (value_read < @as(u16, 1) << (max_bits - 1)) + value_read + else + value_read - cutoff; + }; + + accumulated_probability += if (value != 0) value - 1 else 1; + + values[value_count] = value; + value_count += 1; + + if (value == 1) { + while (true) { + const repeat_flag = try bit_reader.readBitsNoEof(u2, 2); + var i: usize = 0; + while (i < repeat_flag) : (i += 1) { + values[value_count] = 1; + value_count += 1; + } + if (repeat_flag < 3) break; + } + } + } + bit_reader.alignToByte(); + + // TODO: check there are at least 2 non-zero probabilities + + if (accumulated_probability != total_probability) return error.MalformedFseTable; + if (value_count > expected_symbol_count) return error.MalformedFseTable; + + const table_size = total_probability; + + try buildFseTable(values[0..value_count], entries[0..table_size]); + return table_size; +} + +const ReversedByteReader = struct { + remaining_bytes: usize, + bytes: []const u8, + + const Reader = std.io.Reader(*ReversedByteReader, error{}, readFn); + + fn reader(self: *ReversedByteReader) Reader { + return .{ .context = self }; + } +}; + +fn readFn(ctx: *ReversedByteReader, buffer: []u8) !usize { + if (ctx.remaining_bytes == 0) return 0; + const byte_index = ctx.remaining_bytes - 1; + buffer[0] = ctx.bytes[byte_index]; + // buffer[0] = @bitReverse(ctx.bytes[byte_index]); + ctx.remaining_bytes = byte_index; + return 1; +} + +fn reversedByteReader(bytes: []const u8) ReversedByteReader { + return ReversedByteReader{ + .remaining_bytes = bytes.len, + .bytes = bytes, + }; +} + +fn ReverseBitReader(comptime Reader: type) type { + return struct { + underlying: std.io.BitReader(.Big, Reader), + + fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { + return self.underlying.readBitsNoEof(U, num_bits); + } + + fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { + return try self.underlying.readBits(U, num_bits, out_bits); + } + + fn alignToByte(self: *@This()) void { + self.underlying.alignToByte(); + } + }; +} + +fn reverseBitReader(reader: anytype) ReverseBitReader(@TypeOf(reader)) { + return .{ .underlying = std.io.bitReader(.Big, reader) }; +} + +fn BitReader(comptime Reader: type) type { + return struct { + underlying: std.io.BitReader(.Little, Reader), + + fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { + return self.underlying.readBitsNoEof(U, num_bits); + } + + fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { + return self.underlying.readBits(U, num_bits, out_bits); + } + + fn alignToByte(self: *@This()) void { + self.underlying.alignToByte(); + } + }; +} + +fn bitReader(reader: anytype) BitReader(@TypeOf(reader)) { + return .{ .underlying = std.io.bitReader(.Little, reader) }; +} + +test { + std.testing.refAllDecls(@This()); +} + +test buildFseTable { + const literals_length_default_values = [36]u16{ + 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 2, 2, 2, + 0, 0, 0, 0, + }; + + const match_lengths_default_values = [53]u16{ + 2, 5, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 0, + }; + + const offset_codes_default_values = [29]u16{ + 2, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, + }; + + var entries: [64]Table.Fse = undefined; + try buildFseTable(&literals_length_default_values, &entries); + try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_literal_fse_table.fse, &entries); + + try buildFseTable(&match_lengths_default_values, &entries); + try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_match_fse_table.fse, &entries); + + try buildFseTable(&offset_codes_default_values, entries[0..32]); + try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_offset_fse_table.fse, entries[0..32]); +} + +fn dumpFseTable(prefix: []const u8, table: []const Table.Fse) void { + log.debug("{s} fse table:", .{prefix}); + for (table) |entry, i| { + log.debug("state = {d} symbol = {d} bl = {d}, bits = {d}", .{ i, entry.symbol, entry.baseline, entry.bits }); + } +} + +fn dumpHuffmanTree(tree: Literals.HuffmanTree) void { + log.debug("Huffman tree: max bit count = {}, symbol count = {}", .{ tree.max_bit_count, tree.symbol_count_minus_one + 1 }); + for (tree.nodes[0 .. tree.symbol_count_minus_one + 1]) |node| { + log.debug("symbol = {[symbol]d}, prefix = {[prefix]d}, weight = {[weight]d}", node); + } +} diff --git a/lib/std/compress/zstandard/types.zig b/lib/std/compress/zstandard/types.zig new file mode 100644 index 0000000000..edac66f686 --- /dev/null +++ b/lib/std/compress/zstandard/types.zig @@ -0,0 +1,394 @@ +pub const frame = struct { + pub const Kind = enum { zstandard, skippable }; + + pub const ZStandard = struct { + pub const magic_number = 0xFD2FB528; + + header: Header, + data_blocks: []Block, + checksum: ?u32, + + pub const Header = struct { + descriptor: Descriptor, + window_descriptor: ?u8, + dictionary_id: ?u32, + content_size: ?u64, + + pub const Descriptor = packed struct { + dictionary_id_flag: u2, + content_checksum_flag: bool, + reserved: bool, + unused: bool, + single_segment_flag: bool, + content_size_flag: u2, + }; + }; + + pub const Block = struct { + pub const Header = struct { + last_block: bool, + block_type: Block.Type, + block_size: u21, + }; + + pub const Type = enum(u2) { + raw, + rle, + compressed, + reserved, + }; + }; + }; + + pub const Skippable = struct { + pub const magic_number_min = 0x184D2A50; + pub const magic_number_max = 0x184D2A5F; + + pub const Header = struct { + magic_number: u32, + frame_size: u32, + }; + }; +}; + +pub const compressed_block = struct { + pub const Literals = struct { + header: Header, + huffman_tree: ?HuffmanTree, + streams: Streams, + + pub const Streams = union(enum) { + one: []const u8, + four: [4][]const u8, + }; + + pub const Header = struct { + block_type: BlockType, + size_format: u2, + regenerated_size: u20, + compressed_size: ?u18, + }; + + pub const BlockType = enum(u2) { + raw, + rle, + compressed, + treeless, + }; + + pub const HuffmanTree = struct { + max_bit_count: u4, + symbol_count_minus_one: u8, + nodes: [256]PrefixedSymbol, + + pub const PrefixedSymbol = struct { + symbol: u8, + prefix: u16, + weight: u4, + }; + + pub const Result = union(enum) { + symbol: u8, + index: usize, + }; + + pub fn query(self: HuffmanTree, index: usize, prefix: u16) !Result { + var node = self.nodes[index]; + const weight = node.weight; + var i: usize = index; + while (node.weight == weight) { + if (node.prefix == prefix) return Result{ .symbol = node.symbol }; + if (i == 0) return error.PrefixNotFound; + i -= 1; + node = self.nodes[i]; + } + return Result{ .index = i }; + } + + pub fn weightToBitCount(weight: u4, max_bit_count: u4) u4 { + return if (weight == 0) 0 else ((max_bit_count + 1) - weight); + } + }; + + pub const StreamCount = enum { one, four }; + pub fn streamCount(size_format: u2, block_type: BlockType) StreamCount { + return switch (block_type) { + .raw, .rle => .one, + .compressed, .treeless => if (size_format == 0) .one else .four, + }; + } + }; + + pub const Sequences = struct { + header: Sequences.Header, + literals_length_table: Table, + offset_table: Table, + match_length_table: Table, + + pub const Header = struct { + sequence_count: u24, + match_lengths: Mode, + offsets: Mode, + literal_lengths: Mode, + + pub const Mode = enum(u2) { + predefined, + rle, + fse, + repeat, + }; + }; + }; + + pub const Table = union(enum) { + fse: []const Fse, + rle: u8, + + pub const Fse = struct { + symbol: u8, + baseline: u16, + bits: u8, + }; + }; + + pub const literals_length_code_table = [36]struct { u32, u5 }{ + .{ 0, 0 }, .{ 1, 0 }, .{ 2, 0 }, .{ 3, 0 }, + .{ 4, 0 }, .{ 5, 0 }, .{ 6, 0 }, .{ 7, 0 }, + .{ 8, 0 }, .{ 9, 0 }, .{ 10, 0 }, .{ 11, 0 }, + .{ 12, 0 }, .{ 13, 0 }, .{ 14, 0 }, .{ 15, 0 }, + .{ 16, 1 }, .{ 18, 1 }, .{ 20, 1 }, .{ 22, 1 }, + .{ 24, 2 }, .{ 28, 2 }, .{ 32, 3 }, .{ 40, 3 }, + .{ 48, 4 }, .{ 64, 6 }, .{ 128, 7 }, .{ 256, 8 }, + .{ 512, 9 }, .{ 1024, 10 }, .{ 2048, 11 }, .{ 4096, 12 }, + .{ 8192, 13 }, .{ 16384, 14 }, .{ 32768, 15 }, .{ 65536, 16 }, + }; + + pub const match_length_code_table = [53]struct { u32, u5 }{ + .{ 3, 0 }, .{ 4, 0 }, .{ 5, 0 }, .{ 6, 0 }, .{ 7, 0 }, .{ 8, 0 }, .{ 9, 0 }, .{ 10, 0 }, + .{ 11, 0 }, .{ 12, 0 }, .{ 13, 0 }, .{ 14, 0 }, .{ 15, 0 }, .{ 16, 0 }, .{ 17, 0 }, .{ 18, 0 }, + .{ 19, 0 }, .{ 20, 0 }, .{ 21, 0 }, .{ 22, 0 }, .{ 23, 0 }, .{ 24, 0 }, .{ 25, 0 }, .{ 26, 0 }, + .{ 27, 0 }, .{ 28, 0 }, .{ 29, 0 }, .{ 30, 0 }, .{ 31, 0 }, .{ 32, 0 }, .{ 33, 0 }, .{ 34, 0 }, + .{ 35, 1 }, .{ 37, 1 }, .{ 39, 1 }, .{ 41, 1 }, .{ 43, 2 }, .{ 47, 2 }, .{ 51, 3 }, .{ 59, 3 }, + .{ 67, 4 }, .{ 83, 4 }, .{ 99, 5 }, .{ 131, 7 }, .{ 259, 8 }, .{ 515, 9 }, .{ 1027, 10 }, .{ 2051, 11 }, + .{ 4099, 12 }, .{ 8195, 13 }, .{ 16387, 14 }, .{ 32771, 15 }, .{ 65539, 16 }, + }; + + pub const literals_length_default_distribution = [36]i16{ + 4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, + -1, -1, -1, -1, + }; + + pub const match_lengths_default_distribution = [53]i16{ + 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, + -1, -1, -1, -1, -1, + }; + + pub const offset_codes_default_distribution = [29]i16{ + 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + }; + + pub const predefined_literal_fse_table = Table{ + .fse = &[64]Table.Fse{ + .{ .symbol = 0, .bits = 4, .baseline = 0 }, + .{ .symbol = 0, .bits = 4, .baseline = 16 }, + .{ .symbol = 1, .bits = 5, .baseline = 32 }, + .{ .symbol = 3, .bits = 5, .baseline = 0 }, + .{ .symbol = 4, .bits = 5, .baseline = 0 }, + .{ .symbol = 6, .bits = 5, .baseline = 0 }, + .{ .symbol = 7, .bits = 5, .baseline = 0 }, + .{ .symbol = 9, .bits = 5, .baseline = 0 }, + .{ .symbol = 10, .bits = 5, .baseline = 0 }, + .{ .symbol = 12, .bits = 5, .baseline = 0 }, + .{ .symbol = 14, .bits = 6, .baseline = 0 }, + .{ .symbol = 16, .bits = 5, .baseline = 0 }, + .{ .symbol = 18, .bits = 5, .baseline = 0 }, + .{ .symbol = 19, .bits = 5, .baseline = 0 }, + .{ .symbol = 21, .bits = 5, .baseline = 0 }, + .{ .symbol = 22, .bits = 5, .baseline = 0 }, + .{ .symbol = 24, .bits = 5, .baseline = 0 }, + .{ .symbol = 25, .bits = 5, .baseline = 32 }, + .{ .symbol = 26, .bits = 5, .baseline = 0 }, + .{ .symbol = 27, .bits = 6, .baseline = 0 }, + .{ .symbol = 29, .bits = 6, .baseline = 0 }, + .{ .symbol = 31, .bits = 6, .baseline = 0 }, + .{ .symbol = 0, .bits = 4, .baseline = 32 }, + .{ .symbol = 1, .bits = 4, .baseline = 0 }, + .{ .symbol = 2, .bits = 5, .baseline = 0 }, + .{ .symbol = 4, .bits = 5, .baseline = 32 }, + .{ .symbol = 5, .bits = 5, .baseline = 0 }, + .{ .symbol = 7, .bits = 5, .baseline = 32 }, + .{ .symbol = 8, .bits = 5, .baseline = 0 }, + .{ .symbol = 10, .bits = 5, .baseline = 32 }, + .{ .symbol = 11, .bits = 5, .baseline = 0 }, + .{ .symbol = 13, .bits = 6, .baseline = 0 }, + .{ .symbol = 16, .bits = 5, .baseline = 32 }, + .{ .symbol = 17, .bits = 5, .baseline = 0 }, + .{ .symbol = 19, .bits = 5, .baseline = 32 }, + .{ .symbol = 20, .bits = 5, .baseline = 0 }, + .{ .symbol = 22, .bits = 5, .baseline = 32 }, + .{ .symbol = 23, .bits = 5, .baseline = 0 }, + .{ .symbol = 25, .bits = 4, .baseline = 0 }, + .{ .symbol = 25, .bits = 4, .baseline = 16 }, + .{ .symbol = 26, .bits = 5, .baseline = 32 }, + .{ .symbol = 28, .bits = 6, .baseline = 0 }, + .{ .symbol = 30, .bits = 6, .baseline = 0 }, + .{ .symbol = 0, .bits = 4, .baseline = 48 }, + .{ .symbol = 1, .bits = 4, .baseline = 16 }, + .{ .symbol = 2, .bits = 5, .baseline = 32 }, + .{ .symbol = 3, .bits = 5, .baseline = 32 }, + .{ .symbol = 5, .bits = 5, .baseline = 32 }, + .{ .symbol = 6, .bits = 5, .baseline = 32 }, + .{ .symbol = 8, .bits = 5, .baseline = 32 }, + .{ .symbol = 9, .bits = 5, .baseline = 32 }, + .{ .symbol = 11, .bits = 5, .baseline = 32 }, + .{ .symbol = 12, .bits = 5, .baseline = 32 }, + .{ .symbol = 15, .bits = 6, .baseline = 0 }, + .{ .symbol = 17, .bits = 5, .baseline = 32 }, + .{ .symbol = 18, .bits = 5, .baseline = 32 }, + .{ .symbol = 20, .bits = 5, .baseline = 32 }, + .{ .symbol = 21, .bits = 5, .baseline = 32 }, + .{ .symbol = 23, .bits = 5, .baseline = 32 }, + .{ .symbol = 24, .bits = 5, .baseline = 32 }, + .{ .symbol = 35, .bits = 6, .baseline = 0 }, + .{ .symbol = 34, .bits = 6, .baseline = 0 }, + .{ .symbol = 33, .bits = 6, .baseline = 0 }, + .{ .symbol = 32, .bits = 6, .baseline = 0 }, + }, + }; + + pub const predefined_match_fse_table = Table{ + .fse = &[64]Table.Fse{ + .{ .symbol = 0, .bits = 6, .baseline = 0 }, + .{ .symbol = 1, .bits = 4, .baseline = 0 }, + .{ .symbol = 2, .bits = 5, .baseline = 32 }, + .{ .symbol = 3, .bits = 5, .baseline = 0 }, + .{ .symbol = 5, .bits = 5, .baseline = 0 }, + .{ .symbol = 6, .bits = 5, .baseline = 0 }, + .{ .symbol = 8, .bits = 5, .baseline = 0 }, + .{ .symbol = 10, .bits = 6, .baseline = 0 }, + .{ .symbol = 13, .bits = 6, .baseline = 0 }, + .{ .symbol = 16, .bits = 6, .baseline = 0 }, + .{ .symbol = 19, .bits = 6, .baseline = 0 }, + .{ .symbol = 22, .bits = 6, .baseline = 0 }, + .{ .symbol = 25, .bits = 6, .baseline = 0 }, + .{ .symbol = 28, .bits = 6, .baseline = 0 }, + .{ .symbol = 31, .bits = 6, .baseline = 0 }, + .{ .symbol = 33, .bits = 6, .baseline = 0 }, + .{ .symbol = 35, .bits = 6, .baseline = 0 }, + .{ .symbol = 37, .bits = 6, .baseline = 0 }, + .{ .symbol = 39, .bits = 6, .baseline = 0 }, + .{ .symbol = 41, .bits = 6, .baseline = 0 }, + .{ .symbol = 43, .bits = 6, .baseline = 0 }, + .{ .symbol = 45, .bits = 6, .baseline = 0 }, + .{ .symbol = 1, .bits = 4, .baseline = 16 }, + .{ .symbol = 2, .bits = 4, .baseline = 0 }, + .{ .symbol = 3, .bits = 5, .baseline = 32 }, + .{ .symbol = 4, .bits = 5, .baseline = 0 }, + .{ .symbol = 6, .bits = 5, .baseline = 32 }, + .{ .symbol = 7, .bits = 5, .baseline = 0 }, + .{ .symbol = 9, .bits = 6, .baseline = 0 }, + .{ .symbol = 12, .bits = 6, .baseline = 0 }, + .{ .symbol = 15, .bits = 6, .baseline = 0 }, + .{ .symbol = 18, .bits = 6, .baseline = 0 }, + .{ .symbol = 21, .bits = 6, .baseline = 0 }, + .{ .symbol = 24, .bits = 6, .baseline = 0 }, + .{ .symbol = 27, .bits = 6, .baseline = 0 }, + .{ .symbol = 30, .bits = 6, .baseline = 0 }, + .{ .symbol = 32, .bits = 6, .baseline = 0 }, + .{ .symbol = 34, .bits = 6, .baseline = 0 }, + .{ .symbol = 36, .bits = 6, .baseline = 0 }, + .{ .symbol = 38, .bits = 6, .baseline = 0 }, + .{ .symbol = 40, .bits = 6, .baseline = 0 }, + .{ .symbol = 42, .bits = 6, .baseline = 0 }, + .{ .symbol = 44, .bits = 6, .baseline = 0 }, + .{ .symbol = 1, .bits = 4, .baseline = 32 }, + .{ .symbol = 1, .bits = 4, .baseline = 48 }, + .{ .symbol = 2, .bits = 4, .baseline = 16 }, + .{ .symbol = 4, .bits = 5, .baseline = 32 }, + .{ .symbol = 5, .bits = 5, .baseline = 32 }, + .{ .symbol = 7, .bits = 5, .baseline = 32 }, + .{ .symbol = 8, .bits = 5, .baseline = 32 }, + .{ .symbol = 11, .bits = 6, .baseline = 0 }, + .{ .symbol = 14, .bits = 6, .baseline = 0 }, + .{ .symbol = 17, .bits = 6, .baseline = 0 }, + .{ .symbol = 20, .bits = 6, .baseline = 0 }, + .{ .symbol = 23, .bits = 6, .baseline = 0 }, + .{ .symbol = 26, .bits = 6, .baseline = 0 }, + .{ .symbol = 29, .bits = 6, .baseline = 0 }, + .{ .symbol = 52, .bits = 6, .baseline = 0 }, + .{ .symbol = 51, .bits = 6, .baseline = 0 }, + .{ .symbol = 50, .bits = 6, .baseline = 0 }, + .{ .symbol = 49, .bits = 6, .baseline = 0 }, + .{ .symbol = 48, .bits = 6, .baseline = 0 }, + .{ .symbol = 47, .bits = 6, .baseline = 0 }, + .{ .symbol = 46, .bits = 6, .baseline = 0 }, + }, + }; + + pub const predefined_offset_fse_table = Table{ + .fse = &[32]Table.Fse{ + .{ .symbol = 0, .bits = 5, .baseline = 0 }, + .{ .symbol = 6, .bits = 4, .baseline = 0 }, + .{ .symbol = 9, .bits = 5, .baseline = 0 }, + .{ .symbol = 15, .bits = 5, .baseline = 0 }, + .{ .symbol = 21, .bits = 5, .baseline = 0 }, + .{ .symbol = 3, .bits = 5, .baseline = 0 }, + .{ .symbol = 7, .bits = 4, .baseline = 0 }, + .{ .symbol = 12, .bits = 5, .baseline = 0 }, + .{ .symbol = 18, .bits = 5, .baseline = 0 }, + .{ .symbol = 23, .bits = 5, .baseline = 0 }, + .{ .symbol = 5, .bits = 5, .baseline = 0 }, + .{ .symbol = 8, .bits = 4, .baseline = 0 }, + .{ .symbol = 14, .bits = 5, .baseline = 0 }, + .{ .symbol = 20, .bits = 5, .baseline = 0 }, + .{ .symbol = 2, .bits = 5, .baseline = 0 }, + .{ .symbol = 7, .bits = 4, .baseline = 16 }, + .{ .symbol = 11, .bits = 5, .baseline = 0 }, + .{ .symbol = 17, .bits = 5, .baseline = 0 }, + .{ .symbol = 22, .bits = 5, .baseline = 0 }, + .{ .symbol = 4, .bits = 5, .baseline = 0 }, + .{ .symbol = 8, .bits = 4, .baseline = 16 }, + .{ .symbol = 13, .bits = 5, .baseline = 0 }, + .{ .symbol = 19, .bits = 5, .baseline = 0 }, + .{ .symbol = 1, .bits = 5, .baseline = 0 }, + .{ .symbol = 6, .bits = 4, .baseline = 16 }, + .{ .symbol = 10, .bits = 5, .baseline = 0 }, + .{ .symbol = 16, .bits = 5, .baseline = 0 }, + .{ .symbol = 28, .bits = 5, .baseline = 0 }, + .{ .symbol = 27, .bits = 5, .baseline = 0 }, + .{ .symbol = 26, .bits = 5, .baseline = 0 }, + .{ .symbol = 25, .bits = 5, .baseline = 0 }, + .{ .symbol = 24, .bits = 5, .baseline = 0 }, + }, + }; + pub const start_repeated_offset_1 = 1; + pub const start_repeated_offset_2 = 4; + pub const start_repeated_offset_3 = 8; + + pub const table_accuracy_log_max = struct { + pub const literal = 9; + pub const match = 9; + pub const offset = 8; + }; + + pub const table_symbol_count_max = struct { + pub const literal = 36; + pub const match = 53; + pub const offset = 32; + }; + + pub const default_accuracy_log = struct { + pub const literal = 6; + pub const match = 6; + pub const offset = 5; + }; +}; + +test { + const testing = @import("std").testing; + testing.refAllDeclsRecursive(@This()); +} From 18091723d5afa8001e0fd71274dc4b74d601d0e1 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 22 Jan 2023 13:32:16 +1100 Subject: [PATCH 012/122] std.compress.zstandard: cleanup decodeBlock --- lib/std/compress/zstandard/decompress.zig | 148 ++++++++++++---------- 1 file changed, 78 insertions(+), 70 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 37fe722a84..9483b4d9d7 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -65,12 +65,14 @@ const DecodeState = struct { match_fse_buffer: []Table.Fse, literal_fse_buffer: []Table.Fse, - literal_written_count: usize, + fse_tables_undefined: bool, literal_stream_reader: ReverseBitReader(ReversedByteReader.Reader), literal_stream_bytes: ReversedByteReader, literal_stream_index: usize, - huffman_tree: Literals.HuffmanTree, + huffman_tree: ?Literals.HuffmanTree, + + literal_written_count: usize, fn StateData(comptime max_accuracy_log: comptime_int) type { return struct { @@ -129,7 +131,6 @@ const DecodeState = struct { src: []const u8, comptime choice: DataType, mode: Sequences.Header.Mode, - first_compressed_block: bool, ) !usize { const field_name = @tagName(choice); switch (mode) { @@ -162,7 +163,7 @@ const DecodeState = struct { dumpFseTable(field_name, @field(self, field_name).table.fse); return counting_reader.bytes_read; }, - .repeat => return if (first_compressed_block) error.RepeatModeFirst else 0, + .repeat => return if (self.fse_tables_undefined) error.RepeatModeFirst else 0, } } @@ -275,7 +276,7 @@ const DecodeState = struct { }, .compressed, .treeless => { // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; - const huffman_tree = self.huffman_tree; + const huffman_tree = self.huffman_tree orelse unreachable; const max_bit_count = huffman_tree.max_bit_count; const starting_bit_count = Literals.HuffmanTree.weightToBitCount( huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, @@ -399,14 +400,14 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha .match_fse_buffer = &match_fse_data, .offset_fse_buffer = &offset_fse_data, + .fse_tables_undefined = true, + .literal_written_count = 0, .literal_stream_reader = undefined, .literal_stream_bytes = undefined, .literal_stream_index = undefined, - .huffman_tree = undefined, + .huffman_tree = null, }; - var first_compressed_block = true; - var first_compressed_literals = true; var written_count: usize = 0; while (true) : ({ block_header = decodeBlockHeader(src[bytes_read..][0..3]); @@ -417,8 +418,6 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha src[bytes_read..], block_header, &decode_state, - &first_compressed_block, - &first_compressed_literals, &bytes_read, written_count, ); @@ -430,13 +429,77 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha return written_count; } +fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) usize { + log.debug("writing raw block - size {d}", .{block_size}); + const data = src[0..block_size]; + std.mem.copy(u8, dest, data); + consumed_count.* += block_size; + return block_size; +} + +fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) usize { + log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); + var write_pos: usize = 0; + while (write_pos < block_size) : (write_pos += 1) { + dest[write_pos] = src[0]; + } + consumed_count.* += 1; + return block_size; +} + +fn prepareDecodeState( + decode_state: *DecodeState, + src: []const u8, + literals: Literals, + sequences_header: Sequences.Header, +) !usize { + if (literals.huffman_tree) |tree| { + decode_state.huffman_tree = tree; + } else if (literals.header.block_type == .treeless and decode_state.huffman_tree == null) { + return error.TreelessLiteralsFirst; + } + + switch (literals.header.block_type) { + .raw, .rle => {}, + .compressed, .treeless => { + decode_state.literal_stream_index = 0; + switch (literals.streams) { + .one => |slice| try decode_state.initLiteralStream(slice), + .four => |streams| try decode_state.initLiteralStream(streams[0]), + } + }, + } + + if (sequences_header.sequence_count > 0) { + var bytes_read = try decode_state.updateFseTable( + src, + .literal, + sequences_header.literal_lengths, + ); + + bytes_read += try decode_state.updateFseTable( + src[bytes_read..], + .offset, + sequences_header.offsets, + ); + + bytes_read += try decode_state.updateFseTable( + src[bytes_read..], + .match, + sequences_header.match_lengths, + ); + decode_state.fse_tables_undefined = false; + + return bytes_read; + } + return 0; +} + pub fn decodeBlock( dest: []u8, src: []const u8, block_header: frame.ZStandard.Block.Header, decode_state: *DecodeState, - first_compressed_block: *bool, - first_compressed_literals: *bool, consumed_count: *usize, written_count: usize, ) !usize { @@ -445,69 +508,14 @@ pub fn decodeBlock( if (block_maximum_size < block_size) return error.BlockSizeOverMaximum; // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) switch (block_header.block_type) { - .raw => { - log.debug("writing raw block - size {d}", .{block_size}); - const data = src[0..block_size]; - std.mem.copy(u8, dest[written_count..], data); - consumed_count.* += block_size; - return block_size; - }, - .rle => { - log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); - var write_pos: usize = written_count; - while (write_pos < block_size + written_count) : (write_pos += 1) { - dest[write_pos] = src[0]; - } - consumed_count.* += 1; - return block_size; - }, + .raw => return decodeRawBlock(dest[written_count..], src, block_size, consumed_count), + .rle => return decodeRleBlock(dest[written_count..], src, block_size, consumed_count), .compressed => { var bytes_read: usize = 0; const literals = try decodeLiteralsSection(src, &bytes_read); const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); - if (first_compressed_literals.* and literals.header.block_type == .treeless) - return error.TreelessLiteralsFirst; - - if (literals.huffman_tree) |tree| { - decode_state.huffman_tree = tree; - first_compressed_literals.* = false; - } - - switch (literals.header.block_type) { - .raw, .rle => {}, - .compressed, .treeless => { - decode_state.literal_stream_index = 0; - switch (literals.streams) { - .one => |slice| try decode_state.initLiteralStream(slice), - .four => |streams| try decode_state.initLiteralStream(streams[0]), - } - }, - } - - if (sequences_header.sequence_count > 0) { - bytes_read += try decode_state.updateFseTable( - src[bytes_read..], - .literal, - sequences_header.literal_lengths, - first_compressed_block.*, - ); - - bytes_read += try decode_state.updateFseTable( - src[bytes_read..], - .offset, - sequences_header.offsets, - first_compressed_block.*, - ); - - bytes_read += try decode_state.updateFseTable( - src[bytes_read..], - .match, - sequences_header.match_lengths, - first_compressed_block.*, - ); - first_compressed_block.* = false; - } + bytes_read += try prepareDecodeState(decode_state, src[bytes_read..], literals, sequences_header); var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { From 05e63f241edb2199e91ce29c488e104dfb826935 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 22 Jan 2023 16:11:47 +1100 Subject: [PATCH 013/122] std.compress.zstandard: add functions decoding into ring buffer This supports decoding frames that do not declare the content size or decoding in a streaming fashion. --- lib/std/compress/zstandard/RingBuffer.zig | 81 +++++++++ lib/std/compress/zstandard/decompress.zig | 194 +++++++++++++++++++++- 2 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 lib/std/compress/zstandard/RingBuffer.zig diff --git a/lib/std/compress/zstandard/RingBuffer.zig b/lib/std/compress/zstandard/RingBuffer.zig new file mode 100644 index 0000000000..1a369596cb --- /dev/null +++ b/lib/std/compress/zstandard/RingBuffer.zig @@ -0,0 +1,81 @@ +//! This ring buffer stores read and write indices while being able to utilise the full +//! backing slice by incrementing the indices modulo twice the slice's length and reducing +//! indices modulo the slice's length on slice access. This means that the bit of information +//! distinguishing whether the buffer is full or empty in an implementation utilising +//! and extra flag is stored in difference of the indices. + +const assert = @import("std").debug.assert; + +const RingBuffer = @This(); + +data: []u8, +read_index: usize, +write_index: usize, + +pub fn mask(self: RingBuffer, index: usize) usize { + return index % self.data.len; +} + +pub fn mask2(self: RingBuffer, index: usize) usize { + return index % (2 * self.data.len); +} + +pub fn write(self: *RingBuffer, byte: u8) !void { + if (self.isFull()) return error.Full; + self.writeAssumeCapacity(byte); +} + +pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { + self.data[self.mask(self.write_index)] = byte; + self.write_index = self.mask2(self.write_index + 1); +} + +pub fn writeSlice(self: *RingBuffer, bytes: []const u8) !void { + if (self.len() + bytes.len > self.data.len) return error.Full; + self.writeSliceAssumeCapacity(bytes); +} + +pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { + for (bytes) |b| self.writeAssumeCapacity(b); +} + +pub fn read(self: *RingBuffer) ?u8 { + if (self.isEmpty()) return null; + const byte = self.data[self.mask(self.read_index)]; + self.read_index = self.mask2(self.read_index + 1); + return byte; +} + +pub fn isEmpty(self: RingBuffer) bool { + return self.write_index == self.read_index; +} + +pub fn isFull(self: RingBuffer) bool { + return self.mask2(self.write_index + self.data.len) == self.read_index; +} + +pub fn len(self: RingBuffer) usize { + const adjusted_write_index = self.write_index + @boolToInt(self.write_index < self.read_index) * 2 * self.data.len; + return adjusted_write_index - self.read_index; +} + +const Slice = struct { + first: []u8, + second: []u8, +}; + +pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { + assert(length <= self.data.len); + const slice1_start = self.mask(start_unmasked); + const slice1_end = @min(self.data.len, slice1_start + length); + const slice1 = self.data[slice1_start..slice1_end]; + const slice2 = self.data[0 .. length - slice1.len]; + return Slice{ + .first = slice1, + .second = slice2, + }; +} + +pub fn sliceLast(self: RingBuffer, length: usize) Slice { + return self.sliceAt(self.write_index + self.data.len - length, length); +} diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 9483b4d9d7..6e107c2d7b 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -6,6 +6,7 @@ const frame = types.frame; const Literals = types.compressed_block.Literals; const Sequences = types.compressed_block.Sequences; const Table = types.compressed_block.Table; +const RingBuffer = @import("RingBuffer.zig"); const readInt = std.mem.readIntLittle; const readIntSlice = std.mem.readIntSliceLittle; @@ -214,7 +215,7 @@ const DecodeState = struct { } fn executeSequenceSlice(self: *DecodeState, dest: []u8, write_pos: usize, literals: Literals, sequence: Sequence) !void { - try self.decodeLiteralsInto(dest[write_pos..], literals, sequence.literal_length); + try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length); // TODO: should we validate offset against max_window_size? assert(sequence.offset <= write_pos + sequence.literal_length); @@ -225,6 +226,15 @@ const DecodeState = struct { std.mem.copy(u8, dest[write_pos + sequence.literal_length ..], dest[copy_start..copy_end]); } + fn executeSequenceRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: Literals, sequence: Sequence) !void { + try self.decodeLiteralsRingBuffer(dest, literals, sequence.literal_length); + // TODO: check that ring buffer window is full enough for match copies + const copy_slice = dest.sliceAt(dest.write_index + dest.data.len - sequence.offset, sequence.match_length); + // TODO: would std.mem.copy and figuring out dest slice be better/faster? + for (copy_slice.first) |b| dest.writeAssumeCapacity(b); + for (copy_slice.second) |b| dest.writeAssumeCapacity(b); + } + fn decodeSequenceSlice( self: *DecodeState, dest: []u8, @@ -246,6 +256,31 @@ const DecodeState = struct { return sequence.match_length + sequence.literal_length; } + fn decodeSequenceRingBuffer( + self: *DecodeState, + dest: *RingBuffer, + literals: Literals, + bit_reader: anytype, + last_sequence: bool, + ) !usize { + const sequence = try self.nextSequence(bit_reader); + try self.executeSequenceRingBuffer(dest, literals, sequence); + if (std.options.log_level == .debug) { + const sequence_length = sequence.literal_length + sequence.match_length; + const written_slice = dest.sliceLast(sequence_length); + log.debug("sequence decompressed into '{x}{x}'", .{ + std.fmt.fmtSliceHexUpper(written_slice.first), + std.fmt.fmtSliceHexUpper(written_slice.second), + }); + } + if (!last_sequence) { + try self.updateState(.literal, bit_reader); + try self.updateState(.match, bit_reader); + try self.updateState(.offset, bit_reader); + } + return sequence.match_length + sequence.literal_length; + } + fn nextLiteralMultiStream(self: *DecodeState, literals: Literals) !void { self.literal_stream_index += 1; try self.initLiteralStream(literals.streams.four[self.literal_stream_index]); @@ -258,7 +293,7 @@ const DecodeState = struct { while (0 == try self.literal_stream_reader.readBitsNoEof(u1, 1)) {} } - fn decodeLiteralsInto(self: *DecodeState, dest: []u8, literals: Literals, len: usize) !void { + fn decodeLiteralsSlice(self: *DecodeState, dest: []u8, literals: Literals, len: usize) !void { if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; switch (literals.header.block_type) { .raw => { @@ -327,6 +362,74 @@ const DecodeState = struct { } } + fn decodeLiteralsRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: Literals, len: usize) !void { + if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; + switch (literals.header.block_type) { + .raw => { + const literal_data = literals.streams.one[self.literal_written_count .. self.literal_written_count + len]; + dest.writeSliceAssumeCapacity(literal_data); + self.literal_written_count += len; + }, + .rle => { + var i: usize = 0; + while (i < len) : (i += 1) { + dest.writeAssumeCapacity(literals.streams.one[0]); + } + self.literal_written_count += len; + }, + .compressed, .treeless => { + // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; + const huffman_tree = self.huffman_tree orelse unreachable; + const max_bit_count = huffman_tree.max_bit_count; + const starting_bit_count = Literals.HuffmanTree.weightToBitCount( + huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, + max_bit_count, + ); + var bits_read: u4 = 0; + var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; + var bit_count_to_read: u4 = starting_bit_count; + var i: usize = 0; + while (i < len) : (i += 1) { + var prefix: u16 = 0; + while (true) { + const new_bits = self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch |err| + switch (err) { + error.EndOfStream => if (literals.streams == .four and self.literal_stream_index < 3) bits: { + try self.nextLiteralMultiStream(literals); + break :bits try self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read); + } else { + return error.UnexpectedEndOfLiteralStream; + }, + }; + prefix <<= bit_count_to_read; + prefix |= new_bits; + bits_read += bit_count_to_read; + const result = try huffman_tree.query(huffman_tree_index, prefix); + + switch (result) { + .symbol => |sym| { + dest.writeAssumeCapacity(sym); + bit_count_to_read = starting_bit_count; + bits_read = 0; + huffman_tree_index = huffman_tree.symbol_count_minus_one; + break; + }, + .index => |index| { + huffman_tree_index = index; + const bit_count = Literals.HuffmanTree.weightToBitCount( + huffman_tree.nodes[index].weight, + max_bit_count, + ); + bit_count_to_read = bit_count - bits_read; + }, + } + } + } + self.literal_written_count += len; + }, + } + } + fn getCode(self: *DecodeState, comptime choice: DataType) u32 { return switch (@field(self, @tagName(choice)).table) { .rle => |value| value, @@ -437,6 +540,14 @@ fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: return block_size; } +fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) usize { + log.debug("writing raw block - size {d}", .{block_size}); + const data = src[0..block_size]; + dest.writeSliceAssumeCapacity(data); + consumed_count.* += block_size; + return block_size; +} + fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) usize { log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); var write_pos: usize = 0; @@ -447,6 +558,16 @@ fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: return block_size; } +fn decodeRleBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) usize { + log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); + var write_pos: usize = 0; + while (write_pos < block_size) : (write_pos += 1) { + dest.writeAssumeCapacity(src[0]); + } + consumed_count.* += 1; + return block_size; +} + fn prepareDecodeState( decode_state: *DecodeState, src: []const u8, @@ -545,7 +666,7 @@ pub fn decodeBlock( if (decode_state.literal_written_count < literals.header.regenerated_size) { log.debug("decoding remaining literals", .{}); const len = literals.header.regenerated_size - decode_state.literal_written_count; - try decode_state.decodeLiteralsInto(dest[written_count + bytes_written ..], literals, len); + try decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], literals, len); log.debug("remaining decoded literals at {d}: {}", .{ written_count, std.fmt.fmtSliceHexUpper(dest[written_count .. written_count + len]), @@ -562,6 +683,73 @@ pub fn decodeBlock( } } +pub fn decodeBlockRingBuffer( + dest: *RingBuffer, + src: []const u8, + block_header: frame.ZStandard.Block.Header, + decode_state: *DecodeState, + consumed_count: *usize, + block_size_maximum: usize, +) !usize { + const block_size = block_header.block_size; + if (block_size_maximum < block_size) return error.BlockSizeOverMaximum; + // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) + switch (block_header.block_type) { + .raw => return decodeRawBlockRingBuffer(dest, src, block_size, consumed_count), + .rle => return decodeRleBlockRingBuffer(dest, src, block_size, consumed_count), + .compressed => { + var bytes_read: usize = 0; + const literals = try decodeLiteralsSection(src, &bytes_read); + const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); + + bytes_read += try prepareDecodeState(decode_state, src[bytes_read..], literals, sequences_header); + + var bytes_written: usize = 0; + if (sequences_header.sequence_count > 0) { + const bit_stream_bytes = src[bytes_read..block_size]; + var reverse_byte_reader = reversedByteReader(bit_stream_bytes); + var bit_stream = reverseBitReader(reverse_byte_reader.reader()); + + while (0 == try bit_stream.readBitsNoEof(u1, 1)) {} + try decode_state.readInitialState(&bit_stream); + + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + log.debug("decoding sequence {d}", .{i}); + const decompressed_size = try decode_state.decodeSequenceRingBuffer( + dest, + literals, + &bit_stream, + i == sequences_header.sequence_count - 1, + ); + bytes_written += decompressed_size; + } + + bytes_read += bit_stream_bytes.len; + } + + if (decode_state.literal_written_count < literals.header.regenerated_size) { + log.debug("decoding remaining literals", .{}); + const len = literals.header.regenerated_size - decode_state.literal_written_count; + try decode_state.decodeLiteralsRingBuffer(dest, literals, len); + const written_slice = dest.sliceLast(len); + log.debug("remaining decoded literals at {d}: {}{}", .{ + bytes_written, + std.fmt.fmtSliceHexUpper(written_slice.first), + std.fmt.fmtSliceHexUpper(written_slice.second), + }); + bytes_written += len; + } + + decode_state.literal_written_count = 0; + assert(bytes_read == block_header.block_size); + consumed_count.* += bytes_read; + return bytes_written; + }, + .reserved => return error.FrameContainsReservedBlock, + } +} + pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { const magic = readInt(u32, src[0..4]); assert(isSkippableMagic(magic)); From c819e58c20b23d625b2b2350d1fc655481309d9f Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 22 Jan 2023 16:12:05 +1100 Subject: [PATCH 014/122] std.compress.zstandard: add decodeZStandardFrameAlloc This is a convenience wrapper - best to use `decodeZStandardFrame()` if the content size is known, or directly use `decodeBlockRingBuffer()`. --- lib/std/compress/zstandard/decompress.zig | 84 +++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 6e107c2d7b..b5a37878d1 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -480,6 +480,90 @@ pub fn decodeZStandardFrame(dest: []u8, src: []const u8, verify_checksum: bool) return ReadWriteCount{ .read_count = consumed_count, .write_count = written_count }; } +pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, verify_checksum: bool) ![]u8 { + var result = std.ArrayList(u8).init(allocator); + assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); + var consumed_count: usize = 4; + + const frame_header = try decodeZStandardHeader(src[consumed_count..], &consumed_count); + + if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; + + const window_size = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; + log.debug("window size = {d}", .{window_size}); + + const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; + var hash = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; + + const block_size_maximum = @min(1 << 17, window_size); + log.debug("block size maximum = {d}", .{block_size_maximum}); + + var window_data = try allocator.alloc(u8, window_size); + defer allocator.free(window_data); + var ring_buffer = RingBuffer{ + .data = window_data, + .write_index = 0, + .read_index = 0, + }; + + // These tables take 7680 bytes + var literal_fse_data: [literal_table_size_max]Table.Fse = undefined; + var match_fse_data: [match_table_size_max]Table.Fse = undefined; + var offset_fse_data: [offset_table_size_max]Table.Fse = undefined; + + var block_header = decodeBlockHeader(src[consumed_count..][0..3]); + consumed_count += 3; + var decode_state = DecodeState{ + .repeat_offsets = .{ + types.compressed_block.start_repeated_offset_1, + types.compressed_block.start_repeated_offset_2, + types.compressed_block.start_repeated_offset_3, + }, + + .offset = undefined, + .match = undefined, + .literal = undefined, + + .literal_fse_buffer = &literal_fse_data, + .match_fse_buffer = &match_fse_data, + .offset_fse_buffer = &offset_fse_data, + + .fse_tables_undefined = true, + + .literal_written_count = 0, + .literal_stream_reader = undefined, + .literal_stream_bytes = undefined, + .literal_stream_index = undefined, + .huffman_tree = null, + }; + var written_count: usize = 0; + while (true) : ({ + block_header = decodeBlockHeader(src[consumed_count..][0..3]); + consumed_count += 3; + }) { + if (block_header.block_size > block_size_maximum) return error.CompressedBlockSizeOverMaximum; + const written_size = try decodeBlockRingBuffer( + &ring_buffer, + src[consumed_count..], + block_header, + &decode_state, + &consumed_count, + block_size_maximum, + ); + if (written_size > block_size_maximum) return error.DecompressedBlockSizeOverMaximum; + const written_slice = ring_buffer.sliceLast(written_size); + try result.appendSlice(written_slice.first); + try result.appendSlice(written_slice.second); + if (hash) |*hash_state| { + hash_state.update(written_slice.first); + hash_state.update(written_slice.second); + } + written_count += written_size; + if (block_header.last_block) break; + } + return result.toOwnedSlice(); +} + pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, hash: ?*std.hash.XxHash64) !usize { // These tables take 7680 bytes var literal_fse_data: [literal_table_size_max]Table.Fse = undefined; From cbfaa876d466a885c54ead16a5901399619ed0c8 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 23 Jan 2023 12:47:46 +1100 Subject: [PATCH 015/122] std.compress.zstandard: cleanup ReverseBitReader --- lib/std/compress/zstandard/decompress.zig | 91 +++++++++++------------ 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index b5a37878d1..22ed22c0de 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -68,8 +68,7 @@ const DecodeState = struct { fse_tables_undefined: bool, - literal_stream_reader: ReverseBitReader(ReversedByteReader.Reader), - literal_stream_bytes: ReversedByteReader, + literal_stream_reader: ReverseBitReader, literal_stream_index: usize, huffman_tree: ?Literals.HuffmanTree, @@ -288,9 +287,7 @@ const DecodeState = struct { fn initLiteralStream(self: *DecodeState, bytes: []const u8) !void { log.debug("initing literal stream: {}", .{std.fmt.fmtSliceHexUpper(bytes)}); - self.literal_stream_bytes = reversedByteReader(bytes); - self.literal_stream_reader = reverseBitReader(self.literal_stream_bytes.reader()); - while (0 == try self.literal_stream_reader.readBitsNoEof(u1, 1)) {} + try self.literal_stream_reader.init(bytes); } fn decodeLiteralsSlice(self: *DecodeState, dest: []u8, literals: Literals, len: usize) !void { @@ -532,7 +529,6 @@ pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, .literal_written_count = 0, .literal_stream_reader = undefined, - .literal_stream_bytes = undefined, .literal_stream_index = undefined, .huffman_tree = null, }; @@ -591,7 +587,6 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha .literal_written_count = 0, .literal_stream_reader = undefined, - .literal_stream_bytes = undefined, .literal_stream_index = undefined, .huffman_tree = null, }; @@ -725,10 +720,9 @@ pub fn decodeBlock( var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { const bit_stream_bytes = src[bytes_read..block_size]; - var reverse_byte_reader = reversedByteReader(bit_stream_bytes); - var bit_stream = reverseBitReader(reverse_byte_reader.reader()); + var bit_stream: ReverseBitReader = undefined; + try bit_stream.init(bit_stream_bytes); - while (0 == try bit_stream.readBitsNoEof(u1, 1)) {} try decode_state.readInitialState(&bit_stream); var i: usize = 0; @@ -791,10 +785,9 @@ pub fn decodeBlockRingBuffer( var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { const bit_stream_bytes = src[bytes_read..block_size]; - var reverse_byte_reader = reversedByteReader(bit_stream_bytes); - var bit_stream = reverseBitReader(reverse_byte_reader.reader()); + var bit_stream: ReverseBitReader = undefined; + try bit_stream.init(bit_stream_bytes); - while (0 == try bit_stream.readBitsNoEof(u1, 1)) {} try decode_state.readInitialState(&bit_stream); var i: usize = 0; @@ -1028,9 +1021,8 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !Literals.HuffmanT const accuracy_log = std.math.log2_int_ceil(usize, table_size); var huff_data = src[1 + counting_reader.bytes_read .. compressed_size + 1]; - var huff_data_bytes = reversedByteReader(huff_data); - var huff_bits = reverseBitReader(huff_data_bytes.reader()); - while (0 == try huff_bits.readBitsNoEof(u1, 1)) {} + var huff_bits: ReverseBitReader = undefined; + try huff_bits.init(huff_data); dumpFseTable("huffman", entries[0..table_size]); @@ -1415,48 +1407,49 @@ const ReversedByteReader = struct { const Reader = std.io.Reader(*ReversedByteReader, error{}, readFn); + fn init(bytes: []const u8) ReversedByteReader { + return .{ + .bytes = bytes, + .remaining_bytes = bytes.len, + }; + } + fn reader(self: *ReversedByteReader) Reader { return .{ .context = self }; } + + fn readFn(ctx: *ReversedByteReader, buffer: []u8) !usize { + if (ctx.remaining_bytes == 0) return 0; + const byte_index = ctx.remaining_bytes - 1; + buffer[0] = ctx.bytes[byte_index]; + // buffer[0] = @bitReverse(ctx.bytes[byte_index]); + ctx.remaining_bytes = byte_index; + return 1; + } }; -fn readFn(ctx: *ReversedByteReader, buffer: []u8) !usize { - if (ctx.remaining_bytes == 0) return 0; - const byte_index = ctx.remaining_bytes - 1; - buffer[0] = ctx.bytes[byte_index]; - // buffer[0] = @bitReverse(ctx.bytes[byte_index]); - ctx.remaining_bytes = byte_index; - return 1; -} +const ReverseBitReader = struct { + byte_reader: ReversedByteReader, + bit_reader: std.io.BitReader(.Big, ReversedByteReader.Reader), -fn reversedByteReader(bytes: []const u8) ReversedByteReader { - return ReversedByteReader{ - .remaining_bytes = bytes.len, - .bytes = bytes, - }; -} + fn init(self: *ReverseBitReader, bytes: []const u8) !void { + self.byte_reader = ReversedByteReader.init(bytes); + self.bit_reader = std.io.bitReader(.Big, self.byte_reader.reader()); + while (0 == self.readBitsNoEof(u1, 1) catch return error.BitStreamHasNoStartBit) {} + } -fn ReverseBitReader(comptime Reader: type) type { - return struct { - underlying: std.io.BitReader(.Big, Reader), + fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { + return self.bit_reader.readBitsNoEof(U, num_bits); + } - fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { - return self.underlying.readBitsNoEof(U, num_bits); - } + fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { + return try self.bit_reader.readBits(U, num_bits, out_bits); + } - fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { - return try self.underlying.readBits(U, num_bits, out_bits); - } - - fn alignToByte(self: *@This()) void { - self.underlying.alignToByte(); - } - }; -} - -fn reverseBitReader(reader: anytype) ReverseBitReader(@TypeOf(reader)) { - return .{ .underlying = std.io.bitReader(.Big, reader) }; -} + fn alignToByte(self: *@This()) void { + self.bit_reader.alignToByte(); + } +}; fn BitReader(comptime Reader: type) type { return struct { From fc64c279a497263c15feb857eb5442aa615179c4 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 23 Jan 2023 16:26:03 +1100 Subject: [PATCH 016/122] std.compress.zstandard: clean up api --- lib/std/compress/zstandard.zig | 1 + lib/std/compress/zstandard/decompress.zig | 207 ++++++++++++---------- lib/std/compress/zstandard/types.zig | 6 +- 3 files changed, 113 insertions(+), 101 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index c1e9cef58c..d83b3a3336 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -1,6 +1,7 @@ const std = @import("std"); pub const decompress = @import("zstandard/decompress.zig"); +pub usingnamespace @import("zstandard/types.zig"); test "decompression" { const uncompressed = @embedFile("testdata/rfc8478.txt"); diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 22ed22c0de..59268dad57 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -3,10 +3,10 @@ const assert = std.debug.assert; const types = @import("types.zig"); const frame = types.frame; -const Literals = types.compressed_block.Literals; -const Sequences = types.compressed_block.Sequences; +const LiteralsSection = types.compressed_block.LiteralsSection; +const SequencesSection = types.compressed_block.SequencesSection; const Table = types.compressed_block.Table; -const RingBuffer = @import("RingBuffer.zig"); +pub const RingBuffer = @import("RingBuffer.zig"); const readInt = std.mem.readIntLittle; const readIntSlice = std.mem.readIntSliceLittle; @@ -55,7 +55,7 @@ pub fn decodeFrame(dest: []u8, src: []const u8, verify_checksum: bool) !ReadWrit }; } -const DecodeState = struct { +pub const DecodeState = struct { repeat_offsets: [3]u32, offset: StateData(8), @@ -70,7 +70,7 @@ const DecodeState = struct { literal_stream_reader: ReverseBitReader, literal_stream_index: usize, - huffman_tree: ?Literals.HuffmanTree, + huffman_tree: ?LiteralsSection.HuffmanTree, literal_written_count: usize, @@ -84,7 +84,55 @@ const DecodeState = struct { }; } - fn readInitialState(self: *DecodeState, bit_reader: anytype) !void { + pub fn prepare( + self: *DecodeState, + src: []const u8, + literals: LiteralsSection, + sequences_header: SequencesSection.Header, + ) !usize { + if (literals.huffman_tree) |tree| { + self.huffman_tree = tree; + } else if (literals.header.block_type == .treeless and self.huffman_tree == null) { + return error.TreelessLiteralsFirst; + } + + switch (literals.header.block_type) { + .raw, .rle => {}, + .compressed, .treeless => { + self.literal_stream_index = 0; + switch (literals.streams) { + .one => |slice| try self.initLiteralStream(slice), + .four => |streams| try self.initLiteralStream(streams[0]), + } + }, + } + + if (sequences_header.sequence_count > 0) { + var bytes_read = try self.updateFseTable( + src, + .literal, + sequences_header.literal_lengths, + ); + + bytes_read += try self.updateFseTable( + src[bytes_read..], + .offset, + sequences_header.offsets, + ); + + bytes_read += try self.updateFseTable( + src[bytes_read..], + .match, + sequences_header.match_lengths, + ); + self.fse_tables_undefined = false; + + return bytes_read; + } + return 0; + } + + pub fn readInitialFseState(self: *DecodeState, bit_reader: anytype) !void { self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); self.match.state = try bit_reader.readBitsNoEof(u9, self.match.accuracy_log); @@ -130,7 +178,7 @@ const DecodeState = struct { self: *DecodeState, src: []const u8, comptime choice: DataType, - mode: Sequences.Header.Mode, + mode: SequencesSection.Header.Mode, ) !usize { const field_name = @tagName(choice); switch (mode) { @@ -213,7 +261,13 @@ const DecodeState = struct { }; } - fn executeSequenceSlice(self: *DecodeState, dest: []u8, write_pos: usize, literals: Literals, sequence: Sequence) !void { + fn executeSequenceSlice( + self: *DecodeState, + dest: []u8, + write_pos: usize, + literals: LiteralsSection, + sequence: Sequence, + ) !void { try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length); // TODO: should we validate offset against max_window_size? @@ -225,7 +279,12 @@ const DecodeState = struct { std.mem.copy(u8, dest[write_pos + sequence.literal_length ..], dest[copy_start..copy_end]); } - fn executeSequenceRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: Literals, sequence: Sequence) !void { + fn executeSequenceRingBuffer( + self: *DecodeState, + dest: *RingBuffer, + literals: LiteralsSection, + sequence: Sequence, + ) !void { try self.decodeLiteralsRingBuffer(dest, literals, sequence.literal_length); // TODO: check that ring buffer window is full enough for match copies const copy_slice = dest.sliceAt(dest.write_index + dest.data.len - sequence.offset, sequence.match_length); @@ -234,11 +293,11 @@ const DecodeState = struct { for (copy_slice.second) |b| dest.writeAssumeCapacity(b); } - fn decodeSequenceSlice( + pub fn decodeSequenceSlice( self: *DecodeState, dest: []u8, write_pos: usize, - literals: Literals, + literals: LiteralsSection, bit_reader: anytype, last_sequence: bool, ) !usize { @@ -255,10 +314,10 @@ const DecodeState = struct { return sequence.match_length + sequence.literal_length; } - fn decodeSequenceRingBuffer( + pub fn decodeSequenceRingBuffer( self: *DecodeState, dest: *RingBuffer, - literals: Literals, + literals: LiteralsSection, bit_reader: anytype, last_sequence: bool, ) !usize { @@ -280,7 +339,7 @@ const DecodeState = struct { return sequence.match_length + sequence.literal_length; } - fn nextLiteralMultiStream(self: *DecodeState, literals: Literals) !void { + fn nextLiteralMultiStream(self: *DecodeState, literals: LiteralsSection) !void { self.literal_stream_index += 1; try self.initLiteralStream(literals.streams.four[self.literal_stream_index]); } @@ -290,7 +349,7 @@ const DecodeState = struct { try self.literal_stream_reader.init(bytes); } - fn decodeLiteralsSlice(self: *DecodeState, dest: []u8, literals: Literals, len: usize) !void { + pub fn decodeLiteralsSlice(self: *DecodeState, dest: []u8, literals: LiteralsSection, len: usize) !void { if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; switch (literals.header.block_type) { .raw => { @@ -310,7 +369,7 @@ const DecodeState = struct { // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; const huffman_tree = self.huffman_tree orelse unreachable; const max_bit_count = huffman_tree.max_bit_count; - const starting_bit_count = Literals.HuffmanTree.weightToBitCount( + const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount( huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, max_bit_count, ); @@ -345,7 +404,7 @@ const DecodeState = struct { }, .index => |index| { huffman_tree_index = index; - const bit_count = Literals.HuffmanTree.weightToBitCount( + const bit_count = LiteralsSection.HuffmanTree.weightToBitCount( huffman_tree.nodes[index].weight, max_bit_count, ); @@ -359,7 +418,7 @@ const DecodeState = struct { } } - fn decodeLiteralsRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: Literals, len: usize) !void { + pub fn decodeLiteralsRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: LiteralsSection, len: usize) !void { if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; switch (literals.header.block_type) { .raw => { @@ -378,7 +437,7 @@ const DecodeState = struct { // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; const huffman_tree = self.huffman_tree orelse unreachable; const max_bit_count = huffman_tree.max_bit_count; - const starting_bit_count = Literals.HuffmanTree.weightToBitCount( + const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount( huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, max_bit_count, ); @@ -413,7 +472,7 @@ const DecodeState = struct { }, .index => |index| { huffman_tree_index = index; - const bit_count = Literals.HuffmanTree.weightToBitCount( + const bit_count = LiteralsSection.HuffmanTree.weightToBitCount( huffman_tree.nodes[index].weight, max_bit_count, ); @@ -647,54 +706,6 @@ fn decodeRleBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, return block_size; } -fn prepareDecodeState( - decode_state: *DecodeState, - src: []const u8, - literals: Literals, - sequences_header: Sequences.Header, -) !usize { - if (literals.huffman_tree) |tree| { - decode_state.huffman_tree = tree; - } else if (literals.header.block_type == .treeless and decode_state.huffman_tree == null) { - return error.TreelessLiteralsFirst; - } - - switch (literals.header.block_type) { - .raw, .rle => {}, - .compressed, .treeless => { - decode_state.literal_stream_index = 0; - switch (literals.streams) { - .one => |slice| try decode_state.initLiteralStream(slice), - .four => |streams| try decode_state.initLiteralStream(streams[0]), - } - }, - } - - if (sequences_header.sequence_count > 0) { - var bytes_read = try decode_state.updateFseTable( - src, - .literal, - sequences_header.literal_lengths, - ); - - bytes_read += try decode_state.updateFseTable( - src[bytes_read..], - .offset, - sequences_header.offsets, - ); - - bytes_read += try decode_state.updateFseTable( - src[bytes_read..], - .match, - sequences_header.match_lengths, - ); - decode_state.fse_tables_undefined = false; - - return bytes_read; - } - return 0; -} - pub fn decodeBlock( dest: []u8, src: []const u8, @@ -715,7 +726,7 @@ pub fn decodeBlock( const literals = try decodeLiteralsSection(src, &bytes_read); const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); - bytes_read += try prepareDecodeState(decode_state, src[bytes_read..], literals, sequences_header); + bytes_read += try decode_state.prepare(src[bytes_read..], literals, sequences_header); var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { @@ -723,7 +734,7 @@ pub fn decodeBlock( var bit_stream: ReverseBitReader = undefined; try bit_stream.init(bit_stream_bytes); - try decode_state.readInitialState(&bit_stream); + try decode_state.readInitialFseState(&bit_stream); var i: usize = 0; while (i < sequences_header.sequence_count) : (i += 1) { @@ -780,7 +791,7 @@ pub fn decodeBlockRingBuffer( const literals = try decodeLiteralsSection(src, &bytes_read); const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); - bytes_read += try prepareDecodeState(decode_state, src[bytes_read..], literals, sequences_header); + bytes_read += try decode_state.prepare(src[bytes_read..], literals, sequences_header); var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { @@ -788,7 +799,7 @@ pub fn decodeBlockRingBuffer( var bit_stream: ReverseBitReader = undefined; try bit_stream.init(bit_stream_bytes); - try decode_state.readInitialState(&bit_stream); + try decode_state.readInitialFseState(&bit_stream); var i: usize = 0; while (i < sequences_header.sequence_count) : (i += 1) { @@ -928,7 +939,7 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { }; } -pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !Literals { +pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsSection { // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) var bytes_read: usize = 0; const header = decodeLiteralsHeader(src, &bytes_read); @@ -936,7 +947,7 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !Literals .raw => { const stream = src[bytes_read .. bytes_read + header.regenerated_size]; consumed_count.* += header.regenerated_size + bytes_read; - return Literals{ + return LiteralsSection{ .header = header, .huffman_tree = null, .streams = .{ .one = stream }, @@ -945,7 +956,7 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !Literals .rle => { const stream = src[bytes_read .. bytes_read + 1]; consumed_count.* += 1 + bytes_read; - return Literals{ + return LiteralsSection{ .header = header, .huffman_tree = null, .streams = .{ .one = stream }, @@ -966,7 +977,7 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !Literals const stream = src[bytes_read .. bytes_read + total_streams_size]; bytes_read += total_streams_size; consumed_count.* += bytes_read; - return Literals{ + return LiteralsSection{ .header = header, .huffman_tree = huffman_tree, .streams = .{ .one = stream }, @@ -988,7 +999,7 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !Literals consumed_count.* += total_streams_size + bytes_read; - return Literals{ + return LiteralsSection{ .header = header, .huffman_tree = huffman_tree, .streams = .{ .four = .{ @@ -1002,7 +1013,7 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !Literals } } -fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !Literals.HuffmanTree { +fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.HuffmanTree { var bytes_read: usize = 0; bytes_read += 1; const header = src[0]; @@ -1094,7 +1105,7 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !Literals.HuffmanT weights[symbol_count - 1] = @intCast(u4, std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1); log.debug("weights[{d}] = {d}", .{ symbol_count - 1, weights[symbol_count - 1] }); - var weight_sorted_prefixed_symbols: [256]Literals.HuffmanTree.PrefixedSymbol = undefined; + var weight_sorted_prefixed_symbols: [256]LiteralsSection.HuffmanTree.PrefixedSymbol = undefined; for (weight_sorted_prefixed_symbols[0..symbol_count]) |_, i| { weight_sorted_prefixed_symbols[i] = .{ .symbol = @intCast(u8, i), @@ -1104,7 +1115,7 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !Literals.HuffmanT } std.sort.sort( - Literals.HuffmanTree.PrefixedSymbol, + LiteralsSection.HuffmanTree.PrefixedSymbol, weight_sorted_prefixed_symbols[0..symbol_count], weights, lessThanByWeight, @@ -1137,7 +1148,7 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !Literals.HuffmanT } } consumed_count.* += bytes_read; - const tree = Literals.HuffmanTree{ + const tree = LiteralsSection.HuffmanTree{ .max_bit_count = max_number_of_bits, .symbol_count_minus_one = @intCast(u8, prefixed_symbol_count - 1), .nodes = weight_sorted_prefixed_symbols, @@ -1148,8 +1159,8 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !Literals.HuffmanT fn lessThanByWeight( weights: [256]u4, - lhs: Literals.HuffmanTree.PrefixedSymbol, - rhs: Literals.HuffmanTree.PrefixedSymbol, + lhs: LiteralsSection.HuffmanTree.PrefixedSymbol, + rhs: LiteralsSection.HuffmanTree.PrefixedSymbol, ) bool { // NOTE: this function relies on the use of a stable sorting algorithm, // otherwise a special case of if (weights[lhs] == weights[rhs]) return lhs < rhs; @@ -1157,11 +1168,11 @@ fn lessThanByWeight( return weights[lhs.symbol] < weights[rhs.symbol]; } -pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) Literals.Header { +pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) LiteralsSection.Header { // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) const start = consumed_count.*; const byte0 = src[0]; - const block_type = @intToEnum(Literals.BlockType, byte0 & 0b11); + const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); const size_format = @intCast(u2, (byte0 & 0b1100) >> 2); var regenerated_size: u20 = undefined; var compressed_size: ?u18 = null; @@ -1220,7 +1231,7 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) Literals.He compressed_size, }, ); - return Literals.Header{ + return LiteralsSection.Header{ .block_type = block_type, .size_format = size_format, .regenerated_size = regenerated_size, @@ -1228,7 +1239,7 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) Literals.He }; } -fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences.Header { +pub fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !SequencesSection.Header { var sequence_count: u24 = undefined; var bytes_read: usize = 0; @@ -1237,7 +1248,7 @@ fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences.Hea bytes_read += 1; log.debug("decoded sequences header '{}': sequence count = 0", .{std.fmt.fmtSliceHexUpper(src[0..bytes_read])}); consumed_count.* += bytes_read; - return Sequences.Header{ + return SequencesSection.Header{ .sequence_count = 0, .offsets = undefined, .match_lengths = undefined, @@ -1258,9 +1269,9 @@ fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences.Hea bytes_read += 1; consumed_count.* += bytes_read; - const matches_mode = @intToEnum(Sequences.Header.Mode, (compression_modes & 0b00001100) >> 2); - const offsets_mode = @intToEnum(Sequences.Header.Mode, (compression_modes & 0b00110000) >> 4); - const literal_mode = @intToEnum(Sequences.Header.Mode, (compression_modes & 0b11000000) >> 6); + const matches_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00001100) >> 2); + const offsets_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00110000) >> 4); + const literal_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b11000000) >> 6); log.debug("decoded sequences header '{}': (sc={d},o={s},m={s},l={s})", .{ std.fmt.fmtSliceHexUpper(src[0..bytes_read]), sequence_count, @@ -1270,7 +1281,7 @@ fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences.Hea }); if (compression_modes & 0b11 != 0) return error.ReservedBitSet; - return Sequences.Header{ + return SequencesSection.Header{ .sequence_count = sequence_count, .offsets = offsets_mode, .match_lengths = matches_mode, @@ -1428,25 +1439,25 @@ const ReversedByteReader = struct { } }; -const ReverseBitReader = struct { +pub const ReverseBitReader = struct { byte_reader: ReversedByteReader, bit_reader: std.io.BitReader(.Big, ReversedByteReader.Reader), - fn init(self: *ReverseBitReader, bytes: []const u8) !void { + pub fn init(self: *ReverseBitReader, bytes: []const u8) !void { self.byte_reader = ReversedByteReader.init(bytes); self.bit_reader = std.io.bitReader(.Big, self.byte_reader.reader()); while (0 == self.readBitsNoEof(u1, 1) catch return error.BitStreamHasNoStartBit) {} } - fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { + pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { return self.bit_reader.readBitsNoEof(U, num_bits); } - fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { + pub fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { return try self.bit_reader.readBits(U, num_bits, out_bits); } - fn alignToByte(self: *@This()) void { + pub fn alignToByte(self: *@This()) void { self.bit_reader.alignToByte(); } }; @@ -1514,7 +1525,7 @@ fn dumpFseTable(prefix: []const u8, table: []const Table.Fse) void { } } -fn dumpHuffmanTree(tree: Literals.HuffmanTree) void { +fn dumpHuffmanTree(tree: LiteralsSection.HuffmanTree) void { log.debug("Huffman tree: max bit count = {}, symbol count = {}", .{ tree.max_bit_count, tree.symbol_count_minus_one + 1 }); for (tree.nodes[0 .. tree.symbol_count_minus_one + 1]) |node| { log.debug("symbol = {[symbol]d}, prefix = {[prefix]d}, weight = {[weight]d}", node); diff --git a/lib/std/compress/zstandard/types.zig b/lib/std/compress/zstandard/types.zig index edac66f686..f703dc29eb 100644 --- a/lib/std/compress/zstandard/types.zig +++ b/lib/std/compress/zstandard/types.zig @@ -52,7 +52,7 @@ pub const frame = struct { }; pub const compressed_block = struct { - pub const Literals = struct { + pub const LiteralsSection = struct { header: Header, huffman_tree: ?HuffmanTree, streams: Streams, @@ -119,8 +119,8 @@ pub const compressed_block = struct { } }; - pub const Sequences = struct { - header: Sequences.Header, + pub const SequencesSection = struct { + header: SequencesSection.Header, literals_length_table: Table, offset_table: Table, match_length_table: Table, From 082acd7f17358ed3a7787f284b48b754fefd8187 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 23 Jan 2023 23:46:15 +1100 Subject: [PATCH 017/122] std.compress.zstandard: clean up integer casts --- lib/std/compress/zstandard/decompress.zig | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 59268dad57..c80492c3f1 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -168,8 +168,11 @@ pub const DecodeState = struct { const data = table[@field(self, @tagName(choice)).state]; const T = @TypeOf(@field(self, @tagName(choice))).State; const bits_summand = try bit_reader.readBitsNoEof(T, data.bits); - const next_state = data.baseline + bits_summand; - @field(self, @tagName(choice)).state = @intCast(@TypeOf(@field(self, @tagName(choice))).State, next_state); + const next_state = std.math.cast( + @TypeOf(@field(self, @tagName(choice))).State, + data.baseline + bits_summand, + ) orelse return error.MalformedFseBits; + @field(self, @tagName(choice)).state = next_state; }, } } @@ -1045,10 +1048,10 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H const even_data = entries[even_state]; var read_bits: usize = 0; const even_bits = try huff_bits.readBits(u32, even_data.bits, &read_bits); - weights[i] = @intCast(u4, even_data.symbol); + weights[i] = std.math.cast(u4, even_data.symbol) orelse return error.MalformedHuffmanTree; i += 1; if (read_bits < even_data.bits) { - weights[i] = @intCast(u4, entries[odd_state].symbol); + weights[i] = std.math.cast(u4, entries[odd_state].symbol) orelse return error.MalformedHuffmanTree; log.debug("overflow condition: setting weights[{d}] = {d}", .{ i, weights[i] }); i += 1; break; @@ -1058,11 +1061,11 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H read_bits = 0; const odd_data = entries[odd_state]; const odd_bits = try huff_bits.readBits(u32, odd_data.bits, &read_bits); - weights[i] = @intCast(u4, odd_data.symbol); + weights[i] = std.math.cast(u4, odd_data.symbol) orelse return error.MalformedHuffmanTree; i += 1; if (read_bits < odd_data.bits) { if (i == 256) return error.MalformedHuffmanTree; - weights[i] = @intCast(u4, entries[even_state].symbol); + weights[i] = std.math.cast(u4, entries[even_state].symbol) orelse return error.MalformedHuffmanTree; log.debug("overflow condition: setting weights[{d}] = {d}", .{ i, weights[i] }); i += 1; break; @@ -1100,9 +1103,9 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H log.debug("weight power sum = {d}", .{weight_power_sum}); // advance to next power of two (even if weight_power_sum is a power of 2) - max_number_of_bits = @intCast(u4, std.math.log2_int(u16, weight_power_sum) + 1); + max_number_of_bits = std.math.log2_int(u16, weight_power_sum) + 1; const next_power_of_two = @as(u16, 1) << max_number_of_bits; - weights[symbol_count - 1] = @intCast(u4, std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1); + weights[symbol_count - 1] = std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1; log.debug("weights[{d}] = {d}", .{ symbol_count - 1, weights[symbol_count - 1] }); var weight_sorted_prefixed_symbols: [256]LiteralsSection.HuffmanTree.PrefixedSymbol = undefined; @@ -1367,7 +1370,7 @@ fn decodeFseTable( while (accumulated_probability < total_probability) { // WARNING: The RFC in poorly worded, and would suggest std.math.log2_int_ceil is correct here, // but power of two (remaining probabilities + 1) need max bits set to 1 more. - const max_bits = @intCast(u4, std.math.log2_int(u16, total_probability - accumulated_probability + 1)) + 1; + const max_bits = std.math.log2_int(u16, total_probability - accumulated_probability + 1) + 1; const small = try bit_reader.readBitsNoEof(u16, max_bits - 1); const cutoff = (@as(u16, 1) << max_bits) - 1 - (total_probability - accumulated_probability + 1); From 6b85373875e2a7a8ef9d74e20e8e827dc622a29c Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 24 Jan 2023 13:07:58 +1100 Subject: [PATCH 018/122] std.compress.zstandard: validate sequence lengths --- lib/std/compress/zstandard/decompress.zig | 38 ++++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index c80492c3f1..315bc0196c 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -271,10 +271,9 @@ pub const DecodeState = struct { literals: LiteralsSection, sequence: Sequence, ) !void { - try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length); + if (sequence.offset > write_pos + sequence.literal_length) return error.MalformedSequence; - // TODO: should we validate offset against max_window_size? - assert(sequence.offset <= write_pos + sequence.literal_length); + try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length); const copy_start = write_pos + sequence.literal_length - sequence.offset; const copy_end = copy_start + sequence.match_length; // NOTE: we ignore the usage message for std.mem.copy and copy with dest.ptr >= src.ptr @@ -288,8 +287,9 @@ pub const DecodeState = struct { literals: LiteralsSection, sequence: Sequence, ) !void { + if (sequence.offset > dest.data.len) return error.MalformedSequence; + try self.decodeLiteralsRingBuffer(dest, literals, sequence.literal_length); - // TODO: check that ring buffer window is full enough for match copies const copy_slice = dest.sliceAt(dest.write_index + dest.data.len - sequence.offset, sequence.match_length); // TODO: would std.mem.copy and figuring out dest slice be better/faster? for (copy_slice.first) |b| dest.writeAssumeCapacity(b); @@ -302,9 +302,13 @@ pub const DecodeState = struct { write_pos: usize, literals: LiteralsSection, bit_reader: anytype, + sequence_size_limit: usize, last_sequence: bool, ) !usize { const sequence = try self.nextSequence(bit_reader); + const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; + if (sequence_length > sequence_size_limit) return error.MalformedSequence; + try self.executeSequenceSlice(dest, write_pos, literals, sequence); log.debug("sequence decompressed into '{x}'", .{ std.fmt.fmtSliceHexUpper(dest[write_pos .. write_pos + sequence.literal_length + sequence.match_length]), @@ -314,7 +318,7 @@ pub const DecodeState = struct { try self.updateState(.match, bit_reader); try self.updateState(.offset, bit_reader); } - return sequence.match_length + sequence.literal_length; + return sequence_length; } pub fn decodeSequenceRingBuffer( @@ -322,12 +326,15 @@ pub const DecodeState = struct { dest: *RingBuffer, literals: LiteralsSection, bit_reader: anytype, + sequence_size_limit: usize, last_sequence: bool, ) !usize { const sequence = try self.nextSequence(bit_reader); + const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; + if (sequence_length > sequence_size_limit) return error.MalformedSequence; + try self.executeSequenceRingBuffer(dest, literals, sequence); if (std.options.log_level == .debug) { - const sequence_length = sequence.literal_length + sequence.match_length; const written_slice = dest.sliceLast(sequence_length); log.debug("sequence decompressed into '{x}{x}'", .{ std.fmt.fmtSliceHexUpper(written_slice.first), @@ -339,7 +346,7 @@ pub const DecodeState = struct { try self.updateState(.match, bit_reader); try self.updateState(.offset, bit_reader); } - return sequence.match_length + sequence.literal_length; + return sequence_length; } fn nextLiteralMultiStream(self: *DecodeState, literals: LiteralsSection) !void { @@ -717,9 +724,9 @@ pub fn decodeBlock( consumed_count: *usize, written_count: usize, ) !usize { - const block_maximum_size = 1 << 17; // 128KiB + const block_size_max = @min(1 << 17, dest[written_count..].len); // 128KiB const block_size = block_header.block_size; - if (block_maximum_size < block_size) return error.BlockSizeOverMaximum; + if (block_size_max < block_size) return error.BlockSizeOverMaximum; // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) switch (block_header.block_type) { .raw => return decodeRawBlock(dest[written_count..], src, block_size, consumed_count), @@ -739,17 +746,21 @@ pub fn decodeBlock( try decode_state.readInitialFseState(&bit_stream); + var sequence_size_limit = block_size_max; var i: usize = 0; while (i < sequences_header.sequence_count) : (i += 1) { log.debug("decoding sequence {d}", .{i}); + const write_pos = written_count + bytes_written; const decompressed_size = try decode_state.decodeSequenceSlice( dest, - written_count + bytes_written, + write_pos, literals, &bit_stream, + sequence_size_limit, i == sequences_header.sequence_count - 1, ); bytes_written += decompressed_size; + sequence_size_limit -= decompressed_size; } bytes_read += bit_stream_bytes.len; @@ -781,10 +792,10 @@ pub fn decodeBlockRingBuffer( block_header: frame.ZStandard.Block.Header, decode_state: *DecodeState, consumed_count: *usize, - block_size_maximum: usize, + block_size_max: usize, ) !usize { const block_size = block_header.block_size; - if (block_size_maximum < block_size) return error.BlockSizeOverMaximum; + if (block_size_max < block_size) return error.BlockSizeOverMaximum; // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) switch (block_header.block_type) { .raw => return decodeRawBlockRingBuffer(dest, src, block_size, consumed_count), @@ -804,6 +815,7 @@ pub fn decodeBlockRingBuffer( try decode_state.readInitialFseState(&bit_stream); + var sequence_size_limit = block_size_max; var i: usize = 0; while (i < sequences_header.sequence_count) : (i += 1) { log.debug("decoding sequence {d}", .{i}); @@ -811,9 +823,11 @@ pub fn decodeBlockRingBuffer( dest, literals, &bit_stream, + sequence_size_limit, i == sequences_header.sequence_count - 1, ); bytes_written += decompressed_size; + sequence_size_limit -= decompressed_size; } bytes_read += bit_stream_bytes.len; From 95953e1ee6a50229e21423aa2ac4e6929b37468a Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 24 Jan 2023 13:10:45 +1100 Subject: [PATCH 019/122] std.compress.zstandard: fix dictionary field size --- lib/std/compress/zstandard/decompress.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 315bc0196c..75ca1b68a8 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -898,8 +898,8 @@ pub fn decodeZStandardHeader(src: []const u8, consumed_count: ?*usize) !frame.ZS var dictionary_id: ?u32 = null; if (descriptor.dictionary_id_flag > 0) { - // if flag is 3 we field_size = 4, else field_size = flag - const field_size = (@as(u3, 1) << descriptor.dictionary_id_flag) >> 1; + // if flag is 3 then field_size = 4, else field_size = flag + const field_size = (@as(u4, 1) << descriptor.dictionary_id_flag) >> 1; dictionary_id = readVarInt(u32, src[bytes_read_count .. bytes_read_count + field_size]); bytes_read_count += field_size; } From 31d1cae8c68fbc765fd4394863b071788dbc9746 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 24 Jan 2023 13:14:06 +1100 Subject: [PATCH 020/122] std.compress.zstandard: validate fse table value count --- lib/std/compress/zstandard/decompress.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 75ca1b68a8..37ec8ebfd0 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -1418,8 +1418,7 @@ fn decodeFseTable( } bit_reader.alignToByte(); - // TODO: check there are at least 2 non-zero probabilities - + if (value_count < 2) return error.MalformedFseTable; if (accumulated_probability != total_probability) return error.MalformedFseTable; if (value_count > expected_symbol_count) return error.MalformedFseTable; From 774e2f5a5c918cccfc455bcb73d90be43ec9a9eb Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 24 Jan 2023 14:30:32 +1100 Subject: [PATCH 021/122] std.compress.zstandard: add input length safety checks --- lib/std/compress/zstandard/decompress.zig | 56 +++++++++++++++-------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 37ec8ebfd0..5563ca9199 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -680,7 +680,8 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha return written_count; } -fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) usize { +fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) !usize { + if (src.len < block_size) return error.MalformedBlockSize; log.debug("writing raw block - size {d}", .{block_size}); const data = src[0..block_size]; std.mem.copy(u8, dest, data); @@ -688,7 +689,8 @@ fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: return block_size; } -fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) usize { +fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) !usize { + if (src.len < block_size) return error.MalformedBlockSize; log.debug("writing raw block - size {d}", .{block_size}); const data = src[0..block_size]; dest.writeSliceAssumeCapacity(data); @@ -696,7 +698,8 @@ fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, return block_size; } -fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) usize { +fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) !usize { + if (src.len < 1) return error.MalformedRleBlock; log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); var write_pos: usize = 0; while (write_pos < block_size) : (write_pos += 1) { @@ -706,7 +709,8 @@ fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: return block_size; } -fn decodeRleBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) usize { +fn decodeRleBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) !usize { + if (src.len < 1) return error.MalformedRleBlock; log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); var write_pos: usize = 0; while (write_pos < block_size) : (write_pos += 1) { @@ -727,11 +731,11 @@ pub fn decodeBlock( const block_size_max = @min(1 << 17, dest[written_count..].len); // 128KiB const block_size = block_header.block_size; if (block_size_max < block_size) return error.BlockSizeOverMaximum; - // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) switch (block_header.block_type) { .raw => return decodeRawBlock(dest[written_count..], src, block_size, consumed_count), .rle => return decodeRleBlock(dest[written_count..], src, block_size, consumed_count), .compressed => { + if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; const literals = try decodeLiteralsSection(src, &bytes_read); const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); @@ -796,11 +800,11 @@ pub fn decodeBlockRingBuffer( ) !usize { const block_size = block_header.block_size; if (block_size_max < block_size) return error.BlockSizeOverMaximum; - // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) switch (block_header.block_type) { .raw => return decodeRawBlockRingBuffer(dest, src, block_size, consumed_count), .rle => return decodeRleBlockRingBuffer(dest, src, block_size, consumed_count), .compressed => { + if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; const literals = try decodeLiteralsSection(src, &bytes_read); const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); @@ -957,11 +961,11 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { } pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsSection { - // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) var bytes_read: usize = 0; - const header = decodeLiteralsHeader(src, &bytes_read); + const header = try decodeLiteralsHeader(src, &bytes_read); switch (header.block_type) { .raw => { + if (src.len < bytes_read + header.regenerated_size) return error.MalformedLiteralsSection; const stream = src[bytes_read .. bytes_read + header.regenerated_size]; consumed_count.* += header.regenerated_size + bytes_read; return LiteralsSection{ @@ -971,6 +975,7 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsS }; }, .rle => { + if (src.len < bytes_read + 1) return error.MalformedLiteralsSection; const stream = src[bytes_read .. bytes_read + 1]; consumed_count.* += 1 + bytes_read; return LiteralsSection{ @@ -990,18 +995,19 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsS log.debug("huffman tree size = {}, total streams size = {}", .{ huffman_tree_size, total_streams_size }); if (huffman_tree) |tree| dumpHuffmanTree(tree); + if (src.len < bytes_read + total_streams_size) return error.MalformedLiteralsSection; + const stream_data = src[bytes_read .. bytes_read + total_streams_size]; + if (header.size_format == 0) { - const stream = src[bytes_read .. bytes_read + total_streams_size]; - bytes_read += total_streams_size; - consumed_count.* += bytes_read; + consumed_count.* += total_streams_size + bytes_read; return LiteralsSection{ .header = header, .huffman_tree = huffman_tree, - .streams = .{ .one = stream }, + .streams = .{ .one = stream_data }, }; } - const stream_data = src[bytes_read .. bytes_read + total_streams_size]; + if (stream_data.len < 6) return error.MalformedLiteralsSection; log.debug("jump table: {}", .{std.fmt.fmtSliceHexUpper(stream_data[0..6])}); const stream_1_length = @as(usize, readInt(u16, stream_data[0..2])); @@ -1014,6 +1020,7 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsS const stream_3_start = stream_2_start + stream_2_length; const stream_4_start = stream_3_start + stream_3_length; + if (stream_data.len < stream_4_start + stream_4_length) return error.MalformedLiteralsSection; consumed_count.* += total_streams_size + bytes_read; return LiteralsSection{ @@ -1033,13 +1040,15 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsS fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.HuffmanTree { var bytes_read: usize = 0; bytes_read += 1; + if (src.len == 0) return error.MalformedHuffmanTree; const header = src[0]; var symbol_count: usize = undefined; var weights: [256]u4 = undefined; var max_number_of_bits: u4 = undefined; if (header < 128) { - // FSE compressed weigths + // FSE compressed weights const compressed_size = header; + if (src.len < 1 + compressed_size) return error.MalformedHuffmanTree; var stream = std.io.fixedBufferStream(src[1 .. compressed_size + 1]); var counting_reader = std.io.countingReader(stream.reader()); var bit_reader = bitReader(counting_reader.reader()); @@ -1185,8 +1194,8 @@ fn lessThanByWeight( return weights[lhs.symbol] < weights[rhs.symbol]; } -pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) LiteralsSection.Header { - // TODO: we probably want to enable safety for release-fast and release-small (or insert custom checks) +pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) !LiteralsSection.Header { + if (src.len == 0) return error.MalformedLiteralsSection; const start = consumed_count.*; const byte0 = src[0]; const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); @@ -1201,14 +1210,16 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) LiteralsSec consumed_count.* += 1; }, 1 => { + if (src.len < 2) return error.MalformedLiteralsHeader; regenerated_size = (byte0 >> 4) + - (@as(u20, src[consumed_count.* + 1]) << 4); + (@as(u20, src[1]) << 4); consumed_count.* += 2; }, 3 => { + if (src.len < 3) return error.MalformedLiteralsHeader; regenerated_size = (byte0 >> 4) + - (@as(u20, src[consumed_count.* + 1]) << 4) + - (@as(u20, src[consumed_count.* + 2]) << 12); + (@as(u20, src[1]) << 4) + + (@as(u20, src[2]) << 12); consumed_count.* += 3; }, } @@ -1218,17 +1229,20 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) LiteralsSec const byte2 = src[2]; switch (size_format) { 0, 1 => { + if (src.len < 3) return error.MalformedLiteralsHeader; regenerated_size = (byte0 >> 4) + ((@as(u20, byte1) & 0b00111111) << 4); compressed_size = ((byte1 & 0b11000000) >> 6) + (@as(u18, byte2) << 2); consumed_count.* += 3; }, 2 => { + if (src.len < 4) return error.MalformedLiteralsHeader; const byte3 = src[3]; regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00000011) << 12); compressed_size = ((byte2 & 0b11111100) >> 2) + (@as(u18, byte3) << 6); consumed_count.* += 4; }, 3 => { + if (src.len < 5) return error.MalformedLiteralsHeader; const byte3 = src[3]; const byte4 = src[4]; regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00111111) << 12); @@ -1257,6 +1271,7 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) LiteralsSec } pub fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !SequencesSection.Header { + if (src.len == 0) return error.MalformedSequencesSection; var sequence_count: u24 = undefined; var bytes_read: usize = 0; @@ -1275,13 +1290,16 @@ pub fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences sequence_count = byte0; bytes_read += 1; } else if (byte0 < 255) { + if (src.len < 2) return error.MalformedSequencesSection; sequence_count = (@as(u24, (byte0 - 128)) << 8) + src[1]; bytes_read += 2; } else { + if (src.len < 3) return error.MalformedSequencesSection; sequence_count = src[1] + (@as(u24, src[2]) << 8) + 0x7F00; bytes_read += 3; } + if (src.len < bytes_read + 1) return error.MalformedSequencesSection; const compression_modes = src[bytes_read]; bytes_read += 1; From d40b135e958b0f2195bd9bcf2a44cb9952ba51fc Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 24 Jan 2023 17:12:48 +1100 Subject: [PATCH 022/122] std.compress.zstandard: properly track consumed count in decodeFrameBlocks --- lib/std/compress/zstandard/decompress.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 5563ca9199..1d85b498bc 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -601,7 +601,6 @@ pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, .literal_stream_index = undefined, .huffman_tree = null, }; - var written_count: usize = 0; while (true) : ({ block_header = decodeBlockHeader(src[consumed_count..][0..3]); consumed_count += 3; @@ -623,7 +622,6 @@ pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, hash_state.update(written_slice.first); hash_state.update(written_slice.second); } - written_count += written_size; if (block_header.last_block) break; } return result.toOwnedSlice(); @@ -637,6 +635,7 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha var block_header = decodeBlockHeader(src[0..3]); var bytes_read: usize = 3; + defer consumed_count.* += bytes_read; var decode_state = DecodeState{ .repeat_offsets = .{ types.compressed_block.start_repeated_offset_1, @@ -676,7 +675,6 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha written_count += written_size; if (block_header.last_block) break; } - consumed_count.* += bytes_read; return written_count; } From ab18adf5c38e2640411039600f8de7de817ac200 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 24 Jan 2023 22:51:39 +1100 Subject: [PATCH 023/122] std.compress.zstandard: remove debug logging --- lib/std/compress/zstandard/decompress.zig | 122 ---------------------- 1 file changed, 122 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 1d85b498bc..9119a51c7d 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -14,8 +14,6 @@ fn readVarInt(comptime T: type, bytes: []const u8) T { return std.mem.readVarInt(T, bytes, .Little); } -const log = std.log.scoped(.Decompress); - fn isSkippableMagic(magic: u32) bool { return frame.Skippable.magic_number_min <= magic and magic <= frame.Skippable.magic_number_max; } @@ -136,11 +134,6 @@ pub const DecodeState = struct { self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); self.match.state = try bit_reader.readBitsNoEof(u9, self.match.accuracy_log); - log.debug("initial decoder state: literal = {d}, offset = {d} match = {d}", .{ - self.literal.state, - self.offset.state, - self.match.state, - }); } fn updateRepeatOffset(self: *DecodeState, offset: u32) void { @@ -208,10 +201,6 @@ pub const DecodeState = struct { ); @field(self, field_name).table = .{ .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size] }; @field(self, field_name).accuracy_log = std.math.log2_int_ceil(usize, table_size); - log.debug("decoded fse " ++ field_name ++ " table '{}'", .{ - std.fmt.fmtSliceHexUpper(src[0..counting_reader.bytes_read]), - }); - dumpFseTable(field_name, @field(self, field_name).table.fse); return counting_reader.bytes_read; }, .repeat => return if (self.fse_tables_undefined) error.RepeatModeFirst else 0, @@ -227,7 +216,6 @@ pub const DecodeState = struct { fn nextSequence(self: *DecodeState, bit_reader: anytype) !Sequence { const raw_code = self.getCode(.offset); const offset_code = std.math.cast(u5, raw_code) orelse { - log.err("got offset code of {d}", .{raw_code}); return error.OffsetCodeTooLarge; }; const offset_value = (@as(u32, 1) << offset_code) + try bit_reader.readBitsNoEof(u32, offset_code); @@ -256,7 +244,6 @@ pub const DecodeState = struct { break :offset self.useRepeatOffset(offset_value - 1); }; - log.debug("sequence = ({d}, {d}, {d})", .{ literal_length, offset, match_length }); return .{ .literal_length = literal_length, .match_length = match_length, @@ -310,9 +297,6 @@ pub const DecodeState = struct { if (sequence_length > sequence_size_limit) return error.MalformedSequence; try self.executeSequenceSlice(dest, write_pos, literals, sequence); - log.debug("sequence decompressed into '{x}'", .{ - std.fmt.fmtSliceHexUpper(dest[write_pos .. write_pos + sequence.literal_length + sequence.match_length]), - }); if (!last_sequence) { try self.updateState(.literal, bit_reader); try self.updateState(.match, bit_reader); @@ -334,13 +318,6 @@ pub const DecodeState = struct { if (sequence_length > sequence_size_limit) return error.MalformedSequence; try self.executeSequenceRingBuffer(dest, literals, sequence); - if (std.options.log_level == .debug) { - const written_slice = dest.sliceLast(sequence_length); - log.debug("sequence decompressed into '{x}{x}'", .{ - std.fmt.fmtSliceHexUpper(written_slice.first), - std.fmt.fmtSliceHexUpper(written_slice.second), - }); - } if (!last_sequence) { try self.updateState(.literal, bit_reader); try self.updateState(.match, bit_reader); @@ -355,7 +332,6 @@ pub const DecodeState = struct { } fn initLiteralStream(self: *DecodeState, bytes: []const u8) !void { - log.debug("initing literal stream: {}", .{std.fmt.fmtSliceHexUpper(bytes)}); try self.literal_stream_reader.init(bytes); } @@ -372,7 +348,6 @@ pub const DecodeState = struct { while (i < len) : (i += 1) { dest[i] = literals.streams.one[0]; } - log.debug("rle: {}", .{std.fmt.fmtSliceHexUpper(dest[0..len])}); self.literal_written_count += len; }, .compressed, .treeless => { @@ -538,7 +513,6 @@ pub fn decodeZStandardFrame(dest: []u8, src: []const u8, verify_checksum: bool) const hash = hash_state.final(); const hash_low_bytes = hash & 0xFFFFFFFF; if (checksum != hash_low_bytes) { - std.log.err("expected checksum {x}, got {x} (full hash {x})", .{ checksum, hash_low_bytes, hash }); return error.ChecksumFailure; } } @@ -556,13 +530,11 @@ pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; const window_size = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; - log.debug("window size = {d}", .{window_size}); const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; var hash = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; const block_size_maximum = @min(1 << 17, window_size); - log.debug("block size maximum = {d}", .{block_size_maximum}); var window_data = try allocator.alloc(u8, window_size); defer allocator.free(window_data); @@ -680,7 +652,6 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) !usize { if (src.len < block_size) return error.MalformedBlockSize; - log.debug("writing raw block - size {d}", .{block_size}); const data = src[0..block_size]; std.mem.copy(u8, dest, data); consumed_count.* += block_size; @@ -689,7 +660,6 @@ fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) !usize { if (src.len < block_size) return error.MalformedBlockSize; - log.debug("writing raw block - size {d}", .{block_size}); const data = src[0..block_size]; dest.writeSliceAssumeCapacity(data); consumed_count.* += block_size; @@ -698,7 +668,6 @@ fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) !usize { if (src.len < 1) return error.MalformedRleBlock; - log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); var write_pos: usize = 0; while (write_pos < block_size) : (write_pos += 1) { dest[write_pos] = src[0]; @@ -709,7 +678,6 @@ fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: fn decodeRleBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) !usize { if (src.len < 1) return error.MalformedRleBlock; - log.debug("writing rle block - '{x}'x{d}", .{ src[0], block_size }); var write_pos: usize = 0; while (write_pos < block_size) : (write_pos += 1) { dest.writeAssumeCapacity(src[0]); @@ -751,7 +719,6 @@ pub fn decodeBlock( var sequence_size_limit = block_size_max; var i: usize = 0; while (i < sequences_header.sequence_count) : (i += 1) { - log.debug("decoding sequence {d}", .{i}); const write_pos = written_count + bytes_written; const decompressed_size = try decode_state.decodeSequenceSlice( dest, @@ -769,13 +736,8 @@ pub fn decodeBlock( } if (decode_state.literal_written_count < literals.header.regenerated_size) { - log.debug("decoding remaining literals", .{}); const len = literals.header.regenerated_size - decode_state.literal_written_count; try decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], literals, len); - log.debug("remaining decoded literals at {d}: {}", .{ - written_count, - std.fmt.fmtSliceHexUpper(dest[written_count .. written_count + len]), - }); bytes_written += len; } @@ -820,7 +782,6 @@ pub fn decodeBlockRingBuffer( var sequence_size_limit = block_size_max; var i: usize = 0; while (i < sequences_header.sequence_count) : (i += 1) { - log.debug("decoding sequence {d}", .{i}); const decompressed_size = try decode_state.decodeSequenceRingBuffer( dest, literals, @@ -836,15 +797,8 @@ pub fn decodeBlockRingBuffer( } if (decode_state.literal_written_count < literals.header.regenerated_size) { - log.debug("decoding remaining literals", .{}); const len = literals.header.regenerated_size - decode_state.literal_written_count; try decode_state.decodeLiteralsRingBuffer(dest, literals, len); - const written_slice = dest.sliceLast(len); - log.debug("remaining decoded literals at {d}: {}{}", .{ - bytes_written, - std.fmt.fmtSliceHexUpper(written_slice.first), - std.fmt.fmtSliceHexUpper(written_slice.second), - }); bytes_written += len; } @@ -922,22 +876,6 @@ pub fn decodeZStandardHeader(src: []const u8, consumed_count: ?*usize) !frame.ZS .dictionary_id = dictionary_id, .content_size = content_size, }; - log.debug( - "decoded ZStandard frame header {x}: " ++ - "desc = (d={d},c={},r={},u={},s={},cs={d}), win_desc = {?x}, dict_id = {?x}, content_size = {?d}", - .{ - std.fmt.fmtSliceHexUpper(src[0..bytes_read_count]), - header.descriptor.dictionary_id_flag, - header.descriptor.content_checksum_flag, - header.descriptor.reserved, - header.descriptor.unused, - header.descriptor.single_segment_flag, - header.descriptor.content_size_flag, - header.window_descriptor, - header.dictionary_id, - header.content_size, - }, - ); return header; } @@ -945,12 +883,6 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { const last_block = src[0] & 1 == 1; const block_type = @intToEnum(frame.ZStandard.Block.Type, (src[0] & 0b110) >> 1); const block_size = ((src[0] & 0b11111000) >> 3) + (@as(u21, src[1]) << 5) + (@as(u21, src[2]) << 13); - log.debug("decoded block header {}: last = {}, type = {s}, size = {d}", .{ - std.fmt.fmtSliceHexUpper(src), - last_block, - @tagName(block_type), - block_size, - }); return .{ .last_block = last_block, .block_type = block_type, @@ -990,8 +922,6 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsS null; const huffman_tree_size = bytes_read - huffman_tree_start; const total_streams_size = @as(usize, header.compressed_size.?) - huffman_tree_size; - log.debug("huffman tree size = {}, total streams size = {}", .{ huffman_tree_size, total_streams_size }); - if (huffman_tree) |tree| dumpHuffmanTree(tree); if (src.len < bytes_read + total_streams_size) return error.MalformedLiteralsSection; const stream_data = src[bytes_read .. bytes_read + total_streams_size]; @@ -1007,7 +937,6 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsS if (stream_data.len < 6) return error.MalformedLiteralsSection; - log.debug("jump table: {}", .{std.fmt.fmtSliceHexUpper(stream_data[0..6])}); const stream_1_length = @as(usize, readInt(u16, stream_data[0..2])); const stream_2_length = @as(usize, readInt(u16, stream_data[2..4])); const stream_3_length = @as(usize, readInt(u16, stream_data[4..6])); @@ -1059,8 +988,6 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H var huff_bits: ReverseBitReader = undefined; try huff_bits.init(huff_data); - dumpFseTable("huffman", entries[0..table_size]); - var i: usize = 0; var even_state: u32 = try huff_bits.readBitsNoEof(u32, accuracy_log); var odd_state: u32 = try huff_bits.readBitsNoEof(u32, accuracy_log); @@ -1073,7 +1000,6 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H i += 1; if (read_bits < even_data.bits) { weights[i] = std.math.cast(u4, entries[odd_state].symbol) orelse return error.MalformedHuffmanTree; - log.debug("overflow condition: setting weights[{d}] = {d}", .{ i, weights[i] }); i += 1; break; } @@ -1087,7 +1013,6 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H if (read_bits < odd_data.bits) { if (i == 256) return error.MalformedHuffmanTree; weights[i] = std.math.cast(u4, entries[even_state].symbol) orelse return error.MalformedHuffmanTree; - log.debug("overflow condition: setting weights[{d}] = {d}", .{ i, weights[i] }); i += 1; break; } @@ -1099,19 +1024,12 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H } else { const encoded_symbol_count = header - 127; symbol_count = encoded_symbol_count + 1; - log.debug("huffman tree symbol count = {d}", .{symbol_count}); const weights_byte_count = (encoded_symbol_count + 1) / 2; - log.debug("decoding direct huffman tree: {}|{}", .{ - std.fmt.fmtSliceHexUpper(src[0..1]), - std.fmt.fmtSliceHexUpper(src[1 .. weights_byte_count + 1]), - }); if (src.len < weights_byte_count) return error.MalformedHuffmanTree; var i: usize = 0; while (i < weights_byte_count) : (i += 1) { weights[2 * i] = @intCast(u4, src[i + 1] >> 4); weights[2 * i + 1] = @intCast(u4, src[i + 1] & 0xF); - log.debug("weights[{d}] = {d}", .{ 2 * i, weights[2 * i] }); - log.debug("weights[{d}] = {d}", .{ 2 * i + 1, weights[2 * i + 1] }); } bytes_read += weights_byte_count; } @@ -1121,13 +1039,11 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H weight_power_sum += @as(u16, 1) << (value - 1); } } - log.debug("weight power sum = {d}", .{weight_power_sum}); // advance to next power of two (even if weight_power_sum is a power of 2) max_number_of_bits = std.math.log2_int(u16, weight_power_sum) + 1; const next_power_of_two = @as(u16, 1) << max_number_of_bits; weights[symbol_count - 1] = std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1; - log.debug("weights[{d}] = {d}", .{ symbol_count - 1, weights[symbol_count - 1] }); var weight_sorted_prefixed_symbols: [256]LiteralsSection.HuffmanTree.PrefixedSymbol = undefined; for (weight_sorted_prefixed_symbols[0..symbol_count]) |_, i| { @@ -1177,7 +1093,6 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H .symbol_count_minus_one = @intCast(u8, prefixed_symbol_count - 1), .nodes = weight_sorted_prefixed_symbols, }; - log.debug("decoded huffman tree {}:", .{std.fmt.fmtSliceHexUpper(src[0..bytes_read])}); return tree; } @@ -1194,7 +1109,6 @@ fn lessThanByWeight( pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) !LiteralsSection.Header { if (src.len == 0) return error.MalformedLiteralsSection; - const start = consumed_count.*; const byte0 = src[0]; const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); const size_format = @intCast(u2, (byte0 & 0b1100) >> 2); @@ -1250,16 +1164,6 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) !LiteralsSe } }, } - log.debug( - "decoded literals section header '{}': type = {s}, size_format = {}, regen_size = {d}, compressed size = {?d}", - .{ - std.fmt.fmtSliceHexUpper(src[0 .. consumed_count.* - start]), - @tagName(block_type), - size_format, - regenerated_size, - compressed_size, - }, - ); return LiteralsSection.Header{ .block_type = block_type, .size_format = size_format, @@ -1276,7 +1180,6 @@ pub fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences const byte0 = src[0]; if (byte0 == 0) { bytes_read += 1; - log.debug("decoded sequences header '{}': sequence count = 0", .{std.fmt.fmtSliceHexUpper(src[0..bytes_read])}); consumed_count.* += bytes_read; return SequencesSection.Header{ .sequence_count = 0, @@ -1305,13 +1208,6 @@ pub fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences const matches_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00001100) >> 2); const offsets_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00110000) >> 4); const literal_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b11000000) >> 6); - log.debug("decoded sequences header '{}': (sc={d},o={s},m={s},l={s})", .{ - std.fmt.fmtSliceHexUpper(src[0..bytes_read]), - sequence_count, - @tagName(offsets_mode), - @tagName(matches_mode), - @tagName(literal_mode), - }); if (compression_modes & 0b11 != 0) return error.ReservedBitSet; return SequencesSection.Header{ @@ -1383,10 +1279,7 @@ fn decodeFseTable( max_accuracy_log: u4, entries: []Table.Fse, ) !usize { - log.debug("decoding fse table {d} {d}", .{ max_accuracy_log, expected_symbol_count }); - const accuracy_log_biased = try bit_reader.readBitsNoEof(u4, 4); - log.debug("accuracy_log_biased = {d}", .{accuracy_log_biased}); if (accuracy_log_biased > max_accuracy_log -| 5) return error.MalformedAccuracyLog; const accuracy_log = accuracy_log_biased + 5; @@ -1394,7 +1287,6 @@ fn decodeFseTable( var value_count: usize = 0; const total_probability = @as(u16, 1) << accuracy_log; - log.debug("total probability = {d}", .{total_probability}); var accumulated_probability: u16 = 0; while (accumulated_probability < total_probability) { @@ -1549,17 +1441,3 @@ test buildFseTable { try buildFseTable(&offset_codes_default_values, entries[0..32]); try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_offset_fse_table.fse, entries[0..32]); } - -fn dumpFseTable(prefix: []const u8, table: []const Table.Fse) void { - log.debug("{s} fse table:", .{prefix}); - for (table) |entry, i| { - log.debug("state = {d} symbol = {d} bl = {d}, bits = {d}", .{ i, entry.symbol, entry.baseline, entry.bits }); - } -} - -fn dumpHuffmanTree(tree: LiteralsSection.HuffmanTree) void { - log.debug("Huffman tree: max bit count = {}, symbol count = {}", .{ tree.max_bit_count, tree.symbol_count_minus_one + 1 }); - for (tree.nodes[0 .. tree.symbol_count_minus_one + 1]) |node| { - log.debug("symbol = {[symbol]d}, prefix = {[prefix]d}, weight = {[weight]d}", node); - } -} From 7558bf64513ec2be59b95aecc5e0ac50ad88b1f5 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Wed, 25 Jan 2023 01:30:17 +1100 Subject: [PATCH 024/122] std.compress.zstandard: minor cleanup and add doc comments --- lib/std/compress/zstandard/decompress.zig | 72 +++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 9119a51c7d..ff554ee6c8 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -18,6 +18,10 @@ fn isSkippableMagic(magic: u32) bool { return frame.Skippable.magic_number_min <= magic and magic <= frame.Skippable.magic_number_max; } +/// Returns the decompressed size of the frame at the start of `src`. Returns 0 +/// if the the frame is skippable, `null` for Zstanndard frames that do not +/// declare their content size. Returns `UnusedBitSet` and `ReservedBitSet` +/// errors if the respective bits of the the frame descriptor are set. pub fn getFrameDecompressedSize(src: []const u8) !?usize { switch (try frameType(src)) { .zstandard => { @@ -28,7 +32,10 @@ pub fn getFrameDecompressedSize(src: []const u8) !?usize { } } -pub fn frameType(src: []const u8) !frame.Kind { +/// Returns the kind of frame at the beginning of `src`. Returns `BadMagic` if +/// `src` begin with bytes not equal to the Zstandard frame magic number, or +/// outside the range of magic numbers for skippable frames. +pub fn frameType(src: []const u8) error{BadMagic}!frame.Kind { const magic = readInt(u32, src[0..4]); return if (magic == frame.ZStandard.magic_number) .zstandard @@ -43,11 +50,13 @@ const ReadWriteCount = struct { write_count: usize, }; +/// Decodes the frame at the start of `src` into `dest`. Returns the number of +/// bytes read from `src` and written to `dest`. pub fn decodeFrame(dest: []u8, src: []const u8, verify_checksum: bool) !ReadWriteCount { return switch (try frameType(src)) { .zstandard => decodeZStandardFrame(dest, src, verify_checksum), .skippable => ReadWriteCount{ - .read_count = try skippableFrameSize(src[0..8]) + 8, + .read_count = skippableFrameSize(src[0..8]) + 8, .write_count = 0, }, }; @@ -82,6 +91,10 @@ pub const DecodeState = struct { }; } + /// Prepare the decoder to decode a compressed block. Loads the literals + /// stream and Huffman tree from `literals` and reads the FSE tables from `src`. + /// Returns `error.BitStreamHasNoStartBit` if the (reversed) literal bitstream's + /// first byte does not have any bits set. pub fn prepare( self: *DecodeState, src: []const u8, @@ -130,6 +143,8 @@ pub const DecodeState = struct { return 0; } + /// Read initial FSE states for sequence decoding. Returns `error.EndOfStream` + /// if `bit_reader` does not contain enough bits. pub fn readInitialFseState(self: *DecodeState, bit_reader: anytype) !void { self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); @@ -283,6 +298,14 @@ pub const DecodeState = struct { for (copy_slice.second) |b| dest.writeAssumeCapacity(b); } + /// Decode one sequence from `bit_reader` into `dest`, written starting at + /// `write_pos` and update FSE states if `last_sequence` is `false`. Returns + /// `error.MalformedSequence` error if the decompressed sequence would be longer + /// than `sequence_size_limit` or the sequence's offset is too large; returns + /// `error.EndOfStream` if `bit_reader` does not contain enough bits; returns + /// `error.UnexpectedEndOfLiteralStream` if the decoder state's literal streams + /// do not contain enough literals for the sequence (this may mean the literal + /// stream or the sequence is malformed). pub fn decodeSequenceSlice( self: *DecodeState, dest: []u8, @@ -305,6 +328,7 @@ pub const DecodeState = struct { return sequence_length; } + /// Decode one sequence from `bit_reader` into `dest`; see `decodeSequenceSlice`. pub fn decodeSequenceRingBuffer( self: *DecodeState, dest: *RingBuffer, @@ -335,6 +359,12 @@ pub const DecodeState = struct { try self.literal_stream_reader.init(bytes); } + /// Decode `len` bytes of literals into `dest`. `literals` should be the + /// `LiteralsSection` that was passed to `prepare()`. Returns + /// `error.MalformedLiteralsLength` if the number of literal bytes decoded by + /// `self` plus `len` is greater than the regenerated size of `literals`. + /// Returns `error.UnexpectedEndOfLiteralStream` and `error.PrefixNotFound` if + /// there are problems decoding Huffman compressed literals. pub fn decodeLiteralsSlice(self: *DecodeState, dest: []u8, literals: LiteralsSection, len: usize) !void { if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; switch (literals.header.block_type) { @@ -403,6 +433,7 @@ pub const DecodeState = struct { } } + /// Decode literals into `dest`; see `decodeLiteralsSlice()`. pub fn decodeLiteralsRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: LiteralsSection, len: usize) !void { if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; switch (literals.header.block_type) { @@ -483,6 +514,13 @@ const literal_table_size_max = 1 << types.compressed_block.table_accuracy_log_ma const match_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; const offset_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; +/// Decode a Zstandard frame from `src` into `dest`, returning the number of +/// bytes read from `src` and written to `dest`; if the frame does not declare +/// its decompressed content size `error.UnknownContentSizeUnsupported` is +/// returned. Returns `error.DictionaryIdFlagUnsupported` if the frame uses a +/// dictionary, and `error.ChecksumFailure` if `verify_checksum` is `true` and +/// the frame contains a checksum that does not match the checksum computed from +/// the decompressed frame. pub fn decodeZStandardFrame(dest: []u8, src: []const u8, verify_checksum: bool) !ReadWriteCount { assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; @@ -520,6 +558,10 @@ pub fn decodeZStandardFrame(dest: []u8, src: []const u8, verify_checksum: bool) return ReadWriteCount{ .read_count = consumed_count, .write_count = written_count }; } +/// Decode a Zstandard from from `src` and return the decompressed bytes; see +/// `decodeZStandardFrame()`. Returns `error.WindowSizeUnknown` if the frame +/// does not declare its content size or a window descriptor (this indicates a +/// malformed frame). pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, verify_checksum: bool) ![]u8 { var result = std.ArrayList(u8).init(allocator); assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); @@ -599,6 +641,7 @@ pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, return result.toOwnedSlice(); } +/// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, hash: ?*std.hash.XxHash64) !usize { // These tables take 7680 bytes var literal_fse_data: [literal_table_size_max]Table.Fse = undefined; @@ -686,6 +729,10 @@ fn decodeRleBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, return block_size; } +/// Decode a single block from `src` into `dest`. The beginning of `src` should +/// be the start of the block content (i.e. directly after the block header). +/// Increments `consumed_count` by the number of bytes read from `src` to decode +/// the block and returns the decompressed size of the block. pub fn decodeBlock( dest: []u8, src: []const u8, @@ -750,6 +797,9 @@ pub fn decodeBlock( } } +/// Decode a single block from `src` into `dest`; see `decodeBlock()`. Returns +/// the size of the decompressed block, which can be used with `dest.sliceLast()` +/// to get the decompressed bytes. pub fn decodeBlockRingBuffer( dest: *RingBuffer, src: []const u8, @@ -811,6 +861,7 @@ pub fn decodeBlockRingBuffer( } } +/// Decode the header of a skippable frame. pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { const magic = readInt(u32, src[0..4]); assert(isSkippableMagic(magic)); @@ -821,12 +872,15 @@ pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { }; } -pub fn skippableFrameSize(src: *const [8]u8) !usize { +/// Returns the content size of a skippable frame. +pub fn skippableFrameSize(src: *const [8]u8) usize { assert(isSkippableMagic(readInt(u32, src[0..4]))); const frame_size = readInt(u32, src[4..8]); return frame_size; } +/// Returns the window size required to decompress a frame, or `null` if it cannot be +/// determined, which indicates a malformed frame header. pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { if (header.window_descriptor) |descriptor| { const exponent = (descriptor & 0b11111000) >> 3; @@ -838,6 +892,8 @@ pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { } else return header.content_size; } +/// Decode the header of a Zstandard frame. Returns `error.UnusedBitSet` or +/// `error.ReservedBitSet` if the corresponding bits are sets. pub fn decodeZStandardHeader(src: []const u8, consumed_count: ?*usize) !frame.ZStandard.Header { const descriptor = @bitCast(frame.ZStandard.Header.Descriptor, src[0]); @@ -879,6 +935,7 @@ pub fn decodeZStandardHeader(src: []const u8, consumed_count: ?*usize) !frame.ZS return header; } +/// Decode the header of a block. pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { const last_block = src[0] & 1 == 1; const block_type = @intToEnum(frame.ZStandard.Block.Type, (src[0] & 0b110) >> 1); @@ -890,6 +947,8 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { }; } +/// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the +/// number of bytes the section uses. pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsSection { var bytes_read: usize = 0; const header = try decodeLiteralsHeader(src, &bytes_read); @@ -1107,6 +1166,7 @@ fn lessThanByWeight( return weights[lhs.symbol] < weights[rhs.symbol]; } +/// Decode a literals section header. pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) !LiteralsSection.Header { if (src.len == 0) return error.MalformedLiteralsSection; const byte0 = src[0]; @@ -1172,6 +1232,7 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) !LiteralsSe }; } +/// Decode a sequences section header. pub fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !SequencesSection.Header { if (src.len == 0) return error.MalformedSequencesSection; var sequence_count: u24 = undefined; @@ -1241,7 +1302,8 @@ fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { if (value == 0 or value == 1) continue; const probability = value - 1; - const state_share_dividend = try std.math.ceilPowerOfTwo(u16, probability); + const state_share_dividend = std.math.ceilPowerOfTwo(u16, probability) catch + return error.MalformedFseTable; const share_size = @divExact(total_probability, state_share_dividend); const double_state_count = state_share_dividend - probability; const single_state_count = probability - double_state_count; @@ -1363,6 +1425,8 @@ const ReversedByteReader = struct { } }; +/// A bit reader for reading the reversed bit streams used to encode +/// FSE compressed data. pub const ReverseBitReader = struct { byte_reader: ReversedByteReader, bit_reader: std.io.BitReader(.Big, ReversedByteReader.Reader), From e2306ef0a027ab6257613dad234551a013729de3 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 26 Jan 2023 17:04:44 +1100 Subject: [PATCH 025/122] std.compress.zstandard: add integer casts u64 -> usize --- lib/std/compress/zstandard/decompress.zig | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index ff554ee6c8..c82e4f71a3 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -22,7 +22,7 @@ fn isSkippableMagic(magic: u32) bool { /// if the the frame is skippable, `null` for Zstanndard frames that do not /// declare their content size. Returns `UnusedBitSet` and `ReservedBitSet` /// errors if the respective bits of the the frame descriptor are set. -pub fn getFrameDecompressedSize(src: []const u8) !?usize { +pub fn getFrameDecompressedSize(src: []const u8) !?u64 { switch (try frameType(src)) { .zstandard => { const header = try decodeZStandardHeader(src[4..], null); @@ -216,7 +216,7 @@ pub const DecodeState = struct { ); @field(self, field_name).table = .{ .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size] }; @field(self, field_name).accuracy_log = std.math.log2_int_ceil(usize, table_size); - return counting_reader.bytes_read; + return std.math.cast(usize, counting_reader.bytes_read) orelse return error.MalformedFseTable; }, .repeat => return if (self.fse_tables_undefined) error.RepeatModeFirst else 0, } @@ -571,7 +571,8 @@ pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; - const window_size = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; + const window_size_raw = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; + const window_size = std.math.cast(usize, window_size_raw) orelse return error.WindowTooLarge; const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; var hash = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; @@ -1043,7 +1044,8 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H const table_size = try decodeFseTable(&bit_reader, 256, 6, &entries); const accuracy_log = std.math.log2_int_ceil(usize, table_size); - var huff_data = src[1 + counting_reader.bytes_read .. compressed_size + 1]; + const start_index = std.math.cast(usize, 1 + counting_reader.bytes_read) orelse return error.MalformedHuffmanTree; + var huff_data = src[start_index .. compressed_size + 1]; var huff_bits: ReverseBitReader = undefined; try huff_bits.init(huff_data); From 1e5b8be5099d976628aaa3fc4208a0bbd45c7700 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 26 Jan 2023 17:12:40 +1100 Subject: [PATCH 026/122] std.compress.zstandard: add window size limit param --- lib/std/compress/zstandard/decompress.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index c82e4f71a3..6a1ab364e7 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -562,7 +562,12 @@ pub fn decodeZStandardFrame(dest: []u8, src: []const u8, verify_checksum: bool) /// `decodeZStandardFrame()`. Returns `error.WindowSizeUnknown` if the frame /// does not declare its content size or a window descriptor (this indicates a /// malformed frame). -pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, verify_checksum: bool) ![]u8 { +pub fn decodeZStandardFrameAlloc( + allocator: std.mem.Allocator, + src: []const u8, + verify_checksum: bool, + window_size_max: usize, +) ![]u8 { var result = std.ArrayList(u8).init(allocator); assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; @@ -572,7 +577,10 @@ pub fn decodeZStandardFrameAlloc(allocator: std.mem.Allocator, src: []const u8, if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; const window_size_raw = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; - const window_size = std.math.cast(usize, window_size_raw) orelse return error.WindowTooLarge; + const window_size = if (window_size_raw > window_size_max) + return error.WindowTooLarge + else + @intCast(usize, window_size_raw); const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; var hash = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; From 3c06e2e7d0de92c0674c16ae23e1462a3acbe718 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Fri, 27 Jan 2023 23:50:23 +1100 Subject: [PATCH 027/122] std.compress.zstandard: add doc comments for RingBuffer --- lib/std/compress/zstandard/RingBuffer.zig | 29 +++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/std/compress/zstandard/RingBuffer.zig b/lib/std/compress/zstandard/RingBuffer.zig index 1a369596cb..070c440b0f 100644 --- a/lib/std/compress/zstandard/RingBuffer.zig +++ b/lib/std/compress/zstandard/RingBuffer.zig @@ -1,8 +1,8 @@ //! This ring buffer stores read and write indices while being able to utilise the full //! backing slice by incrementing the indices modulo twice the slice's length and reducing -//! indices modulo the slice's length on slice access. This means that the bit of information -//! distinguishing whether the buffer is full or empty in an implementation utilising -//! and extra flag is stored in difference of the indices. +//! indices modulo the slice's length on slice access. This means that whether the ring buffer +//! if full or empty can be distinguised by looking at the different between the read and write +//! indices without adding an extra boolean flag or having to reserve a slot in the buffer. const assert = @import("std").debug.assert; @@ -12,33 +12,45 @@ data: []u8, read_index: usize, write_index: usize, +/// Returns `index` modulo the length of the backing slice. pub fn mask(self: RingBuffer, index: usize) usize { return index % self.data.len; } +/// Returns `index` module twice the length of the backing slice. pub fn mask2(self: RingBuffer, index: usize) usize { return index % (2 * self.data.len); } +/// Write `byte` into the ring buffer. Returns `error.Full` if the ring +/// buffer is full. pub fn write(self: *RingBuffer, byte: u8) !void { if (self.isFull()) return error.Full; self.writeAssumeCapacity(byte); } +/// Write `byte` into the ring buffer. If the ring buffer is full, the +/// oldest byte is overwritten. pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { self.data[self.mask(self.write_index)] = byte; self.write_index = self.mask2(self.write_index + 1); } +/// Write `bytes` into the ring bufffer. Returns `error.Full` if the ring +/// buffer does not have enough space, without writing any data. pub fn writeSlice(self: *RingBuffer, bytes: []const u8) !void { if (self.len() + bytes.len > self.data.len) return error.Full; self.writeSliceAssumeCapacity(bytes); } +/// Write `bytes` into the ring buffer. If there is not enough space, older +/// bytes will be overwritten. pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { for (bytes) |b| self.writeAssumeCapacity(b); } +/// Consume a byte from the ring buffer and return it. Returns `null` if the +/// ring buffer is empty. pub fn read(self: *RingBuffer) ?u8 { if (self.isEmpty()) return null; const byte = self.data[self.mask(self.read_index)]; @@ -46,24 +58,32 @@ pub fn read(self: *RingBuffer) ?u8 { return byte; } +/// Returns `true` if the ring buffer is empty and `false` otherwise. pub fn isEmpty(self: RingBuffer) bool { return self.write_index == self.read_index; } +/// Returns `true` if the ring buffer is full and `false` otherwise. pub fn isFull(self: RingBuffer) bool { return self.mask2(self.write_index + self.data.len) == self.read_index; } +/// Returns the length pub fn len(self: RingBuffer) usize { const adjusted_write_index = self.write_index + @boolToInt(self.write_index < self.read_index) * 2 * self.data.len; return adjusted_write_index - self.read_index; } -const Slice = struct { +/// A `Slice` represents a region of a ring buffer. The region is split into two +/// sections as the ring buffer data will not be contiguous if the desired region +/// wraps to the start of the backing slice. +pub const Slice = struct { first: []u8, second: []u8, }; +/// Returns a `Slice` for the region of the ring buffer staring at `self.mask(start_unmasked)` +/// with the specified length. pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { assert(length <= self.data.len); const slice1_start = self.mask(start_unmasked); @@ -76,6 +96,7 @@ pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { }; } +/// Returns a `Slice` for the last `length` bytes written to the ring buffer. pub fn sliceLast(self: RingBuffer, length: usize) Slice { return self.sliceAt(self.write_index + self.data.len - length, length); } From 3bfba365483ccf30b197195cce8d5656f2c73736 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sat, 28 Jan 2023 21:03:55 +1100 Subject: [PATCH 028/122] std.compress.zstandard: clean up error sets and line lengths --- lib/std/compress/zstandard/decompress.zig | 307 +++++++++++++++------- lib/std/compress/zstandard/types.zig | 2 +- 2 files changed, 215 insertions(+), 94 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 6a1ab364e7..ea72e33730 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -22,7 +22,7 @@ fn isSkippableMagic(magic: u32) bool { /// if the the frame is skippable, `null` for Zstanndard frames that do not /// declare their content size. Returns `UnusedBitSet` and `ReservedBitSet` /// errors if the respective bits of the the frame descriptor are set. -pub fn getFrameDecompressedSize(src: []const u8) !?u64 { +pub fn getFrameDecompressedSize(src: []const u8) (InvalidBit || error{BadMagic})!?u64 { switch (try frameType(src)) { .zstandard => { const header = try decodeZStandardHeader(src[4..], null); @@ -52,7 +52,11 @@ const ReadWriteCount = struct { /// Decodes the frame at the start of `src` into `dest`. Returns the number of /// bytes read from `src` and written to `dest`. -pub fn decodeFrame(dest: []u8, src: []const u8, verify_checksum: bool) !ReadWriteCount { +pub fn decodeFrame( + dest: []u8, + src: []const u8, + verify_checksum: bool, +) (error{ UnknownContentSizeUnsupported, ContentTooLarge, BadMagic } || FrameError)!ReadWriteCount { return switch (try frameType(src)) { .zstandard => decodeZStandardFrame(dest, src, verify_checksum), .skippable => ReadWriteCount{ @@ -100,7 +104,7 @@ pub const DecodeState = struct { src: []const u8, literals: LiteralsSection, sequences_header: SequencesSection.Header, - ) !usize { + ) (error{ BitStreamHasNoStartBit, TreelessLiteralsFirst } || FseTableError)!usize { if (literals.huffman_tree) |tree| { self.huffman_tree = tree; } else if (literals.header.block_type == .treeless and self.huffman_tree == null) { @@ -145,7 +149,7 @@ pub const DecodeState = struct { /// Read initial FSE states for sequence decoding. Returns `error.EndOfStream` /// if `bit_reader` does not contain enough bits. - pub fn readInitialFseState(self: *DecodeState, bit_reader: anytype) !void { + pub fn readInitialFseState(self: *DecodeState, bit_reader: *ReverseBitReader) error{EndOfStream}!void { self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); self.match.state = try bit_reader.readBitsNoEof(u9, self.match.accuracy_log); @@ -169,7 +173,11 @@ pub const DecodeState = struct { const DataType = enum { offset, match, literal }; - fn updateState(self: *DecodeState, comptime choice: DataType, bit_reader: anytype) !void { + fn updateState( + self: *DecodeState, + comptime choice: DataType, + bit_reader: *ReverseBitReader, + ) error{ MalformedFseBits, EndOfStream }!void { switch (@field(self, @tagName(choice)).table) { .rle => {}, .fse => |table| { @@ -185,17 +193,27 @@ pub const DecodeState = struct { } } + const FseTableError = error{ + MalformedFseTable, + MalformedAccuracyLog, + RepeatModeFirst, + EndOfStream, + }; + fn updateFseTable( self: *DecodeState, src: []const u8, comptime choice: DataType, mode: SequencesSection.Header.Mode, - ) !usize { + ) FseTableError!usize { const field_name = @tagName(choice); switch (mode) { .predefined => { - @field(self, field_name).accuracy_log = @field(types.compressed_block.default_accuracy_log, field_name); - @field(self, field_name).table = @field(types.compressed_block, "predefined_" ++ field_name ++ "_fse_table"); + @field(self, field_name).accuracy_log = + @field(types.compressed_block.default_accuracy_log, field_name); + + @field(self, field_name).table = + @field(types.compressed_block, "predefined_" ++ field_name ++ "_fse_table"); return 0; }, .rle => { @@ -214,9 +232,11 @@ pub const DecodeState = struct { @field(types.compressed_block.table_accuracy_log_max, field_name), @field(self, field_name ++ "_fse_buffer"), ); - @field(self, field_name).table = .{ .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size] }; + @field(self, field_name).table = .{ + .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size], + }; @field(self, field_name).accuracy_log = std.math.log2_int_ceil(usize, table_size); - return std.math.cast(usize, counting_reader.bytes_read) orelse return error.MalformedFseTable; + return std.math.cast(usize, counting_reader.bytes_read) orelse error.MalformedFseTable; }, .repeat => return if (self.fse_tables_undefined) error.RepeatModeFirst else 0, } @@ -228,7 +248,10 @@ pub const DecodeState = struct { offset: u32, }; - fn nextSequence(self: *DecodeState, bit_reader: anytype) !Sequence { + fn nextSequence( + self: *DecodeState, + bit_reader: *ReverseBitReader, + ) error{ OffsetCodeTooLarge, EndOfStream }!Sequence { const raw_code = self.getCode(.offset); const offset_code = std.math.cast(u5, raw_code) orelse { return error.OffsetCodeTooLarge; @@ -272,7 +295,7 @@ pub const DecodeState = struct { write_pos: usize, literals: LiteralsSection, sequence: Sequence, - ) !void { + ) (error{MalformedSequence} || DecodeLiteralsError)!void { if (sequence.offset > write_pos + sequence.literal_length) return error.MalformedSequence; try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length); @@ -288,16 +311,23 @@ pub const DecodeState = struct { dest: *RingBuffer, literals: LiteralsSection, sequence: Sequence, - ) !void { + ) (error{MalformedSequence} || DecodeLiteralsError)!void { if (sequence.offset > dest.data.len) return error.MalformedSequence; try self.decodeLiteralsRingBuffer(dest, literals, sequence.literal_length); - const copy_slice = dest.sliceAt(dest.write_index + dest.data.len - sequence.offset, sequence.match_length); + const copy_start = dest.write_index + dest.data.len - sequence.offset; + const copy_slice = dest.sliceAt(copy_start, sequence.match_length); // TODO: would std.mem.copy and figuring out dest slice be better/faster? for (copy_slice.first) |b| dest.writeAssumeCapacity(b); for (copy_slice.second) |b| dest.writeAssumeCapacity(b); } + const DecodeSequenceError = error{ + OffsetCodeTooLarge, + EndOfStream, + MalformedSequence, + MalformedFseBits, + } || DecodeLiteralsError; /// Decode one sequence from `bit_reader` into `dest`, written starting at /// `write_pos` and update FSE states if `last_sequence` is `false`. Returns /// `error.MalformedSequence` error if the decompressed sequence would be longer @@ -311,10 +341,10 @@ pub const DecodeState = struct { dest: []u8, write_pos: usize, literals: LiteralsSection, - bit_reader: anytype, + bit_reader: *ReverseBitReader, sequence_size_limit: usize, last_sequence: bool, - ) !usize { + ) DecodeSequenceError!usize { const sequence = try self.nextSequence(bit_reader); const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; if (sequence_length > sequence_size_limit) return error.MalformedSequence; @@ -336,7 +366,7 @@ pub const DecodeState = struct { bit_reader: anytype, sequence_size_limit: usize, last_sequence: bool, - ) !usize { + ) DecodeSequenceError!usize { const sequence = try self.nextSequence(bit_reader); const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; if (sequence_length > sequence_size_limit) return error.MalformedSequence; @@ -350,26 +380,63 @@ pub const DecodeState = struct { return sequence_length; } - fn nextLiteralMultiStream(self: *DecodeState, literals: LiteralsSection) !void { + fn nextLiteralMultiStream( + self: *DecodeState, + literals: LiteralsSection, + ) error{BitStreamHasNoStartBit}!void { self.literal_stream_index += 1; try self.initLiteralStream(literals.streams.four[self.literal_stream_index]); } - fn initLiteralStream(self: *DecodeState, bytes: []const u8) !void { + fn initLiteralStream(self: *DecodeState, bytes: []const u8) error{BitStreamHasNoStartBit}!void { try self.literal_stream_reader.init(bytes); } + const LiteralBitsError = error{ + BitStreamHasNoStartBit, + UnexpectedEndOfLiteralStream, + }; + fn readLiteralsBits( + self: *DecodeState, + comptime T: type, + bit_count_to_read: usize, + literals: LiteralsSection, + ) LiteralBitsError!T { + return self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch bits: { + if (literals.streams == .four and self.literal_stream_index < 3) { + try self.nextLiteralMultiStream(literals); + break :bits self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch + return error.UnexpectedEndOfLiteralStream; + } else { + return error.UnexpectedEndOfLiteralStream; + } + }; + } + + const DecodeLiteralsError = error{ + MalformedLiteralsLength, + PrefixNotFound, + } || LiteralBitsError; + /// Decode `len` bytes of literals into `dest`. `literals` should be the /// `LiteralsSection` that was passed to `prepare()`. Returns /// `error.MalformedLiteralsLength` if the number of literal bytes decoded by /// `self` plus `len` is greater than the regenerated size of `literals`. /// Returns `error.UnexpectedEndOfLiteralStream` and `error.PrefixNotFound` if /// there are problems decoding Huffman compressed literals. - pub fn decodeLiteralsSlice(self: *DecodeState, dest: []u8, literals: LiteralsSection, len: usize) !void { - if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; + pub fn decodeLiteralsSlice( + self: *DecodeState, + dest: []u8, + literals: LiteralsSection, + len: usize, + ) DecodeLiteralsError!void { + if (self.literal_written_count + len > literals.header.regenerated_size) + return error.MalformedLiteralsLength; + switch (literals.header.block_type) { .raw => { - const literal_data = literals.streams.one[self.literal_written_count .. self.literal_written_count + len]; + const literals_end = self.literal_written_count + len; + const literal_data = literals.streams.one[self.literal_written_count..literals_end]; std.mem.copy(u8, dest, literal_data); self.literal_written_count += len; }, @@ -395,15 +462,7 @@ pub const DecodeState = struct { while (i < len) : (i += 1) { var prefix: u16 = 0; while (true) { - const new_bits = self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch |err| - switch (err) { - error.EndOfStream => if (literals.streams == .four and self.literal_stream_index < 3) bits: { - try self.nextLiteralMultiStream(literals); - break :bits try self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read); - } else { - return error.UnexpectedEndOfLiteralStream; - }, - }; + const new_bits = try self.readLiteralsBits(u16, bit_count_to_read, literals); prefix <<= bit_count_to_read; prefix |= new_bits; bits_read += bit_count_to_read; @@ -434,11 +493,19 @@ pub const DecodeState = struct { } /// Decode literals into `dest`; see `decodeLiteralsSlice()`. - pub fn decodeLiteralsRingBuffer(self: *DecodeState, dest: *RingBuffer, literals: LiteralsSection, len: usize) !void { - if (self.literal_written_count + len > literals.header.regenerated_size) return error.MalformedLiteralsLength; + pub fn decodeLiteralsRingBuffer( + self: *DecodeState, + dest: *RingBuffer, + literals: LiteralsSection, + len: usize, + ) DecodeLiteralsError!void { + if (self.literal_written_count + len > literals.header.regenerated_size) + return error.MalformedLiteralsLength; + switch (literals.header.block_type) { .raw => { - const literal_data = literals.streams.one[self.literal_written_count .. self.literal_written_count + len]; + const literals_end = self.literal_written_count + len; + const literal_data = literals.streams.one[self.literal_written_count..literals_end]; dest.writeSliceAssumeCapacity(literal_data); self.literal_written_count += len; }, @@ -464,15 +531,7 @@ pub const DecodeState = struct { while (i < len) : (i += 1) { var prefix: u16 = 0; while (true) { - const new_bits = self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch |err| - switch (err) { - error.EndOfStream => if (literals.streams == .four and self.literal_stream_index < 3) bits: { - try self.nextLiteralMultiStream(literals); - break :bits try self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read); - } else { - return error.UnexpectedEndOfLiteralStream; - }, - }; + const new_bits = try self.readLiteralsBits(u16, bit_count_to_read, literals); prefix <<= bit_count_to_read; prefix |= new_bits; bits_read += bit_count_to_read; @@ -514,6 +573,11 @@ const literal_table_size_max = 1 << types.compressed_block.table_accuracy_log_ma const match_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; const offset_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; +const FrameError = error{ + DictionaryIdFlagUnsupported, + ChecksumFailure, +} || InvalidBit || DecodeBlockError; + /// Decode a Zstandard frame from `src` into `dest`, returning the number of /// bytes read from `src` and written to `dest`; if the frame does not declare /// its decompressed content size `error.UnknownContentSizeUnsupported` is @@ -521,7 +585,11 @@ const offset_table_size_max = 1 << types.compressed_block.table_accuracy_log_max /// dictionary, and `error.ChecksumFailure` if `verify_checksum` is `true` and /// the frame contains a checksum that does not match the checksum computed from /// the decompressed frame. -pub fn decodeZStandardFrame(dest: []u8, src: []const u8, verify_checksum: bool) !ReadWriteCount { +pub fn decodeZStandardFrame( + dest: []u8, + src: []const u8, + verify_checksum: bool, +) (error{ UnknownContentSizeUnsupported, ContentTooLarge } || FrameError)!ReadWriteCount { assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; @@ -530,13 +598,11 @@ pub fn decodeZStandardFrame(dest: []u8, src: []const u8, verify_checksum: bool) if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; const content_size = frame_header.content_size orelse return error.UnknownContentSizeUnsupported; - // const window_size = frameWindowSize(header) orelse return error.WindowSizeUnknown; if (dest.len < content_size) return error.ContentTooLarge; const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; var hash_state = if (should_compute_checksum) std.hash.XxHash64.init(0) else undefined; - // TODO: block_maximum_size should be @min(1 << 17, window_size); const written_count = try decodeFrameBlocks( dest, src[consumed_count..], @@ -567,7 +633,7 @@ pub fn decodeZStandardFrameAlloc( src: []const u8, verify_checksum: bool, window_size_max: usize, -) ![]u8 { +) (error{ WindowSizeUnknown, WindowTooLarge, OutOfMemory } || FrameError)![]u8 { var result = std.ArrayList(u8).init(allocator); assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; @@ -628,7 +694,7 @@ pub fn decodeZStandardFrameAlloc( block_header = decodeBlockHeader(src[consumed_count..][0..3]); consumed_count += 3; }) { - if (block_header.block_size > block_size_maximum) return error.CompressedBlockSizeOverMaximum; + if (block_header.block_size > block_size_maximum) return error.BlockSizeOverMaximum; const written_size = try decodeBlockRingBuffer( &ring_buffer, src[consumed_count..], @@ -637,7 +703,7 @@ pub fn decodeZStandardFrameAlloc( &consumed_count, block_size_maximum, ); - if (written_size > block_size_maximum) return error.DecompressedBlockSizeOverMaximum; + if (written_size > block_size_maximum) return error.BlockSizeOverMaximum; const written_slice = ring_buffer.sliceLast(written_size); try result.appendSlice(written_slice.first); try result.appendSlice(written_slice.second); @@ -650,8 +716,21 @@ pub fn decodeZStandardFrameAlloc( return result.toOwnedSlice(); } +const DecodeBlockError = error{ + BlockSizeOverMaximum, + MalformedBlockSize, + ReservedBlock, + MalformedRleBlock, + MalformedCompressedBlock, +}; + /// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. -pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, hash: ?*std.hash.XxHash64) !usize { +pub fn decodeFrameBlocks( + dest: []u8, + src: []const u8, + consumed_count: *usize, + hash: ?*std.hash.XxHash64, +) DecodeBlockError!usize { // These tables take 7680 bytes var literal_fse_data: [literal_table_size_max]Table.Fse = undefined; var match_fse_data: [match_table_size_max]Table.Fse = undefined; @@ -702,7 +781,12 @@ pub fn decodeFrameBlocks(dest: []u8, src: []const u8, consumed_count: *usize, ha return written_count; } -fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) !usize { +fn decodeRawBlock( + dest: []u8, + src: []const u8, + block_size: u21, + consumed_count: *usize, +) error{MalformedBlockSize}!usize { if (src.len < block_size) return error.MalformedBlockSize; const data = src[0..block_size]; std.mem.copy(u8, dest, data); @@ -710,7 +794,12 @@ fn decodeRawBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: return block_size; } -fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) !usize { +fn decodeRawBlockRingBuffer( + dest: *RingBuffer, + src: []const u8, + block_size: u21, + consumed_count: *usize, +) error{MalformedBlockSize}!usize { if (src.len < block_size) return error.MalformedBlockSize; const data = src[0..block_size]; dest.writeSliceAssumeCapacity(data); @@ -718,7 +807,12 @@ fn decodeRawBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, return block_size; } -fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: *usize) !usize { +fn decodeRleBlock( + dest: []u8, + src: []const u8, + block_size: u21, + consumed_count: *usize, +) error{MalformedRleBlock}!usize { if (src.len < 1) return error.MalformedRleBlock; var write_pos: usize = 0; while (write_pos < block_size) : (write_pos += 1) { @@ -728,7 +822,12 @@ fn decodeRleBlock(dest: []u8, src: []const u8, block_size: u21, consumed_count: return block_size; } -fn decodeRleBlockRingBuffer(dest: *RingBuffer, src: []const u8, block_size: u21, consumed_count: *usize) !usize { +fn decodeRleBlockRingBuffer( + dest: *RingBuffer, + src: []const u8, + block_size: u21, + consumed_count: *usize, +) error{MalformedRleBlock}!usize { if (src.len < 1) return error.MalformedRleBlock; var write_pos: usize = 0; while (write_pos < block_size) : (write_pos += 1) { @@ -749,7 +848,7 @@ pub fn decodeBlock( decode_state: *DecodeState, consumed_count: *usize, written_count: usize, -) !usize { +) DecodeBlockError!usize { const block_size_max = @min(1 << 17, dest[written_count..].len); // 128KiB const block_size = block_header.block_size; if (block_size_max < block_size) return error.BlockSizeOverMaximum; @@ -759,31 +858,33 @@ pub fn decodeBlock( .compressed => { if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; - const literals = try decodeLiteralsSection(src, &bytes_read); - const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); + const literals = decodeLiteralsSection(src, &bytes_read) catch return error.MalformedCompressedBlock; + const sequences_header = decodeSequencesHeader(src[bytes_read..], &bytes_read) catch + return error.MalformedCompressedBlock; - bytes_read += try decode_state.prepare(src[bytes_read..], literals, sequences_header); + bytes_read += decode_state.prepare(src[bytes_read..], literals, sequences_header) catch + return error.MalformedCompressedBlock; var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { const bit_stream_bytes = src[bytes_read..block_size]; var bit_stream: ReverseBitReader = undefined; - try bit_stream.init(bit_stream_bytes); + bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; - try decode_state.readInitialFseState(&bit_stream); + decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; var sequence_size_limit = block_size_max; var i: usize = 0; while (i < sequences_header.sequence_count) : (i += 1) { const write_pos = written_count + bytes_written; - const decompressed_size = try decode_state.decodeSequenceSlice( + const decompressed_size = decode_state.decodeSequenceSlice( dest, write_pos, literals, &bit_stream, sequence_size_limit, i == sequences_header.sequence_count - 1, - ); + ) catch return error.MalformedCompressedBlock; bytes_written += decompressed_size; sequence_size_limit -= decompressed_size; } @@ -793,7 +894,8 @@ pub fn decodeBlock( if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; - try decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], literals, len); + decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], literals, len) catch + return error.MalformedCompressedBlock; bytes_written += len; } @@ -802,7 +904,7 @@ pub fn decodeBlock( consumed_count.* += bytes_read; return bytes_written; }, - .reserved => return error.FrameContainsReservedBlock, + .reserved => return error.ReservedBlock, } } @@ -816,7 +918,7 @@ pub fn decodeBlockRingBuffer( decode_state: *DecodeState, consumed_count: *usize, block_size_max: usize, -) !usize { +) DecodeBlockError!usize { const block_size = block_header.block_size; if (block_size_max < block_size) return error.BlockSizeOverMaximum; switch (block_header.block_type) { @@ -825,29 +927,31 @@ pub fn decodeBlockRingBuffer( .compressed => { if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; - const literals = try decodeLiteralsSection(src, &bytes_read); - const sequences_header = try decodeSequencesHeader(src[bytes_read..], &bytes_read); + const literals = decodeLiteralsSection(src, &bytes_read) catch return error.MalformedCompressedBlock; + const sequences_header = decodeSequencesHeader(src[bytes_read..], &bytes_read) catch + return error.MalformedCompressedBlock; - bytes_read += try decode_state.prepare(src[bytes_read..], literals, sequences_header); + bytes_read += decode_state.prepare(src[bytes_read..], literals, sequences_header) catch + return error.MalformedCompressedBlock; var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { const bit_stream_bytes = src[bytes_read..block_size]; var bit_stream: ReverseBitReader = undefined; - try bit_stream.init(bit_stream_bytes); + bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; - try decode_state.readInitialFseState(&bit_stream); + decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; var sequence_size_limit = block_size_max; var i: usize = 0; while (i < sequences_header.sequence_count) : (i += 1) { - const decompressed_size = try decode_state.decodeSequenceRingBuffer( + const decompressed_size = decode_state.decodeSequenceRingBuffer( dest, literals, &bit_stream, sequence_size_limit, i == sequences_header.sequence_count - 1, - ); + ) catch return error.MalformedCompressedBlock; bytes_written += decompressed_size; sequence_size_limit -= decompressed_size; } @@ -857,7 +961,8 @@ pub fn decodeBlockRingBuffer( if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; - try decode_state.decodeLiteralsRingBuffer(dest, literals, len); + decode_state.decodeLiteralsRingBuffer(dest, literals, len) catch + return error.MalformedCompressedBlock; bytes_written += len; } @@ -866,7 +971,7 @@ pub fn decodeBlockRingBuffer( consumed_count.* += bytes_read; return bytes_written; }, - .reserved => return error.FrameContainsReservedBlock, + .reserved => return error.ReservedBlock, } } @@ -901,9 +1006,10 @@ pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { } else return header.content_size; } +const InvalidBit = error{ UnusedBitSet, ReservedBitSet }; /// Decode the header of a Zstandard frame. Returns `error.UnusedBitSet` or /// `error.ReservedBitSet` if the corresponding bits are sets. -pub fn decodeZStandardHeader(src: []const u8, consumed_count: ?*usize) !frame.ZStandard.Header { +pub fn decodeZStandardHeader(src: []const u8, consumed_count: ?*usize) InvalidBit!frame.ZStandard.Header { const descriptor = @bitCast(frame.ZStandard.Header.Descriptor, src[0]); if (descriptor.unused) return error.UnusedBitSet; @@ -958,7 +1064,10 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { /// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the /// number of bytes the section uses. -pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsSection { +pub fn decodeLiteralsSection( + src: []const u8, + consumed_count: *usize, +) (error{ MalformedLiteralsHeader, MalformedLiteralsSection } || DecodeHuffmanError)!LiteralsSection { var bytes_read: usize = 0; const header = try decodeLiteralsHeader(src, &bytes_read); switch (header.block_type) { @@ -1032,7 +1141,13 @@ pub fn decodeLiteralsSection(src: []const u8, consumed_count: *usize) !LiteralsS } } -fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.HuffmanTree { +const DecodeHuffmanError = error{ + MalformedHuffmanTree, + MalformedFseTable, + MalformedAccuracyLog, +}; + +fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) DecodeHuffmanError!LiteralsSection.HuffmanTree { var bytes_read: usize = 0; bytes_read += 1; if (src.len == 0) return error.MalformedHuffmanTree; @@ -1049,22 +1164,25 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H var bit_reader = bitReader(counting_reader.reader()); var entries: [1 << 6]Table.Fse = undefined; - const table_size = try decodeFseTable(&bit_reader, 256, 6, &entries); + const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { + error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, + error.EndOfStream => return error.MalformedFseTable, + }; const accuracy_log = std.math.log2_int_ceil(usize, table_size); const start_index = std.math.cast(usize, 1 + counting_reader.bytes_read) orelse return error.MalformedHuffmanTree; var huff_data = src[start_index .. compressed_size + 1]; var huff_bits: ReverseBitReader = undefined; - try huff_bits.init(huff_data); + huff_bits.init(huff_data) catch return error.MalformedHuffmanTree; var i: usize = 0; - var even_state: u32 = try huff_bits.readBitsNoEof(u32, accuracy_log); - var odd_state: u32 = try huff_bits.readBitsNoEof(u32, accuracy_log); + var even_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; + var odd_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; while (i < 255) { const even_data = entries[even_state]; var read_bits: usize = 0; - const even_bits = try huff_bits.readBits(u32, even_data.bits, &read_bits); + const even_bits = huff_bits.readBits(u32, even_data.bits, &read_bits) catch unreachable; weights[i] = std.math.cast(u4, even_data.symbol) orelse return error.MalformedHuffmanTree; i += 1; if (read_bits < even_data.bits) { @@ -1076,7 +1194,7 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) !LiteralsSection.H read_bits = 0; const odd_data = entries[odd_state]; - const odd_bits = try huff_bits.readBits(u32, odd_data.bits, &read_bits); + const odd_bits = huff_bits.readBits(u32, odd_data.bits, &read_bits) catch unreachable; weights[i] = std.math.cast(u4, odd_data.symbol) orelse return error.MalformedHuffmanTree; i += 1; if (read_bits < odd_data.bits) { @@ -1177,8 +1295,8 @@ fn lessThanByWeight( } /// Decode a literals section header. -pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) !LiteralsSection.Header { - if (src.len == 0) return error.MalformedLiteralsSection; +pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) error{MalformedLiteralsHeader}!LiteralsSection.Header { + if (src.len == 0) return error.MalformedLiteralsHeader; const byte0 = src[0]; const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); const size_format = @intCast(u2, (byte0 & 0b1100) >> 2); @@ -1243,8 +1361,11 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) !LiteralsSe } /// Decode a sequences section header. -pub fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !SequencesSection.Header { - if (src.len == 0) return error.MalformedSequencesSection; +pub fn decodeSequencesHeader( + src: []const u8, + consumed_count: *usize, +) error{ MalformedSequencesHeader, ReservedBitSet }!SequencesSection.Header { + if (src.len == 0) return error.MalformedSequencesHeader; var sequence_count: u24 = undefined; var bytes_read: usize = 0; @@ -1262,16 +1383,16 @@ pub fn decodeSequencesHeader(src: []const u8, consumed_count: *usize) !Sequences sequence_count = byte0; bytes_read += 1; } else if (byte0 < 255) { - if (src.len < 2) return error.MalformedSequencesSection; + if (src.len < 2) return error.MalformedSequencesHeader; sequence_count = (@as(u24, (byte0 - 128)) << 8) + src[1]; bytes_read += 2; } else { - if (src.len < 3) return error.MalformedSequencesSection; + if (src.len < 3) return error.MalformedSequencesHeader; sequence_count = src[1] + (@as(u24, src[2]) << 8) + 0x7F00; bytes_read += 3; } - if (src.len < bytes_read + 1) return error.MalformedSequencesSection; + if (src.len < bytes_read + 1) return error.MalformedSequencesHeader; const compression_modes = src[bytes_read]; bytes_read += 1; @@ -1441,17 +1562,17 @@ pub const ReverseBitReader = struct { byte_reader: ReversedByteReader, bit_reader: std.io.BitReader(.Big, ReversedByteReader.Reader), - pub fn init(self: *ReverseBitReader, bytes: []const u8) !void { + pub fn init(self: *ReverseBitReader, bytes: []const u8) error{BitStreamHasNoStartBit}!void { self.byte_reader = ReversedByteReader.init(bytes); self.bit_reader = std.io.bitReader(.Big, self.byte_reader.reader()); while (0 == self.readBitsNoEof(u1, 1) catch return error.BitStreamHasNoStartBit) {} } - pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { + pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) error{EndOfStream}!U { return self.bit_reader.readBitsNoEof(U, num_bits); } - pub fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { + pub fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) error{}!U { return try self.bit_reader.readBits(U, num_bits, out_bits); } diff --git a/lib/std/compress/zstandard/types.zig b/lib/std/compress/zstandard/types.zig index f703dc29eb..37a716a9d7 100644 --- a/lib/std/compress/zstandard/types.zig +++ b/lib/std/compress/zstandard/types.zig @@ -92,7 +92,7 @@ pub const compressed_block = struct { index: usize, }; - pub fn query(self: HuffmanTree, index: usize, prefix: u16) !Result { + pub fn query(self: HuffmanTree, index: usize, prefix: u16) error{PrefixNotFound}!Result { var node = self.nodes[index]; const weight = node.weight; var i: usize = index; From e92575d3d47b2701d0b93aa0f044caade57b71c8 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sat, 28 Jan 2023 22:02:08 +1100 Subject: [PATCH 029/122] std.compress.zstandard: verify checksum in decodeFrameAlloc() --- lib/std/compress/zstandard/decompress.zig | 33 ++++++++++++++--------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index ea72e33730..090110f9d0 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -573,6 +573,11 @@ const literal_table_size_max = 1 << types.compressed_block.table_accuracy_log_ma const match_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; const offset_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; +pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { + const hash = hasher.final(); + return @intCast(u32, hash & 0xFFFFFFFF); +} + const FrameError = error{ DictionaryIdFlagUnsupported, ChecksumFailure, @@ -601,24 +606,20 @@ pub fn decodeZStandardFrame( if (dest.len < content_size) return error.ContentTooLarge; const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; - var hash_state = if (should_compute_checksum) std.hash.XxHash64.init(0) else undefined; + var hasher_opt = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; const written_count = try decodeFrameBlocks( dest, src[consumed_count..], &consumed_count, - if (should_compute_checksum) &hash_state else null, + if (hasher_opt) |*hasher| hasher else null, ); if (frame_header.descriptor.content_checksum_flag) { const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); consumed_count += 4; - if (verify_checksum) { - const hash = hash_state.final(); - const hash_low_bytes = hash & 0xFFFFFFFF; - if (checksum != hash_low_bytes) { - return error.ChecksumFailure; - } + if (hasher_opt) |*hasher| { + if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; } } return ReadWriteCount{ .read_count = consumed_count, .write_count = written_count }; @@ -649,7 +650,7 @@ pub fn decodeZStandardFrameAlloc( @intCast(usize, window_size_raw); const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; - var hash = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; + var hasher_opt = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; const block_size_maximum = @min(1 << 17, window_size); @@ -707,12 +708,20 @@ pub fn decodeZStandardFrameAlloc( const written_slice = ring_buffer.sliceLast(written_size); try result.appendSlice(written_slice.first); try result.appendSlice(written_slice.second); - if (hash) |*hash_state| { - hash_state.update(written_slice.first); - hash_state.update(written_slice.second); + if (hasher_opt) |*hasher| { + hasher.update(written_slice.first); + hasher.update(written_slice.second); } if (block_header.last_block) break; } + + if (frame_header.descriptor.content_checksum_flag) { + const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); + consumed_count += 4; + if (hasher_opt) |*hasher| { + if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; + } + } return result.toOwnedSlice(); } From 2d35c16ee7e4a8f69bbbe19b2a48cc03aed755c8 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 31 Jan 2023 12:54:05 +1100 Subject: [PATCH 030/122] std.compress.zstandard: add init/deinit for ring buffer, fix len() --- lib/std/compress/zstandard/RingBuffer.zig | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/std/compress/zstandard/RingBuffer.zig b/lib/std/compress/zstandard/RingBuffer.zig index 070c440b0f..cf70c087b6 100644 --- a/lib/std/compress/zstandard/RingBuffer.zig +++ b/lib/std/compress/zstandard/RingBuffer.zig @@ -4,6 +4,7 @@ //! if full or empty can be distinguised by looking at the different between the read and write //! indices without adding an extra boolean flag or having to reserve a slot in the buffer. +const Allocator = @import("std").mem.Allocator; const assert = @import("std").debug.assert; const RingBuffer = @This(); @@ -12,6 +13,22 @@ data: []u8, read_index: usize, write_index: usize, +/// Allocate a new `RingBuffer` +pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { + const bytes = try allocator.alloc(u8, capacity); + return RingBuffer{ + .data = bytes, + .write_index = 0, + .read_index = 0, + }; +} + +/// Free a `RingBuffer` +pub fn deinit(self: *RingBuffer, allocator: Allocator) void { + allocator.free(self.data); + self.* = undefined; +} + /// Returns `index` modulo the length of the backing slice. pub fn mask(self: RingBuffer, index: usize) usize { return index % self.data.len; @@ -70,7 +87,7 @@ pub fn isFull(self: RingBuffer) bool { /// Returns the length pub fn len(self: RingBuffer) usize { - const adjusted_write_index = self.write_index + @boolToInt(self.write_index < self.read_index) * 2 * self.data.len; + const adjusted_write_index = self.write_index + @as(usize, @boolToInt(self.write_index < self.read_index)) * 2 * self.data.len; return adjusted_write_index - self.read_index; } From 947ad3e26816270fa11298dd5f118e50126a1320 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 31 Jan 2023 13:24:27 +1100 Subject: [PATCH 031/122] std.compress.zstandard: add FrameContext and add literals into DecodeState --- lib/std/compress/zstandard/decompress.zig | 149 +++++++++++----------- lib/std/compress/zstandard/types.zig | 5 + 2 files changed, 83 insertions(+), 71 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 090110f9d0..0caf31fa33 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -81,6 +81,8 @@ pub const DecodeState = struct { literal_stream_reader: ReverseBitReader, literal_stream_index: usize, + literal_streams: LiteralsSection.Streams, + literal_header: LiteralsSection.Header, huffman_tree: ?LiteralsSection.HuffmanTree, literal_written_count: usize, @@ -105,6 +107,10 @@ pub const DecodeState = struct { literals: LiteralsSection, sequences_header: SequencesSection.Header, ) (error{ BitStreamHasNoStartBit, TreelessLiteralsFirst } || FseTableError)!usize { + self.literal_written_count = 0; + self.literal_header = literals.header; + self.literal_streams = literals.streams; + if (literals.huffman_tree) |tree| { self.huffman_tree = tree; } else if (literals.header.block_type == .treeless and self.huffman_tree == null) { @@ -293,12 +299,11 @@ pub const DecodeState = struct { self: *DecodeState, dest: []u8, write_pos: usize, - literals: LiteralsSection, sequence: Sequence, ) (error{MalformedSequence} || DecodeLiteralsError)!void { if (sequence.offset > write_pos + sequence.literal_length) return error.MalformedSequence; - try self.decodeLiteralsSlice(dest[write_pos..], literals, sequence.literal_length); + try self.decodeLiteralsSlice(dest[write_pos..], sequence.literal_length); const copy_start = write_pos + sequence.literal_length - sequence.offset; const copy_end = copy_start + sequence.match_length; // NOTE: we ignore the usage message for std.mem.copy and copy with dest.ptr >= src.ptr @@ -309,12 +314,11 @@ pub const DecodeState = struct { fn executeSequenceRingBuffer( self: *DecodeState, dest: *RingBuffer, - literals: LiteralsSection, sequence: Sequence, ) (error{MalformedSequence} || DecodeLiteralsError)!void { if (sequence.offset > dest.data.len) return error.MalformedSequence; - try self.decodeLiteralsRingBuffer(dest, literals, sequence.literal_length); + try self.decodeLiteralsRingBuffer(dest, sequence.literal_length); const copy_start = dest.write_index + dest.data.len - sequence.offset; const copy_slice = dest.sliceAt(copy_start, sequence.match_length); // TODO: would std.mem.copy and figuring out dest slice be better/faster? @@ -328,6 +332,7 @@ pub const DecodeState = struct { MalformedSequence, MalformedFseBits, } || DecodeLiteralsError; + /// Decode one sequence from `bit_reader` into `dest`, written starting at /// `write_pos` and update FSE states if `last_sequence` is `false`. Returns /// `error.MalformedSequence` error if the decompressed sequence would be longer @@ -340,7 +345,6 @@ pub const DecodeState = struct { self: *DecodeState, dest: []u8, write_pos: usize, - literals: LiteralsSection, bit_reader: *ReverseBitReader, sequence_size_limit: usize, last_sequence: bool, @@ -349,7 +353,7 @@ pub const DecodeState = struct { const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; if (sequence_length > sequence_size_limit) return error.MalformedSequence; - try self.executeSequenceSlice(dest, write_pos, literals, sequence); + try self.executeSequenceSlice(dest, write_pos, sequence); if (!last_sequence) { try self.updateState(.literal, bit_reader); try self.updateState(.match, bit_reader); @@ -362,7 +366,6 @@ pub const DecodeState = struct { pub fn decodeSequenceRingBuffer( self: *DecodeState, dest: *RingBuffer, - literals: LiteralsSection, bit_reader: anytype, sequence_size_limit: usize, last_sequence: bool, @@ -371,7 +374,7 @@ pub const DecodeState = struct { const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; if (sequence_length > sequence_size_limit) return error.MalformedSequence; - try self.executeSequenceRingBuffer(dest, literals, sequence); + try self.executeSequenceRingBuffer(dest, sequence); if (!last_sequence) { try self.updateState(.literal, bit_reader); try self.updateState(.match, bit_reader); @@ -382,13 +385,12 @@ pub const DecodeState = struct { fn nextLiteralMultiStream( self: *DecodeState, - literals: LiteralsSection, ) error{BitStreamHasNoStartBit}!void { self.literal_stream_index += 1; - try self.initLiteralStream(literals.streams.four[self.literal_stream_index]); + try self.initLiteralStream(self.literal_streams.four[self.literal_stream_index]); } - fn initLiteralStream(self: *DecodeState, bytes: []const u8) error{BitStreamHasNoStartBit}!void { + pub fn initLiteralStream(self: *DecodeState, bytes: []const u8) error{BitStreamHasNoStartBit}!void { try self.literal_stream_reader.init(bytes); } @@ -400,11 +402,10 @@ pub const DecodeState = struct { self: *DecodeState, comptime T: type, bit_count_to_read: usize, - literals: LiteralsSection, ) LiteralBitsError!T { return self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch bits: { - if (literals.streams == .four and self.literal_stream_index < 3) { - try self.nextLiteralMultiStream(literals); + if (self.literal_streams == .four and self.literal_stream_index < 3) { + try self.nextLiteralMultiStream(); break :bits self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch return error.UnexpectedEndOfLiteralStream; } else { @@ -427,23 +428,22 @@ pub const DecodeState = struct { pub fn decodeLiteralsSlice( self: *DecodeState, dest: []u8, - literals: LiteralsSection, len: usize, ) DecodeLiteralsError!void { - if (self.literal_written_count + len > literals.header.regenerated_size) + if (self.literal_written_count + len > self.literal_header.regenerated_size) return error.MalformedLiteralsLength; - switch (literals.header.block_type) { + switch (self.literal_header.block_type) { .raw => { const literals_end = self.literal_written_count + len; - const literal_data = literals.streams.one[self.literal_written_count..literals_end]; + const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; std.mem.copy(u8, dest, literal_data); self.literal_written_count += len; }, .rle => { var i: usize = 0; while (i < len) : (i += 1) { - dest[i] = literals.streams.one[0]; + dest[i] = self.literal_streams.one[0]; } self.literal_written_count += len; }, @@ -462,7 +462,7 @@ pub const DecodeState = struct { while (i < len) : (i += 1) { var prefix: u16 = 0; while (true) { - const new_bits = try self.readLiteralsBits(u16, bit_count_to_read, literals); + const new_bits = try self.readLiteralsBits(u16, bit_count_to_read); prefix <<= bit_count_to_read; prefix |= new_bits; bits_read += bit_count_to_read; @@ -496,23 +496,22 @@ pub const DecodeState = struct { pub fn decodeLiteralsRingBuffer( self: *DecodeState, dest: *RingBuffer, - literals: LiteralsSection, len: usize, ) DecodeLiteralsError!void { - if (self.literal_written_count + len > literals.header.regenerated_size) + if (self.literal_written_count + len > self.literal_header.regenerated_size) return error.MalformedLiteralsLength; - switch (literals.header.block_type) { + switch (self.literal_header.block_type) { .raw => { const literals_end = self.literal_written_count + len; - const literal_data = literals.streams.one[self.literal_written_count..literals_end]; + const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; dest.writeSliceAssumeCapacity(literal_data); self.literal_written_count += len; }, .rle => { var i: usize = 0; while (i < len) : (i += 1) { - dest.writeAssumeCapacity(literals.streams.one[0]); + dest.writeAssumeCapacity(self.literal_streams.one[0]); } self.literal_written_count += len; }, @@ -531,7 +530,7 @@ pub const DecodeState = struct { while (i < len) : (i += 1) { var prefix: u16 = 0; while (true) { - const new_bits = try self.readLiteralsBits(u16, bit_count_to_read, literals); + const new_bits = try self.readLiteralsBits(u16, bit_count_to_read); prefix <<= bit_count_to_read; prefix |= new_bits; bits_read += bit_count_to_read; @@ -569,10 +568,6 @@ pub const DecodeState = struct { } }; -const literal_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.literal; -const match_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; -const offset_table_size_max = 1 << types.compressed_block.table_accuracy_log_max.match; - pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { const hash = hasher.final(); return @intCast(u32, hash & 0xFFFFFFFF); @@ -625,6 +620,31 @@ pub fn decodeZStandardFrame( return ReadWriteCount{ .read_count = consumed_count, .write_count = written_count }; } +pub const FrameContext = struct { + hasher_opt: ?std.hash.XxHash64, + window_size: usize, + has_checksum: bool, + block_size_max: usize, + + pub fn init(frame_header: frame.ZStandard.Header, window_size_max: usize, verify_checksum: bool) !FrameContext { + if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; + + const window_size_raw = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; + const window_size = if (window_size_raw > window_size_max) + return error.WindowTooLarge + else + @intCast(usize, window_size_raw); + + const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; + return .{ + .hasher_opt = if (should_compute_checksum) std.hash.XxHash64.init(0) else null, + .window_size = window_size, + .has_checksum = frame_header.descriptor.content_checksum_flag, + .block_size_max = @min(1 << 17, window_size), + }; + } +}; + /// Decode a Zstandard from from `src` and return the decompressed bytes; see /// `decodeZStandardFrame()`. Returns `error.WindowSizeUnknown` if the frame /// does not declare its content size or a window descriptor (this indicates a @@ -639,33 +659,18 @@ pub fn decodeZStandardFrameAlloc( assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; - const frame_header = try decodeZStandardHeader(src[consumed_count..], &consumed_count); - - if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; - - const window_size_raw = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; - const window_size = if (window_size_raw > window_size_max) - return error.WindowTooLarge - else - @intCast(usize, window_size_raw); - - const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; - var hasher_opt = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; - - const block_size_maximum = @min(1 << 17, window_size); - - var window_data = try allocator.alloc(u8, window_size); - defer allocator.free(window_data); - var ring_buffer = RingBuffer{ - .data = window_data, - .write_index = 0, - .read_index = 0, + var frame_context = context: { + const frame_header = try decodeZStandardHeader(src[consumed_count..], &consumed_count); + break :context try FrameContext.init(frame_header, window_size_max, verify_checksum); }; + var ring_buffer = try RingBuffer.init(allocator, frame_context.window_size); + defer ring_buffer.deinit(allocator); + // These tables take 7680 bytes - var literal_fse_data: [literal_table_size_max]Table.Fse = undefined; - var match_fse_data: [match_table_size_max]Table.Fse = undefined; - var offset_fse_data: [offset_table_size_max]Table.Fse = undefined; + var literal_fse_data: [types.compressed_block.table_size_max.literal]Table.Fse = undefined; + var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; + var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; var block_header = decodeBlockHeader(src[consumed_count..][0..3]); consumed_count += 3; @@ -687,6 +692,8 @@ pub fn decodeZStandardFrameAlloc( .fse_tables_undefined = true, .literal_written_count = 0, + .literal_header = undefined, + .literal_streams = undefined, .literal_stream_reader = undefined, .literal_stream_index = undefined, .huffman_tree = null, @@ -695,30 +702,29 @@ pub fn decodeZStandardFrameAlloc( block_header = decodeBlockHeader(src[consumed_count..][0..3]); consumed_count += 3; }) { - if (block_header.block_size > block_size_maximum) return error.BlockSizeOverMaximum; + if (block_header.block_size > frame_context.block_size_max) return error.BlockSizeOverMaximum; const written_size = try decodeBlockRingBuffer( &ring_buffer, src[consumed_count..], block_header, &decode_state, &consumed_count, - block_size_maximum, + frame_context.block_size_max, ); - if (written_size > block_size_maximum) return error.BlockSizeOverMaximum; const written_slice = ring_buffer.sliceLast(written_size); try result.appendSlice(written_slice.first); try result.appendSlice(written_slice.second); - if (hasher_opt) |*hasher| { + if (frame_context.hasher_opt) |*hasher| { hasher.update(written_slice.first); hasher.update(written_slice.second); } if (block_header.last_block) break; } - if (frame_header.descriptor.content_checksum_flag) { + if (frame_context.has_checksum) { const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); consumed_count += 4; - if (hasher_opt) |*hasher| { + if (frame_context.hasher_opt) |*hasher| { if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; } } @@ -741,9 +747,9 @@ pub fn decodeFrameBlocks( hash: ?*std.hash.XxHash64, ) DecodeBlockError!usize { // These tables take 7680 bytes - var literal_fse_data: [literal_table_size_max]Table.Fse = undefined; - var match_fse_data: [match_table_size_max]Table.Fse = undefined; - var offset_fse_data: [offset_table_size_max]Table.Fse = undefined; + var literal_fse_data: [types.compressed_block.table_size_max.literal]Table.Fse = undefined; + var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; + var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; var block_header = decodeBlockHeader(src[0..3]); var bytes_read: usize = 3; @@ -766,6 +772,8 @@ pub fn decodeFrameBlocks( .fse_tables_undefined = true, .literal_written_count = 0, + .literal_header = undefined, + .literal_streams = undefined, .literal_stream_reader = undefined, .literal_stream_index = undefined, .huffman_tree = null, @@ -867,7 +875,8 @@ pub fn decodeBlock( .compressed => { if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; - const literals = decodeLiteralsSection(src, &bytes_read) catch return error.MalformedCompressedBlock; + const literals = decodeLiteralsSection(src, &bytes_read) catch + return error.MalformedCompressedBlock; const sequences_header = decodeSequencesHeader(src[bytes_read..], &bytes_read) catch return error.MalformedCompressedBlock; @@ -889,7 +898,6 @@ pub fn decodeBlock( const decompressed_size = decode_state.decodeSequenceSlice( dest, write_pos, - literals, &bit_stream, sequence_size_limit, i == sequences_header.sequence_count - 1, @@ -903,12 +911,11 @@ pub fn decodeBlock( if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; - decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], literals, len) catch + decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], len) catch return error.MalformedCompressedBlock; bytes_written += len; } - decode_state.literal_written_count = 0; assert(bytes_read == block_header.block_size); consumed_count.* += bytes_read; return bytes_written; @@ -936,7 +943,8 @@ pub fn decodeBlockRingBuffer( .compressed => { if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; - const literals = decodeLiteralsSection(src, &bytes_read) catch return error.MalformedCompressedBlock; + const literals = decodeLiteralsSection(src, &bytes_read) catch + return error.MalformedCompressedBlock; const sequences_header = decodeSequencesHeader(src[bytes_read..], &bytes_read) catch return error.MalformedCompressedBlock; @@ -956,7 +964,6 @@ pub fn decodeBlockRingBuffer( while (i < sequences_header.sequence_count) : (i += 1) { const decompressed_size = decode_state.decodeSequenceRingBuffer( dest, - literals, &bit_stream, sequence_size_limit, i == sequences_header.sequence_count - 1, @@ -970,14 +977,14 @@ pub fn decodeBlockRingBuffer( if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; - decode_state.decodeLiteralsRingBuffer(dest, literals, len) catch + decode_state.decodeLiteralsRingBuffer(dest, len) catch return error.MalformedCompressedBlock; bytes_written += len; } - decode_state.literal_written_count = 0; assert(bytes_read == block_header.block_size); consumed_count.* += bytes_read; + if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; return bytes_written; }, .reserved => return error.ReservedBlock, diff --git a/lib/std/compress/zstandard/types.zig b/lib/std/compress/zstandard/types.zig index 37a716a9d7..d94a55ebe5 100644 --- a/lib/std/compress/zstandard/types.zig +++ b/lib/std/compress/zstandard/types.zig @@ -386,6 +386,11 @@ pub const compressed_block = struct { pub const match = 6; pub const offset = 5; }; + pub const table_size_max = struct { + pub const literal = 1 << table_accuracy_log_max.literal; + pub const match = 1 << table_accuracy_log_max.match; + pub const offset = 1 << table_accuracy_log_max.match; + }; }; test { From 5723291444116419440a187adcfa5ecb9557544e Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 2 Feb 2023 16:19:13 +1100 Subject: [PATCH 032/122] std.compress.zstandard: add `decodeBlockReader` --- lib/std/compress/zstandard/decompress.zig | 822 ++++++++++++---------- 1 file changed, 462 insertions(+), 360 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 0caf31fa33..e32f4d0282 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -14,29 +14,18 @@ fn readVarInt(comptime T: type, bytes: []const u8) T { return std.mem.readVarInt(T, bytes, .Little); } -fn isSkippableMagic(magic: u32) bool { +pub fn isSkippableMagic(magic: u32) bool { return frame.Skippable.magic_number_min <= magic and magic <= frame.Skippable.magic_number_max; } -/// Returns the decompressed size of the frame at the start of `src`. Returns 0 -/// if the the frame is skippable, `null` for Zstanndard frames that do not -/// declare their content size. Returns `UnusedBitSet` and `ReservedBitSet` -/// errors if the respective bits of the the frame descriptor are set. -pub fn getFrameDecompressedSize(src: []const u8) (InvalidBit || error{BadMagic})!?u64 { - switch (try frameType(src)) { - .zstandard => { - const header = try decodeZStandardHeader(src[4..], null); - return header.content_size; - }, - .skippable => return 0, - } -} - -/// Returns the kind of frame at the beginning of `src`. Returns `BadMagic` if -/// `src` begin with bytes not equal to the Zstandard frame magic number, or -/// outside the range of magic numbers for skippable frames. -pub fn frameType(src: []const u8) error{BadMagic}!frame.Kind { - const magic = readInt(u32, src[0..4]); +/// Returns the kind of frame at the beginning of `src`. +/// +/// Errors: +/// - returns `error.BadMagic` if `source` begins with bytes not equal to the +/// Zstandard frame magic number, or outside the range of magic numbers for +/// skippable frames. +pub fn decodeFrameType(source: anytype) !frame.Kind { + const magic = try source.readIntLittle(u32); return if (magic == frame.ZStandard.magic_number) .zstandard else if (isSkippableMagic(magic)) @@ -52,15 +41,21 @@ const ReadWriteCount = struct { /// Decodes the frame at the start of `src` into `dest`. Returns the number of /// bytes read from `src` and written to `dest`. +/// +/// Errors: +/// - returns `error.UnknownContentSizeUnsupported` +/// - returns `error.ContentTooLarge` +/// - returns `error.BadMagic` pub fn decodeFrame( dest: []u8, src: []const u8, verify_checksum: bool, -) (error{ UnknownContentSizeUnsupported, ContentTooLarge, BadMagic } || FrameError)!ReadWriteCount { - return switch (try frameType(src)) { +) !ReadWriteCount { + var fbs = std.io.fixedBufferStream(src); + return switch (try decodeFrameType(fbs.reader())) { .zstandard => decodeZStandardFrame(dest, src, verify_checksum), .skippable => ReadWriteCount{ - .read_count = skippableFrameSize(src[0..8]) + 8, + .read_count = try fbs.reader().readIntLittle(u32) + 8, .write_count = 0, }, }; @@ -97,16 +92,52 @@ pub const DecodeState = struct { }; } + pub fn init( + literal_fse_buffer: []Table.Fse, + match_fse_buffer: []Table.Fse, + offset_fse_buffer: []Table.Fse, + ) DecodeState { + return DecodeState{ + .repeat_offsets = .{ + types.compressed_block.start_repeated_offset_1, + types.compressed_block.start_repeated_offset_2, + types.compressed_block.start_repeated_offset_3, + }, + + .offset = undefined, + .match = undefined, + .literal = undefined, + + .literal_fse_buffer = literal_fse_buffer, + .match_fse_buffer = match_fse_buffer, + .offset_fse_buffer = offset_fse_buffer, + + .fse_tables_undefined = true, + + .literal_written_count = 0, + .literal_header = undefined, + .literal_streams = undefined, + .literal_stream_reader = undefined, + .literal_stream_index = undefined, + .huffman_tree = null, + }; + } + /// Prepare the decoder to decode a compressed block. Loads the literals - /// stream and Huffman tree from `literals` and reads the FSE tables from `src`. - /// Returns `error.BitStreamHasNoStartBit` if the (reversed) literal bitstream's - /// first byte does not have any bits set. + /// stream and Huffman tree from `literals` and reads the FSE tables from + /// `source`. + /// + /// Errors: + /// - returns `error.BitStreamHasNoStartBit` if the (reversed) literal bitstream's + /// first byte does not have any bits set. + /// - returns `error.TreelessLiteralsFirst` `literals` is a treeless literals section + /// and the decode state does not have a Huffman tree from a previous block. pub fn prepare( self: *DecodeState, - src: []const u8, + source: anytype, literals: LiteralsSection, sequences_header: SequencesSection.Header, - ) (error{ BitStreamHasNoStartBit, TreelessLiteralsFirst } || FseTableError)!usize { + ) !void { self.literal_written_count = 0; self.literal_header = literals.header; self.literal_streams = literals.streams; @@ -129,28 +160,11 @@ pub const DecodeState = struct { } if (sequences_header.sequence_count > 0) { - var bytes_read = try self.updateFseTable( - src, - .literal, - sequences_header.literal_lengths, - ); - - bytes_read += try self.updateFseTable( - src[bytes_read..], - .offset, - sequences_header.offsets, - ); - - bytes_read += try self.updateFseTable( - src[bytes_read..], - .match, - sequences_header.match_lengths, - ); + try self.updateFseTable(source, .literal, sequences_header.literal_lengths); + try self.updateFseTable(source, .offset, sequences_header.offsets); + try self.updateFseTable(source, .match, sequences_header.match_lengths); self.fse_tables_undefined = false; - - return bytes_read; } - return 0; } /// Read initial FSE states for sequence decoding. Returns `error.EndOfStream` @@ -208,10 +222,10 @@ pub const DecodeState = struct { fn updateFseTable( self: *DecodeState, - src: []const u8, + source: anytype, comptime choice: DataType, mode: SequencesSection.Header.Mode, - ) FseTableError!usize { + ) !void { const field_name = @tagName(choice); switch (mode) { .predefined => { @@ -220,17 +234,13 @@ pub const DecodeState = struct { @field(self, field_name).table = @field(types.compressed_block, "predefined_" ++ field_name ++ "_fse_table"); - return 0; }, .rle => { @field(self, field_name).accuracy_log = 0; - @field(self, field_name).table = .{ .rle = src[0] }; - return 1; + @field(self, field_name).table = .{ .rle = try source.readByte() }; }, .fse => { - var stream = std.io.fixedBufferStream(src); - var counting_reader = std.io.countingReader(stream.reader()); - var bit_reader = bitReader(counting_reader.reader()); + var bit_reader = bitReader(source); const table_size = try decodeFseTable( &bit_reader, @@ -242,9 +252,8 @@ pub const DecodeState = struct { .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size], }; @field(self, field_name).accuracy_log = std.math.log2_int_ceil(usize, table_size); - return std.math.cast(usize, counting_reader.bytes_read) orelse error.MalformedFseTable; }, - .repeat => return if (self.fse_tables_undefined) error.RepeatModeFirst else 0, + .repeat => if (self.fse_tables_undefined) return error.RepeatModeFirst, } } @@ -462,11 +471,15 @@ pub const DecodeState = struct { while (i < len) : (i += 1) { var prefix: u16 = 0; while (true) { - const new_bits = try self.readLiteralsBits(u16, bit_count_to_read); + const new_bits = self.readLiteralsBits(u16, bit_count_to_read) catch |err| { + return err; + }; prefix <<= bit_count_to_read; prefix |= new_bits; bits_read += bit_count_to_read; - const result = try huffman_tree.query(huffman_tree_index, prefix); + const result = huffman_tree.query(huffman_tree_index, prefix) catch |err| { + return err; + }; switch (result) { .symbol => |sym| { @@ -589,11 +602,14 @@ pub fn decodeZStandardFrame( dest: []u8, src: []const u8, verify_checksum: bool, -) (error{ UnknownContentSizeUnsupported, ContentTooLarge } || FrameError)!ReadWriteCount { +) (error{ UnknownContentSizeUnsupported, ContentTooLarge, EndOfStream } || FrameError)!ReadWriteCount { assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; - const frame_header = try decodeZStandardHeader(src[consumed_count..], &consumed_count); + var fbs = std.io.fixedBufferStream(src[consumed_count..]); + var source = fbs.reader(); + const frame_header = try decodeZStandardHeader(source); + consumed_count += fbs.pos; if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; @@ -649,18 +665,25 @@ pub const FrameContext = struct { /// `decodeZStandardFrame()`. Returns `error.WindowSizeUnknown` if the frame /// does not declare its content size or a window descriptor (this indicates a /// malformed frame). +/// +/// Errors: +/// - returns `error.WindowTooLarge` +/// - returns `error.WindowSizeUnknown` pub fn decodeZStandardFrameAlloc( allocator: std.mem.Allocator, src: []const u8, verify_checksum: bool, window_size_max: usize, -) (error{ WindowSizeUnknown, WindowTooLarge, OutOfMemory } || FrameError)![]u8 { +) (error{ WindowSizeUnknown, WindowTooLarge, OutOfMemory, EndOfStream } || FrameError)![]u8 { var result = std.ArrayList(u8).init(allocator); assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; var frame_context = context: { - const frame_header = try decodeZStandardHeader(src[consumed_count..], &consumed_count); + var fbs = std.io.fixedBufferStream(src[consumed_count..]); + var source = fbs.reader(); + const frame_header = try decodeZStandardHeader(source); + consumed_count += fbs.pos; break :context try FrameContext.init(frame_header, window_size_max, verify_checksum); }; @@ -674,30 +697,7 @@ pub fn decodeZStandardFrameAlloc( var block_header = decodeBlockHeader(src[consumed_count..][0..3]); consumed_count += 3; - var decode_state = DecodeState{ - .repeat_offsets = .{ - types.compressed_block.start_repeated_offset_1, - types.compressed_block.start_repeated_offset_2, - types.compressed_block.start_repeated_offset_3, - }, - - .offset = undefined, - .match = undefined, - .literal = undefined, - - .literal_fse_buffer = &literal_fse_data, - .match_fse_buffer = &match_fse_data, - .offset_fse_buffer = &offset_fse_data, - - .fse_tables_undefined = true, - - .literal_written_count = 0, - .literal_header = undefined, - .literal_streams = undefined, - .literal_stream_reader = undefined, - .literal_stream_index = undefined, - .huffman_tree = null, - }; + var decode_state = DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); while (true) : ({ block_header = decodeBlockHeader(src[consumed_count..][0..3]); consumed_count += 3; @@ -754,30 +754,7 @@ pub fn decodeFrameBlocks( var block_header = decodeBlockHeader(src[0..3]); var bytes_read: usize = 3; defer consumed_count.* += bytes_read; - var decode_state = DecodeState{ - .repeat_offsets = .{ - types.compressed_block.start_repeated_offset_1, - types.compressed_block.start_repeated_offset_2, - types.compressed_block.start_repeated_offset_3, - }, - - .offset = undefined, - .match = undefined, - .literal = undefined, - - .literal_fse_buffer = &literal_fse_data, - .match_fse_buffer = &match_fse_data, - .offset_fse_buffer = &offset_fse_data, - - .fse_tables_undefined = true, - - .literal_written_count = 0, - .literal_header = undefined, - .literal_streams = undefined, - .literal_stream_reader = undefined, - .literal_stream_index = undefined, - .huffman_tree = null, - }; + var decode_state = DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); var written_count: usize = 0; while (true) : ({ block_header = decodeBlockHeader(src[bytes_read..][0..3]); @@ -798,62 +775,6 @@ pub fn decodeFrameBlocks( return written_count; } -fn decodeRawBlock( - dest: []u8, - src: []const u8, - block_size: u21, - consumed_count: *usize, -) error{MalformedBlockSize}!usize { - if (src.len < block_size) return error.MalformedBlockSize; - const data = src[0..block_size]; - std.mem.copy(u8, dest, data); - consumed_count.* += block_size; - return block_size; -} - -fn decodeRawBlockRingBuffer( - dest: *RingBuffer, - src: []const u8, - block_size: u21, - consumed_count: *usize, -) error{MalformedBlockSize}!usize { - if (src.len < block_size) return error.MalformedBlockSize; - const data = src[0..block_size]; - dest.writeSliceAssumeCapacity(data); - consumed_count.* += block_size; - return block_size; -} - -fn decodeRleBlock( - dest: []u8, - src: []const u8, - block_size: u21, - consumed_count: *usize, -) error{MalformedRleBlock}!usize { - if (src.len < 1) return error.MalformedRleBlock; - var write_pos: usize = 0; - while (write_pos < block_size) : (write_pos += 1) { - dest[write_pos] = src[0]; - } - consumed_count.* += 1; - return block_size; -} - -fn decodeRleBlockRingBuffer( - dest: *RingBuffer, - src: []const u8, - block_size: u21, - consumed_count: *usize, -) error{MalformedRleBlock}!usize { - if (src.len < 1) return error.MalformedRleBlock; - var write_pos: usize = 0; - while (write_pos < block_size) : (write_pos += 1) { - dest.writeAssumeCapacity(src[0]); - } - consumed_count.* += 1; - return block_size; -} - /// Decode a single block from `src` into `dest`. The beginning of `src` should /// be the start of the block content (i.e. directly after the block header). /// Increments `consumed_count` by the number of bytes read from `src` to decode @@ -870,19 +791,37 @@ pub fn decodeBlock( const block_size = block_header.block_size; if (block_size_max < block_size) return error.BlockSizeOverMaximum; switch (block_header.block_type) { - .raw => return decodeRawBlock(dest[written_count..], src, block_size, consumed_count), - .rle => return decodeRleBlock(dest[written_count..], src, block_size, consumed_count), + .raw => { + if (src.len < block_size) return error.MalformedBlockSize; + const data = src[0..block_size]; + std.mem.copy(u8, dest[written_count..], data); + consumed_count.* += block_size; + return block_size; + }, + .rle => { + if (src.len < 1) return error.MalformedRleBlock; + var write_pos: usize = written_count; + while (write_pos < block_size + written_count) : (write_pos += 1) { + dest[write_pos] = src[0]; + } + consumed_count.* += 1; + return block_size; + }, .compressed => { if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; - const literals = decodeLiteralsSection(src, &bytes_read) catch + const literals = decodeLiteralsSectionSlice(src, &bytes_read) catch return error.MalformedCompressedBlock; - const sequences_header = decodeSequencesHeader(src[bytes_read..], &bytes_read) catch + var fbs = std.io.fixedBufferStream(src[bytes_read..]); + const fbs_reader = fbs.reader(); + const sequences_header = decodeSequencesHeader(fbs_reader) catch return error.MalformedCompressedBlock; - bytes_read += decode_state.prepare(src[bytes_read..], literals, sequences_header) catch + decode_state.prepare(fbs_reader, literals, sequences_header) catch return error.MalformedCompressedBlock; + bytes_read += fbs.pos; + var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { const bit_stream_bytes = src[bytes_read..block_size]; @@ -938,19 +877,37 @@ pub fn decodeBlockRingBuffer( const block_size = block_header.block_size; if (block_size_max < block_size) return error.BlockSizeOverMaximum; switch (block_header.block_type) { - .raw => return decodeRawBlockRingBuffer(dest, src, block_size, consumed_count), - .rle => return decodeRleBlockRingBuffer(dest, src, block_size, consumed_count), + .raw => { + if (src.len < block_size) return error.MalformedBlockSize; + const data = src[0..block_size]; + dest.writeSliceAssumeCapacity(data); + consumed_count.* += block_size; + return block_size; + }, + .rle => { + if (src.len < 1) return error.MalformedRleBlock; + var write_pos: usize = 0; + while (write_pos < block_size) : (write_pos += 1) { + dest.writeAssumeCapacity(src[0]); + } + consumed_count.* += 1; + return block_size; + }, .compressed => { if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; - const literals = decodeLiteralsSection(src, &bytes_read) catch + const literals = decodeLiteralsSectionSlice(src, &bytes_read) catch return error.MalformedCompressedBlock; - const sequences_header = decodeSequencesHeader(src[bytes_read..], &bytes_read) catch + var fbs = std.io.fixedBufferStream(src[bytes_read..]); + const fbs_reader = fbs.reader(); + const sequences_header = decodeSequencesHeader(fbs_reader) catch return error.MalformedCompressedBlock; - bytes_read += decode_state.prepare(src[bytes_read..], literals, sequences_header) catch + decode_state.prepare(fbs_reader, literals, sequences_header) catch return error.MalformedCompressedBlock; + bytes_read += fbs.pos; + var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { const bit_stream_bytes = src[bytes_read..block_size]; @@ -991,6 +948,82 @@ pub fn decodeBlockRingBuffer( } } +/// Decode a single block from `source` into `dest`. Literal and sequence data +/// from the block is copied into `literals_buffer` and `sequence_buffer`, which +/// must be large enough or `error.LiteralsBufferTooSmall` and +/// `error.SequenceBufferTooSmall` are returned (the maximum block size is an +/// upper bound for the size of both buffers). See `decodeBlock` +/// and `decodeBlockRingBuffer` for function that can decode a block without +/// these extra copies. +pub fn decodeBlockReader( + dest: *RingBuffer, + source: anytype, + block_header: frame.ZStandard.Block.Header, + decode_state: *DecodeState, + block_size_max: usize, + literals_buffer: []u8, + sequence_buffer: []u8, +) !void { + const block_size = block_header.block_size; + var block_reader_limited = std.io.limitedReader(source, block_size); + const block_reader = block_reader_limited.reader(); + if (block_size_max < block_size) return error.BlockSizeOverMaximum; + switch (block_header.block_type) { + .raw => { + const slice = dest.sliceAt(dest.write_index, block_size); + try source.readNoEof(slice.first); + try source.readNoEof(slice.second); + dest.write_index = dest.mask2(dest.write_index + block_size); + }, + .rle => { + const byte = try source.readByte(); + var i: usize = 0; + while (i < block_size) : (i += 1) { + dest.writeAssumeCapacity(byte); + } + }, + .compressed => { + const literals = try decodeLiteralsSection(block_reader, literals_buffer); + const sequences_header = try decodeSequencesHeader(block_reader); + + try decode_state.prepare(block_reader, literals, sequences_header); + + if (sequences_header.sequence_count > 0) { + if (sequence_buffer.len < block_reader_limited.bytes_left) + return error.SequenceBufferTooSmall; + + const size = try block_reader.readAll(sequence_buffer); + var bit_stream: ReverseBitReader = undefined; + try bit_stream.init(sequence_buffer[0..size]); + + decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; + + var sequence_size_limit = block_size_max; + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + const decompressed_size = decode_state.decodeSequenceRingBuffer( + dest, + &bit_stream, + sequence_size_limit, + i == sequences_header.sequence_count - 1, + ) catch return error.MalformedCompressedBlock; + sequence_size_limit -= decompressed_size; + } + } + + if (decode_state.literal_written_count < literals.header.regenerated_size) { + const len = literals.header.regenerated_size - decode_state.literal_written_count; + decode_state.decodeLiteralsRingBuffer(dest, len) catch + return error.MalformedCompressedBlock; + } + + decode_state.literal_written_count = 0; + assert(block_reader.readByte() == error.EndOfStream); + }, + .reserved => return error.ReservedBlock, + } +} + /// Decode the header of a skippable frame. pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { const magic = readInt(u32, src[0..4]); @@ -1002,13 +1035,6 @@ pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { }; } -/// Returns the content size of a skippable frame. -pub fn skippableFrameSize(src: *const [8]u8) usize { - assert(isSkippableMagic(readInt(u32, src[0..4]))); - const frame_size = readInt(u32, src[4..8]); - return frame_size; -} - /// Returns the window size required to decompress a frame, or `null` if it cannot be /// determined, which indicates a malformed frame header. pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { @@ -1023,40 +1049,37 @@ pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { } const InvalidBit = error{ UnusedBitSet, ReservedBitSet }; -/// Decode the header of a Zstandard frame. Returns `error.UnusedBitSet` or -/// `error.ReservedBitSet` if the corresponding bits are sets. -pub fn decodeZStandardHeader(src: []const u8, consumed_count: ?*usize) InvalidBit!frame.ZStandard.Header { - const descriptor = @bitCast(frame.ZStandard.Header.Descriptor, src[0]); +/// Decode the header of a Zstandard frame. +/// +/// Errors: +/// - returns `error.UnusedBitSet` if the unused bits of the header are set +/// - returns `error.ReservedBitSet` if the reserved bits of the header are +/// set +pub fn decodeZStandardHeader(source: anytype) (error{EndOfStream} || InvalidBit)!frame.ZStandard.Header { + const descriptor = @bitCast(frame.ZStandard.Header.Descriptor, try source.readByte()); if (descriptor.unused) return error.UnusedBitSet; if (descriptor.reserved) return error.ReservedBitSet; - var bytes_read_count: usize = 1; - var window_descriptor: ?u8 = null; if (!descriptor.single_segment_flag) { - window_descriptor = src[bytes_read_count]; - bytes_read_count += 1; + window_descriptor = try source.readByte(); } var dictionary_id: ?u32 = null; if (descriptor.dictionary_id_flag > 0) { // if flag is 3 then field_size = 4, else field_size = flag const field_size = (@as(u4, 1) << descriptor.dictionary_id_flag) >> 1; - dictionary_id = readVarInt(u32, src[bytes_read_count .. bytes_read_count + field_size]); - bytes_read_count += field_size; + dictionary_id = try source.readVarInt(u32, .Little, field_size); } var content_size: ?u64 = null; if (descriptor.single_segment_flag or descriptor.content_size_flag > 0) { const field_size = @as(u4, 1) << descriptor.content_size_flag; - content_size = readVarInt(u64, src[bytes_read_count .. bytes_read_count + field_size]); + content_size = try source.readVarInt(u64, .Little, field_size); if (field_size == 2) content_size.? += 256; - bytes_read_count += field_size; } - if (consumed_count) |p| p.* += bytes_read_count; - const header = frame.ZStandard.Header{ .descriptor = descriptor, .window_descriptor = window_descriptor, @@ -1080,12 +1103,20 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { /// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the /// number of bytes the section uses. -pub fn decodeLiteralsSection( +/// +/// Errors: +/// - returns `error.MalformedLiteralsHeader` if the header is invalid +/// - returns `error.MalformedLiteralsSection` if there are errors decoding +pub fn decodeLiteralsSectionSlice( src: []const u8, consumed_count: *usize, -) (error{ MalformedLiteralsHeader, MalformedLiteralsSection } || DecodeHuffmanError)!LiteralsSection { +) (error{ MalformedLiteralsHeader, MalformedLiteralsSection, EndOfStream } || DecodeHuffmanError)!LiteralsSection { var bytes_read: usize = 0; - const header = try decodeLiteralsHeader(src, &bytes_read); + const header = header: { + var fbs = std.io.fixedBufferStream(src); + defer bytes_read = fbs.pos; + break :header decodeLiteralsHeader(fbs.reader()) catch return error.MalformedLiteralsHeader; + }; switch (header.block_type) { .raw => { if (src.len < bytes_read + header.regenerated_size) return error.MalformedLiteralsSection; @@ -1110,7 +1141,7 @@ pub fn decodeLiteralsSection( .compressed, .treeless => { const huffman_tree_start = bytes_read; const huffman_tree = if (header.block_type == .compressed) - try decodeHuffmanTree(src[bytes_read..], &bytes_read) + try decodeHuffmanTreeSlice(src[bytes_read..], &bytes_read) else null; const huffman_tree_size = bytes_read - huffman_tree_start; @@ -1119,137 +1150,185 @@ pub fn decodeLiteralsSection( if (src.len < bytes_read + total_streams_size) return error.MalformedLiteralsSection; const stream_data = src[bytes_read .. bytes_read + total_streams_size]; - if (header.size_format == 0) { - consumed_count.* += total_streams_size + bytes_read; - return LiteralsSection{ - .header = header, - .huffman_tree = huffman_tree, - .streams = .{ .one = stream_data }, - }; - } - - if (stream_data.len < 6) return error.MalformedLiteralsSection; - - const stream_1_length = @as(usize, readInt(u16, stream_data[0..2])); - const stream_2_length = @as(usize, readInt(u16, stream_data[2..4])); - const stream_3_length = @as(usize, readInt(u16, stream_data[4..6])); - const stream_4_length = (total_streams_size - 6) - (stream_1_length + stream_2_length + stream_3_length); - - const stream_1_start = 6; - const stream_2_start = stream_1_start + stream_1_length; - const stream_3_start = stream_2_start + stream_2_length; - const stream_4_start = stream_3_start + stream_3_length; - - if (stream_data.len < stream_4_start + stream_4_length) return error.MalformedLiteralsSection; - consumed_count.* += total_streams_size + bytes_read; - + const streams = try decodeStreams(header.size_format, stream_data); + consumed_count.* += bytes_read + total_streams_size; return LiteralsSection{ .header = header, .huffman_tree = huffman_tree, - .streams = .{ .four = .{ - stream_data[stream_1_start .. stream_1_start + stream_1_length], - stream_data[stream_2_start .. stream_2_start + stream_2_length], - stream_data[stream_3_start .. stream_3_start + stream_3_length], - stream_data[stream_4_start .. stream_4_start + stream_4_length], - } }, + .streams = streams, }; }, } } +/// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the +/// number of bytes the section uses. +/// +/// Errors: +/// - returns `error.MalformedLiteralsHeader` if the header is invalid +/// - returns `error.MalformedLiteralsSection` if there are errors decoding +pub fn decodeLiteralsSection( + source: anytype, + buffer: []u8, +) !LiteralsSection { + const header = try decodeLiteralsHeader(source); + switch (header.block_type) { + .raw => { + try source.readNoEof(buffer[0..header.regenerated_size]); + return LiteralsSection{ + .header = header, + .huffman_tree = null, + .streams = .{ .one = buffer }, + }; + }, + .rle => { + buffer[0] = try source.readByte(); + return LiteralsSection{ + .header = header, + .huffman_tree = null, + .streams = .{ .one = buffer[0..1] }, + }; + }, + .compressed, .treeless => { + var counting_reader = std.io.countingReader(source); + const huffman_tree = if (header.block_type == .compressed) + try decodeHuffmanTree(counting_reader.reader(), buffer) + else + null; + const huffman_tree_size = counting_reader.bytes_read; + const total_streams_size = @as(usize, header.compressed_size.?) - @intCast(usize, huffman_tree_size); + + if (total_streams_size > buffer.len) return error.LiteralsBufferTooSmall; + try source.readNoEof(buffer[0..total_streams_size]); + const stream_data = buffer[0..total_streams_size]; + + const streams = try decodeStreams(header.size_format, stream_data); + return LiteralsSection{ + .header = header, + .huffman_tree = huffman_tree, + .streams = streams, + }; + }, + } +} + +fn decodeStreams(size_format: u2, stream_data: []const u8) !LiteralsSection.Streams { + if (size_format == 0) { + return .{ .one = stream_data }; + } + + if (stream_data.len < 6) return error.MalformedLiteralsSection; + + const stream_1_length = @as(usize, readInt(u16, stream_data[0..2])); + const stream_2_length = @as(usize, readInt(u16, stream_data[2..4])); + const stream_3_length = @as(usize, readInt(u16, stream_data[4..6])); + + const stream_1_start = 6; + const stream_2_start = stream_1_start + stream_1_length; + const stream_3_start = stream_2_start + stream_2_length; + const stream_4_start = stream_3_start + stream_3_length; + + return .{ .four = .{ + stream_data[stream_1_start .. stream_1_start + stream_1_length], + stream_data[stream_2_start .. stream_2_start + stream_2_length], + stream_data[stream_3_start .. stream_3_start + stream_3_length], + stream_data[stream_4_start..], + } }; +} + const DecodeHuffmanError = error{ MalformedHuffmanTree, MalformedFseTable, MalformedAccuracyLog, }; -fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) DecodeHuffmanError!LiteralsSection.HuffmanTree { - var bytes_read: usize = 0; - bytes_read += 1; - if (src.len == 0) return error.MalformedHuffmanTree; - const header = src[0]; - var symbol_count: usize = undefined; - var weights: [256]u4 = undefined; - var max_number_of_bits: u4 = undefined; - if (header < 128) { - // FSE compressed weights - const compressed_size = header; - if (src.len < 1 + compressed_size) return error.MalformedHuffmanTree; - var stream = std.io.fixedBufferStream(src[1 .. compressed_size + 1]); - var counting_reader = std.io.countingReader(stream.reader()); - var bit_reader = bitReader(counting_reader.reader()); +fn decodeFseHuffmanTree(source: anytype, compressed_size: usize, buffer: []u8, weights: *[256]u4) !usize { + var stream = std.io.limitedReader(source, compressed_size); + var bit_reader = bitReader(stream.reader()); - var entries: [1 << 6]Table.Fse = undefined; - const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { - error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, - error.EndOfStream => return error.MalformedFseTable, - }; - const accuracy_log = std.math.log2_int_ceil(usize, table_size); + var entries: [1 << 6]Table.Fse = undefined; + const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { + error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, + error.EndOfStream => return error.MalformedFseTable, + }; + const accuracy_log = std.math.log2_int_ceil(usize, table_size); - const start_index = std.math.cast(usize, 1 + counting_reader.bytes_read) orelse return error.MalformedHuffmanTree; - var huff_data = src[start_index .. compressed_size + 1]; - var huff_bits: ReverseBitReader = undefined; - huff_bits.init(huff_data) catch return error.MalformedHuffmanTree; + const amount = try stream.reader().readAll(buffer); + var huff_bits: ReverseBitReader = undefined; + huff_bits.init(buffer[0..amount]) catch return error.MalformedHuffmanTree; - var i: usize = 0; - var even_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; - var odd_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; + return assignWeights(&huff_bits, accuracy_log, &entries, weights); +} - while (i < 255) { - const even_data = entries[even_state]; - var read_bits: usize = 0; - const even_bits = huff_bits.readBits(u32, even_data.bits, &read_bits) catch unreachable; - weights[i] = std.math.cast(u4, even_data.symbol) orelse return error.MalformedHuffmanTree; +fn decodeFseHuffmanTreeSlice(src: []const u8, compressed_size: usize, weights: *[256]u4) !usize { + if (src.len < compressed_size) return error.MalformedHuffmanTree; + var stream = std.io.fixedBufferStream(src[0..compressed_size]); + var counting_reader = std.io.countingReader(stream.reader()); + var bit_reader = bitReader(counting_reader.reader()); + + var entries: [1 << 6]Table.Fse = undefined; + const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { + error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, + error.EndOfStream => return error.MalformedFseTable, + }; + const accuracy_log = std.math.log2_int_ceil(usize, table_size); + + const start_index = std.math.cast(usize, counting_reader.bytes_read) orelse return error.MalformedHuffmanTree; + var huff_data = src[start_index..compressed_size]; + var huff_bits: ReverseBitReader = undefined; + huff_bits.init(huff_data) catch return error.MalformedHuffmanTree; + + return assignWeights(&huff_bits, accuracy_log, &entries, weights); +} + +fn assignWeights(huff_bits: *ReverseBitReader, accuracy_log: usize, entries: *[1 << 6]Table.Fse, weights: *[256]u4) !usize { + var i: usize = 0; + var even_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; + var odd_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; + + while (i < 255) { + const even_data = entries[even_state]; + var read_bits: usize = 0; + const even_bits = huff_bits.readBits(u32, even_data.bits, &read_bits) catch unreachable; + weights[i] = std.math.cast(u4, even_data.symbol) orelse return error.MalformedHuffmanTree; + i += 1; + if (read_bits < even_data.bits) { + weights[i] = std.math.cast(u4, entries[odd_state].symbol) orelse return error.MalformedHuffmanTree; i += 1; - if (read_bits < even_data.bits) { - weights[i] = std.math.cast(u4, entries[odd_state].symbol) orelse return error.MalformedHuffmanTree; - i += 1; - break; - } - even_state = even_data.baseline + even_bits; + break; + } + even_state = even_data.baseline + even_bits; - read_bits = 0; - const odd_data = entries[odd_state]; - const odd_bits = huff_bits.readBits(u32, odd_data.bits, &read_bits) catch unreachable; - weights[i] = std.math.cast(u4, odd_data.symbol) orelse return error.MalformedHuffmanTree; + read_bits = 0; + const odd_data = entries[odd_state]; + const odd_bits = huff_bits.readBits(u32, odd_data.bits, &read_bits) catch unreachable; + weights[i] = std.math.cast(u4, odd_data.symbol) orelse return error.MalformedHuffmanTree; + i += 1; + if (read_bits < odd_data.bits) { + if (i == 256) return error.MalformedHuffmanTree; + weights[i] = std.math.cast(u4, entries[even_state].symbol) orelse return error.MalformedHuffmanTree; i += 1; - if (read_bits < odd_data.bits) { - if (i == 256) return error.MalformedHuffmanTree; - weights[i] = std.math.cast(u4, entries[even_state].symbol) orelse return error.MalformedHuffmanTree; - i += 1; - break; - } - odd_state = odd_data.baseline + odd_bits; - } else return error.MalformedHuffmanTree; - - symbol_count = i + 1; // stream contains all but the last symbol - bytes_read += compressed_size; - } else { - const encoded_symbol_count = header - 127; - symbol_count = encoded_symbol_count + 1; - const weights_byte_count = (encoded_symbol_count + 1) / 2; - if (src.len < weights_byte_count) return error.MalformedHuffmanTree; - var i: usize = 0; - while (i < weights_byte_count) : (i += 1) { - weights[2 * i] = @intCast(u4, src[i + 1] >> 4); - weights[2 * i + 1] = @intCast(u4, src[i + 1] & 0xF); + break; } - bytes_read += weights_byte_count; - } - var weight_power_sum: u16 = 0; - for (weights[0 .. symbol_count - 1]) |value| { - if (value > 0) { - weight_power_sum += @as(u16, 1) << (value - 1); - } - } + odd_state = odd_data.baseline + odd_bits; + } else return error.MalformedHuffmanTree; - // advance to next power of two (even if weight_power_sum is a power of 2) - max_number_of_bits = std.math.log2_int(u16, weight_power_sum) + 1; - const next_power_of_two = @as(u16, 1) << max_number_of_bits; - weights[symbol_count - 1] = std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1; + return i + 1; // stream contains all but the last symbol +} - var weight_sorted_prefixed_symbols: [256]LiteralsSection.HuffmanTree.PrefixedSymbol = undefined; - for (weight_sorted_prefixed_symbols[0..symbol_count]) |_, i| { +fn decodeDirectHuffmanTree(source: anytype, encoded_symbol_count: usize, weights: *[256]u4) !usize { + const weights_byte_count = (encoded_symbol_count + 1) / 2; + var i: usize = 0; + while (i < weights_byte_count) : (i += 1) { + const byte = try source.readByte(); + weights[2 * i] = @intCast(u4, byte >> 4); + weights[2 * i + 1] = @intCast(u4, byte & 0xF); + } + return encoded_symbol_count + 1; +} + +fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.PrefixedSymbol, weights: [256]u4) usize { + for (weight_sorted_prefixed_symbols) |_, i| { weight_sorted_prefixed_symbols[i] = .{ .symbol = @intCast(u8, i), .weight = undefined, @@ -1259,7 +1338,7 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) DecodeHuffmanError std.sort.sort( LiteralsSection.HuffmanTree.PrefixedSymbol, - weight_sorted_prefixed_symbols[0..symbol_count], + weight_sorted_prefixed_symbols, weights, lessThanByWeight, ); @@ -1267,6 +1346,7 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) DecodeHuffmanError var prefix: u16 = 0; var prefixed_symbol_count: usize = 0; var sorted_index: usize = 0; + const symbol_count = weight_sorted_prefixed_symbols.len; while (sorted_index < symbol_count) { var symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; const weight = weights[symbol]; @@ -1290,7 +1370,24 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) DecodeHuffmanError weight_sorted_prefixed_symbols[prefixed_symbol_count].weight = weight; } } - consumed_count.* += bytes_read; + return prefixed_symbol_count; +} + +fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) LiteralsSection.HuffmanTree { + var weight_power_sum: u16 = 0; + for (weights[0 .. symbol_count - 1]) |value| { + if (value > 0) { + weight_power_sum += @as(u16, 1) << (value - 1); + } + } + + // advance to next power of two (even if weight_power_sum is a power of 2) + const max_number_of_bits = std.math.log2_int(u16, weight_power_sum) + 1; + const next_power_of_two = @as(u16, 1) << max_number_of_bits; + weights[symbol_count - 1] = std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1; + + var weight_sorted_prefixed_symbols: [256]LiteralsSection.HuffmanTree.PrefixedSymbol = undefined; + const prefixed_symbol_count = assignSymbols(weight_sorted_prefixed_symbols[0..symbol_count], weights.*); const tree = LiteralsSection.HuffmanTree{ .max_bit_count = max_number_of_bits, .symbol_count_minus_one = @intCast(u8, prefixed_symbol_count - 1), @@ -1299,6 +1396,37 @@ fn decodeHuffmanTree(src: []const u8, consumed_count: *usize) DecodeHuffmanError return tree; } +fn decodeHuffmanTree(source: anytype, buffer: []u8) !LiteralsSection.HuffmanTree { + const header = try source.readByte(); + var weights: [256]u4 = undefined; + const symbol_count = if (header < 128) + // FSE compressed weights + try decodeFseHuffmanTree(source, header, buffer, &weights) + else + try decodeDirectHuffmanTree(source, header - 127, &weights); + + return buildHuffmanTree(&weights, symbol_count); +} + +fn decodeHuffmanTreeSlice(src: []const u8, consumed_count: *usize) (error{EndOfStream} || DecodeHuffmanError)!LiteralsSection.HuffmanTree { + if (src.len == 0) return error.MalformedHuffmanTree; + const header = src[0]; + var bytes_read: usize = 1; + var weights: [256]u4 = undefined; + const symbol_count = if (header < 128) count: { + // FSE compressed weights + bytes_read += header; + break :count try decodeFseHuffmanTreeSlice(src[1..], header, &weights); + } else count: { + var fbs = std.io.fixedBufferStream(src[1..]); + defer bytes_read += fbs.pos; + break :count try decodeDirectHuffmanTree(fbs.reader(), header - 127, &weights); + }; + + consumed_count.* += bytes_read; + return buildHuffmanTree(&weights, symbol_count); +} + fn lessThanByWeight( weights: [256]u4, lhs: LiteralsSection.HuffmanTree.PrefixedSymbol, @@ -1311,9 +1439,8 @@ fn lessThanByWeight( } /// Decode a literals section header. -pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) error{MalformedLiteralsHeader}!LiteralsSection.Header { - if (src.len == 0) return error.MalformedLiteralsHeader; - const byte0 = src[0]; +pub fn decodeLiteralsHeader(source: anytype) !LiteralsSection.Header { + const byte0 = try source.readByte(); const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); const size_format = @intCast(u2, (byte0 & 0b1100) >> 2); var regenerated_size: u20 = undefined; @@ -1323,47 +1450,31 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) error{Malfo switch (size_format) { 0, 2 => { regenerated_size = byte0 >> 3; - consumed_count.* += 1; - }, - 1 => { - if (src.len < 2) return error.MalformedLiteralsHeader; - regenerated_size = (byte0 >> 4) + - (@as(u20, src[1]) << 4); - consumed_count.* += 2; - }, - 3 => { - if (src.len < 3) return error.MalformedLiteralsHeader; - regenerated_size = (byte0 >> 4) + - (@as(u20, src[1]) << 4) + - (@as(u20, src[2]) << 12); - consumed_count.* += 3; }, + 1 => regenerated_size = (byte0 >> 4) + (@as(u20, try source.readByte()) << 4), + 3 => regenerated_size = (byte0 >> 4) + + (@as(u20, try source.readByte()) << 4) + + (@as(u20, try source.readByte()) << 12), } }, .compressed, .treeless => { - const byte1 = src[1]; - const byte2 = src[2]; + const byte1 = try source.readByte(); + const byte2 = try source.readByte(); switch (size_format) { 0, 1 => { - if (src.len < 3) return error.MalformedLiteralsHeader; regenerated_size = (byte0 >> 4) + ((@as(u20, byte1) & 0b00111111) << 4); compressed_size = ((byte1 & 0b11000000) >> 6) + (@as(u18, byte2) << 2); - consumed_count.* += 3; }, 2 => { - if (src.len < 4) return error.MalformedLiteralsHeader; - const byte3 = src[3]; + const byte3 = try source.readByte(); regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00000011) << 12); compressed_size = ((byte2 & 0b11111100) >> 2) + (@as(u18, byte3) << 6); - consumed_count.* += 4; }, 3 => { - if (src.len < 5) return error.MalformedLiteralsHeader; - const byte3 = src[3]; - const byte4 = src[4]; + const byte3 = try source.readByte(); + const byte4 = try source.readByte(); regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00111111) << 12); compressed_size = ((byte2 & 0b11000000) >> 6) + (@as(u18, byte3) << 2) + (@as(u18, byte4) << 10); - consumed_count.* += 5; }, } }, @@ -1377,18 +1488,17 @@ pub fn decodeLiteralsHeader(src: []const u8, consumed_count: *usize) error{Malfo } /// Decode a sequences section header. +/// +/// Errors: +/// - returns `error.ReservedBitSet` is the reserved bit is set +/// - returns `error.MalformedSequencesHeader` if the header is invalid pub fn decodeSequencesHeader( - src: []const u8, - consumed_count: *usize, -) error{ MalformedSequencesHeader, ReservedBitSet }!SequencesSection.Header { - if (src.len == 0) return error.MalformedSequencesHeader; + source: anytype, +) !SequencesSection.Header { var sequence_count: u24 = undefined; - var bytes_read: usize = 0; - const byte0 = src[0]; + const byte0 = try source.readByte(); if (byte0 == 0) { - bytes_read += 1; - consumed_count.* += bytes_read; return SequencesSection.Header{ .sequence_count = 0, .offsets = undefined, @@ -1397,22 +1507,14 @@ pub fn decodeSequencesHeader( }; } else if (byte0 < 128) { sequence_count = byte0; - bytes_read += 1; } else if (byte0 < 255) { - if (src.len < 2) return error.MalformedSequencesHeader; - sequence_count = (@as(u24, (byte0 - 128)) << 8) + src[1]; - bytes_read += 2; + sequence_count = (@as(u24, (byte0 - 128)) << 8) + try source.readByte(); } else { - if (src.len < 3) return error.MalformedSequencesHeader; - sequence_count = src[1] + (@as(u24, src[2]) << 8) + 0x7F00; - bytes_read += 3; + sequence_count = (try source.readByte()) + (@as(u24, try source.readByte()) << 8) + 0x7F00; } - if (src.len < bytes_read + 1) return error.MalformedSequencesHeader; - const compression_modes = src[bytes_read]; - bytes_read += 1; + const compression_modes = try source.readByte(); - consumed_count.* += bytes_read; const matches_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00001100) >> 2); const offsets_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00110000) >> 4); const literal_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b11000000) >> 6); @@ -1615,7 +1717,7 @@ fn BitReader(comptime Reader: type) type { }; } -fn bitReader(reader: anytype) BitReader(@TypeOf(reader)) { +pub fn bitReader(reader: anytype) BitReader(@TypeOf(reader)) { return .{ .underlying = std.io.bitReader(.Little, reader) }; } From a180fcc93d3eff8add7b0344ddc77241dcf78f1e Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 2 Feb 2023 16:20:03 +1100 Subject: [PATCH 033/122] std.compress.zstandard: add `ZstandardStream` --- lib/std/compress/zstandard.zig | 153 +++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index d83b3a3336..6fb6a70027 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -1,8 +1,158 @@ const std = @import("std"); +const Allocator = std.mem.Allocator; +const types = @import("zstandard/types.zig"); + +const RingBuffer = @import("zstandard/RingBuffer.zig"); pub const decompress = @import("zstandard/decompress.zig"); pub usingnamespace @import("zstandard/types.zig"); +pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool, comptime window_size_max: usize) type { + return struct { + const Self = @This(); + + allocator: Allocator, + in_reader: ReaderType, + decode_state: decompress.DecodeState, + frame_context: decompress.FrameContext, + buffer: RingBuffer, + last_block: bool, + literal_fse_buffer: []types.compressed_block.Table.Fse, + match_fse_buffer: []types.compressed_block.Table.Fse, + offset_fse_buffer: []types.compressed_block.Table.Fse, + literals_buffer: []u8, + sequence_buffer: []u8, + checksum: if (verify_checksum) ?u32 else void, + + pub const Error = ReaderType.Error || error{ MalformedBlock, MalformedFrame, EndOfStream }; + + pub const Reader = std.io.Reader(*Self, Error, read); + + pub fn init(allocator: Allocator, source: ReaderType) !Self { + switch (try decompress.decodeFrameType(source)) { + .skippable => return error.SkippableFrame, + .zstandard => { + const frame_context = context: { + const frame_header = try decompress.decodeZStandardHeader(source); + break :context try decompress.FrameContext.init(frame_header, window_size_max, verify_checksum); + }; + + const literal_fse_buffer = try allocator.alloc(types.compressed_block.Table.Fse, types.compressed_block.table_size_max.literal); + errdefer allocator.free(literal_fse_buffer); + const match_fse_buffer = try allocator.alloc(types.compressed_block.Table.Fse, types.compressed_block.table_size_max.match); + errdefer allocator.free(match_fse_buffer); + const offset_fse_buffer = try allocator.alloc(types.compressed_block.Table.Fse, types.compressed_block.table_size_max.offset); + errdefer allocator.free(offset_fse_buffer); + + const decode_state = decompress.DecodeState.init(literal_fse_buffer, match_fse_buffer, offset_fse_buffer); + const buffer = try RingBuffer.init(allocator, frame_context.window_size); + + const literals_data = try allocator.alloc(u8, window_size_max); + errdefer allocator.free(literals_data); + const sequence_data = try allocator.alloc(u8, window_size_max); + errdefer allocator.free(sequence_data); + + return Self{ + .allocator = allocator, + .in_reader = source, + .decode_state = decode_state, + .frame_context = frame_context, + .buffer = buffer, + .checksum = if (verify_checksum) null else {}, + .last_block = false, + .literal_fse_buffer = literal_fse_buffer, + .match_fse_buffer = match_fse_buffer, + .offset_fse_buffer = offset_fse_buffer, + .literals_buffer = literals_data, + .sequence_buffer = sequence_data, + }; + }, + } + } + + pub fn deinit(self: *Self) void { + self.allocator.free(self.decode_state.literal_fse_buffer); + self.allocator.free(self.decode_state.match_fse_buffer); + self.allocator.free(self.decode_state.offset_fse_buffer); + self.allocator.free(self.literals_buffer); + self.allocator.free(self.sequence_buffer); + self.buffer.deinit(self.allocator); + } + + pub fn reader(self: *Self) Reader { + return .{ .context = self }; + } + + pub fn read(self: *Self, buffer: []u8) Error!usize { + if (buffer.len == 0) return 0; + + if (self.buffer.isEmpty() and !self.last_block) { + const header_bytes = try self.in_reader.readBytesNoEof(3); + const block_header = decompress.decodeBlockHeader(&header_bytes); + + decompress.decodeBlockReader( + &self.buffer, + self.in_reader, + block_header, + &self.decode_state, + self.frame_context.block_size_max, + self.literals_buffer, + self.sequence_buffer, + ) catch + return error.MalformedBlock; + + self.last_block = block_header.last_block; + if (self.frame_context.hasher_opt) |*hasher| { + const written_slice = self.buffer.sliceLast(self.buffer.len()); + hasher.update(written_slice.first); + hasher.update(written_slice.second); + } + if (block_header.last_block and self.frame_context.has_checksum) { + const checksum = self.in_reader.readIntLittle(u32) catch return error.MalformedFrame; + if (verify_checksum) self.checksum = checksum; + } + } + + const decoded_data_len = self.buffer.len(); + var written_count: usize = 0; + while (written_count < decoded_data_len and written_count < buffer.len) : (written_count += 1) { + buffer[written_count] = self.buffer.read().?; + } + return written_count; + } + + pub fn verifyChecksum(self: *Self) !bool { + if (verify_checksum) { + if (self.checksum) |checksum| { + if (self.frame_context.hasher_opt) |*hasher| { + return checksum == decompress.computeChecksum(hasher); + } + } + } + return true; + } + }; +} + +pub fn zstandardStream(allocator: Allocator, reader: anytype) !ZstandardStream(@TypeOf(reader), true, 8 * (1 << 20)) { + return ZstandardStream(@TypeOf(reader), true, 8 * (1 << 20)).init(allocator, reader); +} + +fn testDecompress(data: []const u8) ![]u8 { + var in_stream = std.io.fixedBufferStream(data); + var stream = try zstandardStream(std.testing.allocator, in_stream.reader()); + defer stream.deinit(); + const result = stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); + try std.testing.expect(try stream.verifyChecksum()); + return result; +} + +fn testReader(data: []const u8, comptime expected: []const u8) !void { + const buf = try testDecompress(data); + defer std.testing.allocator.free(buf); + try std.testing.expectEqualSlices(u8, expected, buf); +} + test "decompression" { const uncompressed = @embedFile("testdata/rfc8478.txt"); const compressed3 = @embedFile("testdata/rfc8478.txt.zst.3"); @@ -20,4 +170,7 @@ test "decompression" { try std.testing.expectEqual(compressed19.len, res19.read_count); try std.testing.expectEqual(uncompressed.len, res19.write_count); try std.testing.expectEqualSlices(u8, uncompressed, buffer); + + try testReader(compressed3, uncompressed); + try testReader(compressed19, uncompressed); } From 6e3e72884bdc1a2f9f3ae372716b50803565e696 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 2 Feb 2023 18:01:03 +1100 Subject: [PATCH 034/122] std.compress.zstandard: fix crashes --- lib/std/compress/zstandard/decompress.zig | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index e32f4d0282..2b397007a9 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -695,11 +695,11 @@ pub fn decodeZStandardFrameAlloc( var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; - var block_header = decodeBlockHeader(src[consumed_count..][0..3]); + var block_header = try decodeBlockHeaderSlice(src[consumed_count..]); consumed_count += 3; var decode_state = DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); while (true) : ({ - block_header = decodeBlockHeader(src[consumed_count..][0..3]); + block_header = try decodeBlockHeaderSlice(src[consumed_count..]); consumed_count += 3; }) { if (block_header.block_size > frame_context.block_size_max) return error.BlockSizeOverMaximum; @@ -737,6 +737,7 @@ const DecodeBlockError = error{ ReservedBlock, MalformedRleBlock, MalformedCompressedBlock, + EndOfStream, }; /// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. @@ -751,13 +752,13 @@ pub fn decodeFrameBlocks( var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; - var block_header = decodeBlockHeader(src[0..3]); + var block_header = try decodeBlockHeaderSlice(src); var bytes_read: usize = 3; defer consumed_count.* += bytes_read; var decode_state = DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); var written_count: usize = 0; while (true) : ({ - block_header = decodeBlockHeader(src[bytes_read..][0..3]); + block_header = try decodeBlockHeaderSlice(src[bytes_read..]); bytes_read += 3; }) { const written_size = try decodeBlock( @@ -847,6 +848,7 @@ pub fn decodeBlock( bytes_read += bit_stream_bytes.len; } + if (bytes_read != block_size) return error.MalformedCompressedBlock; if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; @@ -855,7 +857,6 @@ pub fn decodeBlock( bytes_written += len; } - assert(bytes_read == block_header.block_size); consumed_count.* += bytes_read; return bytes_written; }, @@ -931,6 +932,7 @@ pub fn decodeBlockRingBuffer( bytes_read += bit_stream_bytes.len; } + if (bytes_read != block_size) return error.MalformedCompressedBlock; if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; @@ -939,7 +941,6 @@ pub fn decodeBlockRingBuffer( bytes_written += len; } - assert(bytes_read == block_header.block_size); consumed_count.* += bytes_read; if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; return bytes_written; @@ -1101,6 +1102,11 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { }; } +pub fn decodeBlockHeaderSlice(src: []const u8) error{EndOfStream}!frame.ZStandard.Block.Header { + if (src.len < 3) return error.EndOfStream; + return decodeBlockHeader(src[0..3]); +} + /// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the /// number of bytes the section uses. /// From 7e2755646f5c9cab9973708a79c8aaa369d148e7 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 2 Feb 2023 18:44:01 +1100 Subject: [PATCH 035/122] std.compress.zstandard: split decompressor into multiple files --- lib/std/compress/zstandard.zig | 40 +- lib/std/compress/zstandard/decode/block.zig | 1051 ++++++++++++ lib/std/compress/zstandard/decode/fse.zig | 154 ++ lib/std/compress/zstandard/decode/huffman.zig | 212 +++ lib/std/compress/zstandard/decompress.zig | 1472 +---------------- lib/std/compress/zstandard/readers.zig | 75 + 6 files changed, 1538 insertions(+), 1466 deletions(-) create mode 100644 lib/std/compress/zstandard/decode/block.zig create mode 100644 lib/std/compress/zstandard/decode/fse.zig create mode 100644 lib/std/compress/zstandard/decode/huffman.zig create mode 100644 lib/std/compress/zstandard/readers.zig diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index 6fb6a70027..d17e6bac46 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -13,7 +13,7 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool allocator: Allocator, in_reader: ReaderType, - decode_state: decompress.DecodeState, + decode_state: decompress.block.DecodeState, frame_context: decompress.FrameContext, buffer: RingBuffer, last_block: bool, @@ -24,7 +24,7 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool sequence_buffer: []u8, checksum: if (verify_checksum) ?u32 else void, - pub const Error = ReaderType.Error || error{ MalformedBlock, MalformedFrame, EndOfStream }; + pub const Error = ReaderType.Error || error{ MalformedBlock, MalformedFrame }; pub const Reader = std.io.Reader(*Self, Error, read); @@ -34,21 +34,41 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool .zstandard => { const frame_context = context: { const frame_header = try decompress.decodeZStandardHeader(source); - break :context try decompress.FrameContext.init(frame_header, window_size_max, verify_checksum); + break :context try decompress.FrameContext.init( + frame_header, + window_size_max, + verify_checksum, + ); }; - const literal_fse_buffer = try allocator.alloc(types.compressed_block.Table.Fse, types.compressed_block.table_size_max.literal); + const literal_fse_buffer = try allocator.alloc( + types.compressed_block.Table.Fse, + types.compressed_block.table_size_max.literal, + ); errdefer allocator.free(literal_fse_buffer); - const match_fse_buffer = try allocator.alloc(types.compressed_block.Table.Fse, types.compressed_block.table_size_max.match); + + const match_fse_buffer = try allocator.alloc( + types.compressed_block.Table.Fse, + types.compressed_block.table_size_max.match, + ); errdefer allocator.free(match_fse_buffer); - const offset_fse_buffer = try allocator.alloc(types.compressed_block.Table.Fse, types.compressed_block.table_size_max.offset); + + const offset_fse_buffer = try allocator.alloc( + types.compressed_block.Table.Fse, + types.compressed_block.table_size_max.offset, + ); errdefer allocator.free(offset_fse_buffer); - const decode_state = decompress.DecodeState.init(literal_fse_buffer, match_fse_buffer, offset_fse_buffer); + const decode_state = decompress.block.DecodeState.init( + literal_fse_buffer, + match_fse_buffer, + offset_fse_buffer, + ); const buffer = try RingBuffer.init(allocator, frame_context.window_size); const literals_data = try allocator.alloc(u8, window_size_max); errdefer allocator.free(literals_data); + const sequence_data = try allocator.alloc(u8, window_size_max); errdefer allocator.free(sequence_data); @@ -87,10 +107,10 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool if (buffer.len == 0) return 0; if (self.buffer.isEmpty() and !self.last_block) { - const header_bytes = try self.in_reader.readBytesNoEof(3); - const block_header = decompress.decodeBlockHeader(&header_bytes); + const header_bytes = self.in_reader.readBytesNoEof(3) catch return error.MalformedFrame; + const block_header = decompress.block.decodeBlockHeader(&header_bytes); - decompress.decodeBlockReader( + decompress.block.decodeBlockReader( &self.buffer, self.in_reader, block_header, diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig new file mode 100644 index 0000000000..7a7bf28b31 --- /dev/null +++ b/lib/std/compress/zstandard/decode/block.zig @@ -0,0 +1,1051 @@ +const std = @import("std"); +const assert = std.debug.assert; + +const types = @import("../types.zig"); +const frame = types.frame; +const Table = types.compressed_block.Table; +const LiteralsSection = types.compressed_block.LiteralsSection; +const SequencesSection = types.compressed_block.SequencesSection; + +const huffman = @import("huffman.zig"); + +const RingBuffer = @import("../RingBuffer.zig"); + +const readers = @import("../readers.zig"); + +const decodeFseTable = @import("fse.zig").decodeFseTable; + +const readInt = std.mem.readIntLittle; + +pub const Error = error{ + BlockSizeOverMaximum, + MalformedBlockSize, + ReservedBlock, + MalformedRleBlock, + MalformedCompressedBlock, + EndOfStream, +}; + +pub const DecodeState = struct { + repeat_offsets: [3]u32, + + offset: StateData(8), + match: StateData(9), + literal: StateData(9), + + offset_fse_buffer: []Table.Fse, + match_fse_buffer: []Table.Fse, + literal_fse_buffer: []Table.Fse, + + fse_tables_undefined: bool, + + literal_stream_reader: readers.ReverseBitReader, + literal_stream_index: usize, + literal_streams: LiteralsSection.Streams, + literal_header: LiteralsSection.Header, + huffman_tree: ?LiteralsSection.HuffmanTree, + + literal_written_count: usize, + + fn StateData(comptime max_accuracy_log: comptime_int) type { + return struct { + state: State, + table: Table, + accuracy_log: u8, + + const State = std.meta.Int(.unsigned, max_accuracy_log); + }; + } + + pub fn init( + literal_fse_buffer: []Table.Fse, + match_fse_buffer: []Table.Fse, + offset_fse_buffer: []Table.Fse, + ) DecodeState { + return DecodeState{ + .repeat_offsets = .{ + types.compressed_block.start_repeated_offset_1, + types.compressed_block.start_repeated_offset_2, + types.compressed_block.start_repeated_offset_3, + }, + + .offset = undefined, + .match = undefined, + .literal = undefined, + + .literal_fse_buffer = literal_fse_buffer, + .match_fse_buffer = match_fse_buffer, + .offset_fse_buffer = offset_fse_buffer, + + .fse_tables_undefined = true, + + .literal_written_count = 0, + .literal_header = undefined, + .literal_streams = undefined, + .literal_stream_reader = undefined, + .literal_stream_index = undefined, + .huffman_tree = null, + }; + } + + /// Prepare the decoder to decode a compressed block. Loads the literals + /// stream and Huffman tree from `literals` and reads the FSE tables from + /// `source`. + /// + /// Errors: + /// - returns `error.BitStreamHasNoStartBit` if the (reversed) literal bitstream's + /// first byte does not have any bits set. + /// - returns `error.TreelessLiteralsFirst` `literals` is a treeless literals section + /// and the decode state does not have a Huffman tree from a previous block. + pub fn prepare( + self: *DecodeState, + source: anytype, + literals: LiteralsSection, + sequences_header: SequencesSection.Header, + ) !void { + self.literal_written_count = 0; + self.literal_header = literals.header; + self.literal_streams = literals.streams; + + if (literals.huffman_tree) |tree| { + self.huffman_tree = tree; + } else if (literals.header.block_type == .treeless and self.huffman_tree == null) { + return error.TreelessLiteralsFirst; + } + + switch (literals.header.block_type) { + .raw, .rle => {}, + .compressed, .treeless => { + self.literal_stream_index = 0; + switch (literals.streams) { + .one => |slice| try self.initLiteralStream(slice), + .four => |streams| try self.initLiteralStream(streams[0]), + } + }, + } + + if (sequences_header.sequence_count > 0) { + try self.updateFseTable(source, .literal, sequences_header.literal_lengths); + try self.updateFseTable(source, .offset, sequences_header.offsets); + try self.updateFseTable(source, .match, sequences_header.match_lengths); + self.fse_tables_undefined = false; + } + } + + /// Read initial FSE states for sequence decoding. Returns `error.EndOfStream` + /// if `bit_reader` does not contain enough bits. + pub fn readInitialFseState(self: *DecodeState, bit_reader: *readers.ReverseBitReader) error{EndOfStream}!void { + self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); + self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); + self.match.state = try bit_reader.readBitsNoEof(u9, self.match.accuracy_log); + } + + fn updateRepeatOffset(self: *DecodeState, offset: u32) void { + std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[1]); + std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[2]); + self.repeat_offsets[0] = offset; + } + + fn useRepeatOffset(self: *DecodeState, index: usize) u32 { + if (index == 1) + std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[1]) + else if (index == 2) { + std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[2]); + std.mem.swap(u32, &self.repeat_offsets[1], &self.repeat_offsets[2]); + } + return self.repeat_offsets[0]; + } + + const DataType = enum { offset, match, literal }; + + fn updateState( + self: *DecodeState, + comptime choice: DataType, + bit_reader: *readers.ReverseBitReader, + ) error{ MalformedFseBits, EndOfStream }!void { + switch (@field(self, @tagName(choice)).table) { + .rle => {}, + .fse => |table| { + const data = table[@field(self, @tagName(choice)).state]; + const T = @TypeOf(@field(self, @tagName(choice))).State; + const bits_summand = try bit_reader.readBitsNoEof(T, data.bits); + const next_state = std.math.cast( + @TypeOf(@field(self, @tagName(choice))).State, + data.baseline + bits_summand, + ) orelse return error.MalformedFseBits; + @field(self, @tagName(choice)).state = next_state; + }, + } + } + + const FseTableError = error{ + MalformedFseTable, + MalformedAccuracyLog, + RepeatModeFirst, + EndOfStream, + }; + + fn updateFseTable( + self: *DecodeState, + source: anytype, + comptime choice: DataType, + mode: SequencesSection.Header.Mode, + ) !void { + const field_name = @tagName(choice); + switch (mode) { + .predefined => { + @field(self, field_name).accuracy_log = + @field(types.compressed_block.default_accuracy_log, field_name); + + @field(self, field_name).table = + @field(types.compressed_block, "predefined_" ++ field_name ++ "_fse_table"); + }, + .rle => { + @field(self, field_name).accuracy_log = 0; + @field(self, field_name).table = .{ .rle = try source.readByte() }; + }, + .fse => { + var bit_reader = readers.bitReader(source); + + const table_size = try decodeFseTable( + &bit_reader, + @field(types.compressed_block.table_symbol_count_max, field_name), + @field(types.compressed_block.table_accuracy_log_max, field_name), + @field(self, field_name ++ "_fse_buffer"), + ); + @field(self, field_name).table = .{ + .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size], + }; + @field(self, field_name).accuracy_log = std.math.log2_int_ceil(usize, table_size); + }, + .repeat => if (self.fse_tables_undefined) return error.RepeatModeFirst, + } + } + + const Sequence = struct { + literal_length: u32, + match_length: u32, + offset: u32, + }; + + fn nextSequence( + self: *DecodeState, + bit_reader: *readers.ReverseBitReader, + ) error{ OffsetCodeTooLarge, EndOfStream }!Sequence { + const raw_code = self.getCode(.offset); + const offset_code = std.math.cast(u5, raw_code) orelse { + return error.OffsetCodeTooLarge; + }; + const offset_value = (@as(u32, 1) << offset_code) + try bit_reader.readBitsNoEof(u32, offset_code); + + const match_code = self.getCode(.match); + const match = types.compressed_block.match_length_code_table[match_code]; + const match_length = match[0] + try bit_reader.readBitsNoEof(u32, match[1]); + + const literal_code = self.getCode(.literal); + const literal = types.compressed_block.literals_length_code_table[literal_code]; + const literal_length = literal[0] + try bit_reader.readBitsNoEof(u32, literal[1]); + + const offset = if (offset_value > 3) offset: { + const offset = offset_value - 3; + self.updateRepeatOffset(offset); + break :offset offset; + } else offset: { + if (literal_length == 0) { + if (offset_value == 3) { + const offset = self.repeat_offsets[0] - 1; + self.updateRepeatOffset(offset); + break :offset offset; + } + break :offset self.useRepeatOffset(offset_value); + } + break :offset self.useRepeatOffset(offset_value - 1); + }; + + return .{ + .literal_length = literal_length, + .match_length = match_length, + .offset = offset, + }; + } + + fn executeSequenceSlice( + self: *DecodeState, + dest: []u8, + write_pos: usize, + sequence: Sequence, + ) (error{MalformedSequence} || DecodeLiteralsError)!void { + if (sequence.offset > write_pos + sequence.literal_length) return error.MalformedSequence; + + try self.decodeLiteralsSlice(dest[write_pos..], sequence.literal_length); + const copy_start = write_pos + sequence.literal_length - sequence.offset; + const copy_end = copy_start + sequence.match_length; + // NOTE: we ignore the usage message for std.mem.copy and copy with dest.ptr >= src.ptr + // to allow repeats + std.mem.copy(u8, dest[write_pos + sequence.literal_length ..], dest[copy_start..copy_end]); + } + + fn executeSequenceRingBuffer( + self: *DecodeState, + dest: *RingBuffer, + sequence: Sequence, + ) (error{MalformedSequence} || DecodeLiteralsError)!void { + if (sequence.offset > dest.data.len) return error.MalformedSequence; + + try self.decodeLiteralsRingBuffer(dest, sequence.literal_length); + const copy_start = dest.write_index + dest.data.len - sequence.offset; + const copy_slice = dest.sliceAt(copy_start, sequence.match_length); + // TODO: would std.mem.copy and figuring out dest slice be better/faster? + for (copy_slice.first) |b| dest.writeAssumeCapacity(b); + for (copy_slice.second) |b| dest.writeAssumeCapacity(b); + } + + const DecodeSequenceError = error{ + OffsetCodeTooLarge, + EndOfStream, + MalformedSequence, + MalformedFseBits, + } || DecodeLiteralsError; + + /// Decode one sequence from `bit_reader` into `dest`, written starting at + /// `write_pos` and update FSE states if `last_sequence` is `false`. Returns + /// `error.MalformedSequence` error if the decompressed sequence would be longer + /// than `sequence_size_limit` or the sequence's offset is too large; returns + /// `error.EndOfStream` if `bit_reader` does not contain enough bits; returns + /// `error.UnexpectedEndOfLiteralStream` if the decoder state's literal streams + /// do not contain enough literals for the sequence (this may mean the literal + /// stream or the sequence is malformed). + pub fn decodeSequenceSlice( + self: *DecodeState, + dest: []u8, + write_pos: usize, + bit_reader: *readers.ReverseBitReader, + sequence_size_limit: usize, + last_sequence: bool, + ) DecodeSequenceError!usize { + const sequence = try self.nextSequence(bit_reader); + const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; + if (sequence_length > sequence_size_limit) return error.MalformedSequence; + + try self.executeSequenceSlice(dest, write_pos, sequence); + if (!last_sequence) { + try self.updateState(.literal, bit_reader); + try self.updateState(.match, bit_reader); + try self.updateState(.offset, bit_reader); + } + return sequence_length; + } + + /// Decode one sequence from `bit_reader` into `dest`; see `decodeSequenceSlice`. + pub fn decodeSequenceRingBuffer( + self: *DecodeState, + dest: *RingBuffer, + bit_reader: anytype, + sequence_size_limit: usize, + last_sequence: bool, + ) DecodeSequenceError!usize { + const sequence = try self.nextSequence(bit_reader); + const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; + if (sequence_length > sequence_size_limit) return error.MalformedSequence; + + try self.executeSequenceRingBuffer(dest, sequence); + if (!last_sequence) { + try self.updateState(.literal, bit_reader); + try self.updateState(.match, bit_reader); + try self.updateState(.offset, bit_reader); + } + return sequence_length; + } + + fn nextLiteralMultiStream( + self: *DecodeState, + ) error{BitStreamHasNoStartBit}!void { + self.literal_stream_index += 1; + try self.initLiteralStream(self.literal_streams.four[self.literal_stream_index]); + } + + pub fn initLiteralStream(self: *DecodeState, bytes: []const u8) error{BitStreamHasNoStartBit}!void { + try self.literal_stream_reader.init(bytes); + } + + const LiteralBitsError = error{ + BitStreamHasNoStartBit, + UnexpectedEndOfLiteralStream, + }; + fn readLiteralsBits( + self: *DecodeState, + comptime T: type, + bit_count_to_read: usize, + ) LiteralBitsError!T { + return self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch bits: { + if (self.literal_streams == .four and self.literal_stream_index < 3) { + try self.nextLiteralMultiStream(); + break :bits self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch + return error.UnexpectedEndOfLiteralStream; + } else { + return error.UnexpectedEndOfLiteralStream; + } + }; + } + + const DecodeLiteralsError = error{ + MalformedLiteralsLength, + PrefixNotFound, + } || LiteralBitsError; + + /// Decode `len` bytes of literals into `dest`. `literals` should be the + /// `LiteralsSection` that was passed to `prepare()`. Returns + /// `error.MalformedLiteralsLength` if the number of literal bytes decoded by + /// `self` plus `len` is greater than the regenerated size of `literals`. + /// Returns `error.UnexpectedEndOfLiteralStream` and `error.PrefixNotFound` if + /// there are problems decoding Huffman compressed literals. + pub fn decodeLiteralsSlice( + self: *DecodeState, + dest: []u8, + len: usize, + ) DecodeLiteralsError!void { + if (self.literal_written_count + len > self.literal_header.regenerated_size) + return error.MalformedLiteralsLength; + + switch (self.literal_header.block_type) { + .raw => { + const literals_end = self.literal_written_count + len; + const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; + std.mem.copy(u8, dest, literal_data); + self.literal_written_count += len; + }, + .rle => { + var i: usize = 0; + while (i < len) : (i += 1) { + dest[i] = self.literal_streams.one[0]; + } + self.literal_written_count += len; + }, + .compressed, .treeless => { + // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; + const huffman_tree = self.huffman_tree orelse unreachable; + const max_bit_count = huffman_tree.max_bit_count; + const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount( + huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, + max_bit_count, + ); + var bits_read: u4 = 0; + var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; + var bit_count_to_read: u4 = starting_bit_count; + var i: usize = 0; + while (i < len) : (i += 1) { + var prefix: u16 = 0; + while (true) { + const new_bits = self.readLiteralsBits(u16, bit_count_to_read) catch |err| { + return err; + }; + prefix <<= bit_count_to_read; + prefix |= new_bits; + bits_read += bit_count_to_read; + const result = huffman_tree.query(huffman_tree_index, prefix) catch |err| { + return err; + }; + + switch (result) { + .symbol => |sym| { + dest[i] = sym; + bit_count_to_read = starting_bit_count; + bits_read = 0; + huffman_tree_index = huffman_tree.symbol_count_minus_one; + break; + }, + .index => |index| { + huffman_tree_index = index; + const bit_count = LiteralsSection.HuffmanTree.weightToBitCount( + huffman_tree.nodes[index].weight, + max_bit_count, + ); + bit_count_to_read = bit_count - bits_read; + }, + } + } + } + self.literal_written_count += len; + }, + } + } + + /// Decode literals into `dest`; see `decodeLiteralsSlice()`. + pub fn decodeLiteralsRingBuffer( + self: *DecodeState, + dest: *RingBuffer, + len: usize, + ) DecodeLiteralsError!void { + if (self.literal_written_count + len > self.literal_header.regenerated_size) + return error.MalformedLiteralsLength; + + switch (self.literal_header.block_type) { + .raw => { + const literals_end = self.literal_written_count + len; + const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; + dest.writeSliceAssumeCapacity(literal_data); + self.literal_written_count += len; + }, + .rle => { + var i: usize = 0; + while (i < len) : (i += 1) { + dest.writeAssumeCapacity(self.literal_streams.one[0]); + } + self.literal_written_count += len; + }, + .compressed, .treeless => { + // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; + const huffman_tree = self.huffman_tree orelse unreachable; + const max_bit_count = huffman_tree.max_bit_count; + const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount( + huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, + max_bit_count, + ); + var bits_read: u4 = 0; + var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; + var bit_count_to_read: u4 = starting_bit_count; + var i: usize = 0; + while (i < len) : (i += 1) { + var prefix: u16 = 0; + while (true) { + const new_bits = try self.readLiteralsBits(u16, bit_count_to_read); + prefix <<= bit_count_to_read; + prefix |= new_bits; + bits_read += bit_count_to_read; + const result = try huffman_tree.query(huffman_tree_index, prefix); + + switch (result) { + .symbol => |sym| { + dest.writeAssumeCapacity(sym); + bit_count_to_read = starting_bit_count; + bits_read = 0; + huffman_tree_index = huffman_tree.symbol_count_minus_one; + break; + }, + .index => |index| { + huffman_tree_index = index; + const bit_count = LiteralsSection.HuffmanTree.weightToBitCount( + huffman_tree.nodes[index].weight, + max_bit_count, + ); + bit_count_to_read = bit_count - bits_read; + }, + } + } + } + self.literal_written_count += len; + }, + } + } + + fn getCode(self: *DecodeState, comptime choice: DataType) u32 { + return switch (@field(self, @tagName(choice)).table) { + .rle => |value| value, + .fse => |table| table[@field(self, @tagName(choice)).state].symbol, + }; + } +}; + +/// Decode a single block from `src` into `dest`. The beginning of `src` must be +/// the start of the block content (i.e. directly after the block header). +/// Increments `consumed_count` by the number of bytes read from `src` to decode +/// the block and returns the decompressed size of the block. +/// +/// Errors returned: +/// +/// - `error.BlockSizeOverMaximum` if block's size is larger than 1 << 17 or +/// `dest[written_count..].len` +/// - `error.MalformedBlockSize` if `src.len` is smaller than the block size +/// and the block is a raw or compressed block +/// - `error.ReservedBlock` if the block is a reserved block +/// - `error.MalformedRleBlock` if the block is an RLE block and `src.len < 1` +/// - `error.MalformedCompressedBlock` if there are errors decoding a +/// compressed block +/// - `error.EndOfStream` if the sequence bit stream ends unexpectedly +pub fn decodeBlock( + dest: []u8, + src: []const u8, + block_header: frame.ZStandard.Block.Header, + decode_state: *DecodeState, + consumed_count: *usize, + written_count: usize, +) Error!usize { + const block_size_max = @min(1 << 17, dest[written_count..].len); // 128KiB + const block_size = block_header.block_size; + if (block_size_max < block_size) return error.BlockSizeOverMaximum; + switch (block_header.block_type) { + .raw => { + if (src.len < block_size) return error.MalformedBlockSize; + const data = src[0..block_size]; + std.mem.copy(u8, dest[written_count..], data); + consumed_count.* += block_size; + return block_size; + }, + .rle => { + if (src.len < 1) return error.MalformedRleBlock; + var write_pos: usize = written_count; + while (write_pos < block_size + written_count) : (write_pos += 1) { + dest[write_pos] = src[0]; + } + consumed_count.* += 1; + return block_size; + }, + .compressed => { + if (src.len < block_size) return error.MalformedBlockSize; + var bytes_read: usize = 0; + const literals = decodeLiteralsSectionSlice(src, &bytes_read) catch + return error.MalformedCompressedBlock; + var fbs = std.io.fixedBufferStream(src[bytes_read..]); + const fbs_reader = fbs.reader(); + const sequences_header = decodeSequencesHeader(fbs_reader) catch + return error.MalformedCompressedBlock; + + decode_state.prepare(fbs_reader, literals, sequences_header) catch + return error.MalformedCompressedBlock; + + bytes_read += fbs.pos; + + var bytes_written: usize = 0; + if (sequences_header.sequence_count > 0) { + const bit_stream_bytes = src[bytes_read..block_size]; + var bit_stream: readers.ReverseBitReader = undefined; + bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; + + decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; + + var sequence_size_limit = block_size_max; + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + const write_pos = written_count + bytes_written; + const decompressed_size = decode_state.decodeSequenceSlice( + dest, + write_pos, + &bit_stream, + sequence_size_limit, + i == sequences_header.sequence_count - 1, + ) catch return error.MalformedCompressedBlock; + bytes_written += decompressed_size; + sequence_size_limit -= decompressed_size; + } + + bytes_read += bit_stream_bytes.len; + } + if (bytes_read != block_size) return error.MalformedCompressedBlock; + + if (decode_state.literal_written_count < literals.header.regenerated_size) { + const len = literals.header.regenerated_size - decode_state.literal_written_count; + decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], len) catch + return error.MalformedCompressedBlock; + bytes_written += len; + } + + consumed_count.* += bytes_read; + return bytes_written; + }, + .reserved => return error.ReservedBlock, + } +} + +/// Decode a single block from `src` into `dest`; see `decodeBlock()`. Returns +/// the size of the decompressed block, which can be used with `dest.sliceLast()` +/// to get the decompressed bytes. `error.BlockSizeOverMaximum` is returned if +/// the block's compressed or decompressed size is larger than `block_size_max`. +pub fn decodeBlockRingBuffer( + dest: *RingBuffer, + src: []const u8, + block_header: frame.ZStandard.Block.Header, + decode_state: *DecodeState, + consumed_count: *usize, + block_size_max: usize, +) Error!usize { + const block_size = block_header.block_size; + if (block_size_max < block_size) return error.BlockSizeOverMaximum; + switch (block_header.block_type) { + .raw => { + if (src.len < block_size) return error.MalformedBlockSize; + const data = src[0..block_size]; + dest.writeSliceAssumeCapacity(data); + consumed_count.* += block_size; + return block_size; + }, + .rle => { + if (src.len < 1) return error.MalformedRleBlock; + var write_pos: usize = 0; + while (write_pos < block_size) : (write_pos += 1) { + dest.writeAssumeCapacity(src[0]); + } + consumed_count.* += 1; + return block_size; + }, + .compressed => { + if (src.len < block_size) return error.MalformedBlockSize; + var bytes_read: usize = 0; + const literals = decodeLiteralsSectionSlice(src, &bytes_read) catch + return error.MalformedCompressedBlock; + var fbs = std.io.fixedBufferStream(src[bytes_read..]); + const fbs_reader = fbs.reader(); + const sequences_header = decodeSequencesHeader(fbs_reader) catch + return error.MalformedCompressedBlock; + + decode_state.prepare(fbs_reader, literals, sequences_header) catch + return error.MalformedCompressedBlock; + + bytes_read += fbs.pos; + + var bytes_written: usize = 0; + if (sequences_header.sequence_count > 0) { + const bit_stream_bytes = src[bytes_read..block_size]; + var bit_stream: readers.ReverseBitReader = undefined; + bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; + + decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; + + var sequence_size_limit = block_size_max; + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + const decompressed_size = decode_state.decodeSequenceRingBuffer( + dest, + &bit_stream, + sequence_size_limit, + i == sequences_header.sequence_count - 1, + ) catch return error.MalformedCompressedBlock; + bytes_written += decompressed_size; + sequence_size_limit -= decompressed_size; + } + + bytes_read += bit_stream_bytes.len; + } + if (bytes_read != block_size) return error.MalformedCompressedBlock; + + if (decode_state.literal_written_count < literals.header.regenerated_size) { + const len = literals.header.regenerated_size - decode_state.literal_written_count; + decode_state.decodeLiteralsRingBuffer(dest, len) catch + return error.MalformedCompressedBlock; + bytes_written += len; + } + + consumed_count.* += bytes_read; + if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; + return bytes_written; + }, + .reserved => return error.ReservedBlock, + } +} + +/// Decode a single block from `source` into `dest`. Literal and sequence data +/// from the block is copied into `literals_buffer` and `sequence_buffer`, which +/// must be large enough or `error.LiteralsBufferTooSmall` and +/// `error.SequenceBufferTooSmall` are returned (the maximum block size is an +/// upper bound for the size of both buffers). See `decodeBlock` +/// and `decodeBlockRingBuffer` for function that can decode a block without +/// these extra copies. +pub fn decodeBlockReader( + dest: *RingBuffer, + source: anytype, + block_header: frame.ZStandard.Block.Header, + decode_state: *DecodeState, + block_size_max: usize, + literals_buffer: []u8, + sequence_buffer: []u8, +) !void { + const block_size = block_header.block_size; + var block_reader_limited = std.io.limitedReader(source, block_size); + const block_reader = block_reader_limited.reader(); + if (block_size_max < block_size) return error.BlockSizeOverMaximum; + switch (block_header.block_type) { + .raw => { + const slice = dest.sliceAt(dest.write_index, block_size); + try source.readNoEof(slice.first); + try source.readNoEof(slice.second); + dest.write_index = dest.mask2(dest.write_index + block_size); + }, + .rle => { + const byte = try source.readByte(); + var i: usize = 0; + while (i < block_size) : (i += 1) { + dest.writeAssumeCapacity(byte); + } + }, + .compressed => { + const literals = try decodeLiteralsSection(block_reader, literals_buffer); + const sequences_header = try decodeSequencesHeader(block_reader); + + try decode_state.prepare(block_reader, literals, sequences_header); + + if (sequences_header.sequence_count > 0) { + if (sequence_buffer.len < block_reader_limited.bytes_left) + return error.SequenceBufferTooSmall; + + const size = try block_reader.readAll(sequence_buffer); + var bit_stream: readers.ReverseBitReader = undefined; + try bit_stream.init(sequence_buffer[0..size]); + + decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; + + var sequence_size_limit = block_size_max; + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + const decompressed_size = decode_state.decodeSequenceRingBuffer( + dest, + &bit_stream, + sequence_size_limit, + i == sequences_header.sequence_count - 1, + ) catch return error.MalformedCompressedBlock; + sequence_size_limit -= decompressed_size; + } + } + + if (decode_state.literal_written_count < literals.header.regenerated_size) { + const len = literals.header.regenerated_size - decode_state.literal_written_count; + decode_state.decodeLiteralsRingBuffer(dest, len) catch + return error.MalformedCompressedBlock; + } + + decode_state.literal_written_count = 0; + assert(block_reader.readByte() == error.EndOfStream); + }, + .reserved => return error.ReservedBlock, + } +} + +/// Decode the header of a block. +pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { + const last_block = src[0] & 1 == 1; + const block_type = @intToEnum(frame.ZStandard.Block.Type, (src[0] & 0b110) >> 1); + const block_size = ((src[0] & 0b11111000) >> 3) + (@as(u21, src[1]) << 5) + (@as(u21, src[2]) << 13); + return .{ + .last_block = last_block, + .block_type = block_type, + .block_size = block_size, + }; +} + +pub fn decodeBlockHeaderSlice(src: []const u8) error{EndOfStream}!frame.ZStandard.Block.Header { + if (src.len < 3) return error.EndOfStream; + return decodeBlockHeader(src[0..3]); +} + +/// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the +/// number of bytes the section uses. +/// +/// Errors: +/// - returns `error.MalformedLiteralsHeader` if the header is invalid +/// - returns `error.MalformedLiteralsSection` if there are errors decoding +pub fn decodeLiteralsSectionSlice( + src: []const u8, + consumed_count: *usize, +) (error{ MalformedLiteralsHeader, MalformedLiteralsSection, EndOfStream } || huffman.Error)!LiteralsSection { + var bytes_read: usize = 0; + const header = header: { + var fbs = std.io.fixedBufferStream(src); + defer bytes_read = fbs.pos; + break :header decodeLiteralsHeader(fbs.reader()) catch return error.MalformedLiteralsHeader; + }; + switch (header.block_type) { + .raw => { + if (src.len < bytes_read + header.regenerated_size) return error.MalformedLiteralsSection; + const stream = src[bytes_read .. bytes_read + header.regenerated_size]; + consumed_count.* += header.regenerated_size + bytes_read; + return LiteralsSection{ + .header = header, + .huffman_tree = null, + .streams = .{ .one = stream }, + }; + }, + .rle => { + if (src.len < bytes_read + 1) return error.MalformedLiteralsSection; + const stream = src[bytes_read .. bytes_read + 1]; + consumed_count.* += 1 + bytes_read; + return LiteralsSection{ + .header = header, + .huffman_tree = null, + .streams = .{ .one = stream }, + }; + }, + .compressed, .treeless => { + const huffman_tree_start = bytes_read; + const huffman_tree = if (header.block_type == .compressed) + try huffman.decodeHuffmanTreeSlice(src[bytes_read..], &bytes_read) + else + null; + const huffman_tree_size = bytes_read - huffman_tree_start; + const total_streams_size = @as(usize, header.compressed_size.?) - huffman_tree_size; + + if (src.len < bytes_read + total_streams_size) return error.MalformedLiteralsSection; + const stream_data = src[bytes_read .. bytes_read + total_streams_size]; + + const streams = try decodeStreams(header.size_format, stream_data); + consumed_count.* += bytes_read + total_streams_size; + return LiteralsSection{ + .header = header, + .huffman_tree = huffman_tree, + .streams = streams, + }; + }, + } +} + +/// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the +/// number of bytes the section uses. +/// +/// Errors: +/// - returns `error.MalformedLiteralsHeader` if the header is invalid +/// - returns `error.MalformedLiteralsSection` if there are errors decoding +pub fn decodeLiteralsSection( + source: anytype, + buffer: []u8, +) !LiteralsSection { + const header = try decodeLiteralsHeader(source); + switch (header.block_type) { + .raw => { + try source.readNoEof(buffer[0..header.regenerated_size]); + return LiteralsSection{ + .header = header, + .huffman_tree = null, + .streams = .{ .one = buffer }, + }; + }, + .rle => { + buffer[0] = try source.readByte(); + return LiteralsSection{ + .header = header, + .huffman_tree = null, + .streams = .{ .one = buffer[0..1] }, + }; + }, + .compressed, .treeless => { + var counting_reader = std.io.countingReader(source); + const huffman_tree = if (header.block_type == .compressed) + try huffman.decodeHuffmanTree(counting_reader.reader(), buffer) + else + null; + const huffman_tree_size = counting_reader.bytes_read; + const total_streams_size = @as(usize, header.compressed_size.?) - @intCast(usize, huffman_tree_size); + + if (total_streams_size > buffer.len) return error.LiteralsBufferTooSmall; + try source.readNoEof(buffer[0..total_streams_size]); + const stream_data = buffer[0..total_streams_size]; + + const streams = try decodeStreams(header.size_format, stream_data); + return LiteralsSection{ + .header = header, + .huffman_tree = huffman_tree, + .streams = streams, + }; + }, + } +} + +fn decodeStreams(size_format: u2, stream_data: []const u8) !LiteralsSection.Streams { + if (size_format == 0) { + return .{ .one = stream_data }; + } + + if (stream_data.len < 6) return error.MalformedLiteralsSection; + + const stream_1_length = @as(usize, readInt(u16, stream_data[0..2])); + const stream_2_length = @as(usize, readInt(u16, stream_data[2..4])); + const stream_3_length = @as(usize, readInt(u16, stream_data[4..6])); + + const stream_1_start = 6; + const stream_2_start = stream_1_start + stream_1_length; + const stream_3_start = stream_2_start + stream_2_length; + const stream_4_start = stream_3_start + stream_3_length; + + return .{ .four = .{ + stream_data[stream_1_start .. stream_1_start + stream_1_length], + stream_data[stream_2_start .. stream_2_start + stream_2_length], + stream_data[stream_3_start .. stream_3_start + stream_3_length], + stream_data[stream_4_start..], + } }; +} + +/// Decode a literals section header. +pub fn decodeLiteralsHeader(source: anytype) !LiteralsSection.Header { + const byte0 = try source.readByte(); + const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); + const size_format = @intCast(u2, (byte0 & 0b1100) >> 2); + var regenerated_size: u20 = undefined; + var compressed_size: ?u18 = null; + switch (block_type) { + .raw, .rle => { + switch (size_format) { + 0, 2 => { + regenerated_size = byte0 >> 3; + }, + 1 => regenerated_size = (byte0 >> 4) + (@as(u20, try source.readByte()) << 4), + 3 => regenerated_size = (byte0 >> 4) + + (@as(u20, try source.readByte()) << 4) + + (@as(u20, try source.readByte()) << 12), + } + }, + .compressed, .treeless => { + const byte1 = try source.readByte(); + const byte2 = try source.readByte(); + switch (size_format) { + 0, 1 => { + regenerated_size = (byte0 >> 4) + ((@as(u20, byte1) & 0b00111111) << 4); + compressed_size = ((byte1 & 0b11000000) >> 6) + (@as(u18, byte2) << 2); + }, + 2 => { + const byte3 = try source.readByte(); + regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00000011) << 12); + compressed_size = ((byte2 & 0b11111100) >> 2) + (@as(u18, byte3) << 6); + }, + 3 => { + const byte3 = try source.readByte(); + const byte4 = try source.readByte(); + regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00111111) << 12); + compressed_size = ((byte2 & 0b11000000) >> 6) + (@as(u18, byte3) << 2) + (@as(u18, byte4) << 10); + }, + } + }, + } + return LiteralsSection.Header{ + .block_type = block_type, + .size_format = size_format, + .regenerated_size = regenerated_size, + .compressed_size = compressed_size, + }; +} + +/// Decode a sequences section header. +/// +/// Errors: +/// - returns `error.ReservedBitSet` is the reserved bit is set +/// - returns `error.MalformedSequencesHeader` if the header is invalid +pub fn decodeSequencesHeader( + source: anytype, +) !SequencesSection.Header { + var sequence_count: u24 = undefined; + + const byte0 = try source.readByte(); + if (byte0 == 0) { + return SequencesSection.Header{ + .sequence_count = 0, + .offsets = undefined, + .match_lengths = undefined, + .literal_lengths = undefined, + }; + } else if (byte0 < 128) { + sequence_count = byte0; + } else if (byte0 < 255) { + sequence_count = (@as(u24, (byte0 - 128)) << 8) + try source.readByte(); + } else { + sequence_count = (try source.readByte()) + (@as(u24, try source.readByte()) << 8) + 0x7F00; + } + + const compression_modes = try source.readByte(); + + const matches_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00001100) >> 2); + const offsets_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00110000) >> 4); + const literal_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b11000000) >> 6); + if (compression_modes & 0b11 != 0) return error.ReservedBitSet; + + return SequencesSection.Header{ + .sequence_count = sequence_count, + .offsets = offsets_mode, + .match_lengths = matches_mode, + .literal_lengths = literal_mode, + }; +} diff --git a/lib/std/compress/zstandard/decode/fse.zig b/lib/std/compress/zstandard/decode/fse.zig new file mode 100644 index 0000000000..5f87c1f81b --- /dev/null +++ b/lib/std/compress/zstandard/decode/fse.zig @@ -0,0 +1,154 @@ +const std = @import("std"); +const assert = std.debug.assert; + +const types = @import("../types.zig"); +const Table = types.compressed_block.Table; + +pub fn decodeFseTable( + bit_reader: anytype, + expected_symbol_count: usize, + max_accuracy_log: u4, + entries: []Table.Fse, +) !usize { + const accuracy_log_biased = try bit_reader.readBitsNoEof(u4, 4); + if (accuracy_log_biased > max_accuracy_log -| 5) return error.MalformedAccuracyLog; + const accuracy_log = accuracy_log_biased + 5; + + var values: [256]u16 = undefined; + var value_count: usize = 0; + + const total_probability = @as(u16, 1) << accuracy_log; + var accumulated_probability: u16 = 0; + + while (accumulated_probability < total_probability) { + // WARNING: The RFC in poorly worded, and would suggest std.math.log2_int_ceil is correct here, + // but power of two (remaining probabilities + 1) need max bits set to 1 more. + const max_bits = std.math.log2_int(u16, total_probability - accumulated_probability + 1) + 1; + const small = try bit_reader.readBitsNoEof(u16, max_bits - 1); + + const cutoff = (@as(u16, 1) << max_bits) - 1 - (total_probability - accumulated_probability + 1); + + const value = if (small < cutoff) + small + else value: { + const value_read = small + (try bit_reader.readBitsNoEof(u16, 1) << (max_bits - 1)); + break :value if (value_read < @as(u16, 1) << (max_bits - 1)) + value_read + else + value_read - cutoff; + }; + + accumulated_probability += if (value != 0) value - 1 else 1; + + values[value_count] = value; + value_count += 1; + + if (value == 1) { + while (true) { + const repeat_flag = try bit_reader.readBitsNoEof(u2, 2); + var i: usize = 0; + while (i < repeat_flag) : (i += 1) { + values[value_count] = 1; + value_count += 1; + } + if (repeat_flag < 3) break; + } + } + } + bit_reader.alignToByte(); + + if (value_count < 2) return error.MalformedFseTable; + if (accumulated_probability != total_probability) return error.MalformedFseTable; + if (value_count > expected_symbol_count) return error.MalformedFseTable; + + const table_size = total_probability; + + try buildFseTable(values[0..value_count], entries[0..table_size]); + return table_size; +} + +fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { + const total_probability = @intCast(u16, entries.len); + const accuracy_log = std.math.log2_int(u16, total_probability); + assert(total_probability <= 1 << 9); + + var less_than_one_count: usize = 0; + for (values) |value, i| { + if (value == 0) { + entries[entries.len - 1 - less_than_one_count] = Table.Fse{ + .symbol = @intCast(u8, i), + .baseline = 0, + .bits = accuracy_log, + }; + less_than_one_count += 1; + } + } + + var position: usize = 0; + var temp_states: [1 << 9]u16 = undefined; + for (values) |value, symbol| { + if (value == 0 or value == 1) continue; + const probability = value - 1; + + const state_share_dividend = std.math.ceilPowerOfTwo(u16, probability) catch + return error.MalformedFseTable; + const share_size = @divExact(total_probability, state_share_dividend); + const double_state_count = state_share_dividend - probability; + const single_state_count = probability - double_state_count; + const share_size_log = std.math.log2_int(u16, share_size); + + var i: u16 = 0; + while (i < probability) : (i += 1) { + temp_states[i] = @intCast(u16, position); + position += (entries.len >> 1) + (entries.len >> 3) + 3; + position &= entries.len - 1; + while (position >= entries.len - less_than_one_count) { + position += (entries.len >> 1) + (entries.len >> 3) + 3; + position &= entries.len - 1; + } + } + std.sort.sort(u16, temp_states[0..probability], {}, std.sort.asc(u16)); + i = 0; + while (i < probability) : (i += 1) { + entries[temp_states[i]] = if (i < double_state_count) Table.Fse{ + .symbol = @intCast(u8, symbol), + .bits = share_size_log + 1, + .baseline = single_state_count * share_size + i * 2 * share_size, + } else Table.Fse{ + .symbol = @intCast(u8, symbol), + .bits = share_size_log, + .baseline = (i - double_state_count) * share_size, + }; + } + } +} + +test buildFseTable { + const literals_length_default_values = [36]u16{ + 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 2, 2, 2, + 0, 0, 0, 0, + }; + + const match_lengths_default_values = [53]u16{ + 2, 5, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 0, + }; + + const offset_codes_default_values = [29]u16{ + 2, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, + }; + + var entries: [64]Table.Fse = undefined; + try buildFseTable(&literals_length_default_values, &entries); + try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_literal_fse_table.fse, &entries); + + try buildFseTable(&match_lengths_default_values, &entries); + try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_match_fse_table.fse, &entries); + + try buildFseTable(&offset_codes_default_values, entries[0..32]); + try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_offset_fse_table.fse, entries[0..32]); +} diff --git a/lib/std/compress/zstandard/decode/huffman.zig b/lib/std/compress/zstandard/decode/huffman.zig new file mode 100644 index 0000000000..c759bfd6ab --- /dev/null +++ b/lib/std/compress/zstandard/decode/huffman.zig @@ -0,0 +1,212 @@ +const std = @import("std"); + +const types = @import("../types.zig"); +const LiteralsSection = types.compressed_block.LiteralsSection; +const Table = types.compressed_block.Table; + +const readers = @import("../readers.zig"); + +const decodeFseTable = @import("fse.zig").decodeFseTable; + +pub const Error = error{ + MalformedHuffmanTree, + MalformedFseTable, + MalformedAccuracyLog, + EndOfStream, +}; + +fn decodeFseHuffmanTree(source: anytype, compressed_size: usize, buffer: []u8, weights: *[256]u4) !usize { + var stream = std.io.limitedReader(source, compressed_size); + var bit_reader = readers.bitReader(stream.reader()); + + var entries: [1 << 6]Table.Fse = undefined; + const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { + error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, + error.EndOfStream => return error.MalformedFseTable, + }; + const accuracy_log = std.math.log2_int_ceil(usize, table_size); + + const amount = try stream.reader().readAll(buffer); + var huff_bits: readers.ReverseBitReader = undefined; + huff_bits.init(buffer[0..amount]) catch return error.MalformedHuffmanTree; + + return assignWeights(&huff_bits, accuracy_log, &entries, weights); +} + +fn decodeFseHuffmanTreeSlice(src: []const u8, compressed_size: usize, weights: *[256]u4) !usize { + if (src.len < compressed_size) return error.MalformedHuffmanTree; + var stream = std.io.fixedBufferStream(src[0..compressed_size]); + var counting_reader = std.io.countingReader(stream.reader()); + var bit_reader = readers.bitReader(counting_reader.reader()); + + var entries: [1 << 6]Table.Fse = undefined; + const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { + error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, + error.EndOfStream => return error.MalformedFseTable, + }; + const accuracy_log = std.math.log2_int_ceil(usize, table_size); + + const start_index = std.math.cast(usize, counting_reader.bytes_read) orelse return error.MalformedHuffmanTree; + var huff_data = src[start_index..compressed_size]; + var huff_bits: readers.ReverseBitReader = undefined; + huff_bits.init(huff_data) catch return error.MalformedHuffmanTree; + + return assignWeights(&huff_bits, accuracy_log, &entries, weights); +} + +fn assignWeights(huff_bits: *readers.ReverseBitReader, accuracy_log: usize, entries: *[1 << 6]Table.Fse, weights: *[256]u4) !usize { + var i: usize = 0; + var even_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; + var odd_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; + + while (i < 255) { + const even_data = entries[even_state]; + var read_bits: usize = 0; + const even_bits = huff_bits.readBits(u32, even_data.bits, &read_bits) catch unreachable; + weights[i] = std.math.cast(u4, even_data.symbol) orelse return error.MalformedHuffmanTree; + i += 1; + if (read_bits < even_data.bits) { + weights[i] = std.math.cast(u4, entries[odd_state].symbol) orelse return error.MalformedHuffmanTree; + i += 1; + break; + } + even_state = even_data.baseline + even_bits; + + read_bits = 0; + const odd_data = entries[odd_state]; + const odd_bits = huff_bits.readBits(u32, odd_data.bits, &read_bits) catch unreachable; + weights[i] = std.math.cast(u4, odd_data.symbol) orelse return error.MalformedHuffmanTree; + i += 1; + if (read_bits < odd_data.bits) { + if (i == 256) return error.MalformedHuffmanTree; + weights[i] = std.math.cast(u4, entries[even_state].symbol) orelse return error.MalformedHuffmanTree; + i += 1; + break; + } + odd_state = odd_data.baseline + odd_bits; + } else return error.MalformedHuffmanTree; + + return i + 1; // stream contains all but the last symbol +} + +fn decodeDirectHuffmanTree(source: anytype, encoded_symbol_count: usize, weights: *[256]u4) !usize { + const weights_byte_count = (encoded_symbol_count + 1) / 2; + var i: usize = 0; + while (i < weights_byte_count) : (i += 1) { + const byte = try source.readByte(); + weights[2 * i] = @intCast(u4, byte >> 4); + weights[2 * i + 1] = @intCast(u4, byte & 0xF); + } + return encoded_symbol_count + 1; +} + +fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.PrefixedSymbol, weights: [256]u4) usize { + for (weight_sorted_prefixed_symbols) |_, i| { + weight_sorted_prefixed_symbols[i] = .{ + .symbol = @intCast(u8, i), + .weight = undefined, + .prefix = undefined, + }; + } + + std.sort.sort( + LiteralsSection.HuffmanTree.PrefixedSymbol, + weight_sorted_prefixed_symbols, + weights, + lessThanByWeight, + ); + + var prefix: u16 = 0; + var prefixed_symbol_count: usize = 0; + var sorted_index: usize = 0; + const symbol_count = weight_sorted_prefixed_symbols.len; + while (sorted_index < symbol_count) { + var symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; + const weight = weights[symbol]; + if (weight == 0) { + sorted_index += 1; + continue; + } + + while (sorted_index < symbol_count) : ({ + sorted_index += 1; + prefixed_symbol_count += 1; + prefix += 1; + }) { + symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; + if (weights[symbol] != weight) { + prefix = ((prefix - 1) >> (weights[symbol] - weight)) + 1; + break; + } + weight_sorted_prefixed_symbols[prefixed_symbol_count].symbol = symbol; + weight_sorted_prefixed_symbols[prefixed_symbol_count].prefix = prefix; + weight_sorted_prefixed_symbols[prefixed_symbol_count].weight = weight; + } + } + return prefixed_symbol_count; +} + +fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) LiteralsSection.HuffmanTree { + var weight_power_sum: u16 = 0; + for (weights[0 .. symbol_count - 1]) |value| { + if (value > 0) { + weight_power_sum += @as(u16, 1) << (value - 1); + } + } + + // advance to next power of two (even if weight_power_sum is a power of 2) + const max_number_of_bits = std.math.log2_int(u16, weight_power_sum) + 1; + const next_power_of_two = @as(u16, 1) << max_number_of_bits; + weights[symbol_count - 1] = std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1; + + var weight_sorted_prefixed_symbols: [256]LiteralsSection.HuffmanTree.PrefixedSymbol = undefined; + const prefixed_symbol_count = assignSymbols(weight_sorted_prefixed_symbols[0..symbol_count], weights.*); + const tree = LiteralsSection.HuffmanTree{ + .max_bit_count = max_number_of_bits, + .symbol_count_minus_one = @intCast(u8, prefixed_symbol_count - 1), + .nodes = weight_sorted_prefixed_symbols, + }; + return tree; +} + +pub fn decodeHuffmanTree(source: anytype, buffer: []u8) !LiteralsSection.HuffmanTree { + const header = try source.readByte(); + var weights: [256]u4 = undefined; + const symbol_count = if (header < 128) + // FSE compressed weights + try decodeFseHuffmanTree(source, header, buffer, &weights) + else + try decodeDirectHuffmanTree(source, header - 127, &weights); + + return buildHuffmanTree(&weights, symbol_count); +} + +pub fn decodeHuffmanTreeSlice(src: []const u8, consumed_count: *usize) Error!LiteralsSection.HuffmanTree { + if (src.len == 0) return error.MalformedHuffmanTree; + const header = src[0]; + var bytes_read: usize = 1; + var weights: [256]u4 = undefined; + const symbol_count = if (header < 128) count: { + // FSE compressed weights + bytes_read += header; + break :count try decodeFseHuffmanTreeSlice(src[1..], header, &weights); + } else count: { + var fbs = std.io.fixedBufferStream(src[1..]); + defer bytes_read += fbs.pos; + break :count try decodeDirectHuffmanTree(fbs.reader(), header - 127, &weights); + }; + + consumed_count.* += bytes_read; + return buildHuffmanTree(&weights, symbol_count); +} + +fn lessThanByWeight( + weights: [256]u4, + lhs: LiteralsSection.HuffmanTree.PrefixedSymbol, + rhs: LiteralsSection.HuffmanTree.PrefixedSymbol, +) bool { + // NOTE: this function relies on the use of a stable sorting algorithm, + // otherwise a special case of if (weights[lhs] == weights[rhs]) return lhs < rhs; + // should be added + return weights[lhs.symbol] < weights[rhs.symbol]; +} diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 2b397007a9..abc30fda2a 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -6,8 +6,13 @@ const frame = types.frame; const LiteralsSection = types.compressed_block.LiteralsSection; const SequencesSection = types.compressed_block.SequencesSection; const Table = types.compressed_block.Table; + +pub const block = @import("decode/block.zig"); + pub const RingBuffer = @import("RingBuffer.zig"); +const readers = @import("readers.zig"); + const readInt = std.mem.readIntLittle; const readIntSlice = std.mem.readIntSliceLittle; fn readVarInt(comptime T: type, bytes: []const u8) T { @@ -61,526 +66,6 @@ pub fn decodeFrame( }; } -pub const DecodeState = struct { - repeat_offsets: [3]u32, - - offset: StateData(8), - match: StateData(9), - literal: StateData(9), - - offset_fse_buffer: []Table.Fse, - match_fse_buffer: []Table.Fse, - literal_fse_buffer: []Table.Fse, - - fse_tables_undefined: bool, - - literal_stream_reader: ReverseBitReader, - literal_stream_index: usize, - literal_streams: LiteralsSection.Streams, - literal_header: LiteralsSection.Header, - huffman_tree: ?LiteralsSection.HuffmanTree, - - literal_written_count: usize, - - fn StateData(comptime max_accuracy_log: comptime_int) type { - return struct { - state: State, - table: Table, - accuracy_log: u8, - - const State = std.meta.Int(.unsigned, max_accuracy_log); - }; - } - - pub fn init( - literal_fse_buffer: []Table.Fse, - match_fse_buffer: []Table.Fse, - offset_fse_buffer: []Table.Fse, - ) DecodeState { - return DecodeState{ - .repeat_offsets = .{ - types.compressed_block.start_repeated_offset_1, - types.compressed_block.start_repeated_offset_2, - types.compressed_block.start_repeated_offset_3, - }, - - .offset = undefined, - .match = undefined, - .literal = undefined, - - .literal_fse_buffer = literal_fse_buffer, - .match_fse_buffer = match_fse_buffer, - .offset_fse_buffer = offset_fse_buffer, - - .fse_tables_undefined = true, - - .literal_written_count = 0, - .literal_header = undefined, - .literal_streams = undefined, - .literal_stream_reader = undefined, - .literal_stream_index = undefined, - .huffman_tree = null, - }; - } - - /// Prepare the decoder to decode a compressed block. Loads the literals - /// stream and Huffman tree from `literals` and reads the FSE tables from - /// `source`. - /// - /// Errors: - /// - returns `error.BitStreamHasNoStartBit` if the (reversed) literal bitstream's - /// first byte does not have any bits set. - /// - returns `error.TreelessLiteralsFirst` `literals` is a treeless literals section - /// and the decode state does not have a Huffman tree from a previous block. - pub fn prepare( - self: *DecodeState, - source: anytype, - literals: LiteralsSection, - sequences_header: SequencesSection.Header, - ) !void { - self.literal_written_count = 0; - self.literal_header = literals.header; - self.literal_streams = literals.streams; - - if (literals.huffman_tree) |tree| { - self.huffman_tree = tree; - } else if (literals.header.block_type == .treeless and self.huffman_tree == null) { - return error.TreelessLiteralsFirst; - } - - switch (literals.header.block_type) { - .raw, .rle => {}, - .compressed, .treeless => { - self.literal_stream_index = 0; - switch (literals.streams) { - .one => |slice| try self.initLiteralStream(slice), - .four => |streams| try self.initLiteralStream(streams[0]), - } - }, - } - - if (sequences_header.sequence_count > 0) { - try self.updateFseTable(source, .literal, sequences_header.literal_lengths); - try self.updateFseTable(source, .offset, sequences_header.offsets); - try self.updateFseTable(source, .match, sequences_header.match_lengths); - self.fse_tables_undefined = false; - } - } - - /// Read initial FSE states for sequence decoding. Returns `error.EndOfStream` - /// if `bit_reader` does not contain enough bits. - pub fn readInitialFseState(self: *DecodeState, bit_reader: *ReverseBitReader) error{EndOfStream}!void { - self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); - self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); - self.match.state = try bit_reader.readBitsNoEof(u9, self.match.accuracy_log); - } - - fn updateRepeatOffset(self: *DecodeState, offset: u32) void { - std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[1]); - std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[2]); - self.repeat_offsets[0] = offset; - } - - fn useRepeatOffset(self: *DecodeState, index: usize) u32 { - if (index == 1) - std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[1]) - else if (index == 2) { - std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[2]); - std.mem.swap(u32, &self.repeat_offsets[1], &self.repeat_offsets[2]); - } - return self.repeat_offsets[0]; - } - - const DataType = enum { offset, match, literal }; - - fn updateState( - self: *DecodeState, - comptime choice: DataType, - bit_reader: *ReverseBitReader, - ) error{ MalformedFseBits, EndOfStream }!void { - switch (@field(self, @tagName(choice)).table) { - .rle => {}, - .fse => |table| { - const data = table[@field(self, @tagName(choice)).state]; - const T = @TypeOf(@field(self, @tagName(choice))).State; - const bits_summand = try bit_reader.readBitsNoEof(T, data.bits); - const next_state = std.math.cast( - @TypeOf(@field(self, @tagName(choice))).State, - data.baseline + bits_summand, - ) orelse return error.MalformedFseBits; - @field(self, @tagName(choice)).state = next_state; - }, - } - } - - const FseTableError = error{ - MalformedFseTable, - MalformedAccuracyLog, - RepeatModeFirst, - EndOfStream, - }; - - fn updateFseTable( - self: *DecodeState, - source: anytype, - comptime choice: DataType, - mode: SequencesSection.Header.Mode, - ) !void { - const field_name = @tagName(choice); - switch (mode) { - .predefined => { - @field(self, field_name).accuracy_log = - @field(types.compressed_block.default_accuracy_log, field_name); - - @field(self, field_name).table = - @field(types.compressed_block, "predefined_" ++ field_name ++ "_fse_table"); - }, - .rle => { - @field(self, field_name).accuracy_log = 0; - @field(self, field_name).table = .{ .rle = try source.readByte() }; - }, - .fse => { - var bit_reader = bitReader(source); - - const table_size = try decodeFseTable( - &bit_reader, - @field(types.compressed_block.table_symbol_count_max, field_name), - @field(types.compressed_block.table_accuracy_log_max, field_name), - @field(self, field_name ++ "_fse_buffer"), - ); - @field(self, field_name).table = .{ - .fse = @field(self, field_name ++ "_fse_buffer")[0..table_size], - }; - @field(self, field_name).accuracy_log = std.math.log2_int_ceil(usize, table_size); - }, - .repeat => if (self.fse_tables_undefined) return error.RepeatModeFirst, - } - } - - const Sequence = struct { - literal_length: u32, - match_length: u32, - offset: u32, - }; - - fn nextSequence( - self: *DecodeState, - bit_reader: *ReverseBitReader, - ) error{ OffsetCodeTooLarge, EndOfStream }!Sequence { - const raw_code = self.getCode(.offset); - const offset_code = std.math.cast(u5, raw_code) orelse { - return error.OffsetCodeTooLarge; - }; - const offset_value = (@as(u32, 1) << offset_code) + try bit_reader.readBitsNoEof(u32, offset_code); - - const match_code = self.getCode(.match); - const match = types.compressed_block.match_length_code_table[match_code]; - const match_length = match[0] + try bit_reader.readBitsNoEof(u32, match[1]); - - const literal_code = self.getCode(.literal); - const literal = types.compressed_block.literals_length_code_table[literal_code]; - const literal_length = literal[0] + try bit_reader.readBitsNoEof(u32, literal[1]); - - const offset = if (offset_value > 3) offset: { - const offset = offset_value - 3; - self.updateRepeatOffset(offset); - break :offset offset; - } else offset: { - if (literal_length == 0) { - if (offset_value == 3) { - const offset = self.repeat_offsets[0] - 1; - self.updateRepeatOffset(offset); - break :offset offset; - } - break :offset self.useRepeatOffset(offset_value); - } - break :offset self.useRepeatOffset(offset_value - 1); - }; - - return .{ - .literal_length = literal_length, - .match_length = match_length, - .offset = offset, - }; - } - - fn executeSequenceSlice( - self: *DecodeState, - dest: []u8, - write_pos: usize, - sequence: Sequence, - ) (error{MalformedSequence} || DecodeLiteralsError)!void { - if (sequence.offset > write_pos + sequence.literal_length) return error.MalformedSequence; - - try self.decodeLiteralsSlice(dest[write_pos..], sequence.literal_length); - const copy_start = write_pos + sequence.literal_length - sequence.offset; - const copy_end = copy_start + sequence.match_length; - // NOTE: we ignore the usage message for std.mem.copy and copy with dest.ptr >= src.ptr - // to allow repeats - std.mem.copy(u8, dest[write_pos + sequence.literal_length ..], dest[copy_start..copy_end]); - } - - fn executeSequenceRingBuffer( - self: *DecodeState, - dest: *RingBuffer, - sequence: Sequence, - ) (error{MalformedSequence} || DecodeLiteralsError)!void { - if (sequence.offset > dest.data.len) return error.MalformedSequence; - - try self.decodeLiteralsRingBuffer(dest, sequence.literal_length); - const copy_start = dest.write_index + dest.data.len - sequence.offset; - const copy_slice = dest.sliceAt(copy_start, sequence.match_length); - // TODO: would std.mem.copy and figuring out dest slice be better/faster? - for (copy_slice.first) |b| dest.writeAssumeCapacity(b); - for (copy_slice.second) |b| dest.writeAssumeCapacity(b); - } - - const DecodeSequenceError = error{ - OffsetCodeTooLarge, - EndOfStream, - MalformedSequence, - MalformedFseBits, - } || DecodeLiteralsError; - - /// Decode one sequence from `bit_reader` into `dest`, written starting at - /// `write_pos` and update FSE states if `last_sequence` is `false`. Returns - /// `error.MalformedSequence` error if the decompressed sequence would be longer - /// than `sequence_size_limit` or the sequence's offset is too large; returns - /// `error.EndOfStream` if `bit_reader` does not contain enough bits; returns - /// `error.UnexpectedEndOfLiteralStream` if the decoder state's literal streams - /// do not contain enough literals for the sequence (this may mean the literal - /// stream or the sequence is malformed). - pub fn decodeSequenceSlice( - self: *DecodeState, - dest: []u8, - write_pos: usize, - bit_reader: *ReverseBitReader, - sequence_size_limit: usize, - last_sequence: bool, - ) DecodeSequenceError!usize { - const sequence = try self.nextSequence(bit_reader); - const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; - if (sequence_length > sequence_size_limit) return error.MalformedSequence; - - try self.executeSequenceSlice(dest, write_pos, sequence); - if (!last_sequence) { - try self.updateState(.literal, bit_reader); - try self.updateState(.match, bit_reader); - try self.updateState(.offset, bit_reader); - } - return sequence_length; - } - - /// Decode one sequence from `bit_reader` into `dest`; see `decodeSequenceSlice`. - pub fn decodeSequenceRingBuffer( - self: *DecodeState, - dest: *RingBuffer, - bit_reader: anytype, - sequence_size_limit: usize, - last_sequence: bool, - ) DecodeSequenceError!usize { - const sequence = try self.nextSequence(bit_reader); - const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; - if (sequence_length > sequence_size_limit) return error.MalformedSequence; - - try self.executeSequenceRingBuffer(dest, sequence); - if (!last_sequence) { - try self.updateState(.literal, bit_reader); - try self.updateState(.match, bit_reader); - try self.updateState(.offset, bit_reader); - } - return sequence_length; - } - - fn nextLiteralMultiStream( - self: *DecodeState, - ) error{BitStreamHasNoStartBit}!void { - self.literal_stream_index += 1; - try self.initLiteralStream(self.literal_streams.four[self.literal_stream_index]); - } - - pub fn initLiteralStream(self: *DecodeState, bytes: []const u8) error{BitStreamHasNoStartBit}!void { - try self.literal_stream_reader.init(bytes); - } - - const LiteralBitsError = error{ - BitStreamHasNoStartBit, - UnexpectedEndOfLiteralStream, - }; - fn readLiteralsBits( - self: *DecodeState, - comptime T: type, - bit_count_to_read: usize, - ) LiteralBitsError!T { - return self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch bits: { - if (self.literal_streams == .four and self.literal_stream_index < 3) { - try self.nextLiteralMultiStream(); - break :bits self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch - return error.UnexpectedEndOfLiteralStream; - } else { - return error.UnexpectedEndOfLiteralStream; - } - }; - } - - const DecodeLiteralsError = error{ - MalformedLiteralsLength, - PrefixNotFound, - } || LiteralBitsError; - - /// Decode `len` bytes of literals into `dest`. `literals` should be the - /// `LiteralsSection` that was passed to `prepare()`. Returns - /// `error.MalformedLiteralsLength` if the number of literal bytes decoded by - /// `self` plus `len` is greater than the regenerated size of `literals`. - /// Returns `error.UnexpectedEndOfLiteralStream` and `error.PrefixNotFound` if - /// there are problems decoding Huffman compressed literals. - pub fn decodeLiteralsSlice( - self: *DecodeState, - dest: []u8, - len: usize, - ) DecodeLiteralsError!void { - if (self.literal_written_count + len > self.literal_header.regenerated_size) - return error.MalformedLiteralsLength; - - switch (self.literal_header.block_type) { - .raw => { - const literals_end = self.literal_written_count + len; - const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; - std.mem.copy(u8, dest, literal_data); - self.literal_written_count += len; - }, - .rle => { - var i: usize = 0; - while (i < len) : (i += 1) { - dest[i] = self.literal_streams.one[0]; - } - self.literal_written_count += len; - }, - .compressed, .treeless => { - // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; - const huffman_tree = self.huffman_tree orelse unreachable; - const max_bit_count = huffman_tree.max_bit_count; - const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount( - huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, - max_bit_count, - ); - var bits_read: u4 = 0; - var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; - var bit_count_to_read: u4 = starting_bit_count; - var i: usize = 0; - while (i < len) : (i += 1) { - var prefix: u16 = 0; - while (true) { - const new_bits = self.readLiteralsBits(u16, bit_count_to_read) catch |err| { - return err; - }; - prefix <<= bit_count_to_read; - prefix |= new_bits; - bits_read += bit_count_to_read; - const result = huffman_tree.query(huffman_tree_index, prefix) catch |err| { - return err; - }; - - switch (result) { - .symbol => |sym| { - dest[i] = sym; - bit_count_to_read = starting_bit_count; - bits_read = 0; - huffman_tree_index = huffman_tree.symbol_count_minus_one; - break; - }, - .index => |index| { - huffman_tree_index = index; - const bit_count = LiteralsSection.HuffmanTree.weightToBitCount( - huffman_tree.nodes[index].weight, - max_bit_count, - ); - bit_count_to_read = bit_count - bits_read; - }, - } - } - } - self.literal_written_count += len; - }, - } - } - - /// Decode literals into `dest`; see `decodeLiteralsSlice()`. - pub fn decodeLiteralsRingBuffer( - self: *DecodeState, - dest: *RingBuffer, - len: usize, - ) DecodeLiteralsError!void { - if (self.literal_written_count + len > self.literal_header.regenerated_size) - return error.MalformedLiteralsLength; - - switch (self.literal_header.block_type) { - .raw => { - const literals_end = self.literal_written_count + len; - const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; - dest.writeSliceAssumeCapacity(literal_data); - self.literal_written_count += len; - }, - .rle => { - var i: usize = 0; - while (i < len) : (i += 1) { - dest.writeAssumeCapacity(self.literal_streams.one[0]); - } - self.literal_written_count += len; - }, - .compressed, .treeless => { - // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; - const huffman_tree = self.huffman_tree orelse unreachable; - const max_bit_count = huffman_tree.max_bit_count; - const starting_bit_count = LiteralsSection.HuffmanTree.weightToBitCount( - huffman_tree.nodes[huffman_tree.symbol_count_minus_one].weight, - max_bit_count, - ); - var bits_read: u4 = 0; - var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; - var bit_count_to_read: u4 = starting_bit_count; - var i: usize = 0; - while (i < len) : (i += 1) { - var prefix: u16 = 0; - while (true) { - const new_bits = try self.readLiteralsBits(u16, bit_count_to_read); - prefix <<= bit_count_to_read; - prefix |= new_bits; - bits_read += bit_count_to_read; - const result = try huffman_tree.query(huffman_tree_index, prefix); - - switch (result) { - .symbol => |sym| { - dest.writeAssumeCapacity(sym); - bit_count_to_read = starting_bit_count; - bits_read = 0; - huffman_tree_index = huffman_tree.symbol_count_minus_one; - break; - }, - .index => |index| { - huffman_tree_index = index; - const bit_count = LiteralsSection.HuffmanTree.weightToBitCount( - huffman_tree.nodes[index].weight, - max_bit_count, - ); - bit_count_to_read = bit_count - bits_read; - }, - } - } - } - self.literal_written_count += len; - }, - } - } - - fn getCode(self: *DecodeState, comptime choice: DataType) u32 { - return switch (@field(self, @tagName(choice)).table) { - .rle => |value| value, - .fse => |table| table[@field(self, @tagName(choice)).state].symbol, - }; - } -}; - pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { const hash = hasher.final(); return @intCast(u32, hash & 0xFFFFFFFF); @@ -589,7 +74,7 @@ pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { const FrameError = error{ DictionaryIdFlagUnsupported, ChecksumFailure, -} || InvalidBit || DecodeBlockError; +} || InvalidBit || block.Error; /// Decode a Zstandard frame from `src` into `dest`, returning the number of /// bytes read from `src` and written to `dest`; if the frame does not declare @@ -695,15 +180,15 @@ pub fn decodeZStandardFrameAlloc( var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; - var block_header = try decodeBlockHeaderSlice(src[consumed_count..]); + var block_header = try block.decodeBlockHeaderSlice(src[consumed_count..]); consumed_count += 3; - var decode_state = DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); + var decode_state = block.DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); while (true) : ({ - block_header = try decodeBlockHeaderSlice(src[consumed_count..]); + block_header = try block.decodeBlockHeaderSlice(src[consumed_count..]); consumed_count += 3; }) { if (block_header.block_size > frame_context.block_size_max) return error.BlockSizeOverMaximum; - const written_size = try decodeBlockRingBuffer( + const written_size = try block.decodeBlockRingBuffer( &ring_buffer, src[consumed_count..], block_header, @@ -731,37 +216,28 @@ pub fn decodeZStandardFrameAlloc( return result.toOwnedSlice(); } -const DecodeBlockError = error{ - BlockSizeOverMaximum, - MalformedBlockSize, - ReservedBlock, - MalformedRleBlock, - MalformedCompressedBlock, - EndOfStream, -}; - /// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. -pub fn decodeFrameBlocks( +fn decodeFrameBlocks( dest: []u8, src: []const u8, consumed_count: *usize, hash: ?*std.hash.XxHash64, -) DecodeBlockError!usize { +) block.Error!usize { // These tables take 7680 bytes var literal_fse_data: [types.compressed_block.table_size_max.literal]Table.Fse = undefined; var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; - var block_header = try decodeBlockHeaderSlice(src); + var block_header = try block.decodeBlockHeaderSlice(src); var bytes_read: usize = 3; defer consumed_count.* += bytes_read; - var decode_state = DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); + var decode_state = block.DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); var written_count: usize = 0; while (true) : ({ - block_header = try decodeBlockHeaderSlice(src[bytes_read..]); + block_header = try block.decodeBlockHeaderSlice(src[bytes_read..]); bytes_read += 3; }) { - const written_size = try decodeBlock( + const written_size = try block.decodeBlock( dest, src[bytes_read..], block_header, @@ -776,255 +252,6 @@ pub fn decodeFrameBlocks( return written_count; } -/// Decode a single block from `src` into `dest`. The beginning of `src` should -/// be the start of the block content (i.e. directly after the block header). -/// Increments `consumed_count` by the number of bytes read from `src` to decode -/// the block and returns the decompressed size of the block. -pub fn decodeBlock( - dest: []u8, - src: []const u8, - block_header: frame.ZStandard.Block.Header, - decode_state: *DecodeState, - consumed_count: *usize, - written_count: usize, -) DecodeBlockError!usize { - const block_size_max = @min(1 << 17, dest[written_count..].len); // 128KiB - const block_size = block_header.block_size; - if (block_size_max < block_size) return error.BlockSizeOverMaximum; - switch (block_header.block_type) { - .raw => { - if (src.len < block_size) return error.MalformedBlockSize; - const data = src[0..block_size]; - std.mem.copy(u8, dest[written_count..], data); - consumed_count.* += block_size; - return block_size; - }, - .rle => { - if (src.len < 1) return error.MalformedRleBlock; - var write_pos: usize = written_count; - while (write_pos < block_size + written_count) : (write_pos += 1) { - dest[write_pos] = src[0]; - } - consumed_count.* += 1; - return block_size; - }, - .compressed => { - if (src.len < block_size) return error.MalformedBlockSize; - var bytes_read: usize = 0; - const literals = decodeLiteralsSectionSlice(src, &bytes_read) catch - return error.MalformedCompressedBlock; - var fbs = std.io.fixedBufferStream(src[bytes_read..]); - const fbs_reader = fbs.reader(); - const sequences_header = decodeSequencesHeader(fbs_reader) catch - return error.MalformedCompressedBlock; - - decode_state.prepare(fbs_reader, literals, sequences_header) catch - return error.MalformedCompressedBlock; - - bytes_read += fbs.pos; - - var bytes_written: usize = 0; - if (sequences_header.sequence_count > 0) { - const bit_stream_bytes = src[bytes_read..block_size]; - var bit_stream: ReverseBitReader = undefined; - bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; - - decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; - - var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { - const write_pos = written_count + bytes_written; - const decompressed_size = decode_state.decodeSequenceSlice( - dest, - write_pos, - &bit_stream, - sequence_size_limit, - i == sequences_header.sequence_count - 1, - ) catch return error.MalformedCompressedBlock; - bytes_written += decompressed_size; - sequence_size_limit -= decompressed_size; - } - - bytes_read += bit_stream_bytes.len; - } - if (bytes_read != block_size) return error.MalformedCompressedBlock; - - if (decode_state.literal_written_count < literals.header.regenerated_size) { - const len = literals.header.regenerated_size - decode_state.literal_written_count; - decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], len) catch - return error.MalformedCompressedBlock; - bytes_written += len; - } - - consumed_count.* += bytes_read; - return bytes_written; - }, - .reserved => return error.ReservedBlock, - } -} - -/// Decode a single block from `src` into `dest`; see `decodeBlock()`. Returns -/// the size of the decompressed block, which can be used with `dest.sliceLast()` -/// to get the decompressed bytes. -pub fn decodeBlockRingBuffer( - dest: *RingBuffer, - src: []const u8, - block_header: frame.ZStandard.Block.Header, - decode_state: *DecodeState, - consumed_count: *usize, - block_size_max: usize, -) DecodeBlockError!usize { - const block_size = block_header.block_size; - if (block_size_max < block_size) return error.BlockSizeOverMaximum; - switch (block_header.block_type) { - .raw => { - if (src.len < block_size) return error.MalformedBlockSize; - const data = src[0..block_size]; - dest.writeSliceAssumeCapacity(data); - consumed_count.* += block_size; - return block_size; - }, - .rle => { - if (src.len < 1) return error.MalformedRleBlock; - var write_pos: usize = 0; - while (write_pos < block_size) : (write_pos += 1) { - dest.writeAssumeCapacity(src[0]); - } - consumed_count.* += 1; - return block_size; - }, - .compressed => { - if (src.len < block_size) return error.MalformedBlockSize; - var bytes_read: usize = 0; - const literals = decodeLiteralsSectionSlice(src, &bytes_read) catch - return error.MalformedCompressedBlock; - var fbs = std.io.fixedBufferStream(src[bytes_read..]); - const fbs_reader = fbs.reader(); - const sequences_header = decodeSequencesHeader(fbs_reader) catch - return error.MalformedCompressedBlock; - - decode_state.prepare(fbs_reader, literals, sequences_header) catch - return error.MalformedCompressedBlock; - - bytes_read += fbs.pos; - - var bytes_written: usize = 0; - if (sequences_header.sequence_count > 0) { - const bit_stream_bytes = src[bytes_read..block_size]; - var bit_stream: ReverseBitReader = undefined; - bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; - - decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; - - var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { - const decompressed_size = decode_state.decodeSequenceRingBuffer( - dest, - &bit_stream, - sequence_size_limit, - i == sequences_header.sequence_count - 1, - ) catch return error.MalformedCompressedBlock; - bytes_written += decompressed_size; - sequence_size_limit -= decompressed_size; - } - - bytes_read += bit_stream_bytes.len; - } - if (bytes_read != block_size) return error.MalformedCompressedBlock; - - if (decode_state.literal_written_count < literals.header.regenerated_size) { - const len = literals.header.regenerated_size - decode_state.literal_written_count; - decode_state.decodeLiteralsRingBuffer(dest, len) catch - return error.MalformedCompressedBlock; - bytes_written += len; - } - - consumed_count.* += bytes_read; - if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; - return bytes_written; - }, - .reserved => return error.ReservedBlock, - } -} - -/// Decode a single block from `source` into `dest`. Literal and sequence data -/// from the block is copied into `literals_buffer` and `sequence_buffer`, which -/// must be large enough or `error.LiteralsBufferTooSmall` and -/// `error.SequenceBufferTooSmall` are returned (the maximum block size is an -/// upper bound for the size of both buffers). See `decodeBlock` -/// and `decodeBlockRingBuffer` for function that can decode a block without -/// these extra copies. -pub fn decodeBlockReader( - dest: *RingBuffer, - source: anytype, - block_header: frame.ZStandard.Block.Header, - decode_state: *DecodeState, - block_size_max: usize, - literals_buffer: []u8, - sequence_buffer: []u8, -) !void { - const block_size = block_header.block_size; - var block_reader_limited = std.io.limitedReader(source, block_size); - const block_reader = block_reader_limited.reader(); - if (block_size_max < block_size) return error.BlockSizeOverMaximum; - switch (block_header.block_type) { - .raw => { - const slice = dest.sliceAt(dest.write_index, block_size); - try source.readNoEof(slice.first); - try source.readNoEof(slice.second); - dest.write_index = dest.mask2(dest.write_index + block_size); - }, - .rle => { - const byte = try source.readByte(); - var i: usize = 0; - while (i < block_size) : (i += 1) { - dest.writeAssumeCapacity(byte); - } - }, - .compressed => { - const literals = try decodeLiteralsSection(block_reader, literals_buffer); - const sequences_header = try decodeSequencesHeader(block_reader); - - try decode_state.prepare(block_reader, literals, sequences_header); - - if (sequences_header.sequence_count > 0) { - if (sequence_buffer.len < block_reader_limited.bytes_left) - return error.SequenceBufferTooSmall; - - const size = try block_reader.readAll(sequence_buffer); - var bit_stream: ReverseBitReader = undefined; - try bit_stream.init(sequence_buffer[0..size]); - - decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; - - var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { - const decompressed_size = decode_state.decodeSequenceRingBuffer( - dest, - &bit_stream, - sequence_size_limit, - i == sequences_header.sequence_count - 1, - ) catch return error.MalformedCompressedBlock; - sequence_size_limit -= decompressed_size; - } - } - - if (decode_state.literal_written_count < literals.header.regenerated_size) { - const len = literals.header.regenerated_size - decode_state.literal_written_count; - decode_state.decodeLiteralsRingBuffer(dest, len) catch - return error.MalformedCompressedBlock; - } - - decode_state.literal_written_count = 0; - assert(block_reader.readByte() == error.EndOfStream); - }, - .reserved => return error.ReservedBlock, - } -} - /// Decode the header of a skippable frame. pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { const magic = readInt(u32, src[0..4]); @@ -1090,673 +317,6 @@ pub fn decodeZStandardHeader(source: anytype) (error{EndOfStream} || InvalidBit) return header; } -/// Decode the header of a block. -pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { - const last_block = src[0] & 1 == 1; - const block_type = @intToEnum(frame.ZStandard.Block.Type, (src[0] & 0b110) >> 1); - const block_size = ((src[0] & 0b11111000) >> 3) + (@as(u21, src[1]) << 5) + (@as(u21, src[2]) << 13); - return .{ - .last_block = last_block, - .block_type = block_type, - .block_size = block_size, - }; -} - -pub fn decodeBlockHeaderSlice(src: []const u8) error{EndOfStream}!frame.ZStandard.Block.Header { - if (src.len < 3) return error.EndOfStream; - return decodeBlockHeader(src[0..3]); -} - -/// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the -/// number of bytes the section uses. -/// -/// Errors: -/// - returns `error.MalformedLiteralsHeader` if the header is invalid -/// - returns `error.MalformedLiteralsSection` if there are errors decoding -pub fn decodeLiteralsSectionSlice( - src: []const u8, - consumed_count: *usize, -) (error{ MalformedLiteralsHeader, MalformedLiteralsSection, EndOfStream } || DecodeHuffmanError)!LiteralsSection { - var bytes_read: usize = 0; - const header = header: { - var fbs = std.io.fixedBufferStream(src); - defer bytes_read = fbs.pos; - break :header decodeLiteralsHeader(fbs.reader()) catch return error.MalformedLiteralsHeader; - }; - switch (header.block_type) { - .raw => { - if (src.len < bytes_read + header.regenerated_size) return error.MalformedLiteralsSection; - const stream = src[bytes_read .. bytes_read + header.regenerated_size]; - consumed_count.* += header.regenerated_size + bytes_read; - return LiteralsSection{ - .header = header, - .huffman_tree = null, - .streams = .{ .one = stream }, - }; - }, - .rle => { - if (src.len < bytes_read + 1) return error.MalformedLiteralsSection; - const stream = src[bytes_read .. bytes_read + 1]; - consumed_count.* += 1 + bytes_read; - return LiteralsSection{ - .header = header, - .huffman_tree = null, - .streams = .{ .one = stream }, - }; - }, - .compressed, .treeless => { - const huffman_tree_start = bytes_read; - const huffman_tree = if (header.block_type == .compressed) - try decodeHuffmanTreeSlice(src[bytes_read..], &bytes_read) - else - null; - const huffman_tree_size = bytes_read - huffman_tree_start; - const total_streams_size = @as(usize, header.compressed_size.?) - huffman_tree_size; - - if (src.len < bytes_read + total_streams_size) return error.MalformedLiteralsSection; - const stream_data = src[bytes_read .. bytes_read + total_streams_size]; - - const streams = try decodeStreams(header.size_format, stream_data); - consumed_count.* += bytes_read + total_streams_size; - return LiteralsSection{ - .header = header, - .huffman_tree = huffman_tree, - .streams = streams, - }; - }, - } -} - -/// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the -/// number of bytes the section uses. -/// -/// Errors: -/// - returns `error.MalformedLiteralsHeader` if the header is invalid -/// - returns `error.MalformedLiteralsSection` if there are errors decoding -pub fn decodeLiteralsSection( - source: anytype, - buffer: []u8, -) !LiteralsSection { - const header = try decodeLiteralsHeader(source); - switch (header.block_type) { - .raw => { - try source.readNoEof(buffer[0..header.regenerated_size]); - return LiteralsSection{ - .header = header, - .huffman_tree = null, - .streams = .{ .one = buffer }, - }; - }, - .rle => { - buffer[0] = try source.readByte(); - return LiteralsSection{ - .header = header, - .huffman_tree = null, - .streams = .{ .one = buffer[0..1] }, - }; - }, - .compressed, .treeless => { - var counting_reader = std.io.countingReader(source); - const huffman_tree = if (header.block_type == .compressed) - try decodeHuffmanTree(counting_reader.reader(), buffer) - else - null; - const huffman_tree_size = counting_reader.bytes_read; - const total_streams_size = @as(usize, header.compressed_size.?) - @intCast(usize, huffman_tree_size); - - if (total_streams_size > buffer.len) return error.LiteralsBufferTooSmall; - try source.readNoEof(buffer[0..total_streams_size]); - const stream_data = buffer[0..total_streams_size]; - - const streams = try decodeStreams(header.size_format, stream_data); - return LiteralsSection{ - .header = header, - .huffman_tree = huffman_tree, - .streams = streams, - }; - }, - } -} - -fn decodeStreams(size_format: u2, stream_data: []const u8) !LiteralsSection.Streams { - if (size_format == 0) { - return .{ .one = stream_data }; - } - - if (stream_data.len < 6) return error.MalformedLiteralsSection; - - const stream_1_length = @as(usize, readInt(u16, stream_data[0..2])); - const stream_2_length = @as(usize, readInt(u16, stream_data[2..4])); - const stream_3_length = @as(usize, readInt(u16, stream_data[4..6])); - - const stream_1_start = 6; - const stream_2_start = stream_1_start + stream_1_length; - const stream_3_start = stream_2_start + stream_2_length; - const stream_4_start = stream_3_start + stream_3_length; - - return .{ .four = .{ - stream_data[stream_1_start .. stream_1_start + stream_1_length], - stream_data[stream_2_start .. stream_2_start + stream_2_length], - stream_data[stream_3_start .. stream_3_start + stream_3_length], - stream_data[stream_4_start..], - } }; -} - -const DecodeHuffmanError = error{ - MalformedHuffmanTree, - MalformedFseTable, - MalformedAccuracyLog, -}; - -fn decodeFseHuffmanTree(source: anytype, compressed_size: usize, buffer: []u8, weights: *[256]u4) !usize { - var stream = std.io.limitedReader(source, compressed_size); - var bit_reader = bitReader(stream.reader()); - - var entries: [1 << 6]Table.Fse = undefined; - const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { - error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, - error.EndOfStream => return error.MalformedFseTable, - }; - const accuracy_log = std.math.log2_int_ceil(usize, table_size); - - const amount = try stream.reader().readAll(buffer); - var huff_bits: ReverseBitReader = undefined; - huff_bits.init(buffer[0..amount]) catch return error.MalformedHuffmanTree; - - return assignWeights(&huff_bits, accuracy_log, &entries, weights); -} - -fn decodeFseHuffmanTreeSlice(src: []const u8, compressed_size: usize, weights: *[256]u4) !usize { - if (src.len < compressed_size) return error.MalformedHuffmanTree; - var stream = std.io.fixedBufferStream(src[0..compressed_size]); - var counting_reader = std.io.countingReader(stream.reader()); - var bit_reader = bitReader(counting_reader.reader()); - - var entries: [1 << 6]Table.Fse = undefined; - const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { - error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, - error.EndOfStream => return error.MalformedFseTable, - }; - const accuracy_log = std.math.log2_int_ceil(usize, table_size); - - const start_index = std.math.cast(usize, counting_reader.bytes_read) orelse return error.MalformedHuffmanTree; - var huff_data = src[start_index..compressed_size]; - var huff_bits: ReverseBitReader = undefined; - huff_bits.init(huff_data) catch return error.MalformedHuffmanTree; - - return assignWeights(&huff_bits, accuracy_log, &entries, weights); -} - -fn assignWeights(huff_bits: *ReverseBitReader, accuracy_log: usize, entries: *[1 << 6]Table.Fse, weights: *[256]u4) !usize { - var i: usize = 0; - var even_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; - var odd_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; - - while (i < 255) { - const even_data = entries[even_state]; - var read_bits: usize = 0; - const even_bits = huff_bits.readBits(u32, even_data.bits, &read_bits) catch unreachable; - weights[i] = std.math.cast(u4, even_data.symbol) orelse return error.MalformedHuffmanTree; - i += 1; - if (read_bits < even_data.bits) { - weights[i] = std.math.cast(u4, entries[odd_state].symbol) orelse return error.MalformedHuffmanTree; - i += 1; - break; - } - even_state = even_data.baseline + even_bits; - - read_bits = 0; - const odd_data = entries[odd_state]; - const odd_bits = huff_bits.readBits(u32, odd_data.bits, &read_bits) catch unreachable; - weights[i] = std.math.cast(u4, odd_data.symbol) orelse return error.MalformedHuffmanTree; - i += 1; - if (read_bits < odd_data.bits) { - if (i == 256) return error.MalformedHuffmanTree; - weights[i] = std.math.cast(u4, entries[even_state].symbol) orelse return error.MalformedHuffmanTree; - i += 1; - break; - } - odd_state = odd_data.baseline + odd_bits; - } else return error.MalformedHuffmanTree; - - return i + 1; // stream contains all but the last symbol -} - -fn decodeDirectHuffmanTree(source: anytype, encoded_symbol_count: usize, weights: *[256]u4) !usize { - const weights_byte_count = (encoded_symbol_count + 1) / 2; - var i: usize = 0; - while (i < weights_byte_count) : (i += 1) { - const byte = try source.readByte(); - weights[2 * i] = @intCast(u4, byte >> 4); - weights[2 * i + 1] = @intCast(u4, byte & 0xF); - } - return encoded_symbol_count + 1; -} - -fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.PrefixedSymbol, weights: [256]u4) usize { - for (weight_sorted_prefixed_symbols) |_, i| { - weight_sorted_prefixed_symbols[i] = .{ - .symbol = @intCast(u8, i), - .weight = undefined, - .prefix = undefined, - }; - } - - std.sort.sort( - LiteralsSection.HuffmanTree.PrefixedSymbol, - weight_sorted_prefixed_symbols, - weights, - lessThanByWeight, - ); - - var prefix: u16 = 0; - var prefixed_symbol_count: usize = 0; - var sorted_index: usize = 0; - const symbol_count = weight_sorted_prefixed_symbols.len; - while (sorted_index < symbol_count) { - var symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; - const weight = weights[symbol]; - if (weight == 0) { - sorted_index += 1; - continue; - } - - while (sorted_index < symbol_count) : ({ - sorted_index += 1; - prefixed_symbol_count += 1; - prefix += 1; - }) { - symbol = weight_sorted_prefixed_symbols[sorted_index].symbol; - if (weights[symbol] != weight) { - prefix = ((prefix - 1) >> (weights[symbol] - weight)) + 1; - break; - } - weight_sorted_prefixed_symbols[prefixed_symbol_count].symbol = symbol; - weight_sorted_prefixed_symbols[prefixed_symbol_count].prefix = prefix; - weight_sorted_prefixed_symbols[prefixed_symbol_count].weight = weight; - } - } - return prefixed_symbol_count; -} - -fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) LiteralsSection.HuffmanTree { - var weight_power_sum: u16 = 0; - for (weights[0 .. symbol_count - 1]) |value| { - if (value > 0) { - weight_power_sum += @as(u16, 1) << (value - 1); - } - } - - // advance to next power of two (even if weight_power_sum is a power of 2) - const max_number_of_bits = std.math.log2_int(u16, weight_power_sum) + 1; - const next_power_of_two = @as(u16, 1) << max_number_of_bits; - weights[symbol_count - 1] = std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1; - - var weight_sorted_prefixed_symbols: [256]LiteralsSection.HuffmanTree.PrefixedSymbol = undefined; - const prefixed_symbol_count = assignSymbols(weight_sorted_prefixed_symbols[0..symbol_count], weights.*); - const tree = LiteralsSection.HuffmanTree{ - .max_bit_count = max_number_of_bits, - .symbol_count_minus_one = @intCast(u8, prefixed_symbol_count - 1), - .nodes = weight_sorted_prefixed_symbols, - }; - return tree; -} - -fn decodeHuffmanTree(source: anytype, buffer: []u8) !LiteralsSection.HuffmanTree { - const header = try source.readByte(); - var weights: [256]u4 = undefined; - const symbol_count = if (header < 128) - // FSE compressed weights - try decodeFseHuffmanTree(source, header, buffer, &weights) - else - try decodeDirectHuffmanTree(source, header - 127, &weights); - - return buildHuffmanTree(&weights, symbol_count); -} - -fn decodeHuffmanTreeSlice(src: []const u8, consumed_count: *usize) (error{EndOfStream} || DecodeHuffmanError)!LiteralsSection.HuffmanTree { - if (src.len == 0) return error.MalformedHuffmanTree; - const header = src[0]; - var bytes_read: usize = 1; - var weights: [256]u4 = undefined; - const symbol_count = if (header < 128) count: { - // FSE compressed weights - bytes_read += header; - break :count try decodeFseHuffmanTreeSlice(src[1..], header, &weights); - } else count: { - var fbs = std.io.fixedBufferStream(src[1..]); - defer bytes_read += fbs.pos; - break :count try decodeDirectHuffmanTree(fbs.reader(), header - 127, &weights); - }; - - consumed_count.* += bytes_read; - return buildHuffmanTree(&weights, symbol_count); -} - -fn lessThanByWeight( - weights: [256]u4, - lhs: LiteralsSection.HuffmanTree.PrefixedSymbol, - rhs: LiteralsSection.HuffmanTree.PrefixedSymbol, -) bool { - // NOTE: this function relies on the use of a stable sorting algorithm, - // otherwise a special case of if (weights[lhs] == weights[rhs]) return lhs < rhs; - // should be added - return weights[lhs.symbol] < weights[rhs.symbol]; -} - -/// Decode a literals section header. -pub fn decodeLiteralsHeader(source: anytype) !LiteralsSection.Header { - const byte0 = try source.readByte(); - const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); - const size_format = @intCast(u2, (byte0 & 0b1100) >> 2); - var regenerated_size: u20 = undefined; - var compressed_size: ?u18 = null; - switch (block_type) { - .raw, .rle => { - switch (size_format) { - 0, 2 => { - regenerated_size = byte0 >> 3; - }, - 1 => regenerated_size = (byte0 >> 4) + (@as(u20, try source.readByte()) << 4), - 3 => regenerated_size = (byte0 >> 4) + - (@as(u20, try source.readByte()) << 4) + - (@as(u20, try source.readByte()) << 12), - } - }, - .compressed, .treeless => { - const byte1 = try source.readByte(); - const byte2 = try source.readByte(); - switch (size_format) { - 0, 1 => { - regenerated_size = (byte0 >> 4) + ((@as(u20, byte1) & 0b00111111) << 4); - compressed_size = ((byte1 & 0b11000000) >> 6) + (@as(u18, byte2) << 2); - }, - 2 => { - const byte3 = try source.readByte(); - regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00000011) << 12); - compressed_size = ((byte2 & 0b11111100) >> 2) + (@as(u18, byte3) << 6); - }, - 3 => { - const byte3 = try source.readByte(); - const byte4 = try source.readByte(); - regenerated_size = (byte0 >> 4) + (@as(u20, byte1) << 4) + ((@as(u20, byte2) & 0b00111111) << 12); - compressed_size = ((byte2 & 0b11000000) >> 6) + (@as(u18, byte3) << 2) + (@as(u18, byte4) << 10); - }, - } - }, - } - return LiteralsSection.Header{ - .block_type = block_type, - .size_format = size_format, - .regenerated_size = regenerated_size, - .compressed_size = compressed_size, - }; -} - -/// Decode a sequences section header. -/// -/// Errors: -/// - returns `error.ReservedBitSet` is the reserved bit is set -/// - returns `error.MalformedSequencesHeader` if the header is invalid -pub fn decodeSequencesHeader( - source: anytype, -) !SequencesSection.Header { - var sequence_count: u24 = undefined; - - const byte0 = try source.readByte(); - if (byte0 == 0) { - return SequencesSection.Header{ - .sequence_count = 0, - .offsets = undefined, - .match_lengths = undefined, - .literal_lengths = undefined, - }; - } else if (byte0 < 128) { - sequence_count = byte0; - } else if (byte0 < 255) { - sequence_count = (@as(u24, (byte0 - 128)) << 8) + try source.readByte(); - } else { - sequence_count = (try source.readByte()) + (@as(u24, try source.readByte()) << 8) + 0x7F00; - } - - const compression_modes = try source.readByte(); - - const matches_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00001100) >> 2); - const offsets_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b00110000) >> 4); - const literal_mode = @intToEnum(SequencesSection.Header.Mode, (compression_modes & 0b11000000) >> 6); - if (compression_modes & 0b11 != 0) return error.ReservedBitSet; - - return SequencesSection.Header{ - .sequence_count = sequence_count, - .offsets = offsets_mode, - .match_lengths = matches_mode, - .literal_lengths = literal_mode, - }; -} - -fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { - const total_probability = @intCast(u16, entries.len); - const accuracy_log = std.math.log2_int(u16, total_probability); - assert(total_probability <= 1 << 9); - - var less_than_one_count: usize = 0; - for (values) |value, i| { - if (value == 0) { - entries[entries.len - 1 - less_than_one_count] = Table.Fse{ - .symbol = @intCast(u8, i), - .baseline = 0, - .bits = accuracy_log, - }; - less_than_one_count += 1; - } - } - - var position: usize = 0; - var temp_states: [1 << 9]u16 = undefined; - for (values) |value, symbol| { - if (value == 0 or value == 1) continue; - const probability = value - 1; - - const state_share_dividend = std.math.ceilPowerOfTwo(u16, probability) catch - return error.MalformedFseTable; - const share_size = @divExact(total_probability, state_share_dividend); - const double_state_count = state_share_dividend - probability; - const single_state_count = probability - double_state_count; - const share_size_log = std.math.log2_int(u16, share_size); - - var i: u16 = 0; - while (i < probability) : (i += 1) { - temp_states[i] = @intCast(u16, position); - position += (entries.len >> 1) + (entries.len >> 3) + 3; - position &= entries.len - 1; - while (position >= entries.len - less_than_one_count) { - position += (entries.len >> 1) + (entries.len >> 3) + 3; - position &= entries.len - 1; - } - } - std.sort.sort(u16, temp_states[0..probability], {}, std.sort.asc(u16)); - i = 0; - while (i < probability) : (i += 1) { - entries[temp_states[i]] = if (i < double_state_count) Table.Fse{ - .symbol = @intCast(u8, symbol), - .bits = share_size_log + 1, - .baseline = single_state_count * share_size + i * 2 * share_size, - } else Table.Fse{ - .symbol = @intCast(u8, symbol), - .bits = share_size_log, - .baseline = (i - double_state_count) * share_size, - }; - } - } -} - -fn decodeFseTable( - bit_reader: anytype, - expected_symbol_count: usize, - max_accuracy_log: u4, - entries: []Table.Fse, -) !usize { - const accuracy_log_biased = try bit_reader.readBitsNoEof(u4, 4); - if (accuracy_log_biased > max_accuracy_log -| 5) return error.MalformedAccuracyLog; - const accuracy_log = accuracy_log_biased + 5; - - var values: [256]u16 = undefined; - var value_count: usize = 0; - - const total_probability = @as(u16, 1) << accuracy_log; - var accumulated_probability: u16 = 0; - - while (accumulated_probability < total_probability) { - // WARNING: The RFC in poorly worded, and would suggest std.math.log2_int_ceil is correct here, - // but power of two (remaining probabilities + 1) need max bits set to 1 more. - const max_bits = std.math.log2_int(u16, total_probability - accumulated_probability + 1) + 1; - const small = try bit_reader.readBitsNoEof(u16, max_bits - 1); - - const cutoff = (@as(u16, 1) << max_bits) - 1 - (total_probability - accumulated_probability + 1); - - const value = if (small < cutoff) - small - else value: { - const value_read = small + (try bit_reader.readBitsNoEof(u16, 1) << (max_bits - 1)); - break :value if (value_read < @as(u16, 1) << (max_bits - 1)) - value_read - else - value_read - cutoff; - }; - - accumulated_probability += if (value != 0) value - 1 else 1; - - values[value_count] = value; - value_count += 1; - - if (value == 1) { - while (true) { - const repeat_flag = try bit_reader.readBitsNoEof(u2, 2); - var i: usize = 0; - while (i < repeat_flag) : (i += 1) { - values[value_count] = 1; - value_count += 1; - } - if (repeat_flag < 3) break; - } - } - } - bit_reader.alignToByte(); - - if (value_count < 2) return error.MalformedFseTable; - if (accumulated_probability != total_probability) return error.MalformedFseTable; - if (value_count > expected_symbol_count) return error.MalformedFseTable; - - const table_size = total_probability; - - try buildFseTable(values[0..value_count], entries[0..table_size]); - return table_size; -} - -const ReversedByteReader = struct { - remaining_bytes: usize, - bytes: []const u8, - - const Reader = std.io.Reader(*ReversedByteReader, error{}, readFn); - - fn init(bytes: []const u8) ReversedByteReader { - return .{ - .bytes = bytes, - .remaining_bytes = bytes.len, - }; - } - - fn reader(self: *ReversedByteReader) Reader { - return .{ .context = self }; - } - - fn readFn(ctx: *ReversedByteReader, buffer: []u8) !usize { - if (ctx.remaining_bytes == 0) return 0; - const byte_index = ctx.remaining_bytes - 1; - buffer[0] = ctx.bytes[byte_index]; - // buffer[0] = @bitReverse(ctx.bytes[byte_index]); - ctx.remaining_bytes = byte_index; - return 1; - } -}; - -/// A bit reader for reading the reversed bit streams used to encode -/// FSE compressed data. -pub const ReverseBitReader = struct { - byte_reader: ReversedByteReader, - bit_reader: std.io.BitReader(.Big, ReversedByteReader.Reader), - - pub fn init(self: *ReverseBitReader, bytes: []const u8) error{BitStreamHasNoStartBit}!void { - self.byte_reader = ReversedByteReader.init(bytes); - self.bit_reader = std.io.bitReader(.Big, self.byte_reader.reader()); - while (0 == self.readBitsNoEof(u1, 1) catch return error.BitStreamHasNoStartBit) {} - } - - pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) error{EndOfStream}!U { - return self.bit_reader.readBitsNoEof(U, num_bits); - } - - pub fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) error{}!U { - return try self.bit_reader.readBits(U, num_bits, out_bits); - } - - pub fn alignToByte(self: *@This()) void { - self.bit_reader.alignToByte(); - } -}; - -fn BitReader(comptime Reader: type) type { - return struct { - underlying: std.io.BitReader(.Little, Reader), - - fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { - return self.underlying.readBitsNoEof(U, num_bits); - } - - fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { - return self.underlying.readBits(U, num_bits, out_bits); - } - - fn alignToByte(self: *@This()) void { - self.underlying.alignToByte(); - } - }; -} - -pub fn bitReader(reader: anytype) BitReader(@TypeOf(reader)) { - return .{ .underlying = std.io.bitReader(.Little, reader) }; -} - test { std.testing.refAllDecls(@This()); } - -test buildFseTable { - const literals_length_default_values = [36]u16{ - 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 2, 2, 2, 2, 2, - 0, 0, 0, 0, - }; - - const match_lengths_default_values = [53]u16{ - 2, 5, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 0, - }; - - const offset_codes_default_values = [29]u16{ - 2, 2, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, - }; - - var entries: [64]Table.Fse = undefined; - try buildFseTable(&literals_length_default_values, &entries); - try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_literal_fse_table.fse, &entries); - - try buildFseTable(&match_lengths_default_values, &entries); - try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_match_fse_table.fse, &entries); - - try buildFseTable(&offset_codes_default_values, entries[0..32]); - try std.testing.expectEqualSlices(Table.Fse, types.compressed_block.predefined_offset_fse_table.fse, entries[0..32]); -} diff --git a/lib/std/compress/zstandard/readers.zig b/lib/std/compress/zstandard/readers.zig new file mode 100644 index 0000000000..489f933310 --- /dev/null +++ b/lib/std/compress/zstandard/readers.zig @@ -0,0 +1,75 @@ +const std = @import("std"); + +pub const ReversedByteReader = struct { + remaining_bytes: usize, + bytes: []const u8, + + const Reader = std.io.Reader(*ReversedByteReader, error{}, readFn); + + pub fn init(bytes: []const u8) ReversedByteReader { + return .{ + .bytes = bytes, + .remaining_bytes = bytes.len, + }; + } + + pub fn reader(self: *ReversedByteReader) Reader { + return .{ .context = self }; + } + + fn readFn(ctx: *ReversedByteReader, buffer: []u8) !usize { + if (ctx.remaining_bytes == 0) return 0; + const byte_index = ctx.remaining_bytes - 1; + buffer[0] = ctx.bytes[byte_index]; + // buffer[0] = @bitReverse(ctx.bytes[byte_index]); + ctx.remaining_bytes = byte_index; + return 1; + } +}; + +/// A bit reader for reading the reversed bit streams used to encode +/// FSE compressed data. +pub const ReverseBitReader = struct { + byte_reader: ReversedByteReader, + bit_reader: std.io.BitReader(.Big, ReversedByteReader.Reader), + + pub fn init(self: *ReverseBitReader, bytes: []const u8) error{BitStreamHasNoStartBit}!void { + self.byte_reader = ReversedByteReader.init(bytes); + self.bit_reader = std.io.bitReader(.Big, self.byte_reader.reader()); + while (0 == self.readBitsNoEof(u1, 1) catch return error.BitStreamHasNoStartBit) {} + } + + pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) error{EndOfStream}!U { + return self.bit_reader.readBitsNoEof(U, num_bits); + } + + pub fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) error{}!U { + return try self.bit_reader.readBits(U, num_bits, out_bits); + } + + pub fn alignToByte(self: *@This()) void { + self.bit_reader.alignToByte(); + } +}; + +pub fn BitReader(comptime Reader: type) type { + return struct { + underlying: std.io.BitReader(.Little, Reader), + + pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) !U { + return self.underlying.readBitsNoEof(U, num_bits); + } + + pub fn readBits(self: *@This(), comptime U: type, num_bits: usize, out_bits: *usize) !U { + return self.underlying.readBits(U, num_bits, out_bits); + } + + pub fn alignToByte(self: *@This()) void { + self.underlying.alignToByte(); + } + }; +} + +pub fn bitReader(reader: anytype) BitReader(@TypeOf(reader)) { + return .{ .underlying = std.io.bitReader(.Little, reader) }; +} From 89f9c5cb373c81af5cb052cd9e68e44a123d0b04 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 2 Feb 2023 20:49:11 +1100 Subject: [PATCH 036/122] std.compress.zstandard: improve doc comments --- lib/std/compress/zstandard/decode/block.zig | 96 ++++++++++------- lib/std/compress/zstandard/decompress.zig | 108 ++++++++++++++------ 2 files changed, 137 insertions(+), 67 deletions(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 7a7bf28b31..13b91b4bc2 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -23,7 +23,6 @@ pub const Error = error{ ReservedBlock, MalformedRleBlock, MalformedCompressedBlock, - EndOfStream, }; pub const DecodeState = struct { @@ -92,11 +91,17 @@ pub const DecodeState = struct { /// stream and Huffman tree from `literals` and reads the FSE tables from /// `source`. /// - /// Errors: - /// - returns `error.BitStreamHasNoStartBit` if the (reversed) literal bitstream's - /// first byte does not have any bits set. - /// - returns `error.TreelessLiteralsFirst` `literals` is a treeless literals section - /// and the decode state does not have a Huffman tree from a previous block. + /// Errors returned: + /// - `error.BitStreamHasNoStartBit` if the (reversed) literal bitstream's + /// first byte does not have any bits set + /// - `error.TreelessLiteralsFirst` `literals` is a treeless literals + /// section and the decode state does not have a Huffman tree from a + /// previous block + /// - `error.RepeatModeFirst` on the first call if one of the sequence FSE + /// tables is set to repeat mode + /// - `error.MalformedAccuracyLog` if an FSE table has an invalid accuracy + /// - `error.MalformedFseTable` if there are errors decoding an FSE table + /// - `error.EndOfStream` if `source` ends before all FSE tables are read pub fn prepare( self: *DecodeState, source: anytype, @@ -132,8 +137,10 @@ pub const DecodeState = struct { } } - /// Read initial FSE states for sequence decoding. Returns `error.EndOfStream` - /// if `bit_reader` does not contain enough bits. + /// Read initial FSE states for sequence decoding. + /// + /// Errors returned: + /// - `error.EndOfStream` if `bit_reader` does not contain enough bits. pub fn readInitialFseState(self: *DecodeState, bit_reader: *readers.ReverseBitReader) error{EndOfStream}!void { self.literal.state = try bit_reader.readBitsNoEof(u9, self.literal.accuracy_log); self.offset.state = try bit_reader.readBitsNoEof(u8, self.offset.accuracy_log); @@ -308,13 +315,19 @@ pub const DecodeState = struct { } || DecodeLiteralsError; /// Decode one sequence from `bit_reader` into `dest`, written starting at - /// `write_pos` and update FSE states if `last_sequence` is `false`. Returns - /// `error.MalformedSequence` error if the decompressed sequence would be longer - /// than `sequence_size_limit` or the sequence's offset is too large; returns - /// `error.EndOfStream` if `bit_reader` does not contain enough bits; returns - /// `error.UnexpectedEndOfLiteralStream` if the decoder state's literal streams - /// do not contain enough literals for the sequence (this may mean the literal - /// stream or the sequence is malformed). + /// `write_pos` and update FSE states if `last_sequence` is `false`. + /// `prepare()` must be called for the block before attempting to decode + /// sequences. + /// + /// Errors returned: + /// - `error.MalformedSequence` if the decompressed sequence would be + /// longer than `sequence_size_limit` or the sequence's offset is too + /// large + /// - `error.UnexpectedEndOfLiteralStream` if the decoder state's literal + /// streams do not contain enough literals for the sequence (this may + /// mean the literal stream or the sequence is malformed). + /// - `error.OffsetCodeTooLarge` if an invalid offset code is found + /// - `error.EndOfStream` if `bit_reader` does not contain enough bits pub fn decodeSequenceSlice( self: *DecodeState, dest: []u8, @@ -336,7 +349,8 @@ pub const DecodeState = struct { return sequence_length; } - /// Decode one sequence from `bit_reader` into `dest`; see `decodeSequenceSlice`. + /// Decode one sequence from `bit_reader` into `dest`; see + /// `decodeSequenceSlice`. pub fn decodeSequenceRingBuffer( self: *DecodeState, dest: *RingBuffer, @@ -364,7 +378,7 @@ pub const DecodeState = struct { try self.initLiteralStream(self.literal_streams.four[self.literal_stream_index]); } - pub fn initLiteralStream(self: *DecodeState, bytes: []const u8) error{BitStreamHasNoStartBit}!void { + fn initLiteralStream(self: *DecodeState, bytes: []const u8) error{BitStreamHasNoStartBit}!void { try self.literal_stream_reader.init(bytes); } @@ -393,12 +407,14 @@ pub const DecodeState = struct { PrefixNotFound, } || LiteralBitsError; - /// Decode `len` bytes of literals into `dest`. `literals` should be the - /// `LiteralsSection` that was passed to `prepare()`. Returns - /// `error.MalformedLiteralsLength` if the number of literal bytes decoded by - /// `self` plus `len` is greater than the regenerated size of `literals`. - /// Returns `error.UnexpectedEndOfLiteralStream` and `error.PrefixNotFound` if - /// there are problems decoding Huffman compressed literals. + /// Decode `len` bytes of literals into `dest`. + /// + /// Errors returned: + /// - `error.MalformedLiteralsLength` if the number of literal bytes + /// decoded by `self` plus `len` is greater than the regenerated size of + /// `literals` + /// - `error.UnexpectedEndOfLiteralStream` and `error.PrefixNotFound` if + /// there are problems decoding Huffman compressed literals pub fn decodeLiteralsSlice( self: *DecodeState, dest: []u8, @@ -561,7 +577,6 @@ pub const DecodeState = struct { /// - `error.MalformedRleBlock` if the block is an RLE block and `src.len < 1` /// - `error.MalformedCompressedBlock` if there are errors decoding a /// compressed block -/// - `error.EndOfStream` if the sequence bit stream ends unexpectedly pub fn decodeBlock( dest: []u8, src: []const u8, @@ -738,7 +753,8 @@ pub fn decodeBlockRingBuffer( /// `error.SequenceBufferTooSmall` are returned (the maximum block size is an /// upper bound for the size of both buffers). See `decodeBlock` /// and `decodeBlockRingBuffer` for function that can decode a block without -/// these extra copies. +/// these extra copies. `error.EndOfStream` is returned if `source` does not +/// contain enough bytes. pub fn decodeBlockReader( dest: *RingBuffer, source: anytype, @@ -820,6 +836,10 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { }; } +/// Decode the header of a block. +/// +/// Errors returned: +/// - `error.EndOfStream` if `src.len < 3` pub fn decodeBlockHeaderSlice(src: []const u8) error{EndOfStream}!frame.ZStandard.Block.Header { if (src.len < 3) return error.EndOfStream; return decodeBlockHeader(src[0..3]); @@ -828,9 +848,14 @@ pub fn decodeBlockHeaderSlice(src: []const u8) error{EndOfStream}!frame.ZStandar /// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the /// number of bytes the section uses. /// -/// Errors: -/// - returns `error.MalformedLiteralsHeader` if the header is invalid -/// - returns `error.MalformedLiteralsSection` if there are errors decoding +/// Errors returned: +/// - `error.MalformedLiteralsHeader` if the header is invalid +/// - `error.MalformedLiteralsSection` if there are decoding errors +/// - `error.MalformedAccuracyLog` if compressed literals have invalid +/// accuracy +/// - `error.MalformedFseTable` if compressed literals have invalid FSE table +/// - `error.MalformedHuffmanTree` if there are errors decoding a Huffamn tree +/// - `error.EndOfStream` if there are not enough bytes in `src` pub fn decodeLiteralsSectionSlice( src: []const u8, consumed_count: *usize, @@ -886,11 +911,7 @@ pub fn decodeLiteralsSectionSlice( } /// Decode a `LiteralsSection` from `src`, incrementing `consumed_count` by the -/// number of bytes the section uses. -/// -/// Errors: -/// - returns `error.MalformedLiteralsHeader` if the header is invalid -/// - returns `error.MalformedLiteralsSection` if there are errors decoding +/// number of bytes the section uses. See `decodeLiterasSectionSlice()`. pub fn decodeLiteralsSection( source: anytype, buffer: []u8, @@ -961,6 +982,9 @@ fn decodeStreams(size_format: u2, stream_data: []const u8) !LiteralsSection.Stre } /// Decode a literals section header. +/// +/// Errors returned: +/// - `error.EndOfStream` if there are not enough bytes in `source` pub fn decodeLiteralsHeader(source: anytype) !LiteralsSection.Header { const byte0 = try source.readByte(); const block_type = @intToEnum(LiteralsSection.BlockType, byte0 & 0b11); @@ -1011,9 +1035,9 @@ pub fn decodeLiteralsHeader(source: anytype) !LiteralsSection.Header { /// Decode a sequences section header. /// -/// Errors: -/// - returns `error.ReservedBitSet` is the reserved bit is set -/// - returns `error.MalformedSequencesHeader` if the header is invalid +/// Errors returned: +/// - `error.ReservedBitSet` if the reserved bit is set +/// - `error.EndOfStream` if there are not enough bytes in `source` pub fn decodeSequencesHeader( source: anytype, ) !SequencesSection.Header { diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index abc30fda2a..6941dc6d45 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -25,11 +25,12 @@ pub fn isSkippableMagic(magic: u32) bool { /// Returns the kind of frame at the beginning of `src`. /// -/// Errors: -/// - returns `error.BadMagic` if `source` begins with bytes not equal to the +/// Errors returned: +/// - `error.BadMagic` if `source` begins with bytes not equal to the /// Zstandard frame magic number, or outside the range of magic numbers for /// skippable frames. -pub fn decodeFrameType(source: anytype) !frame.Kind { +/// - `error.EndOfStream` if `source` contains fewer than 4 bytes +pub fn decodeFrameType(source: anytype) error{ BadMagic, EndOfStream }!frame.Kind { const magic = try source.readIntLittle(u32); return if (magic == frame.ZStandard.magic_number) .zstandard @@ -45,12 +46,23 @@ const ReadWriteCount = struct { }; /// Decodes the frame at the start of `src` into `dest`. Returns the number of -/// bytes read from `src` and written to `dest`. +/// bytes read from `src` and written to `dest`. This function can only decode +/// frames that declare the decompressed content size. /// -/// Errors: -/// - returns `error.UnknownContentSizeUnsupported` -/// - returns `error.ContentTooLarge` -/// - returns `error.BadMagic` +/// Errors returned: +/// - `error.UnknownContentSizeUnsupported` if the frame does not declare the +/// uncompressed content size +/// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data +/// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic +/// number for a Zstandard or Skippable frame +/// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary +/// - `error.ChecksumFailure` if `verify_checksum` is true and the frame +/// contains a checksum that does not match the checksum of the decompressed +/// data +/// - `error.ReservedBitSet` if the reserved bit of the frame header is set +/// - `error.UnusedBitSet` if the unused bit of the frame header is set +/// - `error.EndOfStream` if `src` does not contain a complete frame +/// - an error in `block.Error` if there are errors decoding a block pub fn decodeFrame( dest: []u8, src: []const u8, @@ -66,6 +78,7 @@ pub fn decodeFrame( }; } +/// Returns the frame checksum corresponding to the data fed into `hasher` pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { const hash = hasher.final(); return @intCast(u32, hash & 0xFFFFFFFF); @@ -74,20 +87,31 @@ pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { const FrameError = error{ DictionaryIdFlagUnsupported, ChecksumFailure, + EndOfStream, } || InvalidBit || block.Error; /// Decode a Zstandard frame from `src` into `dest`, returning the number of -/// bytes read from `src` and written to `dest`; if the frame does not declare -/// its decompressed content size `error.UnknownContentSizeUnsupported` is -/// returned. Returns `error.DictionaryIdFlagUnsupported` if the frame uses a -/// dictionary, and `error.ChecksumFailure` if `verify_checksum` is `true` and -/// the frame contains a checksum that does not match the checksum computed from -/// the decompressed frame. +/// bytes read from `src` and written to `dest`. The first four bytes of `src` +/// must be the magic number for a Zstandard frame. +/// +/// Error returned: +/// - `error.UnknownContentSizeUnsupported` if the frame does not declare the +/// uncompressed content size +/// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data +/// number for a Zstandard or Skippable frame +/// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary +/// - `error.ChecksumFailure` if `verify_checksum` is true and the frame +/// contains a checksum that does not match the checksum of the decompressed +/// data +/// - `error.ReservedBitSet` if the reserved bit of the frame header is set +/// - `error.UnusedBitSet` if the unused bit of the frame header is set +/// - `error.EndOfStream` if `src` does not contain a complete frame +/// - an error in `block.Error` if there are errors decoding a block pub fn decodeZStandardFrame( dest: []u8, src: []const u8, verify_checksum: bool, -) (error{ UnknownContentSizeUnsupported, ContentTooLarge, EndOfStream } || FrameError)!ReadWriteCount { +) (error{ UnknownContentSizeUnsupported, ContentTooLarge } || FrameError)!ReadWriteCount { assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; @@ -127,7 +151,18 @@ pub const FrameContext = struct { has_checksum: bool, block_size_max: usize, - pub fn init(frame_header: frame.ZStandard.Header, window_size_max: usize, verify_checksum: bool) !FrameContext { + const Error = error{ DictionaryIdFlagUnsupported, WindowSizeUnknown, WindowTooLarge }; + /// Validates `frame_header` and returns the associated `FrameContext`. + /// + /// Errors returned: + /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary + /// - `error.WindowSizeUnknown` if the frame does not have a valid window size + /// - `error.WindowTooLarge` if the window size is larger than + pub fn init( + frame_header: frame.ZStandard.Header, + window_size_max: usize, + verify_checksum: bool, + ) Error!FrameContext { if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; const window_size_raw = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; @@ -147,19 +182,29 @@ pub const FrameContext = struct { }; /// Decode a Zstandard from from `src` and return the decompressed bytes; see -/// `decodeZStandardFrame()`. Returns `error.WindowSizeUnknown` if the frame -/// does not declare its content size or a window descriptor (this indicates a -/// malformed frame). +/// `decodeZStandardFrame()`. `allocator` is used to allocate both the returned +/// slice and internal buffers used during decoding. The first four bytes of +/// `src` must be the magic number for a Zstandard frame. /// -/// Errors: -/// - returns `error.WindowTooLarge` -/// - returns `error.WindowSizeUnknown` +/// Errors returned: +/// - `error.WindowSizeUnknown` if the frame does not have a valid window size +/// - `error.WindowTooLarge` if the window size is larger than +/// `window_size_max` +/// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary +/// - `error.ChecksumFailure` if `verify_checksum` is true and the frame +/// contains a checksum that does not match the checksum of the decompressed +/// data +/// - `error.ReservedBitSet` if the reserved bit of the frame header is set +/// - `error.UnusedBitSet` if the unused bit of the frame header is set +/// - `error.EndOfStream` if `src` does not contain a complete frame +/// - `error.OutOfMemory` if `allocator` cannot allocate enough memory +/// - an error in `block.Error` if there are errors decoding a block pub fn decodeZStandardFrameAlloc( allocator: std.mem.Allocator, src: []const u8, verify_checksum: bool, window_size_max: usize, -) (error{ WindowSizeUnknown, WindowTooLarge, OutOfMemory, EndOfStream } || FrameError)![]u8 { +) (error{OutOfMemory} || FrameContext.Error || FrameError)![]u8 { var result = std.ArrayList(u8).init(allocator); assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; @@ -222,7 +267,7 @@ fn decodeFrameBlocks( src: []const u8, consumed_count: *usize, hash: ?*std.hash.XxHash64, -) block.Error!usize { +) (error{EndOfStream} || block.Error)!usize { // These tables take 7680 bytes var literal_fse_data: [types.compressed_block.table_size_max.literal]Table.Fse = undefined; var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; @@ -252,7 +297,8 @@ fn decodeFrameBlocks( return written_count; } -/// Decode the header of a skippable frame. +/// Decode the header of a skippable frame. The first four bytes of `src` must +/// be a valid magic number for a Skippable frame. pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { const magic = readInt(u32, src[0..4]); assert(isSkippableMagic(magic)); @@ -263,8 +309,8 @@ pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { }; } -/// Returns the window size required to decompress a frame, or `null` if it cannot be -/// determined, which indicates a malformed frame header. +/// Returns the window size required to decompress a frame, or `null` if it +/// cannot be determined (which indicates a malformed frame header). pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { if (header.window_descriptor) |descriptor| { const exponent = (descriptor & 0b11111000) >> 3; @@ -279,10 +325,10 @@ pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { const InvalidBit = error{ UnusedBitSet, ReservedBitSet }; /// Decode the header of a Zstandard frame. /// -/// Errors: -/// - returns `error.UnusedBitSet` if the unused bits of the header are set -/// - returns `error.ReservedBitSet` if the reserved bits of the header are -/// set +/// Errors returned: +/// - `error.UnusedBitSet` if the unused bits of the header are set +/// - `error.ReservedBitSet` if the reserved bits of the header are set +/// - `error.EndOfStream` if `source` does not contain a complete header pub fn decodeZStandardHeader(source: anytype) (error{EndOfStream} || InvalidBit)!frame.ZStandard.Header { const descriptor = @bitCast(frame.ZStandard.Header.Descriptor, try source.readByte()); From ddeabc9aa7a465793ccbfd56dc29f04bf0d2854b Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 2 Feb 2023 22:06:23 +1100 Subject: [PATCH 037/122] std.compress.zstandard: add `decodeFrameAlloc()` --- lib/std/compress/zstandard/decompress.zig | 74 ++++++++++++++++++++--- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 6941dc6d45..470e6858b0 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -1,5 +1,6 @@ const std = @import("std"); const assert = std.debug.assert; +const Allocator = std.mem.Allocator; const types = @import("types.zig"); const frame = types.frame; @@ -32,6 +33,14 @@ pub fn isSkippableMagic(magic: u32) bool { /// - `error.EndOfStream` if `source` contains fewer than 4 bytes pub fn decodeFrameType(source: anytype) error{ BadMagic, EndOfStream }!frame.Kind { const magic = try source.readIntLittle(u32); + return frameType(magic); +} + +/// Returns the kind of frame associated to `magic`. +/// +/// Errors returned: +/// - `error.BadMagic` if `magic` is not a valid magic number. +pub fn frameType(magic: u32) error{BadMagic}!frame.Kind { return if (magic == frame.ZStandard.magic_number) .zstandard else if (isSkippableMagic(magic)) @@ -78,6 +87,56 @@ pub fn decodeFrame( }; } +pub const DecodeResult = struct { + bytes: []u8, + read_count: usize, +}; +pub const DecodedFrame = union(enum) { + zstandard: DecodeResult, + skippable: frame.Skippable.Header, +}; + +/// Decodes the frame at the start of `src` into `dest`. Returns the number of +/// bytes read from `src` and the decoded bytes for a Zstandard frame, or the +/// frame header for a Skippable frame. +/// +/// Errors returned: +/// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic +/// number for a Zstandard or Skippable frame +/// - `error.WindowSizeUnknown` if the frame does not have a valid window size +/// - `error.WindowTooLarge` if the window size is larger than +/// `window_size_max` +/// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary +/// - `error.ChecksumFailure` if `verify_checksum` is true and the frame +/// contains a checksum that does not match the checksum of the decompressed +/// data +/// - `error.ReservedBitSet` if the reserved bit of the frame header is set +/// - `error.UnusedBitSet` if the unused bit of the frame header is set +/// - `error.EndOfStream` if `src` does not contain a complete frame +/// - `error.OutOfMemory` if `allocator` cannot allocate enough memory +/// - an error in `block.Error` if there are errors decoding a block +pub fn decodeFrameAlloc( + allocator: Allocator, + src: []const u8, + verify_checksum: bool, + window_size_max: usize, +) !DecodedFrame { + var fbs = std.io.fixedBufferStream(src); + const reader = fbs.reader(); + const magic = try reader.readIntLittle(u32); + return switch (try frameType(magic)) { + .zstandard => .{ + .zstandard = try decodeZStandardFrameAlloc(allocator, src, verify_checksum, window_size_max), + }, + .skippable => .{ + .skippable = .{ + .magic_number = magic, + .frame_size = try reader.readIntLittle(u32), + }, + }, + }; +} + /// Returns the frame checksum corresponding to the data fed into `hasher` pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { const hash = hasher.final(); @@ -181,10 +240,11 @@ pub const FrameContext = struct { } }; -/// Decode a Zstandard from from `src` and return the decompressed bytes; see -/// `decodeZStandardFrame()`. `allocator` is used to allocate both the returned -/// slice and internal buffers used during decoding. The first four bytes of -/// `src` must be the magic number for a Zstandard frame. +/// Decode a Zstandard from from `src` and return the decompressed bytes and the +/// number of bytes read; see `decodeZStandardFrame()`. `allocator` is used to +/// allocate both the returned slice and internal buffers used during decoding. +/// The first four bytes of `src` must be the magic number for a Zstandard +/// frame. /// /// Errors returned: /// - `error.WindowSizeUnknown` if the frame does not have a valid window size @@ -200,11 +260,11 @@ pub const FrameContext = struct { /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory /// - an error in `block.Error` if there are errors decoding a block pub fn decodeZStandardFrameAlloc( - allocator: std.mem.Allocator, + allocator: Allocator, src: []const u8, verify_checksum: bool, window_size_max: usize, -) (error{OutOfMemory} || FrameContext.Error || FrameError)![]u8 { +) (error{OutOfMemory} || FrameContext.Error || FrameError)!DecodeResult { var result = std.ArrayList(u8).init(allocator); assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); var consumed_count: usize = 4; @@ -258,7 +318,7 @@ pub fn decodeZStandardFrameAlloc( if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; } } - return result.toOwnedSlice(); + return DecodeResult{ .bytes = try result.toOwnedSlice(), .read_count = consumed_count }; } /// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. From 3f1c4306caf14274b60cfb2ff4b088a56d893e13 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 2 Feb 2023 22:23:03 +1100 Subject: [PATCH 038/122] std.compress.zstandard: fix capitalisation of Zstandard --- lib/std/compress/zstandard.zig | 2 +- lib/std/compress/zstandard/decode/block.zig | 12 ++++----- lib/std/compress/zstandard/decompress.zig | 30 ++++++++++----------- lib/std/compress/zstandard/types.zig | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index d17e6bac46..5b6f928db5 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -33,7 +33,7 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool .skippable => return error.SkippableFrame, .zstandard => { const frame_context = context: { - const frame_header = try decompress.decodeZStandardHeader(source); + const frame_header = try decompress.decodeZstandardHeader(source); break :context try decompress.FrameContext.init( frame_header, window_size_max, diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 13b91b4bc2..a0f968736d 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -580,7 +580,7 @@ pub const DecodeState = struct { pub fn decodeBlock( dest: []u8, src: []const u8, - block_header: frame.ZStandard.Block.Header, + block_header: frame.Zstandard.Block.Header, decode_state: *DecodeState, consumed_count: *usize, written_count: usize, @@ -668,7 +668,7 @@ pub fn decodeBlock( pub fn decodeBlockRingBuffer( dest: *RingBuffer, src: []const u8, - block_header: frame.ZStandard.Block.Header, + block_header: frame.Zstandard.Block.Header, decode_state: *DecodeState, consumed_count: *usize, block_size_max: usize, @@ -758,7 +758,7 @@ pub fn decodeBlockRingBuffer( pub fn decodeBlockReader( dest: *RingBuffer, source: anytype, - block_header: frame.ZStandard.Block.Header, + block_header: frame.Zstandard.Block.Header, decode_state: *DecodeState, block_size_max: usize, literals_buffer: []u8, @@ -825,9 +825,9 @@ pub fn decodeBlockReader( } /// Decode the header of a block. -pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { +pub fn decodeBlockHeader(src: *const [3]u8) frame.Zstandard.Block.Header { const last_block = src[0] & 1 == 1; - const block_type = @intToEnum(frame.ZStandard.Block.Type, (src[0] & 0b110) >> 1); + const block_type = @intToEnum(frame.Zstandard.Block.Type, (src[0] & 0b110) >> 1); const block_size = ((src[0] & 0b11111000) >> 3) + (@as(u21, src[1]) << 5) + (@as(u21, src[2]) << 13); return .{ .last_block = last_block, @@ -840,7 +840,7 @@ pub fn decodeBlockHeader(src: *const [3]u8) frame.ZStandard.Block.Header { /// /// Errors returned: /// - `error.EndOfStream` if `src.len < 3` -pub fn decodeBlockHeaderSlice(src: []const u8) error{EndOfStream}!frame.ZStandard.Block.Header { +pub fn decodeBlockHeaderSlice(src: []const u8) error{EndOfStream}!frame.Zstandard.Block.Header { if (src.len < 3) return error.EndOfStream; return decodeBlockHeader(src[0..3]); } diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 470e6858b0..4cf95584d3 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -41,7 +41,7 @@ pub fn decodeFrameType(source: anytype) error{ BadMagic, EndOfStream }!frame.Kin /// Errors returned: /// - `error.BadMagic` if `magic` is not a valid magic number. pub fn frameType(magic: u32) error{BadMagic}!frame.Kind { - return if (magic == frame.ZStandard.magic_number) + return if (magic == frame.Zstandard.magic_number) .zstandard else if (isSkippableMagic(magic)) .skippable @@ -79,7 +79,7 @@ pub fn decodeFrame( ) !ReadWriteCount { var fbs = std.io.fixedBufferStream(src); return switch (try decodeFrameType(fbs.reader())) { - .zstandard => decodeZStandardFrame(dest, src, verify_checksum), + .zstandard => decodeZstandardFrame(dest, src, verify_checksum), .skippable => ReadWriteCount{ .read_count = try fbs.reader().readIntLittle(u32) + 8, .write_count = 0, @@ -126,7 +126,7 @@ pub fn decodeFrameAlloc( const magic = try reader.readIntLittle(u32); return switch (try frameType(magic)) { .zstandard => .{ - .zstandard = try decodeZStandardFrameAlloc(allocator, src, verify_checksum, window_size_max), + .zstandard = try decodeZstandardFrameAlloc(allocator, src, verify_checksum, window_size_max), }, .skippable => .{ .skippable = .{ @@ -166,17 +166,17 @@ const FrameError = error{ /// - `error.UnusedBitSet` if the unused bit of the frame header is set /// - `error.EndOfStream` if `src` does not contain a complete frame /// - an error in `block.Error` if there are errors decoding a block -pub fn decodeZStandardFrame( +pub fn decodeZstandardFrame( dest: []u8, src: []const u8, verify_checksum: bool, ) (error{ UnknownContentSizeUnsupported, ContentTooLarge } || FrameError)!ReadWriteCount { - assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); + assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); var consumed_count: usize = 4; var fbs = std.io.fixedBufferStream(src[consumed_count..]); var source = fbs.reader(); - const frame_header = try decodeZStandardHeader(source); + const frame_header = try decodeZstandardHeader(source); consumed_count += fbs.pos; if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; @@ -218,7 +218,7 @@ pub const FrameContext = struct { /// - `error.WindowSizeUnknown` if the frame does not have a valid window size /// - `error.WindowTooLarge` if the window size is larger than pub fn init( - frame_header: frame.ZStandard.Header, + frame_header: frame.Zstandard.Header, window_size_max: usize, verify_checksum: bool, ) Error!FrameContext { @@ -241,7 +241,7 @@ pub const FrameContext = struct { }; /// Decode a Zstandard from from `src` and return the decompressed bytes and the -/// number of bytes read; see `decodeZStandardFrame()`. `allocator` is used to +/// number of bytes read; see `decodeZstandardFrame()`. `allocator` is used to /// allocate both the returned slice and internal buffers used during decoding. /// The first four bytes of `src` must be the magic number for a Zstandard /// frame. @@ -259,20 +259,20 @@ pub const FrameContext = struct { /// - `error.EndOfStream` if `src` does not contain a complete frame /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory /// - an error in `block.Error` if there are errors decoding a block -pub fn decodeZStandardFrameAlloc( +pub fn decodeZstandardFrameAlloc( allocator: Allocator, src: []const u8, verify_checksum: bool, window_size_max: usize, ) (error{OutOfMemory} || FrameContext.Error || FrameError)!DecodeResult { var result = std.ArrayList(u8).init(allocator); - assert(readInt(u32, src[0..4]) == frame.ZStandard.magic_number); + assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); var consumed_count: usize = 4; var frame_context = context: { var fbs = std.io.fixedBufferStream(src[consumed_count..]); var source = fbs.reader(); - const frame_header = try decodeZStandardHeader(source); + const frame_header = try decodeZstandardHeader(source); consumed_count += fbs.pos; break :context try FrameContext.init(frame_header, window_size_max, verify_checksum); }; @@ -371,7 +371,7 @@ pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { /// Returns the window size required to decompress a frame, or `null` if it /// cannot be determined (which indicates a malformed frame header). -pub fn frameWindowSize(header: frame.ZStandard.Header) ?u64 { +pub fn frameWindowSize(header: frame.Zstandard.Header) ?u64 { if (header.window_descriptor) |descriptor| { const exponent = (descriptor & 0b11111000) >> 3; const mantissa = descriptor & 0b00000111; @@ -389,8 +389,8 @@ const InvalidBit = error{ UnusedBitSet, ReservedBitSet }; /// - `error.UnusedBitSet` if the unused bits of the header are set /// - `error.ReservedBitSet` if the reserved bits of the header are set /// - `error.EndOfStream` if `source` does not contain a complete header -pub fn decodeZStandardHeader(source: anytype) (error{EndOfStream} || InvalidBit)!frame.ZStandard.Header { - const descriptor = @bitCast(frame.ZStandard.Header.Descriptor, try source.readByte()); +pub fn decodeZstandardHeader(source: anytype) (error{EndOfStream} || InvalidBit)!frame.Zstandard.Header { + const descriptor = @bitCast(frame.Zstandard.Header.Descriptor, try source.readByte()); if (descriptor.unused) return error.UnusedBitSet; if (descriptor.reserved) return error.ReservedBitSet; @@ -414,7 +414,7 @@ pub fn decodeZStandardHeader(source: anytype) (error{EndOfStream} || InvalidBit) if (field_size == 2) content_size.? += 256; } - const header = frame.ZStandard.Header{ + const header = frame.Zstandard.Header{ .descriptor = descriptor, .window_descriptor = window_descriptor, .dictionary_id = dictionary_id, diff --git a/lib/std/compress/zstandard/types.zig b/lib/std/compress/zstandard/types.zig index d94a55ebe5..3f61b0bab4 100644 --- a/lib/std/compress/zstandard/types.zig +++ b/lib/std/compress/zstandard/types.zig @@ -1,7 +1,7 @@ pub const frame = struct { pub const Kind = enum { zstandard, skippable }; - pub const ZStandard = struct { + pub const Zstandard = struct { pub const magic_number = 0xFD2FB528; header: Header, From a651704876acfa8386c5eb117800f425e194bdc6 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 2 Feb 2023 22:29:16 +1100 Subject: [PATCH 039/122] std.compress.zstandard: free allocated result on error --- lib/std/compress/zstandard/decompress.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 4cf95584d3..da6e161043 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -266,6 +266,7 @@ pub fn decodeZstandardFrameAlloc( window_size_max: usize, ) (error{OutOfMemory} || FrameContext.Error || FrameError)!DecodeResult { var result = std.ArrayList(u8).init(allocator); + errdefer result.deinit(); assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); var consumed_count: usize = 4; From 596a97fb556a380d9f3780363ee07fc5dfaf43c2 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Fri, 3 Feb 2023 12:56:51 +1100 Subject: [PATCH 040/122] std.compress.zstandard: fix crashes --- lib/std/compress/zstandard/decode/block.zig | 34 ++++++++++++------- lib/std/compress/zstandard/decode/huffman.zig | 3 +- lib/std/compress/zstandard/decompress.zig | 16 +++++---- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index a0f968736d..ab1d476dd6 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -148,8 +148,8 @@ pub const DecodeState = struct { } fn updateRepeatOffset(self: *DecodeState, offset: u32) void { - std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[1]); - std.mem.swap(u32, &self.repeat_offsets[0], &self.repeat_offsets[2]); + self.repeat_offsets[2] = self.repeat_offsets[1]; + self.repeat_offsets[1] = self.repeat_offsets[0]; self.repeat_offsets[0] = offset; } @@ -238,18 +238,22 @@ pub const DecodeState = struct { fn nextSequence( self: *DecodeState, bit_reader: *readers.ReverseBitReader, - ) error{ OffsetCodeTooLarge, EndOfStream }!Sequence { + ) error{ InvalidBitStream, EndOfStream }!Sequence { const raw_code = self.getCode(.offset); const offset_code = std.math.cast(u5, raw_code) orelse { - return error.OffsetCodeTooLarge; + return error.InvalidBitStream; }; const offset_value = (@as(u32, 1) << offset_code) + try bit_reader.readBitsNoEof(u32, offset_code); const match_code = self.getCode(.match); + if (match_code >= types.compressed_block.match_length_code_table.len) + return error.InvalidBitStream; const match = types.compressed_block.match_length_code_table[match_code]; const match_length = match[0] + try bit_reader.readBitsNoEof(u32, match[1]); const literal_code = self.getCode(.literal); + if (literal_code >= types.compressed_block.literals_length_code_table.len) + return error.InvalidBitStream; const literal = types.compressed_block.literals_length_code_table[literal_code]; const literal_length = literal[0] + try bit_reader.readBitsNoEof(u32, literal[1]); @@ -269,6 +273,8 @@ pub const DecodeState = struct { break :offset self.useRepeatOffset(offset_value - 1); }; + if (offset == 0) return error.InvalidBitStream; + return .{ .literal_length = literal_length, .match_length = match_length, @@ -308,7 +314,7 @@ pub const DecodeState = struct { } const DecodeSequenceError = error{ - OffsetCodeTooLarge, + InvalidBitStream, EndOfStream, MalformedSequence, MalformedFseBits, @@ -326,7 +332,7 @@ pub const DecodeState = struct { /// - `error.UnexpectedEndOfLiteralStream` if the decoder state's literal /// streams do not contain enough literals for the sequence (this may /// mean the literal stream or the sequence is malformed). - /// - `error.OffsetCodeTooLarge` if an invalid offset code is found + /// - `error.InvalidBitStream` if the FSE sequence bitstream is malformed /// - `error.EndOfStream` if `bit_reader` does not contain enough bits pub fn decodeSequenceSlice( self: *DecodeState, @@ -608,9 +614,9 @@ pub fn decodeBlock( .compressed => { if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; - const literals = decodeLiteralsSectionSlice(src, &bytes_read) catch + const literals = decodeLiteralsSectionSlice(src[0..block_size], &bytes_read) catch return error.MalformedCompressedBlock; - var fbs = std.io.fixedBufferStream(src[bytes_read..]); + var fbs = std.io.fixedBufferStream(src[bytes_read..block_size]); const fbs_reader = fbs.reader(); const sequences_header = decodeSequencesHeader(fbs_reader) catch return error.MalformedCompressedBlock; @@ -695,9 +701,9 @@ pub fn decodeBlockRingBuffer( .compressed => { if (src.len < block_size) return error.MalformedBlockSize; var bytes_read: usize = 0; - const literals = decodeLiteralsSectionSlice(src, &bytes_read) catch + const literals = decodeLiteralsSectionSlice(src[0..block_size], &bytes_read) catch return error.MalformedCompressedBlock; - var fbs = std.io.fixedBufferStream(src[bytes_read..]); + var fbs = std.io.fixedBufferStream(src[bytes_read..block_size]); const fbs_reader = fbs.reader(); const sequences_header = decodeSequencesHeader(fbs_reader) catch return error.MalformedCompressedBlock; @@ -894,7 +900,8 @@ pub fn decodeLiteralsSectionSlice( else null; const huffman_tree_size = bytes_read - huffman_tree_start; - const total_streams_size = @as(usize, header.compressed_size.?) - huffman_tree_size; + const total_streams_size = std.math.sub(usize, header.compressed_size.?, huffman_tree_size) catch + return error.MalformedLiteralsSection; if (src.len < bytes_read + total_streams_size) return error.MalformedLiteralsSection; const stream_data = src[bytes_read .. bytes_read + total_streams_size]; @@ -940,8 +947,9 @@ pub fn decodeLiteralsSection( try huffman.decodeHuffmanTree(counting_reader.reader(), buffer) else null; - const huffman_tree_size = counting_reader.bytes_read; - const total_streams_size = @as(usize, header.compressed_size.?) - @intCast(usize, huffman_tree_size); + const huffman_tree_size = @intCast(usize, counting_reader.bytes_read); + const total_streams_size = std.math.sub(usize, header.compressed_size.?, huffman_tree_size) catch + return error.MalformedLiteralsSection; if (total_streams_size > buffer.len) return error.LiteralsBufferTooSmall; try source.readNoEof(buffer[0..total_streams_size]); diff --git a/lib/std/compress/zstandard/decode/huffman.zig b/lib/std/compress/zstandard/decode/huffman.zig index c759bfd6ab..01913c7044 100644 --- a/lib/std/compress/zstandard/decode/huffman.zig +++ b/lib/std/compress/zstandard/decode/huffman.zig @@ -146,13 +146,14 @@ fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.P return prefixed_symbol_count; } -fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) LiteralsSection.HuffmanTree { +fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) error{MalformedHuffmanTree}!LiteralsSection.HuffmanTree { var weight_power_sum: u16 = 0; for (weights[0 .. symbol_count - 1]) |value| { if (value > 0) { weight_power_sum += @as(u16, 1) << (value - 1); } } + if (weight_power_sum >= 1 << 11) return error.MalformedHuffmanTree; // advance to next power of two (even if weight_power_sum is a power of 2) const max_number_of_bits = std.math.log2_int(u16, weight_power_sum) + 1; diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index da6e161043..cf172a16ca 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -195,6 +195,7 @@ pub fn decodeZstandardFrame( ); if (frame_header.descriptor.content_checksum_flag) { + if (src.len < consumed_count + 4) return error.EndOfStream; const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); consumed_count += 4; if (hasher_opt) |*hasher| { @@ -302,17 +303,20 @@ pub fn decodeZstandardFrameAlloc( &consumed_count, frame_context.block_size_max, ); - const written_slice = ring_buffer.sliceLast(written_size); - try result.appendSlice(written_slice.first); - try result.appendSlice(written_slice.second); - if (frame_context.hasher_opt) |*hasher| { - hasher.update(written_slice.first); - hasher.update(written_slice.second); + if (written_size > 0) { + const written_slice = ring_buffer.sliceLast(written_size); + try result.appendSlice(written_slice.first); + try result.appendSlice(written_slice.second); + if (frame_context.hasher_opt) |*hasher| { + hasher.update(written_slice.first); + hasher.update(written_slice.second); + } } if (block_header.last_block) break; } if (frame_context.has_checksum) { + if (src.len < consumed_count + 4) return error.EndOfStream; const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); consumed_count += 4; if (frame_context.hasher_opt) |*hasher| { From 1c509f483aef8b826f02ffc7ab8d1f2cfcec0d36 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Fri, 3 Feb 2023 15:35:30 +1100 Subject: [PATCH 041/122] std.compress.zstandard: fix crashes --- lib/std/compress/zstandard/decode/block.zig | 2 ++ lib/std/compress/zstandard/decode/huffman.zig | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index ab1d476dd6..8f97bea399 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -981,6 +981,8 @@ fn decodeStreams(size_format: u2, stream_data: []const u8) !LiteralsSection.Stre const stream_3_start = stream_2_start + stream_2_length; const stream_4_start = stream_3_start + stream_3_length; + if (stream_data.len < stream_4_start) return error.MalformedLiteralsSection; + return .{ .four = .{ stream_data[stream_1_start .. stream_1_start + stream_1_length], stream_data[stream_2_start .. stream_2_start + stream_2_length], diff --git a/lib/std/compress/zstandard/decode/huffman.zig b/lib/std/compress/zstandard/decode/huffman.zig index 01913c7044..f5639e7721 100644 --- a/lib/std/compress/zstandard/decode/huffman.zig +++ b/lib/std/compress/zstandard/decode/huffman.zig @@ -59,7 +59,7 @@ fn assignWeights(huff_bits: *readers.ReverseBitReader, accuracy_log: usize, entr var even_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; var odd_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; - while (i < 255) { + while (i < 254) { const even_data = entries[even_state]; var read_bits: usize = 0; const even_bits = huff_bits.readBits(u32, even_data.bits, &read_bits) catch unreachable; @@ -78,7 +78,7 @@ fn assignWeights(huff_bits: *readers.ReverseBitReader, accuracy_log: usize, entr weights[i] = std.math.cast(u4, odd_data.symbol) orelse return error.MalformedHuffmanTree; i += 1; if (read_bits < odd_data.bits) { - if (i == 256) return error.MalformedHuffmanTree; + if (i == 255) return error.MalformedHuffmanTree; weights[i] = std.math.cast(u4, entries[even_state].symbol) orelse return error.MalformedHuffmanTree; i += 1; break; @@ -147,16 +147,18 @@ fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.P } fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) error{MalformedHuffmanTree}!LiteralsSection.HuffmanTree { - var weight_power_sum: u16 = 0; + var weight_power_sum_big: u32 = 0; for (weights[0 .. symbol_count - 1]) |value| { if (value > 0) { - weight_power_sum += @as(u16, 1) << (value - 1); + weight_power_sum_big += @as(u16, 1) << (value - 1); } } - if (weight_power_sum >= 1 << 11) return error.MalformedHuffmanTree; + if (weight_power_sum_big >= 1 << 11) return error.MalformedHuffmanTree; + const weight_power_sum = @intCast(u16, weight_power_sum_big); // advance to next power of two (even if weight_power_sum is a power of 2) - const max_number_of_bits = std.math.log2_int(u16, weight_power_sum) + 1; + // TODO: is it valid to have weight_power_sum == 0? + const max_number_of_bits = if (weight_power_sum == 0) 1 else std.math.log2_int(u16, weight_power_sum) + 1; const next_power_of_two = @as(u16, 1) << max_number_of_bits; weights[symbol_count - 1] = std.math.log2_int(u16, next_power_of_two - weight_power_sum) + 1; From a625df463648b7f6ff2d15de8be5e168c8bc7363 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sat, 4 Feb 2023 11:39:48 +1100 Subject: [PATCH 042/122] std.compress.zstandard: fix fse decoding crash --- lib/std/compress/zstandard/decode/fse.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/compress/zstandard/decode/fse.zig b/lib/std/compress/zstandard/decode/fse.zig index 5f87c1f81b..726891873c 100644 --- a/lib/std/compress/zstandard/decode/fse.zig +++ b/lib/std/compress/zstandard/decode/fse.zig @@ -46,6 +46,7 @@ pub fn decodeFseTable( if (value == 1) { while (true) { const repeat_flag = try bit_reader.readBitsNoEof(u2, 2); + if (repeat_flag + value_count > 256) return error.MalformedFseTable; var i: usize = 0; while (i < repeat_flag) : (i += 1) { values[value_count] = 1; @@ -54,6 +55,7 @@ pub fn decodeFseTable( if (repeat_flag < 3) break; } } + if (value_count == 256) break; } bit_reader.alignToByte(); From 06ab5a2cd21ecd972477f067bb1d595a9ebef483 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sat, 4 Feb 2023 13:49:06 +1100 Subject: [PATCH 043/122] std.compress.zstandard: add multi-frame decoding functions --- lib/std/compress/zstandard/decompress.zig | 58 +++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index cf172a16ca..5b164ded35 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -54,6 +54,40 @@ const ReadWriteCount = struct { write_count: usize, }; +/// Decodes frames from `src` into `dest`; see `decodeFrame()`. +pub fn decode(dest: []u8, src: []const u8, verify_checksum: bool) !usize { + var write_count: usize = 0; + var read_count: usize = 0; + while (read_count < src.len) { + const counts = try decodeFrame(dest, src[read_count..], verify_checksum); + read_count += counts.read_count; + write_count += counts.write_count; + } + return write_count; +} + +pub fn decodeAlloc( + allocator: Allocator, + src: []const u8, + verify_checksum: bool, + window_size_max: usize, +) ![]u8 { + var result = std.ArrayList(u8).init(allocator); + errdefer result.deinit(); + + var read_count: usize = 0; + while (read_count < src.len) { + read_count += try decodeZstandardFrameArrayList( + allocator, + &result, + src[read_count..], + verify_checksum, + window_size_max, + ); + } + return result.toOwnedSlice(); +} + /// Decodes the frame at the start of `src` into `dest`. Returns the number of /// bytes read from `src` and written to `dest`. This function can only decode /// frames that declare the decompressed content size. @@ -268,6 +302,24 @@ pub fn decodeZstandardFrameAlloc( ) (error{OutOfMemory} || FrameContext.Error || FrameError)!DecodeResult { var result = std.ArrayList(u8).init(allocator); errdefer result.deinit(); + const read_count = try decodeZstandardFrameArrayList( + allocator, + &result, + src, + verify_checksum, + window_size_max, + ); + return DecodeResult{ .bytes = try result.toOwnedSlice(), .read_count = read_count }; +} + +/// Decode a ZStandard frame into `dest`; see `decodeZStandardFrameAlloc()`. +pub fn decodeZstandardFrameArrayList( + allocator: Allocator, + dest: *std.ArrayList(u8), + src: []const u8, + verify_checksum: bool, + window_size_max: usize, +) (error{OutOfMemory} || FrameContext.Error || FrameError)!usize { assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); var consumed_count: usize = 4; @@ -305,8 +357,8 @@ pub fn decodeZstandardFrameAlloc( ); if (written_size > 0) { const written_slice = ring_buffer.sliceLast(written_size); - try result.appendSlice(written_slice.first); - try result.appendSlice(written_slice.second); + try dest.appendSlice(written_slice.first); + try dest.appendSlice(written_slice.second); if (frame_context.hasher_opt) |*hasher| { hasher.update(written_slice.first); hasher.update(written_slice.second); @@ -323,7 +375,7 @@ pub fn decodeZstandardFrameAlloc( if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; } } - return DecodeResult{ .bytes = try result.toOwnedSlice(), .read_count = consumed_count }; + return consumed_count; } /// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. From a9c8376305d4c99591432e0ed267fad665bb4f5f Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sat, 4 Feb 2023 13:49:53 +1100 Subject: [PATCH 044/122] std.compress.zstandard: make ZstandardStream decode multiple frames --- lib/std/compress/zstandard.zig | 144 +++++++++++++++++++++------------ 1 file changed, 94 insertions(+), 50 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index 5b6f928db5..00f475df00 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -13,6 +13,7 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool allocator: Allocator, in_reader: ReaderType, + state: enum { NewFrame, InFrame }, decode_state: decompress.block.DecodeState, frame_context: decompress.FrameContext, buffer: RingBuffer, @@ -24,16 +25,43 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool sequence_buffer: []u8, checksum: if (verify_checksum) ?u32 else void, - pub const Error = ReaderType.Error || error{ MalformedBlock, MalformedFrame }; + pub const Error = ReaderType.Error || error{ ChecksumFailure, MalformedBlock, MalformedFrame, OutOfMemory }; pub const Reader = std.io.Reader(*Self, Error, read); pub fn init(allocator: Allocator, source: ReaderType) !Self { - switch (try decompress.decodeFrameType(source)) { - .skippable => return error.SkippableFrame, + return Self{ + .allocator = allocator, + .in_reader = source, + .state = .NewFrame, + .decode_state = undefined, + .frame_context = undefined, + .buffer = undefined, + .last_block = undefined, + .literal_fse_buffer = undefined, + .match_fse_buffer = undefined, + .offset_fse_buffer = undefined, + .literals_buffer = undefined, + .sequence_buffer = undefined, + .checksum = undefined, + }; + } + + fn frameInit(self: *Self) !void { + var bytes: [4]u8 = undefined; + const bytes_read = try self.in_reader.readAll(&bytes); + if (bytes_read == 0) return error.NoBytes; + if (bytes_read < 4) return error.EndOfStream; + const frame_type = try decompress.frameType(std.mem.readIntLittle(u32, &bytes)); + switch (frame_type) { + .skippable => { + const size = try self.in_reader.readIntLittle(u32); + try self.in_reader.skipBytes(size, .{}); + self.state = .NewFrame; + }, .zstandard => { const frame_context = context: { - const frame_header = try decompress.decodeZstandardHeader(source); + const frame_header = try decompress.decodeZstandardHeader(self.in_reader); break :context try decompress.FrameContext.init( frame_header, window_size_max, @@ -41,56 +69,58 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool ); }; - const literal_fse_buffer = try allocator.alloc( + const literal_fse_buffer = try self.allocator.alloc( types.compressed_block.Table.Fse, types.compressed_block.table_size_max.literal, ); - errdefer allocator.free(literal_fse_buffer); + errdefer self.allocator.free(literal_fse_buffer); - const match_fse_buffer = try allocator.alloc( + const match_fse_buffer = try self.allocator.alloc( types.compressed_block.Table.Fse, types.compressed_block.table_size_max.match, ); - errdefer allocator.free(match_fse_buffer); + errdefer self.allocator.free(match_fse_buffer); - const offset_fse_buffer = try allocator.alloc( + const offset_fse_buffer = try self.allocator.alloc( types.compressed_block.Table.Fse, types.compressed_block.table_size_max.offset, ); - errdefer allocator.free(offset_fse_buffer); + errdefer self.allocator.free(offset_fse_buffer); const decode_state = decompress.block.DecodeState.init( literal_fse_buffer, match_fse_buffer, offset_fse_buffer, ); - const buffer = try RingBuffer.init(allocator, frame_context.window_size); + const buffer = try RingBuffer.init(self.allocator, frame_context.window_size); - const literals_data = try allocator.alloc(u8, window_size_max); - errdefer allocator.free(literals_data); + const literals_data = try self.allocator.alloc(u8, window_size_max); + errdefer self.allocator.free(literals_data); - const sequence_data = try allocator.alloc(u8, window_size_max); - errdefer allocator.free(sequence_data); + const sequence_data = try self.allocator.alloc(u8, window_size_max); + errdefer self.allocator.free(sequence_data); - return Self{ - .allocator = allocator, - .in_reader = source, - .decode_state = decode_state, - .frame_context = frame_context, - .buffer = buffer, - .checksum = if (verify_checksum) null else {}, - .last_block = false, - .literal_fse_buffer = literal_fse_buffer, - .match_fse_buffer = match_fse_buffer, - .offset_fse_buffer = offset_fse_buffer, - .literals_buffer = literals_data, - .sequence_buffer = sequence_data, - }; + self.literal_fse_buffer = literal_fse_buffer; + self.match_fse_buffer = match_fse_buffer; + self.offset_fse_buffer = offset_fse_buffer; + self.literals_buffer = literals_data; + self.sequence_buffer = sequence_data; + + self.buffer = buffer; + + self.decode_state = decode_state; + self.frame_context = frame_context; + + self.checksum = if (verify_checksum) null else {}; + self.last_block = false; + + self.state = .InFrame; }, } } pub fn deinit(self: *Self) void { + if (self.state == .NewFrame) return; self.allocator.free(self.decode_state.literal_fse_buffer); self.allocator.free(self.decode_state.match_fse_buffer); self.allocator.free(self.decode_state.offset_fse_buffer); @@ -105,6 +135,19 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool pub fn read(self: *Self, buffer: []u8) Error!usize { if (buffer.len == 0) return 0; + while (self.state == .NewFrame) { + self.frameInit() catch |err| switch (err) { + error.NoBytes => return 0, + error.OutOfMemory => return error.OutOfMemory, + else => return error.MalformedFrame, + }; + } + + return self.readInner(buffer); + } + + fn readInner(self: *Self, buffer: []u8) Error!usize { + std.debug.assert(self.state == .InFrame); if (self.buffer.isEmpty() and !self.last_block) { const header_bytes = self.in_reader.readBytesNoEof(3) catch return error.MalformedFrame; @@ -127,9 +170,15 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool hasher.update(written_slice.first); hasher.update(written_slice.second); } - if (block_header.last_block and self.frame_context.has_checksum) { - const checksum = self.in_reader.readIntLittle(u32) catch return error.MalformedFrame; - if (verify_checksum) self.checksum = checksum; + if (block_header.last_block) { + if (self.frame_context.has_checksum) { + const checksum = self.in_reader.readIntLittle(u32) catch return error.MalformedFrame; + if (comptime verify_checksum) { + if (self.frame_context.hasher_opt) |*hasher| { + if (checksum != decompress.computeChecksum(hasher)) return error.ChecksumFailure; + } + } + } } } @@ -138,18 +187,16 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool while (written_count < decoded_data_len and written_count < buffer.len) : (written_count += 1) { buffer[written_count] = self.buffer.read().?; } - return written_count; - } - - pub fn verifyChecksum(self: *Self) !bool { - if (verify_checksum) { - if (self.checksum) |checksum| { - if (self.frame_context.hasher_opt) |*hasher| { - return checksum == decompress.computeChecksum(hasher); - } - } + if (self.buffer.len() == 0) { + self.state = .NewFrame; + self.allocator.free(self.literal_fse_buffer); + self.allocator.free(self.match_fse_buffer); + self.allocator.free(self.offset_fse_buffer); + self.allocator.free(self.literals_buffer); + self.allocator.free(self.sequence_buffer); + self.buffer.deinit(self.allocator); } - return true; + return written_count; } }; } @@ -163,7 +210,6 @@ fn testDecompress(data: []const u8) ![]u8 { var stream = try zstandardStream(std.testing.allocator, in_stream.reader()); defer stream.deinit(); const result = stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); - try std.testing.expect(try stream.verifyChecksum()); return result; } @@ -181,14 +227,12 @@ test "decompression" { var buffer = try std.testing.allocator.alloc(u8, uncompressed.len); defer std.testing.allocator.free(buffer); - const res3 = try decompress.decodeFrame(buffer, compressed3, true); - try std.testing.expectEqual(compressed3.len, res3.read_count); - try std.testing.expectEqual(uncompressed.len, res3.write_count); + const res3 = try decompress.decode(buffer, compressed3, true); + try std.testing.expectEqual(uncompressed.len, res3); try std.testing.expectEqualSlices(u8, uncompressed, buffer); - const res19 = try decompress.decodeFrame(buffer, compressed19, true); - try std.testing.expectEqual(compressed19.len, res19.read_count); - try std.testing.expectEqual(uncompressed.len, res19.write_count); + const res19 = try decompress.decode(buffer, compressed19, true); + try std.testing.expectEqual(uncompressed.len, res19); try std.testing.expectEqualSlices(u8, uncompressed, buffer); try testReader(compressed3, uncompressed); From ece52e0771560726a72a11cfafb59bb1dc9ad221 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 5 Feb 2023 22:27:00 +1100 Subject: [PATCH 045/122] std.compress.zstandard: verify content size and fix crash --- lib/std/compress/zstandard/decode/block.zig | 17 ++++- lib/std/compress/zstandard/decompress.zig | 76 +++++++++++++++------ 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 8f97bea399..4182996d43 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -334,6 +334,8 @@ pub const DecodeState = struct { /// mean the literal stream or the sequence is malformed). /// - `error.InvalidBitStream` if the FSE sequence bitstream is malformed /// - `error.EndOfStream` if `bit_reader` does not contain enough bits + /// - `error.DestTooSmall` if `dest` is not large enough to holde the + /// decompressed sequence pub fn decodeSequenceSlice( self: *DecodeState, dest: []u8, @@ -341,10 +343,11 @@ pub const DecodeState = struct { bit_reader: *readers.ReverseBitReader, sequence_size_limit: usize, last_sequence: bool, - ) DecodeSequenceError!usize { + ) (error{DestTooSmall} || DecodeSequenceError)!usize { const sequence = try self.nextSequence(bit_reader); const sequence_length = @as(usize, sequence.literal_length) + sequence.match_length; if (sequence_length > sequence_size_limit) return error.MalformedSequence; + if (sequence_length > dest[write_pos..].len) return error.DestTooSmall; try self.executeSequenceSlice(dest, write_pos, sequence); if (!last_sequence) { @@ -583,6 +586,8 @@ pub const DecodeState = struct { /// - `error.MalformedRleBlock` if the block is an RLE block and `src.len < 1` /// - `error.MalformedCompressedBlock` if there are errors decoding a /// compressed block +/// - `error.DestTooSmall` is `dest` is not large enough to hold the +/// decompressed block pub fn decodeBlock( dest: []u8, src: []const u8, @@ -590,13 +595,14 @@ pub fn decodeBlock( decode_state: *DecodeState, consumed_count: *usize, written_count: usize, -) Error!usize { +) (error{DestTooSmall} || Error)!usize { const block_size_max = @min(1 << 17, dest[written_count..].len); // 128KiB const block_size = block_header.block_size; if (block_size_max < block_size) return error.BlockSizeOverMaximum; switch (block_header.block_type) { .raw => { if (src.len < block_size) return error.MalformedBlockSize; + if (dest[written_count..].len < block_size) return error.DestTooSmall; const data = src[0..block_size]; std.mem.copy(u8, dest[written_count..], data); consumed_count.* += block_size; @@ -604,6 +610,7 @@ pub fn decodeBlock( }, .rle => { if (src.len < 1) return error.MalformedRleBlock; + if (dest[written_count..].len < block_size) return error.DestTooSmall; var write_pos: usize = written_count; while (write_pos < block_size + written_count) : (write_pos += 1) { dest[write_pos] = src[0]; @@ -644,7 +651,10 @@ pub fn decodeBlock( &bit_stream, sequence_size_limit, i == sequences_header.sequence_count - 1, - ) catch return error.MalformedCompressedBlock; + ) catch |err| switch (err) { + error.DestTooSmall => return error.DestTooSmall, + else => return error.MalformedCompressedBlock, + }; bytes_written += decompressed_size; sequence_size_limit -= decompressed_size; } @@ -655,6 +665,7 @@ pub fn decodeBlock( if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; + if (len > dest[written_count + bytes_written ..].len) return error.DestTooSmall; decode_state.decodeLiteralsSlice(dest[written_count + bytes_written ..], len) catch return error.MalformedCompressedBlock; bytes_written += len; diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 5b164ded35..73e9196657 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -96,6 +96,7 @@ pub fn decodeAlloc( /// - `error.UnknownContentSizeUnsupported` if the frame does not declare the /// uncompressed content size /// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data +/// size declared by the frame header /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic /// number for a Zstandard or Skippable frame /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary @@ -180,6 +181,7 @@ pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { const FrameError = error{ DictionaryIdFlagUnsupported, ChecksumFailure, + BadContentSize, EndOfStream, } || InvalidBit || block.Error; @@ -191,7 +193,7 @@ const FrameError = error{ /// - `error.UnknownContentSizeUnsupported` if the frame does not declare the /// uncompressed content size /// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data -/// number for a Zstandard or Skippable frame +/// size declared by the frame header /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame /// contains a checksum that does not match the checksum of the decompressed @@ -200,39 +202,51 @@ const FrameError = error{ /// - `error.UnusedBitSet` if the unused bit of the frame header is set /// - `error.EndOfStream` if `src` does not contain a complete frame /// - an error in `block.Error` if there are errors decoding a block +/// - `error.BadContentSize` if the content size declared by the frame does +/// not equal the actual size of decompressed data pub fn decodeZstandardFrame( dest: []u8, src: []const u8, verify_checksum: bool, -) (error{ UnknownContentSizeUnsupported, ContentTooLarge } || FrameError)!ReadWriteCount { +) (error{ + UnknownContentSizeUnsupported, + ContentTooLarge, + ContentSizeTooLarge, + WindowSizeUnknown, +} || FrameError)!ReadWriteCount { assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); var consumed_count: usize = 4; - var fbs = std.io.fixedBufferStream(src[consumed_count..]); - var source = fbs.reader(); - const frame_header = try decodeZstandardHeader(source); - consumed_count += fbs.pos; + var frame_context = context: { + var fbs = std.io.fixedBufferStream(src[consumed_count..]); + var source = fbs.reader(); + const frame_header = try decodeZstandardHeader(source); + consumed_count += fbs.pos; + break :context FrameContext.init(frame_header, std.math.maxInt(usize), verify_checksum) catch |err| switch (err) { + error.WindowTooLarge => unreachable, + inline else => |e| return e, + }; + }; - if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; - - const content_size = frame_header.content_size orelse return error.UnknownContentSizeUnsupported; + const content_size = frame_context.content_size orelse return error.UnknownContentSizeUnsupported; if (dest.len < content_size) return error.ContentTooLarge; - const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; - var hasher_opt = if (should_compute_checksum) std.hash.XxHash64.init(0) else null; - - const written_count = try decodeFrameBlocks( - dest, + const written_count = decodeFrameBlocks( + dest[0..content_size], src[consumed_count..], &consumed_count, - if (hasher_opt) |*hasher| hasher else null, - ); + if (frame_context.hasher_opt) |*hasher| hasher else null, + ) catch |err| switch (err) { + error.DestTooSmall => return error.BadContentSize, + inline else => |e| return e, + }; - if (frame_header.descriptor.content_checksum_flag) { + if (written_count != content_size) return error.BadContentSize; + if (frame_context.has_checksum) { if (src.len < consumed_count + 4) return error.EndOfStream; const checksum = readIntSlice(u32, src[consumed_count .. consumed_count + 4]); consumed_count += 4; - if (hasher_opt) |*hasher| { + if (frame_context.hasher_opt) |*hasher| { if (checksum != computeChecksum(hasher)) return error.ChecksumFailure; } } @@ -244,8 +258,14 @@ pub const FrameContext = struct { window_size: usize, has_checksum: bool, block_size_max: usize, + content_size: ?usize, - const Error = error{ DictionaryIdFlagUnsupported, WindowSizeUnknown, WindowTooLarge }; + const Error = error{ + DictionaryIdFlagUnsupported, + WindowSizeUnknown, + WindowTooLarge, + ContentSizeTooLarge, + }; /// Validates `frame_header` and returns the associated `FrameContext`. /// /// Errors returned: @@ -266,11 +286,18 @@ pub const FrameContext = struct { @intCast(usize, window_size_raw); const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; + + const content_size = if (frame_header.content_size) |size| + std.math.cast(usize, size) orelse return error.ContentSizeTooLarge + else + null; + return .{ .hasher_opt = if (should_compute_checksum) std.hash.XxHash64.init(0) else null, .window_size = window_size, .has_checksum = frame_header.descriptor.content_checksum_flag, .block_size_max = @min(1 << 17, window_size), + .content_size = content_size, }; } }; @@ -294,6 +321,8 @@ pub const FrameContext = struct { /// - `error.EndOfStream` if `src` does not contain a complete frame /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory /// - an error in `block.Error` if there are errors decoding a block +/// - `error.BadContentSize` if the content size declared by the frame does +/// not equal the size of decompressed data pub fn decodeZstandardFrameAlloc( allocator: Allocator, src: []const u8, @@ -321,6 +350,7 @@ pub fn decodeZstandardFrameArrayList( window_size_max: usize, ) (error{OutOfMemory} || FrameContext.Error || FrameError)!usize { assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); + const initial_len = dest.items.len; var consumed_count: usize = 4; var frame_context = context: { @@ -364,6 +394,12 @@ pub fn decodeZstandardFrameArrayList( hasher.update(written_slice.second); } } + const added_len = dest.items.len - initial_len; + if (frame_context.content_size) |size| { + if (added_len != size) { + return error.BadContentSize; + } + } if (block_header.last_block) break; } @@ -384,7 +420,7 @@ fn decodeFrameBlocks( src: []const u8, consumed_count: *usize, hash: ?*std.hash.XxHash64, -) (error{EndOfStream} || block.Error)!usize { +) (error{ EndOfStream, DestTooSmall } || block.Error)!usize { // These tables take 7680 bytes var literal_fse_data: [types.compressed_block.table_size_max.literal]Table.Fse = undefined; var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; From 98bbd959b08db4d66a9ae68ce3fe4196594c6d9e Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 6 Feb 2023 13:19:24 +1100 Subject: [PATCH 046/122] std.compress.zstandard: improve block size validation --- lib/std/compress/zstandard/decode/block.zig | 6 +++++- lib/std/compress/zstandard/decompress.zig | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 4182996d43..8be072af1c 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -594,9 +594,9 @@ pub fn decodeBlock( block_header: frame.Zstandard.Block.Header, decode_state: *DecodeState, consumed_count: *usize, + block_size_max: usize, written_count: usize, ) (error{DestTooSmall} || Error)!usize { - const block_size_max = @min(1 << 17, dest[written_count..].len); // 128KiB const block_size = block_header.block_size; if (block_size_max < block_size) return error.BlockSizeOverMaximum; switch (block_header.block_type) { @@ -805,6 +805,7 @@ pub fn decodeBlockReader( try decode_state.prepare(block_reader, literals, sequences_header); + var bytes_written: usize = 0; if (sequences_header.sequence_count > 0) { if (sequence_buffer.len < block_reader_limited.bytes_left) return error.SequenceBufferTooSmall; @@ -825,6 +826,7 @@ pub fn decodeBlockReader( i == sequences_header.sequence_count - 1, ) catch return error.MalformedCompressedBlock; sequence_size_limit -= decompressed_size; + bytes_written += decompressed_size; } } @@ -832,8 +834,10 @@ pub fn decodeBlockReader( const len = literals.header.regenerated_size - decode_state.literal_written_count; decode_state.decodeLiteralsRingBuffer(dest, len) catch return error.MalformedCompressedBlock; + bytes_written += len; } + if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; decode_state.literal_written_count = 0; assert(block_reader.readByte() == error.EndOfStream); }, diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 73e9196657..c7707c52a7 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -236,6 +236,7 @@ pub fn decodeZstandardFrame( src[consumed_count..], &consumed_count, if (frame_context.hasher_opt) |*hasher| hasher else null, + frame_context.block_size_max, ) catch |err| switch (err) { error.DestTooSmall => return error.BadContentSize, inline else => |e| return e, @@ -376,7 +377,6 @@ pub fn decodeZstandardFrameArrayList( block_header = try block.decodeBlockHeaderSlice(src[consumed_count..]); consumed_count += 3; }) { - if (block_header.block_size > frame_context.block_size_max) return error.BlockSizeOverMaximum; const written_size = try block.decodeBlockRingBuffer( &ring_buffer, src[consumed_count..], @@ -420,6 +420,7 @@ fn decodeFrameBlocks( src: []const u8, consumed_count: *usize, hash: ?*std.hash.XxHash64, + block_size_max: usize, ) (error{ EndOfStream, DestTooSmall } || block.Error)!usize { // These tables take 7680 bytes var literal_fse_data: [types.compressed_block.table_size_max.literal]Table.Fse = undefined; @@ -441,6 +442,7 @@ fn decodeFrameBlocks( block_header, &decode_state, &bytes_read, + block_size_max, written_count, ); if (hash) |hash_state| hash_state.update(dest[written_count .. written_count + written_size]); From 2134769436f68547a5c1d93184a10fab03085b2a Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 6 Feb 2023 13:20:23 +1100 Subject: [PATCH 047/122] std.compress.zstandard: validate skippable frame size --- lib/std/compress/zstandard/decompress.zig | 44 +++++++++++++++-------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index c7707c52a7..4d9b53aa3b 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -107,19 +107,27 @@ pub fn decodeAlloc( /// - `error.UnusedBitSet` if the unused bit of the frame header is set /// - `error.EndOfStream` if `src` does not contain a complete frame /// - an error in `block.Error` if there are errors decoding a block +/// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a +/// size greater than `src.len` pub fn decodeFrame( dest: []u8, src: []const u8, verify_checksum: bool, ) !ReadWriteCount { var fbs = std.io.fixedBufferStream(src); - return switch (try decodeFrameType(fbs.reader())) { - .zstandard => decodeZstandardFrame(dest, src, verify_checksum), - .skippable => ReadWriteCount{ - .read_count = try fbs.reader().readIntLittle(u32) + 8, - .write_count = 0, + switch (try decodeFrameType(fbs.reader())) { + .zstandard => return decodeZstandardFrame(dest, src, verify_checksum), + .skippable => { + const content_size = try fbs.reader().readIntLittle(u32); + if (content_size > std.math.maxInt(usize) - 8) return error.SkippableSizeTooLarge; + const read_count = @as(usize, content_size) + 8; + if (read_count > src.len) return error.SkippableSizeTooLarge; + return ReadWriteCount{ + .read_count = read_count, + .write_count = 0, + }; }, - }; + } } pub const DecodeResult = struct { @@ -150,6 +158,8 @@ pub const DecodedFrame = union(enum) { /// - `error.EndOfStream` if `src` does not contain a complete frame /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory /// - an error in `block.Error` if there are errors decoding a block +/// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a +/// size greater than `src.len` pub fn decodeFrameAlloc( allocator: Allocator, src: []const u8, @@ -159,17 +169,23 @@ pub fn decodeFrameAlloc( var fbs = std.io.fixedBufferStream(src); const reader = fbs.reader(); const magic = try reader.readIntLittle(u32); - return switch (try frameType(magic)) { - .zstandard => .{ + switch (try frameType(magic)) { + .zstandard => return .{ .zstandard = try decodeZstandardFrameAlloc(allocator, src, verify_checksum, window_size_max), }, - .skippable => .{ - .skippable = .{ - .magic_number = magic, - .frame_size = try reader.readIntLittle(u32), - }, + .skippable => { + const content_size = try fbs.reader().readIntLittle(u32); + if (content_size > std.math.maxInt(usize) - 8) return error.SkippableSizeTooLarge; + const read_count = @as(usize, content_size) + 8; + if (read_count > src.len) return error.SkippableSizeTooLarge; + return .{ + .skippable = .{ + .magic_number = magic, + .frame_size = content_size, + }, + }; }, - }; + } } /// Returns the frame checksum corresponding to the data fed into `hasher` From d9a90e181873d06b9e8632a86ff8c399e024974e Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Wed, 8 Feb 2023 00:28:06 +1100 Subject: [PATCH 048/122] std.compress.zstandard: fix decodeAlloc() and remove decodeFrameAlloc() --- lib/std/compress/zstandard/decompress.zig | 48 +++++------------------ 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 4d9b53aa3b..4ac8fc9c67 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -77,7 +77,7 @@ pub fn decodeAlloc( var read_count: usize = 0; while (read_count < src.len) { - read_count += try decodeZstandardFrameArrayList( + read_count += try decodeFrameArrayList( allocator, &result, src[read_count..], @@ -140,8 +140,7 @@ pub const DecodedFrame = union(enum) { }; /// Decodes the frame at the start of `src` into `dest`. Returns the number of -/// bytes read from `src` and the decoded bytes for a Zstandard frame, or the -/// frame header for a Skippable frame. +/// bytes read from `src`. /// /// Errors returned: /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic @@ -160,30 +159,24 @@ pub const DecodedFrame = union(enum) { /// - an error in `block.Error` if there are errors decoding a block /// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a /// size greater than `src.len` -pub fn decodeFrameAlloc( +pub fn decodeFrameArrayList( allocator: Allocator, + dest: *std.ArrayList(u8), src: []const u8, verify_checksum: bool, window_size_max: usize, -) !DecodedFrame { +) !usize { var fbs = std.io.fixedBufferStream(src); const reader = fbs.reader(); const magic = try reader.readIntLittle(u32); switch (try frameType(magic)) { - .zstandard => return .{ - .zstandard = try decodeZstandardFrameAlloc(allocator, src, verify_checksum, window_size_max), - }, + .zstandard => return decodeZstandardFrameArrayList(allocator, dest, src, verify_checksum, window_size_max), .skippable => { const content_size = try fbs.reader().readIntLittle(u32); if (content_size > std.math.maxInt(usize) - 8) return error.SkippableSizeTooLarge; const read_count = @as(usize, content_size) + 8; if (read_count > src.len) return error.SkippableSizeTooLarge; - return .{ - .skippable = .{ - .magic_number = magic, - .frame_size = content_size, - }, - }; + return read_count; }, } } @@ -319,11 +312,9 @@ pub const FrameContext = struct { } }; -/// Decode a Zstandard from from `src` and return the decompressed bytes and the -/// number of bytes read; see `decodeZstandardFrame()`. `allocator` is used to -/// allocate both the returned slice and internal buffers used during decoding. -/// The first four bytes of `src` must be the magic number for a Zstandard -/// frame. +/// Decode a Zstandard from from `src` and return number of bytes read; see +/// `decodeZstandardFrame()`. The first four bytes of `src` must be the magic +/// number for a Zstandard frame. /// /// Errors returned: /// - `error.WindowSizeUnknown` if the frame does not have a valid window size @@ -340,25 +331,6 @@ pub const FrameContext = struct { /// - an error in `block.Error` if there are errors decoding a block /// - `error.BadContentSize` if the content size declared by the frame does /// not equal the size of decompressed data -pub fn decodeZstandardFrameAlloc( - allocator: Allocator, - src: []const u8, - verify_checksum: bool, - window_size_max: usize, -) (error{OutOfMemory} || FrameContext.Error || FrameError)!DecodeResult { - var result = std.ArrayList(u8).init(allocator); - errdefer result.deinit(); - const read_count = try decodeZstandardFrameArrayList( - allocator, - &result, - src, - verify_checksum, - window_size_max, - ); - return DecodeResult{ .bytes = try result.toOwnedSlice(), .read_count = read_count }; -} - -/// Decode a ZStandard frame into `dest`; see `decodeZStandardFrameAlloc()`. pub fn decodeZstandardFrameArrayList( allocator: Allocator, dest: *std.ArrayList(u8), From 77ca1f7859f6b02399bb52f7d66becc3867036c6 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Wed, 8 Feb 2023 01:07:47 +1100 Subject: [PATCH 049/122] std.compress.zstandard: remove UnusedBitSet error --- lib/std/compress/zstandard/decompress.zig | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 4ac8fc9c67..a5ad3c5e2a 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -104,7 +104,6 @@ pub fn decodeAlloc( /// contains a checksum that does not match the checksum of the decompressed /// data /// - `error.ReservedBitSet` if the reserved bit of the frame header is set -/// - `error.UnusedBitSet` if the unused bit of the frame header is set /// - `error.EndOfStream` if `src` does not contain a complete frame /// - an error in `block.Error` if there are errors decoding a block /// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a @@ -153,7 +152,6 @@ pub const DecodedFrame = union(enum) { /// contains a checksum that does not match the checksum of the decompressed /// data /// - `error.ReservedBitSet` if the reserved bit of the frame header is set -/// - `error.UnusedBitSet` if the unused bit of the frame header is set /// - `error.EndOfStream` if `src` does not contain a complete frame /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory /// - an error in `block.Error` if there are errors decoding a block @@ -192,7 +190,8 @@ const FrameError = error{ ChecksumFailure, BadContentSize, EndOfStream, -} || InvalidBit || block.Error; + ReservedBitSet, +} || block.Error; /// Decode a Zstandard frame from `src` into `dest`, returning the number of /// bytes read from `src` and written to `dest`. The first four bytes of `src` @@ -208,7 +207,6 @@ const FrameError = error{ /// contains a checksum that does not match the checksum of the decompressed /// data /// - `error.ReservedBitSet` if the reserved bit of the frame header is set -/// - `error.UnusedBitSet` if the unused bit of the frame header is set /// - `error.EndOfStream` if `src` does not contain a complete frame /// - an error in `block.Error` if there are errors decoding a block /// - `error.BadContentSize` if the content size declared by the frame does @@ -325,7 +323,6 @@ pub const FrameContext = struct { /// contains a checksum that does not match the checksum of the decompressed /// data /// - `error.ReservedBitSet` if the reserved bit of the frame header is set -/// - `error.UnusedBitSet` if the unused bit of the frame header is set /// - `error.EndOfStream` if `src` does not contain a complete frame /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory /// - an error in `block.Error` if there are errors decoding a block @@ -465,17 +462,14 @@ pub fn frameWindowSize(header: frame.Zstandard.Header) ?u64 { } else return header.content_size; } -const InvalidBit = error{ UnusedBitSet, ReservedBitSet }; /// Decode the header of a Zstandard frame. /// /// Errors returned: -/// - `error.UnusedBitSet` if the unused bits of the header are set /// - `error.ReservedBitSet` if the reserved bits of the header are set /// - `error.EndOfStream` if `source` does not contain a complete header -pub fn decodeZstandardHeader(source: anytype) (error{EndOfStream} || InvalidBit)!frame.Zstandard.Header { +pub fn decodeZstandardHeader(source: anytype) error{ EndOfStream, ReservedBitSet }!frame.Zstandard.Header { const descriptor = @bitCast(frame.Zstandard.Header.Descriptor, try source.readByte()); - if (descriptor.unused) return error.UnusedBitSet; if (descriptor.reserved) return error.ReservedBitSet; var window_descriptor: ?u8 = null; From 3975a9d7ca2013a4ac667b637fb273a9432c53aa Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 9 Feb 2023 17:51:43 +1100 Subject: [PATCH 050/122] std.compress.zstandard: error when FSE bitstream is no fully consumed --- lib/std/compress/zstandard/decode/block.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 8be072af1c..2503180023 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -659,6 +659,10 @@ pub fn decodeBlock( sequence_size_limit -= decompressed_size; } + if (bit_stream.bit_reader.bit_count != 0) { + return error.MalformedCompressedBlock; + } + bytes_read += bit_stream_bytes.len; } if (bytes_read != block_size) return error.MalformedCompressedBlock; @@ -745,6 +749,10 @@ pub fn decodeBlockRingBuffer( sequence_size_limit -= decompressed_size; } + if (bit_stream.bit_reader.bit_count != 0) { + return error.MalformedCompressedBlock; + } + bytes_read += bit_stream_bytes.len; } if (bytes_read != block_size) return error.MalformedCompressedBlock; @@ -828,6 +836,9 @@ pub fn decodeBlockReader( sequence_size_limit -= decompressed_size; bytes_written += decompressed_size; } + if (bit_stream.bit_reader.bit_count != 0) { + return error.MalformedCompressedBlock; + } } if (decode_state.literal_written_count < literals.header.regenerated_size) { From 6d48b055af0cbe17e72426f808fa8408fce3eed6 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 9 Feb 2023 17:52:21 +1100 Subject: [PATCH 051/122] std.compress.zstandard: add decodeFrameHeader Also do some other minor API cleanup --- lib/std/compress/zstandard/decompress.zig | 77 ++++++++++++++++++----- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index a5ad3c5e2a..fd783e3c9c 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -49,6 +49,25 @@ pub fn frameType(magic: u32) error{BadMagic}!frame.Kind { error.BadMagic; } +pub const FrameHeader = union(enum) { + zstandard: types.frame.Zstandard.Header, + skippable: types.frame.Skippable.Header, +}; + +pub fn decodeFrameHeader(source: anytype) error{ BadMagic, EndOfStream, ReservedBitSet }!FrameHeader { + const magic = try source.readIntLittle(u32); + const frame_type = try frameType(magic); + switch (frame_type) { + .zstandard => return FrameHeader{ .zstandard = try decodeZstandardHeader(source) }, + .skippable => return FrameHeader{ + .skippable = .{ + .magic_number = magic, + .frame_size = try source.readIntLittle(u32), + }, + }, + } +} + const ReadWriteCount = struct { read_count: usize, write_count: usize, @@ -129,15 +148,6 @@ pub fn decodeFrame( } } -pub const DecodeResult = struct { - bytes: []u8, - read_count: usize, -}; -pub const DecodedFrame = union(enum) { - zstandard: DecodeResult, - skippable: frame.Skippable.Header, -}; - /// Decodes the frame at the start of `src` into `dest`. Returns the number of /// bytes read from `src`. /// @@ -186,7 +196,6 @@ pub fn computeChecksum(hasher: *std.hash.XxHash64) u32 { } const FrameError = error{ - DictionaryIdFlagUnsupported, ChecksumFailure, BadContentSize, EndOfStream, @@ -220,6 +229,7 @@ pub fn decodeZstandardFrame( ContentTooLarge, ContentSizeTooLarge, WindowSizeUnknown, + DictionaryIdFlagUnsupported, } || FrameError)!ReadWriteCount { assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); var consumed_count: usize = 4; @@ -234,11 +244,27 @@ pub fn decodeZstandardFrame( inline else => |e| return e, }; }; + const counts = try decodeZStandardFrameBlocks( + dest, + src[consumed_count..], + &frame_context, + ); + return ReadWriteCount{ + .read_count = counts.read_count + consumed_count, + .write_count = counts.write_count, + }; +} +pub fn decodeZStandardFrameBlocks( + dest: []u8, + src: []const u8, + frame_context: *FrameContext, +) (error{ ContentTooLarge, UnknownContentSizeUnsupported } || FrameError)!ReadWriteCount { const content_size = frame_context.content_size orelse return error.UnknownContentSizeUnsupported; if (dest.len < content_size) return error.ContentTooLarge; - const written_count = decodeFrameBlocks( + var consumed_count: usize = 0; + const written_count = decodeFrameBlocksInner( dest[0..content_size], src[consumed_count..], &consumed_count, @@ -279,7 +305,7 @@ pub const FrameContext = struct { /// Errors returned: /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary /// - `error.WindowSizeUnknown` if the frame does not have a valid window size - /// - `error.WindowTooLarge` if the window size is larger than + /// - `error.WindowTooLarge` if the window size is larger than `window_size_max` pub fn init( frame_header: frame.Zstandard.Header, window_size_max: usize, @@ -336,7 +362,6 @@ pub fn decodeZstandardFrameArrayList( window_size_max: usize, ) (error{OutOfMemory} || FrameContext.Error || FrameError)!usize { assert(readInt(u32, src[0..4]) == frame.Zstandard.magic_number); - const initial_len = dest.items.len; var consumed_count: usize = 4; var frame_context = context: { @@ -347,6 +372,23 @@ pub fn decodeZstandardFrameArrayList( break :context try FrameContext.init(frame_header, window_size_max, verify_checksum); }; + consumed_count += try decodeZstandardFrameBlocksArrayList( + allocator, + dest, + src[consumed_count..], + &frame_context, + ); + return consumed_count; +} + +pub fn decodeZstandardFrameBlocksArrayList( + allocator: Allocator, + dest: *std.ArrayList(u8), + src: []const u8, + frame_context: *FrameContext, +) (error{OutOfMemory} || FrameError)!usize { + const initial_len = dest.items.len; + var ring_buffer = try RingBuffer.init(allocator, frame_context.window_size); defer ring_buffer.deinit(allocator); @@ -355,8 +397,8 @@ pub fn decodeZstandardFrameArrayList( var match_fse_data: [types.compressed_block.table_size_max.match]Table.Fse = undefined; var offset_fse_data: [types.compressed_block.table_size_max.offset]Table.Fse = undefined; - var block_header = try block.decodeBlockHeaderSlice(src[consumed_count..]); - consumed_count += 3; + var block_header = try block.decodeBlockHeaderSlice(src); + var consumed_count: usize = 3; var decode_state = block.DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); while (true) : ({ block_header = try block.decodeBlockHeaderSlice(src[consumed_count..]); @@ -399,8 +441,9 @@ pub fn decodeZstandardFrameArrayList( return consumed_count; } -/// Convenience wrapper for decoding all blocks in a frame; see `decodeBlock()`. -fn decodeFrameBlocks( +/// Convenience wrapper for decoding all blocks in a frame; see +/// `decodeZStandardFrameBlocks()`. +fn decodeFrameBlocksInner( dest: []u8, src: []const u8, consumed_count: *usize, From 55e6e9409c3c94cca6eb144388104cb03247b045 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 9 Feb 2023 18:52:48 +1100 Subject: [PATCH 052/122] std.compress.zstandard: fix content size check --- lib/std/compress/zstandard/decompress.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index fd783e3c9c..68d43c8aeb 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -421,14 +421,14 @@ pub fn decodeZstandardFrameBlocksArrayList( hasher.update(written_slice.second); } } - const added_len = dest.items.len - initial_len; - if (frame_context.content_size) |size| { - if (added_len != size) { - return error.BadContentSize; - } - } if (block_header.last_block) break; } + const added_len = dest.items.len - initial_len; + if (frame_context.content_size) |size| { + if (added_len != size) { + return error.BadContentSize; + } + } if (frame_context.has_checksum) { if (src.len < consumed_count + 4) return error.EndOfStream; From 31cc4605aba68edc44a31f8383eaa39a906d6ec8 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 9 Feb 2023 20:15:00 +1100 Subject: [PATCH 053/122] std.compress.zstandard: fix errors and crashes in ZstandardStream --- lib/std/compress/zstandard.zig | 59 ++++++++++----------- lib/std/compress/zstandard/decode/block.zig | 1 + 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index 00f475df00..72ed40e03d 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -12,12 +12,11 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool const Self = @This(); allocator: Allocator, - in_reader: ReaderType, - state: enum { NewFrame, InFrame }, + source: std.io.CountingReader(ReaderType), + state: enum { NewFrame, InFrame, LastBlock }, decode_state: decompress.block.DecodeState, frame_context: decompress.FrameContext, buffer: RingBuffer, - last_block: bool, literal_fse_buffer: []types.compressed_block.Table.Fse, match_fse_buffer: []types.compressed_block.Table.Fse, offset_fse_buffer: []types.compressed_block.Table.Fse, @@ -32,12 +31,11 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool pub fn init(allocator: Allocator, source: ReaderType) !Self { return Self{ .allocator = allocator, - .in_reader = source, + .source = std.io.countingReader(source), .state = .NewFrame, .decode_state = undefined, .frame_context = undefined, .buffer = undefined, - .last_block = undefined, .literal_fse_buffer = undefined, .match_fse_buffer = undefined, .offset_fse_buffer = undefined, @@ -48,22 +46,16 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool } fn frameInit(self: *Self) !void { - var bytes: [4]u8 = undefined; - const bytes_read = try self.in_reader.readAll(&bytes); - if (bytes_read == 0) return error.NoBytes; - if (bytes_read < 4) return error.EndOfStream; - const frame_type = try decompress.frameType(std.mem.readIntLittle(u32, &bytes)); - switch (frame_type) { - .skippable => { - const size = try self.in_reader.readIntLittle(u32); - try self.in_reader.skipBytes(size, .{}); + const source_reader = self.source.reader(); + switch (try decompress.decodeFrameHeader(source_reader)) { + .skippable => |header| { + try source_reader.skipBytes(header.frame_size, .{}); self.state = .NewFrame; }, - .zstandard => { + .zstandard => |header| { const frame_context = context: { - const frame_header = try decompress.decodeZstandardHeader(self.in_reader); break :context try decompress.FrameContext.init( - frame_header, + header, window_size_max, verify_checksum, ); @@ -112,7 +104,6 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool self.frame_context = frame_context; self.checksum = if (verify_checksum) null else {}; - self.last_block = false; self.state = .InFrame; }, @@ -134,10 +125,14 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool } pub fn read(self: *Self, buffer: []u8) Error!usize { + const initial_count = self.source.bytes_read; if (buffer.len == 0) return 0; while (self.state == .NewFrame) { self.frameInit() catch |err| switch (err) { - error.NoBytes => return 0, + error.EndOfStream => return if (self.source.bytes_read == initial_count) + 0 + else + error.MalformedFrame, error.OutOfMemory => return error.OutOfMemory, else => return error.MalformedFrame, }; @@ -147,15 +142,16 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool } fn readInner(self: *Self, buffer: []u8) Error!usize { - std.debug.assert(self.state == .InFrame); + std.debug.assert(self.state != .NewFrame); - if (self.buffer.isEmpty() and !self.last_block) { - const header_bytes = self.in_reader.readBytesNoEof(3) catch return error.MalformedFrame; + const source_reader = self.source.reader(); + while (self.buffer.isEmpty() and self.state != .LastBlock) { + const header_bytes = source_reader.readBytesNoEof(3) catch return error.MalformedFrame; const block_header = decompress.block.decodeBlockHeader(&header_bytes); decompress.block.decodeBlockReader( &self.buffer, - self.in_reader, + source_reader, block_header, &self.decode_state, self.frame_context.block_size_max, @@ -164,15 +160,18 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool ) catch return error.MalformedBlock; - self.last_block = block_header.last_block; if (self.frame_context.hasher_opt) |*hasher| { - const written_slice = self.buffer.sliceLast(self.buffer.len()); - hasher.update(written_slice.first); - hasher.update(written_slice.second); + const size = self.buffer.len(); + if (size > 0) { + const written_slice = self.buffer.sliceLast(size); + hasher.update(written_slice.first); + hasher.update(written_slice.second); + } } if (block_header.last_block) { + self.state = .LastBlock; if (self.frame_context.has_checksum) { - const checksum = self.in_reader.readIntLittle(u32) catch return error.MalformedFrame; + const checksum = source_reader.readIntLittle(u32) catch return error.MalformedFrame; if (comptime verify_checksum) { if (self.frame_context.hasher_opt) |*hasher| { if (checksum != decompress.computeChecksum(hasher)) return error.ChecksumFailure; @@ -187,7 +186,7 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool while (written_count < decoded_data_len and written_count < buffer.len) : (written_count += 1) { buffer[written_count] = self.buffer.read().?; } - if (self.buffer.len() == 0) { + if (self.state == .LastBlock and self.buffer.len() == 0) { self.state = .NewFrame; self.allocator.free(self.literal_fse_buffer); self.allocator.free(self.match_fse_buffer); @@ -219,7 +218,7 @@ fn testReader(data: []const u8, comptime expected: []const u8) !void { try std.testing.expectEqualSlices(u8, expected, buf); } -test "decompression" { +test "zstandard decompression" { const uncompressed = @embedFile("testdata/rfc8478.txt"); const compressed3 = @embedFile("testdata/rfc8478.txt.zst.3"); const compressed19 = @embedFile("testdata/rfc8478.txt.zst.19"); diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 2503180023..af87ec694f 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -795,6 +795,7 @@ pub fn decodeBlockReader( if (block_size_max < block_size) return error.BlockSizeOverMaximum; switch (block_header.block_type) { .raw => { + if (block_size == 0) return; const slice = dest.sliceAt(dest.write_index, block_size); try source.readNoEof(slice.first); try source.readNoEof(slice.second); From ee5af3c74c27ebf366ef51486119487650f80468 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Fri, 10 Feb 2023 01:33:38 +1100 Subject: [PATCH 054/122] std.compress.zstandard: cleanup high-level api docs and error sets --- lib/std/compress/zstandard.zig | 40 +++-- lib/std/compress/zstandard/RingBuffer.zig | 9 +- lib/std/compress/zstandard/decode/block.zig | 6 +- lib/std/compress/zstandard/decompress.zig | 169 +++++++++++++++----- lib/std/compress/zstandard/types.zig | 18 ++- 5 files changed, 173 insertions(+), 69 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index 72ed40e03d..afc542478b 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -7,7 +7,11 @@ const RingBuffer = @import("zstandard/RingBuffer.zig"); pub const decompress = @import("zstandard/decompress.zig"); pub usingnamespace @import("zstandard/types.zig"); -pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool, comptime window_size_max: usize) type { +pub fn ZstandardStream( + comptime ReaderType: type, + comptime verify_checksum: bool, + comptime window_size_max: usize, +) type { return struct { const Self = @This(); @@ -24,11 +28,16 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool sequence_buffer: []u8, checksum: if (verify_checksum) ?u32 else void, - pub const Error = ReaderType.Error || error{ ChecksumFailure, MalformedBlock, MalformedFrame, OutOfMemory }; + pub const Error = ReaderType.Error || error{ + ChecksumFailure, + MalformedBlock, + MalformedFrame, + OutOfMemory, + }; pub const Reader = std.io.Reader(*Self, Error, read); - pub fn init(allocator: Allocator, source: ReaderType) !Self { + pub fn init(allocator: Allocator, source: ReaderType) Self { return Self{ .allocator = allocator, .source = std.io.countingReader(source), @@ -146,7 +155,8 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool const source_reader = self.source.reader(); while (self.buffer.isEmpty() and self.state != .LastBlock) { - const header_bytes = source_reader.readBytesNoEof(3) catch return error.MalformedFrame; + const header_bytes = source_reader.readBytesNoEof(3) catch + return error.MalformedFrame; const block_header = decompress.block.decodeBlockHeader(&header_bytes); decompress.block.decodeBlockReader( @@ -171,10 +181,12 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool if (block_header.last_block) { self.state = .LastBlock; if (self.frame_context.has_checksum) { - const checksum = source_reader.readIntLittle(u32) catch return error.MalformedFrame; + const checksum = source_reader.readIntLittle(u32) catch + return error.MalformedFrame; if (comptime verify_checksum) { if (self.frame_context.hasher_opt) |*hasher| { - if (checksum != decompress.computeChecksum(hasher)) return error.ChecksumFailure; + if (checksum != decompress.computeChecksum(hasher)) + return error.ChecksumFailure; } } } @@ -182,9 +194,9 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool } const decoded_data_len = self.buffer.len(); - var written_count: usize = 0; - while (written_count < decoded_data_len and written_count < buffer.len) : (written_count += 1) { - buffer[written_count] = self.buffer.read().?; + var count: usize = 0; + while (count < decoded_data_len and count < buffer.len) : (count += 1) { + buffer[count] = self.buffer.read().?; } if (self.state == .LastBlock and self.buffer.len() == 0) { self.state = .NewFrame; @@ -195,18 +207,22 @@ pub fn ZstandardStream(comptime ReaderType: type, comptime verify_checksum: bool self.allocator.free(self.sequence_buffer); self.buffer.deinit(self.allocator); } - return written_count; + return count; } }; } -pub fn zstandardStream(allocator: Allocator, reader: anytype) !ZstandardStream(@TypeOf(reader), true, 8 * (1 << 20)) { +pub fn zstandardStream( + allocator: Allocator, + reader: anytype, + comptime window_size_max: usize, +) ZstandardStream(@TypeOf(reader), true, window_size_max) { return ZstandardStream(@TypeOf(reader), true, 8 * (1 << 20)).init(allocator, reader); } fn testDecompress(data: []const u8) ![]u8 { var in_stream = std.io.fixedBufferStream(data); - var stream = try zstandardStream(std.testing.allocator, in_stream.reader()); + var stream = zstandardStream(std.testing.allocator, in_stream.reader(), 1 << 23); defer stream.deinit(); const result = stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); return result; diff --git a/lib/std/compress/zstandard/RingBuffer.zig b/lib/std/compress/zstandard/RingBuffer.zig index cf70c087b6..5bd7e5d1f9 100644 --- a/lib/std/compress/zstandard/RingBuffer.zig +++ b/lib/std/compress/zstandard/RingBuffer.zig @@ -13,6 +13,8 @@ data: []u8, read_index: usize, write_index: usize, +pub const Error = error{Full}; + /// Allocate a new `RingBuffer` pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { const bytes = try allocator.alloc(u8, capacity); @@ -41,7 +43,7 @@ pub fn mask2(self: RingBuffer, index: usize) usize { /// Write `byte` into the ring buffer. Returns `error.Full` if the ring /// buffer is full. -pub fn write(self: *RingBuffer, byte: u8) !void { +pub fn write(self: *RingBuffer, byte: u8) Error!void { if (self.isFull()) return error.Full; self.writeAssumeCapacity(byte); } @@ -55,7 +57,7 @@ pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { /// Write `bytes` into the ring bufffer. Returns `error.Full` if the ring /// buffer does not have enough space, without writing any data. -pub fn writeSlice(self: *RingBuffer, bytes: []const u8) !void { +pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void { if (self.len() + bytes.len > self.data.len) return error.Full; self.writeSliceAssumeCapacity(bytes); } @@ -87,7 +89,8 @@ pub fn isFull(self: RingBuffer) bool { /// Returns the length pub fn len(self: RingBuffer) usize { - const adjusted_write_index = self.write_index + @as(usize, @boolToInt(self.write_index < self.read_index)) * 2 * self.data.len; + const wrap_offset = 2 * self.data.len * @boolToInt(self.write_index < self.read_index); + const adjusted_write_index = self.write_index + wrap_offset; return adjusted_write_index - self.read_index; } diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index af87ec694f..7cf84d3034 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -413,7 +413,7 @@ pub const DecodeState = struct { const DecodeLiteralsError = error{ MalformedLiteralsLength, - PrefixNotFound, + NotFound, } || LiteralBitsError; /// Decode `len` bytes of literals into `dest`. @@ -422,8 +422,8 @@ pub const DecodeState = struct { /// - `error.MalformedLiteralsLength` if the number of literal bytes /// decoded by `self` plus `len` is greater than the regenerated size of /// `literals` - /// - `error.UnexpectedEndOfLiteralStream` and `error.PrefixNotFound` if - /// there are problems decoding Huffman compressed literals + /// - `error.UnexpectedEndOfLiteralStream` and `error.NotFound` if there + /// are problems decoding Huffman compressed literals pub fn decodeLiteralsSlice( self: *DecodeState, dest: []u8, diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 68d43c8aeb..7bcfb0a936 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -6,6 +6,8 @@ const types = @import("types.zig"); const frame = types.frame; const LiteralsSection = types.compressed_block.LiteralsSection; const SequencesSection = types.compressed_block.SequencesSection; +const SkippableHeader = types.frame.Skippable.Header; +const ZstandardHeader = types.frame.Zstandard.Header; const Table = types.compressed_block.Table; pub const block = @import("decode/block.zig"); @@ -16,15 +18,13 @@ const readers = @import("readers.zig"); const readInt = std.mem.readIntLittle; const readIntSlice = std.mem.readIntSliceLittle; -fn readVarInt(comptime T: type, bytes: []const u8) T { - return std.mem.readVarInt(T, bytes, .Little); -} +/// Returns `true` is `magic` is a valid magic number for a skippable frame pub fn isSkippableMagic(magic: u32) bool { return frame.Skippable.magic_number_min <= magic and magic <= frame.Skippable.magic_number_max; } -/// Returns the kind of frame at the beginning of `src`. +/// Returns the kind of frame at the beginning of `source`. /// /// Errors returned: /// - `error.BadMagic` if `source` begins with bytes not equal to the @@ -50,11 +50,22 @@ pub fn frameType(magic: u32) error{BadMagic}!frame.Kind { } pub const FrameHeader = union(enum) { - zstandard: types.frame.Zstandard.Header, - skippable: types.frame.Skippable.Header, + zstandard: ZstandardHeader, + skippable: SkippableHeader, }; -pub fn decodeFrameHeader(source: anytype) error{ BadMagic, EndOfStream, ReservedBitSet }!FrameHeader { +pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet }; + +/// Returns the header of the frame at the beginning of `source`. +/// +/// Errors returned: +/// - `error.BadMagic` if `source` begins with bytes not equal to the +/// Zstandard frame magic number, or outside the range of magic numbers for +/// skippable frames. +/// - `error.EndOfStream` if `source` contains fewer than 4 bytes +/// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the +/// reserved bits are set +pub fn decodeFrameHeader(source: anytype) HeaderError!FrameHeader { const magic = try source.readIntLittle(u32); const frame_type = try frameType(magic); switch (frame_type) { @@ -68,41 +79,74 @@ pub fn decodeFrameHeader(source: anytype) error{ BadMagic, EndOfStream, Reserved } } -const ReadWriteCount = struct { +pub const ReadWriteCount = struct { read_count: usize, write_count: usize, }; -/// Decodes frames from `src` into `dest`; see `decodeFrame()`. -pub fn decode(dest: []u8, src: []const u8, verify_checksum: bool) !usize { +/// Decodes frames from `src` into `dest`; returns the length of the result. +/// The stream should not have extra trailing bytes - either all bytes in `src` +/// will be decoded, or an error will be returned. An error will be returned if +/// a Zstandard frame in `src` does not declare its content size. +/// +/// Errors returned: +/// - `error.DictionaryIdFlagUnsupported` if a `src` contains a frame that +/// uses a dictionary +/// - `error.MalformedFrame` if a frame in `src` is invalid +/// - `error.UnknownContentSizeUnsupported` if a frame in `src` does not +/// declare its content size +pub fn decode(dest: []u8, src: []const u8, verify_checksum: bool) error{ + MalformedFrame, + UnknownContentSizeUnsupported, + DictionaryIdFlagUnsupported, +}!usize { var write_count: usize = 0; var read_count: usize = 0; while (read_count < src.len) { - const counts = try decodeFrame(dest, src[read_count..], verify_checksum); + const counts = decodeFrame(dest, src[read_count..], verify_checksum) catch |err| { + switch (err) { + error.UnknownContentSizeUnsupported => return error.UnknownContentSizeUnsupported, + error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, + else => return error.MalformedFrame, + } + }; read_count += counts.read_count; write_count += counts.write_count; } return write_count; } +/// Decodes a stream of frames from `src`; returns the decoded bytes. The stream +/// should not have extra trailing bytes - either all bytes in `src` will be +/// decoded, or an error will be returned. +/// +/// Errors returned: +/// - `error.DictionaryIdFlagUnsupported` if a `src` contains a frame that +/// uses a dictionary +/// - `error.MalformedFrame` if a frame in `src` is invalid +/// - `error.OutOfMemory` if `allocator` cannot allocate enough memory pub fn decodeAlloc( allocator: Allocator, src: []const u8, verify_checksum: bool, window_size_max: usize, -) ![]u8 { +) error{ DictionaryIdFlagUnsupported, MalformedFrame, OutOfMemory }![]u8 { var result = std.ArrayList(u8).init(allocator); errdefer result.deinit(); var read_count: usize = 0; while (read_count < src.len) { - read_count += try decodeFrameArrayList( + read_count += decodeFrameArrayList( allocator, &result, src[read_count..], verify_checksum, window_size_max, - ); + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, + else => return error.MalformedFrame, + }; } return result.toOwnedSlice(); } @@ -112,18 +156,24 @@ pub fn decodeAlloc( /// frames that declare the decompressed content size. /// /// Errors returned: +/// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic +/// number for a Zstandard or skippable frame /// - `error.UnknownContentSizeUnsupported` if the frame does not declare the /// uncompressed content size +/// - `error.WindowSizeUnknown` if the frame does not have a valid window size /// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data /// size declared by the frame header -/// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic -/// number for a Zstandard or Skippable frame +/// - `error.ContentSizeTooLarge` if the frame header indicates a content size +/// that is larger than `std.math.maxInt(usize)` /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame /// contains a checksum that does not match the checksum of the decompressed /// data -/// - `error.ReservedBitSet` if the reserved bit of the frame header is set +/// - `error.ReservedBitSet` if any of the reserved bits of the frame header +/// are set /// - `error.EndOfStream` if `src` does not contain a complete frame +/// - `error.BadContentSize` if the content size declared by the frame does +/// not equal the actual size of decompressed data /// - an error in `block.Error` if there are errors decoding a block /// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a /// size greater than `src.len` @@ -131,7 +181,15 @@ pub fn decodeFrame( dest: []u8, src: []const u8, verify_checksum: bool, -) !ReadWriteCount { +) (error{ + BadMagic, + UnknownContentSizeUnsupported, + ContentTooLarge, + ContentSizeTooLarge, + WindowSizeUnknown, + DictionaryIdFlagUnsupported, + SkippableSizeTooLarge, +} || FrameError)!ReadWriteCount { var fbs = std.io.fixedBufferStream(src); switch (try decodeFrameType(fbs.reader())) { .zstandard => return decodeZstandardFrame(dest, src, verify_checksum), @@ -153,16 +211,21 @@ pub fn decodeFrame( /// /// Errors returned: /// - `error.BadMagic` if the first 4 bytes of `src` is not a valid magic -/// number for a Zstandard or Skippable frame +/// number for a Zstandard or skippable frame /// - `error.WindowSizeUnknown` if the frame does not have a valid window size /// - `error.WindowTooLarge` if the window size is larger than /// `window_size_max` +/// - `error.ContentSizeTooLarge` if the frame header indicates a content size +/// that is larger than `std.math.maxInt(usize)` /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame /// contains a checksum that does not match the checksum of the decompressed /// data -/// - `error.ReservedBitSet` if the reserved bit of the frame header is set +/// - `error.ReservedBitSet` if any of the reserved bits of the frame header +/// are set /// - `error.EndOfStream` if `src` does not contain a complete frame +/// - `error.BadContentSize` if the content size declared by the frame does +/// not equal the actual size of decompressed data /// - `error.OutOfMemory` if `allocator` cannot allocate enough memory /// - an error in `block.Error` if there are errors decoding a block /// - `error.SkippableSizeTooLarge` if the frame is skippable and reports a @@ -173,12 +236,18 @@ pub fn decodeFrameArrayList( src: []const u8, verify_checksum: bool, window_size_max: usize, -) !usize { +) (error{ BadMagic, OutOfMemory, SkippableSizeTooLarge } || FrameContext.Error || FrameError)!usize { var fbs = std.io.fixedBufferStream(src); const reader = fbs.reader(); const magic = try reader.readIntLittle(u32); switch (try frameType(magic)) { - .zstandard => return decodeZstandardFrameArrayList(allocator, dest, src, verify_checksum, window_size_max), + .zstandard => return decodeZstandardFrameArrayList( + allocator, + dest, + src, + verify_checksum, + window_size_max, + ), .skippable => { const content_size = try fbs.reader().readIntLittle(u32); if (content_size > std.math.maxInt(usize) - 8) return error.SkippableSizeTooLarge; @@ -211,7 +280,10 @@ const FrameError = error{ /// uncompressed content size /// - `error.ContentTooLarge` if `dest` is smaller than the uncompressed data /// size declared by the frame header +/// - `error.WindowSizeUnknown` if the frame does not have a valid window size /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary +/// - `error.ContentSizeTooLarge` if the frame header indicates a content size +/// that is larger than `std.math.maxInt(usize)` /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame /// contains a checksum that does not match the checksum of the decompressed /// data @@ -239,7 +311,11 @@ pub fn decodeZstandardFrame( var source = fbs.reader(); const frame_header = try decodeZstandardHeader(source); consumed_count += fbs.pos; - break :context FrameContext.init(frame_header, std.math.maxInt(usize), verify_checksum) catch |err| switch (err) { + break :context FrameContext.init( + frame_header, + std.math.maxInt(usize), + verify_checksum, + ) catch |err| switch (err) { error.WindowTooLarge => unreachable, inline else => |e| return e, }; @@ -260,7 +336,8 @@ pub fn decodeZStandardFrameBlocks( src: []const u8, frame_context: *FrameContext, ) (error{ ContentTooLarge, UnknownContentSizeUnsupported } || FrameError)!ReadWriteCount { - const content_size = frame_context.content_size orelse return error.UnknownContentSizeUnsupported; + const content_size = frame_context.content_size orelse + return error.UnknownContentSizeUnsupported; if (dest.len < content_size) return error.ContentTooLarge; var consumed_count: usize = 0; @@ -304,14 +381,19 @@ pub const FrameContext = struct { /// /// Errors returned: /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary - /// - `error.WindowSizeUnknown` if the frame does not have a valid window size - /// - `error.WindowTooLarge` if the window size is larger than `window_size_max` + /// - `error.WindowSizeUnknown` if the frame does not have a valid window + /// size + /// - `error.WindowTooLarge` if the window size is larger than + /// `window_size_max` + /// - `error.ContentSizeTooLarge` if the frame header indicates a content + /// size larger than `std.math.maxInt(usize)` pub fn init( - frame_header: frame.Zstandard.Header, + frame_header: ZstandardHeader, window_size_max: usize, verify_checksum: bool, ) Error!FrameContext { - if (frame_header.descriptor.dictionary_id_flag != 0) return error.DictionaryIdFlagUnsupported; + if (frame_header.descriptor.dictionary_id_flag != 0) + return error.DictionaryIdFlagUnsupported; const window_size_raw = frameWindowSize(frame_header) orelse return error.WindowSizeUnknown; const window_size = if (window_size_raw > window_size_max) @@ -319,7 +401,8 @@ pub const FrameContext = struct { else @intCast(usize, window_size_raw); - const should_compute_checksum = frame_header.descriptor.content_checksum_flag and verify_checksum; + const should_compute_checksum = + frame_header.descriptor.content_checksum_flag and verify_checksum; const content_size = if (frame_header.content_size) |size| std.math.cast(usize, size) orelse return error.ContentSizeTooLarge @@ -345,6 +428,8 @@ pub const FrameContext = struct { /// - `error.WindowTooLarge` if the window size is larger than /// `window_size_max` /// - `error.DictionaryIdFlagUnsupported` if the frame uses a dictionary +/// - `error.ContentSizeTooLarge` if the frame header indicates a content size +/// that is larger than `std.math.maxInt(usize)` /// - `error.ChecksumFailure` if `verify_checksum` is true and the frame /// contains a checksum that does not match the checksum of the decompressed /// data @@ -441,8 +526,6 @@ pub fn decodeZstandardFrameBlocksArrayList( return consumed_count; } -/// Convenience wrapper for decoding all blocks in a frame; see -/// `decodeZStandardFrameBlocks()`. fn decodeFrameBlocksInner( dest: []u8, src: []const u8, @@ -459,7 +542,7 @@ fn decodeFrameBlocksInner( var bytes_read: usize = 3; defer consumed_count.* += bytes_read; var decode_state = block.DecodeState.init(&literal_fse_data, &match_fse_data, &offset_fse_data); - var written_count: usize = 0; + var count: usize = 0; while (true) : ({ block_header = try block.decodeBlockHeaderSlice(src[bytes_read..]); bytes_read += 3; @@ -471,18 +554,18 @@ fn decodeFrameBlocksInner( &decode_state, &bytes_read, block_size_max, - written_count, + count, ); - if (hash) |hash_state| hash_state.update(dest[written_count .. written_count + written_size]); - written_count += written_size; + if (hash) |hash_state| hash_state.update(dest[count .. count + written_size]); + count += written_size; if (block_header.last_block) break; } - return written_count; + return count; } /// Decode the header of a skippable frame. The first four bytes of `src` must -/// be a valid magic number for a Skippable frame. -pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { +/// be a valid magic number for a skippable frame. +pub fn decodeSkippableHeader(src: *const [8]u8) SkippableHeader { const magic = readInt(u32, src[0..4]); assert(isSkippableMagic(magic)); const frame_size = readInt(u32, src[4..8]); @@ -494,7 +577,7 @@ pub fn decodeSkippableHeader(src: *const [8]u8) frame.Skippable.Header { /// Returns the window size required to decompress a frame, or `null` if it /// cannot be determined (which indicates a malformed frame header). -pub fn frameWindowSize(header: frame.Zstandard.Header) ?u64 { +pub fn frameWindowSize(header: ZstandardHeader) ?u64 { if (header.window_descriptor) |descriptor| { const exponent = (descriptor & 0b11111000) >> 3; const mantissa = descriptor & 0b00000111; @@ -508,10 +591,10 @@ pub fn frameWindowSize(header: frame.Zstandard.Header) ?u64 { /// Decode the header of a Zstandard frame. /// /// Errors returned: -/// - `error.ReservedBitSet` if the reserved bits of the header are set +/// - `error.ReservedBitSet` if any of the reserved bits of the header are set /// - `error.EndOfStream` if `source` does not contain a complete header -pub fn decodeZstandardHeader(source: anytype) error{ EndOfStream, ReservedBitSet }!frame.Zstandard.Header { - const descriptor = @bitCast(frame.Zstandard.Header.Descriptor, try source.readByte()); +pub fn decodeZstandardHeader(source: anytype) error{ EndOfStream, ReservedBitSet }!ZstandardHeader { + const descriptor = @bitCast(ZstandardHeader.Descriptor, try source.readByte()); if (descriptor.reserved) return error.ReservedBitSet; @@ -534,7 +617,7 @@ pub fn decodeZstandardHeader(source: anytype) error{ EndOfStream, ReservedBitSet if (field_size == 2) content_size.? += 256; } - const header = frame.Zstandard.Header{ + const header = ZstandardHeader{ .descriptor = descriptor, .window_descriptor = window_descriptor, .dictionary_id = dictionary_id, diff --git a/lib/std/compress/zstandard/types.zig b/lib/std/compress/zstandard/types.zig index 3f61b0bab4..db4fbdee2d 100644 --- a/lib/std/compress/zstandard/types.zig +++ b/lib/std/compress/zstandard/types.zig @@ -92,13 +92,13 @@ pub const compressed_block = struct { index: usize, }; - pub fn query(self: HuffmanTree, index: usize, prefix: u16) error{PrefixNotFound}!Result { + pub fn query(self: HuffmanTree, index: usize, prefix: u16) error{NotFound}!Result { var node = self.nodes[index]; const weight = node.weight; var i: usize = index; while (node.weight == weight) { if (node.prefix == prefix) return Result{ .symbol = node.symbol }; - if (i == 0) return error.PrefixNotFound; + if (i == 0) return error.NotFound; i -= 1; node = self.nodes[i]; } @@ -164,12 +164,14 @@ pub const compressed_block = struct { }; pub const match_length_code_table = [53]struct { u32, u5 }{ - .{ 3, 0 }, .{ 4, 0 }, .{ 5, 0 }, .{ 6, 0 }, .{ 7, 0 }, .{ 8, 0 }, .{ 9, 0 }, .{ 10, 0 }, - .{ 11, 0 }, .{ 12, 0 }, .{ 13, 0 }, .{ 14, 0 }, .{ 15, 0 }, .{ 16, 0 }, .{ 17, 0 }, .{ 18, 0 }, - .{ 19, 0 }, .{ 20, 0 }, .{ 21, 0 }, .{ 22, 0 }, .{ 23, 0 }, .{ 24, 0 }, .{ 25, 0 }, .{ 26, 0 }, - .{ 27, 0 }, .{ 28, 0 }, .{ 29, 0 }, .{ 30, 0 }, .{ 31, 0 }, .{ 32, 0 }, .{ 33, 0 }, .{ 34, 0 }, - .{ 35, 1 }, .{ 37, 1 }, .{ 39, 1 }, .{ 41, 1 }, .{ 43, 2 }, .{ 47, 2 }, .{ 51, 3 }, .{ 59, 3 }, - .{ 67, 4 }, .{ 83, 4 }, .{ 99, 5 }, .{ 131, 7 }, .{ 259, 8 }, .{ 515, 9 }, .{ 1027, 10 }, .{ 2051, 11 }, + .{ 3, 0 }, .{ 4, 0 }, .{ 5, 0 }, .{ 6, 0 }, .{ 7, 0 }, .{ 8, 0 }, + .{ 9, 0 }, .{ 10, 0 }, .{ 11, 0 }, .{ 12, 0 }, .{ 13, 0 }, .{ 14, 0 }, + .{ 15, 0 }, .{ 16, 0 }, .{ 17, 0 }, .{ 18, 0 }, .{ 19, 0 }, .{ 20, 0 }, + .{ 21, 0 }, .{ 22, 0 }, .{ 23, 0 }, .{ 24, 0 }, .{ 25, 0 }, .{ 26, 0 }, + .{ 27, 0 }, .{ 28, 0 }, .{ 29, 0 }, .{ 30, 0 }, .{ 31, 0 }, .{ 32, 0 }, + .{ 33, 0 }, .{ 34, 0 }, .{ 35, 1 }, .{ 37, 1 }, .{ 39, 1 }, .{ 41, 1 }, + .{ 43, 2 }, .{ 47, 2 }, .{ 51, 3 }, .{ 59, 3 }, .{ 67, 4 }, .{ 83, 4 }, + .{ 99, 5 }, .{ 131, 7 }, .{ 259, 8 }, .{ 515, 9 }, .{ 1027, 10 }, .{ 2051, 11 }, .{ 4099, 12 }, .{ 8195, 13 }, .{ 16387, 14 }, .{ 32771, 15 }, .{ 65539, 16 }, }; From 1530e73648cd9687bbaea3e50da9b2e86d66df0c Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Fri, 10 Feb 2023 12:45:26 +1100 Subject: [PATCH 055/122] std.compress.zstandard: bytes read assert to error in decodeBlockReader --- lib/std/compress/zstandard/decode/block.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 7cf84d3034..8563f41614 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -850,8 +850,8 @@ pub fn decodeBlockReader( } if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; + if (block_reader_limited.bytes_left != 0) return error.MalformedCompressedBlock; decode_state.literal_written_count = 0; - assert(block_reader.readByte() == error.EndOfStream); }, .reserved => return error.ReservedBlock, } From 373d8ef26edca9d16111ae41f960a44ead6ea2c8 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 12 Feb 2023 04:33:20 +1100 Subject: [PATCH 056/122] std.compress.zstandard: check FSE bitstreams are fully consumed --- lib/std/compress/zstandard/decode/block.zig | 36 +++++++++++-------- lib/std/compress/zstandard/decode/huffman.zig | 4 +++ lib/std/compress/zstandard/readers.zig | 8 ++++- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 8563f41614..7cc4c146ca 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -391,15 +391,21 @@ pub const DecodeState = struct { try self.literal_stream_reader.init(bytes); } + fn isLiteralStreamEmpty(self: *DecodeState) bool { + switch (self.literal_streams) { + .one => return self.literal_stream_reader.isEmpty(), + .four => return self.literal_stream_index == 3 and self.literal_stream_reader.isEmpty(), + } + } + const LiteralBitsError = error{ BitStreamHasNoStartBit, UnexpectedEndOfLiteralStream, }; fn readLiteralsBits( self: *DecodeState, - comptime T: type, bit_count_to_read: usize, - ) LiteralBitsError!T { + ) LiteralBitsError!u16 { return self.literal_stream_reader.readBitsNoEof(u16, bit_count_to_read) catch bits: { if (self.literal_streams == .four and self.literal_stream_index < 3) { try self.nextLiteralMultiStream(); @@ -461,7 +467,7 @@ pub const DecodeState = struct { while (i < len) : (i += 1) { var prefix: u16 = 0; while (true) { - const new_bits = self.readLiteralsBits(u16, bit_count_to_read) catch |err| { + const new_bits = self.readLiteralsBits(bit_count_to_read) catch |err| { return err; }; prefix <<= bit_count_to_read; @@ -533,7 +539,7 @@ pub const DecodeState = struct { while (i < len) : (i += 1) { var prefix: u16 = 0; while (true) { - const new_bits = try self.readLiteralsBits(u16, bit_count_to_read); + const new_bits = try self.readLiteralsBits(bit_count_to_read); prefix <<= bit_count_to_read; prefix |= new_bits; bits_read += bit_count_to_read; @@ -659,13 +665,10 @@ pub fn decodeBlock( sequence_size_limit -= decompressed_size; } - if (bit_stream.bit_reader.bit_count != 0) { + if (!bit_stream.isEmpty()) { return error.MalformedCompressedBlock; } - - bytes_read += bit_stream_bytes.len; } - if (bytes_read != block_size) return error.MalformedCompressedBlock; if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; @@ -675,7 +678,9 @@ pub fn decodeBlock( bytes_written += len; } - consumed_count.* += bytes_read; + if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + + consumed_count.* += block_size; return bytes_written; }, .reserved => return error.ReservedBlock, @@ -749,13 +754,10 @@ pub fn decodeBlockRingBuffer( sequence_size_limit -= decompressed_size; } - if (bit_stream.bit_reader.bit_count != 0) { + if (!bit_stream.isEmpty()) { return error.MalformedCompressedBlock; } - - bytes_read += bit_stream_bytes.len; } - if (bytes_read != block_size) return error.MalformedCompressedBlock; if (decode_state.literal_written_count < literals.header.regenerated_size) { const len = literals.header.regenerated_size - decode_state.literal_written_count; @@ -764,7 +766,9 @@ pub fn decodeBlockRingBuffer( bytes_written += len; } - consumed_count.* += bytes_read; + if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + + consumed_count.* += block_size; if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; return bytes_written; }, @@ -837,7 +841,7 @@ pub fn decodeBlockReader( sequence_size_limit -= decompressed_size; bytes_written += decompressed_size; } - if (bit_stream.bit_reader.bit_count != 0) { + if (!bit_stream.isEmpty()) { return error.MalformedCompressedBlock; } } @@ -849,6 +853,8 @@ pub fn decodeBlockReader( bytes_written += len; } + if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; if (block_reader_limited.bytes_left != 0) return error.MalformedCompressedBlock; decode_state.literal_written_count = 0; diff --git a/lib/std/compress/zstandard/decode/huffman.zig b/lib/std/compress/zstandard/decode/huffman.zig index f5639e7721..c3bda380dd 100644 --- a/lib/std/compress/zstandard/decode/huffman.zig +++ b/lib/std/compress/zstandard/decode/huffman.zig @@ -86,6 +86,10 @@ fn assignWeights(huff_bits: *readers.ReverseBitReader, accuracy_log: usize, entr odd_state = odd_data.baseline + odd_bits; } else return error.MalformedHuffmanTree; + if (!huff_bits.isEmpty()) { + return error.MalformedHuffmanTree; + } + return i + 1; // stream contains all but the last symbol } diff --git a/lib/std/compress/zstandard/readers.zig b/lib/std/compress/zstandard/readers.zig index 489f933310..98cac2ed80 100644 --- a/lib/std/compress/zstandard/readers.zig +++ b/lib/std/compress/zstandard/readers.zig @@ -36,7 +36,9 @@ pub const ReverseBitReader = struct { pub fn init(self: *ReverseBitReader, bytes: []const u8) error{BitStreamHasNoStartBit}!void { self.byte_reader = ReversedByteReader.init(bytes); self.bit_reader = std.io.bitReader(.Big, self.byte_reader.reader()); - while (0 == self.readBitsNoEof(u1, 1) catch return error.BitStreamHasNoStartBit) {} + var i: usize = 0; + while (i < 8 and 0 == self.readBitsNoEof(u1, 1) catch return error.BitStreamHasNoStartBit) : (i += 1) {} + if (i == 8) return error.BitStreamHasNoStartBit; } pub fn readBitsNoEof(self: *@This(), comptime U: type, num_bits: usize) error{EndOfStream}!U { @@ -50,6 +52,10 @@ pub const ReverseBitReader = struct { pub fn alignToByte(self: *@This()) void { self.bit_reader.alignToByte(); } + + pub fn isEmpty(self: ReverseBitReader) bool { + return self.byte_reader.remaining_bytes == 0 and self.bit_reader.bit_count == 0; + } }; pub fn BitReader(comptime Reader: type) type { From 476d2fe1fa917a3b7a92cf155c779eb975c906e6 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 12 Feb 2023 13:05:34 +1100 Subject: [PATCH 057/122] std.compress.zstandard: fix zstandardStream finishing early --- lib/std/compress/zstandard.zig | 41 ++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index afc542478b..58af6c7aef 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -27,6 +27,7 @@ pub fn ZstandardStream( literals_buffer: []u8, sequence_buffer: []u8, checksum: if (verify_checksum) ?u32 else void, + current_frame_decompressed_size: usize, pub const Error = ReaderType.Error || error{ ChecksumFailure, @@ -51,6 +52,7 @@ pub fn ZstandardStream( .literals_buffer = undefined, .sequence_buffer = undefined, .checksum = undefined, + .current_frame_decompressed_size = undefined, }; } @@ -113,6 +115,7 @@ pub fn ZstandardStream( self.frame_context = frame_context; self.checksum = if (verify_checksum) null else {}; + self.current_frame_decompressed_size = 0; self.state = .InFrame; }, @@ -134,20 +137,24 @@ pub fn ZstandardStream( } pub fn read(self: *Self, buffer: []u8) Error!usize { - const initial_count = self.source.bytes_read; if (buffer.len == 0) return 0; - while (self.state == .NewFrame) { - self.frameInit() catch |err| switch (err) { - error.EndOfStream => return if (self.source.bytes_read == initial_count) - 0 - else - error.MalformedFrame, - error.OutOfMemory => return error.OutOfMemory, - else => return error.MalformedFrame, - }; - } - return self.readInner(buffer); + var size: usize = 0; + while (size == 0) { + while (self.state == .NewFrame) { + const initial_count = self.source.bytes_read; + self.frameInit() catch |err| switch (err) { + error.EndOfStream => return if (self.source.bytes_read == initial_count) + 0 + else + error.MalformedFrame, + error.OutOfMemory => return error.OutOfMemory, + else => return error.MalformedFrame, + }; + } + size = try self.readInner(buffer); + } + return size; } fn readInner(self: *Self, buffer: []u8) Error!usize { @@ -172,6 +179,7 @@ pub fn ZstandardStream( if (self.frame_context.hasher_opt) |*hasher| { const size = self.buffer.len(); + self.current_frame_decompressed_size += size; if (size > 0) { const written_slice = self.buffer.sliceLast(size); hasher.update(written_slice.first); @@ -190,12 +198,17 @@ pub fn ZstandardStream( } } } + if (self.frame_context.content_size) |content_size| { + if (content_size != self.current_frame_decompressed_size) { + return error.MalformedFrame; + } + } } } - const decoded_data_len = self.buffer.len(); + const size = @min(self.buffer.len(), buffer.len); var count: usize = 0; - while (count < decoded_data_len and count < buffer.len) : (count += 1) { + while (count < size) : (count += 1) { buffer[count] = self.buffer.read().?; } if (self.state == .LastBlock and self.buffer.len() == 0) { From 8fd41314bdb81303d8e674d292a384c9df352a05 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 12 Feb 2023 16:36:13 +1100 Subject: [PATCH 058/122] std.compress.zstandard: remove unneeded branch --- lib/std/compress/zstandard/decode/huffman.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/std/compress/zstandard/decode/huffman.zig b/lib/std/compress/zstandard/decode/huffman.zig index c3bda380dd..9cc0d49479 100644 --- a/lib/std/compress/zstandard/decode/huffman.zig +++ b/lib/std/compress/zstandard/decode/huffman.zig @@ -153,9 +153,7 @@ fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.P fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) error{MalformedHuffmanTree}!LiteralsSection.HuffmanTree { var weight_power_sum_big: u32 = 0; for (weights[0 .. symbol_count - 1]) |value| { - if (value > 0) { - weight_power_sum_big += @as(u16, 1) << (value - 1); - } + weight_power_sum_big += (@as(u16, 1) << value) >> 1; } if (weight_power_sum_big >= 1 << 11) return error.MalformedHuffmanTree; const weight_power_sum = @intCast(u16, weight_power_sum_big); From 5a31fc2014ed6c1d806d08f1393e10b597ec427d Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 12 Feb 2023 22:02:24 +1100 Subject: [PATCH 059/122] std.compress.zstandard: fix erroneous literal stream empty checks --- lib/std/compress/zstandard/decode/block.zig | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 7cc4c146ca..18fbf289e2 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -678,7 +678,12 @@ pub fn decodeBlock( bytes_written += len; } - if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + switch (decode_state.literal_header.block_type) { + .treeless, .compressed => { + if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + }, + .raw, .rle => {}, + } consumed_count.* += block_size; return bytes_written; @@ -766,7 +771,12 @@ pub fn decodeBlockRingBuffer( bytes_written += len; } - if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + switch (decode_state.literal_header.block_type) { + .treeless, .compressed => { + if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + }, + .raw, .rle => {}, + } consumed_count.* += block_size; if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; @@ -853,7 +863,12 @@ pub fn decodeBlockReader( bytes_written += len; } - if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + switch (decode_state.literal_header.block_type) { + .treeless, .compressed => { + if (!decode_state.isLiteralStreamEmpty()) return error.MalformedCompressedBlock; + }, + .raw, .rle => {}, + } if (bytes_written > block_size_max) return error.BlockSizeOverMaximum; if (block_reader_limited.bytes_left != 0) return error.MalformedCompressedBlock; From a53cf299a6a22422d734d54b6abed4ff8b6473c5 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 12 Feb 2023 22:04:07 +1100 Subject: [PATCH 060/122] std.compress.zstandard: add error condition to ring buffer decoding Previously `executeSequenceRingBuffer()` would not verify the offset against the number of bytes already decoded, so it would happily copy garbage bytes rather than return an error before the window was filled. To fix this a new `written_count` is added to the decode state that tracks the total number of bytes decoded. --- lib/std/compress/zstandard/decode/block.zig | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 18fbf289e2..5a197b4edd 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -45,6 +45,7 @@ pub const DecodeState = struct { huffman_tree: ?LiteralsSection.HuffmanTree, literal_written_count: usize, + written_count: usize = 0, fn StateData(comptime max_accuracy_log: comptime_int) type { return struct { @@ -84,6 +85,8 @@ pub const DecodeState = struct { .literal_stream_reader = undefined, .literal_stream_index = undefined, .huffman_tree = null, + + .written_count = 0, }; } @@ -296,6 +299,7 @@ pub const DecodeState = struct { // NOTE: we ignore the usage message for std.mem.copy and copy with dest.ptr >= src.ptr // to allow repeats std.mem.copy(u8, dest[write_pos + sequence.literal_length ..], dest[copy_start..copy_end]); + self.written_count += sequence.match_length; } fn executeSequenceRingBuffer( @@ -303,7 +307,8 @@ pub const DecodeState = struct { dest: *RingBuffer, sequence: Sequence, ) (error{MalformedSequence} || DecodeLiteralsError)!void { - if (sequence.offset > dest.data.len) return error.MalformedSequence; + if (sequence.offset > @min(dest.data.len, self.written_count + sequence.literal_length)) + return error.MalformedSequence; try self.decodeLiteralsRingBuffer(dest, sequence.literal_length); const copy_start = dest.write_index + dest.data.len - sequence.offset; @@ -311,6 +316,7 @@ pub const DecodeState = struct { // TODO: would std.mem.copy and figuring out dest slice be better/faster? for (copy_slice.first) |b| dest.writeAssumeCapacity(b); for (copy_slice.second) |b| dest.writeAssumeCapacity(b); + self.written_count += sequence.match_length; } const DecodeSequenceError = error{ @@ -444,6 +450,7 @@ pub const DecodeState = struct { const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; std.mem.copy(u8, dest, literal_data); self.literal_written_count += len; + self.written_count += len; }, .rle => { var i: usize = 0; @@ -451,6 +458,7 @@ pub const DecodeState = struct { dest[i] = self.literal_streams.one[0]; } self.literal_written_count += len; + self.written_count += len; }, .compressed, .treeless => { // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; @@ -497,6 +505,7 @@ pub const DecodeState = struct { } } self.literal_written_count += len; + self.written_count += len; }, } } @@ -516,6 +525,7 @@ pub const DecodeState = struct { const literal_data = self.literal_streams.one[self.literal_written_count..literals_end]; dest.writeSliceAssumeCapacity(literal_data); self.literal_written_count += len; + self.written_count += len; }, .rle => { var i: usize = 0; @@ -523,6 +533,7 @@ pub const DecodeState = struct { dest.writeAssumeCapacity(self.literal_streams.one[0]); } self.literal_written_count += len; + self.written_count += len; }, .compressed, .treeless => { // const written_bytes_per_stream = (literals.header.regenerated_size + 3) / 4; @@ -565,6 +576,7 @@ pub const DecodeState = struct { } } self.literal_written_count += len; + self.written_count += len; }, } } @@ -612,6 +624,7 @@ pub fn decodeBlock( const data = src[0..block_size]; std.mem.copy(u8, dest[written_count..], data); consumed_count.* += block_size; + decode_state.written_count += block_size; return block_size; }, .rle => { @@ -622,6 +635,7 @@ pub fn decodeBlock( dest[write_pos] = src[0]; } consumed_count.* += 1; + decode_state.written_count += block_size; return block_size; }, .compressed => { @@ -712,6 +726,7 @@ pub fn decodeBlockRingBuffer( const data = src[0..block_size]; dest.writeSliceAssumeCapacity(data); consumed_count.* += block_size; + decode_state.written_count += block_size; return block_size; }, .rle => { @@ -721,6 +736,7 @@ pub fn decodeBlockRingBuffer( dest.writeAssumeCapacity(src[0]); } consumed_count.* += 1; + decode_state.written_count += block_size; return block_size; }, .compressed => { @@ -814,6 +830,7 @@ pub fn decodeBlockReader( try source.readNoEof(slice.first); try source.readNoEof(slice.second); dest.write_index = dest.mask2(dest.write_index + block_size); + decode_state.written_count += block_size; }, .rle => { const byte = try source.readByte(); @@ -821,6 +838,7 @@ pub fn decodeBlockReader( while (i < block_size) : (i += 1) { dest.writeAssumeCapacity(byte); } + decode_state.written_count += block_size; }, .compressed => { const literals = try decodeLiteralsSection(block_reader, literals_buffer); From 12aa478db08c5652d27228183bb898f65db7a2ae Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 13 Feb 2023 17:19:33 +1100 Subject: [PATCH 061/122] std.compress.zstandard: also check block size when sequence count is 0 --- lib/std/compress/zstandard/decode/block.zig | 104 +++++++++++--------- lib/std/compress/zstandard/readers.zig | 3 +- 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 5a197b4edd..16465e654d 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -654,29 +654,32 @@ pub fn decodeBlock( bytes_read += fbs.pos; var bytes_written: usize = 0; - if (sequences_header.sequence_count > 0) { + { const bit_stream_bytes = src[bytes_read..block_size]; var bit_stream: readers.ReverseBitReader = undefined; bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; - decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; + if (sequences_header.sequence_count > 0) { + decode_state.readInitialFseState(&bit_stream) catch + return error.MalformedCompressedBlock; - var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { - const write_pos = written_count + bytes_written; - const decompressed_size = decode_state.decodeSequenceSlice( - dest, - write_pos, - &bit_stream, - sequence_size_limit, - i == sequences_header.sequence_count - 1, - ) catch |err| switch (err) { - error.DestTooSmall => return error.DestTooSmall, - else => return error.MalformedCompressedBlock, - }; - bytes_written += decompressed_size; - sequence_size_limit -= decompressed_size; + var sequence_size_limit = block_size_max; + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + const write_pos = written_count + bytes_written; + const decompressed_size = decode_state.decodeSequenceSlice( + dest, + write_pos, + &bit_stream, + sequence_size_limit, + i == sequences_header.sequence_count - 1, + ) catch |err| switch (err) { + error.DestTooSmall => return error.DestTooSmall, + else => return error.MalformedCompressedBlock, + }; + bytes_written += decompressed_size; + sequence_size_limit -= decompressed_size; + } } if (!bit_stream.isEmpty()) { @@ -755,24 +758,27 @@ pub fn decodeBlockRingBuffer( bytes_read += fbs.pos; var bytes_written: usize = 0; - if (sequences_header.sequence_count > 0) { + { const bit_stream_bytes = src[bytes_read..block_size]; var bit_stream: readers.ReverseBitReader = undefined; bit_stream.init(bit_stream_bytes) catch return error.MalformedCompressedBlock; - decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; + if (sequences_header.sequence_count > 0) { + decode_state.readInitialFseState(&bit_stream) catch + return error.MalformedCompressedBlock; - var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { - const decompressed_size = decode_state.decodeSequenceRingBuffer( - dest, - &bit_stream, - sequence_size_limit, - i == sequences_header.sequence_count - 1, - ) catch return error.MalformedCompressedBlock; - bytes_written += decompressed_size; - sequence_size_limit -= decompressed_size; + var sequence_size_limit = block_size_max; + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + const decompressed_size = decode_state.decodeSequenceRingBuffer( + dest, + &bit_stream, + sequence_size_limit, + i == sequences_header.sequence_count - 1, + ) catch return error.MalformedCompressedBlock; + bytes_written += decompressed_size; + sequence_size_limit -= decompressed_size; + } } if (!bit_stream.isEmpty()) { @@ -847,28 +853,32 @@ pub fn decodeBlockReader( try decode_state.prepare(block_reader, literals, sequences_header); var bytes_written: usize = 0; - if (sequences_header.sequence_count > 0) { - if (sequence_buffer.len < block_reader_limited.bytes_left) - return error.SequenceBufferTooSmall; - + { const size = try block_reader.readAll(sequence_buffer); var bit_stream: readers.ReverseBitReader = undefined; try bit_stream.init(sequence_buffer[0..size]); - decode_state.readInitialFseState(&bit_stream) catch return error.MalformedCompressedBlock; + if (sequences_header.sequence_count > 0) { + if (sequence_buffer.len < block_reader_limited.bytes_left) + return error.SequenceBufferTooSmall; - var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { - const decompressed_size = decode_state.decodeSequenceRingBuffer( - dest, - &bit_stream, - sequence_size_limit, - i == sequences_header.sequence_count - 1, - ) catch return error.MalformedCompressedBlock; - sequence_size_limit -= decompressed_size; - bytes_written += decompressed_size; + decode_state.readInitialFseState(&bit_stream) catch + return error.MalformedCompressedBlock; + + var sequence_size_limit = block_size_max; + var i: usize = 0; + while (i < sequences_header.sequence_count) : (i += 1) { + const decompressed_size = decode_state.decodeSequenceRingBuffer( + dest, + &bit_stream, + sequence_size_limit, + i == sequences_header.sequence_count - 1, + ) catch return error.MalformedCompressedBlock; + sequence_size_limit -= decompressed_size; + bytes_written += decompressed_size; + } } + if (!bit_stream.isEmpty()) { return error.MalformedCompressedBlock; } diff --git a/lib/std/compress/zstandard/readers.zig b/lib/std/compress/zstandard/readers.zig index 98cac2ed80..e2f62ddc51 100644 --- a/lib/std/compress/zstandard/readers.zig +++ b/lib/std/compress/zstandard/readers.zig @@ -36,8 +36,9 @@ pub const ReverseBitReader = struct { pub fn init(self: *ReverseBitReader, bytes: []const u8) error{BitStreamHasNoStartBit}!void { self.byte_reader = ReversedByteReader.init(bytes); self.bit_reader = std.io.bitReader(.Big, self.byte_reader.reader()); + if (bytes.len == 0) return; var i: usize = 0; - while (i < 8 and 0 == self.readBitsNoEof(u1, 1) catch return error.BitStreamHasNoStartBit) : (i += 1) {} + while (i < 8 and 0 == self.readBitsNoEof(u1, 1) catch unreachable) : (i += 1) {} if (i == 8) return error.BitStreamHasNoStartBit; } From 1a862175d52eb35efd7ecb368c1806b4ac1e7886 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 13 Feb 2023 18:02:25 +1100 Subject: [PATCH 062/122] std.compress.zstandard: fix zstandardStream content size validation --- lib/std/compress/zstandard.zig | 9 +++++++-- lib/std/compress/zstandard/decompress.zig | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index 58af6c7aef..e6accb9f2e 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -177,9 +177,14 @@ pub fn ZstandardStream( ) catch return error.MalformedBlock; + if (self.frame_context.content_size) |size| { + if (self.current_frame_decompressed_size > size) return error.MalformedFrame; + } + + const size = self.buffer.len(); + self.current_frame_decompressed_size += size; + if (self.frame_context.hasher_opt) |*hasher| { - const size = self.buffer.len(); - self.current_frame_decompressed_size += size; if (size > 0) { const written_slice = self.buffer.sliceLast(size); hasher.update(written_slice.first); diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 7bcfb0a936..31c5660642 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -497,6 +497,11 @@ pub fn decodeZstandardFrameBlocksArrayList( &consumed_count, frame_context.block_size_max, ); + if (frame_context.content_size) |size| { + if (dest.items.len - initial_len > size) { + return error.BadContentSize; + } + } if (written_size > 0) { const written_slice = ring_buffer.sliceLast(written_size); try dest.appendSlice(written_slice.first); @@ -508,9 +513,8 @@ pub fn decodeZstandardFrameBlocksArrayList( } if (block_header.last_block) break; } - const added_len = dest.items.len - initial_len; if (frame_context.content_size) |size| { - if (added_len != size) { + if (dest.items.len - initial_len != size) { return error.BadContentSize; } } From 2766b704c1805f7b3dce25331c209441cdb774fc Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 14 Feb 2023 22:17:05 +1100 Subject: [PATCH 063/122] std.compress.zstandard: add DictionaryIdFlagUnsupported ZstandardStream.Error --- lib/std/compress/zstandard.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index e6accb9f2e..61ad9b69fd 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -31,6 +31,7 @@ pub fn ZstandardStream( pub const Error = ReaderType.Error || error{ ChecksumFailure, + DictionaryIdFlagUnsupported, MalformedBlock, MalformedFrame, OutOfMemory, @@ -144,6 +145,7 @@ pub fn ZstandardStream( while (self.state == .NewFrame) { const initial_count = self.source.bytes_read; self.frameInit() catch |err| switch (err) { + error.DictionaryIdFlagUnsupported => return error.DictionaryIdFlagUnsupported, error.EndOfStream => return if (self.source.bytes_read == initial_count) 0 else From 53104b91656182094a4048964f6a3f384e7f199f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Feb 2023 17:36:35 -0700 Subject: [PATCH 064/122] add test coverage for fixed bug. closes #5410 --- .../compile_errors/error_set_membership.zig | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 test/cases/compile_errors/error_set_membership.zig diff --git a/test/cases/compile_errors/error_set_membership.zig b/test/cases/compile_errors/error_set_membership.zig new file mode 100644 index 0000000000..5683e9594b --- /dev/null +++ b/test/cases/compile_errors/error_set_membership.zig @@ -0,0 +1,31 @@ +const std = @import("std"); + +const Error = error{InvalidCharacter}; + +const Direction = enum { upside_down }; + +const Barrrr = union(enum) { + float: f64, + direction: Direction, +}; + +fn fooey(bar: std.meta.Tag(Barrrr), args: []const []const u8) !Barrrr { + return switch (bar) { + .float => .{ .float = try std.fmt.parseFloat(f64, args[0]) }, + .direction => if (std.mem.eql(u8, args[0], "upside_down")) + Barrrr{ .direction = .upside_down } + else + error.InvalidDirection, + }; +} + +pub fn main() Error!void { + std.debug.print("{}", .{try fooey(.direction, &[_][]const u8{ "one", "two", "three" })}); +} + +// error +// backend=llvm +// target=native +// +// :23:29: error: expected type 'error{InvalidCharacter}', found '@typeInfo(@typeInfo(@TypeOf(tmp.fooey)).Fn.return_type.?).ErrorUnion.error_set' +// :23:29: note: 'error.InvalidDirection' not a member of destination error set From ffdce5f98c7f3d7e18db933e1fb54f987efd5fbf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Feb 2023 17:47:57 -0700 Subject: [PATCH 065/122] add test coverage for fixed bug. closes #5497 --- test/behavior/struct.zig | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index ed3e1ce88f..8a01d68587 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1578,3 +1578,38 @@ test "directly initiating tuple like struct" { const a = struct { u8 }{8}; try expect(a[0] == 8); } + +test "instantiate struct with comptime field" { + { + var things = struct { + comptime foo: i8 = 1, + }{}; + + comptime std.debug.assert(things.foo == 1); + } + + { + const T = struct { + comptime foo: i8 = 1, + }; + var things = T{}; + + comptime std.debug.assert(things.foo == 1); + } + + { + var things: struct { + comptime foo: i8 = 1, + } = .{}; + + comptime std.debug.assert(things.foo == 1); + } + + { + var things: struct { + comptime foo: i8 = 1, + } = undefined; // Segmentation fault at address 0x0 + + comptime std.debug.assert(things.foo == 1); + } +} From 680d79ebf9591be74790999088c882de50a2863b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Feb 2023 17:55:25 -0700 Subject: [PATCH 066/122] add test coverage for fixed bug. closes #5508 --- test/behavior/type_info.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 419a2f231c..6f64c92006 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -603,3 +603,9 @@ test "@typeInfo decls ignore dependency loops" { }; _ = S.foo; } + +test "type info of tuple of string literal default value" { + const struct_field = @typeInfo(@TypeOf(.{"hi"})).Struct.fields[0]; + const value = @ptrCast(*align(1) const *const [2:0]u8, struct_field.default_value.?).*; + comptime std.debug.assert(value[0] == 'h'); +} From e778e4714006a78814dc09c66713073ab2f05515 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Feb 2023 17:58:27 -0700 Subject: [PATCH 067/122] add test coverage for fixed bug. closes #5516 --- test/behavior/cast.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 16f3c6e2dd..927caa965b 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1568,3 +1568,12 @@ test "@volatileCast without a result location" { try expect(@TypeOf(z) == *i32); try expect(z.* == 1234); } + +test "coercion from single-item pointer to @as to slice" { + var x: u32 = 1; + + // Why the following line gets a compile error? + const t: []u32 = @as(*[1]u32, &x); + + try expect(t[0] == 1); +} From ec4cd87ed76b47eae9fce95f18ec396aa44f2c0f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 19 Feb 2023 18:10:13 -0700 Subject: [PATCH 068/122] add test coverage for fixed bug. closes #5518 --- test/behavior/basic.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index b82bfab99e..669cacfc40 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1143,3 +1143,17 @@ test "orelse coercion as function argument" { var foo = Container.init(optional orelse .{}); try expect(foo.a.?.start == -1); } + +test "runtime-known globals initialized with undefined" { + const S = struct { + var array: [10]u32 = [_]u32{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + var vp: [*]u32 = undefined; + var s: []u32 = undefined; + }; + + S.vp = &S.array; + S.s = S.vp[0..5]; + + try expect(S.s[0] == 1); + try expect(S.s[4] == 5); +} From a74f800dd79fec48a50152394c9fb3c2d6f080d0 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 20 Feb 2023 16:58:48 +1100 Subject: [PATCH 069/122] std.compress.zstandard: update for multi-for-loop change --- lib/std/compress/zstandard.zig | 7 ++--- lib/std/compress/zstandard/decode/block.zig | 30 +++++++------------ lib/std/compress/zstandard/decode/fse.zig | 17 +++++------ lib/std/compress/zstandard/decode/huffman.zig | 5 ++-- 4 files changed, 22 insertions(+), 37 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index 61ad9b69fd..bc69955bd1 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -214,9 +214,8 @@ pub fn ZstandardStream( } const size = @min(self.buffer.len(), buffer.len); - var count: usize = 0; - while (count < size) : (count += 1) { - buffer[count] = self.buffer.read().?; + for (0..size) |i| { + buffer[i] = self.buffer.read().?; } if (self.state == .LastBlock and self.buffer.len() == 0) { self.state = .NewFrame; @@ -227,7 +226,7 @@ pub fn ZstandardStream( self.allocator.free(self.sequence_buffer); self.buffer.deinit(self.allocator); } - return count; + return size; } }; } diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index 16465e654d..ba8e8d998c 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -453,8 +453,7 @@ pub const DecodeState = struct { self.written_count += len; }, .rle => { - var i: usize = 0; - while (i < len) : (i += 1) { + for (0..len) |i| { dest[i] = self.literal_streams.one[0]; } self.literal_written_count += len; @@ -471,8 +470,7 @@ pub const DecodeState = struct { var bits_read: u4 = 0; var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; var bit_count_to_read: u4 = starting_bit_count; - var i: usize = 0; - while (i < len) : (i += 1) { + for (0..len) |i| { var prefix: u16 = 0; while (true) { const new_bits = self.readLiteralsBits(bit_count_to_read) catch |err| { @@ -528,8 +526,7 @@ pub const DecodeState = struct { self.written_count += len; }, .rle => { - var i: usize = 0; - while (i < len) : (i += 1) { + for (0..len) |_| { dest.writeAssumeCapacity(self.literal_streams.one[0]); } self.literal_written_count += len; @@ -546,8 +543,7 @@ pub const DecodeState = struct { var bits_read: u4 = 0; var huffman_tree_index: usize = huffman_tree.symbol_count_minus_one; var bit_count_to_read: u4 = starting_bit_count; - var i: usize = 0; - while (i < len) : (i += 1) { + for (0..len) |_| { var prefix: u16 = 0; while (true) { const new_bits = try self.readLiteralsBits(bit_count_to_read); @@ -630,8 +626,7 @@ pub fn decodeBlock( .rle => { if (src.len < 1) return error.MalformedRleBlock; if (dest[written_count..].len < block_size) return error.DestTooSmall; - var write_pos: usize = written_count; - while (write_pos < block_size + written_count) : (write_pos += 1) { + for (written_count..block_size + written_count) |write_pos| { dest[write_pos] = src[0]; } consumed_count.* += 1; @@ -664,8 +659,7 @@ pub fn decodeBlock( return error.MalformedCompressedBlock; var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { + for (0..sequences_header.sequence_count) |i| { const write_pos = written_count + bytes_written; const decompressed_size = decode_state.decodeSequenceSlice( dest, @@ -734,8 +728,7 @@ pub fn decodeBlockRingBuffer( }, .rle => { if (src.len < 1) return error.MalformedRleBlock; - var write_pos: usize = 0; - while (write_pos < block_size) : (write_pos += 1) { + for (0..block_size) |_| { dest.writeAssumeCapacity(src[0]); } consumed_count.* += 1; @@ -768,8 +761,7 @@ pub fn decodeBlockRingBuffer( return error.MalformedCompressedBlock; var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { + for (0..sequences_header.sequence_count) |i| { const decompressed_size = decode_state.decodeSequenceRingBuffer( dest, &bit_stream, @@ -840,8 +832,7 @@ pub fn decodeBlockReader( }, .rle => { const byte = try source.readByte(); - var i: usize = 0; - while (i < block_size) : (i += 1) { + for (0..block_size) |_| { dest.writeAssumeCapacity(byte); } decode_state.written_count += block_size; @@ -866,8 +857,7 @@ pub fn decodeBlockReader( return error.MalformedCompressedBlock; var sequence_size_limit = block_size_max; - var i: usize = 0; - while (i < sequences_header.sequence_count) : (i += 1) { + for (0..sequences_header.sequence_count) |i| { const decompressed_size = decode_state.decodeSequenceRingBuffer( dest, &bit_stream, diff --git a/lib/std/compress/zstandard/decode/fse.zig b/lib/std/compress/zstandard/decode/fse.zig index 726891873c..41a34d0fc1 100644 --- a/lib/std/compress/zstandard/decode/fse.zig +++ b/lib/std/compress/zstandard/decode/fse.zig @@ -47,8 +47,7 @@ pub fn decodeFseTable( while (true) { const repeat_flag = try bit_reader.readBitsNoEof(u2, 2); if (repeat_flag + value_count > 256) return error.MalformedFseTable; - var i: usize = 0; - while (i < repeat_flag) : (i += 1) { + for (0..repeat_flag) |_| { values[value_count] = 1; value_count += 1; } @@ -75,7 +74,7 @@ fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { assert(total_probability <= 1 << 9); var less_than_one_count: usize = 0; - for (values) |value, i| { + for (values, 0..) |value, i| { if (value == 0) { entries[entries.len - 1 - less_than_one_count] = Table.Fse{ .symbol = @intCast(u8, i), @@ -88,7 +87,7 @@ fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { var position: usize = 0; var temp_states: [1 << 9]u16 = undefined; - for (values) |value, symbol| { + for (values, 0..) |value, symbol| { if (value == 0 or value == 1) continue; const probability = value - 1; @@ -99,8 +98,7 @@ fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { const single_state_count = probability - double_state_count; const share_size_log = std.math.log2_int(u16, share_size); - var i: u16 = 0; - while (i < probability) : (i += 1) { + for (0..probability) |i| { temp_states[i] = @intCast(u16, position); position += (entries.len >> 1) + (entries.len >> 3) + 3; position &= entries.len - 1; @@ -110,16 +108,15 @@ fn buildFseTable(values: []const u16, entries: []Table.Fse) !void { } } std.sort.sort(u16, temp_states[0..probability], {}, std.sort.asc(u16)); - i = 0; - while (i < probability) : (i += 1) { + for (0..probability) |i| { entries[temp_states[i]] = if (i < double_state_count) Table.Fse{ .symbol = @intCast(u8, symbol), .bits = share_size_log + 1, - .baseline = single_state_count * share_size + i * 2 * share_size, + .baseline = single_state_count * share_size + @intCast(u16, i) * 2 * share_size, } else Table.Fse{ .symbol = @intCast(u8, symbol), .bits = share_size_log, - .baseline = (i - double_state_count) * share_size, + .baseline = (@intCast(u16, i) - double_state_count) * share_size, }; } } diff --git a/lib/std/compress/zstandard/decode/huffman.zig b/lib/std/compress/zstandard/decode/huffman.zig index 9cc0d49479..68aac85320 100644 --- a/lib/std/compress/zstandard/decode/huffman.zig +++ b/lib/std/compress/zstandard/decode/huffman.zig @@ -95,8 +95,7 @@ fn assignWeights(huff_bits: *readers.ReverseBitReader, accuracy_log: usize, entr fn decodeDirectHuffmanTree(source: anytype, encoded_symbol_count: usize, weights: *[256]u4) !usize { const weights_byte_count = (encoded_symbol_count + 1) / 2; - var i: usize = 0; - while (i < weights_byte_count) : (i += 1) { + for (0..weights_byte_count) |i| { const byte = try source.readByte(); weights[2 * i] = @intCast(u4, byte >> 4); weights[2 * i + 1] = @intCast(u4, byte & 0xF); @@ -105,7 +104,7 @@ fn decodeDirectHuffmanTree(source: anytype, encoded_symbol_count: usize, weights } fn assignSymbols(weight_sorted_prefixed_symbols: []LiteralsSection.HuffmanTree.PrefixedSymbol, weights: [256]u4) usize { - for (weight_sorted_prefixed_symbols) |_, i| { + for (0..weight_sorted_prefixed_symbols.len) |i| { weight_sorted_prefixed_symbols[i] = .{ .symbol = @intCast(u8, i), .weight = undefined, From a7de8dc2ddb5b1fa45d2fcfba4b305fef9f59be4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 19 Feb 2023 23:18:24 +0100 Subject: [PATCH 070/122] x86: alloc new mcv in bitcast if cannot reuse operand Implement missing pointees when ptr is in register. --- src/arch/x86_64/CodeGen.zig | 67 +++++++++++++++++++++++++++++++------ test/behavior/basic.zig | 1 + 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f63d80486e..20e443b83c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -920,9 +920,6 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { const mod = self.bin_file.options.module.?; return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; - const abi_align = elem_ty.abiAlignment(self.target.*); - if (abi_align > self.stack_align) - self.stack_align = abi_align; if (reg_ok) { switch (elem_ty.zigTypeTag()) { @@ -951,6 +948,10 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { }, } } + + const abi_align = elem_ty.abiAlignment(self.target.*); + if (abi_align > self.stack_align) + self.stack_align = abi_align; const stack_offset = try self.allocMem(inst, abi_size, abi_align); return MCValue{ .stack_offset = @intCast(i32, stack_offset) }; } @@ -990,7 +991,7 @@ fn revertState(self: *Self, state: State) void { pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { const stack_mcv = try self.allocRegOrMem(inst, false); - log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); + log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv }); const reg_mcv = self.getResolvedInstValue(inst); switch (reg_mcv) { .register => |other| { @@ -1016,7 +1017,7 @@ pub fn spillEflagsIfOccupied(self: *Self) !void { }; try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); - log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); + log.debug("spilling %{d} to mcv {any}", .{ inst_to_save, new_mcv }); const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); @@ -2114,6 +2115,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { }; break :result dst_mcv; }; + log.debug("airSliceLen(%{d}): {}", .{ inst, result }); return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -2641,6 +2643,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo fn airLoad(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const elem_ty = self.air.typeOfIndex(inst); + const elem_size = elem_ty.abiSize(self.target.*); const result: MCValue = result: { if (!elem_ty.hasRuntimeBitsIgnoreComptime()) break :result MCValue.none; @@ -2651,13 +2654,14 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { break :result MCValue.dead; const dst_mcv: MCValue = blk: { - if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { + if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { // The MCValue that holds the pointer can be re-used as the value. break :blk ptr; } else { break :blk try self.allocRegOrMem(inst, true); } }; + log.debug("airLoad(%{d}): {} <- {}", .{ inst, dst_mcv, ptr }); try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); break :result dst_mcv; }; @@ -2728,10 +2732,12 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type switch (value) { .none => unreachable, - .undef => unreachable, .dead => unreachable, .unreach => unreachable, .eflags => unreachable, + .undef => { + try self.genSetReg(value_ty, reg, value); + }, .immediate => |imm| { switch (abi_size) { 1, 2, 4 => { @@ -2773,6 +2779,30 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .register => |src_reg| { try self.genInlineMemcpyRegisterRegister(value_ty, reg, src_reg, 0); }, + .register_overflow => |ro| { + const ro_reg_lock = self.register_manager.lockReg(ro.reg); + defer if (ro_reg_lock) |lock| self.register_manager.unlockReg(lock); + + const wrapped_ty = value_ty.structFieldType(0); + try self.genInlineMemcpyRegisterRegister(wrapped_ty, reg, ro.reg, 0); + + const overflow_bit_ty = value_ty.structFieldType(1); + const overflow_bit_offset = value_ty.structFieldOffset(1, self.target.*); + const tmp_reg = try self.register_manager.allocReg(null, gp); + _ = try self.addInst(.{ + .tag = .cond_set_byte, + .ops = Mir.Inst.Ops.encode(.{ + .reg1 = tmp_reg.to8(), + }), + .data = .{ .cc = ro.eflags }, + }); + try self.genInlineMemcpyRegisterRegister( + overflow_bit_ty, + reg, + tmp_reg, + -@intCast(i32, overflow_bit_offset), + ); + }, .linker_load, .memory, .stack_offset, @@ -2787,8 +2817,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .dest_stack_base = reg.to64(), }); }, - else => |other| { - return self.fail("TODO implement set pointee with {}", .{other}); + .ptr_stack_offset => { + const tmp_reg = try self.copyToTmpRegister(value_ty, value); + return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); }, } }, @@ -2902,6 +2933,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { const ptr_ty = self.air.typeOf(bin_op.lhs); const value = try self.resolveInst(bin_op.rhs); const value_ty = self.air.typeOf(bin_op.rhs); + log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value }); try self.store(ptr, value, ptr_ty, value_ty); return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -6321,7 +6353,22 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result = try self.resolveInst(ty_op.operand); + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(ty_op.operand); + if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; + + const operand_lock = switch (operand) { + .register => |reg| self.register_manager.lockReg(reg), + .register_overflow => |ro| self.register_manager.lockReg(ro.reg), + else => null, + }; + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + + const dest = try self.allocRegOrMem(inst, true); + try self.setRegOrMem(self.air.typeOfIndex(inst), dest, operand); + break :result dest; + }; + log.debug("airBitCast(%{d}): {}", .{ inst, result }); return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index b82bfab99e..e026c1800a 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -387,6 +387,7 @@ fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { } test "take address of parameter" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From 87d358024fe14973f6f5276dd4e29a1fc1cee10d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Feb 2023 10:37:22 +0100 Subject: [PATCH 071/122] re-enable x86_64-linux self-hosted behaviour test suite --- test/tests.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/tests.zig b/test/tests.zig index 851de9f2a6..035311372f 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -58,14 +58,14 @@ const test_targets = blk: { .link_libc = true, .backend = .stage2_c, }, - //.{ - // .target = .{ - // .cpu_arch = .x86_64, - // .os_tag = .linux, - // .abi = .none, - // }, - // .backend = .stage2_x86_64, - //}, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + .abi = .none, + }, + .backend = .stage2_x86_64, + }, .{ .target = .{ .cpu_arch = .aarch64, From 59a9373c71854ff79b22d3ea154cc3e06c130cc1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Feb 2023 10:51:36 +0100 Subject: [PATCH 072/122] aarch64: alloc new mcv in bitcast if cannot reuse operand --- src/arch/aarch64/CodeGen.zig | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 5b0db30757..e7fef20a4f 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3958,7 +3958,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type switch (value) { .dead => unreachable, - .undef => unreachable, + .undef => { + try self.genSetReg(value_ty, addr_reg, value); + }, .register => |value_reg| { log.debug("store: register {} to {}", .{ value_reg, addr_reg }); try self.genStrRegister(value_reg, addr_reg, value_ty); @@ -5870,7 +5872,22 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result = try self.resolveInst(ty_op.operand); + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(ty_op.operand); + if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; + + const operand_lock = switch (operand) { + .register => |reg| self.register_manager.lockReg(reg), + .register_with_overflow => |rwo| self.register_manager.lockReg(rwo.reg), + else => null, + }; + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + + const dest_ty = self.air.typeOfIndex(inst); + const dest = try self.allocRegOrMem(dest_ty, true, inst); + try self.setRegOrMem(dest_ty, dest, operand); + break :result dest; + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } From 528c43233f0f566930efc7fc53c4f001dea4dfda Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Feb 2023 12:13:14 +0100 Subject: [PATCH 073/122] arm: alloc new mcv in bitcast if cannot reuse operand --- src/arch/arm/CodeGen.zig | 23 +++++++++++++++++++++-- test/behavior/const_slice_child.zig | 1 + test/behavior/eval.zig | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 0fbf1ee984..fc89b2e26b 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -2765,7 +2765,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type switch (value) { .dead => unreachable, - .undef => unreachable, + .undef => { + try self.genSetReg(value_ty, addr_reg, value); + }, .register => |value_reg| { try self.genStrRegister(value_reg, addr_reg, value_ty); }, @@ -5816,7 +5818,24 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result = try self.resolveInst(ty_op.operand); + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(ty_op.operand); + if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; + + const operand_lock = switch (operand) { + .register, + .register_c_flag, + .register_v_flag, + => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + + const dest_ty = self.air.typeOfIndex(inst); + const dest = try self.allocRegOrMem(dest_ty, true, inst); + try self.setRegOrMem(dest_ty, dest, operand); + break :result dest; + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } diff --git a/test/behavior/const_slice_child.zig b/test/behavior/const_slice_child.zig index 09c6a7233d..9ce526562c 100644 --- a/test/behavior/const_slice_child.zig +++ b/test/behavior/const_slice_child.zig @@ -9,6 +9,7 @@ var argv: [*]const [*]const u8 = undefined; test "const slice child" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const strs = [_][*]const u8{ "one", "two", "three" }; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 680b0576d5..8364196f94 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1338,6 +1338,7 @@ test "lazy sizeof is resolved in division" { test "lazy value is resolved as slice operand" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const A = struct { a: u32 }; From 0aee40bd13fa72ac4ca41e133440917c0ed94ffb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 20 Feb 2023 12:19:40 +0100 Subject: [PATCH 074/122] riscv64+sparc64: alloc new mcv in bitcast if cannot reuse operand --- src/arch/riscv64/CodeGen.zig | 15 ++++++++++++++- src/arch/sparc64/CodeGen.zig | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index b97ac727c1..afcf4b0bb7 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -2338,7 +2338,20 @@ fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result = try self.resolveInst(ty_op.operand); + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(ty_op.operand); + if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; + + const operand_lock = switch (operand) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + + const dest = try self.allocRegOrMem(inst, true); + try self.setRegOrMem(self.air.typeOfIndex(inst), dest, operand); + break :result dest; + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 8344b6e0cc..c8f77fe702 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -1091,7 +1091,21 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result = try self.resolveInst(ty_op.operand); + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(ty_op.operand); + if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; + + const operand_lock = switch (operand) { + .register => |reg| self.register_manager.lockReg(reg), + .register_with_overflow => |rwo| self.register_manager.lockReg(rwo.reg), + else => null, + }; + defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + + const dest = try self.allocRegOrMem(inst, true); + try self.setRegOrMem(self.air.typeOfIndex(inst), dest, operand); + break :result dest; + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } From 5a7d80a5e7d0aaafc6ed3017e0e4342dcd476a51 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Mon, 20 Feb 2023 17:15:21 +0100 Subject: [PATCH 075/122] Linker: -z should be equivalent to -z (#14680) lld accepts both syntaxes, but we were rejecting (and, before 3f7e9ff597a3514bb1c4f1900027c40682ac9f13, ignoring) the former. In particular, "cargo-zigbuild" was broken since Rust unconditionally adds "-znoexecstack" (not "-z noexecstack") on non-Windows platforms. Co-authored-by: Andrew Kelley --- src/main.zig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main.zig b/src/main.zig index e80be06a36..e42974944b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1955,12 +1955,15 @@ fn buildOutputType( linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, arg1) orelse { fatal("expected [none|zlib] after --compress-debug-sections, found '{s}'", .{arg1}); }; - } else if (mem.eql(u8, arg, "-z")) { - i += 1; - if (i >= linker_args.items.len) { - fatal("expected linker extension flag after '{s}'", .{arg}); + } else if (mem.startsWith(u8, arg, "-z")) { + var z_arg = arg[2..]; + if (z_arg.len == 0) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker extension flag after '{s}'", .{arg}); + } + z_arg = linker_args.items[i]; } - const z_arg = linker_args.items[i]; if (mem.eql(u8, z_arg, "nodelete")) { linker_z_nodelete = true; } else if (mem.eql(u8, z_arg, "notext")) { From 6214f66dc108bd34a3ffa1ba5ac7704050a2f156 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Mon, 20 Feb 2023 00:36:05 +0100 Subject: [PATCH 076/122] trim(Left|Right): clarify that values_to_strip is a set The parameter could be confused with a prefix or suffix, instead of a set of values. --- lib/std/mem.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index fdd1c05862..b9b5fb1004 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -941,21 +941,21 @@ pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { return true; } -/// Remove values from the beginning of a slice. +/// Remove a set of values from the beginning of a slice. pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { var begin: usize = 0; while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {} return slice[begin..]; } -/// Remove values from the end of a slice. +/// Remove a set of values from the end of a slice. pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { var end: usize = slice.len; while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {} return slice[0..end]; } -/// Remove values from the beginning and end of a slice. +/// Remove a set of values from the beginning and end of a slice. pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { var begin: usize = 0; var end: usize = slice.len; From a34c2de7bcdce8e563ee23bcf0f508e556efd763 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 21 Feb 2023 11:30:59 +1100 Subject: [PATCH 077/122] std.hash: use std.math.rotl in Xxhash64 and Xxhash32 --- lib/std/hash/xxhash.zig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/std/hash/xxhash.zig b/lib/std/hash/xxhash.zig index 633bbbfad2..bf4877e029 100644 --- a/lib/std/hash/xxhash.zig +++ b/lib/std/hash/xxhash.zig @@ -2,9 +2,7 @@ const std = @import("std"); const mem = std.mem; const expectEqual = std.testing.expectEqual; -inline fn rotl(comptime count: comptime_int, value: anytype) @TypeOf(value) { - return (value << count) | (value >> (@bitSizeOf(@TypeOf(value)) - count)); -} +const rotl = std.math.rotl; pub const XxHash64 = struct { acc1: u64, @@ -71,7 +69,7 @@ pub const XxHash64 = struct { inline fn round(acc: u64, lane: u64) u64 { const a = acc +% (lane *% prime_2); - const b = rotl(31, a); + const b = rotl(u64, a, 31); return b *% prime_1; } @@ -81,7 +79,8 @@ pub const XxHash64 = struct { if (self.byte_count < 32) { acc = self.seed +% prime_5; } else { - acc = rotl(1, self.acc1) +% rotl(7, self.acc2) +% rotl(12, self.acc3) +% rotl(18, self.acc4); + acc = rotl(u64, self.acc1, 1) +% rotl(u64, self.acc2, 7) +% + rotl(u64, self.acc3, 12) +% rotl(u64, self.acc4, 18); acc = mergeAccumulator(acc, self.acc1); acc = mergeAccumulator(acc, self.acc2); acc = mergeAccumulator(acc, self.acc3); @@ -94,14 +93,14 @@ pub const XxHash64 = struct { while (pos + 8 <= self.buf_len) : (pos += 8) { const lane = mem.readIntLittle(u64, self.buf[pos..][0..8]); acc ^= round(0, lane); - acc = rotl(27, acc) *% prime_1; + acc = rotl(u64, acc, 27) *% prime_1; acc +%= prime_4; } if (pos + 4 <= self.buf_len) { const lane = @as(u64, mem.readIntLittle(u32, self.buf[pos..][0..4])); acc ^= lane *% prime_1; - acc = rotl(23, acc) *% prime_2; + acc = rotl(u64, acc, 23) *% prime_2; acc +%= prime_3; pos += 4; } @@ -109,7 +108,7 @@ pub const XxHash64 = struct { while (pos < self.buf_len) : (pos += 1) { const lane = @as(u64, self.buf[pos]); acc ^= lane *% prime_5; - acc = rotl(11, acc) *% prime_1; + acc = rotl(u64, acc, 11) *% prime_1; } acc ^= acc >> 33; @@ -199,7 +198,7 @@ pub const XxHash32 = struct { inline fn round(acc: u32, lane: u32) u32 { const a = acc +% (lane *% prime_2); - const b = rotl(13, a); + const b = rotl(u32, a, 13); return b *% prime_1; } @@ -209,7 +208,8 @@ pub const XxHash32 = struct { if (self.byte_count < 16) { acc = self.seed +% prime_5; } else { - acc = rotl(1, self.acc1) +% rotl(7, self.acc2) +% rotl(12, self.acc3) +% rotl(18, self.acc4); + acc = rotl(u32, self.acc1, 1) +% rotl(u32, self.acc2, 7) +% + rotl(u32, self.acc3, 12) +% rotl(u32, self.acc4, 18); } acc = acc +% @intCast(u32, self.byte_count) +% @intCast(u32, self.buf_len); @@ -218,13 +218,13 @@ pub const XxHash32 = struct { while (pos + 4 <= self.buf_len) : (pos += 4) { const lane = mem.readIntLittle(u32, self.buf[pos..][0..4]); acc +%= lane *% prime_3; - acc = rotl(17, acc) *% prime_4; + acc = rotl(u32, acc, 17) *% prime_4; } while (pos < self.buf_len) : (pos += 1) { const lane = @as(u32, self.buf[pos]); acc +%= lane *% prime_5; - acc = rotl(11, acc) *% prime_1; + acc = rotl(u32, acc, 11) *% prime_1; } acc ^= acc >> 15; From 12d9f73ce8e4295541ccecf2d748a2260f2e3957 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 21 Feb 2023 11:33:52 +1100 Subject: [PATCH 078/122] std.compress.zstandard: remove use of usingnamespace --- lib/std/compress/zstandard.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index bc69955bd1..6c6e53478d 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -2,10 +2,11 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const types = @import("zstandard/types.zig"); +pub const frame = types.frame; +pub const compressed_block = types.compressed_block; const RingBuffer = @import("zstandard/RingBuffer.zig"); pub const decompress = @import("zstandard/decompress.zig"); -pub usingnamespace @import("zstandard/types.zig"); pub fn ZstandardStream( comptime ReaderType: type, From 1c518bd993b159d80a24925dc09ae7da5035ee05 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 21 Feb 2023 11:46:03 +1100 Subject: [PATCH 079/122] std.compress.zstandard: rename ZStandardStream -> DecompressStream --- lib/std/compress/zstandard.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index 6c6e53478d..5f61c4dc30 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -8,7 +8,7 @@ pub const compressed_block = types.compressed_block; const RingBuffer = @import("zstandard/RingBuffer.zig"); pub const decompress = @import("zstandard/decompress.zig"); -pub fn ZstandardStream( +pub fn DecompressStream( comptime ReaderType: type, comptime verify_checksum: bool, comptime window_size_max: usize, @@ -232,19 +232,19 @@ pub fn ZstandardStream( }; } -pub fn zstandardStream( +pub fn decompressStream( allocator: Allocator, reader: anytype, comptime window_size_max: usize, -) ZstandardStream(@TypeOf(reader), true, window_size_max) { - return ZstandardStream(@TypeOf(reader), true, 8 * (1 << 20)).init(allocator, reader); +) DecompressStream(@TypeOf(reader), true, window_size_max) { + return DecompressStream(@TypeOf(reader), true, 8 * (1 << 20)).init(allocator, reader); } fn testDecompress(data: []const u8) ![]u8 { var in_stream = std.io.fixedBufferStream(data); - var stream = zstandardStream(std.testing.allocator, in_stream.reader(), 1 << 23); - defer stream.deinit(); - const result = stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); + var zstd_stream = decompressStream(std.testing.allocator, in_stream.reader(), 1 << 23); + defer zstd_stream.deinit(); + const result = zstd_stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); return result; } From c7c35bf9e61035ead3098d109dc894b285373622 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 21 Feb 2023 12:07:44 +1100 Subject: [PATCH 080/122] std.RingBuffer: add (non-concurrent) RingBuffer implementation --- .../{compress/zstandard => }/RingBuffer.zig | 40 +++++++++++++------ lib/std/compress/zstandard.zig | 2 +- lib/std/compress/zstandard/decode/block.zig | 4 +- lib/std/compress/zstandard/decompress.zig | 3 +- lib/std/std.zig | 1 + 5 files changed, 31 insertions(+), 19 deletions(-) rename lib/std/{compress/zstandard => }/RingBuffer.zig (72%) diff --git a/lib/std/compress/zstandard/RingBuffer.zig b/lib/std/RingBuffer.zig similarity index 72% rename from lib/std/compress/zstandard/RingBuffer.zig rename to lib/std/RingBuffer.zig index 5bd7e5d1f9..857775b5a0 100644 --- a/lib/std/compress/zstandard/RingBuffer.zig +++ b/lib/std/RingBuffer.zig @@ -1,8 +1,13 @@ -//! This ring buffer stores read and write indices while being able to utilise the full -//! backing slice by incrementing the indices modulo twice the slice's length and reducing -//! indices modulo the slice's length on slice access. This means that whether the ring buffer -//! if full or empty can be distinguised by looking at the different between the read and write -//! indices without adding an extra boolean flag or having to reserve a slot in the buffer. +//! This ring buffer stores read and write indices while being able to utilise +//! the full backing slice by incrementing the indices modulo twice the slice's +//! length and reducing indices modulo the slice's length on slice access. This +//! means that whether the ring buffer if full or empty can be distinguished by +//! looking at the difference between the read and write indices without adding +//! an extra boolean flag or having to reserve a slot in the buffer. +//! +//! This ring buffer has not been implemented with thread safety in mind, and +//! therefore should not be assumed to be suitable for use cases involving +//! separate reader and writer threads. const Allocator = @import("std").mem.Allocator; const assert = @import("std").debug.assert; @@ -15,7 +20,7 @@ write_index: usize, pub const Error = error{Full}; -/// Allocate a new `RingBuffer` +/// Allocate a new `RingBuffer`; `deinit()` should be called to free the buffer. pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { const bytes = try allocator.alloc(u8, capacity); return RingBuffer{ @@ -25,7 +30,8 @@ pub fn init(allocator: Allocator, capacity: usize) Allocator.Error!RingBuffer { }; } -/// Free a `RingBuffer` +/// Free the data backing a `RingBuffer`; must be passed the same `Allocator` as +/// `init()`. pub fn deinit(self: *RingBuffer, allocator: Allocator) void { allocator.free(self.data); self.* = undefined; @@ -36,7 +42,7 @@ pub fn mask(self: RingBuffer, index: usize) usize { return index % self.data.len; } -/// Returns `index` module twice the length of the backing slice. +/// Returns `index` modulo twice the length of the backing slice. pub fn mask2(self: RingBuffer, index: usize) usize { return index % (2 * self.data.len); } @@ -55,7 +61,7 @@ pub fn writeAssumeCapacity(self: *RingBuffer, byte: u8) void { self.write_index = self.mask2(self.write_index + 1); } -/// Write `bytes` into the ring bufffer. Returns `error.Full` if the ring +/// Write `bytes` into the ring buffer. Returns `error.Full` if the ring /// buffer does not have enough space, without writing any data. pub fn writeSlice(self: *RingBuffer, bytes: []const u8) Error!void { if (self.len() + bytes.len > self.data.len) return error.Full; @@ -72,6 +78,13 @@ pub fn writeSliceAssumeCapacity(self: *RingBuffer, bytes: []const u8) void { /// ring buffer is empty. pub fn read(self: *RingBuffer) ?u8 { if (self.isEmpty()) return null; + return self.readAssumeLength(); +} + +/// Consume a byte from the ring buffer and return it; asserts that the buffer +/// is not empty. +pub fn readAssumeLength(self: *RingBuffer) u8 { + assert(!self.isEmpty()); const byte = self.data[self.mask(self.read_index)]; self.read_index = self.mask2(self.read_index + 1); return byte; @@ -95,15 +108,15 @@ pub fn len(self: RingBuffer) usize { } /// A `Slice` represents a region of a ring buffer. The region is split into two -/// sections as the ring buffer data will not be contiguous if the desired region -/// wraps to the start of the backing slice. +/// sections as the ring buffer data will not be contiguous if the desired +/// region wraps to the start of the backing slice. pub const Slice = struct { first: []u8, second: []u8, }; -/// Returns a `Slice` for the region of the ring buffer staring at `self.mask(start_unmasked)` -/// with the specified length. +/// Returns a `Slice` for the region of the ring buffer starting at +/// `self.mask(start_unmasked)` with the specified length. pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { assert(length <= self.data.len); const slice1_start = self.mask(start_unmasked); @@ -117,6 +130,7 @@ pub fn sliceAt(self: RingBuffer, start_unmasked: usize, length: usize) Slice { } /// Returns a `Slice` for the last `length` bytes written to the ring buffer. +/// Does not check that any bytes have been written into the region. pub fn sliceLast(self: RingBuffer, length: usize) Slice { return self.sliceAt(self.write_index + self.data.len - length, length); } diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index 5f61c4dc30..fcffed99f1 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -1,11 +1,11 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const RingBuffer = std.RingBuffer; const types = @import("zstandard/types.zig"); pub const frame = types.frame; pub const compressed_block = types.compressed_block; -const RingBuffer = @import("zstandard/RingBuffer.zig"); pub const decompress = @import("zstandard/decompress.zig"); pub fn DecompressStream( diff --git a/lib/std/compress/zstandard/decode/block.zig b/lib/std/compress/zstandard/decode/block.zig index ba8e8d998c..4b7353f63c 100644 --- a/lib/std/compress/zstandard/decode/block.zig +++ b/lib/std/compress/zstandard/decode/block.zig @@ -1,5 +1,6 @@ const std = @import("std"); const assert = std.debug.assert; +const RingBuffer = std.RingBuffer; const types = @import("../types.zig"); const frame = types.frame; @@ -8,9 +9,6 @@ const LiteralsSection = types.compressed_block.LiteralsSection; const SequencesSection = types.compressed_block.SequencesSection; const huffman = @import("huffman.zig"); - -const RingBuffer = @import("../RingBuffer.zig"); - const readers = @import("../readers.zig"); const decodeFseTable = @import("fse.zig").decodeFseTable; diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index 31c5660642..ffa01b94f1 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -1,6 +1,7 @@ const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; +const RingBuffer = std.RingBuffer; const types = @import("types.zig"); const frame = types.frame; @@ -12,8 +13,6 @@ const Table = types.compressed_block.Table; pub const block = @import("decode/block.zig"); -pub const RingBuffer = @import("RingBuffer.zig"); - const readers = @import("readers.zig"); const readInt = std.mem.readIntLittle; diff --git a/lib/std/std.zig b/lib/std/std.zig index 5b0963ba20..4a6d330003 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -31,6 +31,7 @@ pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceE pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; pub const PriorityDequeue = @import("priority_dequeue.zig").PriorityDequeue; pub const Progress = @import("Progress.zig"); +pub const RingBuffer = @import("RingBuffer.zig"); pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SemanticVersion = @import("SemanticVersion.zig"); pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; From 705d2a3c2cd94faf8e16c660b3b342d6fe900e55 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 17 Feb 2023 01:44:08 +0000 Subject: [PATCH 081/122] Implement new module CLI --- src/Autodoc.zig | 10 +-- src/Compilation.zig | 100 +++++++++++---------- src/Module.zig | 62 ++++++++----- src/Package.zig | 123 +++++++++++++++++++------- src/Sema.zig | 6 +- src/main.zig | 206 ++++++++++++++++++++++++++++---------------- src/test.zig | 1 - 7 files changed, 324 insertions(+), 184 deletions(-) diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 47dd4a28f7..3cf3fff4c0 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -860,17 +860,9 @@ fn walkInstruction( const str_tok = data[inst_index].str_tok; var path = str_tok.get(file.zir); - const maybe_other_package: ?*Package = blk: { - if (self.module.main_pkg_is_std and std.mem.eql(u8, path, "std")) { - path = "std"; - break :blk self.module.main_pkg; - } else { - break :blk file.pkg.table.get(path); - } - }; // importFile cannot error out since all files // are already loaded at this point - if (maybe_other_package) |other_package| { + if (file.pkg.table.get(path)) |other_package| { const result = try self.packages.getOrPut(self.arena, other_package); // Immediately add this package to the import table of our diff --git a/src/Compilation.zig b/src/Compilation.zig index ebc0e9b563..9b054fbe62 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1596,36 +1596,53 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const builtin_pkg = try Package.createWithDir( gpa, - "builtin", zig_cache_artifact_directory, null, "builtin.zig", ); errdefer builtin_pkg.destroy(gpa); - const std_pkg = try Package.createWithDir( - gpa, - "std", - options.zig_lib_directory, - "std", - "std.zig", - ); - errdefer std_pkg.destroy(gpa); + // When you're testing std, the main module is std. In that case, we'll just set the std + // module to the main one, since avoiding the errors caused by duplicating it is more + // effort than it's worth. + const main_pkg_is_std = m: { + const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ + options.zig_lib_directory.path orelse ".", + "std", + "std.zig", + }); + defer arena.free(std_path); + const main_path = try std.fs.path.resolve(arena, &[_][]const u8{ + main_pkg.root_src_directory.path orelse ".", + main_pkg.root_src_path, + }); + defer arena.free(main_path); + break :m mem.eql(u8, main_path, std_path); + }; + + const std_pkg = if (main_pkg_is_std) + main_pkg + else + try Package.createWithDir( + gpa, + options.zig_lib_directory, + "std", + "std.zig", + ); + + errdefer if (!main_pkg_is_std) std_pkg.destroy(gpa); const root_pkg = if (options.is_test) root_pkg: { - // TODO: we currently have two packages named 'root' here, which is weird. This - // should be changed as part of the resolution of #12201 const test_pkg = if (options.test_runner_path) |test_runner| test_pkg: { const test_dir = std.fs.path.dirname(test_runner); const basename = std.fs.path.basename(test_runner); - const pkg = try Package.create(gpa, "root", test_dir, basename); + const pkg = try Package.create(gpa, test_dir, basename); // copy package table from main_pkg to root_pkg pkg.table = try main_pkg.table.clone(gpa); break :test_pkg pkg; } else try Package.createWithDir( gpa, - "root", options.zig_lib_directory, null, "test_runner.zig", @@ -1639,7 +1656,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { const compiler_rt_pkg = if (include_compiler_rt and options.output_mode == .Obj) compiler_rt_pkg: { break :compiler_rt_pkg try Package.createWithDir( gpa, - "compiler_rt", options.zig_lib_directory, null, "compiler_rt.zig", @@ -1647,28 +1663,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } else null; errdefer if (compiler_rt_pkg) |p| p.destroy(gpa); - try main_pkg.addAndAdopt(gpa, builtin_pkg); - try main_pkg.add(gpa, root_pkg); - try main_pkg.addAndAdopt(gpa, std_pkg); + try main_pkg.add(gpa, "builtin", builtin_pkg); + try main_pkg.add(gpa, "root", root_pkg); + try main_pkg.add(gpa, "std", std_pkg); if (compiler_rt_pkg) |p| { - try main_pkg.addAndAdopt(gpa, p); + try main_pkg.add(gpa, "compiler_rt", p); } - const main_pkg_is_std = m: { - const std_path = try std.fs.path.resolve(arena, &[_][]const u8{ - std_pkg.root_src_directory.path orelse ".", - std_pkg.root_src_path, - }); - defer arena.free(std_path); - const main_path = try std.fs.path.resolve(arena, &[_][]const u8{ - main_pkg.root_src_directory.path orelse ".", - main_pkg.root_src_path, - }); - defer arena.free(main_path); - break :m mem.eql(u8, main_path, std_path); - }; - // Pre-open the directory handles for cached ZIR code so that it does not need // to redundantly happen for each AstGen operation. const zir_sub_dir = "z"; @@ -1705,7 +1707,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .gpa = gpa, .comp = comp, .main_pkg = main_pkg, - .main_pkg_is_std = main_pkg_is_std, .root_pkg = root_pkg, .zig_cache_artifact_directory = zig_cache_artifact_directory, .global_zir_cache = global_zir_cache, @@ -3107,18 +3108,26 @@ pub fn performAllTheWork( for (notes, 0..) |*note, i| { errdefer for (notes[0..i]) |*n| n.deinit(mod.gpa); note.* = switch (file.references.items[i]) { - .import => |loc| try Module.ErrorMsg.init( - mod.gpa, - loc, - "imported from package {s}", - .{loc.file_scope.pkg.name}, - ), - .root => |pkg| try Module.ErrorMsg.init( - mod.gpa, - .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, - "root of package {s}", - .{pkg.name}, - ), + .import => |loc| blk: { + const name = try loc.file_scope.pkg.getName(mod.gpa, mod.*); + defer mod.gpa.free(name); + break :blk try Module.ErrorMsg.init( + mod.gpa, + loc, + "imported from package {s}", + .{name}, + ); + }, + .root => |pkg| blk: { + const name = try pkg.getName(mod.gpa, mod.*); + defer mod.gpa.free(name); + break :blk try Module.ErrorMsg.init( + mod.gpa, + .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, + "root of package {s}", + .{name}, + ); + }, }; } errdefer for (notes) |*n| n.deinit(mod.gpa); @@ -5408,7 +5417,6 @@ fn buildOutputFromZig( var main_pkg: Package = .{ .root_src_directory = comp.zig_lib_directory, .root_src_path = src_basename, - .name = "root", }; defer main_pkg.deinitTable(comp.gpa); const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; diff --git a/src/Module.zig b/src/Module.zig index 76777532ab..2afe3f5df1 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -144,10 +144,6 @@ stage1_flags: packed struct { } = .{}, job_queued_update_builtin_zig: bool = true, -/// This makes it so that we can run `zig test` on the standard library. -/// Otherwise, the logic for scanning test decls skips all of them because -/// `main_pkg != std_pkg`. -main_pkg_is_std: bool, compile_log_text: ArrayListUnmanaged(u8) = .{}, @@ -2113,7 +2109,27 @@ pub const File = struct { /// Add a reference to this file during AstGen. pub fn addReference(file: *File, mod: Module, ref: Reference) !void { - try file.references.append(mod.gpa, ref); + // Don't add the same module root twice. Note that since we always add module roots at the + // front of the references array (see below), this loop is actually O(1) on valid code. + if (ref == .root) { + for (file.references.items) |other| { + switch (other) { + .root => |r| if (ref.root == r) return, + else => break, // reached the end of the "is-root" references + } + } + } + + switch (ref) { + // We put root references at the front of the list both to make the above loop fast and + // to make multi-module errors more helpful (since "root-of" notes are generally more + // informative than "imported-from" notes). This path is hit very rarely, so the speed + // of the insert operation doesn't matter too much. + .root => try file.references.insert(mod.gpa, 0, ref), + + // Other references we'll just put at the end. + else => try file.references.append(mod.gpa, ref), + } const pkg = switch (ref) { .import => |loc| loc.file_scope.pkg, @@ -3323,10 +3339,19 @@ pub fn deinit(mod: *Module) void { // The callsite of `Compilation.create` owns the `main_pkg`, however // Module owns the builtin and std packages that it adds. if (mod.main_pkg.table.fetchRemove("builtin")) |kv| { + gpa.free(kv.key); kv.value.destroy(gpa); } if (mod.main_pkg.table.fetchRemove("std")) |kv| { - kv.value.destroy(gpa); + gpa.free(kv.key); + // It's possible for main_pkg to be std when running 'zig test'! In this case, we must not + // destroy it, since it would lead to a double-free. + if (kv.value != mod.main_pkg) { + kv.value.destroy(gpa); + } + } + if (mod.main_pkg.table.fetchRemove("root")) |kv| { + gpa.free(kv.key); } if (mod.root_pkg != mod.main_pkg) { mod.root_pkg.destroy(gpa); @@ -4808,11 +4833,14 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult { const gop = try mod.import_table.getOrPut(gpa, resolved_path); errdefer _ = mod.import_table.pop(); - if (gop.found_existing) return ImportFileResult{ - .file = gop.value_ptr.*, - .is_new = false, - .is_pkg = true, - }; + if (gop.found_existing) { + try gop.value_ptr.*.addReference(mod.*, .{ .root = pkg }); + return ImportFileResult{ + .file = gop.value_ptr.*, + .is_new = false, + .is_pkg = true, + }; + } const sub_file_path = try gpa.dupe(u8, pkg.root_src_path); errdefer gpa.free(sub_file_path); @@ -5208,22 +5236,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err // test decl with no name. Skip the part where we check against // the test name filter. if (!comp.bin_file.options.is_test) break :blk false; - if (decl_pkg != mod.main_pkg) { - if (!mod.main_pkg_is_std) break :blk false; - const std_pkg = mod.main_pkg.table.get("std").?; - if (std_pkg != decl_pkg) break :blk false; - } + if (decl_pkg != mod.main_pkg) break :blk false; try mod.test_functions.put(gpa, new_decl_index, {}); break :blk true; }, else => blk: { if (!is_named_test) break :blk false; if (!comp.bin_file.options.is_test) break :blk false; - if (decl_pkg != mod.main_pkg) { - if (!mod.main_pkg_is_std) break :blk false; - const std_pkg = mod.main_pkg.table.get("std").?; - if (std_pkg != decl_pkg) break :blk false; - } + if (decl_pkg != mod.main_pkg) break :blk false; if (comp.test_filter) |test_filter| { if (mem.indexOf(u8, decl_name, test_filter) == null) { break :blk false; diff --git a/src/Package.zig b/src/Package.zig index 5878e7bad6..68d67a6d62 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -22,17 +22,16 @@ pub const Table = std.StringHashMapUnmanaged(*Package); root_src_directory: Compilation.Directory, /// Relative to `root_src_directory`. May contain path separators. root_src_path: []const u8, +/// The dependency table of this module. Shared dependencies such as 'std', 'builtin', and 'root' +/// are not specified in every dependency table, but instead only in the table of `main_pkg`. +/// `Module.importFile` is responsible for detecting these names and using the correct package. table: Table = .{}, -parent: ?*Package = null, /// Whether to free `root_src_directory` on `destroy`. root_src_directory_owned: bool = false, -/// This information can be recovered from 'table', but it's more convenient to store on the package. -name: []const u8, /// Allocate a Package. No references to the slices passed are kept. pub fn create( gpa: Allocator, - name: []const u8, /// Null indicates the current working directory root_src_dir_path: ?[]const u8, /// Relative to root_src_dir_path @@ -47,9 +46,6 @@ pub fn create( const owned_src_path = try gpa.dupe(u8, root_src_path); errdefer gpa.free(owned_src_path); - const owned_name = try gpa.dupe(u8, name); - errdefer gpa.free(owned_name); - ptr.* = .{ .root_src_directory = .{ .path = owned_dir_path, @@ -57,7 +53,6 @@ pub fn create( }, .root_src_path = owned_src_path, .root_src_directory_owned = true, - .name = owned_name, }; return ptr; @@ -65,7 +60,6 @@ pub fn create( pub fn createWithDir( gpa: Allocator, - name: []const u8, directory: Compilation.Directory, /// Relative to `directory`. If null, means `directory` is the root src dir /// and is owned externally. @@ -79,9 +73,6 @@ pub fn createWithDir( const owned_src_path = try gpa.dupe(u8, root_src_path); errdefer gpa.free(owned_src_path); - const owned_name = try gpa.dupe(u8, name); - errdefer gpa.free(owned_name); - if (root_src_dir_path) |p| { const owned_dir_path = try directory.join(gpa, &[1][]const u8{p}); errdefer gpa.free(owned_dir_path); @@ -93,14 +84,12 @@ pub fn createWithDir( }, .root_src_directory_owned = true, .root_src_path = owned_src_path, - .name = owned_name, }; } else { ptr.* = .{ .root_src_directory = directory, .root_src_directory_owned = false, .root_src_path = owned_src_path, - .name = owned_name, }; } return ptr; @@ -110,7 +99,6 @@ pub fn createWithDir( /// inside its table; the caller is responsible for calling destroy() on them. pub fn destroy(pkg: *Package, gpa: Allocator) void { gpa.free(pkg.root_src_path); - gpa.free(pkg.name); if (pkg.root_src_directory_owned) { // If root_src_directory.path is null then the handle is the cwd() @@ -130,15 +118,97 @@ pub fn deinitTable(pkg: *Package, gpa: Allocator) void { pkg.table.deinit(gpa); } -pub fn add(pkg: *Package, gpa: Allocator, package: *Package) !void { +pub fn add(pkg: *Package, gpa: Allocator, name: []const u8, package: *Package) !void { try pkg.table.ensureUnusedCapacity(gpa, 1); - pkg.table.putAssumeCapacityNoClobber(package.name, package); + const name_dupe = try gpa.dupe(u8, name); + pkg.table.putAssumeCapacityNoClobber(name_dupe, package); } -pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void { - assert(child.parent == null); // make up your mind, who is the parent?? - child.parent = parent; - return parent.add(gpa, child); +/// Compute a readable name for the package. The returned name should be freed from gpa. This +/// function is very slow, as it traverses the whole package hierarchy to find a path to this +/// package. It should only be used for error output. +pub fn getName(target: *const Package, gpa: Allocator, mod: Module) ![]const u8 { + // we'll do a breadth-first search from the root module to try and find a short name for this + // module, using a TailQueue of module/parent pairs. note that the "parent" there is just the + // first-found shortest path - a module may be children of arbitrarily many other modules. + // also, this path may vary between executions due to hashmap iteration order, but that doesn't + // matter too much. + var node_arena = std.heap.ArenaAllocator.init(gpa); + defer node_arena.deinit(); + const Parented = struct { + parent: ?*const @This(), + mod: *const Package, + }; + const Queue = std.TailQueue(Parented); + var to_check: Queue = .{}; + + { + const new = try node_arena.allocator().create(Queue.Node); + new.* = .{ .data = .{ .parent = null, .mod = mod.root_pkg } }; + to_check.prepend(new); + } + + if (mod.main_pkg != mod.root_pkg) { + const new = try node_arena.allocator().create(Queue.Node); + // TODO: once #12201 is resolved, we may want a way of indicating a different name for this + new.* = .{ .data = .{ .parent = null, .mod = mod.main_pkg } }; + to_check.prepend(new); + } + + // set of modules we've already checked to prevent loops + var checked = std.AutoHashMap(*const Package, void).init(gpa); + defer checked.deinit(); + + const linked = while (to_check.pop()) |node| { + const check = &node.data; + + if (checked.contains(check.mod)) continue; + try checked.put(check.mod, {}); + + if (check.mod == target) break check; + + var it = check.mod.table.iterator(); + while (it.next()) |kv| { + var new = try node_arena.allocator().create(Queue.Node); + new.* = .{ .data = .{ + .parent = check, + .mod = kv.value_ptr.*, + } }; + to_check.prepend(new); + } + } else { + // this can happen for e.g. @cImport packages + return gpa.dupe(u8, ""); + }; + + // we found a path to the module! unfortunately, we can only traverse *up* it, so we have to put + // all the names into a buffer so we can then print them in order. + var names = std.ArrayList([]const u8).init(gpa); + defer names.deinit(); + + var cur: *const Parented = linked; + while (cur.parent) |parent| : (cur = parent) { + // find cur's name in parent + var it = parent.mod.table.iterator(); + const name = while (it.next()) |kv| { + if (kv.value_ptr.* == cur.mod) { + break kv.key_ptr.*; + } + } else unreachable; + try names.append(name); + } + + // finally, print the names into a buffer! + var buf = std.ArrayList(u8).init(gpa); + defer buf.deinit(); + try buf.writer().writeAll("root"); + var i: usize = names.items.len; + while (i > 0) { + i -= 1; + try buf.writer().print(".{s}", .{names.items[i]}); + } + + return buf.toOwnedSlice(); } pub const build_zig_basename = "build.zig"; @@ -236,7 +306,7 @@ pub fn fetchAndAddDependencies( color, ); - try addAndAdopt(pkg, gpa, sub_pkg); + try add(pkg, gpa, fqn, sub_pkg); try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{ std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn), @@ -248,7 +318,6 @@ pub fn fetchAndAddDependencies( pub fn createFilePkg( gpa: Allocator, - name: []const u8, cache_directory: Compilation.Directory, basename: []const u8, contents: []const u8, @@ -269,7 +338,7 @@ pub fn createFilePkg( const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest; try renameTmpIntoCache(cache_directory.handle, tmp_dir_sub_path, o_dir_sub_path); - return createWithDir(gpa, name, cache_directory, o_dir_sub_path, basename); + return createWithDir(gpa, cache_directory, o_dir_sub_path, basename); } const Report = struct { @@ -363,9 +432,6 @@ fn fetchAndUnpack( const owned_src_path = try gpa.dupe(u8, build_zig_basename); errdefer gpa.free(owned_src_path); - const owned_name = try gpa.dupe(u8, fqn); - errdefer gpa.free(owned_name); - const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); errdefer gpa.free(build_root); @@ -380,7 +446,6 @@ fn fetchAndUnpack( }, .root_src_directory_owned = true, .root_src_path = owned_src_path, - .name = owned_name, }; return ptr; @@ -455,7 +520,7 @@ fn fetchAndUnpack( std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root), }); - return createWithDir(gpa, fqn, global_cache_directory, pkg_dir_sub_path, build_zig_basename); + return createWithDir(gpa, global_cache_directory, pkg_dir_sub_path, build_zig_basename); } fn unpackTarball( diff --git a/src/Sema.zig b/src/Sema.zig index 41e5fdc20e..65f3587b3f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5311,7 +5311,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr } const c_import_pkg = Package.create( sema.gpa, - "c_import", // TODO: should we make this unique? null, c_import_res.out_zig_path, ) catch |err| switch (err) { @@ -11793,8 +11792,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand}); }, error.PackageNotFound => { - const cur_pkg = block.getFileScope().pkg; - return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, cur_pkg.name }); + const name = try block.getFileScope().pkg.getName(sema.gpa, mod.*); + defer sema.gpa.free(name); + return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, name }); }, else => { // TODO: these errors are file system errors; make sure an update() will diff --git a/src/main.zig b/src/main.zig index e42974944b..d544940779 100644 --- a/src/main.zig +++ b/src/main.zig @@ -403,8 +403,11 @@ const usage_build_generic = \\ ReleaseFast Optimizations on, safety off \\ ReleaseSafe Optimizations on, safety on \\ ReleaseSmall Optimize for small binary, safety off - \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg - \\ --pkg-end Pop current pkg + \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name + \\ deps: [dep],[dep],... + \\ dep: [[import=]name] + \\ --deps [dep],[dep],... Set dependency names for the root package + \\ dep: [[import=]name] \\ --main-pkg-path Set the directory of the root package \\ -fPIC Force-enable Position Independent Code \\ -fno-PIC Force-disable Position Independent Code @@ -858,15 +861,21 @@ fn buildOutputType( var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa); defer linker_export_symbol_names.deinit(); - // This package only exists to clean up the code parsing --pkg-begin and - // --pkg-end flags. Use dummy values that are safe for the destroy call. - var pkg_tree_root: Package = .{ - .root_src_directory = .{ .path = null, .handle = fs.cwd() }, - .root_src_path = &[0]u8{}, - .name = &[0]u8{}, - }; - defer freePkgTree(gpa, &pkg_tree_root, false); - var cur_pkg: *Package = &pkg_tree_root; + // Contains every module specified via --mod. The dependencies are added + // after argument parsing is completed. We use a StringArrayHashMap to make + // error output consistent. + var modules = std.StringArrayHashMap(struct { + mod: *Package, + deps_str: []const u8, // still in CLI arg format + }).init(gpa); + defer { + var it = modules.iterator(); + while (it.next()) |kv| kv.value_ptr.mod.destroy(gpa); + modules.deinit(); + } + + // The dependency string for the root package + var root_deps_str: ?[]const u8 = null; // before arg parsing, check for the NO_COLOR environment variable // if it exists, default the color setting to .off @@ -943,34 +952,44 @@ fn buildOutputType( } else { fatal("unexpected end-of-parameter mark: --", .{}); } - } else if (mem.eql(u8, arg, "--pkg-begin")) { - const opt_pkg_name = args_iter.next(); - const opt_pkg_path = args_iter.next(); - if (opt_pkg_name == null or opt_pkg_path == null) - fatal("Expected 2 arguments after {s}", .{arg}); + } else if (mem.eql(u8, arg, "--mod")) { + const info = args_iter.nextOrFatal(); + var info_it = mem.split(u8, info, ":"); + const mod_name = info_it.next() orelse fatal("expected non-empty argument after {s}", .{arg}); + const deps_str = info_it.next() orelse fatal("expected 'name:deps:path' after {s}", .{arg}); + const root_src_orig = info_it.rest(); + if (root_src_orig.len == 0) fatal("expected 'name:deps:path' after {s}", .{arg}); + if (mod_name.len == 0) fatal("empty name for module at '{s}'", .{root_src_orig}); - const pkg_name = opt_pkg_name.?; - const pkg_path = try introspect.resolvePath(arena, opt_pkg_path.?); + const root_src = try introspect.resolvePath(arena, root_src_orig); - const new_cur_pkg = Package.create( - gpa, - pkg_name, - fs.path.dirname(pkg_path), - fs.path.basename(pkg_path), - ) catch |err| { - fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) }); - }; - - if (mem.eql(u8, pkg_name, "std") or mem.eql(u8, pkg_name, "root") or mem.eql(u8, pkg_name, "builtin")) { - fatal("unable to add package '{s}' -> '{s}': conflicts with builtin package", .{ pkg_name, pkg_path }); - } else if (cur_pkg.table.get(pkg_name)) |prev| { - fatal("unable to add package '{s}' -> '{s}': already exists as '{s}", .{ pkg_name, pkg_path, prev.root_src_path }); + for ([_][]const u8{ "std", "root", "builtin" }) |name| { + if (mem.eql(u8, mod_name, name)) { + fatal("unable to add module '{s}' -> '{s}': conflicts with builtin module", .{ mod_name, root_src }); + } } - try cur_pkg.addAndAdopt(gpa, new_cur_pkg); - cur_pkg = new_cur_pkg; - } else if (mem.eql(u8, arg, "--pkg-end")) { - cur_pkg = cur_pkg.parent orelse - fatal("encountered --pkg-end with no matching --pkg-begin", .{}); + + var mod_it = modules.iterator(); + while (mod_it.next()) |kv| { + if (std.mem.eql(u8, mod_name, kv.key_ptr.*)) { + fatal("unable to add module '{s}' -> '{s}': already exists as '{s}'", .{ mod_name, root_src, kv.value_ptr.mod.root_src_path }); + } + } + + try modules.ensureUnusedCapacity(1); + modules.put(mod_name, .{ + .mod = try Package.create( + gpa, + fs.path.dirname(root_src), + fs.path.basename(root_src), + ), + .deps_str = deps_str, + }) catch unreachable; + } else if (mem.eql(u8, arg, "--deps")) { + if (root_deps_str != null) { + fatal("only one --deps argument is allowed", .{}); + } + root_deps_str = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "--main-pkg-path")) { main_pkg_path = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "-cflags")) { @@ -2307,6 +2326,31 @@ fn buildOutputType( }, } + { + // Resolve module dependencies + var it = modules.iterator(); + while (it.next()) |kv| { + const deps_str = kv.value_ptr.deps_str; + var deps_it = ModuleDepIterator.init(deps_str); + while (deps_it.next()) |dep| { + if (dep.expose.len == 0) { + fatal("module '{s}' depends on '{s}' with a blank name", .{ kv.key_ptr.*, dep.name }); + } + + for ([_][]const u8{ "std", "root", "builtin" }) |name| { + if (mem.eql(u8, dep.expose, name)) { + fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose }); + } + } + + const dep_mod = modules.get(dep.name) orelse + fatal("module '{s}' depends on module '{s}' which does not exist", .{ kv.key_ptr.*, dep.name }); + + try kv.value_ptr.mod.add(gpa, dep.expose, dep_mod.mod); + } + } + } + if (arg_mode == .build and optimize_mode == .ReleaseSmall and strip == null) strip = true; @@ -2886,14 +2930,14 @@ fn buildOutputType( if (main_pkg_path) |unresolved_main_pkg_path| { const p = try introspect.resolvePath(arena, unresolved_main_pkg_path); if (p.len == 0) { - break :blk try Package.create(gpa, "root", null, src_path); + break :blk try Package.create(gpa, null, src_path); } else { const rel_src_path = try fs.path.relative(arena, p, src_path); - break :blk try Package.create(gpa, "root", p, rel_src_path); + break :blk try Package.create(gpa, p, rel_src_path); } } else { const root_src_dir_path = fs.path.dirname(src_path); - break :blk Package.create(gpa, "root", root_src_dir_path, fs.path.basename(src_path)) catch |err| { + break :blk Package.create(gpa, root_src_dir_path, fs.path.basename(src_path)) catch |err| { if (root_src_dir_path) |p| { fatal("unable to open '{s}': {s}", .{ p, @errorName(err) }); } else { @@ -2904,23 +2948,24 @@ fn buildOutputType( } else null; defer if (main_pkg) |p| p.destroy(gpa); - // Transfer packages added with --pkg-begin/--pkg-end to the root package - if (main_pkg) |pkg| { - var it = pkg_tree_root.table.valueIterator(); - while (it.next()) |p| { - if (p.*.parent == &pkg_tree_root) { - p.*.parent = pkg; + // Transfer packages added with --deps to the root package + if (main_pkg) |mod| { + var it = ModuleDepIterator.init(root_deps_str orelse ""); + while (it.next()) |dep| { + if (dep.expose.len == 0) { + fatal("root module depends on '{s}' with a blank name", .{dep.name}); } - } - pkg.table = pkg_tree_root.table; - pkg_tree_root.table = .{}; - } else { - // Remove any dangling pointers just in case. - var it = pkg_tree_root.table.valueIterator(); - while (it.next()) |p| { - if (p.*.parent == &pkg_tree_root) { - p.*.parent = null; + + for ([_][]const u8{ "std", "root", "builtin" }) |name| { + if (mem.eql(u8, dep.expose, name)) { + fatal("unable to add module '{s}' under name '{s}': conflicts with builtin module", .{ dep.name, dep.expose }); + } } + + const dep_mod = modules.get(dep.name) orelse + fatal("root module depends on module '{s}' which does not exist", .{dep.name}); + + try mod.add(gpa, dep.expose, dep_mod.mod); } } @@ -3400,6 +3445,32 @@ fn buildOutputType( return cleanExit(); } +const ModuleDepIterator = struct { + split: mem.SplitIterator(u8), + + fn init(deps_str: []const u8) ModuleDepIterator { + return .{ .split = mem.split(u8, deps_str, ",") }; + } + + const Dependency = struct { + expose: []const u8, + name: []const u8, + }; + + fn next(it: *ModuleDepIterator) ?Dependency { + if (it.split.buffer.len == 0) return null; // don't return "" for the first iteration on "" + const str = it.split.next() orelse return null; + if (mem.indexOfScalar(u8, str, '=')) |i| { + return .{ + .expose = str[0..i], + .name = str[i + 1 ..], + }; + } else { + return .{ .expose = str, .name = str }; + } + } +}; + fn parseCrossTargetOrReportFatalError( allocator: Allocator, opts: std.zig.CrossTarget.ParseOptions, @@ -3626,18 +3697,6 @@ fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void } } -fn freePkgTree(gpa: Allocator, pkg: *Package, free_parent: bool) void { - { - var it = pkg.table.valueIterator(); - while (it.next()) |value| { - freePkgTree(gpa, value.*, true); - } - } - if (free_parent) { - pkg.destroy(gpa); - } -} - fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void { if (!build_options.have_llvm) fatal("cannot translate-c: compiler built without LLVM extensions", .{}); @@ -4141,7 +4200,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi var main_pkg: Package = .{ .root_src_directory = zig_lib_directory, .root_src_path = "build_runner.zig", - .name = "root", }; if (!build_options.omit_pkg_fetching_code) { @@ -4184,22 +4242,20 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi const deps_pkg = try Package.createFilePkg( gpa, - "@dependencies", local_cache_directory, "dependencies.zig", dependencies_source.items, ); mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table); - try main_pkg.addAndAdopt(gpa, deps_pkg); + try main_pkg.add(gpa, "@dependencies", deps_pkg); } var build_pkg: Package = .{ .root_src_directory = build_directory, .root_src_path = build_zig_basename, - .name = "@build", }; - try main_pkg.addAndAdopt(gpa, &build_pkg); + try main_pkg.add(gpa, "@build", &build_pkg); const comp = Compilation.create(gpa, .{ .zig_lib_directory = zig_lib_directory, @@ -4434,7 +4490,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void .root_decl = .none, }; - file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); + file.pkg = try Package.create(gpa, null, file.sub_file_path); defer file.pkg.destroy(gpa); file.zir = try AstGen.generate(gpa, file.tree); @@ -4645,7 +4701,7 @@ fn fmtPathFile( .root_decl = .none, }; - file.pkg = try Package.create(fmt.gpa, "root", null, file.sub_file_path); + file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path); defer file.pkg.destroy(fmt.gpa); if (stat.size > max_src_size) @@ -5357,7 +5413,7 @@ pub fn cmdAstCheck( file.stat.size = source.len; } - file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); + file.pkg = try Package.create(gpa, null, file.sub_file_path); defer file.pkg.destroy(gpa); file.tree = try Ast.parse(gpa, file.source, .zig); @@ -5476,7 +5532,7 @@ pub fn cmdChangelist( .root_decl = .none, }; - file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); + file.pkg = try Package.create(gpa, null, file.sub_file_path); defer file.pkg.destroy(gpa); const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0); diff --git a/src/test.zig b/src/test.zig index acc1bcdc1f..bf1b0e912a 100644 --- a/src/test.zig +++ b/src/test.zig @@ -1497,7 +1497,6 @@ pub const TestContext = struct { var main_pkg: Package = .{ .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, .root_src_path = tmp_src_path, - .name = "root", }; defer main_pkg.table.deinit(allocator); From 09a84c8384dffc7884528947b879f32d93c1bd90 Mon Sep 17 00:00:00 2001 From: mlugg Date: Fri, 17 Feb 2023 06:20:52 +0000 Subject: [PATCH 082/122] Update std.Build to new module CLI, update zig1 and CMakeLists Resolves: #14667 --- CMakeLists.txt | 6 +- ci/x86_64-windows-debug.ps1 | 3 +- ci/x86_64-windows-release.ps1 | 3 +- lib/std/Build/CompileStep.zig | 127 ++++++++++++++++++++++++++++------ stage1/zig1.wasm | Bin 2388586 -> 2408069 bytes 5 files changed, 115 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 448fb400b6..be9931f6dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -748,7 +748,8 @@ set(BUILD_ZIG2_ARGS build-exe src/main.zig -ofmt=c -lc -OReleaseSmall --name zig2 -femit-bin="${ZIG2_C_SOURCE}" - --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end + --mod "build_options::${ZIG_CONFIG_ZIG_OUT}" + --deps build_options -target "${HOST_TARGET_TRIPLE}" ) @@ -765,7 +766,8 @@ set(BUILD_COMPILER_RT_ARGS build-obj lib/compiler_rt.zig -ofmt=c -OReleaseSmall --name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}" - --pkg-begin build_options "${ZIG_CONFIG_ZIG_OUT}" --pkg-end + --mod "build_options::${ZIG_CONFIG_ZIG_OUT}" + --deps build_options -target "${HOST_TARGET_TRIPLE}" ) diff --git a/ci/x86_64-windows-debug.ps1 b/ci/x86_64-windows-debug.ps1 index 65b9fbd92a..db2a58e4ad 100644 --- a/ci/x86_64-windows-debug.ps1 +++ b/ci/x86_64-windows-debug.ps1 @@ -87,7 +87,8 @@ CheckLastExitCode -OReleaseSmall ` --name compiler_rt ` -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` - --pkg-begin build_options config.zig --pkg-end ` + --mod build_options::config.zig ` + --deps build_options ` -target x86_64-windows-msvc CheckLastExitCode diff --git a/ci/x86_64-windows-release.ps1 b/ci/x86_64-windows-release.ps1 index e3375ccb72..a256a94a23 100644 --- a/ci/x86_64-windows-release.ps1 +++ b/ci/x86_64-windows-release.ps1 @@ -87,7 +87,8 @@ CheckLastExitCode -OReleaseSmall ` --name compiler_rt ` -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` - --pkg-begin build_options config.zig --pkg-end ` + --mod build_options::config.zig ` + --deps build_options ` -target x86_64-windows-msvc CheckLastExitCode diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index a916de0fc6..6477c20f6b 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -955,7 +955,10 @@ pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { /// package's module table using `name`. pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void { cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM"); - cs.addRecursiveBuildDeps(module); + + var done = std.AutoHashMap(*Module, void).init(cs.builder.allocator); + defer done.deinit(); + cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); } /// Adds a module to be used with `@import` without exposing it in the current @@ -969,10 +972,12 @@ pub fn addOptions(cs: *CompileStep, module_name: []const u8, options: *OptionsSt addModule(cs, module_name, options.createModule()); } -fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module) void { +fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module, done: *std.AutoHashMap(*Module, void)) !void { + if (done.contains(module)) return; + try done.put(module, {}); module.source_file.addStepDependencies(&cs.step); for (module.dependencies.values()) |dep| { - cs.addRecursiveBuildDeps(dep); + try cs.addRecursiveBuildDeps(dep, done); } } @@ -1031,22 +1036,110 @@ fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { fn appendModuleArgs( cs: *CompileStep, zig_args: *ArrayList([]const u8), - name: []const u8, - module: *Module, ) error{OutOfMemory}!void { - try zig_args.append("--pkg-begin"); - try zig_args.append(name); - try zig_args.append(module.builder.pathFromRoot(module.source_file.getPath(module.builder))); + // First, traverse the whole dependency graph and give every module a unique name, ideally one + // named after what it's called somewhere in the graph. It will help here to have both a mapping + // from module to name and a set of all the currently-used names. + var mod_names = std.AutoHashMap(*Module, []const u8).init(cs.builder.allocator); + var names = std.StringHashMap(void).init(cs.builder.allocator); + var to_name = std.ArrayList(struct { + name: []const u8, + mod: *Module, + }).init(cs.builder.allocator); { - const keys = module.dependencies.keys(); - for (module.dependencies.values(), 0..) |sub_module, i| { - const sub_name = keys[i]; - try cs.appendModuleArgs(zig_args, sub_name, sub_module); + var it = cs.modules.iterator(); + while (it.next()) |kv| { + // While we're traversing the root dependencies, let's make sure that no module names + // have colons in them, since the CLI forbids it. We handle this for transitive + // dependencies further down. + if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) { + @panic("Module names cannot contain colons"); + } + try to_name.append(.{ + .name = kv.key_ptr.*, + .mod = kv.value_ptr.*, + }); } } - try zig_args.append("--pkg-end"); + while (to_name.popOrNull()) |dep| { + if (mod_names.contains(dep.mod)) continue; + + // We'll use this buffer to store the name we decide on + var buf = try cs.builder.allocator.alloc(u8, dep.name.len + 32); + // First, try just the exposed dependency name + std.mem.copy(u8, buf, dep.name); + var name = buf[0..dep.name.len]; + var n: usize = 0; + while (names.contains(name)) { + // If that failed, append an incrementing number to the end + name = std.fmt.bufPrint(buf, "{s}{}", .{ dep.name, n }) catch unreachable; + n += 1; + } + + try mod_names.put(dep.mod, name); + try names.put(name, {}); + + var it = dep.mod.dependencies.iterator(); + while (it.next()) |kv| { + // Same colon-in-name check as above, but for transitive dependencies. + if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) { + @panic("Module names cannot contain colons"); + } + try to_name.append(.{ + .name = kv.key_ptr.*, + .mod = kv.value_ptr.*, + }); + } + } + + // Since the module names given to the CLI are based off of the exposed names, we already know + // that none of the CLI names have colons in them, so there's no need to check that explicitly. + + // Every module in the graph is now named; output their definitions + { + var it = mod_names.iterator(); + while (it.next()) |kv| { + const mod = kv.key_ptr.*; + const name = kv.value_ptr.*; + + const deps_str = try constructDepString(cs.builder.allocator, mod_names, mod.dependencies); + const src = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder)); + try zig_args.append("--mod"); + try zig_args.append(try std.fmt.allocPrint(cs.builder.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); + } + } + + // Lastly, output the root dependencies + const deps_str = try constructDepString(cs.builder.allocator, mod_names, cs.modules); + if (deps_str.len > 0) { + try zig_args.append("--deps"); + try zig_args.append(deps_str); + } +} + +fn constructDepString( + allocator: std.mem.Allocator, + mod_names: std.AutoHashMap(*Module, []const u8), + deps: std.StringArrayHashMap(*Module), +) ![]const u8 { + var deps_str = std.ArrayList(u8).init(allocator); + var it = deps.iterator(); + while (it.next()) |kv| { + const expose = kv.key_ptr.*; + const name = mod_names.get(kv.value_ptr.*).?; + if (std.mem.eql(u8, expose, name)) { + try deps_str.writer().print("{s},", .{name}); + } else { + try deps_str.writer().print("{s}={s},", .{ expose, name }); + } + } + if (deps_str.items.len > 0) { + return deps_str.items[0 .. deps_str.items.len - 1]; // omit trailing comma + } else { + return ""; + } } fn make(step: *Step) !void { @@ -1573,13 +1666,7 @@ fn make(step: *Step) !void { try zig_args.append("--test-no-exec"); } - { - const keys = self.modules.keys(); - for (self.modules.values(), 0..) |module, i| { - const name = keys[i]; - try self.appendModuleArgs(&zig_args, name, module); - } - } + try self.appendModuleArgs(&zig_args); for (self.include_dirs.items) |include_dir| { switch (include_dir) { diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index 2fe8728cb3562fe78ff3acede61698bf3a98adf3..d7bf519b41a74966a3bc46b133fd9b34aa200f75 100644 GIT binary patch delta 719858 zcmd3P3t$~png5-+H?Mnh)7vI}7kcLQm6oOt9tEigr_vUvg<>n~1J=jx>Tc?~7SVNg zT_-?+02Ko%CSrjAg$8IbNWln21C%95(I7?QQZQ=Ks8xd$jk|UG|NXvmX70?*?E}%> zKP7i&&YZ`0zSsHAcfNDx7kjUFuK42(PIcnLpZZ59kw`e3%PzRT`k&ngu5rs!XWvz~ zx$YF#bse`nolYm+RC$@3a-Fg=CtY5iM0#1eB3Yhx@L#Ijbt_WN=<-yxn<~q>$@1|@ zWKE~4lWEs!?JJ+(>Qqd5TRL4EExGA(=?l|R0t4LMif`-z` zbRwN{kh!v|^*t4Hs+<&FqcOL&xngSTT@}yOrrcy%0%>JVHk&PXl5VBb+FW@jvZa%) z4OPcDm96Jg)wh1CYIGh8U0s=M{obe*=g+U7J)`El z1r4hf)YR53TsWrR=Yo)kiO&YCTDZqlQnMIcRoKYp@*Ec`NZ^uO6bmq9~e7& zcXHJBm#)dxyNN|E6Suz9I$Zbm`EUHkWjY!dZPXjJ8@Qs4G)>&9`N(|K^AZ_-Q|lMUzk9|*>#xq~irfNy@L+OXHlf_dbn=pX zQtx>B##|$MqmsJk>DJ7IywlTm{$;hbt$M;_=f7JonlR0IwDr;nlbkzSH%(aVY<-}A z!Y3VPbL-iYra6zd{^O(*orA5nPdd%%Y8{yLd}?c5W`5g(57bO({qE$C_%Ga1<_{7aAFevF*xAGD zfrnaiv(d(&b8@nW2~Cxatc(q|G&SXBClmRfIw!mPq|hRF5Z5L5yfFQq7t$vu2l#-@ z!?-HsN;X%r>P2oluIi_)>yDY?{IvDDW2StJRqR3{ib}~In{hxg;^p4R%YMEbstVv> z&F%qPU^tR*D01IiB`uCVmMzXZc8bqJJCR6mdTudLe1@S+n)&Y)y> zy3u>U!OhmHM(;7+Y_Dwe9_P(ax>32>T`LR#0n9Zna`&;do^+#5=(z_nq|^Eg9wZBz zb`S799`^?jG@}s~sq|5LIIZ^#w&vzL39m}OV(+T)dKfjPF7_q^EHIVl0>m@$w^;k$ zc7pwOUGClC==;C4KId(ARGBv&i4Qu-Gt?|^rK+BsuR4^)Ont-PV|}_waUBH zQRjP?IqI*x%N=#H@2z*#yS*zM<#|^+D(fAmR(n@DYMIyUsP}q*r{;L4s_EWxb(W{q z+r6tDb+&hnqu%FT>!>x}21mWa`+}pUdVj6n?|o3^z3UwH9`B2eI>&2q)DrJ{N6qwZ zaMWV2)lp}7H#zD9-iOpYZ=<6w@@{n03g7#Zqm=cdYAY2 zYNGcK%JteEb&B_8M^$)@YLd5B9pimmP4oUyE$}{}W_bUk7J8plW4(3iRmc08qegod zt8w0^)EMs)b*%SkHNpFgn&N#})p#FKb>2U#w|Spcqr87n8Sih@crX92D(C&1N_(GE zj`z2~`Cxe?enHFJNv<#d+Riv-(!{obAC#Tqt5m%^Se`FSouSHjzpp%Bt~cL&WllS} zMbM#02iKj}n0Im~qS9f`Un)95+Sn5tD@uqRi{eeX8)O@9k6-%=d)a2Y^yzb1f+nu1q+liR2Odf(& z?{3fJfkjtiG*s?+lZmZ*cWZ`2;m6SvFu%JrtCRyRE!r}7nv9a3L)@*!pq0oZ{EWc< zGGRC1WmA$ec*PYtT_ykvE&G!JH@pL|IE&m}1pLL@uEa3gi`yljWm67n8uuzSJK3K+ zIoZuDeNwPH)0jJ*@bxA0wM(E3vb-ekURX*SIqJJ3U3Q7NtsiJh6r zd3RZsPwrau1Fz7A+la2Gmu8_T>fK#QZxZSp0?L3D<>bIq^PS;Lz7pLq=xnMkpwmZ@ z=4yk_4b>&^xxPA%Pw(*f%;*mpI_%GQ=zYCAl<~6e8Ki}-gfuM868kh{8o}kRKEF$D zbIL)nT9sBDQ&k0itSOQp4mSZ}ur8=hPHsm>RV_G~*EBer8nUH&1oJ-lm1}bP7-8P> z7kFRZ_TvkkdHyTB_i_s+Wpq!|rkok1!S;>Q6V)fPxsC{e(1p(GykRZDPX+kRRTbw= zhWvRV`yE74@)A-3xi?#c>FTv!h4dUUzcydSo>#y~^FGFYrvR;T(M>OhCyXez_k!JM z*LTFa-fg?yWxKweS7K|nTMgbLBDyRh7PL9%It$Bx+7%#SY}?9nor(UH_Y)HZdko@H zDRM!?Cjd%rVPL8yw7bYvNK}pbi!3%SRt!Ybbz;JmC{~YR^^Lld#oP$4TLenKw@ zkqxm{9AF{3X{t1B#Xn@%-Z(Y-EVu41#jU%Jid+3q{7LN|h+kG^a!YXEZSUWS`+gYI zG_nX<543B&yHkF<<#$IicP1rkPjy7H2I8O&T2S{Df$9sR>^&-2qoCHi{ng&FE+m@D zFpf2$nP@7}{3ZtkDz}u1WBm%UAG#o8gO3p;Pyx%JEcv2krKa`wue@(xIT;LDy#N}X zkK5DvDb(n!Z8_SO!OZC6WiUoy=Ye*sAQrfGVH|@d-eQEmbCs86YR4*DxidMR^U7rO z+#E@p;#C-J?V`mpGGb;qrV7*tWNye!ihLHxh02E2sY)vH6QBeIQ5at#S!E~yk*dJ- z1wvR!J>jKVC4w2jPUlZZabwmImL^=aRu())xd>m z-y7YwV0m(^-@V!!#bP^Fd!u>nS)K7FXh%7h01Z+^8BghEt0g<(``G9W_dg z(e5QMF`y)Y4Rn-g6-+~`tbw>3jTGpq{8-`?KPf?I7Itny@W>3F4bPN0o%BM zO^AIMBc~>uXctG}Wd#+a%dX)jU#3RI`sc7I+q)W-3p?Xb1+&}hlnV+v1T1rQ7Q=we zm0_xv6QU~E>T5lW;WUQO7F!Y%KBFd}5}E z+z&_v=t5F4-$+s&o>B{`j;)a^vN%%WMp?-lYp$5BsgzHN^Ttv{3RH#!8quqXc6f{x zJ2Q&{W@>0kHRi`t`f?&1Sx&fLb+$nE{3uOFgSSNVvT8Ia=3{S7Z zNS={6;6J0rqEB0_uq^;Rtgd1XK^QF@a$iQ;pMobEOJ%rX=qaf3#_D;Q>XV2-&L-}= z_fncE;*SFKOuB6!<`ARoAb`&6m+l#rCNGI7i=83CzNj4vU|}R)&(aVn@*+uXpz+fvo7{ zVF3#CS8}fB=FSQyGC-x6-o74+TL{C}LO4)lA&7ynzsNv1!c>MsoI%vh4}^W;xJ}&M z^IhOB<6GpRdfk)?k*BxAjR*CK4mG9BjAMNY{C~?U@Ch26WF6i6y;f$T6?^~hnj$ZD z6<$1dA6}SQ6H4?IUeGh4%T4=uv%B!7^`4Nbc+qVjVI%i`1HBk85aY#;!i%fEgAyTF zJ%tzTEHTEG03TvB>|RX+c@c(Ybh*0<6X@b8?&V*^yz^L@B~0AR`&?+`>aI3euR}14 z+g6?HjB~!;dg=G;V7$sq;S?&X;W%#$C-W6cvvWWOpz;71MVGDB|Blg118ug#|+ ztCTzRvk45(ey=CvH)Y%YYo&8a`R1>o8z-`hW)9%K^tIOK>T83ZV8C@760Y|oJxTxJ z1QPMhE8xvAb7GN*<^sd8dmB>TY+jpFUPDmiYqx}aAw^o^MK-3qr$lU8QeLl9?OV}% z@uwj@rBr*gly1&~Phpc<#@v+Rj6Zj_O3iaxvOqVCF~@rv)y_^fXT4#OdqF`vQ2`yj ziB}pv5r0Pq(Noo)9l;IzuL1{5GY*fNjKiZt93Fo4qEI#61zxT;qGZME_y0-uw$cSy zbXTkooqU3EoXX!U{okI--|6Jusql2CBy<|3X$`Uy0r1TrVlN@T6F0kX13!T!=Wr{? zDf3!8rEA}agz1q<7Og4Ere;DLhsI#E!(w!E1fyNVXto~BQg+xp5V(WaA_VpzO?Mmo zxlxbN8}t~>6$HAQuWozgLg#q*o~v6g{b0y$As%m{K98&TSII!NG1UJ2hL8=HW?c$h zPoy`0>pALkURwPd=F$I(G3swHM*Rz{@y~j1Q~&JMsgHOy>cid?^%-x1uRiS^t1j`z zs84z0)WtB>f980vI%*wEhfjJ7)jxSN)F-?J>L0yn>f_!qYAqZooJ4%t@lH|g^iTYQ zH&Ok)_b&A@@15$S-bw00?{u}ut5t1|r_`4mZ-u(i@h(yu9dDlckoN&~6TGyl6>eH} zgX7Kg)%A|IM722HIqHj!_a1efFtsVf}s-D~UYhC{?6w+mO@Rbz)$S-%z^E4(gC+eV%1Oe??Z0gP4eh}a@O zdmw6yEaK|hDbfth)?fbyvw(@FhsR3~zj{6TTq7xBEDXo!GsLHQci0YX2QakdR#4TD z7O>rgWcyRF@x*}WsR;~-cYIqci0&Hi4+RUNm%YLOxJ!B$w;=l1)83$`g%y*1T<$h2 zCho>7%=mZH74h*;Rbj*`3@hyD^^b=GU-o!ajrUBwlc{yl?%(xIEK3+=B;9W=qe9^# z6_+KN_ym)z%oy<-%53-h+5Pv!x)*=-`ZDzYT{NKfg#CX;%#i*XZ&#uF1Gf8nOLTvb z-5(g){l{rP%`(IPzN<|4e|uHD`=G9pL7QFt{!OsirvzQB#!xMzDPKMGGG1luD-^1g zk(_UKZ7CS~$AiC_cF%rbCceV-hd!XMQYu)Q<=h*~1W|lJ?8>~aOV3-%yzbEQ9b~zj zloKSk#|v#L^PZJL?PcC~Sja-qeH)6|5DKg4p7}8podL>+2bK_YL@Ij|*2`NqUi`Oa zy}V`P#eZwoOWb%Ft`J7=5PsfVX2#o<;dmRU;Gu{L=1SMFRlx^Jso?!@QU$wbSh{Z^ z-3`0IX-!_-NwOPGuqkb~`N$MHFUSa;|GSLf|C!9#Ka~;uKa&|tMu3537@$)aU~8GN z4n{IS(28z5Z4pX;deP*x~z*=L({2M(^GT z@NZ6x{r^kf1H{<>zw|vojQxo3f#Gb3QbT|#$lfbt-xZgFfbe~B!uMOk?Fz_fOVCBZ2VoWXLpxzj+6lfT{B@k58q+h-=^Mxk zky;UY(yeE-HCB}kpY{oCyW_A0d^!+^ZP3EDuK*kLZxExlw+OhSG&EYj7Fv}7Q#Sn! zOc@#)fOs&{2gugGGK7Uv$Og+khQLs+iDB9n&7+%gqI?JJoN+JbjOTH3xFJVh@JvjB z;R|j-7!0Sv8+k4A1-BzjbKSy;2M@k}CFX*8GZ$>PbHQ7R=7L17e)$pfA`)vi6a^5A zY-QdB?88u*cV@8C@Zg?MSwmz&sd$lLq1A!1+$!%I;k0hwEf})P=^XL7##gS(*Lt@= z4e9bm@2)V{OJS}s(*-6^*!Up5EL8GpXNX zs-5Z6OE;eDN|OJ;F9A*XrV4LZ%NMx9WtOwVt7q1z%9LNOfB&VB%6L_U5*N1k=JlV4eox!oO;4fnuyb6Cf*QnP1U$SYIHE7g5ts}0Tb9^UO4N40Nsa)d z^JF!QkRE&$6gAF$Kq`x2Oup?&bj^U9^^Zr1Rno8aU!xaxF`ucc&B_%^Jo=po4e-h? zd-2tMd}S*mA*PvSF~0gaCuTMTfrvHC`5UGh;~qfy)pnha1^V0K1r`WguP`+zfX3g+ zwLx4@lDEJ6G5Q!Z0+0GTCG`MPgED|>Mz~VQcDioHTjK%ph2%4&P!E%B&;q+MRu&>z z;EP-r510Wx87uj3W@^A`OV%^$JXpnKp=-KOU4Z>15|qx@e6@+M#GCA|0|3?G3MgMb z`#ku|7{LPEXerXq)DVPfyKc(-ulEF%v92n-VQTYZNVPE=$1AS0R%2zAZ?1Y8*-#jgh5T7P#RWyomtBxTSJ^GgBh~nMoDjbbbqOf{vs9VOt@0a zcHX!OI1D%(?I7OBNAe$;9QIEDI)D_@0}L18?lH3)3?Otmy2uF^IB9^<%g*B5mCKduho@oJ6N+yMdE)tVUwBVAJiRO$Qrqu!L`ml=JmdTnqyd5Zq0UWYWV#P|QyA`iYyWwcc6U#SRwK@OH`B$HI}N zhUs`Sr?CFc$}Xq6jnS)*W?&}3f0@SinN1FLduFXS2lXG%`i;?X9SetS%j!0BLyX}W z5c_^dnSF$cTmAYf0yDQM)`OHroNa#vsKq`ka(DB#OjjYyu296^#+prEq~^lzvq;4^ z?FxH?O!vl`A1V);-xKShG(QZZ1I=g9{C?iD+l$;nkvo0XY`Q8nN189>`s&xAC&IC7 zYpnUEu=hP|epG;9X};O?eiWKtZ+brp%@>M%^Xn9{)Kgcq9&^rwe<-9paVOdjv43BzsgAIz2iUXfSkF35Q`P9% zX46zPdR8d%T~=Jvdh0o3{X*hBS0K^I5Rm=VSZiHjYqzttT&%Tj(^?L#Z8xoP_hO;Q z!>l+o=EBXd+&$UlhZR4!3wRA_-4$!Tr=l7`u4MJ!5wA6|=6g-^HE4dPX}$){7mECl z6^An;?6wVeP$qjSd{hk0@B#e2KMajwL*Jr?2=p*y?*r2O15Y3|)W6k;=wWOQ<8D}{ z^8if5U^s<#u8`DkFf}mT(9U~BlChXhz2HlhE!cP+njpw>LI?_?bntwxfllljy z216eJI!_{+nEwf`s)xNx;0;q>WU6s+Wse7_CUS^xZe~{tn%BoGCO^dFU`Usg*Ex5t zwUkEC_`!n)owd1lGo+fa&;P^33npXRDwGVMy2u16vA*woA3Y3=I=r&sRDAUyJ0Ivz zA04kT@m73y&28vsXmeEI6;p3#>ZIso;NGvmW{ku$_7`RvXzV=_Du#JVe|cVZwCus| zPJPeqW>DdnasqNmF<-ppmi3&DA`%jar2D$#* zG3G@jHZ{|%K#a%%ni9h~6O#Hr2eIL@Arc01UsI9sP>l#aM(>fBSKNvog{A^x`_S0? z-cI*^p(`}G&y&4&Nu!VRP>TDI@sd_p~dwGb7c!? z*<&(gk?D_?pKT-`Te;0Z%_^-ya&HmBUN+^4u>X-p#4!CaTM5k;1_#2uhW%a~=}|2l zO@Sk)ut3r@I_Ah}9&Oh-N0)HqtREeBkk|+f<5l8xKvol9aP695$+va0Q-Y8<`?WSgbU# z4*OVE4}mR?stEk>T~Ua!Y-a)9Q3~MgMF5vtnT#wS=?O2a<3=jc|EhG%*!~p_{%s8wMahYes7+m2JXvS4HkC8FE;uUTq zc{~vrwC{+;Zpx0BfrwR=@a7wFu6GAuO_8(r!n)E2=~W8$I-on1dhA;Lac+}I;~v{& zy7%CG007HvGO+ytaoTK?;Q~YT$MuGnd47Ovx5;SaRF&aA8CWLDP6Fzfo!ev(vWl7t z@v2SiA8eEL>|c}VU->N$p?_YadyhZF(9~liJ1J<#w6H8rTH466Ew=3Q+fi0U%KmOU z%f8o^m9W-VK#7J#CD85t(7|RmRqoZRG6dtVT$){ox;Bq#RG;U5yOdgqnID^(h0_jr zt;aSNHrGC8#46j5xx;=cTRyIQJt;IvPC0OIi|z5@DIh%Ppo_Hu_^rI6Go7rpo9hX> zb4+7yb-0qSz(#{~1E|OfQO_7-SnM2A!m!veCT>_9bEKeqz+jFy=_MF_(o`>hVTKzc zyfU|jyX^bNM0eT8K^n9m?JEV6zjq`|l!8MBgNXVa11kkE^o@c2iEC#S_D18nxhkJt zkv&F1&>(ISQ1cn=9+iVF6j<|YoX(-rE5T+8Cq%47PS@YIWXk6;_u-yTomdX%ALc=D zXrZ=_DzDaM3}FSP?wPD8{;gHmpjx4)d|Z=fWxGXJ6*g**&LlW?XPxS)8RTUkDVC3_ zkjE|LN{lW`lcw=r{TxR%@fK@*d(AES$0VEtvldM`zMcY(m2IMr z;bb3Gk$Vq_o&kGTRUcz_Me3nAL5D3t50oP4{vvu#E*P)HgE>beQE1@vCM@TZ@7Hr# zzp2(tLhhyLEQ|>45ATo0qGLO9vLTVsWy`Ve2y|+o4>jRUL2ra^v!R9cXQ{T5YBtr@ zeyep0YAYF2TbDXE|8(Nq^H$V#$=w=J_w;u%1 z^%0=oc(4d)XmAjOkYjWDlv5Mks{=&0)W(E)Q!P9+;D+{ExQs#MvnMm^0a4MlhVNTy zy_}q2l2NtGFpj{*jB|3}CPFT=w1Bg;2xE=P_*@#!W7Bc!*9KI(&2g9uqBRau0oQLWEiG`D=~CJ4?;ZTHW0#;w54Q*h|1iNUnF z?!}eIv+co^J4w4&pX~1DJ#3*qUcn~2aHSuzm(OO{>Pc76YIW{ya9i(gJ$TW?@eQb! zqiP?o&^Ei-Okc)mo317-naqv|?jCV@ygOm`6VJ6}`)%1hEZdJ`qdq`xWUT}C&0fA4 z2;V@^_VEos9psxq+upZ$@=w&9`AHWQMWtx zRLO%tSodml=RV8(y3(6yr?I^akmy2t&2>fXH6cMa z)s1Mc=Sv{FZSQ-$A1m#h@C)9LFWny1GR^M}*4bg}JfpGo{ClUb1eu|fvAq|^c|P)0 zz~iil zuc9tU=&3lm09kQ96ZQ~epo3zf4xZ>0MnI zUwUw_?f@>hgI{gN4Y%~m0c^asP_4+BA-U6a%PYY#1}#=cY)s~><)%Ptiy>QktK&t(wawBZYMpO(d3xiMY!7z%FJK~nv?Lx zvx#7R*5xa3jvhAQtv$(2Ea^+oo`jjzwqR?juI<)Ol$ZIslRcUg0l8z90lE0# zKp3k_9IZgDZC7pqU2(q7N?5IWRHOdfI`gRn$>Ug)PCy777ULnd zbpvS=@OP}(!t5z=ax6?;9^f<;7aa367AUC3w3n~m20-O}1TT+bJ`U;tCsnFriDDf4 z)HpYh(1~+lBfJe0O7sXvyrhZW=?WJv?Yb&2+Z$6bQl-WkR4{>DK*IsXF1UUk?3!F| zIae;$!K#1}|F-5W-bpZKTyz{wf$lot${{kR6_vGy^o54!YB{rv9r zId$e_b%CzLB{$WYQ0R!{zz~j^XI*3NuW=oW+fzfYCUig#n!cH=;#8-B?6 z`-}Q9?uh-s=sVVgR}YN!&NLiaKx1_^EuTR!`Iy{CaJL@7R9iidNj3UJYuO)kam+O3 zdKfhL8<{-~hh?_M%5>Q>!zj{8oOF&2iWC#DJ>K&U+w;wlo|}$yYv-n-p2JN*t$Aoa z&v?8BM~_#NaKkP^a&Wd?AcCxdMei7+r$0~q~M7lVV9jk~LISO%xpZi-? z35~9|<4kjN6d;O;)NI93u5{dD{K}|mRb_?~oNHEQoCy?OSXtPb&moT!xwV-*HFz-G zG1@=Z&Cyil;{uhRf1WrjbAfm^B5J-jTVd2DKXc#Fm5=Nn|pr(-2bs0b`y-Z7A zyqYtD4&Wd@V~(mam<}8@Gtv$8AN`nsv0eHBTjAV`X`Znk9HiSGtHgn6JdYbAI*;4q ziI8xsp@6e&vR4uBK`t2daP}h(L9<29#$l8gzU=r1B4S=!2cY4?ARY0z6^}4nKx=r; z#p+;laK23zL&z6UI4)v}!EsTTpLP#N>roAf6c1kJ`0U}?Rv`mzV+N{pAqzQb0r)Bd z!w(~5oly$mu+|eF)_U!*w#&K+iUPHZhBf&A(DGq;Jf;6*H%`2&2BIG^Gk7vA4p+pZ zdf3Uqc-0->Lkif0CfdW~zf%*pzMEKw5G?j_Po^-Ot}n&;<;=*C#GW;BO*{o}4#dTI zoX>;b#ewxn%)Z1(@4<0fd7Q5c0*DD{&HFB#y|6?gQz1{d7xR8IZ)o0k;6|KBy!rzi zUvr{MA2h4kLsy_~X` z(vJZrL64E#xf}~7r>{FT(X)n~?vJ#ns9E~a2y3f%_qgEll>WX|7C3q^jTJ!kyZI_!ccbbMZUZ&DhY=B;#m~UA=JL9yZ5b()t(9leFlGPT!*aSsYWSXI^JJOVFq#ZZ{z|cI^GFCELprWRwHyIXt)%I zhGSy5Q@9P8x7vy~oM|fFTBw+C#Twyfxq1w9J5nhAASmlP$~Wmd6TG0Tk0m5q-|<_!4!2 z$z*`n-Dn42vIrUisimhpFL!`#3i)@~_V$_fHihjC*!Butw`~lDZD=P$Cj%5GC00c9 z@oxj`uRnTC4tz$`qy2}O^vpNIq?G;zw1NI^H036y{4kpG8tR~r!s$5&@IH-K^3=_z zXM?9fs+gC9r*=LK;%RmwadP7KXX5EczI{v%@$uWegbqm-x|Dt`DBOD%-t$;x{a;8b zD9ZnW2GR$6B1Qg)2Z3dHxtH@9bKdcWP4Z%$Z3TWR9+1n@v{3d10w@N3=6S9sj?hNkX23(G;Q17oUva z;I;qS+jkDb z9Dl)n2ZNH|s7DILUy$BGzk_c3rG(-y*l!t>nhS%H52F$Gnk$+`H25P?krExz+8Yrv zQ4wv?6j2ddqAA}91W{DP^>`&>DJtT|;7L@(oqq5rY|w?Lf{wV`K1I;+2$Dh#{*9n) zK}9@{q#_mZ)VCu=p2S0-BIa|9Mm#1=SPsH_d2})leQN@CDsmSYKS{m2-ZcYhlk277 zs;E>KECaGGK;a};ci`BSNQC3o)+Cb&YTbyVn6-^-#Bb>9Y#x`Fp?||TIgmt_+0QY8O-G9bwDwGU&3V{ z1RMMbml&wDlXi^(W)cEH42jM|@f;QtLh8VwGDawqcf4RRg@jxolw`b1AulNDB%A^z zxzvER@wFRt;>=QO*-d`uZ~wUKB}s zB$^_U^h`A6<$zyBk{-mXf+T$-coIo^I(VWW`Fb}V3zGD#eTqoZ50Ml~(#t{Nf+YPE zNkx+M%V#4+evSt#NuU8{RLz(m1WO=(6CO+mfxF=>&VIzI1d`#6!wMJ78yKJRX&sMu z<~xaLyW*7;pA;&9^k71g$Gor)HZkXiO)Hp)$*z9>GAy||xsMsobV4Qd&L8pY4&8_8 zl%Mb}PkCPZQjW1Au;Tf0w5Y}1^C36TI72`;;r7jW5ydyC2)%T=cJM}D2p zI9DK!kac1Hm4{^Yo}&^- z(OXZt%a*0P2s7OW<5OYMKZvUy?6JLllJTZvBLN{VOT1}tEIU@meRY@lDqI8HY+RZ~ z*wh8kMxxkmfi5&N8l6^nfR1VUN^9sPV)$Tj+-w)$$0jhh6lO3u3gs>b6}7fpI>kun z@1qG6*3ljQ2NGg)h%b$xBxfBmE3GI(cXkS4>_wW`>N?C&-_fytz?N|kQHSQ5z z%t70)V}NtONc}NJ3hFu}2XTf9zfag;4{Q+A;GvqkyoE~A@qiQYESRXkx zfe&>YT0HiA0i7%8lb)!QyxHcusRjg@@xJ+?_R-h>6rC#zI@kT!TWPLiN$KVat=;f2 zqVUVKZ?$E|<8P(8W<=DL>Rq9^KR$zn*pN}0;hi)Z2U|SB1(q&{<&+5RhK|_WeLQUe z!GLBc7z&#k(Hi`2qR3;aEdr7V!kI_v-TE#dGvt(hxJL{6^zfbY&9{PsOl+>*aOPnc z^MAe(ZLa;TFsAQz9kICsw_|fg$e3Mkr8&2+NX9YmD%L5tZWGQ7hVi2n@<|*ssk>w> zx&>C?4hq3tGzW2`yTRe+;I8Ot-EG-(of+l#aZ`hAEwJAW>61+j!2l)0aqssr-rMD) zz`cWS5aH`&Ph9{9FAi-negk@;8xr4IEe>^Y>S9qXrQ6_i2(>r|6h_+XuS8Pewn;p? z+BkA~f8!c|vBS9%AHKY1u_L2LFU$^D^bA}_ckRQ^`Ot44e!+(UJkU7cS4H>n1@3>z z7yIqQK0X|<55MBWdceXWKvqMdeGQI!k#Ao4QjAJBMfAqbPPKs+ePf~p=lFA<5jUIADM3sqcYs6jW zQ!KV4UpPJrj*yX~Ujx>$a9RnjkF7yi8Lp47L0}cGPpo0I8onaB28R~m`s5nKo8h`+ z4Nk$pb?X`rOpm4XHHfc5S31^sgXjTP$ks4IK-}B;w5s;w!)l((gU^5sLOOnP0+#)h z6FxtTXG~PW=ZEl&mE7Q&&_8{_VrQ7993#%oS?ufpguL&@eYd?IJbSUT9rwQe8Y-wz zRgnsSqs2~_WSkA({mF?f<{kzE*6MMuZ$TNXkVnh(p0(K7#CPrE_)*?;p%-ar-OT8Ug;o zPKtf@en0MiY3~Pdzu06S!0j&(W`I#Z_v0QLE#$rr_dd2-@In0s<@T}7Un8NHH{}fZ zuRb-gX(ArD6JsN9&c;F+uKHHM5K=|33qWsXj(47#=wOaJkRzlMb8JVB+nD15>2w!w zeV$R$$-B26s@GRsrQ z5+f--`<{4`IToIpc$U`*vP`?n=lzUR6VLE^tfYOLw+l{9Jk9I7B&ip-`a7sIQy}Z} zli?Z;iMs66L?1q&j87+y0zJRWTl9G2xW$R*a4%B_R6kA4n4CWyl`c{Xu!5QU2N$R3 zVZl&5&=M?F<0c#zvMUJRzMOZ!co%Md!+k%X4t~AY9MGRPzjZ)--n{OB^1RvafY!Wu z!vUdr^E(IoX?XK{2Ug}8L2D!eLjBhUE$SXFaFZNFDyXqzx%eu03qZEOVe=Zc8;w%G z#qW6aI({>1KYr`f8~B~1euv-D>i75^3t+e4yry5{HwQ?!Afk02ey0J(E#6cBxW$_e z=(h0ui@mtaQ*RfqSRX-vHp9lC1cqrc>_Mze;sUV`aIJSUY3eprw{3~Ka=mdlcQI|= zHZ|6y34iZnniZKnyq~sBWg=yIk>&?N(#;HWwyDvP3idMXm~Co&q)-pjrf*Y|3i-&4 zcT9lPQ0G93Y3>H1dnfNHJo|YAQV!Xd-C)Q+u7dtoD?}P(S(YMqYT|%o(a?O-vU;Z4xN@-}r5KXNn3HmKe+9XK@(Rt^oexm7oR0kTb=auAd z=8X~27T!YaHu4JD+RUq!6#q)Z(H*$C*?)4VaHuPlBHYMtAW`nTZ%PRzN+xhIj zk;JV`GXi)IZ;jw}@)iwJeDB1)zRy$)C0mfh2Qb9q74&S_7{sxQG2TWTzsg%Hj`uUt zn^X1NM<-!Y9EMd38Q?LgNkRa{Q@;m>j>KsEfj4OslW7~AP;&eo?34p%DTk{W3rsA? zR*c3{;A!5l0Ao!sT7?C6os9w%Z&|EjUSEG1Gdju4RYqh!2ieym2kPH+4zi0Ic|!op z-2p7(t}#_R2oGvV)O+~EA0oj$PdRqmT}uC$i|c8e7!YD6E%Vd?no>W(OJalG1wH+M zMB_9hb}z@dbQ9p7SZE@s%=_HO86}m{FCZ_V9pmMpOP2A|s)gkR&QtKD4Ki0{& zZoPi;TAb3ws1HXkTw7C-OqP|Exo(P1y*!r6bP|_jMwj8@X7B}>JUi>?S!=a>9yjoM zV#V}x5zZ}a76ctkitH&gLm8HrTIb&|*6&#g7)=y|1QzI7rd#@U25UqSA=V-?M->aL zI4G7pY`wZ|d8*Msz0f3tddb7S8@nw}m5Vo`WN6X_N@l;$Y}2;oe+4JywmfxpPyvm_ zK-JKsgsREIq#M00KNP8q#v&9>9{PR!w)}4+j83d&%y(ZTa6j&=L8AHm_`cFuF$K#G$Ck8?8QymgvjdGrnBf^L@mgF^kSp|wjfC69ZXk%dtKWDQJLzD9h?Wq&kgjoEc>mGE zw9dm4;R9+Kau6BJ7-5{(>`+T0`rVP8zt9z~e(KpPP!BSWz=fYi_XA{vxIm(Wo(!Q! z`o&+O^Vtv?%7JRJ^I*nYg+12y)9+@m#1$CFuFfRR)t4jQ@N>%f%sNAtnq^RMleG&n zei+wMm4(!bP~{f{q^tnQ(P6l?rcF5~wa#xl-Vd?fsE1frUG9_BRYYIp#T7q+!)pv) zR2IG18oUs#9x3t2i?%9>kc+(N+J_gTP*s_@jNP^uRn0^Mv9|uMZFv-yH<)h{@{K8K z@(R8<1~0IcG6v-1zp^iKMK4Ya9ho@r}9_E%}1uv;igp3!Ls0 zU98ommx0g9mN6<>j{y%~|L9dY2#KD6`^~t=65dGQsyI@*!i1sRh$?W`)|5F3hzo{I z#F1*BB5eeOkHgRL3DArXkR!{-&!{X?0b9;KlzhyNu{2%Gz?J?r3p-(9-9w~x<41W= zF(M{)8cTa6r36(Tm68c{Oo69;vru&ho2v$mikZ5PEI~#Jm>O}!qFYU{DnSntB!VYG z_a}Bkg2th!*o2W6KjhKcS-gNfhjA}j2T>nQ;NxXjNwcXY^4S=6Ho0KWGVdm+TH$Ay zHS|Q8Db8l5LK%wr6Q*uus)RPtPJ(kurcIm_GZtqf#sYmmvfx6qD71%OGHEo6Lemff zYR5YlCw{;(ubB5DP?y_CZIzeF?aLfrOqBB;-kzjS_c3ef=RLJLOmE(La1`)L2Sf8p zqxGTat2iV;*C!n(0hGe0yABJ;GC|z}3s%^>QVs|}W$wi|xL_qQ?3m^G6cS`BDVuWS zEfMB#Bo?aGRPBEX&YWu4zj@3n*Qt3IW17gsI@`oLHTL3gHs`20Y8u~Szei961+Y~9 zDOjEcrgds`p+H(K;k(hYYPwD>CNv1-KgMS&PUr|KQPWY)cqWbytC>`&2Gcpd!;UF9 z{Z<-Cb0~q&%YO>auAt#jK?x4`dJuzRm24GdXea{HQx|h!q*txpclSBDb%<0;b6s-u zI)YUlqCr)x+h!|Ob?YL%4?3;J7BXZC84wm=JEV>&WEfw_02{<~;1rz4IH`~&7i1B@ z`zMo`tMr~A;J+Iih#m9TcAHaH>nm)^Ye)!MO$Ns8>7~FKX$<+;t?o1vh&@85e_a4VKE=J-zYPmfUvG0`X=+}f6Q2E*;8YqVl{S985-tz) zVCxipx|lp=E8rnPLMIZ|>1oTo>7vLJ9z$KQiNP!5@e_ee*f}U`Jb?WoD`@7Ks5u?PEP{_BH7(=-IlWWpIDK4peI`^p{5`{;0xw%U;4q`Hf0E;1dBKPT$IdaG$cG&o( z{DK2=>8`uEIA+35p~0B6nP=47^CFXyaxKryHit`cEgrBHTD^#^rU5o6FIR{YYbClB)4CzM3X)F6S}!_K;D$8RccvRC#gNx3RKi4>84y zJXfPp)7J-(Z1PXeXLQs5fJrJp!$WJNXFyn2B?KSWt(EzT6@a6TyN7Vt4(!~|a3?!9 zAg6Jzj7qPVOnk~|JDB_7%2J(`-XwIO4m}!+9bI*77Js9Gc5{Xf5x}it36m;k=3tK| z+|j|AIUse~004KGsd7#d)~*_7b{FF$#ZRvoCHiXwox6b9H(>3KzLXp z4T%Iw#3VH*u;P1xw7<6- zY{?V>5V9pnRy7}5FDB2&3YqHA6*~ziDo&;?@y18Z6V)lDURfCnXxA5~UB8zX((Nfl zy4^*jgD8Ojr8ao^c5676v^=a`<&-Et;bM?QKcS%o1bS2BIw6$h#tKoO=LrC{1#1k<7MDllOWA=C_{Vm9W> z-fY-1IMWCGDr~B2xDnA89>;4A7G=~^p%hVuc;=pwAp=N4iy`oZ$B^govOaf+TJpICJ4>SQUEEvg#07VOYQ#;%~^R*o`W@+BB_*SDUBBRAtLF!xkH-m0;Bk7{j8P z^5|H#V;aEo!K=gI)pk7C^YhxLMR~Q#keR%yJL52QS(vt$f@y0JOow3AcY!fW@~Q(~ zg=R(|A^h6Lg&<-K_C?SiO|XB^4$Tkj8Ah6{Z?%5qPUU~py#3gSx09*(9Z~#g$8plA z-LZNfw!;=D3;RmN-rWyJXOKQnxOaEqo{iJH8oclJ@gOzt#0||t zj%Tz6xu3fs(KN+NqZAhhR6;+%kHd_~eHJOr0FM^se5dp5e2wu0(s7I>w$u^;Dzmxo z;k0WzwE}WBpUsFw;}67!r?d0eRNU-ix&qRHDzpjzqkv(l2}oa4yRIE67J}AvCd)#gI`62F0Yq&7D~7*HS_`JSvAR z?RFx?kYbDVvls)~Y_T!aixoI#tLTe~OnR{ct($ZIIVa$^1DaN#CUHAregS7TYNL77 za}KBDJ*ZZ1zq1twgXjKDDzyV|Hv{NCXR`L5qv2LW9SqOZdzsroZVnwQz%xsD==`V) zcwh}DIvWu&!B9TyU*lpBh`c3-^<6l?Ps5R*$T&NR&8t{cM>cSUY$GLC9THabI1a4! z(!y{;ucI(qS1h<6B{(maK-4(wrZCR1a5L2>iIyxPXFtga2EgWT5@t_n!Z2`8_aO)= zBn&p%B0I1moYTAloa3y`{d>p@kYllVv$8iE7Bbco*ntErW7uuGTkrqc*d^T>_YAtT z)sDh!4(PAZh#SpTi!0AgCd5y}WA(OshEiR)>MqYj_P)Z1;FBV;u3x%5dfq$FVxMPs4IboIMe3+l>JIIg?^ed%OL{)|#yo{h8djx`C?T zZ4p@^vF+(_2nrI7P#+1c+1WYOhrsxKE6nKY&Gj`!f2pC!z>s+2piqq8#ijG_osH`(l zA%hQXh1~0YcwJ zsTqW6(M~6=r3hmyH3KH?D`>vy8CA2Cvo+ldHcOj@q+NC)sn?QE zn9nMcKg@^?1y+wg9%RBPsD(^45Mv_}fV15{{D6dfM`VFgeF#>;rWpeJGTQVI%4kpHp{J$)juC252%*TeS(xS(Zo0Y2Fr6}LMf$I z2~a{+*%wol#e|ES3$|C+Q|4+QqS6NYaVhbPynb9bZx4m@km8x_UwtHF0sMJH+E}{= zK{7G%w2)d9&L{wUnX%2X2+afziUrfc9Tty9zg)AEvqH$-(D>`+2V)5$^~7B(DD5)>1wzF>jzAYMZ)Ar@vH1!DCY zVudl<5hLQLu>TOzSv3Othd}=0hd{nB#x$TK)NfiQ(KM@6@t zq(myJ(fmNA#WmV9halOXonn6RRL#nZg)~HZsDcl#(Jb6yu_MxIN=!&fX|ym>q#z^} z`fJo^sZz9&fU_RPqrFMmJWG^UMrk9Y@uBGBwCVrfqK%Nok4&R2Z7ifAl8=%$7VfZE zKpPa7(WYny$rVEAukmIEDWgplRGKjZ?P$}&s6~nH3z1!+ZzXLiOVLJx90wQy8D>Aa z(dIvi^0E>7?ogx6kN=k_bjZ=h!W*I*-HlqLfjw4Q-&yFx!U3J+3abD+img>JP24_o zM}tsR)?9}SVfRFwPTWUpLf#9Ax5gQFE6eU(Llaup+|&X?tKJ=6Z4B}KtLgB9gKD)m zo7aKWoXp~+v(<<)$930guC8i+WCu?>R_EcMyd-zIV;#}iiyX{KF#%1=s=jRFVaA=6 zdBhCBZL$+SVSF2)5+MX!O|910Rm*q+`ohF>c$nO3Z|l}aCa#Ei6lB+qDDiwEhmXQ0 zu-)2R>+SXJ_R6qhWROV9WH;<%-963tG}Aj4u+3~(tHE*$I33?mOK`quIRXx=DF4U-@mL(g z-g9`1G~Jd_{wbZCraW?+(;s=@0uvILV#{%mvBe^M@`YR1~@OE&}K5-pyHe4#2q9&4Hop(?Bn`_0qy zV%Kb#USiE-{q*>nhpDpE5~F3Z@dg|@F0~o~G608?JOs`w4URt)C-<-=_kmL6-d{v+ z_=XP8^JOY`vG?ylDbJVQG#!w@XWNcz;K^_C@gVUY^LRzPhs1qstVD=Bo?8(AfyL^H z#udh2Ab+AdiHoFFIJlSE1t6q=b3y{`#V?A?V-Nxu-$5u~3YyIx2o2|hoWg;_;$?XN zplqBD`IGf;(j7zJbZ}qibhGw7+pK+;p{7iH?K{J@Z%(VDYu{zz+BZ-BmUmqH##d-% z>3cSO@2F{f!O_z>9SexTmq_SM2>>Xp0FGSy<`=-M(^|?0DmfwX6Q$qGhQO70BkPIq zKE!g0vewx8Z&Uf)tj+Au*AYQo4coqSbfRj> z^jiq_SP0^0HtjBg;E3mPTWo-la9FH-{~*%)hQzihrkAckh_(6_lsI~WZ4v8df*qUOlidSk?Yl9i593A; z&j435q6v;OM*WGFy;`ChvDeJSUNf$*k3y^?)*r9}P-(N6ikFx(qlD4PC0yN=^C@u+ zw|Bg70~jxf*@{xN0awUgvI=pIA#2kLKtgYzeu;VM5x5)1O>R8|i{B@TtaT#AYBz2p z(LNV-aODzmVX?Dw6+Yu3Wjj`xUGflb*prjwcK*`OYsB!`5?!g4z|LX^Os!5tzA3drjWvKAwa>mX*C zPLOtJoFOCjCcO!q`KN_Qvw()(E~0h>X(e!GK{ti5G?$U@YUt*fF|FS+(`fyTGXtd` zmH!Ph<68fS(g(HPv)UXvhACd!_`2HTO?KEOH-}9|G|r}(SeJ?h)!-n2!&`Ly4h9JA zm1Xp;kAc&F)QXRSP`oDd+;S^g@-;AS^u#{T1Vs}=zP@iJVn`T&2^(;+vz4Drck#-f zh@TH=2TP4o+BaYEJLVF7%%}C|&xP<`D4{NlNeJuECPF>(%pt{AjFASnT{9bVC!-A{ zBT99qCx*SkKt?T6i@n#l>Yka{o-Dz?x<6s}G2Sz)QGL+B*@0W0gNzt$Y(*r%DElGA z&0wSLEZj;AHKO5o&q!|s7~xiTp%}Z_4G^w`=pc4sR=yJR2ZISL*v`U!Y=Sk*JIO~P z;$FQ;W=lm6%0^}fy|WCz?3xwuON4kkXT3??WqCpEY$Q8r0$Q58nY;&7u2nw&rERReoR1l7-_D+&> zW~obBVCMD0=u{Okj|A49kf?iMI!mmAsGS)Ihv3)**!S{^z6{LZTIc}Bs5rpiraNN1 z`LNku-cObfjX9UKoqt(v-3)ZD5ntpIuS5rs?dF<+K4}F5zz_$%p&7=pvHi=4fv6}1 zYxv(}!ilB7RmRm;JrrWWF4+zhVSr&HFl-^0h~&lMRk50{lEE>eAF($_)Hz->`76+b zjD@$ohL{95_iw04MqPp*8w(*NN2k`2@rY_YfXNSrXaZ#xN;!82@kqN?h>~ zuTi6{l;X;siv&#uVt$hhTI2n_`rZPri2#EQF)v6Q4%Y zp~wF>!zf43Cl)~RiO;}1jP61IbJsdW95Xx4F|#el%r3<-*!>qBeUE}+{BEg#h#&4Q z%`vkBju|eGats{3!ZExSam+B%^w8`m!wi*!VM=5j%rfu5>=7LE*frpo(+tO4YB}br z5*#DYqYrP5V}|>oSzuEhjb`~j;TQ|xNR9!P!7(B_BUxuooOR|{)|pd^b>l8L0 zbW8nBhgLRo0@i7cDVsT#b$Bgi9i-`@IZ@VWqOOT#9T!9e4l?h+oMP6gc;jlY4h|LN zN73DHTw}D&x2_qfZ3KLD;;r${_NTC%_5U;PSO7=z4$y3P=det4TqDpNZvSXL&N9() zrI_frBQO!iGMHeP+=q)8-BN$cp_%BofQi~;Omv)OB3_G`2x)rgxF{2~kco~{Q%B?- zWZr?}MljK98zM~f;1>)NJ@SQ-OeEl=6K{=)+OC8GLMYS!w+hGtIFgBgX2V2B$4ztN z+%(s6)7(a3>6w zlr_vfKVj@^D3+}9tyItNFxvT7 zR~vl2el=^JWI+~Ns7bH`90|VKF8%&7<9xHpSln<7g#I9B^6>Gg53N%Z@d+XNE3&f3 zRW|b0fUp@gV9rq1y}&b^UNGorRQ6QJv>qoaS4e@QGBkaLu5n7T0zH!%3HLP8I~Ry; zz+!)yc?s55jB5>^#G=WP4dCPqH^?*sO3E(w?jw)5d8&-T$N3o10Hs{xCS70*eQOAW zdTww-G55vLI4U_GG}KEs=HAX`h8m2V?{6p}=ldGsaz6S{(;UArY7B!f65x!Cd_f8= z?5tqCFFIDiEjzkn9%ZWsdH}(@R&(T8D{v2%bvH6Ci)}0O=y8||Hq5J)=6huQ zXaml`mFn|3ba}orFwd@Sq4P##AA-tng2>X1&@>pDXNf#7Aac|hxo=*a$~A|ga!6$C zcA015OZw+|S&Ei=$awFE(QpC}>Iuhmu4z6TVsvnmw1wXJW@L&n@zHm$d46oHYMF1k zw{d>Zy@>B@!~A&fjNj8g>;h&p{4nHB_`GzJy=SoD;QU4@M|N&@%(ntJU{400n*csO zyW!-{Bu)3B+tOe-C0bg$k zT|IGh^#Wbu4BS-;UptHNHR_1mp!))}uqWtK*L?nNpKnmHn;alLLOzQN?Z>#`E%mNH z-n#+I40}s;ZcorT-#TuN$m>-jdG{?r+q6JfcXgnDn->_~-LRkp@2|tqf z!@cv|=9^m={O6<-=uM>d*$+xkwGHuFS>)FoMxq`9O^O_r9alkZ8i%F(@KweP=(#Ft zU(<5Y?Uw2iuSRJR#@`fD8}B8~23h}H#Kth&5vO*irS{fR)ZQFW+YgcQT?&@LJ6^N1XAwWdW6966#%d z3c*_zH0E$zT|=UK0cD}en<{>1NXY^-I(98EA1L0sAU|64B~GJQ;EmNEqC*uGRbq7_ z?`Qd{Qg6qKX(RToSL!agI|iqMRO0xI(droVfM2su$k`+42`RN3AmGdJ=#?6aPkRtn z4yn9yNX1q60=~bJ%>9Nz)BIX*P42yfc`pv-#Fut;3+;Pp2IAenAoo5Sk|NFEwXy#H zeB=gq2EZ6&T8*f5FU1?9d znA@mnpRH+-H4WOD4sw^`Oqw9(taiWiOO&qCHSyCT?iuP)GnO_jF(g^H9j3ERNsuPf&qvdBlm5Q`-8~-^#}<1+8!Ql`9vg%=JI}2P3W4S zGWooLsxyIuPNB8*UFb$v_a>HQEAft7)m#Y_Oym=gk_5x69Yjh( zBO)j&FW(0~j7)3YPr=HXE zP^ax|ek}gDu00ojC*bdR{LRPTiTIm|zXtqq8L|d{qwqH!fAw4mo`t`6;16N>o`=74 z@%L{0ork~G`1>pT&B5P$@pl&f-iN<6_TBC=rYuCG<%rE8TlKd(9ix0wyVvfF&RcHVG zm=3Y???DZK3Y4QDC41SN?3KrWg!XVena+Jttm6JNIE{eoMKTUB;{bDvhTU-@9$qKQ z#YE&FQ(}T~EjG)EIQYPZtozeMl5aqH<{MC|9+NuMt!BY=;uTtbJvzo=vI*CMn7l>^ zLNWOxpF%OYnmI8o9AXXAC5YevZxN(`c!FllGZoqhOH3~AU`kXh)QOURBio+|lY-X< zoKL~)e6Sy{$Ac+(JwdW|@OCcj7G5VJN|4vFIO-SIf)L$^L%SkUbejet+)#>gcsEGQ z9$AbIDMd?h0MoUk`6#hMgy!!9$;td{Kyp7q^De?B2N^(#b2npw<~^D;$8^as2Q&6E zM?mwJh_M*Wf5wy;&0l9qR0Wdc{%+KzK=VFcLGuA#LGwMlawO*!G#|vZK=U8)DWv(U zd@O4eu|978r@6F?;?M?Naw$8a;skW*Q zZ6_&uN*|u>P==W?qnKC9jAA^7aZ*hyC{lf#ph$^nN=p#apomc;2n~uL2vZ{{6EmHe zp!k2kYoB}Wy-8YSe*fmvyB}vi*Z%Ic*IxTY6FAsP6F6vwnZm&nbOktgo~{4~uhRt_ zw7~Rn&_JtZ15Nz;&0lc%C^>9?z;b2a;9vCV0SDWd3~-)Hvv% z6*$;I6FBIk2^{Q%NgTLe(GrZR@6#3F;Ir;H;9ErhD-P};4)Qb(T#lSS0uUy^JTMan zrPQ;?y%dnCc-uq@2Ng#U2NBeTgGD6+Rx(5q2Y17fiGz*w$-u#r^l_6w5FF^W ziE5gFL={clYIU>%9kn!pj`cKk$8Mk%K-okSK&gkB0?InN0-!udmjDI$D-JzD=Lal~ zFXU~ZJp&4F(kC4XL{<~x5eJPlfrHI7frDn6z`<6U8V4=30tc-$frB=hz=69B76RdY zngI@;rz^n0>qm6s2URG~|B8cJ;K0TG!K1(M98hvPF%t*vER0+?g7Jv?rf|?H zIEbPy#6c<44o)er5lP};IqxRV;0L$SCxru!e|OWX2OLx&IpSa`P2fPTK-V}}O)ub} zk|uDlf~Lkn4XwaIHBI243T6ri%jgPlaLbX*Kj0Ya_;CwM?HM?Dl0NA;@I`|< z<_0L#(gYON(*zXiX#xtHU=lJ7bOQ&QX#xjsBP{?z69WJT&(jQW@E~0Q4xTu&8xDB6 z`~QLimjc4gseu6!D)KlUK%h8%f(8OaRcYU5mPW3Lfo#Z(Q$T1tk_;gUgmwmOXNV*a zz9ii;fN&*F7dyAPX_F6UIlX!SLMM_V5O&Z65ISgTAe2F}B@jw!0th?#NduvRUI4;U zngBvM%oGqlM?e4puV?e+kkDd&41lnVu8k~>4}{xj&j7;R^yvWztC<^sP)QR&SV0p& zsGANQ#KqOcRLUd37HVTWJL% znrQ+NO*A3qP~dN)9gt|H2}rcSOhMuqx&q$t3S9vr-lgkGUN!C`;vXzl1|q(sPY;OL zj;Jmn(GCkCv5h7m(LocC*g;c6qLWr2Vkb=?qI3pKaECIOM8sm60U|y@s1zbT&qRcQ zZw)AtaEHm%rtW$cZiRL+3Ey{*(V!<8$a7E8P|m89^B8-QN8C*EW}sm$eKOGS0DaQY zKoqPXm4E^{NRRfZrW+8bq6rABrio@>PZQ1V*1(c#_Z#U7+Wk(tf_A@;E_BICm_A>r zrPpgLhYy9vY0rSdbM)x}3Y(b9{&+gJo)$o%jwYbcKod~dOjARlkyb#Vi6)@XOcPMp z3X@QHg=PSSN9hWf!!t87m;<3ufg&XbibK8+6ejI46!bg;dG09`T3GlN7BC5gk4eA` zDEyZ`8Bkcn_np$Az;&)yO;-Sog^1~_Wg&bVTu*xj4sNDT4>(xLOu-S#XF~oU4$9~O98}N*4pz|A zIH;r*I9N>+IH;lt98|+34(_BG;NV)i0vy~pvl|ZD(r^G6jN6rBZkH@!!XD#5&ofY- zJ;gx{3tt181vq$w1kAv})AY%}!OQgNiUYoXP8`(H1Pb-M~QuP2d0zqD%jF zo9O`@G|>bO`08B>2M^H|;NU5`0vx64BF25m(=f}n+_3lMCj z1t4gn2@q_fsX@?AD?qTFCP2_Z6Cl_DlOXsv%>V>%(G`H;!|oucMR}6UK=XnzJ!)rn z34-x^3<6Fv9MNR$p_I_c!gsQONoH^jt|ZL_fk&SV5ZpnZt{^C%1u6jqWi$Z-xupXj zSV1>HP(c$QSPGNO!>ytRK(Lx7Kv2mK0W-LYt^fop=?XxwhOVbs2pdCey5^8wl7$69{Oa2?T7G?|222W`KYVbOi`_h+6BE zUXjBA-`UGV006)>+AjL}T_T`AR=$?!j~eKmo24RqBrob&2J+rhcEG24okkWkiG(*v z!VGrs0ev!%@GtswMFJnLCla>O1QO(aL6CnN-9SPcO(1~}*$Wan=m8{brwJsq^Fx4y z*Xat7@IGAu5iI4K~)*vZ1Y&A_ZtXsvjL8Ur+pk-@m zqGi|n{ta}aVK>o4!`9P3XxMdh1r7TkT|vV>LDv=*BBL?iq)*V8*vuu_<4H_u%|@DN z%*`~>n9VfNm|J18m@RapFMe4ehL5nu1#h*cX=a(G`H_ z!$Z5VcAUlRHpcryJT~11Yw)`q<1vo$Wr57sWP5lCRUVlAP`}Wfd>K}rmi<`2mTb3&WIt0@wVu}6ci%XJNDueeMT+oOf}D zr+(m7UU!$5THzPGzMA%`Y}#wG;mlWJ|E=&sxPA~<9;n6ZwCPXNI=gr!7WoJ7RrmCo zYuW>`!K1N1fM7Zo<(}H*#n>({ye0+}g}T1+idU*LABT1Q*I`j#zA)SMfmzaqk5dPa z!U6=T$Nb>!g2Qtpm4CwSU z2IRJpT|g<>u9cD$Bb|T#S&vUi?W1NuZTQqhBiX^oqQ|^%BciB)XF30ToW~QGefL-? zY39L=0o!U|QoJ!bU|ZMl5|RwQ<FI|l6v&LzhYzz9~*4bD|T z6P#-)Ofm?!iXPxxt7(FBRq{i?xvrus;9M)|3OLsqx}IhseAaa@?HRo55&CpxUh5H0 zW+*gehC&ONS1nC2uR544TRq)iUYlrwc{TX{n`wf1Jw-EMUK{8NnAbzcbYosTI?|1M zo%S7OC?^W!`1U$c*9B_rDX5*Tq4w)0n!qOKUR0b{l;%z;-u%x`IvKtpwQQSrvdyZgoM!tv(J72C!Aq1lZ&~7J{vY9spZ4 zO@OTmW(sV}=nBAg3ta)&*3q?vh48_)k@gI*JxQOgV5>tsf=!;L`E0uVe%R{(-1==y+# z@IkPJ_6!ibNuRDDXhJ-Kpphm)u$dNspqVB>uoWgj&_Xvr&`J{^X!HHI(F6$Irx}3Y zdAb4+ygnxb1Pb5ef`hIg;8i_)2m)s^Hre-%p)6eOYGIqTq_-JLTsaP3k4G1yc>?%5 z&DrRZG*3jAr1>L^2Q&}FcrcEF$|;!naEI$GsHbV3im4LKxsVWP{umM?&66-Tj+HBI z@Ho-bL@)ct6ylqwCT#m8c@h}cemRLk`&4?@JBb7Ax&GGo>S>AIHa3{!t)*#+KK3km z7P!z@dPKtA-~JK(%a2Hmwhxp;V7S)qh{QO1HoZHJNQ|*h^mpa99+?wns6z5AWk4Jb_7PV2^T37P#gjl)iBH(@v-NXV)C;>H0HX>Uh<`_^`+d0dI}-iNpc zJsQ6j6-48Vv)72Fd7KRzkYM9{8rF})-Hqkwv%YGcViG! zX&+|~m-C$@SQkQmPdd8a<=}EhCLue0Lhd9XyV7yTp3%__j=^J3!XWh7;l@;bTUBI+ zmB0&aXt~ny*qt9=4npE(#_>WCQlo6Vex0;*yxrTRe2IO->&L^q=BhI6x~+kgJ8Y|I zg27esQ!SH%Sv4M4;>A}e!<{OVXQW)8O1W;lK5|z(-tLRQ&ErdOcg!T5(&&pluosFf ziVLQ3v!)4%+i0*g%Dx@9Y+5MHBY5qoz;GIpwLWXek&$qw$Hbo z*+b`mq}|&Z5T8bT&f%#`sI>}>x%L3P&%axZGb`-hV~~c(|GmbU4Or;|a&=FDT;XvAm#M+yAwfpeNvem=WlfK1~`P&x}{F~xK3qZ$p{nY zF@)1iVxX!tpK|f3Dde#VIalV?icjT%saW+oR+>+Pj&)!Viwu?_?norea7QghMRG1p^DNpmM6`zWO z{FFsegYt~R#SAD9%hov>TTXF6XN1|1Etk}fFmbtVI~d>yvph#`?!>)iIW{y>@Tt$S zKN3rGj{Pw#IH1Bp_-~MpFlX7@#h2mH5Xlom+ zIIGXFt+3*_KCLaV;>13!&9H9r3DF39K9rG=vU=mVCO`j!dFl8oTn(ZT(k$|IwFX+6 zPqp~ej0nJ(yd-j45o*6T<}O4v?xC!siQ59U(p-xIGzr|DioKKBZx#=bX9To`#2ebb zc`Pn!ZNLZT??u5im909@kwKo$R$Z=%-tkx=AGmJ!TDn`te+I}PL<=S3J`<2@6(Y~KG_Ppsup;ff^ zk*F(aE!9C*(C*UP4K1hrYVCgm?PnLO3YteswCiY{U#ynWJhNC`P4hSMshrkJiq#UD zzbjT{G=C+ZuAue8VpU4>_r>aK*7$;AwUg%g#p(;1=M}3?mzEDe)TJP*Q}croE229| z6|B$ueW()ljlO;J1YmihbiQT`?X8$~qEA`Z>I&S+{FZ8aHSO1E`yI4jTpU_M^P=L= zZ8Xm)4&CC?GOsvvGtFE`%WT^hkk@Kj>S0+y%TDg^B(HfB`8B}8GCV<#ukfXj9$Qld zdog8gLmA)FWpv(6d2dTvfxn*##s0(se9eh9Q~eYaSx<`Kp41wAVV79T5Pv9EchS5Q z*WJ>rLka6x!s~QJAEf6^+FnQda&3Qr_8YYQe%hC5yL%rU71~it`}Nv>FYL_q9-1Af z#p^z$zd#w=S>YYYI)t6iQr1%N**E;4Um|EHgO(i@dqV5yvZmHft5b>q?Un=<_rZqw#aQ?$BEbdb$-;3jHzvL|UT)Aqm6j_0iC{{-!ew7r4$97kU%96Guv^f)}6tpMuN z8mLwy%w`s<0u*`1r${F#QjYPcm`^PgoYl2jq=yMEY(2IK&StxkPk6Ifa27AD`0 zJo_gHb#j%O36LfDr89#-9q_UqMA}6HpIa#RN^K>9F9woLE2C7nYOs55gl^1OpcTWNVp7xE?Aw<6GLa*dZ@SwTxPEK6Ckx*~!@M$z?wl|I${ z+K3!p^>b)|{XN<#f4v6FW+wDEST-?eEt=t3zuupt$!gdPbph?2`vR+VS#jt!R=61{ zKIcbiLOM-M=j9@hQv>`(TD5?IP;as(6M-r$RJ}pFr(yLvlWNT<`&K5@hOWX(4`8jI zP!eCIIBVB`r2e+z1hgr}R7g*4AztXJoFaXrnd)*AM>DQsa+;_3N7&~lJo9)pCi<5$ z9AMusb#;LHNNx;HUR|_5lzvV-T2Tv7h5Ao!gx|6${IYOpiF1H|-}R;`H~{bwekH9uhk*>PqBcg@t8%0!y+~ezJ$VPo_DZO4%(W z(=ZpwAQG1X9kVF3=|oA^XfNjZX;~gs04d)2IUMlb%uuyR!3O2kqc=H zCw!j&0q9_|re$J|gpUn~3!CGfzd#SAT&w11Fp0$bYOU~yIf)O{S_atwX=j>Mz7~Tp z4e^roUhr57hh5`3wMd>R?LmCNrCap+?+_iG6O*75lZCF@gF^E?17{m@MHkrDM`Ib= z1&8xBd6r6}rl`Z!eSXEHLucgdC}u;Wg6{4qB-G|Qe`fTZ$>@6!duO-UWr8wI-Q6`0 zGY5I_+?r1wV|($pwh2NLW4PCUFF8+tqCFJk;y#L)6FJX$E_q-5)jVp0{>S|h0jIy6 zXXC;SH%J725A(bxj<}`yfggod&Mj7QoDKFHVXr!Zg&l)+_+rHqu%kmO=E^(`TNdmO z&<~)1pG*8>0yo#jRHP7F0qi_!|Ejt6-w^^#Vy?{6U*b{Wuxu(w76Wt$ZJ0aBA+7Jw@?v;0-?#J~1R&FV|W0qIi?^iTtED7RJPv#8323`dVO`JDDA& z=^eC~SU;gTu_#pfLzul;#sT2KbZ`x)oR&Nc>ohTplpk3f`ie1NuQ~#!cNh8}>ZcWl zmcj0xP{|ykpWwHcT3Y_I?aGDrOI1-LOsz&YFA`jJsL=SHZY}6#=-Z6TIh03aC~# zIHLdRRR>^z2eopOyO~m9rTkLxzbwJ#0TurhL%F=FGz4A7kRDUOaw^No-1r)rm5V`V zR$gArn#eoEruWK)ANjH`9Quqv7(gP_16cjtfGMzYa^~*`%s{KZ>8%S|F{UBIt{j0# z5RRb1`kj~p^2fSdSFVu36v8RA`e5ymGib18Fb54**+~awrtk+1R*|d)tY=KZu*w$Q z4Udpvfcr}lxmcvU5hJzUXppPr+nwOX=u1Et6b7(Euo9g%me%KBJ3-EJb@yoFvr2En zC^f|07tM)>V~a6H{{r~Od26mKU{LKvsaGuP$Ki5FhCrcJD`oV1R)>j!iGWY2M(c|X^Gf$NsHCRV9 zT?h;Ky^WY1avsL3D^@P9#t8OJyRsV@rAT$*CzhcL_xCrFAzhS?%@27eK6XefbzDOB zY&bQnDa(OAS)-Vv%v#7%syKL39tkNtu3a0#B5uf!3 z6G&r4lv67evEj{J;hV^rxZRLRt- zo6emE`xKZQx`+Ex7#2i`{}=+ayu_ier??VZy zF(#1sAci!C4In0;?s25mGD?F`GMMkaGzjIF255YNukSq;I)la@5-m-U2q$J{7x1Mx?E2|KqpT#2c0r==P|ICclB>kYJGH+r5IRIl z7+e(IIYk3u=ae3RuwzOl5Zvgu)KU-57~ISSG6aVd`jTGA6AqZ2Wck7Z5*T9SN)4Dn zmv!nFrX)`Yp6oiBU2>N~1Fo8g|4I3>D>iu``L2>D*gwSxj_#6Oz;4$njBUJkUuLAK zXjt5Vz>1`J5wJ)W6cW=bfN2f)Gh>wq^BS3T>GWBH3LfdtsKK9E15?Y+K@CLK(HA6q zTLpw>sSG4P$$)~eNaxj*&iP!fmf=Yw>LZy)9JTa)dz8y5tyys)DpiPx#Z!?~4;$q9 zn1*1T<97WAFmYY&gP;Q73Zbom{`O4hW7uwk66QOtwMGG5Yp-O|m zhCKd{24MET@5^@^GWl+!&v%=8;=7x>@Lh_XA6>mU`bFm0BBm^il!tp0$p&>6}p!Fe_WBn%nxXh;F zczY8mRNy-Pb*y%0j&EreOOTH%8R6x3W|SuxF$Y<_z)yDkstEG*Q>#MUyM&0z)H<^L zWLuJ!hNPti7Mn##dSH_n24N-^UjpI6Y0E;~nM|(v)ewuD-G`J$E7s(&e^lq#x$uC* zor9rv=-FKO7*iA)jAk5%=X#4mgDhDW!^0QWQ16W*Rp3G!j=``xC$AUD*`}bLfh>!T za=+qqm=wV%*qUXXiGFhyl{Xk^_zt1d074zc%r-^2xfquEDE1oXu>?*=>}A0r6M?$#_e@{5g4)C!xD@c zg+^XfloX4+<=>n(*hL8kVMCXBL5UsV1@)9yIfUm!A*ekE!A4KCdNfDE@%u2a@IztVdjI|G;uD!aBqmSO}SirJD<} z_xbnsaO(mrUYw83;OE)-&_7YXvcT?dookP^eh0?~}l zuqR`m1xj;P%vRQK?7`Nr?PIN9*+*EvwDD}OeYiCdYeb&1Eo?oqkHMZJ`!sB)u-${K z)9s&NhlxEETTAR>>?yJL$7T|H8a9#GM`QO0u3ElM*$M0C_K&Tf*?VJWj(r%mnatqr&vX}m2+my-q-pC&S#V>dj@tf;ey7Ic#&v{vTbb1z%6}c z%ASOc7dWYL#UzEx+!rd`>|TmXJuMf6iYy#o(NT?oIU zrmD&LX?|GsruRD^_a2rJ_u;8(hPs#g1F(;wp9jU*MB~bccZ;Kb;T1b-Nc>ei8-YrU z8iJmTAv@%$X{s=~dE@eU{625YG_{Yq-#c-dny4P|uAZijS9RX&)6@Z~##2YAGu^Uh za6HtAUj?QPT`Q0;m2|eQUXtd`dOI8cijZb9q{Zk-wqZnUr|rRTMh2+Rrv2uV}K`LYy6s!YdUD zWXqap9Ekz-Rj>U>H7fUTSRsd*@x#60)76lEo0yRi9|dp7Dh71-_;T!YHF^+278?D~ zlVQXGO@m`Z1Yeg;SA&nd+OUi<>eyajVjPBKlR93Tj%)w9oV`Tk12090SlRxEpYj8u z>U=((t_Hd9yp~Qdsf7RDS&~*UpH?6LUCQcvOUSp|(}@C|tS0ssJRca4&$%48gp;aw zU7sR$fsM(O;k0>+XQ(5Ma@{fW^DtNeRc1f$orfX?^JYMs30f3# zN&<6V+tjK|JDgH!YJYyVNHPd~WPTwoXyhSfbRLfCVe(-Vi1QMXV&70~iBg7_|9WIE ztOyw#=MfyECb&X;f-%(!jSg9&hJi=Iqzxm#Y+?pqXTzZ0lno{z7RKH;L>}meF+Kj0 z(gS8+TvAYcwcC~O|>>Kj#I1cg`y8lzhsdJ)lmqK>2QrmWn zg?RPv=X-gV9eh&8Wy!Xk^)sMW68ES-D?hTo#4pK|Kqh_kZ)z#ir zbj|bXXR3MXO>f97@QbUx!)B@d5b~^9>L_^LGfNdA!st)U14nG;Gcc5PjM<##p~!eg ze3Jqrl!xV%WwOFX{86v;Q*)qt%p3cnxwm@U`yq`P-t~RVVsFEXW}bKY8GUNBM|;wL z%vpUN@4ZRcP_UXpcq_8;gIQ{r%6Z*XCaKWjZF+F;LGF_ZRF95PdC-c6tSBgEXR-a! z_>eA4N4DrdXCQ45x1#JlQP30ZV#en&rX}LJ2vu3#XwUH+iE?)nj6oca$|B&x%wYo12;*RkA*cjX z;V%9}HZ;Xi>AMsUOukm4ohbiYIZZeu$nO+<(w&(wOP1nv`0~~P#>UYWHd~hCc7>W$ z*J($N3OQm}RtSaN1Qv>j-MD#H7PmO{LtBGeF$L zlhZ*lGWJoHk>je%(SFS#Y&m=Ei~N2OUX+mS&HG?be{cI2`8j~VJ13a8dpCZWUj+N3 zJM)iHGrhfc=C4k`hT5&&0c$jh(L#+#3^!&c0uUa4!ow`5`iqF77LNo-K9OV6YllRi`YHpwQ)yw1l zI$FqSTkRb(TXm>Mz5VB?vB>t6IqKAIK^o?$QR*r0lR0Xodcj-tLolPGy|q77OZIZI zrzyv>vgdDDvNn+o?RWNZ8>gP2dMiL-_KE5m7;m1a-pYSYS#r*nWwG;|x@Wv+=BfgG zeSfZ66Hf_+(s^fYTy>IaS8A5${s@6)c{l$^ozbTW53-PR0P7)V^TS@=DQd8K)!Y9R zb)a=Pci9b1L^;fmnLAwrpL>cLW*+W5>s6eh_8B0-^BizR@Ti1QAS3m&-ZQ7Dq3T%g z@29B!hCKVlm2oRl;E1UTfM$#d1x{}KSZ}{m)&70OKa7PK_=mOsX{V|mj))+nae>pY zX1Va=gfTx6rZDP6jw?VTNKScYJ8f&c*G^TJ8lTsBbAPPH8PyLif3Yw*L~OkC$Erf9 zqr8bfRR{NO8Yy8qhsN(REn{>j zPlyW669<^^Ejy7u7IWaoNJ@kyxU>~QAP^SQ%WPS%Wq$W{?H5rrj zxH=Tk9ot!cGdM?Y=L#CbocC_JIv%g@7kk1mOwF*`z_7wHaUbM$TsmN|XI(ZR?1J}4 zWo)7FuSXdR1;f^gFy%M0+v(^L;nAT9s1io>gKab(2ZV?)Oh(hIk39SVUXS0&C2`)r zO3fD+K^SX}7F}pPr2yO+M3d;UFGXiN%TcVn#1rNVYq04UY}=%s4KwV?PDo@O>ZT?a zhkC&mEg;d@Ix--IoIdm_N^QYG8D}MM9*nd?a2*Q6fpiUSIHU3x0}OclE2a_EB%4~2 zh)`YP^eeG*fQk^t1?0gVgH_K^B8uyxqu#s$IRo7-%8ptQ45hm05f<2j2WW`Of@ek1 z%?lE_@n)5XY8@a_0S)!=o$Z9@BSjlC_7D=nFl1$Mqb+9G8>TgfE}yn7RC*Y2gfd_l zr(7ll$U+)fjOrq8GzNR#`~)B5!<-vQvaoixiBXv6^|MBWLQZHFvt_XuEE&kkg(Lo= zVuALREM`!Ej#51qMXKn8SqPhrwSK5e!d!I^#W*^$wu7vk5zHUTe=9P=v`A7?B7vf! zM!<0nVv0*cUB=#s6$6D>TS)LiCW?@nG!)0c6a<&q_=-_FBCRGePh^Z5l!%f9EG{?> zrVrQzisC0zn>rt@7WKybTn$o3c(Z=4j<75>pF0V#l5DA&1va%6(tE5B#c$;mKzKZu#-E7J1e$S#GPWN<%NoPe3SRn)77*-+gn!( zj?8%|e)_DAF4j}rb_v6~GeTO$ezxG2g-VZ)A)rC+{44KZgX^1M&p8#vV4 zxIn#*8S=98)T;2mpI;RB4n8+)iq~QFKHMAXsci3_^VJdV(|DyBugVt19LyY@wbfVB z^@Es`3)eT_KucugP1dgKo8{Z2m~%^m#F-d#Ub_1VhBySd}YzAhoh#$3tvwWq{+aLmE1$=UdVWHd&*R_G$(5nm=#jj0oo8LeIG z?p{dOs2KKvna&qF(UIEq`R$U*fiVYjU+49=CEf`A?dyNax5Aj?vexT#eGZ5@m=8Pe zy(hKaU%URLv)@k_=9_l;wy)0qq5n!U`}m1IBUxZ)J=6TGNxs?I_0KipvNUCG*7@y? zZ!UsVi+t8TvdZ%Pvp#3}B#D#4mB>aD$lLi^AQ+Qbu9*Ww! zrX{4$KwZEa@03Ce&~aMonSz_wUpsHxAQ|=3Nqkc!LHE-2ynBnd`fArZy14l|o~MZ( z*YQ5SLqcM69gB5~{?=Q6`pDRW^oG)Q_vw!@9rRjFyj<-Ql_({ zQWC{(B_{f&*ZU$hbl%-{7z!2m%+4{P7%|+^JRpAY|SpCdc>+9TZzF3XM3-j?e z`i)S(^3M6A8Zz`({+yEAIH<6&qF@7HMzGBGwmVrP5pc^N!C$V=@;>{c+FxDo-Fk@{ z4&O&FQTx;P;5Ym2LvzcttYPr}bXwLBZ`vQ!PmG$|z1#i(?R3-a%j5p@-%u-a=IW$* z<@jD5sZdZKOQ;oh1QBuRgm~4byuh5rt2N~%Q(7cnS{+103+Z6hDL-gEBxYmE3so1d z?I|xXdhx2b)BD4vY9FKeZtu2B)!ut;dq=a>h*N#{@;IiQ@%i41m#Y2x;D8AwP%v@` zT=4}PBbTWxWo&!f>-|Se2z;`iqse~Gx03xFA-lTA>-eKOz_=&RyX_JPi05ds!zpBk zDP%uKlYP_WYK^+a`RK2JuTk&ioMjEeMhK3_XD?NI!*}UYwN~Be zO}JLgS2H(O@k}n#eD6AS(EfGLtc*jQ0!j9C+W_ByT&Pw zWr#P=i(q8}v$g|nP{l#e%1@T3f>wOuU2=n(rC#&?as#HauX)CD5e!ai%A%O3J9)O@T+yvcjm$iV4s`wHC2>J?FqznTNGE0w@ZDMcCBq@Gg1G?g(w)9F@|IR4ik11hQ4{30*5ix|MQB^%^K@j zXJqZ=4fj;k8+wx(<^GK{Gl&=psjq0+28hXq>q{-$05Fu0Hn7Z4PwJ32T_?f}07DtM z^Er`;02sz=(y0J2{MOzg(jox!kWK+$p??iiaE&~mg~>m^3T1yC4gu@<)g6kfZEIKG_u>T{9;K*Ef662AsP!Z=&Cgb0wZ!tVREA}|6a z4EnUr%oj9w1cD~Ny+yoCIWP=FFgY%)=B%&{5HWOzC_C8CkU(+rCjFLEZ_Gu zopQ4z{yV)C!gYsCe{X1&n!S&oO^Z>&inEKfVbEu`j=x}bDO3JUo*%H3NqQZ3sv+6_ z_jA0*t5tzFb&Wa-1KrhY)D)xYVQ<4-YNUF^+p$KC!_tjZt`byR;r-!ubtVJ6bGrnH z-GOENRN8nY{IXOH=rFlK0o9V)I8WUmBGIK7qjTbrOXO>d;c)YS>7W))6GLE}!Wo2A zh@ei*$*XK^|VDEpe3Z8Ng<3wDmCb|2Yy z#wfVOL6)NWps0SFQrbYrWKsP%z51ZYI-kW#C_Kb3ZICD>ZJj7-zE2vRvBlWd02V4> zwI+>>beLQ&3CM7(UNe@a9pGJZr#f-pR2%eI{j_%XBcJ2-{gXOQtz_e#pjLUuY*3TE zkN+g=cSF`@prCAR3I&aVg4~)66x>{sLIJ1=9a~!Q>A-0NY|}ISb|K(OCot`92LkbtY({UmG-t+6!kRg7N z{O0N{7~to{YSmphGvk*j_{`SUpEX2!_W%*Uq(K}aU>9+My8Fcs;$WK9Y1m=UyG!-^ z{*bv(Zy-p6%#7}k$)Jotrd!p5V)_Z5 z+(3-|D6I<38&bIH5L_X^L~rRms-P=lOGp1sjDMJiUFto1k2?LJ?zO<4QIJ{scTieC z&N<#G_hJ=nrB`vUfc|gysvmaEDxHV=8(Mnep-r{mq5gNwOKW$}jBnKWEhgyD#w9e+7|uQ(&OK z8eaAt9mG58lLvX>hoCUJ^%)V)AU>Ipc3Q6$X$RsH^=Hel0R|G1ucEsF z=z$B;(^p#dgXDzGRSvZt@Q38Y6w1)_a6x*a>+6TaC89N?M2gl1rj(Mp=q>v~86-kA z#UieAL8hsF2l~bv#;@@%ei#a&N#2Hs)%gcjEtN^+O*9%q&X#NREV4g^G>eNwSQO#n zPW%n;=tr=ix!n8JBUnCs)7$Wf`Z^o=_40Hr>g!;%zvuCdGapkQEAQYZR714$ zK`JTJcD~&F=U`bgaR-hBxwxId8H%W3tf4rNIxc{U1xX!KkO;*$cxU}Zja3`GRew?c z8gQd>CPKu+*6p*QGIJVCN}|8;ZhKNCjJK5c)&0W;dT%_b`WsiOjU7*7eHvPYVNa>O z2hT(D6(YBam~9Y<_o#E1LIkd;VbX+kNeHR<#e&*S)OC(iD(6Gv*Mqua)VYa% z>4{kV{6sJmo{fE;Fn(&TJ9r7c}u?pVJ7v&j~@dN9+;!*b(8{Wz0SdAY!)fMzOn= z*+=b>hCk^>FfaQV)o1u1Yadar>hdK(0MI0Cnr0Yf-p4!S88z<2K|$$Rk^IziN= zmEvD>7FI7&KDpPMwM>Nhbni&sg96fI<7Vh#pc;{$?>+Pk@PdZ%TAxuP68Yv-ERjO@ ziA@`91+0>Wt$fr1b{sayx5C~5&#D7Vq~QJVS+!qbzFHny zmS@pN4(J7qC<43@joFhCbC?et^i$kdh=Mq!(5kiLm2PB7l?5OUhpeGqEEF#^8Y2nr zqBO0Ga3>%78CP#{Ovy~%e}Ka#raqJ%iGQJhLe4%ULYB)+NIJm3Y7Vxoz^tAFWd?SN zV*`L8drW{aE);`hXD6`D9@R!!aGD+-*e5`R{V2cG++&59i7dT?Jc|-~2 zTD2G%wfh-t^D}H?Mjh!HwWVj&c~S!RV6be-jB+`+aW*N*O5L#!K1)+WuIF&vIPpLl zpx#0gHg`+1g;|*9q~l^f3Sc@*aq=Nak-tG6udGR>NG7LA9T26*H<$&!{RrqxzE* zJX2>{aUqN3vjTfaeEKJ8RO{!lTCRq#;Anw7meNF9mlMGa>3Nhh30@G-Hf}sQgY?LQ zrMIAZ#})%LK!YR>Kf(|xV$)73B4YaM8k|Ss@a4tGKzdv{7eh~n;!z)umyOJ&;1wFi zZ$}e30DU-MT7qoop1`A2I2;C-1MUmvI|h~>VFU9v!gJ7_D zTM+}n0HEfRVMk@3>(F=`Rx?l_2xI-7+bF1cFIv6#@g{6h2mG?XvkJ1e9YgJ$h{J06 zV~9>7>!?Jy0MVMbgPzJnY~-?I%&AF+8g#{Ej6~eQHOPB0V{>+|JZMeB&O2P)#?|}Y z6I;|lZnsMAvW{2*gd((i9s6Jt3N(*+%7p9leQ}~jT(WlGmxpH}_$CYTeQ{m}YRIr7 zOX__o+hQXDo`vSpcwY*)CS2Te0T%>Z*{9H#gCZlz&V;@cZgb$04F-L|47EVqmq^HQ z4qMQ#Uyeu!r#oaYvUsC`c7?bV3tRyyG<>ICq zcuXK$<9@4L45FHhM0tX{Wb};8L2BZ!p7uD?& zkPJ|mtA7X1rPf|nJP5=A9KFrOjTngwlXo|OgJD=Q$(kJYSs3I))`h&f0bRFZPGT@e zMBE%c$G#9>{jtlsP;S;p)|B-`>SRl)ng&WaW}+#AD!X0l?9Zb@sj6$nnAB;d$8KQc zbUFl)19I#pve3unpx_fIh#k-=BQ^ywU=*bv95Ba14Bc#Mj>l|z%FgLc4&H@ArV)ar zO$?D1=eLu9GTlhevLy$gCHc;zPz|Bb)P4Pi`mXKNcaPes!R^veEMJeL4$$NmG$2+` zUfmRa`W)cg~6=D@=cIBP1RrOC;5MOjh+!rU!5Wtc?>azlHh5p`+Th&w-`|ka4 zLv^@8nr&zX;$+$VQ)~mgu0L6a=1TGntfP|4L4C<}$vR|5k#>RA%C;XefF4>=Ib~Av{L<eI8}WFyk6E`N7C!1s6Aac7q)wdcd@7$`t#dk zQPGY-anT=w@|To_r&|Ts7!5TGl_Y^Ld5DnXH*czu2c;LULy89y4#(xY6!U%Q7snQi z`H*%o)qmnmRqXCh$*^F$y+57{T9BB56U&D4jFblx9TcT8Y7Y^;#MUTKb=U&TxF^m) zm{%lBL4vbo2r?6A%Dv8+Br47IH5F4W218+y;Gd(^R;Xim*P`i&2I{g_bs%&OVQ*cl zvQ8OIg^cq=S{WL6M~`scLOd`RtsWwkwknlE#hC~azzjh zdcC-3zoiNy1qCsCrsuq+2D$~)V|KA+Ik=A>__dPyxtyN$b2-${RVeD`a2f82q2JWM?&0s2e|nVqBh919afMXt!lB0$%om(M{j?8F%! zXR;)f1BC*4GjgLb8DDXNzg*G8gcyC&smJNYNpR!3QZNc^w#O~{I0~PT17z5o&n{7q zl(A6kf5ll2tP>$Jj;A`4B`gY$S$bl)uO7+bG=DgLFpDSq=qaYR^lhwWroKpuAWtLa zTZ7}!R+`grnLgw^>O4fWN2=n__D3J#C6V$OI>f-gNugmg{V`DkuR^a6)GA0Eb5{p9 z*SY?M(>bWgzCj{8x+S7#xKPyKz#Kq>$XJ5oTo@V>WnXAag}OTH0;i?3v_We*QC42& zo`rW_p(D$oaQ_vkZe2(vF05oFrc>#XLsH}KpSdzA z0M*}$#=k+s(At?sqo93~?cjW_)v>%^BE5-ntRFCe99-cd#voUl32hW=qrVu?$_vGc zv}c@#2@NA(b;wK;rFl~dCS`2(t)q3kVcM9ajea^6#8@u?ax;>Wiml9&#}&F{!OoaC zC|=68G@LfGBr(Q!Xw5pVQ!$7iqzv@zBg#B11q6lFBod%zvUEs6K z@Vj;cdQOhtdu_5i_EC5yuwjK~nAxxZGV>B2U){Kn>lmdu{-GR^$2uV;Q2$wm(78ffSHIC*)6!HvVN+;XL?y>QA|2jr5172#?Cwpbh7xdH8TFP=Kd)#^fB zp#4eBh&4(Kw3oOhYXgM1E-SEJpS0qk$q0LoXuaboHC%HGjaGWLrF(MKB#6G;w-(a} z;%^Sm%`ak=TTx|8ODK)-#fC7vAcT9wP4Bb!RH2IlWm}2t?O5l-HWXMfuMSn@OfqZ5 z?$qW$Y;IwYHi@qFg1Fu#(4@8JBqCtCJrReS&2ckbjt%4CTi==J^R8XzG$C-o^?;mBTU?8kGnF&0;>{)*^*uig31Jf`;3g`lfm_ z4*YH(glOQ>?KI61#-_e_YGe@e?~vdv`d8$!cB~rSBIW2DBz4Q;1B_h?BdxCs19j~n zaAFpvbTE$q0?he98vg!Uj`RPq_`8x?+2F3Jxk*_^gGn~$uO3|Dpk^GlSHLQp4ozo~ zC^;*sb6JM{kWg;GO2&=R2)zp33&+(Vw=Si3#NquJIDiQ?s7!TB2Mg(|P`adQx14Fa z@i(PpHM(TkSXDg~{A_o%g@zv&_|!8S>Wz^(#|_E5u1nsvl6UoB-TYO9asLUrYFZg; zT@=Fb%g{JD!LyQRKQuxwKw$y^v8(drC81DgG$0-Uv3YQ!x5OKTYh@&rL=1apdLtbO zU6NiHCE&wRTJ&fhjLV=EhG7yEvI$9y{r0-8w(b!{c)$X$^`d4_)bh3z^0(`fVV=S#Y%mrX69#m^uJEXy@ZH zpaDFrmP^;Mk&H7Dh=%uzJJ6gNrBwZg`79x?t8g zs0a*j39h%rdzo9v6fjubc0nq`;XAL21Ff{yk!#OwgVD_} zClvn($B}-NS@yV*1i`MH(CLuoA1(~z9WsnQJ_vVwpN7|%>_EeF88;6^#JLK&rBRRS zhF(tWWTr0AAHv_3^jvr9jwnb*OLD6P;P%}rrVG(I*yiO|*nsc|xAEZMX#CVf@8nt{ zVB(^KO6uP?v)(=*U>@6Fq@oHPJu{EpW#OWJ-`bth^U$mUPrmMX9=ppTIT^N+dF1K! z`pmILrpgsE%fng}t6IGURRz3*VvyqqDK!Jy(`MuNmz*m}5#*Ft>5z=^w8rMBv7(^F zS${LKO-{Df+lk)DM>_lWZA843OGzro8cH0SY{aQWo@qmROCgJ}Dd7)a&iYren@JDR zZH2Os*PDaZcP@+DeWZg9FDwdn2wjEbm0+TU;8EZQC`4MBDzHF%&{@j!EYS=u=$jJE!GWy~_+$Bv z$p|oYzOxB#l#@e5jH7)nXN}^(Fh5Q^VwUBsH>F-l9Z0TANrYPff^C2Cv5Mhnkn=3>>f4VD?pTB0UV##%cvO z6d6dMO0-H40`MLW5@7qf?Mwk%bai8kUj-AdG?QLVRbVAeu=zXJX)`cY)LAtokVwe-MQt~a?Aum9Orvkqd6It89H=7d^$pzkVmj>-Pkq4j{84_m>2&{ zS*n*e_A}f~0EyUeuyYob0FyAB_1Zd?`>F7UUwGEZ#5p{58W89+kQ(vgh91Mg&Rj-o2EL-DEieY{)$sRnRHzI`%M&@=LOSkd`HHIwlotJs~!$pAzL zgLF!CJtJ>NKHS?PVm#&yr9VhSWSRr@%Vg9ET)J~I6ml4`evmPI)Ha}JLYIoC9w|Gy z%Va`WH#Lz(%}SwBI~9B#T2KZ{8+F?Gz5w=G;aMRNfC$C3bE2_hCQNjV4w{%|Z+0Ll z!|rtI@NL@AW$u8(AH_>&B_h2H2s(T_Ua%l@$DK1dmqXEKLW+;sI4;U|&yCKm z#VpZz4VFOyweWF@9^H)wP;~2kAce-#S@uDsS@|qnN(%K#D&a9YVO6z9>AE7C1qnN~ zNC<3_BQ}j1lp0-emOX}M*mE0X5*L3a$=1-nl3#f(@19g?crxpRb>MmS zfmmIzMlP^NBJu5!5_?~-|9^0Y>v$yVucArbq-SCR)XI{7;*j{W05O!AbNs%5wd;v| z@*MANSrf=QS$Iqrv#s&o!vCm!+*h7uJB$G@DHgW!iSI6(rQNTIo4f=XKh1gmp}pA7 zfnNvxh9;k7M=S6$ZSJ9}3#Wvka(ZT=1|+TMnelY1h(qhcP;I02IPTWW!d{r85VcGm zn1><!}g^#tHbtXxJ)zj zeiYZuV(Rr)6!+u8d^_smp+bCEoMj(Fk2j)!OM=i_`qqwvd z=EqUoU<-456gS(#{3MF|Zke%R|B>$XQCyV^^MNRC(gh15b&NEvzEi1v2E|fJ`5JFb zCzg*&fh#1%S8XbMs|97-(d>~s!}iHc_8(DPqYJYmiVJsPeip^0yvTk>*uI?Zol)G| z3-iBGT=NUFGiv{o=I2pd01WessQojVUq$Vo)BG}OpHA~r>@}W;^0uR)P+st9E(V|j zIGx%jXg7E@dL5m(T(phNhVo98S9rto;lZ4dXW|zqO>HDqoOXKt5}pli%9k0O4zb!VP)!6S zLZ_v7J?w3>U^807_7yCOu`O&b63h0mjioDq;BQg;S1ikWQTx|4--z13K^FgvI&Vc` z{!4&YK+JQp26RR3#n33gRvoo@6~pMznk;)h&D*o=v-x3Z)W!h~SjwU{7TNIS)-3xR zWTtM)vVTkWRay4$XyQn~xipt&*$Zgi7q!o$d0m!$KFt+b_60OoX4$_7Rkjm97WyM+ zwC!|^lQGHJM*A#nZ=+qrQs~8L7ikrTm1*Bkcjji=MLfmeM*CPDw~=-XaBTd|wD-~W z7E(T9?F*5C60C`9%&`BKo_*knbt`((uYrCXQm}b32_x(GvXt_VLU=|;ar&xKBw4eA;zqV`qNez#>|t2w4&d;sh)UD_tv zr)YaUu;ue}Ys5sO^%$i2$4SOknvgG>X&#Eb(=ahEqx;nWNYgCzJ({RhErB%;;#E=T zy5nIzkye-Q|C!ba;*GZjP3wnZy@u8~)_9%+DGFUp*K9CIaa~2%@z^piu5!9&7KN^) zd6q<8LhG4gT}(6O*Lknrw@6VUgdYXjzh9vx{Sih$ACt}6Bg{Jiz zvEn^M)B3eozohj*v3^18abm^mil%j}Sn)ceDW@iiLjR?eM>mQ>|Do08Zv3LqzhS3z zu!Da4h}XYprI20}`X{Z_$`yq^qm{w}o>7C9vzqOs{6gG}y^WS1%eO_W$uY&Mn&t%p ziaTgMU%uW>t8JZko}*kOw1!T+-&jQx_iMoGHd=9%0M=V+Jy)!^(ArC^t7)Aq)|+Yl zomg+8m75BRLO0S1?GLP#v=)igqqTogXcf&uydvq+vcG)8Gm@q?NUV5C5_@*UikBo! z>jz@RW0Iyk7%q=Vn${uWdp&(G2d_fyod(V%^%^WXk@eeRbr(NRC{{Hze^sphM6(o+ zs+ArKi|bA@J4v=vC9Qk-@f5Lcp!G*$T@Nb>UBwt>h+0X@B3LSDxdN7QS}p^|XX}GOFG|j^{}2 zYe@*b@_H|=hl3DIw;YJuM9Y=1Y^Ei@ICLLFoGtO{Xq_+C2WY(t!I~Hh1!|myd)&qD zVsJs}YRRE41cd3i1R+~#Sq#foSe$x34+_alzm>KPwlQkc@&0Ow79#glOq9hA%)I>T zfyaXoIB7Y;oHGe(jHrY2T%_?wzGEXt-K_O}#jY$s^-HuQP=}<>uxe;D`1cCP^ z?35YCcA3aZ0DsQ0O>|){n-I|@5MC)HcbB|SkgWzNiF=!!N?dx2Nea{hk<@O5DDTY0 zVuT)nB$aaU(4&te5|QLed3`Y?P}UkN^ciWm>$@gW-E$(VnFw#+HF&HL^0ux^qxjUD zLBTl(K$zN;4{2IsPBQA5!gl%E8l>Pxoo(W^H5I{W6`!V*50lu8B(_2ejw+#jqEs!R z(@4Fil=ugjq|#_5To9cM)|v_ig;JMr$69U>a7QZO_HF^sN+q~d1^ME@N0$UIONE1i zDp|hO-NNBC3Z$62sO{p8CF{uP)^sp&@YpgsG2exC5v{2APS#tbrxjsVoTpt{i7(W% ziT-94<^QPV;8|v(6Y3Lds)phb+}C!zH4V4uBE`i_5wY4B+rU&2r={F#UI>n|T#4;M z2(v_T9UWSQGca&o={w8y*K*%^wRYn97KwX}c3$l}mulxVzVlk`T#C*$I`p3?v=UM4 zA}h9`4E)B#wcvhzul0j_I_FC1i%k*eD5Dl0hd^OGiHTaHPk0N$Mn555LcUe-n=hZ zH)!E~p%l<0j|kmv^n>5zo2$eu4z2K`cd##XhLiD`;(D@?x)f+OrL3D%)~2N5k+-+I z2bVZoQ+mg!11%afo~uRUiBXj7wx83?b7Fjam5c@#*~U0Eu9%HuB~$eux+^$LA*Rp~ zCucTQ(_qq(`1=Ycc!HboWnXP1{-`i*Y@UrnPlR<;IjI3Z2>U0;ACr$u=g3DB0Gk6n zPP7nL+(wWpR~-0$P#;VknA1_%0Tyxcai)bM0@cW|_yEND1E40% z=BM>@oR9?%sAQ~&dx5O%W0g0@^NeCM)yKr=R;O}U8A|C#v+TqF)wn+K={gD`(4y#nrb_j)A_FzWCOM>NDLi`XoK>4DI9Ws|P*4u6&m} z2~_DLgJ9A<)Pn97I*!>KoBLoLO{?nraO;Z*$>NRGGRpTN{`5hCsY_>(4P;1$(OWck zn!P01!V30%GX047(RPH@evTL>%WctX@bIGad92>;S?Bh#1!W z$nz{|T*Jwc9&9-Ivk`c=Oh|Ja;{wdBWn0MryeCig0xrV-Lq=j}p^bX!RfFAtHzOq% zMcZXksLBBB$c-<+jvFEp`Vy`T0c~KP8ke|M9M^#-Fx|rHz#!b(Fo@7Y#Q{@35H=8; zN;5;N&x~F?lAV1a^cKCC2pKP4Bkdsp$yoA~gwpFsMvGyV1RWr}ED^VIk_F6X0S%_@ z7Y}FNS)P1d9_&q$z5+gs(Um31a&hLBZ;MGV4z{>zh&PKzs12D9Vq==hyfV0EI$g-S z(L;nT*sn>#XcpeRV&v$c3^+O$`ZZ--_7su^2S1#+Pa34d?!1c=Dh33vdLJHw2Jq$TeAZ(1V3V?O9kXRZFcF_;f=tvyxFA9( zZW??C624Lm0Hh?5XAQ*V2eR@A0gCJM*ySyQMEClW*MPJbGq_O0UE?XxW%)OBpf*HY zW@P9MtwD$_*-B=TN;>uWqgZkIo@d3O)+n+1C(AX^FPEXqHBg__8EC~V?89V_kad22 zK?o4#emqY+4?#^Z9yZpj z;@!dR3KSrcnVCPL%M~ox50>q;Fp!xM=-`Y{sR~dQ4l1Bh6^MEl#Ep^ew-yn(MdKeP zOB0M@RRZmwN zTuuvq$RzOW-t*xCiP=0G0xDD>7<-*?Tqny>BC*S=5>Lsq^d$8}wi#3vMu~SzKID#X zCjuB_y=g;UL9|UW}rjuV3YDM{p)sU#CTXcb2P~i9{3Sa>v z1^~&Hd6QG@XFAe$R=V^xpFSN-Vs8wF09*n)F4`r@qR=*-SUYQP>{0E##eI#!{BCvh z9_woiaCb7<1BybONY;t$uA#G?(se)`DK7Ny)YWlj*-~`~CHl^&|3}-Kz}Zz)`~GLw zKHYtKN_LXY*a_L^BtQZQ34_c?b{Jwr1ZQ!$7raid8xgoXpNe{d1dJLMzudUO1O?@@`>da=y%9FIsh3Vm2 zS?fn25V)S-cZcvc!2w>iEhbFH;sVe%7cBE7TAl>8(1`HwgnwtJZn!qF4p?dWlX32# zo>H;_fTO2#YVXFjOv^p)uwz@3Wy;6)S^iKz%i}Qf_)5^5kbi=sd7?k$m4;7PrsDDT zEd6qi>#eRliO$Z@To34zrvGIpC5IW;IDE18%`px_TOEqw4H6n!&>DC#hBLr(715t@ z2=nEktYo)NCICXOfqx{D*zI_g5i1JZn3!Gi-Ru&@ltw${p6@I7_`^w9kzq6F%mNu_ zuUP-F@i^+*{#RV&)Q#0wpE=P5!TZy*Cb^R)UT}kRaka&gTHV6YLjmV=6r$_XA5U`o zzvhq3h-5o^xa_RFpK4*-Zy^h0Y-6{x%EO^u*xfttt>y04$+6)8dC+Co-^ee!?@pfV z%Ky59GU5jAHXbHE>(z}H`GVoVph^~yX}G4jCRc1!AuF%3=yDtHf`sSDpTUbrJS^RJ z&~J|klEGVW`V`YGlikGXnp@NkGMj+?%^UQgB^U9Q@2L=F{T;6?3?tx#=Zo0z0{sQa zkes8|Va29VPd`g9a*?j)1>2VA1-Ex)9uMp55J9Ne5g|<FLX2f})YI zj&-408!fo+3q@^>@wjB+x9RTDrn|p3-F>Dpdbsp`sPDwA)Vbj6~) zxLklEnp;M;y&4lQNx0){K+c1hddm{)J~YFR<5gb}&cU%T9DCS|u~UvzRiXGhFNb)^ z8PYv{(K^Z!3ZpZQ_}vC)%)VJ0Xf*`01U&u-uwq6tt8IL1-%2Jkq(GS=L6^uMX2{&a zBs+L{iOARBf^ctfc-FLiE=Lm$OI5eTQqfIv-Af*&JEpoR)g?A0$&zB9_MkC3$?E#$ zhWh2BwM&MSanPnkfVSprR0so&ZqCLxA|p$~&4n#}5pyc1s3#gmxk}(f!!6+x4R=Dz zMq;FrO`cQ)x}V{^*sK@0yPJR@Dxjf0u>o~D+=aY;rjpX7J?;Ixsp6AOcSB8gPc_~B zqUr9J4R;lcb;9)kll5$dtT$R;?*OYVdR0~SsQjHLZ$d}BKU9ONVdYnWjJ6>%FS4C` zyt}?g_Ac+q!;LVwqj9XGPGE9%2 z?)H2YTL;^u23w*nDlOfXI{i)$jl!-Xvl=5Ma67N%tZqO%urQbDF(rzt3EA(!0vZL{q2%5&C z?0~Kp)z=8MEhd*Wkh|JY>}_;Q`nT9kYGQd!Xjs|QluItzJw06P7Xx-f4o$7H1QvmT zyq}j=vzg|*eObdDdfVrB|H;p_nBx2jJ~_9rtpP)G3%_c*JO0-Vw@OQ1hfXob+@Usd z3Xd0v#Orza0AWiP*=cJ4g=j~53U0D@>{!8kh|8>x>q3eN0masR{sJnRO{6yoA;n8s z-9%a$+U#Hk5+oMRtA(ua4{KNQ`>Ye zv7`?sQS^CzP+Cbclc^L(dvbf-_NTtyc5q=p5-Ov`?m)V+{DDuWYGEpwGT2i zPi8CI8vkH!;bCL}eB=fzy3DH|yp$NsK85bXee`W+%e-K@z87x8uucpa7FK$Njh9WGpmJaIOaHm9ThxomXy$l{(lchF$x`cJw9e$uTP{UV zz?5(VaEbd@UQ$o5Dc zxJ5?gNazQKPDS`jj}}sYB+Qi$@meM>lO=ISc&0qeLZ@a*LKSI6o2U(v;Pm*IYx)lsbUN$uo^vV@(t67IwGD;K_jSJS zYv1gzEnc;wmrUI?%-5(adfxg4#}|T5Hn$1`dZ^_q9_Ri;t9=_8HphT5VlvjERtL#f zEcfmjU8`S@q8hSc8V%9k^R)_FJX#L%%IZ=x%OkeQ9S~v1ZLG2eV;GQ3pPnbHAVvCVrh^~ zga^rH)VFzsubn2?Udj{of4dVO<>Q^Bf~C z-Z?m|I-5re_RDZ2${mDSRI^hRk<*IZ#jqryUK;Ae}BSIsQci=aPxRwn}4WtCxu-RM=NMe5^|1-V<; z_<%R6zxH5ZpQ>VOc6esV4W0uQ7UN8FPd2Tu z$?O-zYVevFRZ~IIX2|3r7u&5F7%IKtx-YtcV&oL-HbFq&h}Y|`pkLu))6I6gF8EvH zqq!d9y1*AFNlGvP-3kG&RJ_kf26H*CM4KQ84V{y2MkD&cUQh3y<7T#Ja$v`XbjKVw z{%t!q2vQr97dAL=j3ch(9P$HLA9qnO=wQlucaLCa1l#&5?MS4sKe_{O=LHYQ^^?A9 zuA5w?V8(Gxrz1Uz285BVI|MQZ%z}QYj4nA6lV8gxPOB(~dYT7u^3hP_do<@A7t($* z>xNyEHqJ%cxGQI!_F3_MZu-T2-PoRy#yXv{pPMnQ;mwOR%?5#>mO9fd2M`X_ID;|) zYAk^=&r0;p3y@b#Y$RfkY?B8|>t}&NcZRx>y=A_ca2x76nCFvlnfP)nYg)lLJVGr% zD2z9pDC0wZ#Y5ZLjob0U(?gpEej@Y#=+9_KM={R)iAm1ICz&#BV5!nkZef_hc+^SK zE4&UumQN zltMy>-ZR>WDD}TC@JvIgqq_NJ)?wwjuZ1DB3j*W9t{NC(O@C?^8*XJXN6(wO#6!>y zj*E0%ykGASf9NDCT4&3^8@D#H=8IX`Y~;>WESKd#OTV92e^B~tbp#}p#U zt<p=N6?iOz|O zXM3u*i9yz`)~vhScfy9X6h;r>Z6CptOVc<8jCodAM_3w2U)=pr@Er@lc)1dEtu2`& zDxj$<^Kfoqmg0D(5QM>|sF?4O!Aq_Rbl5o%pnALe1~GHcX%8>Vq81Ew4n>Dd8}_1y zR-@Bw8i@}L1HP+;6pc&(+=IM@FU<6JG$7kCHrT+W_20}_M%s{~ylM$?nn20M1J7?g z+vWx`hgKT6^)?u^maJGZ^&+`Ot)zU(Y5A8@-dfTfbKQgnso5YtWY7teA+^S~2m5N~ zv*spe^FuP{(gzQ4dsJ(mImoN=_50dapVC)Ba1U(0T$VK0u+o()x}fRu3_odfJ3zbO zXLeY6BlJE<_)%hoCIm#fv)SVZ86d#0N2`Kkk5|}2UqY6C7rx~P(_S{D4qJ!KX2kpRL&KdLFMYE|zY!{ghq20Oy zaU~`dlSJY|TWOg*(F^Gn2f0}Udx}@r+#6XtI&p@_@I&TXDf$xxCc!K8tJ_0ZgmqJZ zk;Z1HxH$w~IG@EaQrxl;BTCe^Sq6V*&Rb3&Yo}82`KjMtWT`%ASe=1Ydl!RE{fd%Q zBVRPGE!LCW!|I6vs*v1~^{O#7IJ0i#hD$TFi!*SujH;oB+NW#IK>shMlQ@n|GH`&= zAxl>lTSAC-Tu|(LB}}pJj<1qO1Aw=NHjy)EwVD>?BjhowRS&PiZ6QHA4dI_eT&dv2 z*21>D1ze-dz}>ebj>3L;DlzTC^udGO=q?m2yrgop0oL||-9E?KXtMtn*09PV7`R}` z#Zz*qpDoafepiWZ25PV5Bh5;sZ#~4lJ~$@*#v$%43y%TjwJnf6iW-I@2BVzh(YK5Y zNJ=Jv`Ci0!1n{xcr!{NpMSA3+ZYJj+6y)X zjjKT`pr|w1JA}OM)m~iehadP_dcZb07?5s`>e`3vPvPN>&fJMZ zOmvCJ#!AvrGI#_F&J($xtr2UYIHnVBf=--v9qvlZ`&}9b$Lfd{$m7!DUW;w!{MaBa zDd7}r_&7{5czikyTXQ}=2G5E6h#3BvVj7GgBh=GZnVv0hebn1KpD@)qM92VUm1<`=eQyX}3rVo~I3-r$jUA7a4%0M+m`F8nRh;EfJ zif8;$nx`EST)NB9=`#o$)^2lMsI-Qa$PjJb>2L)U4P5#q_l%lFpc? z%nQKc%eFLomE{-GH?Wsgo~(f)iL9l;SJ0Ahsu&5T^Ux7)S`eoHeT17{9ZvqsyiY+~ zyJaN$U&=hnhDF*V&7{ro6CkX;(}4OJz{b+BUt@3YG$_ky#OA>AsX=WP(41&T z-T)iV=&p3bk&rrb>-b7*HVx#{<8|EHD|$i~>i>XHA{M}E2Hh*SE)0^GQtK|f8l8rZ zeWKh3Cb?LK+wnL^N4(gkNw^4$4YkTsxMP}VomA7L{W#tz^=%whvw6~3iSJb=`KjiRfq49?;81W^`45bW< zlC^kl$>o|LJ?l_69;Fsm;yJ1=3(EXr1U8JL4g_Wf@0O%i*GX!%NQ$nHL{b#1k<sO2(%_2e=z0=a15M@U6*Sb6#)<0*cBC+{snu43(X2X*$?}k@S_qv0i4dzn6ex*{ddiOi$uw12wnq31O z5>o?KhQh0Kv5;I$%k9aJm)mhpR+w5#?LzvZ6(hjt1-U5}5*GTjE|P1{Tgq4DyO-?TM8XMm6Czmvl;Ais3%ECdA;> zRtKC6n*T_YQUzo5N@Im)OkQbgXl71&`GnxD2Ln|?gp6B|Ax{P*=)=i>I8Af~vMl{I z!0NCuIapZd;*JYeh)k!%5ypjIzmZ2w%V!*Itl^O3tQC>wG$8C@q8#a4@i#lOadD=w zUT?ceSoHCEfTF)Jl>Q&P1m^`Cy8YpTHN{`UeYZ6vt-l<*SGoCEYkXLqR+_2h`G$>H zR2L3cAm$psAPZiUG=Dheh$?u1`QcR_nYG2>ZrMxyd%!#TShnB-ITwe~i=ljgi7nG1 zW70E^Ls7-%^xjBNBTpnf#Z-}@C{HBCH)6Ob3N19v%(ug7;AT3Qd%#SZ3>=)MbL2Ul zAN>{GeO~Y=1kfrqR}+{g-tTy1rh30^oefs%~c4&#DTfHMgWk9Pg&h z+DHij1JO7MS1N62d?X!>#f4kaCC9s-&MeI{BDa`+<#-q!JTLlNx^YDd-al#o4R#c^ z1$Y&~;8m~%K4Clg+UK$QrepOvpDveeBc`x%ZYGo4PQUR30!gx+Q?;=Rvo0l@RNGOz z^-e!%$ByEP`ubz@Rmn70?pK)`%7 zdr{s;*^S;_M9Eh_t5DWR9nI`T76IvTL-+FIJ5YpuLnHOGR#}}Jxw2W9;O7{vnC4cE zrfc-yj5^6-NNy?e-PYwjuc-DU4lqB7J%xx!k&W4hQJ3C15RL0#{xOsgM3^Smw2TYk zEa{flxOvk}(OQmxlj>HH7RC3_Ug>R%-L&+G#coDWO#gMU8~ImymX4E>U2Bg*xej@aLD+u<<=FnlZ z+%&F_?mRC_j)=}p0tNb||lqQ+L|tgvQ)ARzMA6vkXu7xJb@~uV2Eab^A7J{Y+)r%mhh@Uo^Sxo=a!?9E8$e zZoDT;+c#D4W&%wrY$2WYI+CpPNb@aeuRj^)=*|Z@D}0@r3TSxI0;8f)}>V-Ziwuyj3j{XuGqT36Y$0 z3k9xVfc60CQZ7TURwstdvkfpmlT+fX(ykH}bUQInyR-SF@OO2;JcB(mI&Z=5^s1;3 z3!P9=(NBb<(G`%wPSApz5J>_(*B3FNyOPN`qFtbcjo#5{5i-fTTGswht3Ukhn=8?U!v3Cd)sEFd2_9QIhJ6 zyGlk$?ljG77Nqe>R<*+iFb#TSG|)HtAREL6i-bV)j|9idL*-|>@R&oV$PxqZ!3Pk6?ve&{wr( zzq;TZhCc*=XM#xv*RB{Gj=;bp&?KiB)38*fy)0(I^|q&u3B$}x2G&&JPPwMUPPjFM z)V5 z$;>~Aac!Ojq%GDo@Cyg8Y!)z^cNxa!Q8Z{6x*9y6@flf-fdC_tC7-kJVCweez-5Dr z8qhT@$24CvOe@huS271C47-z*tBvCTV`i8}F^3b^#xQ;M*6HJ_$fq8jXBe)}b>XX~ zQXA6#zymL2EsWnJtF(b;zLP1yKI-y@|M;}mdz+D(;Z#zz`U>X;1NIM*YMOV`hcnHA zWfG&xI<9IssuDFKa@-w~^is4p6MUlTYP)lWvA8cDYjqWmFg2bGD}rN3%Zgb9!f}XJ zF`zHGF28*-Bh6%^f+I8%nn*ep0qes*s^xHlIE~>F!!#Vf3KY@UgpzDw6G8K)fzjV1 zg6=VujcMM*+NFqPcvmbfjqIMMzY@e7JUJ*A_}!S@Q~5>{DH|ncP-O!%HU1DALg~Dp z-dM3da76eJ$znfZ&-rIUn=mGXsrYK)Mjf+2FI!^sQvt_?f2A?UyPz8%ky1(*oIYuM z^|K~#x70#Uv@mCqqH7dFC^#W3JvJ?kil=)2 z8dt|li^;}(GD{|zdf1_8KQJ79 zJ}2RB5=~1RM(g62m^^8OVZE|C7d>Q4sXk&s3lZuLUy}f5um%Huz4w%)1GF+gWZ*QK z8RgZ}vGFwGQ!)s~L=S;$c0cezyma)An?f{=J~NP_ZQCluBJnKuW|@6NW1CM?sv#wP z=5`F8XpzPR5feqrt=P7%EV~O;syn(WrCxC^5lHr&2Q5I&cV`jhi^9l*($%&%o#me6 zyYUm*>I>m$vX2E%Hgg6c9<#9o>KC-HMCKUBt3(%BR*`J1B>=q+SIt9WOn2-=afD@C z4xB70L1})cu@HiOC8Z&1rXR7JY2XD5 zg!O2CIapWLVD%`kHUUeL2{4cY3zh{g4{P8yLaU4vSTP1|K>T^k4SsbY9AFiABy%R{ zClkdmU^U0uh=!pIA_qD$_DfM5!c5D>N>fm>;coX4hYj?jUHL4TvNqF5FJ{0Q zL$I2IStipY<4`m$el+&cRB|B#_8x$3LD_`N2#s=>+lYmA?PU+;te})-^dP)G~tc=cxLvn)|0O~mj zKua1%%5;bi83!iOgv!V-*F+mEV8Lq0xCGO}f#7*(jkn-tmd3Sv+7K@csjA+DF-MPe zS2>#=ta7}g#rHOhKS?Z1qp%otjeiT{ual*uC`v>}m(*BuQIDg`9o&;HjIu5m$7QvM zI5%8G_6S1>HV7A?b|z5?S!5?I&m>%ODVwQR-pQvhThBnVHR{(Hnlw1C@tv`WgSskO z7lE4hN}4!Kj=ZsO;Ohv{$V|3^Oh~U{^h8%UWkE5&Z&zLFvC!F(zTxexi9UFN)+P!J z@60}}8t5wSi#`)_{+(>m5@_R*PaMRJy#8Y(h%?nc(}GQ&cP8Y%lD&UYFOs|LYC{p4 z2ZNYDD+bt@6J|hkRm1-el|VplnPk>`ChQ*{Zo9*V7?zhQT@@%bQ=j50+{z)17vh4F zZwh~%yhHNW`RxwZBLRGLm0hv8!Y&WoB*zi%L@aunB20;qA_rTx9<3uY_=Hx&%0<%& zSm%-oRE#byzDsgt=pqHXF&XW7bXiVx1Q|69nfj=qxjxK7#kZ>wM8w+b1ed3aPhlJ5 z_lvB=Szyr!2qYPaur&e(JL4&OqYeyTn(IWPj{~xy`bD9greO;4-hVf2%ejx7U>!{6 z-Dou?y&XQc9B`|IOC}=d>Y`q>AI61tv7szBaG8Z7q7Pv$CtvylE?DiU;9XyIfi*GE zL50x_q}QKELp)lqgY_u~Rz8)KKFd!fWUKUAK2Fof+|lq**cY=4$V<^@LnN1@*A8XJ z&rx-MB_Bjgq6bMolP`;haKPx>Lfk3?GP*(6KD)qG0$%E>-xMQtRf`#Vk1=aYj4C0P zGf-XO6r8@s9&j;e9cM{V-{3}W^r!tM(e;1@VA!Ap0OD=JVJKCREkVR9;KN~6iu3_o z;2=&msA>ir6NS5I00_zb$_G`K&y%TSMHPf#ba30cj;yyfk{f+pRiX(g`_WBScT-*$ z#~}CnQ{{h5HYjnRAd>}8Vk5Gj08tWjNnh5ly+E%qaTAt``Mju^Q0RD_O9Kn~> zr6JCP;wBQ-yA`*M8z_O;EXets=r?qvK1d7t!PItISw}0g+yc3@X1QbK8uo{>kW-!^ zw8VQIw8)f`VqMiZ`798OlUHf|C~h-|K_Ln*YqkdVfFBobz|1U;B;wDJl`%d*m-XZ< zKvz|wc|J(eH}-T>W81mI<2s2UtI`6k%I#`9vH0PwwSE=tja?U!c@wO>AgnNdJUq>f zztq(DPTj#L86a#R+a$^lPdQTsKKh_F$F5V~T-ExQwWoSZRx(=b(}W^=APuv#%%waU z^BzwyZSgQ4EZcfH3@jTss-W3CL^J*a+w;L1R=CPJM>3(hVoGZkwnsbiqNw26Y9=#B z1aharei;iInFofY&iZ0(MuqLs53QK(l2jA8!YL*M4>0}oBV3GG7ZKDL5WntsMykUa z3vJ}w^ab4!k+V@3c?7Rj^KSO&Vf(~3n}-T)qUO*Ss44WAXEysvM+S`F?n24r`xu3i zXw;w}{tpQnL7 z?rCR(NRpKNBQ_EnXq3G=0EcUehyxnNH&$z(Ap%N2hSgxHkrO?E_Klp1MBM*M&JK*F zH^QQ5LrZ6PzqEAbF%;FXY=Os)34~fB8B#+xGWQ>dP)X6M2}E?p(aBo87OikA)b>Fs z0s&bt;Hc}6kJ5+);C#3i)-U{!#mzaAr~FRR*_g#>2;nIZimU?H)MQHRR*H9NgR+b< zg@LOg+H|HfBdjG-bGHkU8eB@;=hd6);_%EM3)7sZK zZM^74cU1^8OG0b|%50|?y*G2KkVLA;{#)geDvwS7<=@zM#kw2m{8WWlok~a}wzH8` zZ<{y{O%_+o+92>T`vHd^BWxFKt}!Pl-xN?327+54bl5nAuE&}~c@Dwvjn~G!$#|pXQU({D?GFC zBqNt~B{@T6`N4ZELoekBGl~6jvF*q<^J`cQ|IxTqMyY3%l%TgY{cu>EXxqo>{r@jL zYhG~UeSn1XR+uld&47J($z=F z1eHMRnD|$lg-`qiLk(e!>KcRbWMQ)!dtfqWWbD$R_md4y%VXk7A_Bi8ODFOSuSNk% z@T$pxTD03QCPgX z(q)b)#bkB*tkWH$X$5iO?HdzwC#fuZd@;9J?)1q|lf2sm3Z!3c=hy-)wajhe9SqPr zz4!Uy{$F*pp`WKo*uTP#%H!5C7e^r-#Zef=+{iD&cDTjdy=*sOrT=fc>7YN+UTr*i z&{iAAw>Au9Hi&)$Bdkori1o!t<{l(Eh_4H^k$D-r#auCCcNs#$R4(2BbPk@#qbaf= z0@F=Jff)yl=H%!k;rx9&fT)?E#)=bHxHaXtw(-30dsqGTO!>0*Ll#KF0f1KIO{$cq( zn4Mae$mS}{9RjlT39o-jTFP}-VW*Bc-k4EPw2xF3ra&^)o13*w^M1zWSZf?gu6Hk( z$d*Oq`Y_4F0?9weuSMHkqHP3|B?^c4%54lMmfyRPUsKGs9*FK?O8dk$rrlU$cT9WI zXilv{I>Vps5Y16(`E+(X@6!1lMB91Plk?t&pxo(yWSTZXL$ZD==jq;=SmwpVF5 z6;t;^C`Ygza-E#hFj?wzMT!6i|9GZ1-d3tg&`!r$6(%znn;^F56Ip8~t(e;cwJRd% z5c)WOS?EJi5dVwEn=!*pMi8XsfEdTetqh`Q#ytNyiv;yHBV|=fOced@MS5b%uO%nM zdHAMHeEVkoE3AtN0Iif|AG4`$a2rsK%zeW9c_}g#W!f47p`;V?UXE8Ujm>3!9PiUy zkGFQDaI5oY#UZk*m5s<*R_Cl#o1Xe)*q!N#{~1ntH?G5&$?vsG0!2n1l7D_gz;I1i`Mupb~xvA7jYKWKc2;hek_+V`k?lPe| z@(G0^ETKBSv1eVm(815vQ~|V3DgQA8e^^zQ6^heVCc$M3;Ir(VF_${j)ni_#v1(9{)t;)mR0p~ z)ytY?go5T4+0!+P3_j@vdTx>7hr=SX$ITWQ86BD|GUKoaXjmgA58XiNiXz zZ8!BNsCYu091WOlwyV-Q)>tw;))v(3wpkm*0M$)k%sA2Qc%1cPVUVCl0{t^i=M%{s z$?UT>%%refqHwc)#ym4swlkk%9dSuh3@Q=_$*^zcnUvg-%(Lbi{+lr;HIsp91pDlN z-ciY?#*V&{eoE1MXNMR{rzcW?7RAv`&9cUBTC_&9b(WRN>ePBTtb-G(BhixE^( z0{?&B`QT+bPZpDrN4ciQGs-5j>4{_|{gSY5?7cL6Kgj-MluXrWt}qe?n5OC3VR2k_ zv_*B8%A$pexCckrW1PMy_lfiF6Z~It_{ounyWAV0GTfMX-1J3%U@G8IHBX3fMP5p1 z%9%98Ec49U@(bIN2u?NcH{>sA{T}}U!KJ+fA)?FY!MXC>X-9{?JJglu26$^0Lnq#@ zfFSR=&(v}G-{YqA0P^+xgZQ~h6=3)n?Rlt-Csp~Y7+g6tbMUg@ttq=e>0@-e$-^Lh z4V>|d(on=}%h@Y_lMR$C^6kWaRPD z6(Dk?kJsYea*TKkVHkOiJ(~EDsyfVqs^&UDDUfC4Bh|3REWralO579CK~e=g`&V$` zXuHLLq7-O~^%C*Hoy8@}D@D{=GDndvAQ;410Imj^z)z27SxE6jAAXk&hXNG&%F2se z`iV0q_2x3k95fkz#1mlgyPAC1v-GUdrsD}y2g4E>(<;7?VDEu`T5|c%#(lHhoNPBd zDy7*=|_nu5D)1W4BF$CxWeH!gM87aY8$c zM^Q}DUpzm|@gc9T;R6SO7$k^+L1F~cjUIeg%5~tkFX0QFf{}>sul?wmS+bjE3<5N_ zb2w~N?KSpV;@kW{+P>@cBH1BU+Dm3LHEMjj>&oOL_S9X#~6I6Ii78kN_sOQYAgb zw_4DlJcJ4LmrL?#NS#XWJay{$s-Z+3lEFo2mfTlDiTBfv)EQ|8;6%zd5gxnZBot22 z!VM?1CKsOna`s{CV&*+SQlKV5g^gI}3mLiqY(#ruL~N-SCf>b4koN&hcxA>w#`EUQ z36HRO}95R7-Z!je<_trKZsCBkV}JLOt}+LIRc+o^LQb23a=L9L! zHbU3af67A8wsihk{EN~bdqW`8Jf&}w_EV^x7YxyNWfBNt#>3{1s%=%Iis`wtmD{a6 zgANK2AJk}Qv8|m+p4laCz=*Zu)n{Kknd!4iK5Q%vKkSVD1BS0On_gTg22s94A4K&Z>CxxAz5H1* ziD0)QW&F!z^Ld|*k{Qu_ljZbP%NQBD_s^eM+=!3zs67kR65Xb-I-fkNc3i!k7TYt= zSyb6B`FM`2?U>)?1HLg4hFJi~pVRS6-2uCSc;_&P8RFeEScmS*pKgY()u3ywL)SV2 za7RkL(ePVW>(G5&jRn#CDV@p2C96p%hlT|oM7og_%~n68FtuIarn~c9I_m;=RLM52 z!sgNsUEod%o=$JSz#X^`2SY;~16>vorO)i>?29;idj$45z?HjX5+bMIVg#}KpO7V2W3v<&)}hysgQ$k?f9Fc^SN z3X&3GIo!=GPqe{>E#J(oivw{rWH794P3iQQV~jb(g(F#6?H!#>3PMI%W%#lIMcRmp zo$i5LyI9S@1JnXi43)q(#Y9gi7ttCTVChL41C#(u;!=3>+>KMYZ;``|zA(Y$FYue| zh}oK3z(lT0&;fq#c*S131qanC@(D}8Y#&b|zus(t8Y~Dh8NXsMDgddN{`NDdW&ZI% zk^6C+#9c*;1tUH}$ZTC78f2VfLWXyR^_7&$e{KP;b)4LZInx4*1SBOyV1Po-_gBo1sy8(N_%yBzqkZ|>~wPOU4{ z;Nav{Yl~-Kas=wE?v6Z18F|w3$n|ztt;?!4q6Oa!#2?5{GoTbqxK_AaRg#8<2IR>$ z=(l+Xsw@Vs$Qh`sEsKpX=J&;t1CVB0k3XBgbedeo|0HPb9yDQ8Q{C6D>R>e)0pNzO zOFb=W(v!wOq$&{vDGVs~4gGqxelZ_b@hj?92m$zEwh?y+6t(D&5MEhMHt7`(Wo2TV$T^*$t0 z40cr%=U6~CO^KdRka+=PLYX`5bGeEi@c0#UEBo-6uCWq!YA}z3#wJp~kh};(i|61YpIPUpz6qRRr zb~ve0i92v+R%R~TRzQK>z^gdyOP+-A`3Px{KY2Btt>p4=Tp0DuD-@0>K$INKfXeia zm26BAKM3XhB@7kCN;WYZ-|xBRoxN$U-WktWSa=hI*iG&Qwea?-Qf;`VcL6eW=Jh zK{F$(Enko!nw_R250#N7ilO*;0*cpVpN+a}*fmX&F-2(0Q6p+iPr1bHDN|<2C2r0; zhch3!Yj)uzBQg@tL`KOOpK~*g*FhHK3@UBA6u+5IVwt(ji++)b~D&wI|7@;#6T_= zxR#MC3VtD%F4y3BF<0H%O;Cp!uG9o2vI`t%3B#~$u=GGC*3K<<&W3p!rE!{@yrEOM z_wE(+sjBct&AP7om^Ah6<%XoT)~4M98^fDV()B!a!L9Q#M~4kWxhp6VYQi z9|A2_$|fs4@t%i-v(RZjaiyDm93oz$;<;^Wa*)`yardW{np@stKlqsy>wSoZI&nqq z#$|aZ{oR$AGabwdgS+)snctrr&x+3hFyP1e#eqz*lS}DpQ5m)&%<6g%jH+l326T6!?HlL;Ki#nw? zQyASuYxK*EI=NVBE&~3tS$8F&9}%G+%3zVX#VJR2Jj)0wnzq9H?MsHLDjiT-A)r>e zR?Pb@q}Cu^b{RoCH?mKHpHH^rF<{TfTpN=>*jXm;RYEy9F@f+*GGX5UrD zYL57hb3vCy^_HtO?|K#^!DMg7K<l0-d>|87Ngl&-jV1Hcqg- z8_KVDHt#F^I;)(d&z#vZd3;Cm5@J{jyi2UF=1)oIpV>C4itMu;W*x~g|0-0Is2+%e z;d{(vCL1g+dOEWvHPXhR@=!)@)CI5IWyO)5pT=m&ww5*aaa2{QhL+XC7}dNjC+1sp zq+*b6M@!Bg9i>d$YF2HueMn)9w$zh{F|yFA&=95GK)afZb`Zrq$K!7siv!Th|Elz# zRtpg!CSECH<;djdbqXuxQZ??%wwa+#j8V|GnGFg;Wx-u5`YZq|jFDO1SC0HHGs@}w zPj9O!Kmli@$fLQ2Mf0<*xJ8K{lnjRt`?plar7gcZ!A>YT{iR#u=y-dq^h==P>w^TbhZsln%ix)l00LuJG3ke&a@dr zM34o!b%=W{(dHz%r11^7-0eqZXwDrrNd80DTb3NM!Aub zQBLP}7@C?HI6lI>!miwGcg(Cq>zd14`s`J$3cO0Y%DMZ4G(TIP|LlfKxukv=;y zEgZc?Ps=57k=$jciLF`MBy;(6wT1yWr;cRI zHkca4yeB`XPp--WZvzvl|U5>ehAaSx7C7RD0D;G2vcj$a@Vz=+K-$Ij3Hhou5)L`+3Uv3;aQg-8(rq`m(|exJg9Wsnl7ITW-*J2O z++jU@fo0tWc)$D|_r73fTD%e19!&SV(Y-NvApPi#ZdUL@`qdkmF*l{ZywM#uW~cb- z0&;6f-CWWZ{Utr@Cbt(=PQA&!fhsrLf@1@;0yF=*PaW}g&fG#}LpQ(O;NCNy z?$sBTYW+J^s{gw5yj$D{f``)I-s0wtqo5yyhR)8um15VYufNrODEMo7+pTU=Mu%ag zv!U2;ZgmIF`Kvn2v6lQD&|mWF=+9Jgd@*i=VWp#?#_`{E$M_l>t%eT#q%bFn&vHG! z#^$in)llQ6@4D-I=&+C3Zyc+S-o_a!e@$24mP5COQVlilxy>EhSW}bB6WH|I-GZs> zfSVC&(PBBy^%hRa1Y=C^xaFku+}nZup|pH$%eeG=x4Vg=mKr*1?fmw3cW|&ejlbuP z+6}(X{+?S5ugB9IyqO-1zhjceo=2cWq|WUJu;C_`aAv zd57C~%JVOf-S0q;vaJB$(D9iY-LbvD_yHfiuiy)8Z!U2DM)!`=AAij*r?Jtw>G!_p zUX@O{)9u%rJsJ_Tf4kGYdACqaNT0gXy&KXy`Yw0C)B&Zch3Qtb`r>!#guRx&G`QZ;$6$`NXGHB0`OyMnmmdBD*EL~9sZub-39E&jI56Ky1U01Z`hlBTy5?^dn{aWu zukq&MKeV3&_*$h)yk-oUu9nQN~-Rq|F^Ok$v{`@?AFX*}Q zUN@e~gZElF9+kP%o9;ET=Qyn``FSDoh0B9_ME>yPzh%da>IbQcm0$ug1yAaYe9iP7 z_qk5mf4_2Ku1G&~pF65+)mI^0_CXLLw>sT&pWBBQHaw&5$K|&ooqE5U>b8F~J^X(6 z(cqT!JNLT-f{p2~?|1VL{r(Cc0Gp4X4vaFKX{R0rg7t`(s z3AA}Iz2E`7>p#9cz5M|G5cj1X>QxUK z)Zcs%xwazx{eyJ$^0ezAcVuu!y7(bP$BOjKhuqP@m(#(A+=0PY(rphJE?Ry_&o4{o z{}42$vQc{^wIt}gF}-e! z+qc!9VV*v@g^77XI{IO^SN9Hd1jcG3WA(xl>B5KIYx7ceMH_=7&Y zZr^4}?r{r63)9y<;${awOF!}m9bA=u_YwDIcYaGc=1276iu8aVx%cb#${)Ea7_5UH zb;lgE6caAlFhg@;P=84xtUs@dF5m@0=`$)Ja}R<-c<_9~jOfCa^x8+=zP(m+v(?<_ zYo4qUZ>DDY?4xd?qhQBlu6N@3=IpFCvNhq)=HO^x`|hTwhD=yk%1VAe{)$_d$SCj`8VlN zPXM1U@It-n_FC2SEDB8U1#Yaj0M?@?(t#%+&~@q0o^Wppo=ErqPxrdu*XgJJlkmYi z(r^AJEPfz;9zqQG}wftuY`tPTMKXcE|TZOqy3Q?KQq!Yz=7(`dJl*hsULbNVBJ|I*Yz;V%) z$e>HpM}F>Rb>6`s;42F{lV{SFC*59Smk{qWJDjC@gw(-L-;*Bsr2F5kn>QgezniXq z(w)55qnahp5ojilmBrI1-`dRU$zItG#k`^qreFE3>utaHV#*?HKajp*$W4GE9{vUV z@Z2HSKK=4bcy&NSD}0iv#9!6VN_PH^lJ~0Q+~$&-hnL*r*RH*Hr%Fy}EQ!<5+FR|v z{I@?+YUQPr+M}^lN3B$O2jXQ=11ZL)nC*B+VV6Xx_^WZ zv6R%<#?;bw6>6vY_qGA`bGl>r(xC6yYi zL-`@5n1X^rdG{Hd>wgKFzN|uyhJN%*Y>sL=ahuztwB!n`FFKVTrEguRyCQd|>hANp zo31ZE?RuwQtGn*z?hn{ER@paXO7ngVUb8nwBbR*3zp2ruwx(qKn@9b=UNDZH8_5xM-yJm%DsIDx5DR`sjUWmjZH$H8f+i zarw0wAco?v>aGnygyPaQy3;2k7|54*DWzd= zC?(=3eOILf&8IZ>_v@}k2|w1|c-|Q$Jfpi^D50S}ql62tqZIwA6PqZ(moiHD@-78L z2@TB{C9GE|0b-PJx9)0`aKG+ql<=VLL6nm%vaFOn60IbYehj@?%VnKg$)z{M#p27ZF79=fXC%h$H z_l&Ddd;SkgC8Ats(#gY(KUk6ptS;`(PoI7U?fh@)tN-9;9rn$y@vevimFg|YlaHUT zJ3PIu+}-?y-O-Bfety5*A@)@A)9F|K;J#J5kE^6HWCDpAn%?|g-lNQnAa;uafT(pbz-RB}>dNzt0xTb+u<^ggcwO@vL_imZTr zP$H>!#Q>#dH8wM`NITzPQWB6wBSv zj0my!s;|<_evKc~oNg#3(bfB?N;QF`d0Q(rb^4!FU|wT0VKOuvl_ns*`87%!JwN9T zY`bL;H{!EjNRN2V?N_=+uMTD$&w8#wbV{l`l~;lWMtJ@1AKqcTgk?5{|^za0v8 zNlRDMnfz(%r**CTCCTS*|BS9%Ie#m8?1w0KD%0JWth_U;+try23}t25X8wy8?7D?* z-^m?!+jTojG|6{GCVH>z2;sg`e67+BN;2{kc8w z(B!-3J_Dkp3H;d~7|8JjTVfWqizCXL0)4%xB* zhn?8uYtvDG!B}0F?)w+_PAsg?{KYNg?!Ldcnf(0mFYW`uhV^fI(TxtKeRIhYg3Yi8 zfl^+y&{ zqBZ9sRQS7eQ8*%aJNAx??oJPT$tBoL=f33LHcC6=^m<8*fq#8E^b(NWoX+^0J9fhL zoL+1?m^@-m!S(_tsZ*E_QU3kskT{XD%^4a0*g704bk?^eGj?1@)$AE=7LAb5$ zFQIF%2A@h#yL;lXL09_azfU_nozuzt1sKAhH$>$n92Cfb~SzBRlp*qpwmEqpC6FKr9IQos9DDf|+5Q`^I% zdH1gN@Wb3a&>kKa+_!#gM>r)2?ny5WCokmbnVsQ&ZgZgCPfKs_3}*y)rB8K+XHeqg zuJ8ln#(6Mz+`BSLHs5z?6kXGk?&uEZ%&zs`u}{<%OBm1}1eqTcr#Z`-c%!1*(&Jzw0)9^(AD*dl z6nGQ2<0q0V5I#nb)xp7w>)$>(+!O@2r_*}FmFb6z;j~FL+&2zc7!9OX6~lR;a>o^u z_DG*AhUatrk(Tfp_tl>CnqIJVS~_WJ__Mi>F*)H{Z1L3zMgZ@#%JP_5)K?Ce^g>mE z16ZQ>rj=>oCxaiQ1JlA4rGZ=F&_Q(C`a|{z2ZGT{?q8W7jLJ6T`_f-e4=-hT;Nx+) z@5GNeR-B0!th8dyTXa4L&?S$jx5eS4;L-Ju#bFqf9(eIH)?PYP4)-cO{(0jLr=_DR z;d@74cgIS_S|~=KC0Tn%dR`@bL$D_OQ6>C9!hpPp&wF`vv66I(av|GYI^;i;ePXst+j{^aFoR-RN!Fu z!6d*~Z%MWn`jT6{pdh@I1=^)S+PPPF+R^n&^|;LzD$q2wn`<_NUD{`&3xf2A`#>6- z_6py1de+h`9MAT3;)dr_e|}%`+aKXF9K~9h0=KMKt;DGrd;p!isA4`yHd|7w70Utn zZxw05Yd(6f@GY<9*c5Z=)V@4I7n(t+^`Hhx3n%YHA5K3xJDj{A1E5xjg;@`-FO(ap zyPzk7>O(@t)O280xbN{9Nb?(p;#82KZf-$?l;v@(4EN=1rvbIk1Uw9Z;v zIhjX8TXwp?%UJ($snvIgLkBd?Hm4|)BZaQTC0%w5**HLkT_1H*^ZcNu0-r@ z(cJ;#il?!w(oQq^Yn-4Wd?5{20?D87C*a^bO(o+>J5!alU@|KuGt)71!f|_2dMazt z{BUj!4QlnXO};6|S1dGwpaK@038I+A3mAVJq=4XSdlS3*_=O->r*mW|jY%V*T}M4fm!`9T4`8%iDx} zNe-ateQ!GYz_7e<(0{Bljw}jjQ!&4%QY42eJd~TbFBzg(hKFLZH3Ja;b16C_J^R4$ zz~Ux=NY6bW+$a6ffvlBo`qBD79T*O{p3RSDNEy(85PFboc{IJ|;P8a%md0*w;6)Cj zbuI_M4XWNx$*^1CY+SS{#)Yy!rCH#qkX=KM9`#=lf;Uq>=J!#n2jOHz8pdEquUfY& zlHuX>V~2zX_6+4vE|uA}oUd(vG~IYexPQ;n`J2^xa}{r1crFH|C{=RBvN`_mb|Gc7XQA!Vl!HFO!wJn%9pu zzFxzFp~eSW^YR0f*QDIQZ~EE8!|3%-HXm_S3ErQzDxbzK+CXG)YJSrTG&iJ#V!q=m6&3UzH z&lepXF3y#zMd>rI4j1rlzoWx{rDXHFCyx$a&AUAo*t<_H2wz>Vx2b-|SuJ;#!U<`d zpdD{c4^2X;)Ek%1TNEy+SN=bX!m}yyyG7xcF!<&AmtGq#4ruK7*M)5Mmo?>O?0LB$_I z&SM>#sb#z!jLF=BQVua7)|qDmgpc%lW1CsA=h9o=5WeNrBbrLj=iO2pvXl5x{3@yV ze4G5tEwCLWeccG2(AW4Y*0^iCm$+6$q5=NU$xPe1?0a9)eZxRjpu8>sl{7ss@- zR{8TchR+0lP9Jy^1odC(m^X(@2~*;yl5Ti&IIVNrwV>SFb?MNX!x@={_3ZkNw_r?A z|J=8R$B*UQmxyE{d`;U2!LfgiMRnI(!|je@*PbF%bN%m63BTxwU#gLTdlu7j)5KZ& z{rQtqCw%eh3Ek-#n1B;lG53rVqZM?M8q7tNhWxo7!$j zzjA7LAP;_hT6iQjSae$WSkv9a^zip!7eAZ+*L%zmod0j(g+a&X@v1;6F8X}B@x9^R zvtQzxc{I}$1Kz+mqQ42T(vxaerRZ;I^uBOLwO)L-S;KP+lh81tze9UmdTLRWQjqjW zQHfr{%9~;(^I|p1)vG%XB&l6dVSTcq#-L)3$qtQEgr=mHQ<7xDj!5wXF;ywrbm{s>xuC$Vq7DiZ#@-QqizJ!K zK-EU8<+-SliflcE^q_G^JXZ>#G;rHYiJq;En%SK?!GXy9sIf^DffZU~O1-6C(HhS( zVB36SrfBQ@UsmR&w$qq)(WtRjO))9Jg6U-Cby7${6I8s;fKnyk(HRrFz$15g_L+Reva)dB{E|FX`DL}G zUO1>1ZmE|jU}9!XgpFlupTotQ7YG{5)}G4UR%@YF4X+GOtJ>*N?^^>1{=Idx37T4m z!g`MzI@Exj29z|w*w7yqcd9vLg*tk0E#^-fkk-%(k9LK}j|h3gcN_5E0B!?})rM|1 ze76Cb2Anp4&;*zUR(n?K&@_CZ=>xTa$SDT~hZDQ@xZ%4Epf`NX50K{5h#$+FrPY_M zF9+OY?Io=iLb0Kqrz|n-4Omy12Pa{A zwQ72CkK29G zQM-LrmXTa<-TVjRn96Yn&aI>izXfv}7EJE3u{EaJ!neBnkrF&+UlJQfTz|9{J({xJ z_OQgQm`-+&CjkD?Uuyu@Ua|}aMN6_$f1`C-;ZgRXlEh0^SQ=DUodrW6EqqZQtmqru zaqgPHXg8R6q!M%Ed4@tprIRM<3=NWCCYK8804*pDDFJ~PRBD0nt9V@2594M)ZvbJT z=Dpb!K;Yw4kWuR@bK}l+e&_}?bO_H2#K(ORX3 zkf^lNzQ=9qDH9{Rhut&w^>LG_w8lCz$ltL*ugdh_njyg&BgF_X@}$#d?*%euB0rUh z@o4S6uCAY<1V%_Upb}2cM-NKu_DkF*;o|mTrCmC%ZM)uI0sFNx!%?(ZZ zM5>f-*A5|$Z|y}g^i{_B_lyqRW1l7a;8cdLc0E~(@&_X>1-@C6wD(HMY}w! zY^jR*C{9z47e=FP-BKl~bpD87G3(Yi>Sr*Rvy4D%yhlH%Xl4FrAW$1UqIltWk>Owx z1;7=UYTTjzU0 zqE_p$mB-a7V^Ul`t~;cus_25fC>dR_uR~qltlC#W3(vEU3%3w>B-;xY!kiYl|b_tR{iZqQP1ktO_;oEfQExR?M3^BhoMjIRW+r}wy z5#&KXq~d#_jQG+W$Z6|?ts=RZnvjUuM4I9{UbNO>sHBTyyu`Yya?OwJKcliIoNsYu z4%~6vqO;3;Xn?lNhm+aJRVsaSw1e$`d=9$bQ*DuNxy*@N8p!fq`W>2I-dn#vonM}% z-|h36#{51ozdT#No9CDJq0YWSDJt3osc4w06XShNFN0f33{~1{awdOL!ur{lmq-Kc z)$J1-(%%W?NjB>$IvI3!ypJp^0w3Z|kZe;McFW@vc{?GF`zstIL|QA@8+avHhg}H~a3_Ar%-DdT=LS<@h1It#mElR|^?08N0*F7_?=X1$gI z1`_BG@4aq4(>nxa#xpXc&a4WLEU0N6sx6+8-wBU1G7p9%9!B`OxXa{Mc`kVDVhC%f zoK8-0O<5-LcKyyJ-IM3&LpE!->#sBxk83X?sPqzr$pb@X@~qYlUu%ce+G&4xu>N|J z)oh8Wvit!5on`vvJ4+6rZzu%^NOCMYfNhctUUxjb-z4w+r&GdfCOc!Z)GK5V1seGONI{nNw4ad5;I}`0&_lI#p zhp?hf39s=;2xh~ckXs3b`6PBFXJf$=*B_Vg3aMSvCocL}IK5hevPiw5p@Y^mbeWp%^Jb}D0W zm}x*Ix)ZRt`9L{g#(J9s0TcX_uMN} zryx!^uT4QN8KT+Y#sj@qO!jDc*2fV58DD0eGLgms%DBWS9ikWY;}VH_99AW5**~Wo z@kP0>n2h`sy_MUF?@zLva1O?ildsexcDyJp2hBhnxq+yxlhhVLBQ-S%(w1|>F{G{J zNM|+MwTK}?!vP@-PjrSRy^vOEgQTQ}>L6fRm#{L+G@IKX0P4#;cz7?-PRO=WFwuIj z17(IG%>b+zdr*Q->|tl6gwZG+Mx;zL7=kypU=ersV|F62MIXdT_M>sgB9&DI4bstU zj72Qs*KlP+1K3bkupUrtfz<>8+Gw(swp%l)>*ec~1Mn2I)w+)ChKexb=y*mgyror$ ztJZZGg=neERMTV&ZHl>gJk&AZz(DCFj{!Q@i~%LX&Y$3^5Jr6Ot+i-Cj~6l! z7{!1M#m6v+P>^RVp$N=WFFqC+tm-bZV6cJsySxQrC32TYbC4tVxeHMFM5 z0e}Ql`3;#1cQDe!ZUJ(j>{{v-k`!`?u0nr&Lm|9T{|IxiO6U^79tEC2KK1a0=Nf_3 zax5*vWWawRMy-QNAFGCfSXvCIMK5JvfTs$3v0y0OFcjPcr94cOQQ--;Bs^{6`VM^U z&0j4uS0L;w$^&HxXB<%>Icm-|0g512mG?@DwtyMI&M1IVKya=GJ0ja4U~1tE3L30H z<7h1|rwq=v%zdEKlP3-`+Gebe)I+Rv3%QUUG*#Gv*(8UuYlP;(&&wWI8{vG73!5Ci zDFZN7^Z^i%HGIs(M`Vpa>{bq}kEwc=UXpld5>gznOpe#%buX5X^E=_9CkGNU17Ngu zkny7S5!D)O)X9K>5we@@_Qs#)~$5TikWJsjFK#ORT+8_@AC0#I~wGFO>|k;6_K zhn4iMh9@-i`cv>8cY$c=50J^l#1$ssZ}_F)-)S0qGqG!$*^EbzKw*CqIfD^$XP^lk zm2vHf#-}IhEY%p!-qRTvEFKYHjDHt#Ln@vyju})g;BRzgsYcK38pPgh*0?sPars8H zQZ=*&4FMzJxrP1CvtjU-YA%#ba3H0j^JvRZBfRQVcItN9@r#W2Dv)u5^#>9r@*o*u z5%ORRGm;R3#Zi&eXqXWa^NH%=xP2`QZyv!0E{U?w`pYH+qnKd#Fo2mB;(d}IDFgx` zJ-RnhxUZ^U2M$ejYJoQ8ONJS7{vm~qHlpPX6s;LoVs}Wdl)t}gpNcl;_M;loY@8OUamKQC%nj=3>a+cAzqUF2F zN8_!ZE$3B;-pJEKU!WxXh-ys(TCJtlV~kX|B=j3&Y(avGCS#4Ra4nxQ*65C>>tl_l z;tO~VI3-i9(~JkmS2w(kt47TN8ZZuC50$1G7iiHqqi0#KtEJ}kcto$oAI?h_7^3T8 zs*-K&){fHkY&b9;rFU|SueGE34L*YZPjvP8V(Rf|Ur$@=C(H8lakpf>~mS!H&aSvR&;}I;1b*89SiV$bx5*Ta7 z0XPX9UWBv*3YI9PX04e=sfdifkXxj15`18e(ZiFCc%)M3HQ7iA1NGFw!{DD5z0Ac# z)~7Ef8=f?rS`yi+0k-2fJ24pZ1Seqwz2Qv`I*bBNCd>GXv?)f@&L=TUY-5mYhZ-r! zpJwjl9-o){-_{69~`7?}aNUL*f zhS3T?RdbCN`015vJg1XgLXB@FCFZGJ;mtG3M(4SMVN1a*Vdwv+tncqA1pD{NLr}^^ z`aI9LH}XT?AIKko?XLbTQh$C*p))bzV^nn}SdH`4ekL4~7SYQyvD?;C&P-zhG7MCn z1*f-DG(f88jY?Yg{FFIHo`&FvO>>P#+I7!}G9_uhWb3 zjHEJ_Ymtj_0p90^!_22icwfqKDT%9r<$;C6=ZU0Hhu`r;f0<`Aj7Od(@OgYc7P??@ zURusg&>16wtHFG%`UHAnzERnQ4jni_Z_^`-MEWg#$hEp=LGR4_+dhn()s#Gca3arwg+8o!H1$e5u6I6R@6@Xj+@ z-ZP@*pz&u98ZFgrI=UApF}}6<*Ed?a#Ap>6y&bKDpZGhb}c- zTGf$1Biu32dmX;T<}u|;7Xoz!2q)`0R3>2MIVsHLbk8zl7Kq=mWk$#F)6j@1o^Exs`NzIXpTapt_$MlS^_~K6B{d74XkfKMm=X$?+kS@r99# z1@L@n#2c-X+*E5ye2Si0Pi1KuS{-;Vt9QXV}PRusxysOu~xuu2bAz$c8fHE$t)J-Bf!6@_MmnW522uoF3bv#3lvcAaQ1Z< z{r0U@C^@KqRJpQkp@W3*#>gLHbC772wP#@E_%Dch_yJeg3vEc|9$9e; z>!=M&zT+B&F%EM)UD{ww%P5kdE-E)*5hq3hd9mQ09L%_Ec<=+ibJky|bTRk~M}rsW ziuoYdaD4<(AeO_+fnmrbT_4_y2Jb&kNMQB^nf^(^xQ3RhQe|OmO*9ppq0*;l-V8{s zfGUcoLc&z(Rw{zJY*KWvMY5Au6QAk=u130}DL?Ub(oge{>g#Qk($NE4QOOfG>)6jM z&Z}bJ{V>g~%tm0!Osdq)aoj&(@8IZBmnXvk@qr^c06ffyWtNiStO~F&8M!t(n`@6f?$aDsvsbO4hJW z0T`vB#9K9XP#}BYL3V=x1SBWqMdY!noRt*;|5T`X)eA4KM)Tms3gl6m1g?mei9=fO zDjScih6q8Z2>S*zM{R#Y5+IhC1>?E*$aE{mlB%?SI`!IQR4%`UHzYs~5g4QsLqu(J zGtJ**q?7@8eFNv5GZ;7>Mjdx4up{jmbf$*4h++zjX4vdc6e_yXe6vx3&K4S-w48kW z{VYgD{wG_EAPu6~f~`geZAbpKt#D;WqDIe7!-JGoMh@N`+72{8k}AAgQ~{}}kYAMD zC!i*SvH_s<7VYzgP&mDX`{?3MV^i>4Kl>0u@vqAocaA+h@ju3%o}&J{j0f})pi0k& zr%_4YA2}UUW6K-q9PIfGHrWY1X9tT zp{@1ZIz|5#>ect?boj5(B>Ld5&~Ex(_0WJG{lTaceF`@TvG&7Cdxt+$V}WG4IfC+j zFgE`8&y{HIKI0QE1LLQ!3H*sBpuI&C&+Ip5YNsatX#9YoMIQi%GlFjXh}dD@Q~o^% zjP^Pn#~sAkGa~=9gFwmhNd4tggaMKGda_#>@2VpNPe!2AH3#WrOpX$aOD`Z0f^ z;v>*_9i+#O8tK)>JNeQW_IKtJK1da*=(r^xYM_I+zd0}ev!h0l7WNBs;|F!9?QvbZ zM%q?)S(Tw(g@j+U49CW1+iFpi9_051Om=1KB z2{zH2iS4Gw0|;R7t&>1oa8HTG+DSclCe=6rHhDXBJYh6ycnG2d2gUMUKq`<$6UMWC zjIVIvcv$OO8!sO-*c%At`jQJEAeGjgKtRi3N<3*qMccLP;7jBaL}3gcp|I`o*=5)1 zLw!#g$?kp7D=Es0GnGLR01Y7>ULO`oYfc(LE}v96@str+jx%|Jnc{y!EP+{rw0!NP z5r4-|eJSk}7>iT%_$i|$#MLRMfI7d>^wUPQz+6aKev^4pWv z+f)9xyG{cYTa!3r%&okEq0Bk%mD_>NgjoR|1Qo9^7xk);cAYU==~;rBEr9IO_E#ei z(ch2#YUHPnHj;HlHCt7Yr4C3_y|FYFyK&g?83aV#LygZG+jKDQu5$=G*h}}HGp=ek z>7MgOvvNgxa3_S1T5P^QM{mw~!>yg6x#x|c`dJ8Augf%j!%t*(GTdd6Io8K4TIbW9 zW8`yb-2ka#BCRXZ$kAUStuv6vkK&Q^p%HNEUEZ`}wO@-y;(x~5!Dgn8e8iaYe zvCtV$eKtf!TJab$cVytufMVcOq|T#%7~K;tkAaG`EKn1T;N<1CP?5UAQlaeq!{}Md zE>ogOxl`F&^eKHWA!g|!9l2!G7W3V~UsCzY5bJhOzsn#+ztPCckXDb(qxqM?>ikZd zFB{Fn|6u7GbcF+1HRKO1|EKW^X9XVoCqnx#QLDd8 z?y8A-qtrWT{^NTBm0;co6yRl6E%G1zATw6DW~Bc&`1HJHRF5fv2$)bW65RM9Ld&li zsV*M?=$C6oo5!yCx&#Z3UV0-eH<&rNk_jtT)<0on#PJ@Oe;WvSs9yf!pYWi+jY=^k z6XucdcN+t)Qo-NGjOeeJ)GEGGnD{%yOadVYFb5g*_I2Y?9ltxT8xNNmY=97}kDxx* z_v$yW-G>H%>8npBuqi2as z8Q_;Gr8y|Tw=5VA^NEsWN~hw;AyZl^DAtEng-rR>O`l9zK2V(v*bKkW3|%w~V=tUQ zNXyF5Gh)InQHh#A85cMqT)YQztct)Meru7uTK?YE!Js4+W(x)faE~GP!=V7o3o90s zP{Wq7u$1_`AaDpol02F}Ffhz>gyc*P+ z?Se#l)#bb`k)_P0xi$uaIj;}1h``C{oy$8f2pe}!kZ9?e#!s9j@M4f?9`ZY1EsbLl z{M7_%6f6?l69atIn^|)#yc3sd_X-y2E&uulH93=?pSmMf%SRSrD==R!`2x7X*N|;C zg+RX%e3LCFZaNSwn#Pd72WAs?*)s}7jFN#VZj7%GMC0B|5H8g0`i6=`v^_3V)YphMhKi2*hyaQP z0g65R85#fqwfRJBNv?_co0n-on5gHPJIA7+v@A@xYahhXY8`M&dCdHb#{A5ucBzuD z$Xo!S6Rg!=VWN_2DSyW}DJ5Kt!SVWexX6O0uZ1b@Z3D1d*Px?wxVCYC1{B&zV*JZh zj16hqe4kl7XORk)I+c_bOxJ8gl;UA2mf#R=H8H=%q(_LRe1b=avIpYus;n>t)S|p7AFmErBrwn!lYAuCz6}lkVIcT^E_bVRG|D5E;v4C1@51t^1WQu5tr&tgi>*BH+S_8 zzwd`tI*>tbUpeVf%cPsj14`aNtWZ=7cQ+!S1~~HJkErFkQ!UDh5(!A~_i2>qrhQH! z(V{l4I=ClVG(gU%x1yoe`I1&ei#8eg{`QmDGs@~96RyqPOdK;dsknVu@b^RS(+Z*X zN9by)G5Z?G<_5CSKo#%>$U+D;6dYsJ;3x5?Nl3#x2KUnRr$I5IYFY3O_Oz4iX|JTO zW7Gr=#)x>=UZy5G&`paKRfCp676Xmb&6QLy7U!u#G34e2|8Bs&myp9&4_-tUb#WIMJ6{o%qL+=H*2NFP@9`1PkhEDhNaL2i>UB`$+0hX`gMLgFV+`}DP=|}sme;x@ukJ4iZz!+#9 z%DT=^0{LLTHcJpua3s0j!=32lbp<4oUUJuWt_r|l6Xw)+CBSu(UMuh-9E*H~jD*tG?oH5WJG*P`jhvuK<>-=mu+> zZcsv!s9p*o;{A*8Y?4BVf#4ueY(^3qT}?0dbl1_=(6pZJ_`u(I;=CJZT~BwMf+Jq@ zc>ck&>w=Xw_bPpmEK-8MMo0i_E;zAy$6oIH;}7V(V65e31sF9l2!UJrcLogR7XaA0|~H&aq2ED%O>@>O>w_gaQe0pjU+ zrI=fCA$zf>ue*j`5I`OKf^r?D{(S-7bpe#!*Ih2QfH4Ky9I)Nk20^G@jM{(0;GpiD3cItInP+3&8Xhk&IV1@5~%}mQP7Rqd!3;X4yM)%TN=EBmK=4 zp*eec-$LWOpIa~DfqVBOGu-C^lrzAc;@%L@kS~{)>e?WrJHe+Fl$ItcxxWi&WL)7d z#j4U3eEEWUq=_nUN61bCFa0I0Oan~w>5nv#6y;kc9~FWBLP%_|cO0dq##h@uPWBCrGvZl+^DUnNg$9QYR( z(wHKQeaoFve?vf0`2W;U_!`yHps=3i)&RQrmOIJiYl_~lAu7dxhv1z5*b=R+0ZiRS z{oi&cm(y(Bx3}GIgeWco=OwzkOb2S-GVrPIxNCU)@WMkW#kD{q=*L95uSAaawW4`-e%z4jMEkat%`faYZmc( z_I)=W1I2YjHT^G1%iee6esTP9_Ntfv({7h{j zW|=z!sOvy?Y?C6m-?Etravh>?8N-&s+%$i;pY$<4Sz=@pzO}ANZpRgVTnDP;}v4QYZDPayuEm^2qGMh=^A*mCuArxeO{-8laF(jsh z4P?~IjbP$4DWa!&*r0BM-BqBJz0puam&eJa_@ROb2&@q(Q;0GLD6NsGiS>V`k%+dY z7-AifQyPh2K9fYr*y5TQ;t>^3_eA{K;NWv>N?cjy39E1pta9ZUJgocBA(E91yIB=cdE9VmNo&2x=y>A zh)R*W7*}9sRrF|n8FVqf3|q&-nu_u$x@4H!!<@&iVeS-d7xif>s$!E(Y6>}E2d!-? znt{o>(G*BAmKF_n$AnLGs-r8rES&DVMVsiMW+GNE+(a)oL(x4{^Fwz$B=mM4x+^B$ zdTa+;AW}S(wZC#cRQtM0b9A|Y4t?m31+@D$7Y~--gF}uP)cqg;N8A|l5qOqfp`zv@ z$@L`*!z{^NZ7vc^@m&fh6pvl)9!0?)yhl_35MI3pv?YhNfsFj*$+S4F=9!o`=tZ|o zcTM!3lj%;^v4ILQ-Q~1-)UJi^7;la-9cf|Fnv9WZz|mf4aG-zf>lO9j4w62~-B58_ zKBlAJD0g#aI+l)d*McIwOH0u$c(-$&M2?zZ6Wqtcquuv9@q+)(YbDACg<^HQg)u$h zAy@5QQ3pLfdoP;4LAk92alE8cbmbD-ICn*>ay4JUBdshpY}7b_XDwX&07_ugPvd}5H|V|AKpynmT0E}*5I`B( z?iT#JF5BJVHa^qRLn;2eN{&S#+UB71eKat~T_Hp`TNa2a5e{2o5!^fELYASvZOSpy>ehd!(ci!U6K+(>Rt$p^}$L_>Z zM_tr-Fwz&9(GTHLEylYmS`%FghSC5L>Nnn^mqrgpQP^#d*t5nE(t8j=fld>;cqUv+?%@jC6dD*n|03wn@ZI)dum zAa6%eitF#w5wvsxjqNBJ#C&u+KkO6im($^nY6$;y6e+I!JzzFLZKzr&(Kz@jn;cXk zuB!B#mRL5>JHyI@{4-^B5-pLqY-cC&Jd8bcIzuOQmfq?NKa^bhyt9}AfuQpv;(iDO zlOGWiZ~;(_M@3zHd*M-0SsyBB%%h@l^l)czWh?#z-bR*_4EP6gm_bJ#6_vP{>oL(! z|Ina!A4B%FW3=!w@d#{;iThza&!UvaMK5g(k1~8pIE2%gFhMF;I%R?s zW)>S9S3Y>~4eg*F)Quec=+$lr2ru0&&x)jSMfuF7Ap|8LInX=$ z9J~^1o0BGX6HyWW>W`{)!%mw_t-IlrpQ3t7`&S_~D7X$j#c~1w2IbZ??!o~P{_}1k z6}Yvlk4T4U>1H1Roo{y$2fHjTE)+bp0tE0+Gib;2q8qL5E~27J%(115OgCuo3u1J} zC$MS3$&CAff0w~-_qT&c8P!fB!#P8gM+1>S_a1@DYX$=n>M{^uodagfT(xAEaFZ&A ze%D3;G`jp8G*@{P_B^x+x6Z5T3nHu$+Qo#t-`%jXY~wLgss&K91fvhQXG8g42<^MK z5GA*9$q0Bz006JY+x2YM2*&-A>eH#~3nJEKSLma_k#tvZQ2L7^A$6vj=XmQD&Hpk_ zdi_PwBu2qhD~)y-2pmG-o%#L%FpSrV=){ZSsapGWC~+B-g#am58~fsQ<}Vf9V7Et+ z?nv`$t&-sV0`|Z>LH&COcd7;SX?GvY;vV1uXVI=6aFqPlI@6UN!kh9FHl?27{S%k> zv5)4cfKW$__nQ4QQa$}yL|tDJN$%MS20(g&>>6AF<>=#=L@k67QO>i%eTP2Iz66SW zjyx}m@^$|;0)w-hao&TB*IxrgU~?7pRNpQ{M``>R@c(6r+4t1{ zU94o4e%nj5v)BCG-Agy>Eh^Y;U%Gqg zVZFsuHHP6P0i3EW_Py#u-hN`#|FD)F`-+%b2un@ZnyFwg zIgdO@gx#wB%4)UiU5S8NbKN`1$)t$APQw7NaX12NXt~zqwf_ZSs7inFYMW^cuQ0C; zO_kx@g-})4bOv~v_kR1Hc9Epg#tPie*3ZadAuwbpF1Cs=guO z)0Y@t_C5#Q3iZmyj*hTBZ$r>7Z|h%u;;1^rRh0p6faAPCv)>R+>TMbeBxHXo!R{h} z;2MNsvBw5`t|KSA{a_2SI1(gBsztvk`X<>`OU`w!p_GtTvA(q6O|TbBXz!civ3rIB zHPna=IPx&93Qc!(#36!Vg?bH)nPJ4k!>vUpFd2R<3Qz&(?21XRyakF&l=}fxzhA#4 zo`kzr5dnGN4$x<;hX|H2%lmJwrJhk;N5?tymv$_dEOC8albpk>v3^w z%ip2&A6$BN+@=3ku=~5%B$xANy^CWEg2JZvpca@-H{KIh;9v9S`%qG>rw2X|k6FX@ z>KC2VeYA+C>mLB_#gsNsM9bg!PBk(IirCoS)eP1kf>;9wjY+_R0H>`d0M_F{1Hq?V zqE7}w6?~b#94H!FAy+}rQjZU`Le1FBgitehQNpb;G(+Tp&0LZp`oVw9GYBHU6>2j` zOonp&mq8*+zv`r;V?|kNF+`MwR}2jnotWM z4F|qlp%;c@*(THc;o?4aeY`LnDvzTS{Gm7yf6b{FWGEM%-o0x6oEiWuU_3`R=_5oX z@wYQMng)K%uHPfXeGn&eMu?0uH~5-WR62;xxCV?V$(c~Epw`ows(nDN`n-9WLTfl1 ztUM$|usAFZ=PYdrJwwJs z_yT}Hv%mWk8axVm@+(k6iMEg!Z;TSpL0#Kzw0IOxd1FL5+B#YU1Je6Oi$D3*SiBmW z4KXX1mSjVg9YH(rL*6l>PT)q^hIu1JkawJLo4MEk3iEYuE_gcC9$cGf^BA$uYN{T7 z_KgKO89~>^0%tc;&2dU{y{+eJv}~N%^dGzLh>D&Z=qaaDgB&p?)*tT)Hc7<*0%Dx> zm(lTXj`*d<4=QjW3I~jbqYqOc7lS}S$U%`E0Ay4$yHN?`C1irNKfm}REC|#+q2opE zkOR=GT7l}gq5Gln(4GFoZxD8X+9x3Dt2u)*Q|5T_Ucy1PAaG$oD z5LNWSg4#|H8Mym-+XV5H6Ty~F^E*nPC?;x$Xv0KE0K4heiK1V?PAxgW9EIIWB10eT zq+dw9jBgK3f>m%bZJs0^hkYh#vgio{oi$k`27Sg#U?2zR@)wf<;UDSjWD!^KFx0c0 zV2-8CBmB3@Ol4_7_-uji*iouB1yTSzfTPe9YBxnBd*BkF!%o==Ll?bN`Bf@Y;KHr z{xspKkPSPt-A*dj4M~_mlb|9n@c{$G45#LwohIgJ&|KtvBA$Rp`mgwjDnwk4U2i}9 z^!Ogl{Z76#tji4HiP~K9wb1@rh_9RJqZ#4>G;weS_Rm!MbB1^YKizUgKAMlpgPYHf z^k^Qa?Qwc1Ps9enb`5U_9t3@o2j=@1D!}WCwyhhlVYgw|d{9xOvJ%*&Y&M7J2zBGl zJeUwyQBx{ovU%p?sM{@v}hQr&7OJ;vmNM z@NAJ1_ao5Tp7VU2t0LtAYD0H{+GA&n*vM5**!g^ARw~jJC>#;Dvcb%z;zfE~+&L5ZOf!&4Kpv7-h^6DM3E}!%!#PQ$9QjI>t?NaK-meI(5H* zH1~5Mkx$L9K3BLjjI+T!Am<61IS;@)K^y0Z_wD+kb*P|PKf7b7$9#C|aW;N)Y{{Ze z;^hVz!M~HBl(9b3y!oPb&aYzqiQ{+E&zqwNN+5F$IvF4 zzd+P!G+pil09iVrnkoVuan)ERh)Qy;oW9K%uZ(qb_XKPUNTGQIej=nyjxwm5K2R;V`i90FPR zybZ#}+xeMTgwpsam!Gpl;BKBBG-0V207@RTOtf+?odMV^q{o&)<*|@5mWf8%r?hGr zj;cj;d>Lr-Vv@^&Bac$$<)X54EhFYadStn1A2|%PAl>mkP7y?4!0@aA+d^>b-dD>- zl`yLyP9^m&|F`8}YCv=!S^CG?2Gbpn6OVFs5)GS{d!ne4UP-1qcLHv_T$5)D{QN+6nJAGCD*Q=}! zBUg)#a6)=zHGCjeGa#=Z{2A8(;a{CEpj(Y?z9#>{HSn@U-_Mok6SDf}wk%0DaltE36X{ zK)P}3u%FIQ$91rJeOu}&jV|sGk`Ar|@@=@~eXaGPmHwSUMc=}vGiE*5tBr_?!Ctj8 zYV222gr)~%42Wi6vA9Oi;ffm!nqW(S+wTk@6pXznOQV^vwFL1vv`u< zhHOz~n{81@)^iI;$2$6c3&?MGDzjCzDQSBvZhFa;C7KpKW_)-t)m({fLh(@-5sKhuK}!AOi6cz@Y`v% z6V0rn?mMA*&!>4i#lz_H@=mancv*xkGmFMsKbwj~TX6e2>ZrgWVfrLkP`grR2RM(JKY()1rma6f<%A;_ zM8n^j+=necm8$L&k5|NK5zLkeS0SdC9%mB_ng<2-pUwg=kCx`_gOLSO-?0ybw~#LG z6Xj}@ngn(sG}ez=a^)@4#TN!L@|shf{jexaqb~b_#j|O`eo?c^ZB1|*jK^*T7dR^g z^9%cN25qL`A4N>8556mP0GLS{PzVg}*|f}eA4PYVkAC`5G-K5;Vs=9* z>Ht2SqmBne9t6fe4v0x8nDrB^PQTF3pA?sW;U};eXUTgI*nWp^9t!*!D1Vlk{tU~| z1se9V$PW4iRLcvGS|nu5J_sS9=^+s-e_>|E*XnzRM1AL3W-1HF`?Dxd+YgEIVaPWg zzvo({FuaenM-15tU&GbbJs?Ld**9}}JNGwYc6w(%Sc7rEh*Ktx#GppJBe4bR`=zoia7 z-FQ$&DdqmjO+o0JOS#9z{dWD{AQPxRfa`P2Na^~>3{QtYIRlj42Xxa&#<`U4rK#0WUXjy@&ceQMRk z!N`)0reT)XdW9JdO^3b($$Y)nM-Sor*??v^o{o%CPY`fjcp93=wOD+HC>&(&Q!zMv z<0_bVzHt@oxH*Zsod$K>LGPc2PK%x}u$N>7@#>m8s9K;su0p?*0|h%GVTk7Cz#W>FKi|BR^s# zM2)JTi#o3wqzdx{_`n)k8U!|Md}cUP`Px@obX!ctXGMd^!%m-BmWjiD>_1hLnxBK| z^c(u%99X)=^wT+!>H)1mbElD(g55bmoH>k0SNT0S%62MMI4>HOUtA*JIfDHnC-pxM zbLI&eaUPsiAuZ&eX|(-3G-`_}@PeYwwJ*R%j`8yV7Spr*WQj+3%DNytm2EL|@oi#e zHmIr>DK(A~Ide9?FQyt7MV$nsS7HMb^nUoU?n`7Ni?=SHzUOGr zMG;+LDL$n;I5HN%gD;nf{BEcN7YvC1AVZqDjMiKPot#DC#o!>as7tYkOSG!BQ7Teg zHfq;X*~(C5IZY_W5t~V?ibd5h7Tb_OAKU=er8WIljLnouDZi;5_V90Fdc#xnA-lHQ zI09mVZ3_x9!WLP;^5I1bB!F*-U&M5shmwC6&Er4+7*E2kt@eDX-o~T%6Q0#0p`v36!k;bTRvhb z7R09iiJg!MtxgRsjwvv#`34g2BYl;rI0P*AhtB*He~KGA!c~r46|Yy_XyD=#oc)%w zM;Na(V{D5QIY>Jr*msVhq*Dz;OWnbWqdDd9e(NxOPe*-EcFropT$UeqLp-Y` zEicvM28a2%JJ_^mf=xJ#P9EE%|GIS{?PYK+bh# zPZ+7Zy68}V92Sk=A`qy@nM`VEpH z@&U^WS?9nbIB#_ckwt+gRy!ytLOxLKxUXGB)xE{6nqVdZ z3SicN>)2)w^YZc+Fk2HNWkpB>t0U3r{(ZQqmT!0Fy#qcQ{xbFT-lzcH z4R;Ii5nMY;rh-j;DoU=5K7!($w+zh13Xv@Nv{f!N3n(pG#x~sVgFKU4Q?k{D9c&iZ z*)N!73BV%$h%|_|e5Z_PITs`AQ|zpOk>wOS%h9tjvMVHuZ(?M6{CDLO8v-71NCEnkKAAOOhAlE zR}KCe@&vN5W0jvyl3VX*{Ic?QfsZk;ew>KqmNYYdvX4}SgEcF+2d?{&D5%6B2k^LE zMHvSJPOpkGnLbUHD-%v>SY;$@g(4%wPVa+1qhhVFdm3CpZo_scsdhXNj(sjhpT+b< zicF0-qFaV8J{WQMf&I!!kuU0l1L#T$@ctN8t0XH|@$Y<`TN(>XfWJrO>+)d;KDf-x z0D7;ItQz^ZfZQoj2HY@)1Jf|u&(6@AO7h)Em6jOsLmIMD+Q$v{-|zHrW%+2_aF$Qi zxoM^9#1n_S-Mp!RE`C#4mWOlg_mvUw%(;xw#5B5DS$h7JPp^8ad=O0fJE^iE)S=5$ zPhvbWZkD%OxS!w1bP zl@0L9TzbBSq#o<}Bu3J)Cln*^Em#CpJmO$soefCH8u(es3z+^Pe=aV00kVipWu;0e z1xD!?m>Rc1-D=84&SIn^gbX*MCbq>C+E-K7g<2-0mTc}W098yJN_=gR98w5y?W>)i$O66p0qBT{E=3TT62^qgb9!8ED z$G;H$5F6X0j%3a4ygIT$twLY(ph4{9an}wgysoSdy3(qy%z9`uVur1msH@s|Lb>RO z2e<{j!ebQqss6WtQ+z$3XDKitiEbW^tOqi5ftJ^kl|Vs%tS8&Iz8e5|=Ksq)`ql?2 z1miTdzU-J{`{Y{r;S6t6C?)pz7Yt@I2;$lED58O^oM8LlA|af?X2D#PQ;WtK=rZTg zV+~}F*zrJR@LUUBwpj-tU_KaN`IPoGkV)=Eclr|25MOq~$;*do{yN@25Z=V$7!JP`f z)l5EIlEcnQ`od)2X>Rw;9Y!%_nsFjG)cI!E<=;@k_<%&%Wt#KPb?Q1kpfP1Mmo9yW zq=6FyQs}ehNMv`N+D*WR-uIOHF!dh!zEz_kb!Z`j>>8gV?U?D69Kh1j3-;$aJ=IcH z2d~$#C0HomQ~s!yu-@Wv-F*^%?D=Qzla2L|nV6Yw9S=X@et3uwkTCkSmYtm&;F}DM zz~vh4QWKgEA2-+WSG-Ma$3M^q>sT0=;k} zfMUsbnD#v+KL{a?1cMD2c%M(*+RA#jzR7DV>)iV0NL!f;mlUW)agShQ#}rOJVW+0F z8d9pse46<%fRs<)J&Ymb(}{=Wi_R51#$(i>oty}CJJn7;57X;I?PYsd!KSvCc`$l4 z>mUi=rgoGZ#t_j_vM))^jz*<)%;2O~Bw6wGQ35-_OBQg!P_*IW! zf!EN{N93N6zo8|rpne#)_CQOcT8D=lScNke_M#P)Yr} z$eMblpjlmfZ8jm}DH&z8-+($lC5Knr0J>uzWzxHb4{es2uWP^)KT_mTodsz5Oeh^uM!GqM_Z zm{ZR{AX-I%UBRn?+?*Ta58ImX5opQNPez9C(r7yRS=rdQ=}e~2xpw{I&+REk9>fESZFNg>Z+fG4TabFwWIny)`6 zlVh!tS?cC_rK`^js;_rIljdkz`kahRw+d`?vGAn|6uNNvJQw&yPcF}IRVs(_xZehw z>C$tc-6yHW^BC!3dhmJKB+frl1#K``rAL(ayc*GJlu!Iv5tq|Gga81*`y;|0FZfSd z+y#v)@HPyFGB*g{fQv+(UVt8@1nee#@d60&HroDzZ0y?d8eXt4OD;2*e%e%&2hFXE2!g}E8 z+F{T~eoE^hquQW-o`kQ30>*HTfVlaWQVqVRcr4%7rQH5fw4e34)gq1*wIL8Ynbkv9 zbRFcebH2!xJ!C)MO!`pGm*f=KQ@6e(6YJYUvvr!Gx6E!SLue`TK-eR;3x$L@$?PHB zzM=P_rZ0miuA`@4mI=-6k=kVgSx?}f&g}!X3xRb)M_4zceprb%TsAcO(4m)Qyl)nL z$an?FJd*0YB2V8!k{Z&xJ!PD0B%G6KXho9RKMwiRT4%b{Q+5Vg2yi*jz(C|8zeOMU9*AQ6 ztFnH^_H3+#$rdFd_L(bW2)K1@|WVll+4v`-!|YJ>npSTDe>Ox2RykzQ~SxPh%4UMPj*Pj zg!EvYyXw?sMHA1SeVf?s-~!J9eM^)6kUW;q)BWZ9?yCZ14{;%&!Tb}F9PC|9=lWwU z*HH9pvLytCr(ct=BbCxG=pvOCQ{uZmyR;t)0z<>HmLa z=v+^W-vpAvW#>(K9zR>(0tW`a(6@kQzNh^A-k_e zZ;S89x*%RliojpnaNkxL<^%XY?!}!Pj4>j;qyuI^IG(5^$ObMZgzdb{)MxrU)CI_096bLjMvDdpL?kHA@FC3n z21>I2xW*UlwZZv{pB8`yuBWE!kCsi&FckcdAS^kk)bujpNau;hJ@xoi?Ff}i&AK$E zv@;$qu=OJ2#{2z~k-Cx7KqkA)ioCwXT5=$>VCe6(hKa)`0@sl8+}Num0Av6%kH^UH zxbP)q49I0xz;|W6Y#C_xVhQkuK(|e*?Hp-j_1;}fS z9S)BhZJ-3;;t98=$=7dN)4SC6sGgJ& zrDs?;!KtxP4>#8<{elHbY!IziFr*a)Pb{EEfx()jx=x(I0bnEt-H)3SUJK>j9qI}4 z-gNYDjUPw5*Fa(!k`@&BkFeHwh1iP5j$yhd0}GF{jnNEFEx2V+DdDtU&?+jxIvKEz zU2u9!JaGtbFO&X1=StDs8&l=d< zP9=~Gx_#Oj&(5j9!7yM^64$hAt&m}z8TOJ{2l~(n_zKLi>pYMwaeucDwX7YDN_^je zdbkgm!)T%Ug43N(8f zjT^WOqrUzGQ$6S=nEyJ6>j<+DCz*ptiirfMhQKZ9uP7aSk1^Nb&p07R9m0 zkf9C$T>XWs?6GL6`r9Cd&J_OU`*81mwrx z7JA@z54{D342K>^vWmM=g=cdCSh|IpQ%0_)X2pgWrP@=o;>-Ma@lX{m)WA4RX}L4G z5MTF~tiEz1W*fhSgAvnHr%49b#Mbm`hI|(}AkJjS zS8sV4OLGT7idJ8mQ_I1!CO*GD7^;HSG-I&Lz?Z}!kc)Rv?;)~kk5i@m>scQSqoBIb zjD6nfGOu8cMx4A_I%Y%-uwwXvL=b`IVx^$;FvW$6EJ^TwKnviCc;f`a4G^>u7GBN} zaeWAs6gwzksEjoKU=|U6Isy2o@=4r3R358F1Hz$kAVXqJTvAn;fQFvS@D2pJCxAp;p7sbB=m zjf?5$5i)__VoCU|$b^oph^l0=W|lf;%6e`DdBKF-0q$;PFmN#0nz@svWXgVQX9yW7 zdp=?N2lM%+jL7&GSB#ayl{pd)VKU&rZP_qVL4vhwd+d*0urv4dK%7R^S3q6}9%RvG4MizGL z4JsHX8^#SDhZXXe?|HMY4o1|tC6MZzXcuB}?i7ho?`-L=U>CDx*Rppg-IvCVL6`qf ztz3wyH>v0oS&zz$#Wu5h`XdWHRk~|Wb}_rBGI#7LcHF;u`ea;5Plq^4IPq>hRke%R zJw@E9CyUNRP+p#lHVpHY$+?1py@-zU9;MnjvU2l7C*gxCz^Zw{c6mSEKAfN6ts07_ z>)X^5-01jQvGt_9RSoYbRT5!odRZDXU4~lVsz_r$lJ1sUMq?%wpvhp?MI8{j7rZd2 z)cc@}U@suoz~cgWXNtKb^kKKm0>!K~efkZ=^fH3+>3w_SzM8l6 zp#}sM$IXxawI)M8h92Q7n*OnD)8?z4BTPQ9ae7zm6{ZUZfH8CqzhfY29)$6i#lBKN z5#B$yt1tG`FSGcGEh*TX>EmT5%b!-~uq*Rr#5>Be^vQS`n0y)6Ya$7jkITR=dxr(xcy7KT|l;=e&OA(n|dlUC+dr)!EOX!&FqYi>}{99fxaOpy^!oganW zmWF*<3bG7^0JSfn4e@WCY4*PKXi68UmFObDyqD-AH4E`jpEqnjkKKX2<2Zqg^SEs<-$wVV+QsZ1!tPMoR*QyN zG}3u1jih&`myqR2d;mnWE!s%jUrdkVT!Pu9T} zWI-_K76{ghHiAK?9o~zXAO-=T&FW%d(AAx^eij^V)JAAn?<>CB2+Qd)6lNm?)2C(U zgT=39_n%v(Q0tSq((SS9+oKFDeH?MozI1)|zkrc3M+TIcFcRZ}mI32Jd=J>moiu4q z$++@Z`=9#%dR&$5`u4b>6fQX?)wnFsxh&9ymVnMP4;FuLV^7bM(XkW2$Lro)76U!T zEnaiGD0`lChwWtGhBJ-FX|j`+&x1&%7$rCTJx?aqUBR0r9<%tL=mv|7ARV3O%NG*u zn)di1H2a8xD%6*Lm=9~hKlJB(aKkq#VS&6S(65rBw-?A4VirS`XAQRcNlOms1qVFH zz8+V5`g4Ko1%C3mg)k}Ypz#YK4!eR*cBTD@LiRZ#2G6mb18e%6 z1Ht%03^7|$^~JK9K0>1|i)H0n_d@yrr?2ES1h*)?1!N+e(L-2Ig0HA#y=TUHh@|C< zWrfg|g9brw1H}n`yU;I-W%8mS)tP(OJq;n#@KZ!xaiz`T8bT+c50#R)5D^G zv#0lL>E{!8sNqmIaO&PO3lVrNmzU)`L@^m#`i4H(yO$O$m*1uBDP0v!?JNDft8`P} zFQh4-%f|vH!T-$Mpi$qQiRCGDh4gej9gyr$mmB&qrT_dOIgCwn510c?%h_9h3$)&zu<7p#p`dapfOH8A0 z-7zJ+a5aH_TqL;7;7g43C;5Q$5FVXzF`2G@n}XyP_G)s?Gz9dLFN z6tPXsr}K6Nl~LOfFS3p{Z3k30(B$vfo${Dc)j$%Tt& zioi8?Cq$j_)5an>93D_@cgfZGaqX5Bf>wfR!6_6TyHsztat9i{8|PX+ZQm_N_&mhA z?15k9avHiv*3wu0NMG)e&ESM|X%8A*Ln(W)5T}2nHG5?aTyNTaFW=JF>a_5t9!tM` zFH^%ax0ed|9JHOHe~{I}C!P0O0k;v*u^DOhmobJO5t!feILMWbPhXsr&+~80DS2E! zhbyLUz-YCZTAs#2Zl;XW@(p+cUp@_|Xne%Yjm>Gq85ze9JR4ejM#kuwF8b*Vw%mED z^DFeNH)-v!SnBKa_ph)=U!-bhK@$I<_Ge{W?MZOqRsN++LE#~pTlyJx)%kb^8Dk?x z9Q*MMa?$j&I8kta;#pwu6^b|~>-}F^i9$9dbFhovK8Jvmi!}2boZT+c59hF7E>hTe zoKjb)=6UIDKE&t4%8DzTLT{%6$&W^y}U^86wAcq zyF-?~D#k)wr$fcE@&5&cVKxXK{7p^ht>3WKFVbhfVLdL=nctuhzv85Jzax&R!tb(* zKE%ZjkH=p0T{>6_pzIIEYg+KT`~zv+R{SBK;i;9qB=5&`p1dSq!rOJ1h@2mBE< z3hhqg@}a+gqigA%zhs+$ZQx&VR?+mU2*LREFB}hR>F>XAf^VmUD_}a;Qs*nOUBC|H zUx$&6rp7vBB3RFky=GVj0^Qt3d#-@&tfh#nAQC(1`K!RRZA4dry=$rHDinU(>F=wc z9BZl8HN<=Eq%qgj)3?{uw+q%&#lK~0bP-ZD^7ZY87rF?rbU60`xLv|S-ihUq3+->{ zz;;sB->Uxlzj5wtr+coer#G(4v~s(-YLTH>NbgER*#x%JmsW{${t`<$H3mu|*bK8V zw^RBJ)yDHTd~IZNPF$3@?kh3dDsjMHqS!8RT-t5K{)5wS4-NVU=ky*rWc@U~sh*}- zKUdVxgvXrK5r{b4*-_g*VV{!$6RJ6Dm)XmMXP-xOoT^WAVO$+G^?8Kmd>%^bvzoId zKwDjRGH74YoebKKbXBBKcczv3o?Dm?w@cN+C2qlKsLxFeXBuKE{~y}kJG|;*`5(_Y zNp?3mDJP^?lF)mR-sDXYP!LhEVFA4=0zuJxZKwh1MFa#U^dP+&YUl<;2rY)91Vjl{ z6i~P}K+5+u`!44M@jjp5AHOHhlk?s-+h%8HW@l$#2^0-bW>%oM7ZASZ$ZyCKBoYx> zDM;t+5hUJ4&!NKBr6gU$sUIfd0zYt0BC8(>adq;AHK4}@m8hA|A zei|qkO#VuNXEp)#q|{L3m3SGUYz1E!`)QNXkyp&fCqs1PWOZb0 z65vLL>g4vJ;*t1coSc`~hdF(7hH+KS!p^2ObWfhRn-4{_nJlf(BmC*mw9$k52n5Y^1Jr~QhAda;m zJ{6{OjCXPr#+!BK#aneAFmuRo8^+~0GY4K1FtM;>xQ6k8lOxB<@ms8w<8w2|?@kUl z!`F?UXXj`Xp<(okuxmLVW9BG{v0yAWbDWM~{G*1EI)C3tpw9{J+0|&BzpDFUG)8@~ znS8=YF0_-^b8@Ozmwv=+m(H=1hjQ`+GkKR+)OVK#QNL@!Nfa9;QY^yL*(5wqMF}Q6 zeWP@PwnS-=zeb6*82H8Ibm&Am(GClsVzg){Kn?gnrhl|fnHMeU$Ch%#3-J63&*KbD zqEPzY#bKYNoo@(23TEg$6ZdM!*M{=&*@T}_!Q~m<3-yDlNN(} zs_JfGY^9U&q9YnpJwX`A*e^lMLPk%bXj#?FSeG+4Ft#3psjDeBvVt6~%!Ne(#BucQ zwM2mU9IqOR?tm1Tq#NBnNr$GJp#x^9O0uY*tZC1N=|q8sRc_B;XM`PZp{tDxiWTle1vKDh0Gj+=WFDS zF~0&baXf*;1Z*mt;9U08H~lpEuh_>lhJZ}Z#$nntg5SD=X@7uzMG$`C0$U@(-9DQS z+|q-&1xDz79KfN!YH(lOOInQ zFc-slfDWfzDlC zcH&_dtX`jJFK*)p#iR54-Y3F24uRzSNgad-fqVd*;D+sc{RhMq*QNZ5oyC1F{Pyj_ zdC$^24~icVe4#70S8(yW9s-pq%@6A)+PhpA^B;bg8-I}&JR%-eL&lBtX5juNZJXkH ziqanyqv_0}Vnp_IJQsv`7tX3)^%%GCe54iX?=dc$AwICq@)+x3y@fOm_462?&Cww) z*Vki=HA9@%$7AI9a=zj*R`}9-dyG{po%&w(7~f_ucY-f@jIVt_U-TG1;juTXG5_)y zOMPHoKtpj4-%5MlWBlxEb}x@Hr4%QUW|w+;jH@uFw?fZ(4AxUwp=UkDpmEtc$nAax zx7P7UG7>rTw8!8lld%kYc#HxNDXY*^9%GoVAy0aYzdmvRKH)L0n<0kqIIQ7(IUn;F zC(Z6~T6d4JK&OReKZ-)WVSdD8tk#1U_OQqJ-q+S{=#>xHLrC)h>*_H+HGw_oG5++y z>Vio!VRgm?nbXhH^?=8i;lRocyWe9BH50jIojk@FALx!AV~GhG9w%=4K;P#v@=eg~ zJ;p-cY`60mM@-OMQCp9(4=?CgBiF`beBx9aW_XNJ?q0SzPpv)1ao=p;>oI2gvbORV zqs%H8XiK1wuZk8pYVhT3?lD&SLe22Fp$>&L^%%eE#)maQhfG*OfYsPz)Li5P-3S%= ziZ}EalYKpF;4$#(_=G1!T=p`&CxJeN06;ygirqsv6pT<^;PYu5l$&XFa0Wb%@g}S` z#^{(XqmZi>#;!nz@W^FYO-#~0h7eW*wQtjS#;~fx_4X-^;vA~xF|Pbz)sr0{4${yoekq}$|;!UF^(1LD%hQFs>j$hOc&%(ipSVtjt}D&UYne_ zhRfkqQG;;SjD~Q0qR061unuvV1f(s}(0P2~J;ro1jhh_jF}}zitp|yRKNhRP><<@= z!N6+p8fY|-&+HGUmGc;T^L056MR|;id-T+Ju`<4Ps>WHy-l0z8BTS2iowGRxx3#oE z#E~Dfxvybi9s|uYzSNDu^Km-y;3b_Ewi6gNW5*7iGsFZl;-pSfpaZjXy!0^A7(2hz z@xsit`#d)W7N~Fp7HF)~n8s!ob8uS-@vtUP<&d!Odz}qt1`J|5HRu89WJ&&v9wOfj zo3lrs6;tqY;aPDVjup;5hr|Q)U{CRw>p=eeo??az!8Xr}8VL4(9z3I~IRC>JM5;Ri zn?5#OJ`jP$a93LXBKDO76!ns*fNj3vOX35B*1Uwh|3Ln+mqcYZg05Gvs~*U2_KLWN z!S?SXYT;)_A90nfji&TPEo*6eUy%vL+u6RNvTHAe^%HgBNxfx1aSD;?Sr7yF(gRtd za_nzxT*>YlaX_*NOa${gTWNR}M6q79E(@FA?{qUult(DFznE%!esF2e58zSQ^TY1` zVh5Bl<6gza{wJM&747v{Z`&C-wB=wz!|6(XG36dKha|;JK|Ytdv8&f6#OP7B8WVJO3@Fp^d%%BZeXL!+*q9z+3R9X!(@y zTmgKNb)t|f*nFRl6yUutkF9s}aIE4SDg?uT6l{}oUJ1pr@4V7FXGd{r`Ig8^FMG_u zI#B;=aKf8v5XC3~~j%Em}r%Z+RGt1)mGLu$HFgKl(N%p)rMlA;e>3 zUi~4xQ`xbRFn@ya!~7{LgEW6gd<_r*c(p$9j(FD9ZUZ{%uTb(12A8TW2EnY_hMYHW zIAe^2vv}{~7(0$VCEJRp!2?CZMCkEhs+?kZ=D=swWZX$`dq>cmZvuDz@8rs~;7H&s z&Vy4t8w5T90!}gj;+c}b!4&p^Xap2(`+>-EZ{A0%J`gSW_sR#Nlk0ST^K8+>4Nt{1 zSTu8w45mYaf%(hm)?ncEGHU;!xaFEp?S_Cmo=;gr#QpeLKSZ=}&CmaRhzNG!bHO~Uus77?pFmQc~=$&C$tc5gl7#6GkX+a-V46Z{XhGU@@()8hC zeB7x|Kt8~Z8xi+~gQVO5rDxwd#t4xTHR>nOBNwIwR1|zWu0r-7A(~gv^Ud>;=?3nx zgE*jyzVKqA0%B?32+_e~AqKowy-H~#MSPiv)+5EUO)OAeep~8ppg0yH1dB~BzQlC! zh;R=m)v^JXecd`Vy2?K8s!)C7M*c08=*a1iX8$hYyH| ziywxLoEYO7;(}eW@eOSoCEC_sIdy~=SiJlAaQ-RXqkr~%&OhjTN(Wrl*n55mryVc( zkbkyNqtT*k#^KZEOC2zEbQ#A_ngPafY`biW`8+KcEtUr^EdG!Rrogq%t7AlB_Lvi3 zlmc;Y7*=#{<11VpMF^XPLTC`K%OeCz!fBUyUuGZnMkqpO_UjNfjBtb|&eu?|VMHL5 zJ5Pr&!<=*ZJ}~U;Ye}bFn4{A$&7AWL$+4i5i>dioQ7QRlWaH-Yr4KAswAvWUkBYis zqBIa*MypJbnWjl-`*6k1-JxpIGG_uZW5TTaqc2m_k3_0{we@9s7NJ_!)z+6C0Kt5{ z)CN!&0N^BVlwI4Rk3j3EP~k@+rLA?L)h@}GrQipqWY=VV3~DYeXPI*!#Fu4lcr2ph zaKgGf`Z864uOh(fI8Ib~NWb^X@A_ggT>~-$l3~Y=qt9d$pIzSGo^n7D1J`5hm_^Dn z(B)>;0Ui=`R}26}cWJ!<4ES-${PS_x>lV?4aiR|_fgk%=^i8x~OqfH-gNmDEIDwdx zf9zxIlUUhm0_YIVmg;>Xs=?7wk5ACyCHVtC5rObWP1%#MrkBx|lSIeNWm-aqliNLXKZ2F6Sv38P>dgp@p`JY=WQW}$ zZk6r&0E2s(uRQ3{h1S5BFc_;p(0&0Vz#Q=uTnn*N*0{ZU_=ySDG2qtcpqW@rj&*}y zl66bNdP>9slwf7Rkq#c6f~FBbylZT~$Lx*62YU*e<^*|1L1NJw9DN{B=x-HJfB+wo zQQRyH)nRHj3-b6klsyYv$G0?d7B+`nba+IF4U$nUaP(oi4()LY! z&CkywgV_%5*l2nw7tC8P8k>tb+)Mj%MT_$OAb6=C_V3ZYnN($t=v?3OHjoLbjFGeb z4P=HlcIRRV7q3Saz-WvqjGztQ)m6w_XgwHbhPm#JsN8V0fQeC@KaNH5Vi>y?3EcWZ{BpG0xt zoq=~hi(C~EhUJX~4)d6WZqI`hbt*ZQO23Jb^xC{MS>2{H`jijNH5amq>P~Z=Zh34ls{!YE?^?Ke<74>Bl3S=C?>hxbKHEu8%J}N00oe?L{wqZhFUo? zfg+Y->20UFOU0v{4`;pgD1WJFotO(Q%g3zzjo9Rt^p*jCrAx6TS z;?fE+lbQappNS6XyLrrUX4(SR5SUuw5`!DoFg*3R(d*pIeY9Gn#7u-DwvHTXCOt6=SQIq-JgD%D&eABKD-!r^3k)B=yTDg%Xt`V<7 z7g%$>XwhIl8%DD0Wy!3)pD7}HHT}COB0qTW9?e)UUaM)9G);pTk-3CMVYCI=O!HRj zR(`}f(WzW6{26GEg)j}|opaG5dS#vH3e%7c>o5$a+uySNp&!?Y+Lbv4G+!@QFo)*# z{l9mwWxi+%QutOrVD6(`aQGJL8yWg5AA+IP>!obs$`+XapL=bU{QtSv(HlkWj{mn_ zJ2S3dQ-cuy_i-7u5hUO|E!ZgPdT08%j6D_LT|?(Kii*(LdNzsXX<)r9CRe+C1dFLT zFOVUlHXf?|*Cz2KsLj?*khk;baOkci`+Ge+xasqTTW4 z#$6hUCyPN51yY!Q=dZ**pw9tX>g6kBI2E_rE0}J_@VO^Ezu+gJn)|A&aUhxj4(BPz zWrC>%h_=^&)jD(%C0bNRPF&RLo6Wbg$1-n&;LWa8G zz#VjT5KS)-jgxR*k1oQF7LWcyP~vOl<{;$K#R8EMmdAIAJsM69#cUH#gGn2(O+;tI zp}5eGyl|g=4bOo>0^Q_>FAL&6=+8C2&&|HieL=7#<;(1Ftd3&1qR*c@@EN8*tuF6C zdwIwvK@?_y^YEMg1nwT^@&`5^yf`KFi@xA`U3|44gVroy9~Z+y#GviPCkCr|z;6CH zejc`h2QqB)h-&?=O`L~=de~dq)^=An_VTe0ukhih>B6r{yr%71RT-hI-IYoc|j!*8; zZ@PXI!jxR*B!D!dfYkx2+MkQ{{h zw-DTgF_W9oTsH%UtsKp*faSXVIVdpO{zg>5WCn_tRu=HDyIH_8{H7=C5n0MnsAmaB zCF!X7{F$gf=kX_8ZD5e+@@KsM{FFcA^yeJ@jMbm;z=UD>gl~ao~EZEIcm>_7G8Og-7zTJtG;3i?tgJVXO|}f_gkQ>&bHC z@;)~pU^yDa-P?p;gxzZx0SaL10(!;A)ZQtY;Oq#=WP;;v&2*(81hjb?d{^3P4Ky7k z;Hy>VJ&(Lvp~#NPgi#vOhqrE?fpk$Vup|U?6yk1OjbT|YG=c>PFgAmn@{QM<@MLHL zhM9gwpfQiC?G$O*dyhiZ%$I+mdk8cR?u70EE*kV87Kbuuqc{{=phoWX9Kvq_evJaS zggUzxqMCld6x2qu8Nyf}3U0*ASIGGak?$|gCvnrmmEq>bRSPT*%}i~>j%6bsh>icL3%$uql=DK(epd@z6q=EWM3C7MWaYW~zctY^mS+2`GA#u|lnrK1 zdnMETgs<~ETJ03Y{1k^m$AjOJP@MVe{MzF6mX17(Rx(2rgbhpk0|DmgS*}Mon~~g!@p>RHYg`$mzei$nW@=Q*L1THJgHi>J98VnzMTIBrCK#|7X0FAr zQP3he_bU7ozyx~dX*EX}MAinJ1fe>>dl0o4rJ*b@xjn1si$Z8Sis(|IcqYtrN5mom zIQ_?Vi>*x#1S2qm*A;gU@%XenqnZ(G5U~cz-m2yX>(=hj3Jy@`J)%le#{+3DTOuw) zHXZ`^G7dS+K9)3CZ^?t1q7@_OWfxkuM?4t4&mV3M2s}iA@V%mjcZUTj5XEql?f`Y% z3t9Rg_1`NR#+vfMAY6ym!VYf4vpe1Od&MBOY;3j!Q6dK9ri;${DxlLFE+xYt$Gnc zCG>C+bPPvnR1pqkN@#l#xb0(fqewgfrn~!BuxU6>>%W3U+;{ZHS0JJn7en(~Szlpa zI~5O>?@pgO&u4ORL z#f@?|pKNG6UIby~6r~>0#abN_Xy176xU*mXcIW_-U^iC~hgId<{*p;KphBK}OU*imBImLb> zriESPso?<$#4Y+w--y?7Z@%^s@kmYECKrv%lVED`yG&+;f;wUa_${?SEGSxj1a;@r z;Uf^>x6{odqFpq+-kPn~(j31tI)NT75t%jak`Tzgbz(rN0@u6v1wv33JpweRM5K27 z2b9FItdXv<^G_Uvn~2I!H1qJV7izn99z|V!zBnp6y3ow7$8b=I#rW1SQ8U^`(m)Ug z2tl}M^)szIChn>Di#>-Rg1o-d*%JzUGGQxX@K85-&rqkpb>Fu*2D(CjehX!}t}zfV=%ySOT@cylxG3)~T}Wf_m@e##mK_(ZV3B{} zxHu2;dH6eMDnK-=elM2R9uN7>p@$<8&{PlnKE?xcIKw}A(js!55RW;-}@V_N=1e0-(oUr1>~}43HN=N4Fs{nVh3G>;pEpbj0OBk zO(@>r7*i@bs2Lmz;kP_WA$a~bST_q#e_(c9c(%L7Nl`xLreD2!(+%|gh8N64?FxV1 zLg{cX9^tT>&yLTOtt7!&E5CAZQY5MoxAh6gH7Y(S>ZeSH#f%YgM=`Vx`TsEUFXbFA zH0$0|xIFSFJ#`A2{hRdeDe-vatGr;*wGjXA74n9SH+YTDxk>yu$)Bfir2H4vJT2NP zC-SY+B2BSr5|rZCXy$2=tbT*{SKZfw(;~6)m0)HdoKef+QG+CDRP*{q0i=S3y;9ap zl;TyBbiBdsJ%baVebnv@cC~BN=ZttN^1AH=oOv=_20e5}Jb(t3KdamI=vkas-=Je> zMP|iCKRDQjgLDKm_66WP7&Tml6e>_d`Z-w5-J(I~pcuVHUz`&!h8TPdfr;uu^?ncw z!frEtV&nFB?`=B$gVJS-Jng_RX!B7|GaoNSElflGzJUU*?GslnA^!^ctsfqAT!meO~p$_(HPbEwb1H3~7ZTU&0RvN;u!GlZQ zhdnpId<6cC^bQULhi8&bo%QxlBEd71XADM{0p2T=c|p|eg!gmJjeyrXazm|eQ4+tb z+(kIU(T>mlDGyD@(|~2*@4O)D$!$;o;$)u!FG3;oGgZ9^_3%y7 z%o-9-YyP<*yneYQ;P13f3QlXmpXnRD^xumxAvv5s>XO*uMja1c7HOcR`U#Qk{DjCl zIM3{OMWne`E9z7dT#?3J5u*?n_h0cvBBlr!{wBCvtSZ-=@Lz<(YWTP7O8@;Y6ubG9 z_AAKQ5xVzRk(j7AzyQofI&R3pa{b@>5T>v9NdACdh07)I^qC89g1`M6`n;89{swjW z5vuz;$m20w9fAJ&AWi-q196OY{0{s&NEQDOLlIj12M)ugQ{_KJ2NdZ4r|1+?2$RD1#dbZ0ib_ZJMS zf2PHML7bUFNB82bG)BZryDqtBl_rum=vQ;I&@R7@=@MZyl0w2 z4R7Mi;V2FVVB>R^j^2bJ_%RB*C7NU`Tjwmv0An5k=8}Yc6@cS-{vdU|Em9)l*>X6K z`vkq)S^D^v4IzP!-4c)fBN&wPfvLu}sJF#{$U7>fYqv#{B-7iFHf@Gcv91b=YwX+0 zCO;r6+*xMw`?6BxB>%>bT{n59tR9)`AD6Rk@=vl_;<7^jP9k7+GLl>?;W*`9myE}t zKkkwbV36jzWPdcYoLe@tP$3x4f_HbzdU%3wgj;q-*28X@fz#VR+>+;~LZHOs5%fr) zwC4wVk^lUB5h&YWengNQjtV{slI<|n2ZQ93sk`u2KoE;m`k3&}ap@Q=YXhs^3YHDb z=f-ofVet$cr47My!QJQj4Iu}@NA52|Cdbdx-uOVNf_Ra`_ax}3@-CzdDU(ypL>9xC zeY<1a@}HFQtw4;!fl%2M%zcJO4)k&1^E|RH(!Tb{IuRxxta(81s8A{wChq}q-4`aC zOQ^!ozs~e=UP=PxhRJ9cSFH(?6)Sv))8;xZ+#WVQ*H^Xq6ahrV|Hd}eEpR;^4TELt zBt4Lc4v6S#@`huAj#75GY^2sthWp}RJf^rMT$WF}fh`U_4ECOe5=Ha6tYpqKwZ&8D zdbn&1lk55svP#Taz~Zw+0VKCF?4zs*iF0-OC_>hPT(rsj`4K;SvV#WWND6yg5Wic+ zf?M@SIgkang^}_JHv}A)SJuzC%mN@QSe`&e6OGw}AOgVUbm{$?9`(wq%q>Dc6*sUNHSBhN5c^HbK@n`5tIT@$sm0F?5 zXc?D1do)=0C3a<>BmYmNhB&EBI2C-3z83*= zH?BuHsjWE`dz$qcvoWn0$zd5zdPh#j;sH4V#djI=xWkcX^q0{xUFmJYyTn7@or$Rw z8Y64QPYnbc5`u%ROz<9F2etu9|?LaPNreI{ZE{%lX__>2H3IE z2+^;Qx^T3Plin{}v@K3nF29n;!Bx$T2Wud0F#h9^Cxj1cywf0Z(x3`hn-i%)yc_^K zwFU8@LtoR8cu?+RR3SlD$GUwmLB@$m5X&L8^+}Lb<4#yj9Pj1{Mcddk@enOaknOGh zSFrkz3#@3BNn}mwAABktVWa(P%nn^Jq=936rZK7DY6^* zDQ~KbWl69~s(iXvuEvOc;N4loFmi+Ycn|P6>8=2(#zuG=a2E$7*;KzB$0utkOE{_& zH$%}Iz8~sckNkC9XYeGA4;Y8CgW@VuFUcD zbtn-2c|m$ZjH{@RrBH}pJdJonaeSI=mynMJ)Cw3Y+QZ-resmxPLL%S=a4`*=vf00rB_JebpuVBJE#YRs{*C z>ar6g$dlE)e!*0JQ zWt<1&rROMzdvHS^i5MTS)+hjvr{Uh`nHty$R#5etvITD6y<8J)VhPQzDeK3b#sNhg z*NnhSe$NKLtGaUn@f!UPHNkSNqmWuMIdNSGXSwKRH|=O;ATzQdu=AZtEo%YcO6b*E zG7~F&VlCMhiqKoNWYv)4bEkkw3S?u<+VVTsPZVB9-XC?7O%8C?l84lkvwGKosBo5+ z*O9G&?-%RHkE2Xn3d9Z2BxDMNa5uKDd>-x{n=&pWUG*0 zkD4JjjqpZfy8rUfQg1{Dev#8ODuf{+zsTu|il`TIk=+iVq??*WMWj@^VZZu`?&ED5 zh`fIUYrAiRv=|I#dl?OkifH5hHJCn+is%I?I-^`fTUQRfST14$R`8$YBAP&Csv8|q z0Rq&v2C{yYYwnETF!-H-FKHM(nLR4U^P!(BWDXl=-eBsNaT)kfijLq{ZU;1!_d>0? zvZ2gKn#JpvQB#93v={)KRVeg+Oho<2zn~{zx4`%pV_e%sTVo>1r<~!(bhvL8j~pJ! z3kzdy{I;8VH^Peil|F6+1e{HuHIfPH67EFdjpJ+dRU_F>Ex>IWtwBs~jK@vKu;Px@ zZ><~4iq)Zg&=p?B<_NJHuF|ngwEj-ZcBrBBJVcW~Xd1^gmI<|XvV6k$hbauwZ`vX$ z7=wb>o_Nvep#WvUHiI7ZjEg7_`?*1JdKfALlf``r|3iz z85dioUj3{x*Bjatqc@%IZweaoDfMrvi_K{YjGs>5bL1Qvn-EdojhX*EA);p3$Ixjr z7V1X#Z>A;W$V4<`F4alY4LRCOmRB}zO=&JGFlH@FjHrf9s%vvl?^Tr5T&4hiR1(@c zk5)9-m7Z)alR(mMGzU`*q{&N)=a_xhasJJN{gr) za_}%XWR2Z}(;{j@%vq5Zk;aWEN{dL0S|EAT(=T#ZBY!mwy;(xDTZ2?BrTfd9fZs14 zfeZcyloBOpEhB!Y#f^Puse=K2YaDrx#Z=>g%zEFIlD_t~X z4bJ-2`xxu%9=h_rt1|!2e#e!Hc^%wFR^edXZ(}OBu2WGPFw@uQQX5$j6trAhSs@A9 zpdjyXoKs+Ufzw|mBF?dSajM154rH9O;B<*Y~E4hbC zy3|&-#b#NrolH!bJrN54+G#fgb$xQh`Z+BPL4W;HJ9!TmnBGoSPW7`J<2>4KObdc= zRxauLc3|B%@Ev|$O|rebCuqOS2%xC;GLw3?m(_z#1e(DN`mDW-mJ4G%$K37^nlV4H zGsWH~FN2pA9l!%$qi!8!j9S7^hx6*o>VT2>ktTKkiM>f{JID+V-|sQE|HX8&gKXz} zxF_TR-bomzrT1JI=D<3Q3&x&|C^(Pm2sGPDFL#vXo57(OvU;FX<{wy}@!Z{67Au4= z|LYAJSE#_1!nGU~Fqm6(w4>}5K2PDCOjqE7YP>@y`5w@DM<>}U8OcA}GQ&4xA;T{!CRl3VGaS5Id4KLMm!Nt(?-DO<0??HO3u~-00 zI^Lt7#m^eZfEk!Pz=Lgyb`3tKyL{LUhm*f|M_chu+iUyN66oF@5n=8x6s6BWWuMcW z$3TOw($>dhi)5cQ0^i!;>BM!8?Keky;eTxNF&?}?7zA@hhQSZa7}-$#v&r}hkur$RKY{5xO2(72hZ_>`tS7;(j-yLY$~x}f ziYe_W>@W+c)l-_JJ^qxe<-S@>!=I8hYJ=wDVk5W}#P!4|n1xdI?VVCl&L zI0fO2!vLfcPsvJAEMnS-R_&>;X-`}$Hc8u)#Yo3MKM&{kC6K4K(tF<2XFY7l!BkfP3Jfz^O2XGtBUXqdA=G3&WQf^yjna z@yq?)-DTATpK*GmpBl z7GV&>_@fVRagzyX553fCs`~=O%++`+QMPEfn&ELiOqtGCRp*1WvYPK*cu7ly0epc% z?Q5iO0#xT8vCDM=?6pph-BqUu z!k3)@KZ(HUb&Ziu8AJ?n0w!Xjf*dCb>EmZpi+`xJ|>YsYA>d0>RY1w}{Ed2t9z)sP8OZ|Tg2uK-p{Fkxl?E z1;#o7I20J~1mI6#k`sVCfgC3QZvwtRHoOV=LU1VH3t+eP1>jC#h64fK1ah4KdH?i=hQh0QW+7I00M>Ep!5~ z=iBE5VES3Sm>Rq;54+EtrO-Fzr*g9FE`(pbAWNPKafvB z$iM!9d<>y(+45P0^0MWJYCHC6XpCJ`vSl)L9|ZluC-l}JES$Bpa*%uh*n{1${OYH0t1BK)1d_KlFXK@%w()?|YTs_fLM`nL~VKpYnaD>1_`_n)!BXX|5l= z!E!qfx3WWIY|q>~;ukq#layJ-^PFf+evI`_*raGywgM;G%vP97m4;$4CR2x@vLP${ zhC*qgyIdKUQob1~>%`g8bs=8YE9ONGOJ{L?2LC&Vq1H)w__}Tn$IL3cFZ5}Q=D=jGVwgSm15n!v2kPjp6{SmT8 zH7iYTG9WQ9P7Zn6A&dTf+z zhR}pj;76y?ic#_nm^~q zKu55P!p7owcL%i|D=P%=g7IW~DnCr7(eSZAFr3GXl?{XzEU<}>#lAR|u8ox^+{-+q zg@o{NvTCDQ!rj{4n%$G7um%U#63pbe)8VBg$aHjqOM$*WE!jfl*LR#ut}*x=yrKky zM}a4mAk(c&Fk~lqQVB9$(Xg9zopyv)j+2!$wSjpcpJBnk991=P_Xa?SOUCgn(C0)z zy?bq(e5RIPKJ&htZkd*K|5hZ6hI|Yp9Yt$DmLt){R^#PdXfo?gkaelt1SmfAcSop^ z-kBgDvQw^4kg=gVOs*{es%D+N(?l5)qFFf%c`m&)QFb&Z9>F=3KM~djMRe~cvPmEp z!ACPQT$2`jA{+ZeTd74`mS}``F3u`!(N;St77%SOwS;%)t&}zi^Yl5j;vb@CCqeHy zg5IAZE7OWr!9x)B;ACIa^m~KfGjlbeSEtBuGwT{?fa;SR=UPwONVaiP5Vh_CU*Rb^ zpwdJ&bI``^^l}c!`FbkOk;6dMADasLzmNt`l}#{g8>h

    eTO0pf$$u511=sNKKOu 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%0DSi)?WNQ6&V1O97dhZzCtT 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�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%0Al(MhU@VuDihb96{ay`9>o~GW#a*A;<7 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

    =(#0x% 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%0AVw&UoB!oO4fso0AuO$Y7tkf%u8#cSF^2b6X9Kd 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

    DQ7pTho;Li2O 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 z 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
    jhz?6fC_ZZg@0~j8AG++hnCQ<+~ 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%0AvBTSnMv95J2NTAe5aA}i|;g29{PM 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%0D-(b22ei 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
    @G=aQ;>Kz1)@HPKn$%F`GbyHNB;X4ka^@ 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 N|ILloUKf7+{{hE>M<4(I From b8a96baab887e4dcd54d49fb1bbc8294af47392e Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 18 Feb 2023 04:10:50 +0000 Subject: [PATCH 083/122] Improve multi-module error messages - Fix assertion failure if AstGen failed on a multi-module file - Cap number of per-error reference notes and total multi-module errors each at 5 - Always put "root of package" reference notes first Resolves: #14499 --- src/Compilation.zig | 162 +++++++++++++++++++++++++++++--------------- src/Module.zig | 7 +- 2 files changed, 111 insertions(+), 58 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 9b054fbe62..717a396870 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2773,6 +2773,111 @@ fn emitOthers(comp: *Compilation) void { } } +fn reportMultiModuleErrors(mod: *Module) !void { + // Some cases can give you a whole bunch of multi-module errors, which it's not helpful to + // print all of, so we'll cap the number of these to emit. + var num_errors: u32 = 0; + const max_errors = 5; + // Attach the "some omitted" note to the final error message + var last_err: ?*Module.ErrorMsg = null; + + for (mod.import_table.values()) |file| { + if (!file.multi_pkg) continue; + + num_errors += 1; + if (num_errors > max_errors) continue; + + const err = err_blk: { + // Like with errors, let's cap the number of notes to prevent a huge error spew. + const max_notes = 5; + const omitted = file.references.items.len -| max_notes; + const num_notes = file.references.items.len - omitted; + + const notes = try mod.gpa.alloc(Module.ErrorMsg, if (omitted > 0) num_notes + 1 else num_notes); + errdefer mod.gpa.free(notes); + + for (notes[0..num_notes], file.references.items[0..num_notes], 0..) |*note, ref, i| { + errdefer for (notes[0..i]) |*n| n.deinit(mod.gpa); + note.* = switch (ref) { + .import => |loc| blk: { + const name = try loc.file_scope.pkg.getName(mod.gpa, mod.*); + defer mod.gpa.free(name); + break :blk try Module.ErrorMsg.init( + mod.gpa, + loc, + "imported from module {s}", + .{name}, + ); + }, + .root => |pkg| blk: { + const name = try pkg.getName(mod.gpa, mod.*); + defer mod.gpa.free(name); + break :blk try Module.ErrorMsg.init( + mod.gpa, + .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, + "root of module {s}", + .{name}, + ); + }, + }; + } + errdefer for (notes[0..num_notes]) |*n| n.deinit(mod.gpa); + + if (omitted > 0) { + notes[num_notes] = try Module.ErrorMsg.init( + mod.gpa, + .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, + "{} more references omitted", + .{omitted}, + ); + } + errdefer if (omitted > 0) notes[num_notes].deinit(mod.gpa); + + const err = try Module.ErrorMsg.create( + mod.gpa, + .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, + "file exists in multiple modules", + .{}, + ); + err.notes = notes; + break :err_blk err; + }; + errdefer err.destroy(mod.gpa); + try mod.failed_files.putNoClobber(mod.gpa, file, err); + last_err = err; + } + + // If we omitted any errors, add a note saying that + if (num_errors > max_errors) { + const err = last_err.?; + + // There isn't really any meaningful place to put this note, so just attach it to the + // last failed file + var note = try Module.ErrorMsg.init( + mod.gpa, + err.src_loc, + "{} more errors omitted", + .{num_errors - max_errors}, + ); + errdefer note.deinit(mod.gpa); + + const i = err.notes.len; + err.notes = try mod.gpa.realloc(err.notes, i + 1); + err.notes[i] = note; + } + + // Now that we've reported the errors, we need to deal with + // dependencies. Any file referenced by a multi_pkg file should also be + // marked multi_pkg and have its status set to astgen_failure, as it's + // ambiguous which package they should be analyzed as a part of. We need + // to add this flag after reporting the errors however, as otherwise + // we'd get an error for every single downstream file, which wouldn't be + // very useful. + for (mod.import_table.values()) |file| { + if (file.multi_pkg) file.recursiveMarkMultiPkg(mod); + } +} + /// Having the file open for writing is problematic as far as executing the /// binary is concerned. This will remove the write flag, or close the file, /// or whatever is needed so that it can be executed. @@ -3099,62 +3204,7 @@ pub fn performAllTheWork( } if (comp.bin_file.options.module) |mod| { - for (mod.import_table.values()) |file| { - if (!file.multi_pkg) continue; - const err = err_blk: { - const notes = try mod.gpa.alloc(Module.ErrorMsg, file.references.items.len); - errdefer mod.gpa.free(notes); - - for (notes, 0..) |*note, i| { - errdefer for (notes[0..i]) |*n| n.deinit(mod.gpa); - note.* = switch (file.references.items[i]) { - .import => |loc| blk: { - const name = try loc.file_scope.pkg.getName(mod.gpa, mod.*); - defer mod.gpa.free(name); - break :blk try Module.ErrorMsg.init( - mod.gpa, - loc, - "imported from package {s}", - .{name}, - ); - }, - .root => |pkg| blk: { - const name = try pkg.getName(mod.gpa, mod.*); - defer mod.gpa.free(name); - break :blk try Module.ErrorMsg.init( - mod.gpa, - .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, - "root of package {s}", - .{name}, - ); - }, - }; - } - errdefer for (notes) |*n| n.deinit(mod.gpa); - - const err = try Module.ErrorMsg.create( - mod.gpa, - .{ .file_scope = file, .parent_decl_node = 0, .lazy = .entire_file }, - "file exists in multiple packages", - .{}, - ); - err.notes = notes; - break :err_blk err; - }; - errdefer err.destroy(mod.gpa); - try mod.failed_files.putNoClobber(mod.gpa, file, err); - } - - // Now that we've reported the errors, we need to deal with - // dependencies. Any file referenced by a multi_pkg file should also be - // marked multi_pkg and have its status set to astgen_failure, as it's - // ambiguous which package they should be analyzed as a part of. We need - // to add this flag after reporting the errors however, as otherwise - // we'd get an error for every single downstream file, which wouldn't be - // very useful. - for (mod.import_table.values()) |file| { - if (file.multi_pkg) file.recursiveMarkMultiPkg(mod); - } + try reportMultiModuleErrors(mod); } { diff --git a/src/Module.zig b/src/Module.zig index 2afe3f5df1..a2502d36d3 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1946,7 +1946,7 @@ pub const File = struct { prev_zir: ?*Zir = null, /// A single reference to a file. - const Reference = union(enum) { + pub const Reference = union(enum) { /// The file is imported directly (i.e. not as a package) with @import. import: SrcLoc, /// The file is the root of a package. @@ -2144,7 +2144,10 @@ pub const File = struct { file.multi_pkg = true; file.status = .astgen_failure; - std.debug.assert(file.zir_loaded); + // We can only mark children as failed if the ZIR is loaded, which may not + // be the case if there were other astgen failures in this file + if (!file.zir_loaded) return; + const imports_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; if (imports_index == 0) return; const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); From f94cbab3acc3b31464f45872c1f700874eecb23e Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 18 Feb 2023 06:28:47 +0000 Subject: [PATCH 084/122] Add test coverage for some module structures --- src/test.zig | 39 ++++++++++++++++++- test/compile_errors.zig | 22 +++++++++++ test/stage2/nvptx.zig | 1 + test/standalone.zig | 6 +++ test/standalone/dep_diamond/bar.zig | 1 + test/standalone/dep_diamond/build.zig | 28 +++++++++++++ test/standalone/dep_diamond/foo.zig | 1 + test/standalone/dep_diamond/shared.zig | 1 + test/standalone/dep_diamond/test.zig | 7 ++++ .../standalone/dep_mutually_recursive/bar.zig | 6 +++ .../dep_mutually_recursive/build.zig | 26 +++++++++++++ .../standalone/dep_mutually_recursive/foo.zig | 6 +++ .../dep_mutually_recursive/test.zig | 7 ++++ test/standalone/dep_recursive/build.zig | 22 +++++++++++ test/standalone/dep_recursive/foo.zig | 6 +++ test/standalone/dep_recursive/test.zig | 8 ++++ test/standalone/dep_shared_builtin/build.zig | 19 +++++++++ test/standalone/dep_shared_builtin/foo.zig | 3 ++ test/standalone/dep_shared_builtin/test.zig | 11 ++++++ test/standalone/dep_triangle/build.zig | 25 ++++++++++++ test/standalone/dep_triangle/foo.zig | 1 + test/standalone/dep_triangle/shared.zig | 1 + test/standalone/dep_triangle/test.zig | 7 ++++ 23 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 test/standalone/dep_diamond/bar.zig create mode 100644 test/standalone/dep_diamond/build.zig create mode 100644 test/standalone/dep_diamond/foo.zig create mode 100644 test/standalone/dep_diamond/shared.zig create mode 100644 test/standalone/dep_diamond/test.zig create mode 100644 test/standalone/dep_mutually_recursive/bar.zig create mode 100644 test/standalone/dep_mutually_recursive/build.zig create mode 100644 test/standalone/dep_mutually_recursive/foo.zig create mode 100644 test/standalone/dep_mutually_recursive/test.zig create mode 100644 test/standalone/dep_recursive/build.zig create mode 100644 test/standalone/dep_recursive/foo.zig create mode 100644 test/standalone/dep_recursive/test.zig create mode 100644 test/standalone/dep_shared_builtin/build.zig create mode 100644 test/standalone/dep_shared_builtin/foo.zig create mode 100644 test/standalone/dep_shared_builtin/test.zig create mode 100644 test/standalone/dep_triangle/build.zig create mode 100644 test/standalone/dep_triangle/foo.zig create mode 100644 test/standalone/dep_triangle/shared.zig create mode 100644 test/standalone/dep_triangle/test.zig diff --git a/src/test.zig b/src/test.zig index bf1b0e912a..61cdb705e3 100644 --- a/src/test.zig +++ b/src/test.zig @@ -583,6 +583,11 @@ pub const TestContext = struct { path: []const u8, }; + pub const DepModule = struct { + name: []const u8, + path: []const u8, + }; + pub const Backend = enum { stage1, stage2, @@ -611,6 +616,7 @@ pub const TestContext = struct { link_libc: bool = false, files: std.ArrayList(File), + deps: std.ArrayList(DepModule), result: anyerror!void = {}, @@ -618,6 +624,13 @@ pub const TestContext = struct { case.files.append(.{ .path = name, .src = src }) catch @panic("out of memory"); } + pub fn addDepModule(case: *Case, name: []const u8, path: []const u8) void { + case.deps.append(.{ + .name = name, + .path = path, + }) catch @panic("out of memory"); + } + /// Adds a subcase in which the module is updated with `src`, and a C /// header is generated. pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void { @@ -767,6 +780,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, .files = std.ArrayList(File).init(ctx.arena), + .deps = std.ArrayList(DepModule).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -787,6 +801,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, .files = std.ArrayList(File).init(ctx.arena), + .deps = std.ArrayList(DepModule).init(ctx.arena), .link_libc = true, }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; @@ -801,6 +816,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, .files = std.ArrayList(File).init(ctx.arena), + .deps = std.ArrayList(DepModule).init(ctx.arena), .backend = .llvm, .link_libc = true, }) catch @panic("out of memory"); @@ -818,6 +834,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Obj, .files = std.ArrayList(File).init(ctx.arena), + .deps = std.ArrayList(DepModule).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -834,6 +851,7 @@ pub const TestContext = struct { .output_mode = .Exe, .is_test = true, .files = std.ArrayList(File).init(ctx.arena), + .deps = std.ArrayList(DepModule).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -858,6 +876,7 @@ pub const TestContext = struct { .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Obj, .files = std.ArrayList(File).init(ctx.arena), + .deps = std.ArrayList(DepModule).init(ctx.arena), }) catch @panic("out of memory"); return &ctx.cases.items[ctx.cases.items.len - 1]; } @@ -1145,6 +1164,7 @@ pub const TestContext = struct { .output_mode = output_mode, .link_libc = backend == .llvm, .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), + .deps = std.ArrayList(DepModule).init(ctx.cases.allocator), }); try cases.append(next); } @@ -1498,7 +1518,24 @@ pub const TestContext = struct { .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, .root_src_path = tmp_src_path, }; - defer main_pkg.table.deinit(allocator); + defer { + var it = main_pkg.table.iterator(); + while (it.next()) |kv| { + allocator.free(kv.key_ptr.*); + kv.value_ptr.*.destroy(allocator); + } + main_pkg.table.deinit(allocator); + } + + for (case.deps.items) |dep| { + var pkg = try Package.create( + allocator, + tmp_dir_path, + dep.path, + ); + errdefer pkg.destroy(allocator); + try main_pkg.add(allocator, dep.name, pkg); + } const bin_name = try std.zig.binNameAlloc(arena, .{ .root_name = "test_case", diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 21c8822eb3..e0b78b3000 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -288,4 +288,26 @@ pub fn addCases(ctx: *TestContext) !void { //, &[_][]const u8{ // "tmp.zig:4:1: error: unable to inline function", //}); + + { + const case = ctx.obj("file in multiple modules", .{}); + case.backend = .stage2; + + case.addSourceFile("foo.zig", + \\const dummy = 0; + ); + + case.addDepModule("foo", "foo.zig"); + + case.addError( + \\comptime { + \\ _ = @import("foo"); + \\ _ = @import("foo.zig"); + \\} + , &[_][]const u8{ + ":1:1: error: file exists in multiple modules", + ":1:1: note: root of module root.foo", + ":3:17: note: imported from module root", + }); + } } diff --git a/test/stage2/nvptx.zig b/test/stage2/nvptx.zig index c87f32add0..f08aa9fca4 100644 --- a/test/stage2/nvptx.zig +++ b/test/stage2/nvptx.zig @@ -97,6 +97,7 @@ pub fn addPtx( .updates = std.ArrayList(TestContext.Update).init(ctx.cases.allocator), .output_mode = .Obj, .files = std.ArrayList(TestContext.File).init(ctx.cases.allocator), + .deps = std.ArrayList(TestContext.DepModule).init(ctx.cases.allocator), .link_libc = false, .backend = .llvm, // Bug in Debug mode diff --git a/test/standalone.zig b/test/standalone.zig index ed0d2c2d30..965139235c 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -107,4 +107,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void { cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{}); cases.addBuildFile("test/standalone/issue_12588/build.zig", .{}); cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{}); + + cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{}); + cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{}); + cases.addBuildFile("test/standalone/dep_recursive/build.zig", .{}); + cases.addBuildFile("test/standalone/dep_mutually_recursive/build.zig", .{}); + cases.addBuildFile("test/standalone/dep_shared_builtin/build.zig", .{}); } diff --git a/test/standalone/dep_diamond/bar.zig b/test/standalone/dep_diamond/bar.zig new file mode 100644 index 0000000000..772d21dd58 --- /dev/null +++ b/test/standalone/dep_diamond/bar.zig @@ -0,0 +1 @@ +pub const shared = @import("shared"); diff --git a/test/standalone/dep_diamond/build.zig b/test/standalone/dep_diamond/build.zig new file mode 100644 index 0000000000..b60f898f0b --- /dev/null +++ b/test/standalone/dep_diamond/build.zig @@ -0,0 +1,28 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const optimize = b.standardOptimizeOption(.{}); + + const shared = b.createModule(.{ + .source_file = .{ .path = "shared.zig" }, + }); + + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = .{ .path = "test.zig" }, + .optimize = optimize, + }); + exe.addAnonymousModule("foo", .{ + .source_file = .{ .path = "foo.zig" }, + .dependencies = &.{.{ .name = "shared", .module = shared }}, + }); + exe.addAnonymousModule("bar", .{ + .source_file = .{ .path = "bar.zig" }, + .dependencies = &.{.{ .name = "shared", .module = shared }}, + }); + + const run = exe.run(); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&run.step); +} diff --git a/test/standalone/dep_diamond/foo.zig b/test/standalone/dep_diamond/foo.zig new file mode 100644 index 0000000000..772d21dd58 --- /dev/null +++ b/test/standalone/dep_diamond/foo.zig @@ -0,0 +1 @@ +pub const shared = @import("shared"); diff --git a/test/standalone/dep_diamond/shared.zig b/test/standalone/dep_diamond/shared.zig new file mode 100644 index 0000000000..3d771dbba8 --- /dev/null +++ b/test/standalone/dep_diamond/shared.zig @@ -0,0 +1 @@ +// (empty) diff --git a/test/standalone/dep_diamond/test.zig b/test/standalone/dep_diamond/test.zig new file mode 100644 index 0000000000..227f442943 --- /dev/null +++ b/test/standalone/dep_diamond/test.zig @@ -0,0 +1,7 @@ +const foo = @import("foo"); +const bar = @import("bar"); +const assert = @import("std").debug.assert; + +pub fn main() void { + assert(foo.shared == bar.shared); +} diff --git a/test/standalone/dep_mutually_recursive/bar.zig b/test/standalone/dep_mutually_recursive/bar.zig new file mode 100644 index 0000000000..68957b69e4 --- /dev/null +++ b/test/standalone/dep_mutually_recursive/bar.zig @@ -0,0 +1,6 @@ +const assert = @import("std").debug.assert; +pub const foo = @import("foo"); + +comptime { + assert(foo.bar == @This()); +} diff --git a/test/standalone/dep_mutually_recursive/build.zig b/test/standalone/dep_mutually_recursive/build.zig new file mode 100644 index 0000000000..0123646a9a --- /dev/null +++ b/test/standalone/dep_mutually_recursive/build.zig @@ -0,0 +1,26 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const optimize = b.standardOptimizeOption(.{}); + + const foo = b.createModule(.{ + .source_file = .{ .path = "foo.zig" }, + }); + const bar = b.createModule(.{ + .source_file = .{ .path = "bar.zig" }, + }); + foo.dependencies.put("bar", bar) catch @panic("OOM"); + bar.dependencies.put("foo", foo) catch @panic("OOM"); + + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = .{ .path = "test.zig" }, + .optimize = optimize, + }); + exe.addModule("foo", foo); + + const run = exe.run(); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&run.step); +} diff --git a/test/standalone/dep_mutually_recursive/foo.zig b/test/standalone/dep_mutually_recursive/foo.zig new file mode 100644 index 0000000000..60107fbdf6 --- /dev/null +++ b/test/standalone/dep_mutually_recursive/foo.zig @@ -0,0 +1,6 @@ +const assert = @import("std").debug.assert; +pub const bar = @import("bar"); + +comptime { + assert(bar.foo == @This()); +} diff --git a/test/standalone/dep_mutually_recursive/test.zig b/test/standalone/dep_mutually_recursive/test.zig new file mode 100644 index 0000000000..b7273ad1aa --- /dev/null +++ b/test/standalone/dep_mutually_recursive/test.zig @@ -0,0 +1,7 @@ +const foo = @import("foo"); +const assert = @import("std").debug.assert; + +pub fn main() void { + assert(foo == foo.bar.foo); + assert(foo == foo.bar.foo.bar.foo); +} diff --git a/test/standalone/dep_recursive/build.zig b/test/standalone/dep_recursive/build.zig new file mode 100644 index 0000000000..32d546e283 --- /dev/null +++ b/test/standalone/dep_recursive/build.zig @@ -0,0 +1,22 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const optimize = b.standardOptimizeOption(.{}); + + const foo = b.createModule(.{ + .source_file = .{ .path = "foo.zig" }, + }); + foo.dependencies.put("foo", foo) catch @panic("OOM"); + + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = .{ .path = "test.zig" }, + .optimize = optimize, + }); + exe.addModule("foo", foo); + + const run = exe.run(); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&run.step); +} diff --git a/test/standalone/dep_recursive/foo.zig b/test/standalone/dep_recursive/foo.zig new file mode 100644 index 0000000000..f4a62c2d4f --- /dev/null +++ b/test/standalone/dep_recursive/foo.zig @@ -0,0 +1,6 @@ +const assert = @import("std").debug.assert; +pub const foo = @import("foo"); + +comptime { + assert(foo == @This()); +} diff --git a/test/standalone/dep_recursive/test.zig b/test/standalone/dep_recursive/test.zig new file mode 100644 index 0000000000..f06ac0e018 --- /dev/null +++ b/test/standalone/dep_recursive/test.zig @@ -0,0 +1,8 @@ +const foo = @import("foo"); +const shared = @import("shared"); +const assert = @import("std").debug.assert; + +pub fn main() void { + assert(foo == foo.foo); + assert(foo == foo.foo.foo); +} diff --git a/test/standalone/dep_shared_builtin/build.zig b/test/standalone/dep_shared_builtin/build.zig new file mode 100644 index 0000000000..6c029b654b --- /dev/null +++ b/test/standalone/dep_shared_builtin/build.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = .{ .path = "test.zig" }, + .optimize = optimize, + }); + exe.addAnonymousModule("foo", .{ + .source_file = .{ .path = "foo.zig" }, + }); + + const run = exe.run(); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&run.step); +} diff --git a/test/standalone/dep_shared_builtin/foo.zig b/test/standalone/dep_shared_builtin/foo.zig new file mode 100644 index 0000000000..3b2719146e --- /dev/null +++ b/test/standalone/dep_shared_builtin/foo.zig @@ -0,0 +1,3 @@ +pub const std = @import("std"); +pub const builtin = @import("builtin"); +pub const root = @import("root"); diff --git a/test/standalone/dep_shared_builtin/test.zig b/test/standalone/dep_shared_builtin/test.zig new file mode 100644 index 0000000000..88a11f440a --- /dev/null +++ b/test/standalone/dep_shared_builtin/test.zig @@ -0,0 +1,11 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const root = @import("root"); +const foo = @import("foo"); + +pub fn main() void { + std.debug.assert(root == @This()); + std.debug.assert(std == foo.std); + std.debug.assert(builtin == foo.builtin); + std.debug.assert(root == foo.root); +} diff --git a/test/standalone/dep_triangle/build.zig b/test/standalone/dep_triangle/build.zig new file mode 100644 index 0000000000..f3b73aaf35 --- /dev/null +++ b/test/standalone/dep_triangle/build.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const optimize = b.standardOptimizeOption(.{}); + + const shared = b.createModule(.{ + .source_file = .{ .path = "shared.zig" }, + }); + + const exe = b.addExecutable(.{ + .name = "test", + .root_source_file = .{ .path = "test.zig" }, + .optimize = optimize, + }); + exe.addAnonymousModule("foo", .{ + .source_file = .{ .path = "foo.zig" }, + .dependencies = &.{.{ .name = "shared", .module = shared }}, + }); + exe.addModule("shared", shared); + + const run = exe.run(); + + const test_step = b.step("test", "Test it"); + test_step.dependOn(&run.step); +} diff --git a/test/standalone/dep_triangle/foo.zig b/test/standalone/dep_triangle/foo.zig new file mode 100644 index 0000000000..772d21dd58 --- /dev/null +++ b/test/standalone/dep_triangle/foo.zig @@ -0,0 +1 @@ +pub const shared = @import("shared"); diff --git a/test/standalone/dep_triangle/shared.zig b/test/standalone/dep_triangle/shared.zig new file mode 100644 index 0000000000..3d771dbba8 --- /dev/null +++ b/test/standalone/dep_triangle/shared.zig @@ -0,0 +1 @@ +// (empty) diff --git a/test/standalone/dep_triangle/test.zig b/test/standalone/dep_triangle/test.zig new file mode 100644 index 0000000000..f208e560fa --- /dev/null +++ b/test/standalone/dep_triangle/test.zig @@ -0,0 +1,7 @@ +const foo = @import("foo"); +const shared = @import("shared"); +const assert = @import("std").debug.assert; + +pub fn main() void { + assert(foo.shared == shared); +} From c6ef83efe54bd19d7cd5dfadff23678d6af10de9 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 21 Feb 2023 14:31:02 +1100 Subject: [PATCH 085/122] std.compress.zstandard: clean up streaming API --- lib/std/compress/zstandard.zig | 37 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig index fcffed99f1..f59de87e6b 100644 --- a/lib/std/compress/zstandard.zig +++ b/lib/std/compress/zstandard.zig @@ -8,10 +8,14 @@ pub const compressed_block = types.compressed_block; pub const decompress = @import("zstandard/decompress.zig"); +pub const DecompressStreamOptions = struct { + verify_checksum: bool = true, + window_size_max: usize = 1 << 23, // 8MiB default maximum window size, +}; + pub fn DecompressStream( comptime ReaderType: type, - comptime verify_checksum: bool, - comptime window_size_max: usize, + comptime options: DecompressStreamOptions, ) type { return struct { const Self = @This(); @@ -27,7 +31,7 @@ pub fn DecompressStream( offset_fse_buffer: []types.compressed_block.Table.Fse, literals_buffer: []u8, sequence_buffer: []u8, - checksum: if (verify_checksum) ?u32 else void, + checksum: if (options.verify_checksum) ?u32 else void, current_frame_decompressed_size: usize, pub const Error = ReaderType.Error || error{ @@ -69,8 +73,8 @@ pub fn DecompressStream( const frame_context = context: { break :context try decompress.FrameContext.init( header, - window_size_max, - verify_checksum, + options.window_size_max, + options.verify_checksum, ); }; @@ -99,10 +103,10 @@ pub fn DecompressStream( ); const buffer = try RingBuffer.init(self.allocator, frame_context.window_size); - const literals_data = try self.allocator.alloc(u8, window_size_max); + const literals_data = try self.allocator.alloc(u8, options.window_size_max); errdefer self.allocator.free(literals_data); - const sequence_data = try self.allocator.alloc(u8, window_size_max); + const sequence_data = try self.allocator.alloc(u8, options.window_size_max); errdefer self.allocator.free(sequence_data); self.literal_fse_buffer = literal_fse_buffer; @@ -116,7 +120,7 @@ pub fn DecompressStream( self.decode_state = decode_state; self.frame_context = frame_context; - self.checksum = if (verify_checksum) null else {}; + self.checksum = if (options.verify_checksum) null else {}; self.current_frame_decompressed_size = 0; self.state = .InFrame; @@ -199,7 +203,7 @@ pub fn DecompressStream( if (self.frame_context.has_checksum) { const checksum = source_reader.readIntLittle(u32) catch return error.MalformedFrame; - if (comptime verify_checksum) { + if (comptime options.verify_checksum) { if (self.frame_context.hasher_opt) |*hasher| { if (checksum != decompress.computeChecksum(hasher)) return error.ChecksumFailure; @@ -232,17 +236,24 @@ pub fn DecompressStream( }; } +pub fn decompressStreamOptions( + allocator: Allocator, + reader: anytype, + comptime options: DecompressStreamOptions, +) DecompressStream(@TypeOf(reader, options)) { + return DecompressStream(@TypeOf(reader), options).init(allocator, reader); +} + pub fn decompressStream( allocator: Allocator, reader: anytype, - comptime window_size_max: usize, -) DecompressStream(@TypeOf(reader), true, window_size_max) { - return DecompressStream(@TypeOf(reader), true, 8 * (1 << 20)).init(allocator, reader); +) DecompressStream(@TypeOf(reader), .{}) { + return DecompressStream(@TypeOf(reader), .{}).init(allocator, reader); } fn testDecompress(data: []const u8) ![]u8 { var in_stream = std.io.fixedBufferStream(data); - var zstd_stream = decompressStream(std.testing.allocator, in_stream.reader(), 1 << 23); + var zstd_stream = decompressStream(std.testing.allocator, in_stream.reader()); defer zstd_stream.deinit(); const result = zstd_stream.reader().readAllAlloc(std.testing.allocator, std.math.maxInt(usize)); return result; From d8fada6b6325e07015fddd68bb4c6369a66f23f3 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 20 Feb 2023 21:31:57 -0500 Subject: [PATCH 086/122] CBE: add CType interning --- CMakeLists.txt | 1 + src/Compilation.zig | 7 +- src/codegen/c.zig | 13 +- src/codegen/c/type.zig | 1457 ++++++++++++++++++++++++++++++++++++++++ src/link/C.zig | 33 +- 5 files changed, 1504 insertions(+), 7 deletions(-) create mode 100644 src/codegen/c/type.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 448fb400b6..b31a0f596f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -569,6 +569,7 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig" "${CMAKE_SOURCE_DIR}/src/codegen.zig" "${CMAKE_SOURCE_DIR}/src/codegen/c.zig" + "${CMAKE_SOURCE_DIR}/src/codegen/c/type.zig" "${CMAKE_SOURCE_DIR}/src/codegen/llvm.zig" "${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig" "${CMAKE_SOURCE_DIR}/src/glibc.zig" diff --git a/src/Compilation.zig b/src/Compilation.zig index ebc0e9b563..97153da88b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3266,8 +3266,8 @@ fn processOneJob(comp: *Compilation, job: Job) !void { const decl_emit_h = emit_h.declPtr(decl_index); const fwd_decl = &decl_emit_h.fwd_decl; fwd_decl.shrinkRetainingCapacity(0); - var typedefs_arena = std.heap.ArenaAllocator.init(gpa); - defer typedefs_arena.deinit(); + var ctypes_arena = std.heap.ArenaAllocator.init(gpa); + defer ctypes_arena.deinit(); var dg: c_codegen.DeclGen = .{ .gpa = gpa, @@ -3276,8 +3276,9 @@ fn processOneJob(comp: *Compilation, job: Job) !void { .decl_index = decl_index, .decl = decl, .fwd_decl = fwd_decl.toManaged(gpa), + .ctypes = .{}, .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ .mod = module }), - .typedefs_arena = typedefs_arena.allocator(), + .typedefs_arena = ctypes_arena.allocator(), }; defer { for (dg.typedefs.values()) |typedef| { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 0beb00b236..872d5fd344 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -27,6 +27,8 @@ const Mutability = enum { Const, ConstArgument, Mut }; const BigIntLimb = std.math.big.Limb; const BigInt = std.math.big.int; +pub const CType = @import("c/type.zig").CType; + pub const CValue = union(enum) { none: void, local: LocalIndex, @@ -446,7 +448,8 @@ pub const Function = struct { return f.object.dg.fmtIntLiteral(ty, val); } - pub fn deinit(f: *Function, gpa: mem.Allocator) void { + pub fn deinit(f: *Function) void { + const gpa = f.object.dg.gpa; f.allocs.deinit(gpa); f.locals.deinit(gpa); for (f.free_locals_stack.items) |*free_locals| { @@ -460,6 +463,7 @@ pub const Function = struct { gpa.free(typedef.rendered); } f.object.dg.typedefs.deinit(); + f.object.dg.ctypes.deinit(gpa); f.object.dg.fwd_decl.deinit(); f.arena.deinit(); } @@ -487,6 +491,7 @@ pub const DeclGen = struct { decl_index: Decl.Index, fwd_decl: std.ArrayList(u8), error_msg: ?*Module.ErrorMsg, + ctypes: CType.Store, /// The key of this map is Type which has references to typedefs_arena. typedefs: TypedefMap, typedefs_arena: std.mem.Allocator, @@ -1949,6 +1954,10 @@ pub const DeclGen = struct { return name; } + fn typeToCType(dg: *DeclGen, ty: Type) !CType { + return dg.ctypes.typeToCType(dg.gpa, ty, dg.module); + } + /// Renders a type as a single identifier, generating intermediate typedefs /// if necessary. /// @@ -1967,6 +1976,8 @@ pub const DeclGen = struct { t: Type, kind: TypedefKind, ) error{ OutOfMemory, AnalysisFail }!void { + _ = try dg.typeToCType(t); + const target = dg.module.getTarget(); switch (t.zigTypeTag()) { diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig new file mode 100644 index 0000000000..c9aca79458 --- /dev/null +++ b/src/codegen/c/type.zig @@ -0,0 +1,1457 @@ +const std = @import("std"); +const cstr = std.cstr; +const mem = std.mem; +const Allocator = mem.Allocator; +const assert = std.debug.assert; +const autoHash = std.hash.autoHash; +const Target = std.Target; + +const Module = @import("../../Module.zig"); +const Type = @import("../../type.zig").Type; + +pub const CType = extern union { + /// If the tag value is less than Tag.no_payload_count, then no pointer + /// dereference is needed. + tag_if_small_enough: Tag, + ptr_otherwise: *const Payload, + + pub fn initTag(small_tag: Tag) CType { + assert(!small_tag.hasPayload()); + return .{ .tag_if_small_enough = small_tag }; + } + + pub fn initPayload(pl: anytype) CType { + const T = @typeInfo(@TypeOf(pl)).Pointer.child; + return switch (pl.base.tag) { + inline else => |t| if (comptime t.hasPayload() and t.Type() == T) .{ + .ptr_otherwise = &pl.base, + } else unreachable, + }; + } + + pub fn hasPayload(self: CType) bool { + return self.tag_if_small_enough.hasPayload(); + } + + pub fn tag(self: CType) Tag { + return if (self.hasPayload()) self.ptr_otherwise.tag else self.tag_if_small_enough; + } + + pub fn cast(self: CType, comptime T: type) ?*const T { + if (!self.hasPayload()) return null; + const pl = self.ptr_otherwise; + return switch (pl.tag) { + inline else => |t| if (comptime t.hasPayload() and t.Type() == T) + @fieldParentPtr(T, "base", pl) + else + null, + }; + } + + pub fn castTag(self: CType, comptime t: Tag) ?*const t.Type() { + return if (self.tag() == t) @fieldParentPtr(t.Type(), "base", self.ptr_otherwise) else null; + } + + pub const Tag = enum(usize) { + // The first section of this enum are tags that require no payload. + void, + + // C basic types + char, + + @"signed char", + short, + int, + long, + @"long long", + + _Bool, + @"unsigned char", + @"unsigned short", + @"unsigned int", + @"unsigned long", + @"unsigned long long", + + float, + double, + @"long double", + + // C header types + bool, // stdbool.h + size_t, // stddef.h + ptrdiff_t, // stddef.h + + // zig.h types + zig_u8, + zig_i8, + zig_u16, + zig_i16, + zig_u32, + zig_i32, + zig_u64, + zig_i64, + zig_u128, + zig_i128, + zig_f16, + zig_f32, + zig_f64, + zig_f80, + zig_f128, + + // After this, the tag requires a payload. + pointer, + pointer_const, + pointer_volatile, + pointer_const_volatile, + array, + vector, + fwd_struct, + fwd_union, + anon_struct, + packed_anon_struct, + @"struct", + @"union", + packed_struct, + packed_union, + function, + varargs_function, + + pub const last_no_payload_tag = Tag.zig_f128; + pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; + + pub fn hasPayload(self: Tag) bool { + return @enumToInt(self) >= no_payload_count; + } + + pub fn toIndex(self: Tag) Index { + assert(!self.hasPayload()); + return @intCast(Index, @enumToInt(self)); + } + + pub fn Type(comptime self: Tag) type { + return switch (self) { + .void, + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + ._Bool, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + .bool, + .size_t, + .ptrdiff_t, + .zig_u8, + .zig_i8, + .zig_u16, + .zig_i16, + .zig_u32, + .zig_i32, + .zig_u64, + .zig_i64, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => @compileError("Type Tag " ++ @tagName(self) ++ " has no payload"), + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => Payload.Child, + + .array, + .vector, + => Payload.Sequence, + + .fwd_struct, + .fwd_union, + => Payload.FwdDecl, + + .anon_struct, + .packed_anon_struct, + => Payload.Fields, + + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => Payload.Aggregate, + + .function, + .varargs_function, + => Payload.Function, + }; + } + }; + + pub const Payload = struct { + tag: Tag, + + pub const Child = struct { + base: Payload, + data: Index, + }; + + pub const Sequence = struct { + base: Payload, + data: struct { + len: u64, + elem_type: Index, + }, + }; + + pub const FwdDecl = struct { + base: Payload, + data: Module.Decl.Index, + }; + + pub const Fields = struct { + base: Payload, + data: Data, + + const Data = []const Field; + const Field = struct { + name: [*:0]const u8, + type: Index, + alignas: u32, + }; + }; + + pub const Aggregate = struct { + base: Payload, + data: struct { + fields: Fields.Data, + fwd_decl: Index, + }, + }; + + pub const Function = struct { + base: Payload, + data: struct { + return_type: Index, + param_types: []const Index, + }, + }; + }; + + pub const Index = u32; + pub const Store = struct { + arena: std.heap.ArenaAllocator.State = .{}, + set: Set = .{}, + + const Set = struct { + const Map = std.ArrayHashMapUnmanaged(CType, void, HashContext32, true); + + map: Map = .{}, + + fn indexToCType(self: Set, index: Index) CType { + if (index < Tag.no_payload_count) return initTag(@intToEnum(Tag, index)); + return self.map.keys()[index - Tag.no_payload_count]; + } + + fn indexToHash(self: Set, index: Index) Map.Hash { + if (index < Tag.no_payload_count) return self.indexToCType(index).hash(self); + return self.map.entries.items(.hash)[index - Tag.no_payload_count]; + } + + fn typeToIndex(self: Set, ty: Type, target: Target, kind: Kind) ?Index { + const lookup = Convert.Lookup{ .imm = .{ .set = &self, .target = target } }; + + var convert: Convert = undefined; + convert.initType(ty, kind, lookup) catch unreachable; + + const t = convert.tag(); + if (!t.hasPayload()) return t.toIndex(); + + return if (self.map.getIndexAdapted( + ty, + TypeAdapter32{ .kind = kind, .lookup = lookup, .convert = &convert }, + )) |idx| @intCast(Index, Tag.no_payload_count + idx) else null; + } + }; + + const Promoted = struct { + arena: std.heap.ArenaAllocator, + set: Set, + + fn gpa(self: *Promoted) Allocator { + return self.arena.child_allocator; + } + + fn cTypeToIndex(self: *Promoted, cty: CType) Allocator.Error!Index { + const t = cty.tag(); + if (@enumToInt(t) < Tag.no_payload_count) return @intCast(Index, @enumToInt(t)); + + const gop = try self.set.map.getOrPutContext(self.gpa(), cty, .{ .store = &self.set }); + if (!gop.found_existing) gop.key_ptr.* = cty; + if (std.debug.runtime_safety) { + const key = self.set.map.entries.items(.key)[gop.index]; + assert(key.eql(cty)); + assert(cty.hash(self.set) == key.hash(self.set)); + } + return @intCast(Index, Tag.no_payload_count + gop.index); + } + + fn typeToIndex(self: *Promoted, ty: Type, mod: *Module, kind: Kind) Allocator.Error!Index { + const lookup = Convert.Lookup{ .mut = .{ .promoted = self, .mod = mod } }; + + var convert: Convert = undefined; + try convert.initType(ty, kind, lookup); + + const t = convert.tag(); + if (!t.hasPayload()) return t.toIndex(); + + const gop = try self.set.map.getOrPutContextAdapted( + self.gpa(), + ty, + TypeAdapter32{ .kind = kind, .lookup = lookup.freeze(), .convert = &convert }, + .{ .store = &self.set }, + ); + if (!gop.found_existing) { + errdefer _ = self.set.map.pop(); + gop.key_ptr.* = try createFromConvert(self, ty, lookup.getTarget(), kind, convert); + } + if (std.debug.runtime_safety) { + const adapter = TypeAdapter64{ + .kind = kind, + .lookup = lookup.freeze(), + .convert = &convert, + }; + const key = self.set.map.entries.items(.key)[gop.index]; + assert(adapter.eql(ty, key)); + assert(adapter.hash(ty) == key.hash(self.set)); + } + return @intCast(Index, Tag.no_payload_count + gop.index); + } + }; + + fn promote(self: Store, gpa: Allocator) Promoted { + return .{ .arena = self.arena.promote(gpa), .set = self.set }; + } + + fn demote(self: *Store, promoted: Promoted) void { + self.arena = promoted.arena.state; + self.set = promoted.set; + } + + pub fn indexToCType(self: Store, index: Index) CType { + return self.set.indexToCType(index); + } + + pub fn cTypeToIndex(self: *Store, gpa: Allocator, cty: CType) !Index { + var promoted = self.promote(gpa); + defer self.demote(promoted); + return promoted.cTypeToIndex(cty); + } + + pub fn typeToCType(self: *Store, gpa: Allocator, ty: Type, mod: *Module) !CType { + const idx = try self.typeToIndex(gpa, ty, mod); + return self.indexToCType(idx); + } + + pub fn typeToIndex(self: *Store, gpa: Allocator, ty: Type, mod: *Module) !Index { + var promoted = self.promote(gpa); + defer self.demote(promoted); + return promoted.typeToIndex(ty, mod, .complete); + } + + pub fn clearRetainingCapacity(self: *Store, gpa: Allocator) void { + var promoted = self.promote(gpa); + defer self.demote(promoted); + promoted.set.map.clearRetainingCapacity(); + _ = promoted.arena.reset(.retain_capacity); + } + + pub fn shrinkToFit(self: *Store, gpa: Allocator) void { + self.map.shrinkAndFree(gpa, self.map.entries.len); + } + + pub fn shrinkAndFree(self: *Store, gpa: Allocator) void { + var promoted = self.promote(gpa); + defer self.demote(promoted); + promoted.set.map.clearAndFree(gpa); + _ = promoted.arena.reset(.free_all); + } + + pub fn move(self: *Store) Store { + const moved = self.*; + self.* = .{}; + return moved; + } + + pub fn deinit(self: *Store, gpa: Allocator) void { + var promoted = self.promote(gpa); + promoted.set.map.deinit(gpa); + _ = promoted.arena.deinit(); + self.* = undefined; + } + }; + + pub fn eql(lhs: CType, rhs: CType) bool { + // As a shortcut, if the small tags / addresses match, we're done. + if (lhs.tag_if_small_enough == rhs.tag_if_small_enough) return true; + + const lhs_tag = lhs.tag(); + const rhs_tag = rhs.tag(); + if (lhs_tag != rhs_tag) return false; + + return switch (lhs_tag) { + .void, + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + ._Bool, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + .bool, + .size_t, + .ptrdiff_t, + .zig_u8, + .zig_i8, + .zig_u16, + .zig_i16, + .zig_u32, + .zig_i32, + .zig_u64, + .zig_i64, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => false, + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => lhs.cast(Payload.Child).?.data == rhs.cast(Payload.Child).?.data, + + .array, + .vector, + => std.meta.eql(lhs.cast(Payload.Sequence).?.data, rhs.cast(Payload.Sequence).?.data), + + .fwd_struct, + .fwd_union, + => lhs.cast(Payload.FwdDecl).?.data == rhs.cast(Payload.FwdDecl).?.data, + + .anon_struct, + .packed_anon_struct, + => { + const lhs_data = lhs.cast(Payload.Fields).?.data; + const rhs_data = rhs.cast(Payload.Fields).?.data; + if (lhs_data.len != rhs_data.len) return false; + for (lhs_data, rhs_data) |lhs_field, rhs_field| { + if (lhs_field.type != rhs_field.type) return false; + if (lhs_field.alignas != rhs_field.alignas) return false; + if (cstr.cmp(lhs_field.name, rhs_field.name) != 0) return false; + } + return true; + }, + + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => std.meta.eql( + lhs.cast(Payload.Aggregate).?.data.fwd_decl, + rhs.cast(Payload.Aggregate).?.data.fwd_decl, + ), + + .function, + .varargs_function, + => { + const lhs_data = lhs.cast(Payload.Function).?.data; + const rhs_data = rhs.cast(Payload.Function).?.data; + if (lhs_data.return_type != rhs_data.return_type) return false; + if (lhs_data.param_types.len != rhs_data.param_types.len) return false; + for (lhs_data.param_types, rhs_data.param_types) |lhs_param_cty, rhs_param_cty| { + if (lhs_param_cty != rhs_param_cty) return false; + } + return true; + }, + }; + } + + pub fn hash(self: CType, store: Store.Set) u64 { + var hasher = std.hash.Wyhash.init(0); + self.updateHasher(&hasher, store); + return hasher.final(); + } + + pub fn updateHasher(self: CType, hasher: anytype, store: Store.Set) void { + const t = self.tag(); + autoHash(hasher, t); + switch (t) { + .void, + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + ._Bool, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + .bool, + .size_t, + .ptrdiff_t, + .zig_u8, + .zig_i8, + .zig_u16, + .zig_i16, + .zig_u32, + .zig_i32, + .zig_u64, + .zig_i64, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => {}, + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => store.indexToCType(self.cast(Payload.Child).?.data).updateHasher(hasher, store), + + .array, + .vector, + => { + const data = self.cast(Payload.Sequence).?.data; + autoHash(hasher, data.len); + store.indexToCType(data.elem_type).updateHasher(hasher, store); + }, + + .fwd_struct, + .fwd_union, + => autoHash(hasher, self.cast(Payload.FwdDecl).?.data), + + .anon_struct, + .packed_anon_struct, + => for (self.cast(Payload.Fields).?.data) |field| { + store.indexToCType(field.type).updateHasher(hasher, store); + hasher.update(mem.span(field.name)); + autoHash(hasher, field.alignas); + }, + + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => store.indexToCType(self.cast(Payload.Aggregate).?.data.fwd_decl) + .updateHasher(hasher, store), + + .function, + .varargs_function, + => { + const data = self.cast(Payload.Function).?.data; + store.indexToCType(data.return_type).updateHasher(hasher, store); + for (data.param_types) |param_ty| { + store.indexToCType(param_ty).updateHasher(hasher, store); + } + }, + } + } + + pub const Kind = enum { forward, complete, global, parameter }; + + const Convert = struct { + storage: union { + none: void, + child: Payload.Child, + seq: Payload.Sequence, + fwd: Payload.FwdDecl, + anon: struct { + fields: [2]Payload.Fields.Field, + pl: Payload.Fields, + }, + agg: Payload.Aggregate, + }, + value: union(enum) { + tag: Tag, + cty: CType, + }, + + pub fn init(self: *@This(), t: Tag) void { + self.* = if (t.hasPayload()) .{ + .storage = .{ .none = {} }, + .value = .{ .tag = t }, + } else .{ + .storage = .{ .none = {} }, + .value = .{ .cty = initTag(t) }, + }; + } + + pub fn tag(self: @This()) Tag { + return switch (self.value) { + .tag => |t| t, + .cty => |c| c.tag(), + }; + } + + fn tagFromIntInfo(signedness: std.builtin.Signedness, bits: u16) Tag { + return switch (bits) { + 0 => .void, + 1...8 => switch (signedness) { + .unsigned => .zig_u8, + .signed => .zig_i8, + }, + 9...16 => switch (signedness) { + .unsigned => .zig_u16, + .signed => .zig_i16, + }, + 17...32 => switch (signedness) { + .unsigned => .zig_u32, + .signed => .zig_i32, + }, + 33...64 => switch (signedness) { + .unsigned => .zig_u64, + .signed => .zig_i64, + }, + 65...128 => switch (signedness) { + .unsigned => .zig_u128, + .signed => .zig_i128, + }, + else => .array, + }; + } + + pub const Lookup = union(enum) { + fail: Target, + imm: struct { + set: *const Store.Set, + target: Target, + }, + mut: struct { + promoted: *Store.Promoted, + mod: *Module, + }, + + pub fn isMutable(self: @This()) bool { + return switch (self) { + .fail, .imm => false, + .mut => true, + }; + } + + pub fn getTarget(self: @This()) Target { + return switch (self) { + .fail => |target| target, + .imm => |imm| imm.target, + .mut => |mut| mut.mod.getTarget(), + }; + } + + pub fn getSet(self: @This()) ?*const Store.Set { + return switch (self) { + .fail => null, + .imm => |imm| imm.set, + .mut => |mut| &mut.promoted.set, + }; + } + + pub fn typeToIndex(self: @This(), ty: Type, kind: Kind) !?Index { + return switch (self) { + .fail => null, + .imm => |imm| imm.set.typeToIndex(ty, imm.target, kind), + .mut => |mut| try mut.promoted.typeToIndex(ty, mut.mod, kind), + }; + } + + pub fn indexToCType(self: @This(), index: Index) ?CType { + return if (self.getSet()) |set| set.indexToCType(index) else null; + } + + pub fn freeze(self: @This()) @This() { + return switch (self) { + .fail, .imm => self, + .mut => |mut| .{ .imm = .{ .set = &mut.promoted.set, .target = self.getTarget() } }, + }; + } + }; + + pub fn initType(self: *@This(), ty: Type, kind: Kind, lookup: Lookup) !void { + const target = lookup.getTarget(); + + self.* = undefined; + if (!ty.isFnOrHasRuntimeBitsIgnoreComptime()) + self.init(.void) + else if (ty.isAbiInt()) switch (ty.tag()) { + .usize => self.init(.size_t), + .isize => self.init(.ptrdiff_t), + .c_short => self.init(.short), + .c_ushort => self.init(.@"unsigned short"), + .c_int => self.init(.int), + .c_uint => self.init(.@"unsigned int"), + .c_long => self.init(.long), + .c_ulong => self.init(.@"unsigned long"), + .c_longlong => self.init(.@"long long"), + .c_ulonglong => self.init(.@"unsigned long long"), + else => { + const info = ty.intInfo(target); + const t = tagFromIntInfo(info.signedness, info.bits); + switch (t) { + .void => unreachable, + else => self.init(t), + .array => { + const abi_size = ty.abiSize(target); + const abi_align = ty.abiAlignment(target); + self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ + .len = @divExact(abi_size, abi_align), + .elem_type = tagFromIntInfo( + .unsigned, + @intCast(u16, abi_align * 8), + ).toIndex(), + } } }; + self.value = .{ .cty = initPayload(&self.storage.seq) }; + }, + } + }, + } else switch (ty.zigTypeTag()) { + .Frame => unreachable, + .AnyFrame => unreachable, + + .Int, + .Enum, + .ErrorSet, + .Type, + .Void, + .NoReturn, + .ComptimeFloat, + .ComptimeInt, + .Undefined, + .Null, + .EnumLiteral, + => unreachable, + + .Bool => self.init(.bool), + + .Float => self.init(switch (ty.tag()) { + .f16 => .zig_f16, + .f32 => .zig_f32, + .f64 => .zig_f64, + .f80 => .zig_f80, + .f128 => .zig_f128, + .c_longdouble => .@"long double", + else => unreachable, + }), + + .Pointer => switch (ty.ptrSize()) { + .Slice => { + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const ptr_ty = ty.slicePtrFieldType(&buf); + if (try lookup.typeToIndex(ptr_ty, kind)) |ptr_idx| { + self.storage = .{ .anon = .{ .fields = .{ + .{ + .name = "ptr", + .type = ptr_idx, + .alignas = ptr_ty.abiAlignment(target), + }, + .{ + .name = "len", + .type = Tag.size_t.toIndex(), + .alignas = Type.usize.abiAlignment(target), + }, + }, .pl = undefined } }; + self.storage.anon.pl = .{ + .base = .{ .tag = .anon_struct }, + .data = self.storage.anon.fields[0..2], + }; + self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; + } else self.init(.anon_struct); + }, + + .One, .Many, .C => { + const t: Tag = switch (ty.isVolatilePtr()) { + false => switch (ty.isConstPtr()) { + false => .pointer, + true => .pointer_const, + }, + true => switch (ty.isConstPtr()) { + false => .pointer_volatile, + true => .pointer_const_volatile, + }, + }; + if (try lookup.typeToIndex(ty.childType(), .forward)) |child_idx| { + self.storage = .{ .child = .{ .base = .{ .tag = t }, .data = child_idx } }; + self.value = .{ .cty = initPayload(&self.storage.child) }; + } else self.init(t); + }, + }, + + .Struct, .Union => |zig_tag| if (ty.isTupleOrAnonStruct()) { + if (lookup.isMutable()) { + for (0..ty.structFieldCount()) |field_i| { + if (ty.structFieldIsComptime(field_i)) continue; + _ = try lookup.typeToIndex(ty.structFieldType(field_i), switch (kind) { + .forward, .complete, .parameter => .complete, + .global => .global, + }); + } + } + self.init(.anon_struct); + } else { + const is_struct = zig_tag == .Struct or ty.unionTagTypeSafety() != null; + switch (kind) { + .forward => { + self.storage = .{ .fwd = .{ + .base = .{ .tag = if (is_struct) .fwd_struct else .fwd_union }, + .data = ty.getOwnerDecl(), + } }; + self.value = .{ .cty = initPayload(&self.storage.fwd) }; + }, + else => { + if (lookup.isMutable()) { + for (0..switch (zig_tag) { + .Struct => ty.structFieldCount(), + .Union => ty.cast(Type.Payload.Union).?.data.fields.count(), + else => unreachable, + }) |field_i| { + if (zig_tag == .Struct and ty.structFieldIsComptime(field_i)) + continue; + _ = try lookup.typeToIndex( + ty.structFieldType(field_i), + switch (kind) { + .forward => unreachable, + .complete, .parameter => .complete, + .global => .global, + }, + ); + } + _ = try lookup.typeToIndex(ty, .forward); + } + self.init(if (is_struct) .@"struct" else .@"union"); + }, + } + }, + + .Array, .Vector => |zig_tag| { + const t: Tag = switch (zig_tag) { + .Array => .array, + .Vector => .vector, + else => unreachable, + }; + if (try lookup.typeToIndex(ty.childType(), kind)) |child_idx| { + self.storage = .{ .seq = .{ .base = .{ .tag = t }, .data = .{ + .len = ty.arrayLenIncludingSentinel(), + .elem_type = child_idx, + } } }; + self.value = .{ .cty = initPayload(&self.storage.seq) }; + } else self.init(t); + }, + + .Optional => { + var buf: Type.Payload.ElemType = undefined; + const payload_ty = ty.optionalChild(&buf); + if (payload_ty.hasRuntimeBitsIgnoreComptime()) { + if (ty.optionalReprIsPayload()) + try self.initType(payload_ty, kind, lookup) + else if (try lookup.typeToIndex(payload_ty, kind)) |payload_idx| { + self.storage = .{ .anon = .{ .fields = .{ + .{ + .name = "payload", + .type = payload_idx, + .alignas = payload_ty.abiAlignment(target), + }, + .{ + .name = "is_null", + .type = Tag.bool.toIndex(), + .alignas = Type.bool.abiAlignment(target), + }, + }, .pl = undefined } }; + self.storage.anon.pl = .{ + .base = .{ .tag = .anon_struct }, + .data = self.storage.anon.fields[0..2], + }; + self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; + } else self.init(.anon_struct); + } else self.init(.bool); + }, + + .ErrorUnion => { + const payload_ty = ty.errorUnionPayload(); + if (try lookup.typeToIndex(payload_ty, switch (kind) { + .forward, .complete, .parameter => .complete, + .global => .global, + })) |payload_idx| { + const error_ty = ty.errorUnionSet(); + if (payload_idx == Tag.void.toIndex()) + try self.initType(error_ty, kind, lookup) + else if (try lookup.typeToIndex(error_ty, kind)) |error_idx| { + self.storage = .{ .anon = .{ .fields = .{ + .{ + .name = "payload", + .type = payload_idx, + .alignas = payload_ty.abiAlignment(target), + }, + .{ + .name = "error", + .type = error_idx, + .alignas = error_ty.abiAlignment(target), + }, + }, .pl = undefined } }; + self.storage.anon.pl = .{ + .base = .{ .tag = .anon_struct }, + .data = self.storage.anon.fields[0..2], + }; + self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; + } else self.init(.anon_struct); + } else self.init(.anon_struct); + }, + + .Opaque => switch (ty.tag()) { + .anyopaque => self.init(.void), + .@"opaque" => { + self.storage = .{ .fwd = .{ + .base = .{ .tag = .fwd_struct }, + .data = ty.getOwnerDecl(), + } }; + self.value = .{ .cty = initPayload(&self.storage.fwd) }; + }, + else => unreachable, + }, + + .Fn => { + const info = ty.fnInfo(); + if (lookup.isMutable()) { + _ = try lookup.typeToIndex(info.return_type, switch (kind) { + .forward => .forward, + .complete, .parameter, .global => .complete, + }); + for (info.param_types, 0..) |param_ty, param_i| { + if (info.paramIsComptime(param_i)) continue; + _ = try lookup.typeToIndex(param_ty, switch (kind) { + .forward => .forward, + .complete, .parameter, .global => unreachable, + }); + } + } + self.init(if (info.is_var_args) .varargs_function else .function); + }, + } + } + }; + + fn copyFields(arena: Allocator, fields: Payload.Fields.Data) !Payload.Fields.Data { + const new_fields = try arena.dupe(Payload.Fields.Field, fields); + for (new_fields) |*new_field| { + new_field.name = try arena.dupeZ(u8, mem.span(new_field.name)); + new_field.type = new_field.type; + } + return new_fields; + } + + pub fn copy(self: CType, arena: Allocator) !CType { + switch (self.tag()) { + .void, + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + ._Bool, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + .bool, + .size_t, + .ptrdiff_t, + .zig_u8, + .zig_i8, + .zig_u16, + .zig_i16, + .zig_u32, + .zig_i32, + .zig_u64, + .zig_i64, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => return self, + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => { + const pl = self.cast(Payload.Child).?; + const new_pl = try arena.create(Payload.Child); + new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = pl.data }; + return initPayload(new_pl); + }, + + .array, + .vector, + => { + const pl = self.cast(Payload.Sequence).?; + const new_pl = try arena.create(Payload.Sequence); + new_pl.* = .{ + .base = .{ .tag = pl.base.tag }, + .data = .{ .len = pl.data.len, .elem_type = pl.data.elem_type }, + }; + return initPayload(new_pl); + }, + + .fwd_struct, + .fwd_union, + => { + const pl = self.cast(Payload.FwdDecl).?; + const new_pl = try arena.create(Payload.FwdDecl); + new_pl.* = .{ + .base = .{ .tag = pl.base.tag }, + .data = pl.data, + }; + return initPayload(new_pl); + }, + + .anon_struct, + .packed_anon_struct, + => { + const pl = self.cast(Payload.Fields).?; + const new_pl = try arena.create(Payload.Fields); + new_pl.* = .{ + .base = .{ .tag = pl.base.tag }, + .data = try copyFields(arena, pl.data), + }; + return initPayload(new_pl); + }, + + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => { + const pl = self.cast(Payload.Aggregate).?; + const new_pl = try arena.create(Payload.Aggregate); + new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ + .fields = try copyFields(arena, pl.data.fields), + .fwd_decl = pl.data.fwd_decl, + } }; + return initPayload(new_pl); + }, + + .function, + .varargs_function, + => { + const pl = self.cast(Payload.Function).?; + const new_pl = try arena.create(Payload.Function); + new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ + .return_type = pl.data.return_type, + .param_types = try arena.dupe(Index, pl.data.param_types), + } }; + return initPayload(new_pl); + }, + } + } + + fn createFromType(store: *Store.Promoted, ty: Type, target: Target, kind: Kind) !CType { + var convert: Convert = undefined; + try convert.initType(ty, kind, .{ .imm = .{ .set = &store.set, .target = target } }); + return createFromConvert(store, ty, target, kind, &convert); + } + + fn createFromConvert( + store: *Store.Promoted, + ty: Type, + target: Target, + kind: Kind, + convert: Convert, + ) !CType { + const arena = store.arena.allocator(); + switch (convert.value) { + .cty => |c| return c.copy(arena), + .tag => |t| switch (t) { + .anon_struct, + .packed_anon_struct, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => switch (ty.zigTypeTag()) { + .Struct => { + const fields_len = ty.structFieldCount(); + + var c_fields_len: usize = 0; + for (0..fields_len) |field_i| { + if (ty.structFieldIsComptime(field_i)) continue; + c_fields_len += 1; + } + + const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); + var c_field_i: usize = 0; + for (0..fields_len) |field_i| { + if (ty.structFieldIsComptime(field_i)) continue; + + fields_pl[c_field_i] = .{ + .name = try if (ty.isSimpleTuple()) + std.fmt.allocPrintZ(arena, "f{}", .{field_i}) + else + arena.dupeZ(u8, ty.structFieldName(field_i)), + .type = store.set.typeToIndex( + ty.structFieldType(field_i), + target, + switch (kind) { + .forward, .complete, .parameter => .complete, + .global => .global, + }, + ).?, + .alignas = ty.structFieldAlign(field_i, target), + }; + c_field_i += 1; + } + + if (ty.isTupleOrAnonStruct()) { + const anon_pl = try arena.create(Payload.Fields); + anon_pl.* = .{ .base = .{ .tag = .anon_struct }, .data = fields_pl }; + return initPayload(anon_pl); + } + + const struct_pl = try arena.create(Payload.Aggregate); + struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, + } }; + return initPayload(struct_pl); + }, + + .Union => { + const fields = ty.unionFields(); + const fields_len = fields.count(); + + var c_fields_len: usize = 0; + for (0..fields_len) |field_i| { + const field_ty = ty.structFieldType(field_i); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + c_fields_len += 1; + } + + const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); + var field_i: usize = 0; + var c_field_i: usize = 0; + var field_it = fields.iterator(); + while (field_it.next()) |field| { + defer field_i += 1; + if (!field.value_ptr.ty.hasRuntimeBitsIgnoreComptime()) continue; + + fields_pl[c_field_i] = .{ + .name = try arena.dupeZ(u8, field.key_ptr.*), + .type = store.set.typeToIndex(field.value_ptr.ty, target, switch (kind) { + .forward => unreachable, + .complete, .parameter => .complete, + .global => .global, + }).?, + .alignas = ty.structFieldAlign(field_i, target), + }; + c_field_i += 1; + } + + const union_pl = try arena.create(Payload.Aggregate); + union_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, + } }; + return initPayload(union_pl); + }, + + else => unreachable, + }, + + .function, + .varargs_function, + => { + const info = ty.fnInfo(); + const recurse_kind: Kind = switch (kind) { + .forward => .forward, + .complete, .parameter, .global => unreachable, + }; + + var c_params_len: usize = 0; + for (0..info.param_types.len) |param_i| { + if (info.paramIsComptime(param_i)) continue; + c_params_len += 1; + } + + const params_pl = try arena.alloc(Index, c_params_len); + var c_param_i: usize = 0; + for (info.param_types, 0..) |param_ty, param_i| { + if (info.paramIsComptime(param_i)) continue; + params_pl[c_param_i] = store.set.typeToIndex(param_ty, target, recurse_kind).?; + c_param_i += 1; + } + + const fn_pl = try arena.create(Payload.Function); + fn_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .return_type = store.set.typeToIndex(info.return_type, target, recurse_kind).?, + .param_types = params_pl, + } }; + return initPayload(fn_pl); + }, + + else => unreachable, + }, + } + } + + pub const HashContext64 = struct { + store: *const Store.Set, + + pub fn hash(_: @This(), cty: CType) u64 { + return cty.hash(); + } + pub fn eql(_: @This(), lhs: CType, rhs: CType) bool { + return lhs.eql(rhs); + } + }; + + pub const HashContext32 = struct { + store: *const Store.Set, + + pub fn hash(self: @This(), cty: CType) u32 { + return @truncate(u32, cty.hash(self.store.*)); + } + pub fn eql(_: @This(), lhs: CType, rhs: CType, _: usize) bool { + return lhs.eql(rhs); + } + }; + + pub const TypeAdapter64 = struct { + kind: Kind, + lookup: Convert.Lookup, + convert: *const Convert, + + fn eqlRecurse(self: @This(), ty: Type, cty: Index, kind: Kind) bool { + assert(!self.lookup.isMutable()); + + var convert: Convert = undefined; + convert.initType(ty, kind, self.lookup) catch unreachable; + + const self_recurse = @This(){ .kind = kind, .lookup = self.lookup, .convert = &convert }; + return self_recurse.eql(ty, self.lookup.indexToCType(cty).?); + } + + pub fn eql(self: @This(), ty: Type, cty: CType) bool { + switch (self.convert.value) { + .cty => |c| return c.eql(cty), + .tag => |t| { + if (t != cty.tag()) return false; + + const target = self.lookup.getTarget(); + switch (t) { + .anon_struct, + .packed_anon_struct, + => { + if (!ty.isTupleOrAnonStruct()) return false; + + var name_buf: [ + std.fmt.count("f{}", .{std.math.maxInt(usize)}) + ]u8 = undefined; + const c_fields = cty.cast(Payload.Fields).?.data; + + var c_field_i: usize = 0; + for (0..ty.structFieldCount()) |field_i| { + if (ty.structFieldIsComptime(field_i)) continue; + + const c_field = &c_fields[c_field_i]; + c_field_i += 1; + + if (!self.eqlRecurse( + ty.structFieldType(field_i), + c_field.type, + switch (self.kind) { + .forward, .complete, .parameter => .complete, + .global => .global, + }, + ) or !mem.eql( + u8, + if (ty.isSimpleTuple()) + std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable + else + ty.structFieldName(field_i), + mem.span(c_field.name), + ) or ty.structFieldAlign(field_i, target) != c_field.alignas) + return false; + } + return true; + }, + + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => return self.eqlRecurse( + ty, + cty.cast(Payload.Aggregate).?.data.fwd_decl, + .forward, + ), + + .function, + .varargs_function, + => { + if (ty.zigTypeTag() != .Fn) return false; + + const info = ty.fnInfo(); + const data = cty.cast(Payload.Function).?.data; + const recurse_kind: Kind = switch (self.kind) { + .forward => .forward, + .complete, .parameter, .global => unreachable, + }; + + if (info.param_types.len != data.param_types.len or + !self.eqlRecurse(info.return_type, data.return_type, recurse_kind)) + return false; + for (info.param_types, data.param_types, 0..) |param_ty, param_cty, param_i| { + if (info.paramIsComptime(param_i)) continue; + if (!self.eqlRecurse(param_ty, param_cty, recurse_kind)) + return false; + } + return true; + }, + + else => unreachable, + } + }, + } + } + + pub fn hash(self: @This(), ty: Type) u64 { + var hasher = std.hash.Wyhash.init(0); + self.updateHasher(&hasher, ty); + return hasher.final(); + } + + fn updateHasherRecurse(self: @This(), hasher: anytype, ty: Type, kind: Kind) void { + assert(!self.lookup.isMutable()); + + var convert: Convert = undefined; + convert.initType(ty, kind, self.lookup) catch unreachable; + + const self_recurse = @This(){ .kind = kind, .lookup = self.lookup, .convert = &convert }; + self_recurse.updateHasher(hasher, ty); + } + + pub fn updateHasher(self: @This(), hasher: anytype, ty: Type) void { + switch (self.convert.value) { + .cty => |c| return c.updateHasher(hasher, self.lookup.getSet().?.*), + .tag => |t| { + autoHash(hasher, t); + + const target = self.lookup.getTarget(); + switch (t) { + .anon_struct, + .packed_anon_struct, + => { + var name_buf: [ + std.fmt.count("f{}", .{std.math.maxInt(usize)}) + ]u8 = undefined; + for (0..ty.structFieldCount()) |field_i| { + if (ty.structFieldIsComptime(field_i)) continue; + + self.updateHasherRecurse( + hasher, + ty.structFieldType(field_i), + switch (self.kind) { + .forward, .complete, .parameter => .complete, + .global => .global, + }, + ); + hasher.update(if (ty.isSimpleTuple()) + std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable + else + ty.structFieldName(field_i)); + autoHash(hasher, ty.structFieldAlign(field_i, target)); + } + }, + + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => self.updateHasherRecurse(hasher, ty, .forward), + + .function, + .varargs_function, + => { + const info = ty.fnInfo(); + const recurse_kind: Kind = switch (self.kind) { + .forward => .forward, + .complete, .parameter, .global => unreachable, + }; + + self.updateHasherRecurse(hasher, info.return_type, recurse_kind); + for (info.param_types, 0..) |param_ty, param_i| { + if (info.paramIsComptime(param_i)) continue; + self.updateHasherRecurse(hasher, param_ty, recurse_kind); + } + }, + + else => unreachable, + } + }, + } + } + }; + + pub const TypeAdapter32 = struct { + kind: Kind, + lookup: Convert.Lookup, + convert: *const Convert, + + fn to64(self: @This()) TypeAdapter64 { + return .{ .kind = self.kind, .lookup = self.lookup, .convert = self.convert }; + } + + pub fn eql(self: @This(), ty: Type, cty: CType, cty_index: usize) bool { + _ = cty_index; + return self.to64().eql(ty, cty); + } + + pub fn hash(self: @This(), ty: Type) u32 { + return @truncate(u32, self.to64().hash(ty)); + } + }; +}; diff --git a/src/link/C.zig b/src/link/C.zig index 02e5cadfbc..7fb23b2642 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -30,6 +30,7 @@ arena: std.heap.ArenaAllocator, const DeclBlock = struct { code: std.ArrayListUnmanaged(u8) = .{}, fwd_decl: std.ArrayListUnmanaged(u8) = .{}, + ctypes: codegen.CType.Store = .{}, /// Each Decl stores a mapping of Zig Types to corresponding C types, for every /// Zig Type used by the Decl. In flush(), we iterate over each Decl /// and emit the typedef code for all types, making sure to not emit the same thing twice. @@ -37,12 +38,13 @@ const DeclBlock = struct { typedefs: codegen.TypedefMap.Unmanaged = .{}, fn deinit(db: *DeclBlock, gpa: Allocator) void { - db.code.deinit(gpa); - db.fwd_decl.deinit(gpa); for (db.typedefs.values()) |typedef| { gpa.free(typedef.rendered); } db.typedefs.deinit(gpa); + db.ctypes.deinit(gpa); + db.fwd_decl.deinit(gpa); + db.code.deinit(gpa); db.* = undefined; } }; @@ -105,9 +107,11 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes gop.value_ptr.* = .{}; } const fwd_decl = &gop.value_ptr.fwd_decl; + const ctypes = &gop.value_ptr.ctypes; const typedefs = &gop.value_ptr.typedefs; const code = &gop.value_ptr.code; fwd_decl.shrinkRetainingCapacity(0); + ctypes.clearRetainingCapacity(module.gpa); for (typedefs.values()) |typedef| { module.gpa.free(typedef.rendered); } @@ -127,6 +131,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes .decl_index = decl_index, .decl = module.declPtr(decl_index), .fwd_decl = fwd_decl.toManaged(module.gpa), + .ctypes = ctypes.*, .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), .typedefs_arena = self.arena.allocator(), }, @@ -137,7 +142,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes }; function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; - defer function.deinit(module.gpa); + defer function.deinit(); codegen.genFunc(&function) catch |err| switch (err) { error.AnalysisFail => { @@ -148,6 +153,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes }; fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); + ctypes.* = function.object.dg.ctypes.move(); typedefs.* = function.object.dg.typedefs.unmanaged; function.object.dg.typedefs.unmanaged = .{}; code.* = function.object.code.moveToUnmanaged(); @@ -155,6 +161,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes // Free excess allocated memory for this Decl. fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len); code.shrinkAndFree(module.gpa, code.items.len); + ctypes.shrinkAndFree(module.gpa); } pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { @@ -166,9 +173,11 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi gop.value_ptr.* = .{}; } const fwd_decl = &gop.value_ptr.fwd_decl; + const ctypes = &gop.value_ptr.ctypes; const typedefs = &gop.value_ptr.typedefs; const code = &gop.value_ptr.code; fwd_decl.shrinkRetainingCapacity(0); + ctypes.clearRetainingCapacity(module.gpa); for (typedefs.values()) |value| { module.gpa.free(value.rendered); } @@ -185,6 +194,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi .decl_index = decl_index, .decl = decl, .fwd_decl = fwd_decl.toManaged(module.gpa), + .ctypes = ctypes.*, .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), .typedefs_arena = self.arena.allocator(), }, @@ -198,6 +208,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi module.gpa.free(typedef.rendered); } object.dg.typedefs.deinit(); + object.dg.ctypes.deinit(object.dg.gpa); object.dg.fwd_decl.deinit(); } @@ -210,6 +221,8 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi }; fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); + ctypes.* = object.dg.ctypes; + object.dg.ctypes = .{}; typedefs.* = object.dg.typedefs.unmanaged; object.dg.typedefs.unmanaged = .{}; code.* = object.code.moveToUnmanaged(); @@ -217,6 +230,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi // Free excess allocated memory for this Decl. fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len); code.shrinkAndFree(module.gpa, code.items.len); + ctypes.shrinkAndFree(module.gpa); } pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { @@ -326,6 +340,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const Flush = struct { err_decls: DeclBlock = .{}, remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, + + ctypes: CTypes = .{}, typedefs: Typedefs = .{}, typedef_buf: std.ArrayListUnmanaged(u8) = .{}, asm_buf: std.ArrayListUnmanaged(u8) = .{}, @@ -334,6 +350,13 @@ const Flush = struct { /// Keeps track of the total bytes of `all_buffers`. file_size: u64 = 0, + const CTypes = std.ArrayHashMapUnmanaged( + codegen.CType, + void, + codegen.CType.HashContext32, + true, + ); + const Typedefs = std.HashMapUnmanaged( Type, void, @@ -351,6 +374,7 @@ const Flush = struct { f.all_buffers.deinit(gpa); f.typedef_buf.deinit(gpa); f.typedefs.deinit(gpa); + f.ctypes.deinit(gpa); f.remaining_decls.deinit(gpa); f.err_decls.deinit(gpa); } @@ -383,6 +407,7 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { const module = self.base.options.module.?; const fwd_decl = &f.err_decls.fwd_decl; + const ctypes = &f.err_decls.ctypes; const typedefs = &f.err_decls.typedefs; const code = &f.err_decls.code; @@ -394,6 +419,7 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { .decl_index = undefined, .decl = undefined, .fwd_decl = fwd_decl.toManaged(module.gpa), + .ctypes = ctypes.*, .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), .typedefs_arena = self.arena.allocator(), }, @@ -403,6 +429,7 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { object.code.deinit(); + object.dg.ctypes.deinit(module.gpa); for (object.dg.typedefs.values()) |typedef| { module.gpa.free(typedef.rendered); } From 7768d2024bfbb4aad143fb8a4143e324445bfd93 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 17 Feb 2023 05:00:17 -0500 Subject: [PATCH 087/122] CBE: use CType for type rendering --- src/codegen/c.zig | 596 +++++++++++++++++++++++----------------------- 1 file changed, 298 insertions(+), 298 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 872d5fd344..aa540d6984 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1954,9 +1954,289 @@ pub const DeclGen = struct { return name; } + fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { + return dg.ctypes.indexToCType(idx); + } fn typeToCType(dg: *DeclGen, ty: Type) !CType { return dg.ctypes.typeToCType(dg.gpa, ty, dg.module); } + fn typeToIndex(dg: *DeclGen, ty: Type) !CType.Index { + return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module); + } + + const CTypeFix = enum { prefix, suffix }; + const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); + const CTypeRenderTrailing = enum { + no_space, + maybe_space, + + pub fn format( + self: @This(), + comptime fmt: []const u8, + _: std.fmt.FormatOptions, + w: anytype, + ) @TypeOf(w).Error!void { + if (fmt.len != 0) + @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ + @typeName(@This()) ++ "'"); + comptime assert(fmt.len == 0); + switch (self) { + .no_space => {}, + .maybe_space => try w.writeByte(' '), + } + } + }; + fn renderTypePrefix( + dg: *DeclGen, + w: anytype, + idx: CType.Index, + parent_fix: CTypeFix, + qualifiers: CQualifiers, + ) @TypeOf(w).Error!CTypeRenderTrailing { + var trailing = CTypeRenderTrailing.maybe_space; + + const cty = dg.indexToCType(idx); + switch (cty.tag()) { + .void, + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + ._Bool, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + .bool, + .size_t, + .ptrdiff_t, + .zig_u8, + .zig_i8, + .zig_u16, + .zig_i16, + .zig_u32, + .zig_i32, + .zig_u64, + .zig_i64, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => |tag| try w.writeAll(@tagName(tag)), + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => |tag| { + const child_idx = cty.cast(CType.Payload.Child).?.data; + try w.print("{}*", .{try dg.renderTypePrefix(w, child_idx, .prefix, CQualifiers.init(.{ + .@"const" = switch (tag) { + .pointer, .pointer_volatile => false, + .pointer_const, .pointer_const_volatile => true, + else => unreachable, + }, + .@"volatile" = switch (tag) { + .pointer, .pointer_const => false, + .pointer_volatile, .pointer_const_volatile => true, + else => unreachable, + }, + }))}); + trailing = .no_space; + }, + + .array, + .vector, + => { + const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type; + const child_trailing = try dg.renderTypePrefix(w, child_idx, .suffix, qualifiers); + switch (parent_fix) { + .prefix => { + try w.print("{}(", .{child_trailing}); + return .no_space; + }, + .suffix => return child_trailing, + } + }, + + .fwd_struct, + .fwd_union, + .anon_struct, + .packed_anon_struct, + => |tag| try w.print("{s} {}__{d}", .{ + switch (tag) { + .fwd_struct, + .anon_struct, + .packed_anon_struct, + => "struct", + .fwd_union => "union", + else => unreachable, + }, + fmtIdent(switch (tag) { + .fwd_struct, + .fwd_union, + => mem.span(dg.module.declPtr(cty.cast(CType.Payload.FwdDecl).?.data).name), + .anon_struct, + .packed_anon_struct, + => "anon", + else => unreachable, + }), + idx, + }), + + .@"struct", + .packed_struct, + .@"union", + .packed_union, + => return dg.renderTypePrefix( + w, + cty.cast(CType.Payload.Aggregate).?.data.fwd_decl, + parent_fix, + qualifiers, + ), + + .function, + .varargs_function, + => { + const child_trailing = try dg.renderTypePrefix( + w, + cty.cast(CType.Payload.Function).?.data.return_type, + .suffix, + CQualifiers.initEmpty(), + ); + switch (parent_fix) { + .prefix => { + try w.print("{}(", .{child_trailing}); + return .no_space; + }, + .suffix => return child_trailing, + } + }, + } + + var qualifier_it = qualifiers.iterator(); + while (qualifier_it.next()) |qualifier| { + try w.print("{}{s}", .{ trailing, @tagName(qualifier) }); + trailing = .maybe_space; + } + + return trailing; + } + fn renderTypeSuffix( + dg: *DeclGen, + w: anytype, + idx: CType.Index, + parent_fix: CTypeFix, + ) @TypeOf(w).Error!void { + const cty = dg.indexToCType(idx); + switch (cty.tag()) { + .void, + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + ._Bool, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + .bool, + .size_t, + .ptrdiff_t, + .zig_u8, + .zig_i8, + .zig_u16, + .zig_i16, + .zig_u32, + .zig_i32, + .zig_u64, + .zig_i64, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => {}, + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => try dg.renderTypeSuffix(w, cty.cast(CType.Payload.Child).?.data, .prefix), + + .array, + .vector, + => { + switch (parent_fix) { + .prefix => try w.writeByte(')'), + .suffix => {}, + } + + try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len}); + try dg.renderTypeSuffix(w, cty.cast(CType.Payload.Sequence).?.data.elem_type, .suffix); + }, + + .fwd_struct, + .fwd_union, + .anon_struct, + .packed_anon_struct, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => {}, + + .function, + .varargs_function, + => |tag| { + switch (parent_fix) { + .prefix => try w.writeByte(')'), + .suffix => {}, + } + + const data = cty.cast(CType.Payload.Function).?.data; + + try w.writeByte('('); + var need_comma = false; + for (data.param_types) |param_type| { + if (need_comma) try w.writeAll(", "); + need_comma = true; + _ = try dg.renderTypePrefix(w, param_type, .suffix, CQualifiers.initEmpty()); + try dg.renderTypeSuffix(w, param_type, .suffix); + } + switch (tag) { + .function => {}, + .varargs_function => { + if (need_comma) try w.writeAll(", "); + need_comma = true; + try w.writeAll("..."); + }, + else => unreachable, + } + if (!need_comma) try w.writeAll("void"); + try w.writeByte(')'); + + try dg.renderTypeSuffix(w, data.return_type, .suffix); + }, + } + } /// Renders a type as a single identifier, generating intermediate typedefs /// if necessary. @@ -1968,277 +2248,17 @@ pub const DeclGen = struct { /// |---------------------|-----------------|---------------------| /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | + /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// fn renderType( dg: *DeclGen, w: anytype, t: Type, - kind: TypedefKind, + _: TypedefKind, ) error{ OutOfMemory, AnalysisFail }!void { - _ = try dg.typeToCType(t); - - const target = dg.module.getTarget(); - - switch (t.zigTypeTag()) { - .Void => try w.writeAll("void"), - .Bool => try w.writeAll("bool"), - .NoReturn, .Float => { - try w.writeAll("zig_"); - try t.print(w, dg.module); - }, - .Int => { - if (t.isNamedInt()) { - try w.writeAll("zig_"); - try t.print(w, dg.module); - } else { - return renderTypeUnnamed(dg, w, t, kind); - } - }, - .ErrorSet => { - return renderTypeUnnamed(dg, w, t, kind); - }, - .Pointer => { - const ptr_info = t.ptrInfo().data; - if (ptr_info.size == .Slice) { - var slice_pl = Type.Payload.ElemType{ - .base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice }, - .data = ptr_info.pointee_type, - }; - const slice_ty = Type.initPayload(&slice_pl.base); - - const name = dg.getTypedefName(slice_ty) orelse - try dg.renderSliceTypedef(slice_ty); - - return w.writeAll(name); - } - - if (ptr_info.pointee_type.zigTypeTag() == .Fn) { - const name = dg.getTypedefName(ptr_info.pointee_type) orelse - try dg.renderPtrToFnTypedef(ptr_info.pointee_type); - - return w.writeAll(name); - } - - if (ptr_info.host_size != 0) { - var host_pl = Type.Payload.Bits{ - .base = .{ .tag = .int_unsigned }, - .data = ptr_info.host_size * 8, - }; - const host_ty = Type.initPayload(&host_pl.base); - - try dg.renderType(w, host_ty, .Forward); - } else if (t.isCPtr() and ptr_info.pointee_type.eql(Type.u8, dg.module) and - (dg.decl.val.tag() == .extern_fn or - std.mem.eql(u8, std.mem.span(dg.decl.name), "main"))) - { - // This is a hack, since the c compiler expects a lot of external - // library functions to have char pointers in their signatures, but - // u8 and i8 produce unsigned char and signed char respectively, - // which in C are (not very usefully) different than char. - try w.writeAll("char"); - } else try dg.renderType(w, switch (ptr_info.pointee_type.tag()) { - .anyopaque => Type.void, - else => ptr_info.pointee_type, - }, .Forward); - if (t.isConstPtr()) try w.writeAll(" const"); - if (t.isVolatilePtr()) try w.writeAll(" volatile"); - return w.writeAll(" *"); - }, - .Array, .Vector => { - var array_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{ - .len = t.arrayLenIncludingSentinel(), - .elem_type = t.childType(), - } }; - const array_ty = Type.initPayload(&array_pl.base); - - const name = dg.getTypedefName(array_ty) orelse - try dg.renderArrayTypedef(array_ty); - - return w.writeAll(name); - }, - .Optional => { - var opt_buf: Type.Payload.ElemType = undefined; - const child_ty = t.optionalChild(&opt_buf); - - if (!child_ty.hasRuntimeBitsIgnoreComptime()) - return dg.renderType(w, Type.bool, kind); - - if (t.optionalReprIsPayload()) - return dg.renderType(w, child_ty, kind); - - switch (kind) { - .Complete => { - const name = dg.getTypedefName(t) orelse - try dg.renderOptionalTypedef(t); - - try w.writeAll(name); - }, - .Forward => { - var ptr_pl = Type.Payload.ElemType{ - .base = .{ .tag = .single_const_pointer }, - .data = t, - }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); - - try w.writeAll(name); - }, - } - }, - .ErrorUnion => { - const payload_ty = t.errorUnionPayload(); - - if (!payload_ty.hasRuntimeBitsIgnoreComptime()) - return dg.renderType(w, Type.anyerror, kind); - - var error_union_pl = Type.Payload.ErrorUnion{ - .data = .{ .error_set = Type.anyerror, .payload = payload_ty }, - }; - const error_union_ty = Type.initPayload(&error_union_pl.base); - - switch (kind) { - .Complete => { - const name = dg.getTypedefName(error_union_ty) orelse - try dg.renderErrorUnionTypedef(error_union_ty); - - try w.writeAll(name); - }, - .Forward => { - var ptr_pl = Type.Payload.ElemType{ - .base = .{ .tag = .single_const_pointer }, - .data = error_union_ty, - }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); - - try w.writeAll(name); - }, - } - }, - .Struct, .Union => |tag| if (t.containerLayout() == .Packed) { - if (t.castTag(.@"struct")) |struct_obj| { - try dg.renderType(w, struct_obj.data.backing_int_ty, kind); - } else { - var buf: Type.Payload.Bits = .{ - .base = .{ .tag = .int_unsigned }, - .data = @intCast(u16, t.bitSize(target)), - }; - try dg.renderType(w, Type.initPayload(&buf.base), kind); - } - } else if (t.isSimpleTupleOrAnonStruct()) { - const ExpectedContents = struct { types: [8]Type, values: [8]Value }; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), dg.gpa); - const allocator = stack.get(); - - var tuple_storage = std.MultiArrayList(struct { type: Type, value: Value }){}; - defer tuple_storage.deinit(allocator); - try tuple_storage.ensureTotalCapacity(allocator, t.structFieldCount()); - - const fields = t.tupleFields(); - for (fields.values, 0..) |value, index| - if (value.tag() == .unreachable_value) - tuple_storage.appendAssumeCapacity(.{ - .type = fields.types[index], - .value = value, - }); - - const tuple_slice = tuple_storage.slice(); - var tuple_pl = Type.Payload.Tuple{ .data = .{ - .types = tuple_slice.items(.type), - .values = tuple_slice.items(.value), - } }; - const tuple_ty = Type.initPayload(&tuple_pl.base); - - const name = dg.getTypedefName(tuple_ty) orelse - try dg.renderTupleTypedef(tuple_ty); - - try w.writeAll(name); - } else switch (kind) { - .Complete => { - const name = dg.getTypedefName(t) orelse switch (tag) { - .Struct => try dg.renderStructTypedef(t), - .Union => try dg.renderUnionTypedef(t), - else => unreachable, - }; - - try w.writeAll(name); - }, - .Forward => { - var ptr_pl = Type.Payload.ElemType{ - .base = .{ .tag = .single_const_pointer }, - .data = t, - }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); - - try w.writeAll(name); - }, - }, - .Enum => { - // For enums, we simply use the integer tag type. - var int_tag_buf: Type.Payload.Bits = undefined; - const int_tag_ty = t.intTagType(&int_tag_buf); - - try dg.renderType(w, int_tag_ty, kind); - }, - .Opaque => switch (t.tag()) { - .@"opaque" => { - const name = dg.getTypedefName(t) orelse - try dg.renderOpaqueTypedef(t); - - try w.writeAll(name); - }, - else => unreachable, - }, - - .Frame, - .AnyFrame, - => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ - @tagName(tag), - }), - - .Fn => unreachable, // This is a function body, not a function pointer. - - .Null, - .Undefined, - .EnumLiteral, - .ComptimeFloat, - .ComptimeInt, - .Type, - => unreachable, // must be const or comptime - } - } - - fn renderTypeUnnamed( - dg: *DeclGen, - w: anytype, - t: Type, - kind: TypedefKind, - ) error{ OutOfMemory, AnalysisFail }!void { - const target = dg.module.getTarget(); - const int_info = t.intInfo(target); - if (toCIntBits(int_info.bits)) |c_bits| - return w.print("zig_{c}{d}", .{ signAbbrev(int_info.signedness), c_bits }) - else if (loweredArrayInfo(t, target)) |array_info| { - assert(array_info.sentinel == null); - var array_pl = Type.Payload.Array{ - .base = .{ .tag = .array }, - .data = .{ .len = array_info.len, .elem_type = array_info.elem_type }, - }; - const array_ty = Type.initPayload(&array_pl.base); - - return dg.renderType(w, array_ty, kind); - } else return dg.fail("C backend: Unable to lower unnamed integer type {}", .{ - t.fmt(dg.module), - }); + const idx = try dg.typeToIndex(t); + _ = try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.initEmpty()); + try dg.renderTypeSuffix(w, idx, .suffix); } const IntCastContext = union(enum) { @@ -2348,10 +2368,10 @@ pub const DeclGen = struct { /// |---------------------|-----------------|---------------------| /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | + /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// fn renderTypecast(dg: *DeclGen, w: anytype, ty: Type) error{ OutOfMemory, AnalysisFail }!void { - return renderTypeAndName(dg, w, ty, .{ .bytes = "" }, .Mut, 0, .Complete); + try dg.renderType(w, ty, undefined); } /// Renders a type and name in field declaration/definition format. @@ -2361,7 +2381,7 @@ pub const DeclGen = struct { /// |---------------------|-----------------|---------------------| /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" | + /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// fn renderTypeAndName( dg: *DeclGen, @@ -2370,46 +2390,26 @@ pub const DeclGen = struct { name: CValue, mutability: Mutability, alignment: u32, - kind: TypedefKind, + _: TypedefKind, ) error{ OutOfMemory, AnalysisFail }!void { - var suffix = std.ArrayList(u8).init(dg.gpa); - defer suffix.deinit(); - const suffix_writer = suffix.writer(); - - // Any top-level array types are rendered here as a suffix, which - // avoids creating typedefs for every array type - const target = dg.module.getTarget(); - var render_ty = ty; - var depth: u32 = 0; - while (loweredArrayInfo(render_ty, target)) |array_info| { - const c_len = array_info.len + @boolToInt(array_info.sentinel != null); - var c_len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = c_len }; - const c_len_val = Value.initPayload(&c_len_pl.base); - - try suffix_writer.writeByte('['); - if (mutability == .ConstArgument and depth == 0) try suffix_writer.writeAll("zig_const_arr "); - try suffix.writer().print("{}]", .{try dg.fmtIntLiteral(Type.usize, c_len_val)}); - render_ty = array_info.elem_type; - depth += 1; - } - if (alignment != 0) { - const abi_alignment = ty.abiAlignment(target); + const abi_alignment = ty.abiAlignment(dg.module.getTarget()); if (alignment < abi_alignment) { try w.print("zig_under_align({}) ", .{alignment}); } else if (alignment > abi_alignment) { try w.print("zig_align({}) ", .{alignment}); } } - try dg.renderType(w, render_ty, kind); - const const_prefix = switch (mutability) { - .Const, .ConstArgument => "const ", - .Mut => "", - }; - try w.print(" {s}", .{const_prefix}); + const idx = try dg.typeToIndex(ty); + try w.print("{}", .{try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.init(.{ + .@"const" = switch (mutability) { + .Const, .ConstArgument => true, + .Mut => false, + }, + }))}); try dg.writeCValue(w, name); - try w.writeAll(suffix.items); + try dg.renderTypeSuffix(w, idx, .suffix); } fn renderTagNameFn(dg: *DeclGen, enum_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { From d513792afa4893c21d5a9635c61d8e41689d9541 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 17 Feb 2023 05:33:47 -0500 Subject: [PATCH 088/122] CBE: fix comptime checks --- src/codegen/c/type.zig | 80 ++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index c9aca79458..71132b5a97 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -817,8 +817,10 @@ pub const CType = extern union { .Struct, .Union => |zig_tag| if (ty.isTupleOrAnonStruct()) { if (lookup.isMutable()) { for (0..ty.structFieldCount()) |field_i| { - if (ty.structFieldIsComptime(field_i)) continue; - _ = try lookup.typeToIndex(ty.structFieldType(field_i), switch (kind) { + const field_ty = ty.structFieldType(field_i); + if (ty.structFieldIsComptime(field_i) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; + _ = try lookup.typeToIndex(field_ty, switch (kind) { .forward, .complete, .parameter => .complete, .global => .global, }); @@ -842,16 +844,13 @@ pub const CType = extern union { .Union => ty.cast(Type.Payload.Union).?.data.fields.count(), else => unreachable, }) |field_i| { - if (zig_tag == .Struct and ty.structFieldIsComptime(field_i)) - continue; - _ = try lookup.typeToIndex( - ty.structFieldType(field_i), - switch (kind) { - .forward => unreachable, - .complete, .parameter => .complete, - .global => .global, - }, - ); + const field_ty = ty.structFieldType(field_i); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + _ = try lookup.typeToIndex(field_ty, switch (kind) { + .forward => unreachable, + .complete, .parameter => .complete, + .global => .global, + }); } _ = try lookup.typeToIndex(ty, .forward); } @@ -953,9 +952,9 @@ pub const CType = extern union { .forward => .forward, .complete, .parameter, .global => .complete, }); - for (info.param_types, 0..) |param_ty, param_i| { - if (info.paramIsComptime(param_i)) continue; - _ = try lookup.typeToIndex(param_ty, switch (kind) { + for (info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + _ = try lookup.typeToIndex(param_type, switch (kind) { .forward => .forward, .complete, .parameter, .global => unreachable, }); @@ -1118,28 +1117,28 @@ pub const CType = extern union { var c_fields_len: usize = 0; for (0..fields_len) |field_i| { - if (ty.structFieldIsComptime(field_i)) continue; + const field_ty = ty.structFieldType(field_i); + if (ty.structFieldIsComptime(field_i) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; c_fields_len += 1; } const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); var c_field_i: usize = 0; for (0..fields_len) |field_i| { - if (ty.structFieldIsComptime(field_i)) continue; + const field_ty = ty.structFieldType(field_i); + if (ty.structFieldIsComptime(field_i) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; fields_pl[c_field_i] = .{ .name = try if (ty.isSimpleTuple()) std.fmt.allocPrintZ(arena, "f{}", .{field_i}) else arena.dupeZ(u8, ty.structFieldName(field_i)), - .type = store.set.typeToIndex( - ty.structFieldType(field_i), - target, - switch (kind) { - .forward, .complete, .parameter => .complete, - .global => .global, - }, - ).?, + .type = store.set.typeToIndex(field_ty, target, switch (kind) { + .forward, .complete, .parameter => .complete, + .global => .global, + }).?, .alignas = ty.structFieldAlign(field_i, target), }; c_field_i += 1; @@ -1211,16 +1210,16 @@ pub const CType = extern union { }; var c_params_len: usize = 0; - for (0..info.param_types.len) |param_i| { - if (info.paramIsComptime(param_i)) continue; + for (info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; c_params_len += 1; } const params_pl = try arena.alloc(Index, c_params_len); var c_param_i: usize = 0; - for (info.param_types, 0..) |param_ty, param_i| { - if (info.paramIsComptime(param_i)) continue; - params_pl[c_param_i] = store.set.typeToIndex(param_ty, target, recurse_kind).?; + for (info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + params_pl[c_param_i] = store.set.typeToIndex(param_type, target, recurse_kind).?; c_param_i += 1; } @@ -1294,7 +1293,9 @@ pub const CType = extern union { var c_field_i: usize = 0; for (0..ty.structFieldCount()) |field_i| { - if (ty.structFieldIsComptime(field_i)) continue; + const field_ty = ty.structFieldType(field_i); + if (ty.structFieldIsComptime(field_i) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; const c_field = &c_fields[c_field_i]; c_field_i += 1; @@ -1344,10 +1345,9 @@ pub const CType = extern union { if (info.param_types.len != data.param_types.len or !self.eqlRecurse(info.return_type, data.return_type, recurse_kind)) return false; - for (info.param_types, data.param_types, 0..) |param_ty, param_cty, param_i| { - if (info.paramIsComptime(param_i)) continue; - if (!self.eqlRecurse(param_ty, param_cty, recurse_kind)) - return false; + for (info.param_types, data.param_types) |param_ty, param_cty| { + if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; + if (!self.eqlRecurse(param_ty, param_cty, recurse_kind)) return false; } return true; }, @@ -1389,7 +1389,9 @@ pub const CType = extern union { std.fmt.count("f{}", .{std.math.maxInt(usize)}) ]u8 = undefined; for (0..ty.structFieldCount()) |field_i| { - if (ty.structFieldIsComptime(field_i)) continue; + const field_ty = ty.structFieldType(field_i); + if (ty.structFieldIsComptime(field_i) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; self.updateHasherRecurse( hasher, @@ -1423,9 +1425,9 @@ pub const CType = extern union { }; self.updateHasherRecurse(hasher, info.return_type, recurse_kind); - for (info.param_types, 0..) |param_ty, param_i| { - if (info.paramIsComptime(param_i)) continue; - self.updateHasherRecurse(hasher, param_ty, recurse_kind); + for (info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + self.updateHasherRecurse(hasher, param_type, recurse_kind); } }, From 3eed197c95c21d850d503687f445946e6bd429c5 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 20 Feb 2023 20:52:26 -0500 Subject: [PATCH 089/122] CBE: use stdint.h types instead of `zig_` prefixes This requires manual defines before C99 which may not have stdint.h. Also have update-zig1 leave a copy of lib/zig.h in stage1/zig.h, which allows lib/zig.h to be updated without needing to update zig1.wasm. Note that since the object already existed with the exact same contents, this completely avoids repo bloat due to zig.h changes. --- CMakeLists.txt | 2 +- build.zig | 31 + lib/zig.h | 1434 ++++++++++++----------- src/codegen/c.zig | 69 +- src/codegen/c/type.zig | 119 +- stage1/zig.h | 2486 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 3393 insertions(+), 748 deletions(-) create mode 100644 stage1/zig.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b31a0f596f..3fb011e493 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -783,7 +783,7 @@ set_target_properties(zig2 PROPERTIES COMPILE_FLAGS ${ZIG2_COMPILE_FLAGS} LINK_FLAGS ${ZIG2_LINK_FLAGS} ) -target_include_directories(zig2 PUBLIC "${CMAKE_SOURCE_DIR}/lib") +target_include_directories(zig2 PUBLIC "${CMAKE_SOURCE_DIR}/stage1") target_link_libraries(zig2 LINK_PUBLIC zigcpp) if(MSVC) diff --git a/build.zig b/build.zig index faf14cc405..175beeb422 100644 --- a/build.zig +++ b/build.zig @@ -506,8 +506,39 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { run_opt.addArg("-o"); run_opt.addFileSourceArg(.{ .path = "stage1/zig1.wasm" }); + const CopyFileStep = struct { + const Step = std.Build.Step; + const FileSource = std.Build.FileSource; + const CopyFileStep = @This(); + + step: Step, + builder: *std.Build, + source: FileSource, + dest_rel_path: []const u8, + + pub fn init(builder: *std.Build, source: FileSource, dest_rel_path: []const u8) CopyFileStep { + return CopyFileStep{ + .builder = builder, + .step = Step.init(.custom, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make), + .source = source.dupe(builder), + .dest_rel_path = builder.dupePath(dest_rel_path), + }; + } + + fn make(step: *Step) !void { + const self = @fieldParentPtr(CopyFileStep, "step", step); + const full_src_path = self.source.getPath(self.builder); + const full_dest_path = self.builder.pathFromRoot(self.dest_rel_path); + try self.builder.updateFile(full_src_path, full_dest_path); + } + }; + + const copy_zig_h = try b.allocator.create(CopyFileStep); + copy_zig_h.* = CopyFileStep.init(b, .{ .path = "lib/zig.h" }, "stage1/zig.h"); + const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm"); update_zig1_step.dependOn(&run_opt.step); + update_zig1_step.dependOn(©_zig_h.step); } fn addCompilerStep( diff --git a/lib/zig.h b/lib/zig.h index 0756d9f731..5929656985 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -1,6 +1,8 @@ #undef linux +#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ #define __STDC_WANT_IEC_60559_TYPES_EXT__ +#endif #include #include #include @@ -297,690 +299,791 @@ typedef char bool; #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) -typedef uintptr_t zig_usize; -typedef intptr_t zig_isize; -typedef signed short int zig_c_short; -typedef unsigned short int zig_c_ushort; -typedef signed int zig_c_int; -typedef unsigned int zig_c_uint; -typedef signed long int zig_c_long; -typedef unsigned long int zig_c_ulong; -typedef signed long long int zig_c_longlong; -typedef unsigned long long int zig_c_ulonglong; +#define zig_compiler_rt_abbrev_uint32_t si +#define zig_compiler_rt_abbrev_int32_t si +#define zig_compiler_rt_abbrev_uint64_t di +#define zig_compiler_rt_abbrev_int64_t di +#define zig_compiler_rt_abbrev_zig_u128 ti +#define zig_compiler_rt_abbrev_zig_i128 ti +#define zig_compiler_rt_abbrev_zig_f16 hf +#define zig_compiler_rt_abbrev_zig_f32 sf +#define zig_compiler_rt_abbrev_zig_f64 df +#define zig_compiler_rt_abbrev_zig_f80 xf +#define zig_compiler_rt_abbrev_zig_f128 tf -typedef uint8_t zig_u8; -typedef int8_t zig_i8; -typedef uint16_t zig_u16; -typedef int16_t zig_i16; -typedef uint32_t zig_u32; -typedef int32_t zig_i32; -typedef uint64_t zig_u64; -typedef int64_t zig_i64; +zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t); +zig_extern void *memset (void *, int, size_t); -#define zig_as_u8(val) UINT8_C(val) -#define zig_as_i8(val) INT8_C(val) -#define zig_as_u16(val) UINT16_C(val) -#define zig_as_i16(val) INT16_C(val) -#define zig_as_u32(val) UINT32_C(val) -#define zig_as_i32(val) INT32_C(val) -#define zig_as_u64(val) UINT64_C(val) -#define zig_as_i64(val) INT64_C(val) +/* ===================== 8/16/32/64-bit Integer Support ===================== */ + +#if __STDC_VERSION__ >= 199901L +#include +#else + +#if SCHAR_MIN == ~0x7F && SCHAR_MAX == 0x7F && UCHAR_MAX == 0xFF +typedef unsigned char uint8_t; +typedef signed char int8_t; +#define INT8_C(c) c +#define UINT8_C(c) c##U +#elif SHRT_MIN == ~0x7F && SHRT_MAX == 0x7F && USHRT_MAX == 0xFF +typedef unsigned short uint8_t; +typedef signed short int8_t; +#define INT8_C(c) c +#define UINT8_C(c) c##U +#elif INT_MIN == ~0x7F && INT_MAX == 0x7F && UINT_MAX == 0xFF +typedef unsigned int uint8_t; +typedef signed int int8_t; +#define INT8_C(c) c +#define UINT8_C(c) c##U +#elif LONG_MIN == ~0x7F && LONG_MAX == 0x7F && ULONG_MAX == 0xFF +typedef unsigned long uint8_t; +typedef signed long int8_t; +#define INT8_C(c) c##L +#define UINT8_C(c) c##LU +#elif LLONG_MIN == ~0x7F && LLONG_MAX == 0x7F && ULLONG_MAX == 0xFF +typedef unsigned long long uint8_t; +typedef signed long long int8_t; +#define INT8_C(c) c##LL +#define UINT8_C(c) c##LLU +#endif +#define INT8_MIN (~INT8_C(0x7F)) +#define INT8_MAX ( INT8_C(0x7F)) +#define UINT8_MAX ( INT8_C(0xFF)) + +#if SCHAR_MIN == ~0x7FFF && SCHAR_MAX == 0x7FFF && UCHAR_MAX == 0xFFFF +typedef unsigned char uint16_t; +typedef signed char int16_t; +#define INT16_C(c) c +#define UINT16_C(c) c##U +#elif SHRT_MIN == ~0x7FFF && SHRT_MAX == 0x7FFF && USHRT_MAX == 0xFFFF +typedef unsigned short uint16_t; +typedef signed short int16_t; +#define INT16_C(c) c +#define UINT16_C(c) c##U +#elif INT_MIN == ~0x7FFF && INT_MAX == 0x7FFF && UINT_MAX == 0xFFFF +typedef unsigned int uint16_t; +typedef signed int int16_t; +#define INT16_C(c) c +#define UINT16_C(c) c##U +#elif LONG_MIN == ~0x7FFF && LONG_MAX == 0x7FFF && ULONG_MAX == 0xFFFF +typedef unsigned long uint16_t; +typedef signed long int16_t; +#define INT16_C(c) c##L +#define UINT16_C(c) c##LU +#elif LLONG_MIN == ~0x7FFF && LLONG_MAX == 0x7FFF && ULLONG_MAX == 0xFFFF +typedef unsigned long long uint16_t; +typedef signed long long int16_t; +#define INT16_C(c) c##LL +#define UINT16_C(c) c##LLU +#endif +#define INT16_MIN (~INT16_C(0x7FFF)) +#define INT16_MAX ( INT16_C(0x7FFF)) +#define UINT16_MAX ( INT16_C(0xFFFF)) + +#if SCHAR_MIN == ~0x7FFFFFFF && SCHAR_MAX == 0x7FFFFFFF && UCHAR_MAX == 0xFFFFFFFF +typedef unsigned char uint32_t; +typedef signed char int32_t; +#define INT32_C(c) c +#define UINT32_C(c) c##U +#elif SHRT_MIN == ~0x7FFFFFFF && SHRT_MAX == 0x7FFFFFFF && USHRT_MAX == 0xFFFFFFFF +typedef unsigned short uint32_t; +typedef signed short int32_t; +#define INT32_C(c) c +#define UINT32_C(c) c##U +#elif INT_MIN == ~0x7FFFFFFF && INT_MAX == 0x7FFFFFFF && UINT_MAX == 0xFFFFFFFF +typedef unsigned int uint32_t; +typedef signed int int32_t; +#define INT32_C(c) c +#define UINT32_C(c) c##U +#elif LONG_MIN == ~0x7FFFFFFF && LONG_MAX == 0x7FFFFFFF && ULONG_MAX == 0xFFFFFFFF +typedef unsigned long uint32_t; +typedef signed long int32_t; +#define INT32_C(c) c##L +#define UINT32_C(c) c##LU +#elif LLONG_MIN == ~0x7FFFFFFF && LLONG_MAX == 0x7FFFFFFF && ULLONG_MAX == 0xFFFFFFFF +typedef unsigned long long uint32_t; +typedef signed long long int32_t; +#define INT32_C(c) c##LL +#define UINT32_C(c) c##LLU +#endif +#define INT32_MIN (~INT32_C(0x7FFFFFFF)) +#define INT32_MAX ( INT32_C(0x7FFFFFFF)) +#define UINT32_MAX ( INT32_C(0xFFFFFFFF)) + +#if SCHAR_MIN == ~0x7FFFFFFFFFFFFFFF && SCHAR_MAX == 0x7FFFFFFFFFFFFFFF && UCHAR_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned char uint64_t; +typedef signed char int64_t; +#define INT64_C(c) c +#define UINT64_C(c) c##U +#elif SHRT_MIN == ~0x7FFFFFFFFFFFFFFF && SHRT_MAX == 0x7FFFFFFFFFFFFFFF && USHRT_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned short uint64_t; +typedef signed short int64_t; +#define INT64_C(c) c +#define UINT64_C(c) c##U +#elif INT_MIN == ~0x7FFFFFFFFFFFFFFF && INT_MAX == 0x7FFFFFFFFFFFFFFF && UINT_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned int uint64_t; +typedef signed int int64_t; +#define INT64_C(c) c +#define UINT64_C(c) c##U +#elif LONG_MIN == ~0x7FFFFFFFFFFFFFFF && LONG_MAX == 0x7FFFFFFFFFFFFFFF && ULONG_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned long uint64_t; +typedef signed long int64_t; +#define INT64_C(c) c##L +#define UINT64_C(c) c##LU +#elif LLONG_MIN == ~0x7FFFFFFFFFFFFFFF && LLONG_MAX == 0x7FFFFFFFFFFFFFFF && ULLONG_MAX == 0xFFFFFFFFFFFFFFFF +typedef unsigned long long uint64_t; +typedef signed long long int64_t; +#define INT64_C(c) c##LL +#define UINT64_C(c) c##LLU +#endif +#define INT64_MIN (~INT64_C(0x7FFFFFFFFFFFFFFF)) +#define INT64_MAX ( INT64_C(0x7FFFFFFFFFFFFFFF)) +#define UINT64_MAX ( INT64_C(0xFFFFFFFFFFFFFFFF)) + +typedef size_t uintptr_t; +typedef ptrdiff_t intptr_t; + +#endif -#define zig_minInt_u8 zig_as_u8(0) -#define zig_maxInt_u8 UINT8_MAX #define zig_minInt_i8 INT8_MIN #define zig_maxInt_i8 INT8_MAX -#define zig_minInt_u16 zig_as_u16(0) -#define zig_maxInt_u16 UINT16_MAX +#define zig_minInt_u8 UINT8_C(0) +#define zig_maxInt_u8 UINT8_MAX #define zig_minInt_i16 INT16_MIN #define zig_maxInt_i16 INT16_MAX -#define zig_minInt_u32 zig_as_u32(0) -#define zig_maxInt_u32 UINT32_MAX +#define zig_minInt_u16 UINT16_C(0) +#define zig_maxInt_u16 UINT16_MAX #define zig_minInt_i32 INT32_MIN #define zig_maxInt_i32 INT32_MAX -#define zig_minInt_u64 zig_as_u64(0) -#define zig_maxInt_u64 UINT64_MAX +#define zig_minInt_u32 UINT32_C(0) +#define zig_maxInt_u32 UINT32_MAX #define zig_minInt_i64 INT64_MIN #define zig_maxInt_i64 INT64_MAX +#define zig_minInt_u64 UINT64_C(0) +#define zig_maxInt_u64 UINT64_MAX -#define zig_compiler_rt_abbrev_u32 si -#define zig_compiler_rt_abbrev_i32 si -#define zig_compiler_rt_abbrev_u64 di -#define zig_compiler_rt_abbrev_i64 di -#define zig_compiler_rt_abbrev_u128 ti -#define zig_compiler_rt_abbrev_i128 ti -#define zig_compiler_rt_abbrev_f16 hf -#define zig_compiler_rt_abbrev_f32 sf -#define zig_compiler_rt_abbrev_f64 df -#define zig_compiler_rt_abbrev_f80 xf -#define zig_compiler_rt_abbrev_f128 tf - -zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); -zig_extern void *memset (void *, int, zig_usize); - -/* ==================== 8/16/32/64-bit Integer Routines ===================== */ - -#define zig_maxInt(Type, bits) zig_shr_##Type(zig_maxInt_##Type, (zig_bitSizeOf(zig_##Type) - bits)) -#define zig_expand_maxInt(Type, bits) zig_maxInt(Type, bits) -#define zig_minInt(Type, bits) zig_not_##Type(zig_maxInt(Type, bits), bits) -#define zig_expand_minInt(Type, bits) zig_minInt(Type, bits) +#define zig_intLimit(s, w, limit, bits) zig_shr_##s##w(zig_##limit##Int_##s##w, w - (bits)) +#define zig_minInt_i(w, bits) zig_intLimit(i, w, min, bits) +#define zig_maxInt_i(w, bits) zig_intLimit(i, w, max, bits) +#define zig_minInt_u(w, bits) zig_intLimit(u, w, min, bits) +#define zig_maxInt_u(w, bits) zig_intLimit(u, w, max, bits) #define zig_int_operator(Type, RhsType, operation, operator) \ - static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##RhsType rhs) { \ + static inline Type zig_##operation(Type lhs, RhsType rhs) { \ return lhs operator rhs; \ } #define zig_int_basic_operator(Type, operation, operator) \ - zig_int_operator(Type, Type, operation, operator) + zig_int_operator(Type, Type, operation, operator) #define zig_int_shift_operator(Type, operation, operator) \ - zig_int_operator(Type, u8, operation, operator) + zig_int_operator(Type, uint8_t, operation, operator) #define zig_int_helpers(w) \ - zig_int_basic_operator(u##w, and, &) \ - zig_int_basic_operator(i##w, and, &) \ - zig_int_basic_operator(u##w, or, |) \ - zig_int_basic_operator(i##w, or, |) \ - zig_int_basic_operator(u##w, xor, ^) \ - zig_int_basic_operator(i##w, xor, ^) \ - zig_int_shift_operator(u##w, shl, <<) \ - zig_int_shift_operator(i##w, shl, <<) \ - zig_int_shift_operator(u##w, shr, >>) \ + zig_int_basic_operator(uint##w##_t, and_u##w, &) \ + zig_int_basic_operator( int##w##_t, and_i##w, &) \ + zig_int_basic_operator(uint##w##_t, or_u##w, |) \ + zig_int_basic_operator( int##w##_t, or_i##w, |) \ + zig_int_basic_operator(uint##w##_t, xor_u##w, ^) \ + zig_int_basic_operator( int##w##_t, xor_i##w, ^) \ + zig_int_shift_operator(uint##w##_t, shl_u##w, <<) \ + zig_int_shift_operator( int##w##_t, shl_i##w, <<) \ + zig_int_shift_operator(uint##w##_t, shr_u##w, >>) \ \ - static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ - zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \ + static inline int##w##_t zig_shr_i##w(int##w##_t lhs, uint8_t rhs) { \ + int##w##_t sign_mask = lhs < INT##w##_C(0) ? -INT##w##_C(1) : INT##w##_C(0); \ return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ } \ \ - static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \ - return val ^ zig_maxInt(u##w, bits); \ + static inline uint##w##_t zig_not_u##w(uint##w##_t val, uint8_t bits) { \ + return val ^ zig_maxInt_u(w, bits); \ } \ \ - static inline zig_i##w zig_not_i##w(zig_i##w val, zig_u8 bits) { \ + static inline int##w##_t zig_not_i##w(int##w##_t val, uint8_t bits) { \ (void)bits; \ return ~val; \ } \ \ - static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \ - return val & zig_maxInt(u##w, bits); \ + static inline uint##w##_t zig_wrap_u##w(uint##w##_t val, uint8_t bits) { \ + return val & zig_maxInt_u(w, bits); \ } \ \ - static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \ - return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \ - ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \ + static inline int##w##_t zig_wrap_i##w(int##w##_t val, uint8_t bits) { \ + return (val & UINT##w##_C(1) << (bits - UINT8_C(1))) != 0 \ + ? val | zig_minInt_i(w, bits) : val & zig_maxInt_i(w, bits); \ } \ \ - zig_int_basic_operator(u##w, div_floor, /) \ + zig_int_basic_operator(uint##w##_t, div_floor_u##w, /) \ \ - static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \ - return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \ + static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \ + return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < INT##w##_C(0)); \ } \ \ - zig_int_basic_operator(u##w, mod, %) \ + zig_int_basic_operator(uint##w##_t, mod_u##w, %) \ \ - static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \ - zig_i##w rem = lhs % rhs; \ - return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \ + static inline int##w##_t zig_mod_i##w(int##w##_t lhs, int##w##_t rhs) { \ + int##w##_t rem = lhs % rhs; \ + return rem + (((lhs ^ rhs) & rem) < INT##w##_C(0) ? rhs : INT##w##_C(0)); \ } \ \ - static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ + static inline uint##w##_t zig_shlw_u##w(uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ } \ \ - static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ - return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \ + static inline int##w##_t zig_shlw_i##w(int##w##_t lhs, uint8_t rhs, uint8_t bits) { \ + return zig_wrap_i##w((int##w##_t)zig_shl_u##w((uint##w##_t)lhs, (uint##w##_t)rhs), bits); \ } \ \ - static inline zig_u##w zig_addw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + static inline uint##w##_t zig_addw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ return zig_wrap_u##w(lhs + rhs, bits); \ } \ \ - static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \ + static inline int##w##_t zig_addw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs + (uint##w##_t)rhs), bits); \ } \ \ - static inline zig_u##w zig_subw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + static inline uint##w##_t zig_subw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ return zig_wrap_u##w(lhs - rhs, bits); \ } \ \ - static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \ + static inline int##w##_t zig_subw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs - (uint##w##_t)rhs), bits); \ } \ \ - static inline zig_u##w zig_mulw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + static inline uint##w##_t zig_mulw_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ return zig_wrap_u##w(lhs * rhs, bits); \ } \ \ - static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \ + static inline int##w##_t zig_mulw_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + return zig_wrap_i##w((int##w##_t)((uint##w##_t)lhs * (uint##w##_t)rhs), bits); \ } zig_int_helpers(8) zig_int_helpers(16) zig_int_helpers(32) zig_int_helpers(64) -static inline bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +static inline bool zig_addo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_u32 full_res; + uint32_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u32(full_res, bits); - return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); + return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); #else *res = zig_addw_u32(lhs, rhs, bits); return *res < lhs; #endif } -static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n, - const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +static inline void zig_vaddo_u32(uint8_t *ov, uint32_t *res, int n, + const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); } -zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); -static inline bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +zig_extern int32_t __addosi4(int32_t lhs, int32_t rhs, int *overflow); +static inline bool zig_addo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_i32 full_res; + int32_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i32 full_res = __addosi4(lhs, rhs, &overflow_int); + int overflow_int; + int32_t full_res = __addosi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i32(full_res, bits); - return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); + return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n, - const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +static inline void zig_vaddo_i32(uint8_t *ov, int32_t *res, int n, + const int32_t *lhs, const int32_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +static inline bool zig_addo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_u64 full_res; + uint64_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u64(full_res, bits); - return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); + return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); #else *res = zig_addw_u64(lhs, rhs, bits); return *res < lhs; #endif } -static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n, - const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +static inline void zig_vaddo_u64(uint8_t *ov, uint64_t *res, int n, + const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); } -zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); -static inline bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +zig_extern int64_t __addodi4(int64_t lhs, int64_t rhs, int *overflow); +static inline bool zig_addo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_i64 full_res; + int64_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i64 full_res = __addodi4(lhs, rhs, &overflow_int); + int overflow_int; + int64_t full_res = __addodi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i64(full_res, bits); - return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); + return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n, - const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +static inline void zig_vaddo_i64(uint8_t *ov, int64_t *res, int n, + const int64_t *lhs, const int64_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_addo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_u8 full_res; + uint8_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u8(full_res, bits); - return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); + return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u8)full_res; + *res = (uint8_t)full_res; return overflow; #endif } -static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n, - const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +static inline void zig_vaddo_u8(uint8_t *ov, uint8_t *res, int n, + const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +static inline bool zig_addo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_i8 full_res; + int8_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_i8(full_res, bits); - return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); + return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i8)full_res; + *res = (int8_t)full_res; return overflow; #endif } -static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n, - const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +static inline void zig_vaddo_i8(uint8_t *ov, int8_t *res, int n, + const int8_t *lhs, const int8_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +static inline bool zig_addo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_u16 full_res; + uint16_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u16(full_res, bits); - return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); + return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u16)full_res; + *res = (uint16_t)full_res; return overflow; #endif } -static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n, - const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +static inline void zig_vaddo_u16(uint8_t *ov, uint16_t *res, int n, + const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +static inline bool zig_addo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) || defined(zig_gnuc) - zig_i16 full_res; + int16_t full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_i16(full_res, bits); - return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); + return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i16)full_res; + *res = (int16_t)full_res; return overflow; #endif } -static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n, - const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +static inline void zig_vaddo_i16(uint8_t *ov, int16_t *res, int n, + const int16_t *lhs, const int16_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +static inline bool zig_subo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_u32 full_res; + uint32_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u32(full_res, bits); - return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); + return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); #else *res = zig_subw_u32(lhs, rhs, bits); return *res > lhs; #endif } -static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n, - const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +static inline void zig_vsubo_u32(uint8_t *ov, uint32_t *res, int n, + const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); } -zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); -static inline bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +zig_extern int32_t __subosi4(int32_t lhs, int32_t rhs, int *overflow); +static inline bool zig_subo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_i32 full_res; + int32_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i32 full_res = __subosi4(lhs, rhs, &overflow_int); + int overflow_int; + int32_t full_res = __subosi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i32(full_res, bits); - return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); + return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n, - const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +static inline void zig_vsubo_i32(uint8_t *ov, int32_t *res, int n, + const int32_t *lhs, const int32_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +static inline bool zig_subo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_u64 full_res; + uint64_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u64(full_res, bits); - return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); + return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); #else *res = zig_subw_u64(lhs, rhs, bits); return *res > lhs; #endif } -static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n, - const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +static inline void zig_vsubo_u64(uint8_t *ov, uint64_t *res, int n, + const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); } -zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); -static inline bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +zig_extern int64_t __subodi4(int64_t lhs, int64_t rhs, int *overflow); +static inline bool zig_subo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_i64 full_res; + int64_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i64 full_res = __subodi4(lhs, rhs, &overflow_int); + int overflow_int; + int64_t full_res = __subodi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i64(full_res, bits); - return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); + return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n, - const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +static inline void zig_vsubo_i64(uint8_t *ov, int64_t *res, int n, + const int64_t *lhs, const int64_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_subo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_u8 full_res; + uint8_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u8(full_res, bits); - return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); + return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u8)full_res; + *res = (uint8_t)full_res; return overflow; #endif } -static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n, - const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +static inline void zig_vsubo_u8(uint8_t *ov, uint8_t *res, int n, + const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +static inline bool zig_subo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_i8 full_res; + int8_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_i8(full_res, bits); - return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); + return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i8)full_res; + *res = (int8_t)full_res; return overflow; #endif } -static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n, - const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +static inline void zig_vsubo_i8(uint8_t *ov, int8_t *res, int n, + const int8_t *lhs, const int8_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +static inline bool zig_subo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_u16 full_res; + uint16_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u16(full_res, bits); - return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); + return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u16)full_res; + *res = (uint16_t)full_res; return overflow; #endif } -static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n, - const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +static inline void zig_vsubo_u16(uint8_t *ov, uint16_t *res, int n, + const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +static inline bool zig_subo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) || defined(zig_gnuc) - zig_i16 full_res; + int16_t full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_i16(full_res, bits); - return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); + return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i16)full_res; + *res = (int16_t)full_res; return overflow; #endif } -static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n, - const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +static inline void zig_vsubo_i16(uint8_t *ov, int16_t *res, int n, + const int16_t *lhs, const int16_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +static inline bool zig_mulo_u32(uint32_t *res, uint32_t lhs, uint32_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_u32 full_res; + uint32_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u32(full_res, bits); - return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); + return overflow || full_res < zig_minInt_u(32, bits) || full_res > zig_maxInt_u(32, bits); #else *res = zig_mulw_u32(lhs, rhs, bits); - return rhs != zig_as_u32(0) && lhs > zig_maxInt(u32, bits) / rhs; + return rhs != UINT32_C(0) && lhs > zig_maxInt_u(32, bits) / rhs; #endif } -static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n, - const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +static inline void zig_vmulo_u32(uint8_t *ov, uint32_t *res, int n, + const uint32_t *lhs, const uint32_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); } -zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); -static inline bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +zig_extern int32_t __mulosi4(int32_t lhs, int32_t rhs, int *overflow); +static inline bool zig_mulo_i32(int32_t *res, int32_t lhs, int32_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_i32 full_res; + int32_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i32 full_res = __mulosi4(lhs, rhs, &overflow_int); + int overflow_int; + int32_t full_res = __mulosi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i32(full_res, bits); - return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); + return overflow || full_res < zig_minInt_i(32, bits) || full_res > zig_maxInt_i(32, bits); } -static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n, - const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +static inline void zig_vmulo_i32(uint8_t *ov, int32_t *res, int n, + const int32_t *lhs, const int32_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +static inline bool zig_mulo_u64(uint64_t *res, uint64_t lhs, uint64_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_u64 full_res; + uint64_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u64(full_res, bits); - return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); + return overflow || full_res < zig_minInt_u(64, bits) || full_res > zig_maxInt_u(64, bits); #else *res = zig_mulw_u64(lhs, rhs, bits); - return rhs != zig_as_u64(0) && lhs > zig_maxInt(u64, bits) / rhs; + return rhs != UINT64_C(0) && lhs > zig_maxInt_u(64, bits) / rhs; #endif } -static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n, - const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +static inline void zig_vmulo_u64(uint8_t *ov, uint64_t *res, int n, + const uint64_t *lhs, const uint64_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); } -zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); -static inline bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +zig_extern int64_t __mulodi4(int64_t lhs, int64_t rhs, int *overflow); +static inline bool zig_mulo_i64(int64_t *res, int64_t lhs, int64_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_i64 full_res; + int64_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; - zig_i64 full_res = __mulodi4(lhs, rhs, &overflow_int); + int overflow_int; + int64_t full_res = __mulodi4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i64(full_res, bits); - return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); + return overflow || full_res < zig_minInt_i(64, bits) || full_res > zig_maxInt_i(64, bits); } -static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n, - const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +static inline void zig_vmulo_i64(uint8_t *ov, int64_t *res, int n, + const int64_t *lhs, const int64_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_mulo_u8(uint8_t *res, uint8_t lhs, uint8_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_u8 full_res; + uint8_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u8(full_res, bits); - return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); + return overflow || full_res < zig_minInt_u(8, bits) || full_res > zig_maxInt_u(8, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u8)full_res; + *res = (uint8_t)full_res; return overflow; #endif } -static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n, - const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +static inline void zig_vmulo_u8(uint8_t *ov, uint8_t *res, int n, + const uint8_t *lhs, const uint8_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +static inline bool zig_mulo_i8(int8_t *res, int8_t lhs, int8_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_i8 full_res; + int8_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_i8(full_res, bits); - return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); + return overflow || full_res < zig_minInt_i(8, bits) || full_res > zig_maxInt_i(8, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i8)full_res; + *res = (int8_t)full_res; return overflow; #endif } -static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n, - const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +static inline void zig_vmulo_i8(uint8_t *ov, int8_t *res, int n, + const int8_t *lhs, const int8_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +static inline bool zig_mulo_u16(uint16_t *res, uint16_t lhs, uint16_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_u16 full_res; + uint16_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u16(full_res, bits); - return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); + return overflow || full_res < zig_minInt_u(16, bits) || full_res > zig_maxInt_u(16, bits); #else - zig_u32 full_res; + uint32_t full_res; bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); - *res = (zig_u16)full_res; + *res = (uint16_t)full_res; return overflow; #endif } -static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n, - const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +static inline void zig_vmulo_u16(uint8_t *ov, uint16_t *res, int n, + const uint16_t *lhs, const uint16_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); } -static inline bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +static inline bool zig_mulo_i16(int16_t *res, int16_t lhs, int16_t rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) || defined(zig_gnuc) - zig_i16 full_res; + int16_t full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_i16(full_res, bits); - return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); + return overflow || full_res < zig_minInt_i(16, bits) || full_res > zig_maxInt_i(16, bits); #else - zig_i32 full_res; + int32_t full_res; bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); - *res = (zig_i16)full_res; + *res = (int16_t)full_res; return overflow; #endif } -static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n, - const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +static inline void zig_vmulo_i16(uint8_t *ov, int16_t *res, int n, + const int16_t *lhs, const int16_t *rhs, uint8_t bits) { for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); } #define zig_int_builtins(w) \ - static inline bool zig_shlo_u##w(zig_u##w *res, zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ + static inline bool zig_shlo_u##w(uint##w##_t *res, uint##w##_t lhs, uint8_t rhs, uint8_t bits) { \ *res = zig_shlw_u##w(lhs, rhs, bits); \ - return lhs > zig_maxInt(u##w, bits) >> rhs; \ + return lhs > zig_maxInt_u(w, bits) >> rhs; \ } \ \ - static inline bool zig_shlo_i##w(zig_i##w *res, zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ + static inline bool zig_shlo_i##w(int##w##_t *res, int##w##_t lhs, uint8_t rhs, uint8_t bits) { \ *res = zig_shlw_i##w(lhs, rhs, bits); \ - zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \ - return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \ + int##w##_t mask = (int##w##_t)(UINT##w##_MAX << (bits - rhs - 1)); \ + return (lhs & mask) != INT##w##_C(0) && (lhs & mask) != mask; \ } \ \ - static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ - zig_u##w res; \ - if (rhs >= bits) return lhs != zig_as_u##w(0) ? zig_maxInt(u##w, bits) : lhs; \ - return zig_shlo_u##w(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + static inline uint##w##_t zig_shls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ + uint##w##_t res; \ + if (rhs >= bits) return lhs != UINT##w##_C(0) ? zig_maxInt_u(w, bits) : lhs; \ + return zig_shlo_u##w(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(w, bits) : res; \ } \ \ - static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - zig_i##w res; \ - if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ - return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + int##w##_t res; \ + if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ + return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } \ \ - static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ - zig_u##w res; \ - return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + static inline uint##w##_t zig_adds_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ + uint##w##_t res; \ + return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \ } \ \ - static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - zig_i##w res; \ + static inline int##w##_t zig_adds_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + int##w##_t res; \ if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ - return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } \ \ - static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ - zig_u##w res; \ - return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \ + static inline uint##w##_t zig_subs_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ + uint##w##_t res; \ + return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt_u(w, bits) : res; \ } \ \ - static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - zig_i##w res; \ + static inline int##w##_t zig_subs_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + int##w##_t res; \ if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ - return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + return res >= INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } \ \ - static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ - zig_u##w res; \ - return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + static inline uint##w##_t zig_muls_u##w(uint##w##_t lhs, uint##w##_t rhs, uint8_t bits) { \ + uint##w##_t res; \ + return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt_u(w, bits) : res; \ } \ \ - static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ - zig_i##w res; \ + static inline int##w##_t zig_muls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ + int##w##_t res; \ if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ - return (lhs ^ rhs) < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + return (lhs ^ rhs) < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } zig_int_builtins(8) zig_int_builtins(16) @@ -988,89 +1091,89 @@ zig_int_builtins(32) zig_int_builtins(64) #define zig_builtin8(name, val) __builtin_##name(val) -typedef zig_c_uint zig_Builtin8; +typedef unsigned int zig_Builtin8; #define zig_builtin16(name, val) __builtin_##name(val) -typedef zig_c_uint zig_Builtin16; +typedef unsigned int zig_Builtin16; #if INT_MIN <= INT32_MIN #define zig_builtin32(name, val) __builtin_##name(val) -typedef zig_c_uint zig_Builtin32; +typedef unsigned int zig_Builtin32; #elif LONG_MIN <= INT32_MIN #define zig_builtin32(name, val) __builtin_##name##l(val) -typedef zig_c_ulong zig_Builtin32; +typedef unsigned long zig_Builtin32; #endif #if INT_MIN <= INT64_MIN #define zig_builtin64(name, val) __builtin_##name(val) -typedef zig_c_uint zig_Builtin64; +typedef unsigned int zig_Builtin64; #elif LONG_MIN <= INT64_MIN #define zig_builtin64(name, val) __builtin_##name##l(val) -typedef zig_c_ulong zig_Builtin64; +typedef unsigned long zig_Builtin64; #elif LLONG_MIN <= INT64_MIN #define zig_builtin64(name, val) __builtin_##name##ll(val) -typedef zig_c_ulonglong zig_Builtin64; +typedef unsigned long long zig_Builtin64; #endif -static inline zig_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) { +static inline uint8_t zig_byte_swap_u8(uint8_t val, uint8_t bits) { return zig_wrap_u8(val >> (8 - bits), bits); } -static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) { - return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits); +static inline int8_t zig_byte_swap_i8(int8_t val, uint8_t bits) { + return zig_wrap_i8((int8_t)zig_byte_swap_u8((uint8_t)val, bits), bits); } -static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) { - zig_u16 full_res; +static inline uint16_t zig_byte_swap_u16(uint16_t val, uint8_t bits) { + uint16_t full_res; #if zig_has_builtin(bswap16) || defined(zig_gnuc) full_res = __builtin_bswap16(val); #else - full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0), 8) << 8 | - (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8), 8) >> 0; + full_res = (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 0), 8) << 8 | + (uint16_t)zig_byte_swap_u8((uint8_t)(val >> 8), 8) >> 0; #endif return zig_wrap_u16(full_res >> (16 - bits), bits); } -static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) { - return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits); +static inline int16_t zig_byte_swap_i16(int16_t val, uint8_t bits) { + return zig_wrap_i16((int16_t)zig_byte_swap_u16((uint16_t)val, bits), bits); } -static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) { - zig_u32 full_res; +static inline uint32_t zig_byte_swap_u32(uint32_t val, uint8_t bits) { + uint32_t full_res; #if zig_has_builtin(bswap32) || defined(zig_gnuc) full_res = __builtin_bswap32(val); #else - full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0), 16) << 16 | - (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16), 16) >> 0; + full_res = (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 0), 16) << 16 | + (uint32_t)zig_byte_swap_u16((uint16_t)(val >> 16), 16) >> 0; #endif return zig_wrap_u32(full_res >> (32 - bits), bits); } -static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) { - return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits); +static inline int32_t zig_byte_swap_i32(int32_t val, uint8_t bits) { + return zig_wrap_i32((int32_t)zig_byte_swap_u32((uint32_t)val, bits), bits); } -static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) { - zig_u64 full_res; +static inline uint64_t zig_byte_swap_u64(uint64_t val, uint8_t bits) { + uint64_t full_res; #if zig_has_builtin(bswap64) || defined(zig_gnuc) full_res = __builtin_bswap64(val); #else - full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0), 32) << 32 | - (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32), 32) >> 0; + full_res = (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 0), 32) << 32 | + (uint64_t)zig_byte_swap_u32((uint32_t)(val >> 32), 32) >> 0; #endif return zig_wrap_u64(full_res >> (64 - bits), bits); } -static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) { - return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits); +static inline int64_t zig_byte_swap_i64(int64_t val, uint8_t bits) { + return zig_wrap_i64((int64_t)zig_byte_swap_u64((uint64_t)val, bits), bits); } -static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { - zig_u8 full_res; +static inline uint8_t zig_bit_reverse_u8(uint8_t val, uint8_t bits) { + uint8_t full_res; #if zig_has_builtin(bitreverse8) full_res = __builtin_bitreverse8(val); #else - static zig_u8 const lut[0x10] = { + static uint8_t const lut[0x10] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; @@ -1079,62 +1182,62 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { return zig_wrap_u8(full_res >> (8 - bits), bits); } -static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) { - return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits); +static inline int8_t zig_bit_reverse_i8(int8_t val, uint8_t bits) { + return zig_wrap_i8((int8_t)zig_bit_reverse_u8((uint8_t)val, bits), bits); } -static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) { - zig_u16 full_res; +static inline uint16_t zig_bit_reverse_u16(uint16_t val, uint8_t bits) { + uint16_t full_res; #if zig_has_builtin(bitreverse16) full_res = __builtin_bitreverse16(val); #else - full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0), 8) << 8 | - (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8), 8) >> 0; + full_res = (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 0), 8) << 8 | + (uint16_t)zig_bit_reverse_u8((uint8_t)(val >> 8), 8) >> 0; #endif return zig_wrap_u16(full_res >> (16 - bits), bits); } -static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) { - return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits); +static inline int16_t zig_bit_reverse_i16(int16_t val, uint8_t bits) { + return zig_wrap_i16((int16_t)zig_bit_reverse_u16((uint16_t)val, bits), bits); } -static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) { - zig_u32 full_res; +static inline uint32_t zig_bit_reverse_u32(uint32_t val, uint8_t bits) { + uint32_t full_res; #if zig_has_builtin(bitreverse32) full_res = __builtin_bitreverse32(val); #else - full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0), 16) << 16 | - (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16), 16) >> 0; + full_res = (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 0), 16) << 16 | + (uint32_t)zig_bit_reverse_u16((uint16_t)(val >> 16), 16) >> 0; #endif return zig_wrap_u32(full_res >> (32 - bits), bits); } -static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) { - return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits); +static inline int32_t zig_bit_reverse_i32(int32_t val, uint8_t bits) { + return zig_wrap_i32((int32_t)zig_bit_reverse_u32((uint32_t)val, bits), bits); } -static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) { - zig_u64 full_res; +static inline uint64_t zig_bit_reverse_u64(uint64_t val, uint8_t bits) { + uint64_t full_res; #if zig_has_builtin(bitreverse64) full_res = __builtin_bitreverse64(val); #else - full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0), 32) << 32 | - (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32), 32) >> 0; + full_res = (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 0), 32) << 32 | + (uint64_t)zig_bit_reverse_u32((uint32_t)(val >> 32), 32) >> 0; #endif return zig_wrap_u64(full_res >> (64 - bits), bits); } -static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { - return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits); +static inline int64_t zig_bit_reverse_i64(int64_t val, uint8_t bits) { + return zig_wrap_i64((int64_t)zig_bit_reverse_u64((uint64_t)val, bits), bits); } #define zig_builtin_popcount_common(w) \ - static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \ - return zig_popcount_u##w((zig_u##w)val, bits); \ + static inline uint8_t zig_popcount_i##w(int##w##_t val, uint8_t bits) { \ + return zig_popcount_u##w((uint##w##_t)val, bits); \ } #if zig_has_builtin(popcount) || defined(zig_gnuc) #define zig_builtin_popcount(w) \ - static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \ (void)bits; \ return zig_builtin##w(popcount, val); \ } \ @@ -1142,12 +1245,12 @@ static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { zig_builtin_popcount_common(w) #else #define zig_builtin_popcount(w) \ - static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_popcount_u##w(uint##w##_t val, uint8_t bits) { \ (void)bits; \ - zig_u##w temp = val - ((val >> 1) & (zig_maxInt_u##w / 3)); \ - temp = (temp & (zig_maxInt_u##w / 5)) + ((temp >> 2) & (zig_maxInt_u##w / 5)); \ - temp = (temp + (temp >> 4)) & (zig_maxInt_u##w / 17); \ - return temp * (zig_maxInt_u##w / 255) >> (w - 8); \ + uint##w##_t temp = val - ((val >> 1) & (UINT##w##_MAX / 3)); \ + temp = (temp & (UINT##w##_MAX / 5)) + ((temp >> 2) & (UINT##w##_MAX / 5)); \ + temp = (temp + (temp >> 4)) & (UINT##w##_MAX / 17); \ + return temp * (UINT##w##_MAX / 255) >> (w - 8); \ } \ \ zig_builtin_popcount_common(w) @@ -1158,12 +1261,12 @@ zig_builtin_popcount(32) zig_builtin_popcount(64) #define zig_builtin_ctz_common(w) \ - static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \ - return zig_ctz_u##w((zig_u##w)val, bits); \ + static inline uint8_t zig_ctz_i##w(int##w##_t val, uint8_t bits) { \ + return zig_ctz_u##w((uint##w##_t)val, bits); \ } #if zig_has_builtin(ctz) || defined(zig_gnuc) #define zig_builtin_ctz(w) \ - static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \ if (val == 0) return bits; \ return zig_builtin##w(ctz, val); \ } \ @@ -1171,7 +1274,7 @@ zig_builtin_popcount(64) zig_builtin_ctz_common(w) #else #define zig_builtin_ctz(w) \ - static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_ctz_u##w(uint##w##_t val, uint8_t bits) { \ return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \ } \ \ @@ -1183,12 +1286,12 @@ zig_builtin_ctz(32) zig_builtin_ctz(64) #define zig_builtin_clz_common(w) \ - static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \ - return zig_clz_u##w((zig_u##w)val, bits); \ + static inline uint8_t zig_clz_i##w(int##w##_t val, uint8_t bits) { \ + return zig_clz_u##w((uint##w##_t)val, bits); \ } #if zig_has_builtin(clz) || defined(zig_gnuc) #define zig_builtin_clz(w) \ - static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \ if (val == 0) return bits; \ return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ } \ @@ -1196,7 +1299,7 @@ zig_builtin_ctz(64) zig_builtin_clz_common(w) #else #define zig_builtin_clz(w) \ - static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ + static inline uint8_t zig_clz_u##w(uint##w##_t val, uint8_t bits) { \ return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \ } \ \ @@ -1207,7 +1310,7 @@ zig_builtin_clz(16) zig_builtin_clz(32) zig_builtin_clz(64) -/* ======================== 128-bit Integer Routines ======================== */ +/* ======================== 128-bit Integer Support ========================= */ #if !defined(zig_has_int128) # if defined(__SIZEOF_INT128__) @@ -1222,18 +1325,18 @@ zig_builtin_clz(64) typedef unsigned __int128 zig_u128; typedef signed __int128 zig_i128; -#define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) -#define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo)) -#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) -#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) -#define zig_hi_u128(val) ((zig_u64)((val) >> 64)) -#define zig_lo_u128(val) ((zig_u64)((val) >> 0)) -#define zig_hi_i128(val) ((zig_i64)((val) >> 64)) -#define zig_lo_i128(val) ((zig_u64)((val) >> 0)) +#define zig_make_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) +#define zig_make_i128(hi, lo) ((zig_i128)zig_make_u128(hi, lo)) +#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) +#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) +#define zig_hi_u128(val) ((uint64_t)((val) >> 64)) +#define zig_lo_u128(val) ((uint64_t)((val) >> 0)) +#define zig_hi_i128(val) (( int64_t)((val) >> 64)) +#define zig_lo_i128(val) ((uint64_t)((val) >> 0)) #define zig_bitcast_u128(val) ((zig_u128)(val)) #define zig_bitcast_i128(val) ((zig_i128)(val)) #define zig_cmp_int128(Type) \ - static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ + static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (lhs > rhs) - (lhs < rhs); \ } #define zig_bit_int128(Type, operation, operator) \ @@ -1244,31 +1347,31 @@ typedef signed __int128 zig_i128; #else /* zig_has_int128 */ #if __LITTLE_ENDIAN__ || _MSC_VER -typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128; -typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128; +typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128; +typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128; #else -typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128; -typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; +typedef struct { zig_align(16) uint64_t hi; uint64_t lo; } zig_u128; +typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; #endif -#define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) -#define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) +#define zig_make_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) +#define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) #if _MSC_VER -#define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } -#define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#define zig_make_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } #else -#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) -#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) +#define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) +#define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) #endif #define zig_hi_u128(val) ((val).hi) #define zig_lo_u128(val) ((val).lo) #define zig_hi_i128(val) ((val).hi) #define zig_lo_i128(val) ((val).lo) -#define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo) -#define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo) +#define zig_bitcast_u128(val) zig_make_u128((uint64_t)(val).hi, (val).lo) +#define zig_bitcast_i128(val) zig_make_i128(( int64_t)(val).hi, (val).lo) #define zig_cmp_int128(Type) \ - static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ + static inline int32_t zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (lhs.hi == rhs.hi) \ ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ @@ -1280,10 +1383,10 @@ typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; #endif /* zig_has_int128 */ -#define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64) -#define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64) -#define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64) -#define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64) +#define zig_minInt_u128 zig_make_u128(zig_minInt_u64, zig_minInt_u64) +#define zig_maxInt_u128 zig_make_u128(zig_maxInt_u64, zig_maxInt_u64) +#define zig_minInt_i128 zig_make_i128(zig_minInt_i64, zig_minInt_u64) +#define zig_maxInt_i128 zig_make_i128(zig_maxInt_i64, zig_maxInt_u64) zig_cmp_int128(u128) zig_cmp_int128(i128) @@ -1297,28 +1400,28 @@ zig_bit_int128(i128, or, |) zig_bit_int128(u128, xor, ^) zig_bit_int128(i128, xor, ^) -static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs); +static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs); #if zig_has_int128 -static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { - return val ^ zig_maxInt(u128, bits); +static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) { + return val ^ zig_maxInt_u(128, bits); } -static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { +static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) { (void)bits; return ~val; } -static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { +static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) { return lhs >> rhs; } -static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { +static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { return lhs << rhs; } -static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { +static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { return lhs << rhs; } @@ -1363,40 +1466,40 @@ static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { } static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { - return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_as_i128(0, 0)); + return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_make_i128(0, 0)); } static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { zig_i128 rem = zig_rem_i128(lhs, rhs); - return rem + (((lhs ^ rhs) & rem) < zig_as_i128(0, 0) ? rhs : zig_as_i128(0, 0)); + return rem + (((lhs ^ rhs) & rem) < zig_make_i128(0, 0) ? rhs : zig_make_i128(0, 0)); } #else /* zig_has_int128 */ -static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { - return (zig_u128){ .hi = zig_not_u64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; +static inline zig_u128 zig_not_u128(zig_u128 val, uint8_t bits) { + return (zig_u128){ .hi = zig_not_u64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) }; } -static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { - return (zig_i128){ .hi = zig_not_i64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; +static inline zig_i128 zig_not_i128(zig_i128 val, uint8_t bits) { + return (zig_i128){ .hi = zig_not_i64(val.hi, bits - UINT8_C(64)), .lo = zig_not_u64(val.lo, UINT8_C(64)) }; } -static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { - if (rhs == zig_as_u8(0)) return lhs; - if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - zig_as_u8(64)) }; - return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (zig_as_u8(64) - rhs) | lhs.lo >> rhs }; +static inline zig_u128 zig_shr_u128(zig_u128 lhs, uint8_t rhs) { + if (rhs == UINT8_C(0)) return lhs; + if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - UINT8_C(64)) }; + return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (UINT8_C(64) - rhs) | lhs.lo >> rhs }; } -static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { - if (rhs == zig_as_u8(0)) return lhs; - if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; - return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; +static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { + if (rhs == UINT8_C(0)) return lhs; + if (rhs >= UINT8_C(64)) return (zig_u128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; + return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; } -static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { - if (rhs == zig_as_u8(0)) return lhs; - if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; - return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; +static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { + if (rhs == UINT8_C(0)) return lhs; + if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; + return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; } static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { @@ -1454,11 +1557,11 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { zig_i128 rem = zig_rem_i128(lhs, rhs); - return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0))); + return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < INT64_C(0) ? rhs : zig_make_i128(0, 0))); } static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { - return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_as_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_as_i128(0, 0)) < zig_as_i32(0))); + return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_make_i128(0, 0)) < INT32_C(0))); } #endif /* zig_has_int128 */ @@ -1471,161 +1574,161 @@ static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { } static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { - return zig_cmp_u128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; + return zig_cmp_u128(lhs, rhs) < INT32_C(0) ? lhs : rhs; } static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) { - return zig_cmp_i128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; + return zig_cmp_i128(lhs, rhs) < INT32_C(0) ? lhs : rhs; } static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) { - return zig_cmp_u128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; + return zig_cmp_u128(lhs, rhs) > INT32_C(0) ? lhs : rhs; } static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { - return zig_cmp_i128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; + return zig_cmp_i128(lhs, rhs) > INT32_C(0) ? lhs : rhs; } -static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { - zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_sub_i128(zig_as_i128(0, 0), zig_as_i128(0, 1)) : zig_as_i128(0, 0); +static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { + zig_i128 sign_mask = zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_sub_i128(zig_make_i128(0, 0), zig_make_i128(0, 1)) : zig_make_i128(0, 0); return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); } -static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) { - return zig_and_u128(val, zig_maxInt(u128, bits)); +static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) { + return zig_and_u128(val, zig_maxInt_u(128, bits)); } -static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 bits) { - return zig_as_i128(zig_wrap_i64(zig_hi_i128(val), bits - zig_as_u8(64)), zig_lo_i128(val)); +static inline zig_i128 zig_wrap_i128(zig_i128 val, uint8_t bits) { + return zig_make_i128(zig_wrap_i64(zig_hi_i128(val), bits - UINT8_C(64)), zig_lo_i128(val)); } -static inline zig_u128 zig_shlw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { +static inline zig_u128 zig_shlw_u128(zig_u128 lhs, uint8_t rhs, uint8_t bits) { return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); } -static inline zig_i128 zig_shlw_i128(zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { +static inline zig_i128 zig_shlw_i128(zig_i128 lhs, uint8_t rhs, uint8_t bits) { return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); } -static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); } -static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); } -static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); } -static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); } -static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); } -static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); } #if zig_has_int128 -static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) zig_u128 full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); *res = zig_wrap_u128(full_res, bits); - return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); + return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); #else *res = zig_addw_u128(lhs, rhs, bits); return *res < lhs; #endif } -zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { #if zig_has_builtin(add_overflow) zig_i128 full_res; bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; + int overflow_int; zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i128(full_res, bits); - return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); + return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); } -static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) zig_u128 full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); *res = zig_wrap_u128(full_res, bits); - return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); + return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); #else *res = zig_subw_u128(lhs, rhs, bits); return *res > lhs; #endif } -zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { #if zig_has_builtin(sub_overflow) zig_i128 full_res; bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; + int overflow_int; zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i128(full_res, bits); - return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); + return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); } -static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) zig_u128 full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); *res = zig_wrap_u128(full_res, bits); - return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); + return overflow || full_res < zig_minInt_u(128, bits) || full_res > zig_maxInt_u(128, bits); #else *res = zig_mulw_u128(lhs, rhs, bits); - return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs; + return rhs != zig_make_u128(0, 0) && lhs > zig_maxInt_u(128, bits) / rhs; #endif } -zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { #if zig_has_builtin(mul_overflow) zig_i128 full_res; bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); #else - zig_c_int overflow_int; + int overflow_int; zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); bool overflow = overflow_int != 0; #endif *res = zig_wrap_i128(full_res, bits); - return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); + return overflow || full_res < zig_minInt_i(128, bits) || full_res > zig_maxInt_i(128, bits); } #else /* zig_has_int128 */ -static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, zig_u8 bits) { +static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, uint8_t bits) { return overflow || - zig_cmp_u128(full_res, zig_minInt(u128, bits)) < zig_as_i32(0) || - zig_cmp_u128(full_res, zig_maxInt(u128, bits)) > zig_as_i32(0); + zig_cmp_u128(full_res, zig_minInt_u(128, bits)) < INT32_C(0) || + zig_cmp_u128(full_res, zig_maxInt_u(128, bits)) > INT32_C(0); } -static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, zig_u8 bits) { +static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, uint8_t bits) { return overflow || - zig_cmp_i128(full_res, zig_minInt(i128, bits)) < zig_as_i32(0) || - zig_cmp_i128(full_res, zig_maxInt(i128, bits)) > zig_as_i32(0); + zig_cmp_i128(full_res, zig_minInt_i(128, bits)) < INT32_C(0) || + zig_cmp_i128(full_res, zig_maxInt_i(128, bits)) > INT32_C(0); } -static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 full_res; bool overflow = zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | @@ -1634,15 +1737,15 @@ static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_ return zig_overflow_u128(overflow, full_res, bits); } -zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { - zig_c_int overflow_int; +zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { + int overflow_int; zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); *res = zig_wrap_i128(full_res, bits); return zig_overflow_i128(overflow_int, full_res, bits); } -static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 full_res; bool overflow = zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | @@ -1651,23 +1754,23 @@ static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_ return zig_overflow_u128(overflow, full_res, bits); } -zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { - zig_c_int overflow_int; +zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { + int overflow_int; zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); *res = zig_wrap_i128(full_res, bits); return zig_overflow_i128(overflow_int, full_res, bits); } -static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { *res = zig_mulw_u128(lhs, rhs, bits); - return zig_cmp_u128(*res, zig_as_u128(0, 0)) != zig_as_i32(0) && - zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); + return zig_cmp_u128(*res, zig_make_u128(0, 0)) != INT32_C(0) && + zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0); } -zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); -static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { - zig_c_int overflow_int; +zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); +static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { + int overflow_int; zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); *res = zig_wrap_i128(full_res, bits); return zig_overflow_i128(overflow_int, full_res, bits); @@ -1675,119 +1778,119 @@ static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_ #endif /* zig_has_int128 */ -static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, uint8_t rhs, uint8_t bits) { *res = zig_shlw_u128(lhs, rhs, bits); - return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); + return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt_u(128, bits), rhs)) > INT32_C(0); } -static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { +static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, uint8_t rhs, uint8_t bits) { *res = zig_shlw_i128(lhs, rhs, bits); - zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1))); - return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i32(0) && - zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i32(0); + zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - UINT8_C(1))); + return zig_cmp_i128(zig_and_i128(lhs, mask), zig_make_i128(0, 0)) != INT32_C(0) && + zig_cmp_i128(zig_and_i128(lhs, mask), mask) != INT32_C(0); } -static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; - if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i32(0)) - return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i32(0) ? zig_maxInt(u128, bits) : lhs; + if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) >= INT32_C(0)) + return zig_cmp_u128(lhs, zig_make_u128(0, 0)) != INT32_C(0) ? zig_maxInt_u(128, bits) : lhs; #if zig_has_int128 - return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res; + return zig_shlo_u128(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(128, bits) : res; #else - return zig_shlo_u128(&res, lhs, (zig_u8)rhs.lo, bits) ? zig_maxInt(u128, bits) : res; + return zig_shlo_u128(&res, lhs, (uint8_t)rhs.lo, bits) ? zig_maxInt_u(128, bits) : res; #endif } -static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; - if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < zig_as_i32(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res; - return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res; + return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } -static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; - return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; + return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res; } -static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } -static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; - return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt(u128, bits) : res; + return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt_u(128, bits) : res; } -static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(res, zig_make_i128(0, 0)) >= INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } -static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; - return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; + return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt_u(128, bits) : res; } -static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; - return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); + return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } -static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) { - if (bits <= zig_as_u8(64)) return zig_clz_u64(zig_lo_u128(val), bits); - if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - zig_as_u8(64)); - return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + (bits - zig_as_u8(64)); +static inline uint8_t zig_clz_u128(zig_u128 val, uint8_t bits) { + if (bits <= UINT8_C(64)) return zig_clz_u64(zig_lo_u128(val), bits); + if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - UINT8_C(64)); + return zig_clz_u64(zig_lo_u128(val), UINT8_C(64)) + (bits - UINT8_C(64)); } -static inline zig_u8 zig_clz_i128(zig_i128 val, zig_u8 bits) { +static inline uint8_t zig_clz_i128(zig_i128 val, uint8_t bits) { return zig_clz_u128(zig_bitcast_u128(val), bits); } -static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) { - if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64)); - return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64); +static inline uint8_t zig_ctz_u128(zig_u128 val, uint8_t bits) { + if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), UINT8_C(64)); + return zig_ctz_u64(zig_hi_u128(val), bits - UINT8_C(64)) + UINT8_C(64); } -static inline zig_u8 zig_ctz_i128(zig_i128 val, zig_u8 bits) { +static inline uint8_t zig_ctz_i128(zig_i128 val, uint8_t bits) { return zig_ctz_u128(zig_bitcast_u128(val), bits); } -static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) { - return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + - zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64)); +static inline uint8_t zig_popcount_u128(zig_u128 val, uint8_t bits) { + return zig_popcount_u64(zig_hi_u128(val), bits - UINT8_C(64)) + + zig_popcount_u64(zig_lo_u128(val), UINT8_C(64)); } -static inline zig_u8 zig_popcount_i128(zig_i128 val, zig_u8 bits) { +static inline uint8_t zig_popcount_i128(zig_i128 val, uint8_t bits) { return zig_popcount_u128(zig_bitcast_u128(val), bits); } -static inline zig_u128 zig_byte_swap_u128(zig_u128 val, zig_u8 bits) { +static inline zig_u128 zig_byte_swap_u128(zig_u128 val, uint8_t bits) { zig_u128 full_res; #if zig_has_builtin(bswap128) full_res = __builtin_bswap128(val); #else - full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)), - zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64))); + full_res = zig_make_u128(zig_byte_swap_u64(zig_lo_u128(val), UINT8_C(64)), + zig_byte_swap_u64(zig_hi_u128(val), UINT8_C(64))); #endif - return zig_shr_u128(full_res, zig_as_u8(128) - bits); + return zig_shr_u128(full_res, UINT8_C(128) - bits); } -static inline zig_i128 zig_byte_swap_i128(zig_i128 val, zig_u8 bits) { +static inline zig_i128 zig_byte_swap_i128(zig_i128 val, uint8_t bits) { return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); } -static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) { - return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)), - zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))), - zig_as_u8(128) - bits); +static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, uint8_t bits) { + return zig_shr_u128(zig_make_u128(zig_bit_reverse_u64(zig_lo_u128(val), UINT8_C(64)), + zig_bit_reverse_u64(zig_hi_u128(val), UINT8_C(64))), + UINT8_C(128) - bits); } -static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { +static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) { return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); } @@ -1810,85 +1913,85 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { #if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) #define zig_has_float_builtins 1 -#define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg) -#define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg) -#define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg) -#define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg) -#define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg) -#define zig_as_special_c_longdouble(sign, name, arg, repr) sign zig_as_c_longdouble(__builtin_##name, )(arg) +#define zig_make_special_f16(sign, name, arg, repr) sign zig_make_f16(__builtin_##name, )(arg) +#define zig_make_special_f32(sign, name, arg, repr) sign zig_make_f32(__builtin_##name, )(arg) +#define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg) +#define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg) +#define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg) +#define zig_make_special_c_longdouble(sign, name, arg, repr) sign zig_make_c_longdouble(__builtin_##name, )(arg) #else #define zig_has_float_builtins 0 -#define zig_as_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) -#define zig_as_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) -#define zig_as_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) -#define zig_as_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) -#define zig_as_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) -#define zig_as_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) +#define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) +#define zig_make_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) +#define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) +#define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) +#define zig_make_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) +#define zig_make_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) #endif #define zig_has_f16 1 #define zig_bitSizeOf_f16 16 #define zig_libc_name_f16(name) __##name##h -#define zig_as_special_constant_f16(sign, name, arg, repr) zig_as_special_f16(sign, name, arg, repr) +#define zig_make_special_constant_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr) #if FLT_MANT_DIG == 11 typedef float zig_f16; -#define zig_as_f16(fp, repr) fp##f +#define zig_make_f16(fp, repr) fp##f #elif DBL_MANT_DIG == 11 typedef double zig_f16; -#define zig_as_f16(fp, repr) fp +#define zig_make_f16(fp, repr) fp #elif LDBL_MANT_DIG == 11 #define zig_bitSizeOf_c_longdouble 16 typedef long double zig_f16; -#define zig_as_f16(fp, repr) fp##l +#define zig_make_f16(fp, repr) fp##l #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) typedef _Float16 zig_f16; -#define zig_as_f16(fp, repr) fp##f16 +#define zig_make_f16(fp, repr) fp##f16 #elif defined(__SIZEOF_FP16__) typedef __fp16 zig_f16; -#define zig_as_f16(fp, repr) fp##f16 +#define zig_make_f16(fp, repr) fp##f16 #else #undef zig_has_f16 #define zig_has_f16 0 -#define zig_repr_f16 i16 -typedef zig_i16 zig_f16; -#define zig_as_f16(fp, repr) repr -#undef zig_as_special_f16 -#define zig_as_special_f16(sign, name, arg, repr) repr -#undef zig_as_special_constant_f16 -#define zig_as_special_constant_f16(sign, name, arg, repr) repr +#define zig_bitSizeOf_repr_f16 16 +typedef int16_t zig_f16; +#define zig_make_f16(fp, repr) repr +#undef zig_make_special_f16 +#define zig_make_special_f16(sign, name, arg, repr) repr +#undef zig_make_special_constant_f16 +#define zig_make_special_constant_f16(sign, name, arg, repr) repr #endif #define zig_has_f32 1 #define zig_bitSizeOf_f32 32 #define zig_libc_name_f32(name) name##f #if _MSC_VER -#define zig_as_special_constant_f32(sign, name, arg, repr) sign zig_as_f32(zig_msvc_flt_##name, ) +#define zig_make_special_constant_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, ) #else -#define zig_as_special_constant_f32(sign, name, arg, repr) zig_as_special_f32(sign, name, arg, repr) +#define zig_make_special_constant_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr) #endif #if FLT_MANT_DIG == 24 typedef float zig_f32; -#define zig_as_f32(fp, repr) fp##f +#define zig_make_f32(fp, repr) fp##f #elif DBL_MANT_DIG == 24 typedef double zig_f32; -#define zig_as_f32(fp, repr) fp +#define zig_make_f32(fp, repr) fp #elif LDBL_MANT_DIG == 24 #define zig_bitSizeOf_c_longdouble 32 typedef long double zig_f32; -#define zig_as_f32(fp, repr) fp##l +#define zig_make_f32(fp, repr) fp##l #elif FLT32_MANT_DIG == 24 typedef _Float32 zig_f32; -#define zig_as_f32(fp, repr) fp##f32 +#define zig_make_f32(fp, repr) fp##f32 #else #undef zig_has_f32 #define zig_has_f32 0 -#define zig_repr_f32 i32 -typedef zig_i32 zig_f32; -#define zig_as_f32(fp, repr) repr -#undef zig_as_special_f32 -#define zig_as_special_f32(sign, name, arg, repr) repr -#undef zig_as_special_constant_f32 -#define zig_as_special_constant_f32(sign, name, arg, repr) repr +#define zig_bitSizeOf_repr_f32 32 +typedef int32_t zig_f32; +#define zig_make_f32(fp, repr) repr +#undef zig_make_special_f32 +#define zig_make_special_f32(sign, name, arg, repr) repr +#undef zig_make_special_constant_f32 +#define zig_make_special_constant_f32(sign, name, arg, repr) repr #endif #define zig_has_f64 1 @@ -1898,108 +2001,108 @@ typedef zig_i32 zig_f32; #ifdef ZIG_TARGET_ABI_MSVC #define zig_bitSizeOf_c_longdouble 64 #endif -#define zig_as_special_constant_f64(sign, name, arg, repr) sign zig_as_f64(zig_msvc_flt_##name, ) +#define zig_make_special_constant_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) #else /* _MSC_VER */ -#define zig_as_special_constant_f64(sign, name, arg, repr) zig_as_special_f64(sign, name, arg, repr) +#define zig_make_special_constant_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr) #endif /* _MSC_VER */ #if FLT_MANT_DIG == 53 typedef float zig_f64; -#define zig_as_f64(fp, repr) fp##f +#define zig_make_f64(fp, repr) fp##f #elif DBL_MANT_DIG == 53 typedef double zig_f64; -#define zig_as_f64(fp, repr) fp +#define zig_make_f64(fp, repr) fp #elif LDBL_MANT_DIG == 53 #define zig_bitSizeOf_c_longdouble 64 typedef long double zig_f64; -#define zig_as_f64(fp, repr) fp##l +#define zig_make_f64(fp, repr) fp##l #elif FLT64_MANT_DIG == 53 typedef _Float64 zig_f64; -#define zig_as_f64(fp, repr) fp##f64 +#define zig_make_f64(fp, repr) fp##f64 #elif FLT32X_MANT_DIG == 53 typedef _Float32x zig_f64; -#define zig_as_f64(fp, repr) fp##f32x +#define zig_make_f64(fp, repr) fp##f32x #else #undef zig_has_f64 #define zig_has_f64 0 -#define zig_repr_f64 i64 -typedef zig_i64 zig_f64; -#define zig_as_f64(fp, repr) repr -#undef zig_as_special_f64 -#define zig_as_special_f64(sign, name, arg, repr) repr -#undef zig_as_special_constant_f64 -#define zig_as_special_constant_f64(sign, name, arg, repr) repr +#define zig_bitSizeOf_repr_f64 64 +typedef int64_t zig_f64; +#define zig_make_f64(fp, repr) repr +#undef zig_make_special_f64 +#define zig_make_special_f64(sign, name, arg, repr) repr +#undef zig_make_special_constant_f64 +#define zig_make_special_constant_f64(sign, name, arg, repr) repr #endif #define zig_has_f80 1 #define zig_bitSizeOf_f80 80 #define zig_libc_name_f80(name) __##name##x -#define zig_as_special_constant_f80(sign, name, arg, repr) zig_as_special_f80(sign, name, arg, repr) +#define zig_make_special_constant_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr) #if FLT_MANT_DIG == 64 typedef float zig_f80; -#define zig_as_f80(fp, repr) fp##f +#define zig_make_f80(fp, repr) fp##f #elif DBL_MANT_DIG == 64 typedef double zig_f80; -#define zig_as_f80(fp, repr) fp +#define zig_make_f80(fp, repr) fp #elif LDBL_MANT_DIG == 64 #define zig_bitSizeOf_c_longdouble 80 typedef long double zig_f80; -#define zig_as_f80(fp, repr) fp##l +#define zig_make_f80(fp, repr) fp##l #elif FLT80_MANT_DIG == 64 typedef _Float80 zig_f80; -#define zig_as_f80(fp, repr) fp##f80 +#define zig_make_f80(fp, repr) fp##f80 #elif FLT64X_MANT_DIG == 64 typedef _Float64x zig_f80; -#define zig_as_f80(fp, repr) fp##f64x +#define zig_make_f80(fp, repr) fp##f64x #elif defined(__SIZEOF_FLOAT80__) typedef __float80 zig_f80; -#define zig_as_f80(fp, repr) fp##l +#define zig_make_f80(fp, repr) fp##l #else #undef zig_has_f80 #define zig_has_f80 0 -#define zig_repr_f80 i128 +#define zig_bitSizeOf_repr_f80 128 typedef zig_i128 zig_f80; -#define zig_as_f80(fp, repr) repr -#undef zig_as_special_f80 -#define zig_as_special_f80(sign, name, arg, repr) repr -#undef zig_as_special_constant_f80 -#define zig_as_special_constant_f80(sign, name, arg, repr) repr +#define zig_make_f80(fp, repr) repr +#undef zig_make_special_f80 +#define zig_make_special_f80(sign, name, arg, repr) repr +#undef zig_make_special_constant_f80 +#define zig_make_special_constant_f80(sign, name, arg, repr) repr #endif #define zig_has_f128 1 #define zig_bitSizeOf_f128 128 #define zig_libc_name_f128(name) name##q -#define zig_as_special_constant_f128(sign, name, arg, repr) zig_as_special_f128(sign, name, arg, repr) +#define zig_make_special_constant_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr) #if FLT_MANT_DIG == 113 typedef float zig_f128; -#define zig_as_f128(fp, repr) fp##f +#define zig_make_f128(fp, repr) fp##f #elif DBL_MANT_DIG == 113 typedef double zig_f128; -#define zig_as_f128(fp, repr) fp +#define zig_make_f128(fp, repr) fp #elif LDBL_MANT_DIG == 113 #define zig_bitSizeOf_c_longdouble 128 typedef long double zig_f128; -#define zig_as_f128(fp, repr) fp##l +#define zig_make_f128(fp, repr) fp##l #elif FLT128_MANT_DIG == 113 typedef _Float128 zig_f128; -#define zig_as_f128(fp, repr) fp##f128 +#define zig_make_f128(fp, repr) fp##f128 #elif FLT64X_MANT_DIG == 113 typedef _Float64x zig_f128; -#define zig_as_f128(fp, repr) fp##f64x +#define zig_make_f128(fp, repr) fp##f64x #elif defined(__SIZEOF_FLOAT128__) typedef __float128 zig_f128; -#define zig_as_f128(fp, repr) fp##q -#undef zig_as_special_f128 -#define zig_as_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) +#define zig_make_f128(fp, repr) fp##q +#undef zig_make_special_f128 +#define zig_make_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) #else #undef zig_has_f128 #define zig_has_f128 0 -#define zig_repr_f128 i128 +#define zig_bitSizeOf_repr_f128 128 typedef zig_i128 zig_f128; -#define zig_as_f128(fp, repr) repr -#undef zig_as_special_f128 -#define zig_as_special_f128(sign, name, arg, repr) repr -#undef zig_as_special_constant_f128 -#define zig_as_special_constant_f128(sign, name, arg, repr) repr +#define zig_make_f128(fp, repr) repr +#undef zig_make_special_f128 +#define zig_make_special_f128(sign, name, arg, repr) repr +#undef zig_make_special_constant_f128 +#define zig_make_special_constant_f128(sign, name, arg, repr) repr #endif #define zig_has_c_longdouble 1 @@ -2010,17 +2113,17 @@ typedef zig_i128 zig_f128; #define zig_libc_name_c_longdouble(name) name##l #endif -#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) zig_as_special_c_longdouble(sign, name, arg, repr) +#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) zig_make_special_c_longdouble(sign, name, arg, repr) #ifdef zig_bitSizeOf_c_longdouble #ifdef ZIG_TARGET_ABI_MSVC typedef double zig_c_longdouble; #undef zig_bitSizeOf_c_longdouble #define zig_bitSizeOf_c_longdouble 64 -#define zig_as_c_longdouble(fp, repr) fp +#define zig_make_c_longdouble(fp, repr) fp #else typedef long double zig_c_longdouble; -#define zig_as_c_longdouble(fp, repr) fp##l +#define zig_make_c_longdouble(fp, repr) fp##l #endif #else /* zig_bitSizeOf_c_longdouble */ @@ -2029,13 +2132,13 @@ typedef long double zig_c_longdouble; #define zig_has_c_longdouble 0 #define zig_bitSizeOf_c_longdouble 80 #define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 -#define zig_repr_c_longdouble i128 +#define zig_bitSizeOf_repr_c_longdouble 128 typedef zig_i128 zig_c_longdouble; -#define zig_as_c_longdouble(fp, repr) repr -#undef zig_as_special_c_longdouble -#define zig_as_special_c_longdouble(sign, name, arg, repr) repr -#undef zig_as_special_constant_c_longdouble -#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) repr +#define zig_make_c_longdouble(fp, repr) repr +#undef zig_make_special_c_longdouble +#define zig_make_special_c_longdouble(sign, name, arg, repr) repr +#undef zig_make_special_constant_c_longdouble +#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) repr #endif /* zig_bitSizeOf_c_longdouble */ @@ -2073,32 +2176,35 @@ zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_lo #endif #define zig_convert_builtin(ResType, operation, ArgType, version) \ - zig_extern zig_##ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ - zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(zig_##ArgType); -zig_convert_builtin(f16, trunc, f32, 2) -zig_convert_builtin(f16, trunc, f64, 2) -zig_convert_builtin(f16, trunc, f80, 2) -zig_convert_builtin(f16, trunc, f128, 2) -zig_convert_builtin(f32, extend, f16, 2) -zig_convert_builtin(f32, trunc, f64, 2) -zig_convert_builtin(f32, trunc, f80, 2) -zig_convert_builtin(f32, trunc, f128, 2) -zig_convert_builtin(f64, extend, f16, 2) -zig_convert_builtin(f64, extend, f32, 2) -zig_convert_builtin(f64, trunc, f80, 2) -zig_convert_builtin(f64, trunc, f128, 2) -zig_convert_builtin(f80, extend, f16, 2) -zig_convert_builtin(f80, extend, f32, 2) -zig_convert_builtin(f80, extend, f64, 2) -zig_convert_builtin(f80, trunc, f128, 2) -zig_convert_builtin(f128, extend, f16, 2) -zig_convert_builtin(f128, extend, f32, 2) -zig_convert_builtin(f128, extend, f64, 2) -zig_convert_builtin(f128, extend, f80, 2) + zig_extern ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(ArgType); +zig_convert_builtin(zig_f16, trunc, zig_f32, 2) +zig_convert_builtin(zig_f16, trunc, zig_f64, 2) +zig_convert_builtin(zig_f16, trunc, zig_f80, 2) +zig_convert_builtin(zig_f16, trunc, zig_f128, 2) +zig_convert_builtin(zig_f32, extend, zig_f16, 2) +zig_convert_builtin(zig_f32, trunc, zig_f64, 2) +zig_convert_builtin(zig_f32, trunc, zig_f80, 2) +zig_convert_builtin(zig_f32, trunc, zig_f128, 2) +zig_convert_builtin(zig_f64, extend, zig_f16, 2) +zig_convert_builtin(zig_f64, extend, zig_f32, 2) +zig_convert_builtin(zig_f64, trunc, zig_f80, 2) +zig_convert_builtin(zig_f64, trunc, zig_f128, 2) +zig_convert_builtin(zig_f80, extend, zig_f16, 2) +zig_convert_builtin(zig_f80, extend, zig_f32, 2) +zig_convert_builtin(zig_f80, extend, zig_f64, 2) +zig_convert_builtin(zig_f80, trunc, zig_f128, 2) +zig_convert_builtin(zig_f128, extend, zig_f16, 2) +zig_convert_builtin(zig_f128, extend, zig_f32, 2) +zig_convert_builtin(zig_f128, extend, zig_f64, 2) +zig_convert_builtin(zig_f128, extend, zig_f80, 2) #define zig_float_negate_builtin_0(Type) \ static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ - return zig_expand_concat(zig_xor_, zig_repr_##Type)(arg, zig_expand_minInt(zig_repr_##Type, zig_bitSizeOf_##Type)); \ + return zig_expand_concat(zig_xor_i, zig_bitSizeOf_repr_##Type)( \ + arg, \ + zig_minInt_i(zig_bitSizeOf_repr_##Type, zig_bitSizeOf_##Type) \ + ); \ } #define zig_float_negate_builtin_1(Type) \ static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ @@ -2106,28 +2212,28 @@ zig_convert_builtin(f128, extend, f80, 2) } #define zig_float_less_builtin_0(Type, operation) \ - zig_extern zig_i32 zig_expand_concat(zig_expand_concat(__##operation, \ - zig_compiler_rt_abbrev_##Type), 2)(zig_##Type, zig_##Type); \ - static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ - return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 2)(lhs, rhs); \ + zig_extern int32_t zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_zig_##Type), 2)(zig_##Type, zig_##Type); \ + static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 2)(lhs, rhs); \ } #define zig_float_less_builtin_1(Type, operation) \ - static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ return (!(lhs <= rhs) - (lhs < rhs)); \ } #define zig_float_greater_builtin_0(Type, operation) \ zig_float_less_builtin_0(Type, operation) #define zig_float_greater_builtin_1(Type, operation) \ - static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + static inline int32_t zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ return ((lhs > rhs) - !(lhs >= rhs)); \ } #define zig_float_binary_builtin_0(Type, operation, operator) \ zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \ - zig_compiler_rt_abbrev_##Type), 3)(zig_##Type, zig_##Type); \ + zig_compiler_rt_abbrev_zig_##Type), 3)(zig_##Type, zig_##Type); \ static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ - return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 3)(lhs, rhs); \ + return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_zig_##Type), 3)(lhs, rhs); \ } #define zig_float_binary_builtin_1(Type, operation, operator) \ static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ @@ -2135,18 +2241,18 @@ zig_convert_builtin(f128, extend, f80, 2) } #define zig_float_builtins(Type) \ - zig_convert_builtin(i32, fix, Type, ) \ - zig_convert_builtin(u32, fixuns, Type, ) \ - zig_convert_builtin(i64, fix, Type, ) \ - zig_convert_builtin(u64, fixuns, Type, ) \ - zig_convert_builtin(i128, fix, Type, ) \ - zig_convert_builtin(u128, fixuns, Type, ) \ - zig_convert_builtin(Type, float, i32, ) \ - zig_convert_builtin(Type, floatun, u32, ) \ - zig_convert_builtin(Type, float, i64, ) \ - zig_convert_builtin(Type, floatun, u64, ) \ - zig_convert_builtin(Type, float, i128, ) \ - zig_convert_builtin(Type, floatun, u128, ) \ + zig_convert_builtin( int32_t, fix, zig_##Type, ) \ + zig_convert_builtin(uint32_t, fixuns, zig_##Type, ) \ + zig_convert_builtin( int64_t, fix, zig_##Type, ) \ + zig_convert_builtin(uint64_t, fixuns, zig_##Type, ) \ + zig_convert_builtin(zig_i128, fix, zig_##Type, ) \ + zig_convert_builtin(zig_u128, fixuns, zig_##Type, ) \ + zig_convert_builtin(zig_##Type, float, int32_t, ) \ + zig_convert_builtin(zig_##Type, floatun, uint32_t, ) \ + zig_convert_builtin(zig_##Type, float, int64_t, ) \ + zig_convert_builtin(zig_##Type, floatun, uint64_t, ) \ + zig_convert_builtin(zig_##Type, float, zig_i128, ) \ + zig_convert_builtin(zig_##Type, floatun, zig_u128, ) \ zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \ zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \ zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \ @@ -2332,17 +2438,17 @@ zig_msvc_flt_atomics(f64, u64, 64) #if _M_IX86 static inline void zig_msvc_atomic_barrier() { - zig_i32 barrier; + int32_t barrier; __asm { xchg barrier, eax } } -static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, zig_u32* arg) { +static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, uint32_t* arg) { return _InterlockedExchangePointer(obj, arg); } -static inline void zig_msvc_atomic_store_p32(void** obj, zig_u32* arg) { +static inline void zig_msvc_atomic_store_p32(void** obj, uint32_t* arg) { _InterlockedExchangePointer(obj, arg); } @@ -2360,11 +2466,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir return exchanged; } #else /* _M_IX86 */ -static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, zig_u64* arg) { +static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, uint64_t* arg) { return _InterlockedExchangePointer(obj, arg); } -static inline void zig_msvc_atomic_store_p64(void** obj, zig_u64* arg) { +static inline void zig_msvc_atomic_store_p64(void** obj, uint64_t* arg) { _InterlockedExchangePointer(obj, arg); } @@ -2383,11 +2489,11 @@ static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desir } static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) { - return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_i64*)expected); + return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (int64_t*)expected); } static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { - return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected); + return _InterlockedCompareExchange128((int64_t volatile*)obj, desired.hi, desired.lo, (uint64_t*)expected); } #define zig_msvc_atomics_128xchg(Type) \ @@ -2429,7 +2535,7 @@ zig_msvc_atomics_128op(u128, max) #endif /* _MSC_VER && (_M_IX86 || _M_X64) */ -/* ========================= Special Case Intrinsics ========================= */ +/* ======================== Special Case Intrinsics ========================= */ #if (_MSC_VER && _M_X64) || defined(__x86_64__) @@ -2459,8 +2565,8 @@ static inline void* zig_x86_windows_teb(void) { #if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__) -static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, zig_u32* ebx, zig_u32* ecx, zig_u32* edx) { - zig_u32 cpu_info[4]; +static inline void zig_x86_cpuid(uint32_t leaf_id, uint32_t subid, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) { + uint32_t cpu_info[4]; #if _MSC_VER __cpuidex(cpu_info, leaf_id, subid); #else @@ -2472,12 +2578,12 @@ static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, z *edx = cpu_info[3]; } -static inline zig_u32 zig_x86_get_xcr0(void) { +static inline uint32_t zig_x86_get_xcr0(void) { #if _MSC_VER - return (zig_u32)_xgetbv(0); + return (uint32_t)_xgetbv(0); #else - zig_u32 eax; - zig_u32 edx; + uint32_t eax; + uint32_t edx; __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); return eax; #endif diff --git a/src/codegen/c.zig b/src/codegen/c.zig index aa540d6984..bed3a37a5c 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -752,7 +752,7 @@ pub const DeclGen = struct { try writer.writeAll("zig_cast_"); try dg.renderTypeForBuiltinFnName(writer, ty); - try writer.writeAll(" zig_as_"); + try writer.writeAll(" zig_make_"); try dg.renderTypeForBuiltinFnName(writer, ty); try writer.writeByte('('); switch (bits) { @@ -962,7 +962,7 @@ pub const DeclGen = struct { try writer.writeByte(' '); var empty = true; if (std.math.isFinite(f128_val)) { - try writer.writeAll("zig_as_"); + try writer.writeAll("zig_make_"); try dg.renderTypeForBuiltinFnName(writer, ty); try writer.writeByte('('); switch (bits) { @@ -997,7 +997,7 @@ pub const DeclGen = struct { // return dg.fail("Only quiet nans are supported in global variable initializers", .{}); } - try writer.writeAll("zig_as_special_"); + try writer.writeAll("zig_make_special_"); if (location == .StaticInitializer) try writer.writeAll("constant_"); try dg.renderTypeForBuiltinFnName(writer, ty); try writer.writeByte('('); @@ -2016,14 +2016,16 @@ pub const DeclGen = struct { .bool, .size_t, .ptrdiff_t, - .zig_u8, - .zig_i8, - .zig_u16, - .zig_i16, - .zig_u32, - .zig_i32, - .zig_u64, - .zig_i64, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, .zig_u128, .zig_i128, .zig_f16, @@ -2158,14 +2160,16 @@ pub const DeclGen = struct { .bool, .size_t, .ptrdiff_t, - .zig_u8, - .zig_i8, - .zig_u16, - .zig_i16, - .zig_u32, - .zig_i32, - .zig_u64, - .zig_i64, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, .zig_u128, .zig_i128, .zig_f16, @@ -2285,16 +2289,16 @@ pub const DeclGen = struct { /// Renders a cast to an int type, from either an int or a pointer. /// /// Some platforms don't have 128 bit integers, so we need to use - /// the zig_as_ and zig_lo_ macros in those cases. + /// the zig_make_ and zig_lo_ macros in those cases. /// /// | Dest type bits | Src type | Result /// |------------------|------------------|---------------------------| /// | < 64 bit integer | pointer | (zig_)(zig_size)src /// | < 64 bit integer | < 64 bit integer | (zig_)src /// | < 64 bit integer | > 64 bit integer | zig_lo(src) - /// | > 64 bit integer | pointer | zig_as_(0, (zig_size)src) - /// | > 64 bit integer | < 64 bit integer | zig_as_(0, src) - /// | > 64 bit integer | > 64 bit integer | zig_as_(zig_hi_(src), zig_lo_(src)) + /// | > 64 bit integer | pointer | zig_make_(0, (zig_size)src) + /// | > 64 bit integer | < 64 bit integer | zig_make_(0, src) + /// | > 64 bit integer | > 64 bit integer | zig_make_(zig_hi_(src), zig_lo_(src)) fn renderIntCast(dg: *DeclGen, w: anytype, dest_ty: Type, context: IntCastContext, src_ty: Type, location: ValueRenderLocation) !void { const target = dg.module.getTarget(); const dest_bits = dest_ty.bitSize(target); @@ -2332,7 +2336,7 @@ pub const DeclGen = struct { try context.writeValue(dg, w, src_ty, .FunctionArgument); try w.writeByte(')'); } else if (dest_bits > 64 and src_bits <= 64) { - try w.writeAll("zig_as_"); + try w.writeAll("zig_make_"); try dg.renderTypeForBuiltinFnName(w, dest_ty); try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral? if (src_is_ptr) { @@ -2344,7 +2348,7 @@ pub const DeclGen = struct { try w.writeByte(')'); } else { assert(!src_is_ptr); - try w.writeAll("zig_as_"); + try w.writeAll("zig_make_"); try dg.renderTypeForBuiltinFnName(w, dest_ty); try w.writeAll("(zig_hi_"); try dg.renderTypeForBuiltinFnName(w, src_eff_ty); @@ -3858,7 +3862,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; if (cant_cast) { if (src_ty.bitSize(target) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{}); - try writer.writeAll("zig_as_"); + try writer.writeAll("zig_make_"); try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeAll("(0, "); } else { @@ -7355,7 +7359,7 @@ fn formatIntLiteral( use_twos_comp = true; } else { // TODO: Use fmtIntLiteral for 0? - try writer.print("zig_sub_{c}{d}(zig_as_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits }); + try writer.print("zig_sub_{c}{d}(zig_make_{c}{d}(0, 0), ", .{ signAbbrev(int_info.signedness), c_bits, signAbbrev(int_info.signedness), c_bits }); } } else { try writer.writeByte('-'); @@ -7365,11 +7369,16 @@ fn formatIntLiteral( switch (data.ty.tag()) { .c_short, .c_ushort, .c_int, .c_uint, .c_long, .c_ulong, .c_longlong, .c_ulonglong => {}, else => { - if (int_info.bits > 64 and data.location != null and data.location.? == .StaticInitializer) { + if (int_info.bits <= 64) { + try writer.print("{s}INT{d}_C(", .{ switch (int_info.signedness) { + .signed => "", + .unsigned => "U", + }, c_bits }); + } else if (data.location != null and data.location.? == .StaticInitializer) { // MSVC treats casting the struct initializer as not constant (C2099), so an alternate form is used in global initializers - try writer.print("zig_as_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); + try writer.print("zig_make_constant_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); } else { - try writer.print("zig_as_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); + try writer.print("zig_make_{c}{d}(", .{ signAbbrev(int_info.signedness), c_bits }); } }, } diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 71132b5a97..601c15abee 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -77,19 +77,24 @@ pub const CType = extern union { @"long double", // C header types - bool, // stdbool.h - size_t, // stddef.h - ptrdiff_t, // stddef.h + // - stdbool.h + bool, + // - stddef.h + size_t, + ptrdiff_t, + // - stdint.h + uint8_t, + int8_t, + uint16_t, + int16_t, + uint32_t, + int32_t, + uint64_t, + int64_t, + uintptr_t, + intptr_t, // zig.h types - zig_u8, - zig_i8, - zig_u16, - zig_i16, - zig_u32, - zig_i32, - zig_u64, - zig_i64, zig_u128, zig_i128, zig_f16, @@ -149,14 +154,16 @@ pub const CType = extern union { .bool, .size_t, .ptrdiff_t, - .zig_u8, - .zig_i8, - .zig_u16, - .zig_i16, - .zig_u32, - .zig_i32, - .zig_u64, - .zig_i64, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, .zig_u128, .zig_i128, .zig_f16, @@ -428,14 +435,16 @@ pub const CType = extern union { .bool, .size_t, .ptrdiff_t, - .zig_u8, - .zig_i8, - .zig_u16, - .zig_i16, - .zig_u32, - .zig_i32, - .zig_u64, - .zig_i64, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, .zig_u128, .zig_i128, .zig_f16, @@ -526,14 +535,16 @@ pub const CType = extern union { .bool, .size_t, .ptrdiff_t, - .zig_u8, - .zig_i8, - .zig_u16, - .zig_i16, - .zig_u32, - .zig_i32, - .zig_u64, - .zig_i64, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, .zig_u128, .zig_i128, .zig_f16, @@ -628,20 +639,20 @@ pub const CType = extern union { return switch (bits) { 0 => .void, 1...8 => switch (signedness) { - .unsigned => .zig_u8, - .signed => .zig_i8, + .unsigned => .uint8_t, + .signed => .int8_t, }, 9...16 => switch (signedness) { - .unsigned => .zig_u16, - .signed => .zig_i16, + .unsigned => .uint16_t, + .signed => .int16_t, }, 17...32 => switch (signedness) { - .unsigned => .zig_u32, - .signed => .zig_i32, + .unsigned => .uint32_t, + .signed => .int32_t, }, 33...64 => switch (signedness) { - .unsigned => .zig_u64, - .signed => .zig_i64, + .unsigned => .uint64_t, + .signed => .int64_t, }, 65...128 => switch (signedness) { .unsigned => .zig_u128, @@ -712,8 +723,8 @@ pub const CType = extern union { if (!ty.isFnOrHasRuntimeBitsIgnoreComptime()) self.init(.void) else if (ty.isAbiInt()) switch (ty.tag()) { - .usize => self.init(.size_t), - .isize => self.init(.ptrdiff_t), + .usize => self.init(.uintptr_t), + .isize => self.init(.intptr_t), .c_short => self.init(.short), .c_ushort => self.init(.@"unsigned short"), .c_int => self.init(.int), @@ -996,14 +1007,16 @@ pub const CType = extern union { .bool, .size_t, .ptrdiff_t, - .zig_u8, - .zig_i8, - .zig_u16, - .zig_i16, - .zig_u32, - .zig_i32, - .zig_u64, - .zig_i64, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, .zig_u128, .zig_i128, .zig_f16, diff --git a/stage1/zig.h b/stage1/zig.h new file mode 100644 index 0000000000..0756d9f731 --- /dev/null +++ b/stage1/zig.h @@ -0,0 +1,2486 @@ +#undef linux + +#define __STDC_WANT_IEC_60559_TYPES_EXT__ +#include +#include +#include +#include + +#if _MSC_VER +#include +#elif defined(__i386__) || defined(__x86_64__) +#include +#endif + +#if !defined(__cplusplus) && __STDC_VERSION__ <= 201710L +#if __STDC_VERSION__ >= 199901L +#include +#else +typedef char bool; +#define false 0 +#define true 1 +#endif +#endif + +#if defined(__has_builtin) +#define zig_has_builtin(builtin) __has_builtin(__builtin_##builtin) +#else +#define zig_has_builtin(builtin) 0 +#endif + +#if defined(__has_attribute) +#define zig_has_attribute(attribute) __has_attribute(attribute) +#else +#define zig_has_attribute(attribute) 0 +#endif + +#if __STDC_VERSION__ >= 201112L +#define zig_threadlocal _Thread_local +#elif defined(__GNUC__) +#define zig_threadlocal __thread +#elif _MSC_VER +#define zig_threadlocal __declspec(thread) +#else +#define zig_threadlocal zig_threadlocal_unavailable +#endif + +#if defined(__clang__) +#define zig_clang +#elif defined(__GNUC__) +#define zig_gnuc +#endif + +#if _MSC_VER +#define zig_const_arr +#define zig_callconv(c) __##c +#else +#define zig_const_arr static const +#define zig_callconv(c) __attribute__((c)) +#endif + +#if zig_has_attribute(naked) || defined(zig_gnuc) +#define zig_naked_decl __attribute__((naked)) +#define zig_naked __attribute__((naked)) +#elif defined(_MSC_VER) +#define zig_naked_decl +#define zig_naked __declspec(naked) +#else +#define zig_naked_decl zig_naked_unavailable +#define zig_naked zig_naked_unavailable +#endif + +#if zig_has_attribute(cold) +#define zig_cold __attribute__((cold)) +#else +#define zig_cold +#endif + +#if __STDC_VERSION__ >= 199901L +#define zig_restrict restrict +#elif defined(__GNUC__) +#define zig_restrict __restrict +#else +#define zig_restrict +#endif + +#if __STDC_VERSION__ >= 201112L +#define zig_align(alignment) _Alignas(alignment) +#elif zig_has_attribute(aligned) +#define zig_align(alignment) __attribute__((aligned(alignment))) +#elif _MSC_VER +#define zig_align(alignment) __declspec(align(alignment)) +#else +#define zig_align zig_align_unavailable +#endif + +#if zig_has_attribute(aligned) +#define zig_under_align(alignment) __attribute__((aligned(alignment))) +#elif _MSC_VER +#define zig_under_align(alignment) zig_align(alignment) +#else +#define zig_align zig_align_unavailable +#endif + +#if zig_has_attribute(aligned) +#define zig_align_fn(alignment) __attribute__((aligned(alignment))) +#elif _MSC_VER +#define zig_align_fn(alignment) +#else +#define zig_align_fn zig_align_fn_unavailable +#endif + +#if zig_has_attribute(packed) +#define zig_packed(definition) __attribute__((packed)) definition +#elif _MSC_VER +#define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack()) +#else +#define zig_packed(definition) zig_packed_unavailable +#endif + +#if zig_has_attribute(section) +#define zig_linksection(name, def, ...) def __attribute__((section(name))) +#elif _MSC_VER +#define zig_linksection(name, def, ...) __pragma(section(name, __VA_ARGS__)) __declspec(allocate(name)) def +#else +#define zig_linksection(name, def, ...) zig_linksection_unavailable +#endif + +#if zig_has_builtin(unreachable) || defined(zig_gnuc) +#define zig_unreachable() __builtin_unreachable() +#else +#define zig_unreachable() +#endif + +#if defined(__cplusplus) +#define zig_extern extern "C" +#else +#define zig_extern extern +#endif + +#if zig_has_attribute(alias) +#define zig_export(sig, symbol, name) zig_extern sig __attribute__((alias(symbol))) +#elif _MSC_VER +#if _M_X64 +#define zig_export(sig, symbol, name) sig;\ + __pragma(comment(linker, "/alternatename:" name "=" symbol )) +#else /*_M_X64 */ +#define zig_export(sig, symbol, name) sig;\ + __pragma(comment(linker, "/alternatename:_" name "=_" symbol )) +#endif /*_M_X64 */ +#else +#define zig_export(sig, symbol, name) __asm(name " = " symbol) +#endif + +#if zig_has_builtin(debugtrap) +#define zig_breakpoint() __builtin_debugtrap() +#elif zig_has_builtin(trap) || defined(zig_gnuc) +#define zig_breakpoint() __builtin_trap() +#elif defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) +#define zig_breakpoint() __debugbreak() +#elif defined(__i386__) || defined(__x86_64__) +#define zig_breakpoint() __asm__ volatile("int $0x03"); +#else +#define zig_breakpoint() raise(SIGTRAP) +#endif + +#if zig_has_builtin(return_address) || defined(zig_gnuc) +#define zig_return_address() __builtin_extract_return_addr(__builtin_return_address(0)) +#elif defined(_MSC_VER) +#define zig_return_address() _ReturnAddress() +#else +#define zig_return_address() 0 +#endif + +#if zig_has_builtin(frame_address) || defined(zig_gnuc) +#define zig_frame_address() __builtin_frame_address(0) +#else +#define zig_frame_address() 0 +#endif + +#if zig_has_builtin(prefetch) || defined(zig_gnuc) +#define zig_prefetch(addr, rw, locality) __builtin_prefetch(addr, rw, locality) +#else +#define zig_prefetch(addr, rw, locality) +#endif + +#if zig_has_builtin(memory_size) && zig_has_builtin(memory_grow) +#define zig_wasm_memory_size(index) __builtin_wasm_memory_size(index) +#define zig_wasm_memory_grow(index, delta) __builtin_wasm_memory_grow(index, delta) +#else +#define zig_wasm_memory_size(index) zig_unimplemented() +#define zig_wasm_memory_grow(index, delta) zig_unimplemented() +#endif + +#define zig_concat(lhs, rhs) lhs##rhs +#define zig_expand_concat(lhs, rhs) zig_concat(lhs, rhs) + +#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) +#include +#define zig_atomic(type) _Atomic(type) +#define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail) +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) atomic_compare_exchange_weak_explicit (obj, &(expected), desired, succ, fail) +#define zig_atomicrmw_xchg(obj, arg, order, type) atomic_exchange_explicit (obj, arg, order) +#define zig_atomicrmw_add(obj, arg, order, type) atomic_fetch_add_explicit (obj, arg, order) +#define zig_atomicrmw_sub(obj, arg, order, type) atomic_fetch_sub_explicit (obj, arg, order) +#define zig_atomicrmw_or(obj, arg, order, type) atomic_fetch_or_explicit (obj, arg, order) +#define zig_atomicrmw_xor(obj, arg, order, type) atomic_fetch_xor_explicit (obj, arg, order) +#define zig_atomicrmw_and(obj, arg, order, type) atomic_fetch_and_explicit (obj, arg, order) +#define zig_atomicrmw_nand(obj, arg, order, type) __atomic_fetch_nand (obj, arg, order) +#define zig_atomicrmw_min(obj, arg, order, type) __atomic_fetch_min (obj, arg, order) +#define zig_atomicrmw_max(obj, arg, order, type) __atomic_fetch_max (obj, arg, order) +#define zig_atomic_store(obj, arg, order, type) atomic_store_explicit (obj, arg, order) +#define zig_atomic_load(obj, order, type) atomic_load_explicit (obj, order) +#define zig_fence(order) atomic_thread_fence(order) +#elif defined(__GNUC__) +#define memory_order_relaxed __ATOMIC_RELAXED +#define memory_order_consume __ATOMIC_CONSUME +#define memory_order_acquire __ATOMIC_ACQUIRE +#define memory_order_release __ATOMIC_RELEASE +#define memory_order_acq_rel __ATOMIC_ACQ_REL +#define memory_order_seq_cst __ATOMIC_SEQ_CST +#define zig_atomic(type) type +#define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) __atomic_compare_exchange_n(obj, &(expected), desired, false, succ, fail) +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) __atomic_compare_exchange_n(obj, &(expected), desired, true , succ, fail) +#define zig_atomicrmw_xchg(obj, arg, order, type) __atomic_exchange_n(obj, arg, order) +#define zig_atomicrmw_add(obj, arg, order, type) __atomic_fetch_add (obj, arg, order) +#define zig_atomicrmw_sub(obj, arg, order, type) __atomic_fetch_sub (obj, arg, order) +#define zig_atomicrmw_or(obj, arg, order, type) __atomic_fetch_or (obj, arg, order) +#define zig_atomicrmw_xor(obj, arg, order, type) __atomic_fetch_xor (obj, arg, order) +#define zig_atomicrmw_and(obj, arg, order, type) __atomic_fetch_and (obj, arg, order) +#define zig_atomicrmw_nand(obj, arg, order, type) __atomic_fetch_nand(obj, arg, order) +#define zig_atomicrmw_min(obj, arg, order, type) __atomic_fetch_min (obj, arg, order) +#define zig_atomicrmw_max(obj, arg, order, type) __atomic_fetch_max (obj, arg, order) +#define zig_atomic_store(obj, arg, order, type) __atomic_store_n (obj, arg, order) +#define zig_atomic_load(obj, order, type) __atomic_load_n (obj, order) +#define zig_fence(order) __atomic_thread_fence(order) +#elif _MSC_VER && (_M_IX86 || _M_X64) +#define memory_order_relaxed 0 +#define memory_order_consume 1 +#define memory_order_acquire 2 +#define memory_order_release 3 +#define memory_order_acq_rel 4 +#define memory_order_seq_cst 5 +#define zig_atomic(type) type +#define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) zig_expand_concat(zig_msvc_cmpxchg_, type)(obj, &(expected), desired) +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) +#define zig_atomicrmw_xchg(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_xchg_, type)(obj, arg) +#define zig_atomicrmw_add(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_add_, type)(obj, arg) +#define zig_atomicrmw_sub(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_sub_, type)(obj, arg) +#define zig_atomicrmw_or(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_or_, type)(obj, arg) +#define zig_atomicrmw_xor(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_xor_, type)(obj, arg) +#define zig_atomicrmw_and(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_and_, type)(obj, arg) +#define zig_atomicrmw_nand(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_nand_, type)(obj, arg) +#define zig_atomicrmw_min(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_min_, type)(obj, arg) +#define zig_atomicrmw_max(obj, arg, order, type) zig_expand_concat(zig_msvc_atomicrmw_max_, type)(obj, arg) +#define zig_atomic_store(obj, arg, order, type) zig_expand_concat(zig_msvc_atomic_store_, type)(obj, arg) +#define zig_atomic_load(obj, order, type) zig_expand_concat(zig_msvc_atomic_load_, type)(obj) +#if _M_X64 +#define zig_fence(order) __faststorefence() +#else +#define zig_fence(order) zig_msvc_atomic_barrier() +#endif + +// TODO: _MSC_VER && (_M_ARM || _M_ARM64) +#else +#define memory_order_relaxed 0 +#define memory_order_consume 1 +#define memory_order_acquire 2 +#define memory_order_release 3 +#define memory_order_acq_rel 4 +#define memory_order_seq_cst 5 +#define zig_atomic(type) type +#define zig_cmpxchg_strong(obj, expected, desired, succ, fail, type) zig_unimplemented() +#define zig_cmpxchg_weak(obj, expected, desired, succ, fail, type) zig_unimplemented() +#define zig_atomicrmw_xchg(obj, arg, order, type) zig_unimplemented() +#define zig_atomicrmw_add(obj, arg, order, type) zig_unimplemented() +#define zig_atomicrmw_sub(obj, arg, order, type) zig_unimplemented() +#define zig_atomicrmw_or(obj, arg, order, type) zig_unimplemented() +#define zig_atomicrmw_xor(obj, arg, order, type) zig_unimplemented() +#define zig_atomicrmw_and(obj, arg, order, type) zig_unimplemented() +#define zig_atomicrmw_nand(obj, arg, order, type) zig_unimplemented() +#define zig_atomicrmw_min(obj, arg, order, type) zig_unimplemented() +#define zig_atomicrmw_max(obj, arg, order, type) zig_unimplemented() +#define zig_atomic_store(obj, arg, order, type) zig_unimplemented() +#define zig_atomic_load(obj, order, type) zig_unimplemented() +#define zig_fence(order) zig_unimplemented() +#endif + +#if __STDC_VERSION__ >= 201112L +#define zig_noreturn _Noreturn void +#elif zig_has_attribute(noreturn) || defined(zig_gnuc) +#define zig_noreturn __attribute__((noreturn)) void +#elif _MSC_VER +#define zig_noreturn __declspec(noreturn) void +#else +#define zig_noreturn void +#endif + +#define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) + +typedef uintptr_t zig_usize; +typedef intptr_t zig_isize; +typedef signed short int zig_c_short; +typedef unsigned short int zig_c_ushort; +typedef signed int zig_c_int; +typedef unsigned int zig_c_uint; +typedef signed long int zig_c_long; +typedef unsigned long int zig_c_ulong; +typedef signed long long int zig_c_longlong; +typedef unsigned long long int zig_c_ulonglong; + +typedef uint8_t zig_u8; +typedef int8_t zig_i8; +typedef uint16_t zig_u16; +typedef int16_t zig_i16; +typedef uint32_t zig_u32; +typedef int32_t zig_i32; +typedef uint64_t zig_u64; +typedef int64_t zig_i64; + +#define zig_as_u8(val) UINT8_C(val) +#define zig_as_i8(val) INT8_C(val) +#define zig_as_u16(val) UINT16_C(val) +#define zig_as_i16(val) INT16_C(val) +#define zig_as_u32(val) UINT32_C(val) +#define zig_as_i32(val) INT32_C(val) +#define zig_as_u64(val) UINT64_C(val) +#define zig_as_i64(val) INT64_C(val) + +#define zig_minInt_u8 zig_as_u8(0) +#define zig_maxInt_u8 UINT8_MAX +#define zig_minInt_i8 INT8_MIN +#define zig_maxInt_i8 INT8_MAX +#define zig_minInt_u16 zig_as_u16(0) +#define zig_maxInt_u16 UINT16_MAX +#define zig_minInt_i16 INT16_MIN +#define zig_maxInt_i16 INT16_MAX +#define zig_minInt_u32 zig_as_u32(0) +#define zig_maxInt_u32 UINT32_MAX +#define zig_minInt_i32 INT32_MIN +#define zig_maxInt_i32 INT32_MAX +#define zig_minInt_u64 zig_as_u64(0) +#define zig_maxInt_u64 UINT64_MAX +#define zig_minInt_i64 INT64_MIN +#define zig_maxInt_i64 INT64_MAX + +#define zig_compiler_rt_abbrev_u32 si +#define zig_compiler_rt_abbrev_i32 si +#define zig_compiler_rt_abbrev_u64 di +#define zig_compiler_rt_abbrev_i64 di +#define zig_compiler_rt_abbrev_u128 ti +#define zig_compiler_rt_abbrev_i128 ti +#define zig_compiler_rt_abbrev_f16 hf +#define zig_compiler_rt_abbrev_f32 sf +#define zig_compiler_rt_abbrev_f64 df +#define zig_compiler_rt_abbrev_f80 xf +#define zig_compiler_rt_abbrev_f128 tf + +zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, zig_usize); +zig_extern void *memset (void *, int, zig_usize); + +/* ==================== 8/16/32/64-bit Integer Routines ===================== */ + +#define zig_maxInt(Type, bits) zig_shr_##Type(zig_maxInt_##Type, (zig_bitSizeOf(zig_##Type) - bits)) +#define zig_expand_maxInt(Type, bits) zig_maxInt(Type, bits) +#define zig_minInt(Type, bits) zig_not_##Type(zig_maxInt(Type, bits), bits) +#define zig_expand_minInt(Type, bits) zig_minInt(Type, bits) + +#define zig_int_operator(Type, RhsType, operation, operator) \ + static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##RhsType rhs) { \ + return lhs operator rhs; \ + } +#define zig_int_basic_operator(Type, operation, operator) \ + zig_int_operator(Type, Type, operation, operator) +#define zig_int_shift_operator(Type, operation, operator) \ + zig_int_operator(Type, u8, operation, operator) +#define zig_int_helpers(w) \ + zig_int_basic_operator(u##w, and, &) \ + zig_int_basic_operator(i##w, and, &) \ + zig_int_basic_operator(u##w, or, |) \ + zig_int_basic_operator(i##w, or, |) \ + zig_int_basic_operator(u##w, xor, ^) \ + zig_int_basic_operator(i##w, xor, ^) \ + zig_int_shift_operator(u##w, shl, <<) \ + zig_int_shift_operator(i##w, shl, <<) \ + zig_int_shift_operator(u##w, shr, >>) \ +\ + static inline zig_i##w zig_shr_i##w(zig_i##w lhs, zig_u8 rhs) { \ + zig_i##w sign_mask = lhs < zig_as_i##w(0) ? -zig_as_i##w(1) : zig_as_i##w(0); \ + return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; \ + } \ +\ + static inline zig_u##w zig_not_u##w(zig_u##w val, zig_u8 bits) { \ + return val ^ zig_maxInt(u##w, bits); \ + } \ +\ + static inline zig_i##w zig_not_i##w(zig_i##w val, zig_u8 bits) { \ + (void)bits; \ + return ~val; \ + } \ +\ + static inline zig_u##w zig_wrap_u##w(zig_u##w val, zig_u8 bits) { \ + return val & zig_maxInt(u##w, bits); \ + } \ +\ + static inline zig_i##w zig_wrap_i##w(zig_i##w val, zig_u8 bits) { \ + return (val & zig_as_u##w(1) << (bits - zig_as_u8(1))) != 0 \ + ? val | zig_minInt(i##w, bits) : val & zig_maxInt(i##w, bits); \ + } \ +\ + zig_int_basic_operator(u##w, div_floor, /) \ +\ + static inline zig_i##w zig_div_floor_i##w(zig_i##w lhs, zig_i##w rhs) { \ + return lhs / rhs - (((lhs ^ rhs) & (lhs % rhs)) < zig_as_i##w(0)); \ + } \ +\ + zig_int_basic_operator(u##w, mod, %) \ +\ + static inline zig_i##w zig_mod_i##w(zig_i##w lhs, zig_i##w rhs) { \ + zig_i##w rem = lhs % rhs; \ + return rem + (((lhs ^ rhs) & rem) < zig_as_i##w(0) ? rhs : zig_as_i##w(0)); \ + } \ +\ + static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ + return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \ + } \ +\ + static inline zig_i##w zig_shlw_i##w(zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ + return zig_wrap_i##w((zig_i##w)zig_shl_u##w((zig_u##w)lhs, (zig_u##w)rhs), bits); \ + } \ +\ + static inline zig_u##w zig_addw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + return zig_wrap_u##w(lhs + rhs, bits); \ + } \ +\ + static inline zig_i##w zig_addw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs + (zig_u##w)rhs), bits); \ + } \ +\ + static inline zig_u##w zig_subw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + return zig_wrap_u##w(lhs - rhs, bits); \ + } \ +\ + static inline zig_i##w zig_subw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs - (zig_u##w)rhs), bits); \ + } \ +\ + static inline zig_u##w zig_mulw_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + return zig_wrap_u##w(lhs * rhs, bits); \ + } \ +\ + static inline zig_i##w zig_mulw_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + return zig_wrap_i##w((zig_i##w)((zig_u##w)lhs * (zig_u##w)rhs), bits); \ + } +zig_int_helpers(8) +zig_int_helpers(16) +zig_int_helpers(32) +zig_int_helpers(64) + +static inline bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) || defined(zig_gnuc) + zig_u32 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u32(full_res, bits); + return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); +#else + *res = zig_addw_u32(lhs, rhs, bits); + return *res < lhs; +#endif +} + +static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n, + const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits); +} + +zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); +static inline bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) || defined(zig_gnuc) + zig_i32 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i32 full_res = __addosi4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i32(full_res, bits); + return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); +} + +static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n, + const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) || defined(zig_gnuc) + zig_u64 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u64(full_res, bits); + return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); +#else + *res = zig_addw_u64(lhs, rhs, bits); + return *res < lhs; +#endif +} + +static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n, + const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits); +} + +zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); +static inline bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) || defined(zig_gnuc) + zig_i64 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i64 full_res = __addodi4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i64(full_res, bits); + return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); +} + +static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n, + const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) || defined(zig_gnuc) + zig_u8 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u8(full_res, bits); + return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); +#else + zig_u32 full_res; + bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); + *res = (zig_u8)full_res; + return overflow; +#endif +} + +static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n, + const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) || defined(zig_gnuc) + zig_i8 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i8(full_res, bits); + return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); +#else + zig_i32 full_res; + bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); + *res = (zig_i8)full_res; + return overflow; +#endif +} + +static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n, + const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) || defined(zig_gnuc) + zig_u16 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u16(full_res, bits); + return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); +#else + zig_u32 full_res; + bool overflow = zig_addo_u32(&full_res, lhs, rhs, bits); + *res = (zig_u16)full_res; + return overflow; +#endif +} + +static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n, + const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) || defined(zig_gnuc) + zig_i16 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i16(full_res, bits); + return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); +#else + zig_i32 full_res; + bool overflow = zig_addo_i32(&full_res, lhs, rhs, bits); + *res = (zig_i16)full_res; + return overflow; +#endif +} + +static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n, + const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) || defined(zig_gnuc) + zig_u32 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u32(full_res, bits); + return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); +#else + *res = zig_subw_u32(lhs, rhs, bits); + return *res > lhs; +#endif +} + +static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n, + const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits); +} + +zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); +static inline bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) || defined(zig_gnuc) + zig_i32 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i32 full_res = __subosi4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i32(full_res, bits); + return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); +} + +static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n, + const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) || defined(zig_gnuc) + zig_u64 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u64(full_res, bits); + return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); +#else + *res = zig_subw_u64(lhs, rhs, bits); + return *res > lhs; +#endif +} + +static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n, + const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits); +} + +zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); +static inline bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) || defined(zig_gnuc) + zig_i64 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i64 full_res = __subodi4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i64(full_res, bits); + return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); +} + +static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n, + const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) || defined(zig_gnuc) + zig_u8 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u8(full_res, bits); + return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); +#else + zig_u32 full_res; + bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); + *res = (zig_u8)full_res; + return overflow; +#endif +} + +static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n, + const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) || defined(zig_gnuc) + zig_i8 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i8(full_res, bits); + return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); +#else + zig_i32 full_res; + bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); + *res = (zig_i8)full_res; + return overflow; +#endif +} + +static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n, + const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits); +} + + +static inline bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) || defined(zig_gnuc) + zig_u16 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u16(full_res, bits); + return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); +#else + zig_u32 full_res; + bool overflow = zig_subo_u32(&full_res, lhs, rhs, bits); + *res = (zig_u16)full_res; + return overflow; +#endif +} + +static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n, + const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits); +} + + +static inline bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) || defined(zig_gnuc) + zig_i16 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i16(full_res, bits); + return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); +#else + zig_i32 full_res; + bool overflow = zig_subo_i32(&full_res, lhs, rhs, bits); + *res = (zig_i16)full_res; + return overflow; +#endif +} + +static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n, + const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) || defined(zig_gnuc) + zig_u32 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u32(full_res, bits); + return overflow || full_res < zig_minInt(u32, bits) || full_res > zig_maxInt(u32, bits); +#else + *res = zig_mulw_u32(lhs, rhs, bits); + return rhs != zig_as_u32(0) && lhs > zig_maxInt(u32, bits) / rhs; +#endif +} + +static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n, + const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits); +} + +zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow); +static inline bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) || defined(zig_gnuc) + zig_i32 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i32 full_res = __mulosi4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i32(full_res, bits); + return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits); +} + +static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n, + const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) || defined(zig_gnuc) + zig_u64 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u64(full_res, bits); + return overflow || full_res < zig_minInt(u64, bits) || full_res > zig_maxInt(u64, bits); +#else + *res = zig_mulw_u64(lhs, rhs, bits); + return rhs != zig_as_u64(0) && lhs > zig_maxInt(u64, bits) / rhs; +#endif +} + +static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n, + const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits); +} + +zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow); +static inline bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) || defined(zig_gnuc) + zig_i64 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i64 full_res = __mulodi4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i64(full_res, bits); + return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits); +} + +static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n, + const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) || defined(zig_gnuc) + zig_u8 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u8(full_res, bits); + return overflow || full_res < zig_minInt(u8, bits) || full_res > zig_maxInt(u8, bits); +#else + zig_u32 full_res; + bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); + *res = (zig_u8)full_res; + return overflow; +#endif +} + +static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n, + const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) || defined(zig_gnuc) + zig_i8 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i8(full_res, bits); + return overflow || full_res < zig_minInt(i8, bits) || full_res > zig_maxInt(i8, bits); +#else + zig_i32 full_res; + bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); + *res = (zig_i8)full_res; + return overflow; +#endif +} + +static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n, + const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) || defined(zig_gnuc) + zig_u16 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u16(full_res, bits); + return overflow || full_res < zig_minInt(u16, bits) || full_res > zig_maxInt(u16, bits); +#else + zig_u32 full_res; + bool overflow = zig_mulo_u32(&full_res, lhs, rhs, bits); + *res = (zig_u16)full_res; + return overflow; +#endif +} + +static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n, + const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits); +} + +static inline bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) || defined(zig_gnuc) + zig_i16 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_i16(full_res, bits); + return overflow || full_res < zig_minInt(i16, bits) || full_res > zig_maxInt(i16, bits); +#else + zig_i32 full_res; + bool overflow = zig_mulo_i32(&full_res, lhs, rhs, bits); + *res = (zig_i16)full_res; + return overflow; +#endif +} + +static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n, + const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits) +{ + for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits); +} + +#define zig_int_builtins(w) \ + static inline bool zig_shlo_u##w(zig_u##w *res, zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \ + *res = zig_shlw_u##w(lhs, rhs, bits); \ + return lhs > zig_maxInt(u##w, bits) >> rhs; \ + } \ +\ + static inline bool zig_shlo_i##w(zig_i##w *res, zig_i##w lhs, zig_u8 rhs, zig_u8 bits) { \ + *res = zig_shlw_i##w(lhs, rhs, bits); \ + zig_i##w mask = (zig_i##w)(zig_maxInt_u##w << (bits - rhs - 1)); \ + return (lhs & mask) != zig_as_i##w(0) && (lhs & mask) != mask; \ + } \ +\ + static inline zig_u##w zig_shls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + zig_u##w res; \ + if (rhs >= bits) return lhs != zig_as_u##w(0) ? zig_maxInt(u##w, bits) : lhs; \ + return zig_shlo_u##w(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + } \ +\ + static inline zig_i##w zig_shls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + zig_i##w res; \ + if ((zig_u##w)rhs < (zig_u##w)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ + return lhs < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + } \ +\ + static inline zig_u##w zig_adds_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + zig_u##w res; \ + return zig_addo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + } \ +\ + static inline zig_i##w zig_adds_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + zig_i##w res; \ + if (!zig_addo_i##w(&res, lhs, rhs, bits)) return res; \ + return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + } \ +\ + static inline zig_u##w zig_subs_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + zig_u##w res; \ + return zig_subo_u##w(&res, lhs, rhs, bits) ? zig_minInt(u##w, bits) : res; \ + } \ +\ + static inline zig_i##w zig_subs_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + zig_i##w res; \ + if (!zig_subo_i##w(&res, lhs, rhs, bits)) return res; \ + return res >= zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + } \ +\ + static inline zig_u##w zig_muls_u##w(zig_u##w lhs, zig_u##w rhs, zig_u8 bits) { \ + zig_u##w res; \ + return zig_mulo_u##w(&res, lhs, rhs, bits) ? zig_maxInt(u##w, bits) : res; \ + } \ +\ + static inline zig_i##w zig_muls_i##w(zig_i##w lhs, zig_i##w rhs, zig_u8 bits) { \ + zig_i##w res; \ + if (!zig_mulo_i##w(&res, lhs, rhs, bits)) return res; \ + return (lhs ^ rhs) < zig_as_i##w(0) ? zig_minInt(i##w, bits) : zig_maxInt(i##w, bits); \ + } +zig_int_builtins(8) +zig_int_builtins(16) +zig_int_builtins(32) +zig_int_builtins(64) + +#define zig_builtin8(name, val) __builtin_##name(val) +typedef zig_c_uint zig_Builtin8; + +#define zig_builtin16(name, val) __builtin_##name(val) +typedef zig_c_uint zig_Builtin16; + +#if INT_MIN <= INT32_MIN +#define zig_builtin32(name, val) __builtin_##name(val) +typedef zig_c_uint zig_Builtin32; +#elif LONG_MIN <= INT32_MIN +#define zig_builtin32(name, val) __builtin_##name##l(val) +typedef zig_c_ulong zig_Builtin32; +#endif + +#if INT_MIN <= INT64_MIN +#define zig_builtin64(name, val) __builtin_##name(val) +typedef zig_c_uint zig_Builtin64; +#elif LONG_MIN <= INT64_MIN +#define zig_builtin64(name, val) __builtin_##name##l(val) +typedef zig_c_ulong zig_Builtin64; +#elif LLONG_MIN <= INT64_MIN +#define zig_builtin64(name, val) __builtin_##name##ll(val) +typedef zig_c_ulonglong zig_Builtin64; +#endif + +static inline zig_u8 zig_byte_swap_u8(zig_u8 val, zig_u8 bits) { + return zig_wrap_u8(val >> (8 - bits), bits); +} + +static inline zig_i8 zig_byte_swap_i8(zig_i8 val, zig_u8 bits) { + return zig_wrap_i8((zig_i8)zig_byte_swap_u8((zig_u8)val, bits), bits); +} + +static inline zig_u16 zig_byte_swap_u16(zig_u16 val, zig_u8 bits) { + zig_u16 full_res; +#if zig_has_builtin(bswap16) || defined(zig_gnuc) + full_res = __builtin_bswap16(val); +#else + full_res = (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 0), 8) << 8 | + (zig_u16)zig_byte_swap_u8((zig_u8)(val >> 8), 8) >> 0; +#endif + return zig_wrap_u16(full_res >> (16 - bits), bits); +} + +static inline zig_i16 zig_byte_swap_i16(zig_i16 val, zig_u8 bits) { + return zig_wrap_i16((zig_i16)zig_byte_swap_u16((zig_u16)val, bits), bits); +} + +static inline zig_u32 zig_byte_swap_u32(zig_u32 val, zig_u8 bits) { + zig_u32 full_res; +#if zig_has_builtin(bswap32) || defined(zig_gnuc) + full_res = __builtin_bswap32(val); +#else + full_res = (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 0), 16) << 16 | + (zig_u32)zig_byte_swap_u16((zig_u16)(val >> 16), 16) >> 0; +#endif + return zig_wrap_u32(full_res >> (32 - bits), bits); +} + +static inline zig_i32 zig_byte_swap_i32(zig_i32 val, zig_u8 bits) { + return zig_wrap_i32((zig_i32)zig_byte_swap_u32((zig_u32)val, bits), bits); +} + +static inline zig_u64 zig_byte_swap_u64(zig_u64 val, zig_u8 bits) { + zig_u64 full_res; +#if zig_has_builtin(bswap64) || defined(zig_gnuc) + full_res = __builtin_bswap64(val); +#else + full_res = (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 0), 32) << 32 | + (zig_u64)zig_byte_swap_u32((zig_u32)(val >> 32), 32) >> 0; +#endif + return zig_wrap_u64(full_res >> (64 - bits), bits); +} + +static inline zig_i64 zig_byte_swap_i64(zig_i64 val, zig_u8 bits) { + return zig_wrap_i64((zig_i64)zig_byte_swap_u64((zig_u64)val, bits), bits); +} + +static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) { + zig_u8 full_res; +#if zig_has_builtin(bitreverse8) + full_res = __builtin_bitreverse8(val); +#else + static zig_u8 const lut[0x10] = { + 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf + }; + full_res = lut[val >> 0 & 0xF] << 4 | lut[val >> 4 & 0xF] << 0; +#endif + return zig_wrap_u8(full_res >> (8 - bits), bits); +} + +static inline zig_i8 zig_bit_reverse_i8(zig_i8 val, zig_u8 bits) { + return zig_wrap_i8((zig_i8)zig_bit_reverse_u8((zig_u8)val, bits), bits); +} + +static inline zig_u16 zig_bit_reverse_u16(zig_u16 val, zig_u8 bits) { + zig_u16 full_res; +#if zig_has_builtin(bitreverse16) + full_res = __builtin_bitreverse16(val); +#else + full_res = (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 0), 8) << 8 | + (zig_u16)zig_bit_reverse_u8((zig_u8)(val >> 8), 8) >> 0; +#endif + return zig_wrap_u16(full_res >> (16 - bits), bits); +} + +static inline zig_i16 zig_bit_reverse_i16(zig_i16 val, zig_u8 bits) { + return zig_wrap_i16((zig_i16)zig_bit_reverse_u16((zig_u16)val, bits), bits); +} + +static inline zig_u32 zig_bit_reverse_u32(zig_u32 val, zig_u8 bits) { + zig_u32 full_res; +#if zig_has_builtin(bitreverse32) + full_res = __builtin_bitreverse32(val); +#else + full_res = (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 0), 16) << 16 | + (zig_u32)zig_bit_reverse_u16((zig_u16)(val >> 16), 16) >> 0; +#endif + return zig_wrap_u32(full_res >> (32 - bits), bits); +} + +static inline zig_i32 zig_bit_reverse_i32(zig_i32 val, zig_u8 bits) { + return zig_wrap_i32((zig_i32)zig_bit_reverse_u32((zig_u32)val, bits), bits); +} + +static inline zig_u64 zig_bit_reverse_u64(zig_u64 val, zig_u8 bits) { + zig_u64 full_res; +#if zig_has_builtin(bitreverse64) + full_res = __builtin_bitreverse64(val); +#else + full_res = (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 0), 32) << 32 | + (zig_u64)zig_bit_reverse_u32((zig_u32)(val >> 32), 32) >> 0; +#endif + return zig_wrap_u64(full_res >> (64 - bits), bits); +} + +static inline zig_i64 zig_bit_reverse_i64(zig_i64 val, zig_u8 bits) { + return zig_wrap_i64((zig_i64)zig_bit_reverse_u64((zig_u64)val, bits), bits); +} + +#define zig_builtin_popcount_common(w) \ + static inline zig_u8 zig_popcount_i##w(zig_i##w val, zig_u8 bits) { \ + return zig_popcount_u##w((zig_u##w)val, bits); \ + } +#if zig_has_builtin(popcount) || defined(zig_gnuc) +#define zig_builtin_popcount(w) \ + static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ + (void)bits; \ + return zig_builtin##w(popcount, val); \ + } \ +\ + zig_builtin_popcount_common(w) +#else +#define zig_builtin_popcount(w) \ + static inline zig_u8 zig_popcount_u##w(zig_u##w val, zig_u8 bits) { \ + (void)bits; \ + zig_u##w temp = val - ((val >> 1) & (zig_maxInt_u##w / 3)); \ + temp = (temp & (zig_maxInt_u##w / 5)) + ((temp >> 2) & (zig_maxInt_u##w / 5)); \ + temp = (temp + (temp >> 4)) & (zig_maxInt_u##w / 17); \ + return temp * (zig_maxInt_u##w / 255) >> (w - 8); \ + } \ +\ + zig_builtin_popcount_common(w) +#endif +zig_builtin_popcount(8) +zig_builtin_popcount(16) +zig_builtin_popcount(32) +zig_builtin_popcount(64) + +#define zig_builtin_ctz_common(w) \ + static inline zig_u8 zig_ctz_i##w(zig_i##w val, zig_u8 bits) { \ + return zig_ctz_u##w((zig_u##w)val, bits); \ + } +#if zig_has_builtin(ctz) || defined(zig_gnuc) +#define zig_builtin_ctz(w) \ + static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ + if (val == 0) return bits; \ + return zig_builtin##w(ctz, val); \ + } \ +\ + zig_builtin_ctz_common(w) +#else +#define zig_builtin_ctz(w) \ + static inline zig_u8 zig_ctz_u##w(zig_u##w val, zig_u8 bits) { \ + return zig_popcount_u##w(zig_not_u##w(val, bits) & zig_subw_u##w(val, 1, bits), bits); \ + } \ +\ + zig_builtin_ctz_common(w) +#endif +zig_builtin_ctz(8) +zig_builtin_ctz(16) +zig_builtin_ctz(32) +zig_builtin_ctz(64) + +#define zig_builtin_clz_common(w) \ + static inline zig_u8 zig_clz_i##w(zig_i##w val, zig_u8 bits) { \ + return zig_clz_u##w((zig_u##w)val, bits); \ + } +#if zig_has_builtin(clz) || defined(zig_gnuc) +#define zig_builtin_clz(w) \ + static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ + if (val == 0) return bits; \ + return zig_builtin##w(clz, val) - (zig_bitSizeOf(zig_Builtin##w) - bits); \ + } \ +\ + zig_builtin_clz_common(w) +#else +#define zig_builtin_clz(w) \ + static inline zig_u8 zig_clz_u##w(zig_u##w val, zig_u8 bits) { \ + return zig_ctz_u##w(zig_bit_reverse_u##w(val, bits), bits); \ + } \ +\ + zig_builtin_clz_common(w) +#endif +zig_builtin_clz(8) +zig_builtin_clz(16) +zig_builtin_clz(32) +zig_builtin_clz(64) + +/* ======================== 128-bit Integer Routines ======================== */ + +#if !defined(zig_has_int128) +# if defined(__SIZEOF_INT128__) +# define zig_has_int128 1 +# else +# define zig_has_int128 0 +# endif +#endif + +#if zig_has_int128 + +typedef unsigned __int128 zig_u128; +typedef signed __int128 zig_i128; + +#define zig_as_u128(hi, lo) ((zig_u128)(hi)<<64|(lo)) +#define zig_as_i128(hi, lo) ((zig_i128)zig_as_u128(hi, lo)) +#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) +#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) +#define zig_hi_u128(val) ((zig_u64)((val) >> 64)) +#define zig_lo_u128(val) ((zig_u64)((val) >> 0)) +#define zig_hi_i128(val) ((zig_i64)((val) >> 64)) +#define zig_lo_i128(val) ((zig_u64)((val) >> 0)) +#define zig_bitcast_u128(val) ((zig_u128)(val)) +#define zig_bitcast_i128(val) ((zig_i128)(val)) +#define zig_cmp_int128(Type) \ + static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return (lhs > rhs) - (lhs < rhs); \ + } +#define zig_bit_int128(Type, operation, operator) \ + static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return lhs operator rhs; \ + } + +#else /* zig_has_int128 */ + +#if __LITTLE_ENDIAN__ || _MSC_VER +typedef struct { zig_align(16) zig_u64 lo; zig_u64 hi; } zig_u128; +typedef struct { zig_align(16) zig_u64 lo; zig_i64 hi; } zig_i128; +#else +typedef struct { zig_align(16) zig_u64 hi; zig_u64 lo; } zig_u128; +typedef struct { zig_align(16) zig_i64 hi; zig_u64 lo; } zig_i128; +#endif + +#define zig_as_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) +#define zig_as_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) + +#if _MSC_VER +#define zig_as_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#define zig_as_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } +#else +#define zig_as_constant_u128(hi, lo) zig_as_u128(hi, lo) +#define zig_as_constant_i128(hi, lo) zig_as_i128(hi, lo) +#endif +#define zig_hi_u128(val) ((val).hi) +#define zig_lo_u128(val) ((val).lo) +#define zig_hi_i128(val) ((val).hi) +#define zig_lo_i128(val) ((val).lo) +#define zig_bitcast_u128(val) zig_as_u128((zig_u64)(val).hi, (val).lo) +#define zig_bitcast_i128(val) zig_as_i128((zig_i64)(val).hi, (val).lo) +#define zig_cmp_int128(Type) \ + static inline zig_i32 zig_cmp_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return (lhs.hi == rhs.hi) \ + ? (lhs.lo > rhs.lo) - (lhs.lo < rhs.lo) \ + : (lhs.hi > rhs.hi) - (lhs.hi < rhs.hi); \ + } +#define zig_bit_int128(Type, operation, operator) \ + static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return (zig_##Type){ .hi = lhs.hi operator rhs.hi, .lo = lhs.lo operator rhs.lo }; \ + } + +#endif /* zig_has_int128 */ + +#define zig_minInt_u128 zig_as_u128(zig_minInt_u64, zig_minInt_u64) +#define zig_maxInt_u128 zig_as_u128(zig_maxInt_u64, zig_maxInt_u64) +#define zig_minInt_i128 zig_as_i128(zig_minInt_i64, zig_minInt_u64) +#define zig_maxInt_i128 zig_as_i128(zig_maxInt_i64, zig_maxInt_u64) + +zig_cmp_int128(u128) +zig_cmp_int128(i128) + +zig_bit_int128(u128, and, &) +zig_bit_int128(i128, and, &) + +zig_bit_int128(u128, or, |) +zig_bit_int128(i128, or, |) + +zig_bit_int128(u128, xor, ^) +zig_bit_int128(i128, xor, ^) + +static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs); + +#if zig_has_int128 + +static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { + return val ^ zig_maxInt(u128, bits); +} + +static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { + (void)bits; + return ~val; +} + +static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { + return lhs >> rhs; +} + +static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { + return lhs << rhs; +} + +static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { + return lhs << rhs; +} + +static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs + rhs; +} + +static inline zig_i128 zig_add_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs + rhs; +} + +static inline zig_u128 zig_sub_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs - rhs; +} + +static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs - rhs; +} + +static inline zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs * rhs; +} + +static inline zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs * rhs; +} + +static inline zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs / rhs; +} + +static inline zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs / rhs; +} + +static inline zig_u128 zig_rem_u128(zig_u128 lhs, zig_u128 rhs) { + return lhs % rhs; +} + +static inline zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { + return lhs % rhs; +} + +static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { + return zig_div_trunc_i128(lhs, rhs) - (((lhs ^ rhs) & zig_rem_i128(lhs, rhs)) < zig_as_i128(0, 0)); +} + +static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { + zig_i128 rem = zig_rem_i128(lhs, rhs); + return rem + (((lhs ^ rhs) & rem) < zig_as_i128(0, 0) ? rhs : zig_as_i128(0, 0)); +} + +#else /* zig_has_int128 */ + +static inline zig_u128 zig_not_u128(zig_u128 val, zig_u8 bits) { + return (zig_u128){ .hi = zig_not_u64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; +} + +static inline zig_i128 zig_not_i128(zig_i128 val, zig_u8 bits) { + return (zig_i128){ .hi = zig_not_i64(val.hi, bits - zig_as_u8(64)), .lo = zig_not_u64(val.lo, zig_as_u8(64)) }; +} + +static inline zig_u128 zig_shr_u128(zig_u128 lhs, zig_u8 rhs) { + if (rhs == zig_as_u8(0)) return lhs; + if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = zig_minInt_u64, .lo = lhs.hi >> (rhs - zig_as_u8(64)) }; + return (zig_u128){ .hi = lhs.hi >> rhs, .lo = lhs.hi << (zig_as_u8(64) - rhs) | lhs.lo >> rhs }; +} + +static inline zig_u128 zig_shl_u128(zig_u128 lhs, zig_u8 rhs) { + if (rhs == zig_as_u8(0)) return lhs; + if (rhs >= zig_as_u8(64)) return (zig_u128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; + return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; +} + +static inline zig_i128 zig_shl_i128(zig_i128 lhs, zig_u8 rhs) { + if (rhs == zig_as_u8(0)) return lhs; + if (rhs >= zig_as_u8(64)) return (zig_i128){ .hi = lhs.lo << (rhs - zig_as_u8(64)), .lo = zig_minInt_u64 }; + return (zig_i128){ .hi = lhs.hi << rhs | lhs.lo >> (zig_as_u8(64) - rhs), .lo = lhs.lo << rhs }; +} + +static inline zig_u128 zig_add_u128(zig_u128 lhs, zig_u128 rhs) { + zig_u128 res; + res.hi = lhs.hi + rhs.hi + zig_addo_u64(&res.lo, lhs.lo, rhs.lo, 64); + return res; +} + +static inline zig_i128 zig_add_i128(zig_i128 lhs, zig_i128 rhs) { + zig_i128 res; + res.hi = lhs.hi + rhs.hi + zig_addo_u64(&res.lo, lhs.lo, rhs.lo, 64); + return res; +} + +static inline zig_u128 zig_sub_u128(zig_u128 lhs, zig_u128 rhs) { + zig_u128 res; + res.hi = lhs.hi - rhs.hi - zig_subo_u64(&res.lo, lhs.lo, rhs.lo, 64); + return res; +} + +static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { + zig_i128 res; + res.hi = lhs.hi - rhs.hi - zig_subo_u64(&res.lo, lhs.lo, rhs.lo, 64); + return res; +} + +zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); +static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { + return zig_bitcast_u128(__multi3(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); +} + +static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { + return __multi3(lhs, rhs); +} + +zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); +static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { + return __udivti3(lhs, rhs); +}; + +zig_extern zig_i128 __divti3(zig_i128 lhs, zig_i128 rhs); +static zig_i128 zig_div_trunc_i128(zig_i128 lhs, zig_i128 rhs) { + return __divti3(lhs, rhs); +}; + +zig_extern zig_u128 __umodti3(zig_u128 lhs, zig_u128 rhs); +static zig_u128 zig_rem_u128(zig_u128 lhs, zig_u128 rhs) { + return __umodti3(lhs, rhs); +} + +zig_extern zig_i128 __modti3(zig_i128 lhs, zig_i128 rhs); +static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { + return __modti3(lhs, rhs); +} + +static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { + zig_i128 rem = zig_rem_i128(lhs, rhs); + return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < zig_as_i64(0) ? rhs : zig_as_i128(0, 0))); +} + +static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { + return zig_sub_i128(zig_div_trunc_i128(lhs, rhs), zig_as_i128(0, zig_cmp_i128(zig_and_i128(zig_xor_i128(lhs, rhs), zig_rem_i128(lhs, rhs)), zig_as_i128(0, 0)) < zig_as_i32(0))); +} + +#endif /* zig_has_int128 */ + +#define zig_div_floor_u128 zig_div_trunc_u128 +#define zig_mod_u128 zig_rem_u128 + +static inline zig_u128 zig_nand_u128(zig_u128 lhs, zig_u128 rhs) { + return zig_not_u128(zig_and_u128(lhs, rhs), 128); +} + +static inline zig_u128 zig_min_u128(zig_u128 lhs, zig_u128 rhs) { + return zig_cmp_u128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; +} + +static inline zig_i128 zig_min_i128(zig_i128 lhs, zig_i128 rhs) { + return zig_cmp_i128(lhs, rhs) < zig_as_i32(0) ? lhs : rhs; +} + +static inline zig_u128 zig_max_u128(zig_u128 lhs, zig_u128 rhs) { + return zig_cmp_u128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; +} + +static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { + return zig_cmp_i128(lhs, rhs) > zig_as_i32(0) ? lhs : rhs; +} + +static inline zig_i128 zig_shr_i128(zig_i128 lhs, zig_u8 rhs) { + zig_i128 sign_mask = zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_sub_i128(zig_as_i128(0, 0), zig_as_i128(0, 1)) : zig_as_i128(0, 0); + return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); +} + +static inline zig_u128 zig_wrap_u128(zig_u128 val, zig_u8 bits) { + return zig_and_u128(val, zig_maxInt(u128, bits)); +} + +static inline zig_i128 zig_wrap_i128(zig_i128 val, zig_u8 bits) { + return zig_as_i128(zig_wrap_i64(zig_hi_i128(val), bits - zig_as_u8(64)), zig_lo_i128(val)); +} + +static inline zig_u128 zig_shlw_u128(zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { + return zig_wrap_u128(zig_shl_u128(lhs, rhs), bits); +} + +static inline zig_i128 zig_shlw_i128(zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { + return zig_wrap_i128(zig_bitcast_i128(zig_shl_u128(zig_bitcast_u128(lhs), rhs)), bits); +} + +static inline zig_u128 zig_addw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + return zig_wrap_u128(zig_add_u128(lhs, rhs), bits); +} + +static inline zig_i128 zig_addw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + return zig_wrap_i128(zig_bitcast_i128(zig_add_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); +} + +static inline zig_u128 zig_subw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + return zig_wrap_u128(zig_sub_u128(lhs, rhs), bits); +} + +static inline zig_i128 zig_subw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + return zig_wrap_i128(zig_bitcast_i128(zig_sub_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); +} + +static inline zig_u128 zig_mulw_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + return zig_wrap_u128(zig_mul_u128(lhs, rhs), bits); +} + +static inline zig_i128 zig_mulw_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + return zig_wrap_i128(zig_bitcast_i128(zig_mul_u128(zig_bitcast_u128(lhs), zig_bitcast_u128(rhs))), bits); +} + +#if zig_has_int128 + +static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_u128 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u128(full_res, bits); + return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); +#else + *res = zig_addw_u128(lhs, rhs, bits); + return *res < lhs; +#endif +} + +zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +#if zig_has_builtin(add_overflow) + zig_i128 full_res; + bool overflow = __builtin_add_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); +} + +static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_u128 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u128(full_res, bits); + return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); +#else + *res = zig_subw_u128(lhs, rhs, bits); + return *res > lhs; +#endif +} + +zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +#if zig_has_builtin(sub_overflow) + zig_i128 full_res; + bool overflow = __builtin_sub_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); +} + +static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_u128 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); + *res = zig_wrap_u128(full_res, bits); + return overflow || full_res < zig_minInt(u128, bits) || full_res > zig_maxInt(u128, bits); +#else + *res = zig_mulw_u128(lhs, rhs, bits); + return rhs != zig_as_u128(0, 0) && lhs > zig_maxInt(u128, bits) / rhs; +#endif +} + +zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { +#if zig_has_builtin(mul_overflow) + zig_i128 full_res; + bool overflow = __builtin_mul_overflow(lhs, rhs, &full_res); +#else + zig_c_int overflow_int; + zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0; +#endif + *res = zig_wrap_i128(full_res, bits); + return overflow || full_res < zig_minInt(i128, bits) || full_res > zig_maxInt(i128, bits); +} + +#else /* zig_has_int128 */ + +static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, zig_u8 bits) { + return overflow || + zig_cmp_u128(full_res, zig_minInt(u128, bits)) < zig_as_i32(0) || + zig_cmp_u128(full_res, zig_maxInt(u128, bits)) > zig_as_i32(0); +} + +static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, zig_u8 bits) { + return overflow || + zig_cmp_i128(full_res, zig_minInt(i128, bits)) < zig_as_i32(0) || + zig_cmp_i128(full_res, zig_maxInt(i128, bits)) > zig_as_i32(0); +} + +static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 full_res; + bool overflow = + zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | + zig_addo_u64(&full_res.hi, full_res.hi, zig_addo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); + *res = zig_wrap_u128(full_res, bits); + return zig_overflow_u128(overflow, full_res, bits); +} + +zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_c_int overflow_int; + zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); + *res = zig_wrap_i128(full_res, bits); + return zig_overflow_i128(overflow_int, full_res, bits); +} + +static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 full_res; + bool overflow = + zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | + zig_subo_u64(&full_res.hi, full_res.hi, zig_subo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); + *res = zig_wrap_u128(full_res, bits); + return zig_overflow_u128(overflow, full_res, bits); +} + +zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_c_int overflow_int; + zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); + *res = zig_wrap_i128(full_res, bits); + return zig_overflow_i128(overflow_int, full_res, bits); +} + +static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + *res = zig_mulw_u128(lhs, rhs, bits); + return zig_cmp_u128(*res, zig_as_u128(0, 0)) != zig_as_i32(0) && + zig_cmp_u128(lhs, zig_div_trunc_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); +} + +zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, zig_c_int *overflow); +static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_c_int overflow_int; + zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); + *res = zig_wrap_i128(full_res, bits); + return zig_overflow_i128(overflow_int, full_res, bits); +} + +#endif /* zig_has_int128 */ + +static inline bool zig_shlo_u128(zig_u128 *res, zig_u128 lhs, zig_u8 rhs, zig_u8 bits) { + *res = zig_shlw_u128(lhs, rhs, bits); + return zig_cmp_u128(lhs, zig_shr_u128(zig_maxInt(u128, bits), rhs)) > zig_as_i32(0); +} + +static inline bool zig_shlo_i128(zig_i128 *res, zig_i128 lhs, zig_u8 rhs, zig_u8 bits) { + *res = zig_shlw_i128(lhs, rhs, bits); + zig_i128 mask = zig_bitcast_i128(zig_shl_u128(zig_maxInt_u128, bits - rhs - zig_as_u8(1))); + return zig_cmp_i128(zig_and_i128(lhs, mask), zig_as_i128(0, 0)) != zig_as_i32(0) && + zig_cmp_i128(zig_and_i128(lhs, mask), mask) != zig_as_i32(0); +} + +static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 res; + if (zig_cmp_u128(rhs, zig_as_u128(0, bits)) >= zig_as_i32(0)) + return zig_cmp_u128(lhs, zig_as_u128(0, 0)) != zig_as_i32(0) ? zig_maxInt(u128, bits) : lhs; + +#if zig_has_int128 + return zig_shlo_u128(&res, lhs, (zig_u8)rhs, bits) ? zig_maxInt(u128, bits) : res; +#else + return zig_shlo_u128(&res, lhs, (zig_u8)rhs.lo, bits) ? zig_maxInt(u128, bits) : res; +#endif +} + +static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_i128 res; + if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_as_u128(0, bits)) < zig_as_i32(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res; + return zig_cmp_i128(lhs, zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); +} + +static inline zig_u128 zig_adds_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 res; + return zig_addo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; +} + +static inline zig_i128 zig_adds_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_i128 res; + if (!zig_addo_i128(&res, lhs, rhs, bits)) return res; + return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); +} + +static inline zig_u128 zig_subs_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 res; + return zig_subo_u128(&res, lhs, rhs, bits) ? zig_minInt(u128, bits) : res; +} + +static inline zig_i128 zig_subs_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_i128 res; + if (!zig_subo_i128(&res, lhs, rhs, bits)) return res; + return zig_cmp_i128(res, zig_as_i128(0, 0)) >= zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); +} + +static inline zig_u128 zig_muls_u128(zig_u128 lhs, zig_u128 rhs, zig_u8 bits) { + zig_u128 res; + return zig_mulo_u128(&res, lhs, rhs, bits) ? zig_maxInt(u128, bits) : res; +} + +static inline zig_i128 zig_muls_i128(zig_i128 lhs, zig_i128 rhs, zig_u8 bits) { + zig_i128 res; + if (!zig_mulo_i128(&res, lhs, rhs, bits)) return res; + return zig_cmp_i128(zig_xor_i128(lhs, rhs), zig_as_i128(0, 0)) < zig_as_i32(0) ? zig_minInt(i128, bits) : zig_maxInt(i128, bits); +} + +static inline zig_u8 zig_clz_u128(zig_u128 val, zig_u8 bits) { + if (bits <= zig_as_u8(64)) return zig_clz_u64(zig_lo_u128(val), bits); + if (zig_hi_u128(val) != 0) return zig_clz_u64(zig_hi_u128(val), bits - zig_as_u8(64)); + return zig_clz_u64(zig_lo_u128(val), zig_as_u8(64)) + (bits - zig_as_u8(64)); +} + +static inline zig_u8 zig_clz_i128(zig_i128 val, zig_u8 bits) { + return zig_clz_u128(zig_bitcast_u128(val), bits); +} + +static inline zig_u8 zig_ctz_u128(zig_u128 val, zig_u8 bits) { + if (zig_lo_u128(val) != 0) return zig_ctz_u64(zig_lo_u128(val), zig_as_u8(64)); + return zig_ctz_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + zig_as_u8(64); +} + +static inline zig_u8 zig_ctz_i128(zig_i128 val, zig_u8 bits) { + return zig_ctz_u128(zig_bitcast_u128(val), bits); +} + +static inline zig_u8 zig_popcount_u128(zig_u128 val, zig_u8 bits) { + return zig_popcount_u64(zig_hi_u128(val), bits - zig_as_u8(64)) + + zig_popcount_u64(zig_lo_u128(val), zig_as_u8(64)); +} + +static inline zig_u8 zig_popcount_i128(zig_i128 val, zig_u8 bits) { + return zig_popcount_u128(zig_bitcast_u128(val), bits); +} + +static inline zig_u128 zig_byte_swap_u128(zig_u128 val, zig_u8 bits) { + zig_u128 full_res; +#if zig_has_builtin(bswap128) + full_res = __builtin_bswap128(val); +#else + full_res = zig_as_u128(zig_byte_swap_u64(zig_lo_u128(val), zig_as_u8(64)), + zig_byte_swap_u64(zig_hi_u128(val), zig_as_u8(64))); +#endif + return zig_shr_u128(full_res, zig_as_u8(128) - bits); +} + +static inline zig_i128 zig_byte_swap_i128(zig_i128 val, zig_u8 bits) { + return zig_bitcast_i128(zig_byte_swap_u128(zig_bitcast_u128(val), bits)); +} + +static inline zig_u128 zig_bit_reverse_u128(zig_u128 val, zig_u8 bits) { + return zig_shr_u128(zig_as_u128(zig_bit_reverse_u64(zig_lo_u128(val), zig_as_u8(64)), + zig_bit_reverse_u64(zig_hi_u128(val), zig_as_u8(64))), + zig_as_u8(128) - bits); +} + +static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, zig_u8 bits) { + return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits)); +} + +/* ========================= Floating Point Support ========================= */ + +#if _MSC_VER +#define zig_msvc_flt_inf ((double)(1e+300 * 1e+300)) +#define zig_msvc_flt_inff ((float)(1e+300 * 1e+300)) +#define zig_msvc_flt_infl ((long double)(1e+300 * 1e+300)) +#define zig_msvc_flt_nan ((double)(zig_msvc_flt_inf * 0.f)) +#define zig_msvc_flt_nanf ((float)(zig_msvc_flt_inf * 0.f)) +#define zig_msvc_flt_nanl ((long double)(zig_msvc_flt_inf * 0.f)) +#define __builtin_nan(str) nan(str) +#define __builtin_nanf(str) nanf(str) +#define __builtin_nanl(str) nanl(str) +#define __builtin_inf() zig_msvc_flt_inf +#define __builtin_inff() zig_msvc_flt_inff +#define __builtin_infl() zig_msvc_flt_infl +#endif + +#if (zig_has_builtin(nan) && zig_has_builtin(nans) && zig_has_builtin(inf)) || defined(zig_gnuc) +#define zig_has_float_builtins 1 +#define zig_as_special_f16(sign, name, arg, repr) sign zig_as_f16(__builtin_##name, )(arg) +#define zig_as_special_f32(sign, name, arg, repr) sign zig_as_f32(__builtin_##name, )(arg) +#define zig_as_special_f64(sign, name, arg, repr) sign zig_as_f64(__builtin_##name, )(arg) +#define zig_as_special_f80(sign, name, arg, repr) sign zig_as_f80(__builtin_##name, )(arg) +#define zig_as_special_f128(sign, name, arg, repr) sign zig_as_f128(__builtin_##name, )(arg) +#define zig_as_special_c_longdouble(sign, name, arg, repr) sign zig_as_c_longdouble(__builtin_##name, )(arg) +#else +#define zig_has_float_builtins 0 +#define zig_as_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr) +#define zig_as_special_f32(sign, name, arg, repr) zig_float_from_repr_f32(repr) +#define zig_as_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr) +#define zig_as_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr) +#define zig_as_special_f128(sign, name, arg, repr) zig_float_from_repr_f128(repr) +#define zig_as_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr) +#endif + +#define zig_has_f16 1 +#define zig_bitSizeOf_f16 16 +#define zig_libc_name_f16(name) __##name##h +#define zig_as_special_constant_f16(sign, name, arg, repr) zig_as_special_f16(sign, name, arg, repr) +#if FLT_MANT_DIG == 11 +typedef float zig_f16; +#define zig_as_f16(fp, repr) fp##f +#elif DBL_MANT_DIG == 11 +typedef double zig_f16; +#define zig_as_f16(fp, repr) fp +#elif LDBL_MANT_DIG == 11 +#define zig_bitSizeOf_c_longdouble 16 +typedef long double zig_f16; +#define zig_as_f16(fp, repr) fp##l +#elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) +typedef _Float16 zig_f16; +#define zig_as_f16(fp, repr) fp##f16 +#elif defined(__SIZEOF_FP16__) +typedef __fp16 zig_f16; +#define zig_as_f16(fp, repr) fp##f16 +#else +#undef zig_has_f16 +#define zig_has_f16 0 +#define zig_repr_f16 i16 +typedef zig_i16 zig_f16; +#define zig_as_f16(fp, repr) repr +#undef zig_as_special_f16 +#define zig_as_special_f16(sign, name, arg, repr) repr +#undef zig_as_special_constant_f16 +#define zig_as_special_constant_f16(sign, name, arg, repr) repr +#endif + +#define zig_has_f32 1 +#define zig_bitSizeOf_f32 32 +#define zig_libc_name_f32(name) name##f +#if _MSC_VER +#define zig_as_special_constant_f32(sign, name, arg, repr) sign zig_as_f32(zig_msvc_flt_##name, ) +#else +#define zig_as_special_constant_f32(sign, name, arg, repr) zig_as_special_f32(sign, name, arg, repr) +#endif +#if FLT_MANT_DIG == 24 +typedef float zig_f32; +#define zig_as_f32(fp, repr) fp##f +#elif DBL_MANT_DIG == 24 +typedef double zig_f32; +#define zig_as_f32(fp, repr) fp +#elif LDBL_MANT_DIG == 24 +#define zig_bitSizeOf_c_longdouble 32 +typedef long double zig_f32; +#define zig_as_f32(fp, repr) fp##l +#elif FLT32_MANT_DIG == 24 +typedef _Float32 zig_f32; +#define zig_as_f32(fp, repr) fp##f32 +#else +#undef zig_has_f32 +#define zig_has_f32 0 +#define zig_repr_f32 i32 +typedef zig_i32 zig_f32; +#define zig_as_f32(fp, repr) repr +#undef zig_as_special_f32 +#define zig_as_special_f32(sign, name, arg, repr) repr +#undef zig_as_special_constant_f32 +#define zig_as_special_constant_f32(sign, name, arg, repr) repr +#endif + +#define zig_has_f64 1 +#define zig_bitSizeOf_f64 64 +#define zig_libc_name_f64(name) name +#if _MSC_VER +#ifdef ZIG_TARGET_ABI_MSVC +#define zig_bitSizeOf_c_longdouble 64 +#endif +#define zig_as_special_constant_f64(sign, name, arg, repr) sign zig_as_f64(zig_msvc_flt_##name, ) +#else /* _MSC_VER */ +#define zig_as_special_constant_f64(sign, name, arg, repr) zig_as_special_f64(sign, name, arg, repr) +#endif /* _MSC_VER */ +#if FLT_MANT_DIG == 53 +typedef float zig_f64; +#define zig_as_f64(fp, repr) fp##f +#elif DBL_MANT_DIG == 53 +typedef double zig_f64; +#define zig_as_f64(fp, repr) fp +#elif LDBL_MANT_DIG == 53 +#define zig_bitSizeOf_c_longdouble 64 +typedef long double zig_f64; +#define zig_as_f64(fp, repr) fp##l +#elif FLT64_MANT_DIG == 53 +typedef _Float64 zig_f64; +#define zig_as_f64(fp, repr) fp##f64 +#elif FLT32X_MANT_DIG == 53 +typedef _Float32x zig_f64; +#define zig_as_f64(fp, repr) fp##f32x +#else +#undef zig_has_f64 +#define zig_has_f64 0 +#define zig_repr_f64 i64 +typedef zig_i64 zig_f64; +#define zig_as_f64(fp, repr) repr +#undef zig_as_special_f64 +#define zig_as_special_f64(sign, name, arg, repr) repr +#undef zig_as_special_constant_f64 +#define zig_as_special_constant_f64(sign, name, arg, repr) repr +#endif + +#define zig_has_f80 1 +#define zig_bitSizeOf_f80 80 +#define zig_libc_name_f80(name) __##name##x +#define zig_as_special_constant_f80(sign, name, arg, repr) zig_as_special_f80(sign, name, arg, repr) +#if FLT_MANT_DIG == 64 +typedef float zig_f80; +#define zig_as_f80(fp, repr) fp##f +#elif DBL_MANT_DIG == 64 +typedef double zig_f80; +#define zig_as_f80(fp, repr) fp +#elif LDBL_MANT_DIG == 64 +#define zig_bitSizeOf_c_longdouble 80 +typedef long double zig_f80; +#define zig_as_f80(fp, repr) fp##l +#elif FLT80_MANT_DIG == 64 +typedef _Float80 zig_f80; +#define zig_as_f80(fp, repr) fp##f80 +#elif FLT64X_MANT_DIG == 64 +typedef _Float64x zig_f80; +#define zig_as_f80(fp, repr) fp##f64x +#elif defined(__SIZEOF_FLOAT80__) +typedef __float80 zig_f80; +#define zig_as_f80(fp, repr) fp##l +#else +#undef zig_has_f80 +#define zig_has_f80 0 +#define zig_repr_f80 i128 +typedef zig_i128 zig_f80; +#define zig_as_f80(fp, repr) repr +#undef zig_as_special_f80 +#define zig_as_special_f80(sign, name, arg, repr) repr +#undef zig_as_special_constant_f80 +#define zig_as_special_constant_f80(sign, name, arg, repr) repr +#endif + +#define zig_has_f128 1 +#define zig_bitSizeOf_f128 128 +#define zig_libc_name_f128(name) name##q +#define zig_as_special_constant_f128(sign, name, arg, repr) zig_as_special_f128(sign, name, arg, repr) +#if FLT_MANT_DIG == 113 +typedef float zig_f128; +#define zig_as_f128(fp, repr) fp##f +#elif DBL_MANT_DIG == 113 +typedef double zig_f128; +#define zig_as_f128(fp, repr) fp +#elif LDBL_MANT_DIG == 113 +#define zig_bitSizeOf_c_longdouble 128 +typedef long double zig_f128; +#define zig_as_f128(fp, repr) fp##l +#elif FLT128_MANT_DIG == 113 +typedef _Float128 zig_f128; +#define zig_as_f128(fp, repr) fp##f128 +#elif FLT64X_MANT_DIG == 113 +typedef _Float64x zig_f128; +#define zig_as_f128(fp, repr) fp##f64x +#elif defined(__SIZEOF_FLOAT128__) +typedef __float128 zig_f128; +#define zig_as_f128(fp, repr) fp##q +#undef zig_as_special_f128 +#define zig_as_special_f128(sign, name, arg, repr) sign __builtin_##name##f128(arg) +#else +#undef zig_has_f128 +#define zig_has_f128 0 +#define zig_repr_f128 i128 +typedef zig_i128 zig_f128; +#define zig_as_f128(fp, repr) repr +#undef zig_as_special_f128 +#define zig_as_special_f128(sign, name, arg, repr) repr +#undef zig_as_special_constant_f128 +#define zig_as_special_constant_f128(sign, name, arg, repr) repr +#endif + +#define zig_has_c_longdouble 1 + +#ifdef ZIG_TARGET_ABI_MSVC +#define zig_libc_name_c_longdouble(name) name +#else +#define zig_libc_name_c_longdouble(name) name##l +#endif + +#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) zig_as_special_c_longdouble(sign, name, arg, repr) +#ifdef zig_bitSizeOf_c_longdouble + +#ifdef ZIG_TARGET_ABI_MSVC +typedef double zig_c_longdouble; +#undef zig_bitSizeOf_c_longdouble +#define zig_bitSizeOf_c_longdouble 64 +#define zig_as_c_longdouble(fp, repr) fp +#else +typedef long double zig_c_longdouble; +#define zig_as_c_longdouble(fp, repr) fp##l +#endif + +#else /* zig_bitSizeOf_c_longdouble */ + +#undef zig_has_c_longdouble +#define zig_has_c_longdouble 0 +#define zig_bitSizeOf_c_longdouble 80 +#define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 +#define zig_repr_c_longdouble i128 +typedef zig_i128 zig_c_longdouble; +#define zig_as_c_longdouble(fp, repr) repr +#undef zig_as_special_c_longdouble +#define zig_as_special_c_longdouble(sign, name, arg, repr) repr +#undef zig_as_special_constant_c_longdouble +#define zig_as_special_constant_c_longdouble(sign, name, arg, repr) repr + +#endif /* zig_bitSizeOf_c_longdouble */ + +#if !zig_has_float_builtins +#define zig_float_from_repr(Type, ReprType) \ + static inline zig_##Type zig_float_from_repr_##Type(zig_##ReprType repr) { \ + return *((zig_##Type*)&repr); \ + } + +zig_float_from_repr(f16, u16) +zig_float_from_repr(f32, u32) +zig_float_from_repr(f64, u64) +zig_float_from_repr(f80, u128) +zig_float_from_repr(f128, u128) +#if zig_bitSizeOf_c_longdouble == 80 +zig_float_from_repr(c_longdouble, u128) +#else +#define zig_expand_float_from_repr(Type, ReprType) zig_float_from_repr(Type, ReprType) +zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_longdouble)) +#endif +#endif + +#define zig_cast_f16 (zig_f16) +#define zig_cast_f32 (zig_f32) +#define zig_cast_f64 (zig_f64) + +#if _MSC_VER && !zig_has_f128 +#define zig_cast_f80 +#define zig_cast_c_longdouble +#define zig_cast_f128 +#else +#define zig_cast_f80 (zig_f80) +#define zig_cast_c_longdouble (zig_c_longdouble) +#define zig_cast_f128 (zig_f128) +#endif + +#define zig_convert_builtin(ResType, operation, ArgType, version) \ + zig_extern zig_##ResType zig_expand_concat(zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_##ArgType), zig_compiler_rt_abbrev_##ResType), version)(zig_##ArgType); +zig_convert_builtin(f16, trunc, f32, 2) +zig_convert_builtin(f16, trunc, f64, 2) +zig_convert_builtin(f16, trunc, f80, 2) +zig_convert_builtin(f16, trunc, f128, 2) +zig_convert_builtin(f32, extend, f16, 2) +zig_convert_builtin(f32, trunc, f64, 2) +zig_convert_builtin(f32, trunc, f80, 2) +zig_convert_builtin(f32, trunc, f128, 2) +zig_convert_builtin(f64, extend, f16, 2) +zig_convert_builtin(f64, extend, f32, 2) +zig_convert_builtin(f64, trunc, f80, 2) +zig_convert_builtin(f64, trunc, f128, 2) +zig_convert_builtin(f80, extend, f16, 2) +zig_convert_builtin(f80, extend, f32, 2) +zig_convert_builtin(f80, extend, f64, 2) +zig_convert_builtin(f80, trunc, f128, 2) +zig_convert_builtin(f128, extend, f16, 2) +zig_convert_builtin(f128, extend, f32, 2) +zig_convert_builtin(f128, extend, f64, 2) +zig_convert_builtin(f128, extend, f80, 2) + +#define zig_float_negate_builtin_0(Type) \ + static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ + return zig_expand_concat(zig_xor_, zig_repr_##Type)(arg, zig_expand_minInt(zig_repr_##Type, zig_bitSizeOf_##Type)); \ + } +#define zig_float_negate_builtin_1(Type) \ + static inline zig_##Type zig_neg_##Type(zig_##Type arg) { \ + return -arg; \ + } + +#define zig_float_less_builtin_0(Type, operation) \ + zig_extern zig_i32 zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_##Type), 2)(zig_##Type, zig_##Type); \ + static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 2)(lhs, rhs); \ + } +#define zig_float_less_builtin_1(Type, operation) \ + static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return (!(lhs <= rhs) - (lhs < rhs)); \ + } + +#define zig_float_greater_builtin_0(Type, operation) \ + zig_float_less_builtin_0(Type, operation) +#define zig_float_greater_builtin_1(Type, operation) \ + static inline zig_i32 zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return ((lhs > rhs) - !(lhs >= rhs)); \ + } + +#define zig_float_binary_builtin_0(Type, operation, operator) \ + zig_extern zig_##Type zig_expand_concat(zig_expand_concat(__##operation, \ + zig_compiler_rt_abbrev_##Type), 3)(zig_##Type, zig_##Type); \ + static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_expand_concat(zig_expand_concat(__##operation, zig_compiler_rt_abbrev_##Type), 3)(lhs, rhs); \ + } +#define zig_float_binary_builtin_1(Type, operation, operator) \ + static inline zig_##Type zig_##operation##_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return lhs operator rhs; \ + } + +#define zig_float_builtins(Type) \ + zig_convert_builtin(i32, fix, Type, ) \ + zig_convert_builtin(u32, fixuns, Type, ) \ + zig_convert_builtin(i64, fix, Type, ) \ + zig_convert_builtin(u64, fixuns, Type, ) \ + zig_convert_builtin(i128, fix, Type, ) \ + zig_convert_builtin(u128, fixuns, Type, ) \ + zig_convert_builtin(Type, float, i32, ) \ + zig_convert_builtin(Type, floatun, u32, ) \ + zig_convert_builtin(Type, float, i64, ) \ + zig_convert_builtin(Type, floatun, u64, ) \ + zig_convert_builtin(Type, float, i128, ) \ + zig_convert_builtin(Type, floatun, u128, ) \ + zig_expand_concat(zig_float_negate_builtin_, zig_has_##Type)(Type) \ + zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, cmp) \ + zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, ne) \ + zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, eq) \ + zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, lt) \ + zig_expand_concat(zig_float_less_builtin_, zig_has_##Type)(Type, le) \ + zig_expand_concat(zig_float_greater_builtin_, zig_has_##Type)(Type, gt) \ + zig_expand_concat(zig_float_greater_builtin_, zig_has_##Type)(Type, ge) \ + zig_expand_concat(zig_float_binary_builtin_, zig_has_##Type)(Type, add, +) \ + zig_expand_concat(zig_float_binary_builtin_, zig_has_##Type)(Type, sub, -) \ + zig_expand_concat(zig_float_binary_builtin_, zig_has_##Type)(Type, mul, *) \ + zig_expand_concat(zig_float_binary_builtin_, zig_has_##Type)(Type, div, /) \ + zig_extern zig_##Type zig_libc_name_##Type(sqrt)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(sin)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(cos)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(tan)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(exp)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(exp2)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(log)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(log2)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(log10)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(fabs)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(floor)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(ceil)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(round)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(trunc)(zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(fmod)(zig_##Type, zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(fmin)(zig_##Type, zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(fmax)(zig_##Type, zig_##Type); \ + zig_extern zig_##Type zig_libc_name_##Type(fma)(zig_##Type, zig_##Type, zig_##Type); \ +\ + static inline zig_##Type zig_div_trunc_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_libc_name_##Type(trunc)(zig_div_##Type(lhs, rhs)); \ + } \ +\ + static inline zig_##Type zig_div_floor_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_libc_name_##Type(floor)(zig_div_##Type(lhs, rhs)); \ + } \ +\ + static inline zig_##Type zig_mod_##Type(zig_##Type lhs, zig_##Type rhs) { \ + return zig_sub_##Type(lhs, zig_mul_##Type(zig_div_floor_##Type(lhs, rhs), rhs)); \ + } +zig_float_builtins(f16) +zig_float_builtins(f32) +zig_float_builtins(f64) +zig_float_builtins(f80) +zig_float_builtins(f128) +zig_float_builtins(c_longdouble) + +#if _MSC_VER && (_M_IX86 || _M_X64) + +// TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 + +#define zig_msvc_atomics(Type, suffix) \ + static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ + zig_##Type comparand = *expected; \ + zig_##Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ + bool exchanged = initial == comparand; \ + if (!exchanged) { \ + *expected = initial; \ + } \ + return exchanged; \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + return _InterlockedExchange##suffix(obj, value); \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + return _InterlockedExchangeAdd##suffix(obj, value); \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + bool success = false; \ + zig_##Type new; \ + zig_##Type prev; \ + while (!success) { \ + prev = *obj; \ + new = prev - value; \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + } \ + return prev; \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_or_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + return _InterlockedOr##suffix(obj, value); \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_xor_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + return _InterlockedXor##suffix(obj, value); \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_and_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + return _InterlockedAnd##suffix(obj, value); \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_nand_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + bool success = false; \ + zig_##Type new; \ + zig_##Type prev; \ + while (!success) { \ + prev = *obj; \ + new = ~(prev & value); \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + } \ + return prev; \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_min_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + bool success = false; \ + zig_##Type new; \ + zig_##Type prev; \ + while (!success) { \ + prev = *obj; \ + new = value < prev ? value : prev; \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + } \ + return prev; \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_max_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + bool success = false; \ + zig_##Type new; \ + zig_##Type prev; \ + while (!success) { \ + prev = *obj; \ + new = value > prev ? value : prev; \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + } \ + return prev; \ + } \ + static inline void zig_msvc_atomic_store_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + _InterlockedExchange##suffix(obj, value); \ + } \ + static inline zig_##Type zig_msvc_atomic_load_##Type(zig_##Type volatile* obj) { \ + return _InterlockedOr##suffix(obj, 0); \ + } + +zig_msvc_atomics(u8, 8) +zig_msvc_atomics(i8, 8) +zig_msvc_atomics(u16, 16) +zig_msvc_atomics(i16, 16) +zig_msvc_atomics(u32, ) +zig_msvc_atomics(i32, ) + +#if _M_X64 +zig_msvc_atomics(u64, 64) +zig_msvc_atomics(i64, 64) +#endif + +#define zig_msvc_flt_atomics(Type, ReprType, suffix) \ + static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ + zig_##ReprType comparand = *((zig_##ReprType*)expected); \ + zig_##ReprType initial = _InterlockedCompareExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&desired), comparand); \ + bool exchanged = initial == comparand; \ + if (!exchanged) { \ + *expected = *((zig_##Type*)&initial); \ + } \ + return exchanged; \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + zig_##ReprType initial = _InterlockedExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&value)); \ + return *((zig_##Type*)&initial); \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + bool success = false; \ + zig_##ReprType new; \ + zig_##Type prev; \ + while (!success) { \ + prev = *obj; \ + new = prev + value; \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ + } \ + return prev; \ + } \ + static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + bool success = false; \ + zig_##ReprType new; \ + zig_##Type prev; \ + while (!success) { \ + prev = *obj; \ + new = prev - value; \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ + } \ + return prev; \ + } + +zig_msvc_flt_atomics(f32, u32, ) +#if _M_X64 +zig_msvc_flt_atomics(f64, u64, 64) +#endif + +#if _M_IX86 +static inline void zig_msvc_atomic_barrier() { + zig_i32 barrier; + __asm { + xchg barrier, eax + } +} + +static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, zig_u32* arg) { + return _InterlockedExchangePointer(obj, arg); +} + +static inline void zig_msvc_atomic_store_p32(void** obj, zig_u32* arg) { + _InterlockedExchangePointer(obj, arg); +} + +static inline void* zig_msvc_atomic_load_p32(void** obj) { + return (void*)_InterlockedOr((void*)obj, 0); +} + +static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desired) { + void* comparand = *expected; + void* initial = _InterlockedCompareExchangePointer(obj, desired, comparand); + bool exchanged = initial == comparand; + if (!exchanged) { + *expected = initial; + } + return exchanged; +} +#else /* _M_IX86 */ +static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, zig_u64* arg) { + return _InterlockedExchangePointer(obj, arg); +} + +static inline void zig_msvc_atomic_store_p64(void** obj, zig_u64* arg) { + _InterlockedExchangePointer(obj, arg); +} + +static inline void* zig_msvc_atomic_load_p64(void** obj) { + return (void*)_InterlockedOr64((void*)obj, 0); +} + +static inline bool zig_msvc_cmpxchg_p64(void** obj, void** expected, void* desired) { + void* comparand = *expected; + void* initial = _InterlockedCompareExchangePointer(obj, desired, comparand); + bool exchanged = initial == comparand; + if (!exchanged) { + *expected = initial; + } + return exchanged; +} + +static inline bool zig_msvc_cmpxchg_u128(zig_u128 volatile* obj, zig_u128* expected, zig_u128 desired) { + return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_i64*)expected); +} + +static inline bool zig_msvc_cmpxchg_i128(zig_i128 volatile* obj, zig_i128* expected, zig_i128 desired) { + return _InterlockedCompareExchange128((zig_i64 volatile*)obj, desired.hi, desired.lo, (zig_u64*)expected); +} + +#define zig_msvc_atomics_128xchg(Type) \ + static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + bool success = false; \ + zig_##Type prev; \ + while (!success) { \ + prev = *obj; \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, value); \ + } \ + return prev; \ + } + +zig_msvc_atomics_128xchg(u128) +zig_msvc_atomics_128xchg(i128) + +#define zig_msvc_atomics_128op(Type, operation) \ + static inline zig_##Type zig_msvc_atomicrmw_##operation##_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + bool success = false; \ + zig_##Type new; \ + zig_##Type prev; \ + while (!success) { \ + prev = *obj; \ + new = zig_##operation##_##Type(prev, value); \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + } \ + return prev; \ + } + +zig_msvc_atomics_128op(u128, add) +zig_msvc_atomics_128op(u128, sub) +zig_msvc_atomics_128op(u128, or) +zig_msvc_atomics_128op(u128, xor) +zig_msvc_atomics_128op(u128, and) +zig_msvc_atomics_128op(u128, nand) +zig_msvc_atomics_128op(u128, min) +zig_msvc_atomics_128op(u128, max) +#endif /* _M_IX86 */ + +#endif /* _MSC_VER && (_M_IX86 || _M_X64) */ + +/* ========================= Special Case Intrinsics ========================= */ + +#if (_MSC_VER && _M_X64) || defined(__x86_64__) + +static inline void* zig_x86_64_windows_teb(void) { +#if _MSC_VER + return (void*)__readgsqword(0x30); +#else + void* teb; + __asm volatile(" movq %%gs:0x30, %[ptr]": [ptr]"=r"(teb)::); + return teb; +#endif +} + +#elif (_MSC_VER && _M_IX86) || defined(__i386__) || defined(__X86__) + +static inline void* zig_x86_windows_teb(void) { +#if _MSC_VER + return (void*)__readfsdword(0x18); +#else + void* teb; + __asm volatile(" movl %%fs:0x18, %[ptr]": [ptr]"=r"(teb)::); + return teb; +#endif +} + +#endif + +#if (_MSC_VER && (_M_IX86 || _M_X64)) || defined(__i386__) || defined(__x86_64__) + +static inline void zig_x86_cpuid(zig_u32 leaf_id, zig_u32 subid, zig_u32* eax, zig_u32* ebx, zig_u32* ecx, zig_u32* edx) { + zig_u32 cpu_info[4]; +#if _MSC_VER + __cpuidex(cpu_info, leaf_id, subid); +#else + __cpuid_count(leaf_id, subid, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); +#endif + *eax = cpu_info[0]; + *ebx = cpu_info[1]; + *ecx = cpu_info[2]; + *edx = cpu_info[3]; +} + +static inline zig_u32 zig_x86_get_xcr0(void) { +#if _MSC_VER + return (zig_u32)_xgetbv(0); +#else + zig_u32 eax; + zig_u32 edx; + __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); + return eax; +#endif +} + +#endif From cf7200e8f9c995bae8bedaf3c727fe710a93f1e9 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 18 Feb 2023 23:03:11 -0500 Subject: [PATCH 090/122] CBE: remove typedef data structures Adds a new mechanism for `@tagName` function generation that doesn't piggyback on the removed typedef system. --- src/Compilation.zig | 7 +- src/codegen/c.zig | 643 ++++++----------------------------------- src/codegen/c/type.zig | 22 +- src/link/C.zig | 274 +++++++++--------- 4 files changed, 241 insertions(+), 705 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 97153da88b..9359d24dc3 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3277,14 +3277,9 @@ fn processOneJob(comp: *Compilation, job: Job) !void { .decl = decl, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = .{}, - .typedefs = c_codegen.TypedefMap.initContext(gpa, .{ .mod = module }), - .typedefs_arena = ctypes_arena.allocator(), }; defer { - for (dg.typedefs.values()) |typedef| { - module.gpa.free(typedef.rendered); - } - dg.typedefs.deinit(); + dg.ctypes.deinit(gpa); dg.fwd_decl.deinit(); } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index bed3a37a5c..cf4c7ec21b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -23,7 +23,7 @@ const libcFloatSuffix = target_util.libcFloatSuffix; const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; -const Mutability = enum { Const, ConstArgument, Mut }; +const Mutability = enum { @"const", mut }; const BigIntLimb = std.math.big.Limb; const BigInt = std.math.big.int; @@ -63,12 +63,17 @@ const TypedefKind = enum { }; pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue); -pub const TypedefMap = std.ArrayHashMap( - Type, - struct { name: []const u8, rendered: []u8 }, - Type.HashContext32, - true, -); + +pub const LazyFnKey = union(enum) { + tag_name: Decl.Index, +}; +pub const LazyFnValue = struct { + fn_name: []const u8, + data: union { + tag_name: Type, + }, +}; +pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue); const LoopDepth = u16; const Local = struct { @@ -83,11 +88,6 @@ const LocalsList = std.ArrayListUnmanaged(LocalIndex); const LocalsMap = std.ArrayHashMapUnmanaged(Type, LocalsList, Type.HashContext32, true); const LocalsStack = std.ArrayListUnmanaged(LocalsMap); -const FormatTypeAsCIdentContext = struct { - ty: Type, - mod: *Module, -}; - const ValueRenderLocation = enum { FunctionArgument, Initializer, @@ -108,26 +108,6 @@ const BuiltinInfo = enum { Bits, }; -fn formatTypeAsCIdentifier( - data: FormatTypeAsCIdentContext, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, -) !void { - var stack = std.heap.stackFallback(128, data.mod.gpa); - const allocator = stack.get(); - const str = std.fmt.allocPrint(allocator, "{}", .{data.ty.fmt(data.mod)}) catch ""; - defer allocator.free(str); - return formatIdent(str, fmt, options, writer); -} - -pub fn typeToCIdentifier(ty: Type, mod: *Module) std.fmt.Formatter(formatTypeAsCIdentifier) { - return .{ .data = .{ - .ty = ty, - .mod = mod, - } }; -} - const reserved_idents = std.ComptimeStringMap(void, .{ // C language .{ "alignas", { @@ -283,6 +263,7 @@ pub const Function = struct { next_arg_index: usize = 0, next_block_index: usize = 0, object: Object, + lazy_fns: LazyFnMap, func: *Module.Fn, /// All the locals, to be emitted at the top of the function. locals: std.ArrayListUnmanaged(Local) = .{}, @@ -319,7 +300,7 @@ pub const Function = struct { const gpa = f.object.dg.gpa; try f.allocs.put(gpa, decl_c_value.local, true); try writer.writeAll("static "); - try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const, alignment, .Complete); + try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .@"const", alignment, .Complete); try writer.writeAll(" = "); try f.object.dg.renderValue(writer, ty, val, .StaticInitializer); try writer.writeAll(";\n "); @@ -353,7 +334,7 @@ pub const Function = struct { } fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { - const result = try f.allocAlignedLocal(ty, .Mut, 0); + const result = try f.allocAlignedLocal(ty, .mut, 0); log.debug("%{d}: allocating t{d}", .{ inst, result.local }); return result; } @@ -448,6 +429,29 @@ pub const Function = struct { return f.object.dg.fmtIntLiteral(ty, val); } + fn getTagNameFn(f: *Function, enum_ty: Type) ![]const u8 { + const gpa = f.object.dg.gpa; + const owner_decl = enum_ty.getOwnerDecl(); + + const gop = try f.lazy_fns.getOrPut(gpa, .{ .tag_name = owner_decl }); + if (!gop.found_existing) { + errdefer _ = f.lazy_fns.pop(); + + var promoted = f.object.dg.ctypes.promote(gpa); + defer f.object.dg.ctypes.demote(promoted); + const arena = promoted.arena.allocator(); + + gop.value_ptr.* = .{ + .fn_name = try std.fmt.allocPrint(arena, "zig_tagName_{}__{d}", .{ + fmtIdent(mem.span(f.object.dg.module.declPtr(owner_decl).name)), + @enumToInt(owner_decl), + }), + .data = .{ .tag_name = try enum_ty.copy(arena) }, + }; + } + return gop.value_ptr.fn_name; + } + pub fn deinit(f: *Function) void { const gpa = f.object.dg.gpa; f.allocs.deinit(gpa); @@ -458,11 +462,8 @@ pub const Function = struct { f.free_locals_stack.deinit(gpa); f.blocks.deinit(gpa); f.value_map.deinit(); + f.lazy_fns.deinit(gpa); f.object.code.deinit(); - for (f.object.dg.typedefs.values()) |typedef| { - gpa.free(typedef.rendered); - } - f.object.dg.typedefs.deinit(); f.object.dg.ctypes.deinit(gpa); f.object.dg.fwd_decl.deinit(); f.arena.deinit(); @@ -492,9 +493,6 @@ pub const DeclGen = struct { fwd_decl: std.ArrayList(u8), error_msg: ?*Module.ErrorMsg, ctypes: CType.Store, - /// The key of this map is Type which has references to typedefs_arena. - typedefs: TypedefMap, - typedefs_arena: std.mem.Allocator, fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { @setCold(true); @@ -504,14 +502,6 @@ pub const DeclGen = struct { return error.AnalysisFail; } - fn getTypedefName(dg: *DeclGen, t: Type) ?[]const u8 { - if (dg.typedefs.get(t)) |typedef| { - return typedef.name; - } else { - return null; - } - } - fn renderDeclValue( dg: *DeclGen, writer: anytype, @@ -1493,7 +1483,7 @@ pub const DeclGen = struct { if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; if (index > 0) try w.writeAll(", "); const name = CValue{ .arg = index }; - try dg.renderTypeAndName(w, param_type, name, .ConstArgument, 0, kind); + try dg.renderTypeAndName(w, param_type, name, .@"const", 0, kind); index += 1; } @@ -1507,453 +1497,6 @@ pub const DeclGen = struct { if (fn_info.alignment > 0 and kind == .Forward) try w.print(" zig_align_fn({})", .{fn_info.alignment}); } - fn renderPtrToFnTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - const bw = buffer.writer(); - - const fn_info = t.fnInfo(); - - const target = dg.module.getTarget(); - var ret_buf: LowerFnRetTyBuffer = undefined; - const ret_ty = lowerFnRetTy(fn_info.return_type, &ret_buf, target); - - try bw.writeAll("typedef "); - try dg.renderType(bw, ret_ty, .Forward); - try bw.writeAll(" (*"); - const name_begin = buffer.items.len; - try bw.print("zig_F_{}", .{typeToCIdentifier(t, dg.module)}); - const name_end = buffer.items.len; - try bw.writeAll(")("); - - const param_len = fn_info.param_types.len; - - var params_written: usize = 0; - var index: usize = 0; - while (index < param_len) : (index += 1) { - const param_ty = fn_info.param_types[index]; - if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; - if (params_written > 0) { - try bw.writeAll(", "); - } - try dg.renderTypeAndName(bw, param_ty, .{ .bytes = "" }, .Mut, 0, .Forward); - params_written += 1; - } - - if (fn_info.is_var_args) { - if (params_written != 0) try bw.writeAll(", "); - try bw.writeAll("..."); - } else if (params_written == 0) { - try dg.renderType(bw, Type.void, .Forward); - } - try bw.writeAll(");\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_begin..name_end]; - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderSliceTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - std.debug.assert(t.sentinel() == null); // expected canonical type - - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - const bw = buffer.writer(); - - var ptr_ty_buf: Type.SlicePtrFieldTypeBuffer = undefined; - const ptr_ty = t.slicePtrFieldType(&ptr_ty_buf); - const ptr_name = CValue{ .identifier = "ptr" }; - const len_ty = Type.usize; - const len_name = CValue{ .identifier = "len" }; - - try bw.writeAll("typedef struct {\n "); - try dg.renderTypeAndName(bw, ptr_ty, ptr_name, .Mut, 0, .Complete); - try bw.writeAll(";\n "); - try dg.renderTypeAndName(bw, len_ty, len_name, .Mut, 0, .Complete); - - try bw.writeAll(";\n} "); - const name_begin = buffer.items.len; - try bw.print("zig_{c}_{}", .{ - @as(u8, if (t.isConstPtr()) 'L' else 'M'), - typeToCIdentifier(t.childType(), dg.module), - }); - const name_end = buffer.items.len; - try bw.writeAll(";\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_begin..name_end]; - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderFwdTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - // The forward declaration for T is stored with a key of *const T. - const child_ty = t.childType(); - - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - const bw = buffer.writer(); - - const tag = switch (child_ty.zigTypeTag()) { - .Struct, .ErrorUnion, .Optional => "struct", - .Union => if (child_ty.unionTagTypeSafety()) |_| "struct" else "union", - else => unreachable, - }; - try bw.writeAll("typedef "); - try bw.writeAll(tag); - const name_begin = buffer.items.len + " ".len; - try bw.writeAll(" zig_"); - switch (child_ty.zigTypeTag()) { - .Struct, .Union => { - var fqn_buf = std.ArrayList(u8).init(dg.typedefs.allocator); - defer fqn_buf.deinit(); - - const owner_decl_index = child_ty.getOwnerDecl(); - const owner_decl = dg.module.declPtr(owner_decl_index); - try owner_decl.renderFullyQualifiedName(dg.module, fqn_buf.writer()); - - try bw.print("S_{}__{d}", .{ fmtIdent(fqn_buf.items), @enumToInt(owner_decl_index) }); - }, - .ErrorUnion => { - try bw.print("E_{}", .{typeToCIdentifier(child_ty.errorUnionPayload(), dg.module)}); - }, - .Optional => { - var opt_buf: Type.Payload.ElemType = undefined; - try bw.print("Q_{}", .{typeToCIdentifier(child_ty.optionalChild(&opt_buf), dg.module)}); - }, - else => unreachable, - } - const name_end = buffer.items.len; - try buffer.ensureUnusedCapacity(" ".len + (name_end - name_begin) + ";\n".len); - buffer.appendAssumeCapacity(' '); - buffer.appendSliceAssumeCapacity(buffer.items[name_begin..name_end]); - buffer.appendSliceAssumeCapacity(";\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_begin..name_end]; - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); - - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - - try buffer.appendSlice("struct "); - - var needs_pack_attr = false; - { - var it = t.structFields().iterator(); - while (it.next()) |field| { - const field_ty = field.value_ptr.ty; - if (!field_ty.hasRuntimeBits()) continue; - const alignment = field.value_ptr.abi_align; - if (alignment != 0 and alignment < field_ty.abiAlignment(dg.module.getTarget())) { - needs_pack_attr = true; - try buffer.appendSlice("zig_packed("); - break; - } - } - } - - try buffer.appendSlice(name); - try buffer.appendSlice(" {\n"); - { - var it = t.structFields().iterator(); - var empty = true; - while (it.next()) |field| { - const field_ty = field.value_ptr.ty; - if (!field_ty.hasRuntimeBits()) continue; - - const alignment = field.value_ptr.alignment(dg.module.getTarget(), t.containerLayout()); - const field_name = CValue{ .identifier = field.key_ptr.* }; - try buffer.append(' '); - try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); - try buffer.appendSlice(";\n"); - - empty = false; - } - if (empty) try buffer.appendSlice(" char empty_struct;\n"); - } - if (needs_pack_attr) try buffer.appendSlice("});\n") else try buffer.appendSlice("};\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderTupleTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - - try buffer.appendSlice("typedef struct {\n"); - { - const fields = t.tupleFields(); - var field_id: usize = 0; - for (fields.types, 0..) |field_ty, i| { - if (!field_ty.hasRuntimeBits() or fields.values[i].tag() != .unreachable_value) continue; - - try buffer.append(' '); - try dg.renderTypeAndName(buffer.writer(), field_ty, .{ .field = field_id }, .Mut, 0, .Complete); - try buffer.appendSlice(";\n"); - - field_id += 1; - } - if (field_id == 0) try buffer.appendSlice(" char empty_tuple;\n"); - } - const name_begin = buffer.items.len + "} ".len; - try buffer.writer().print("}} zig_T_{}_{d};\n", .{ typeToCIdentifier(t, dg.module), @truncate(u16, t.hash(dg.module)) }); - const name_end = buffer.items.len - ";\n".len; - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_begin..name_end]; - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); - - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - - try buffer.appendSlice(if (t.unionTagTypeSafety()) |_| "struct " else "union "); - try buffer.appendSlice(name); - try buffer.appendSlice(" {\n"); - - const indent = if (t.unionTagTypeSafety()) |tag_ty| indent: { - const target = dg.module.getTarget(); - const layout = t.unionGetLayout(target); - if (layout.tag_size != 0) { - try buffer.append(' '); - try dg.renderTypeAndName(buffer.writer(), tag_ty, .{ .identifier = "tag" }, .Mut, 0, .Complete); - try buffer.appendSlice(";\n"); - } - try buffer.appendSlice(" union {\n"); - break :indent " "; - } else " "; - - { - var it = t.unionFields().iterator(); - var empty = true; - while (it.next()) |field| { - const field_ty = field.value_ptr.ty; - if (!field_ty.hasRuntimeBits()) continue; - - const alignment = field.value_ptr.abi_align; - const field_name = CValue{ .identifier = field.key_ptr.* }; - try buffer.appendSlice(indent); - try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); - try buffer.appendSlice(";\n"); - - empty = false; - } - if (empty) { - try buffer.appendSlice(indent); - try buffer.appendSlice("char empty_union;\n"); - } - } - - if (t.unionTagTypeSafety()) |_| try buffer.appendSlice(" } payload;\n"); - try buffer.appendSlice("};\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - assert(t.errorUnionSet().tag() == .anyerror); - - var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); - - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - const bw = buffer.writer(); - - const payload_ty = t.errorUnionPayload(); - const payload_name = CValue{ .identifier = "payload" }; - const error_ty = t.errorUnionSet(); - const error_name = CValue{ .identifier = "error" }; - - const target = dg.module.getTarget(); - const payload_align = payload_ty.abiAlignment(target); - const error_align = error_ty.abiAlignment(target); - try bw.writeAll("struct "); - try bw.writeAll(name); - try bw.writeAll(" {\n "); - if (error_align > payload_align) { - try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); - try bw.writeAll(";\n "); - try dg.renderTypeAndName(bw, error_ty, error_name, .Mut, 0, .Complete); - } else { - try dg.renderTypeAndName(bw, error_ty, error_name, .Mut, 0, .Complete); - try bw.writeAll(";\n "); - try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0, .Complete); - } - try bw.writeAll(";\n};\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderArrayTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - const info = t.arrayInfo(); - std.debug.assert(info.sentinel == null); // expected canonical type - - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - const bw = buffer.writer(); - - try bw.writeAll("typedef "); - try dg.renderType(bw, info.elem_type, .Complete); - - const name_begin = buffer.items.len + " ".len; - try bw.print(" zig_A_{}_{d}", .{ typeToCIdentifier(info.elem_type, dg.module), info.len }); - const name_end = buffer.items.len; - - const c_len = if (info.len > 0) info.len else 1; - var c_len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = c_len }; - const c_len_val = Value.initPayload(&c_len_pl.base); - try bw.print("[{}];\n", .{try dg.fmtIntLiteral(Type.usize, c_len_val)}); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_begin..name_end]; - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderOptionalTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - var ptr_pl = Type.Payload.ElemType{ .base = .{ .tag = .single_const_pointer }, .data = t }; - const ptr_ty = Type.initPayload(&ptr_pl.base); - const name = dg.getTypedefName(ptr_ty) orelse - try dg.renderFwdTypedef(ptr_ty); - - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - const bw = buffer.writer(); - - var opt_buf: Type.Payload.ElemType = undefined; - const child_ty = t.optionalChild(&opt_buf); - - try bw.writeAll("struct "); - try bw.writeAll(name); - try bw.writeAll(" {\n"); - try dg.renderTypeAndName(bw, child_ty, .{ .identifier = "payload" }, .Mut, 0, .Complete); - try bw.writeAll(";\n "); - try dg.renderTypeAndName(bw, Type.bool, .{ .identifier = "is_null" }, .Mut, 0, .Complete); - try bw.writeAll(";\n};\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn renderOpaqueTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - const opaque_ty = t.cast(Type.Payload.Opaque).?.data; - const unqualified_name = dg.module.declPtr(opaque_ty.owner_decl).name; - const fqn = try opaque_ty.getFullyQualifiedName(dg.module); - defer dg.typedefs.allocator.free(fqn); - - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - - try buffer.writer().print("typedef struct { } ", .{fmtIdent(std.mem.span(unqualified_name))}); - - const name_begin = buffer.items.len; - try buffer.writer().print("zig_O_{}", .{fmtIdent(fqn)}); - const name_end = buffer.items.len; - try buffer.appendSlice(";\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_begin..name_end]; - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try t.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { return dg.ctypes.indexToCType(idx); } @@ -2408,31 +1951,27 @@ pub const DeclGen = struct { const idx = try dg.typeToIndex(ty); try w.print("{}", .{try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.init(.{ .@"const" = switch (mutability) { - .Const, .ConstArgument => true, - .Mut => false, + .mut => false, + .@"const" => true, }, }))}); try dg.writeCValue(w, name); try dg.renderTypeSuffix(w, idx, .suffix); } - fn renderTagNameFn(dg: *DeclGen, enum_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 { - var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); - defer buffer.deinit(); - const bw = buffer.writer(); - + fn renderTagNameFn(dg: *DeclGen, w: anytype, fn_name: []const u8, enum_ty: Type) !void { const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); - try buffer.appendSlice("static "); - try dg.renderType(bw, name_slice_ty, .Complete); - const name_begin = buffer.items.len + " ".len; - try bw.print(" zig_tagName_{}_{d}(", .{ typeToCIdentifier(enum_ty, dg.module), @enumToInt(enum_ty.getOwnerDecl()) }); - const name_end = buffer.items.len - "(".len; - try dg.renderTypeAndName(bw, enum_ty, .{ .identifier = "tag" }, .Const, 0, .Complete); - try buffer.appendSlice(") {\n switch (tag) {\n"); + try w.writeAll("static "); + try dg.renderType(w, name_slice_ty, .Complete); + try w.writeByte(' '); + try w.writeAll(fn_name); + try w.writeByte('('); + try dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, .@"const", 0, .Complete); + try w.writeAll(") {\n switch (tag) {\n"); for (enum_ty.enumFields().keys(), 0..) |name, index| { - const name_z = try dg.typedefs.allocator.dupeZ(u8, name); - defer dg.typedefs.allocator.free(name_z); + const name_z = try dg.gpa.dupeZ(u8, name); + defer dg.gpa.free(name_z); const name_bytes = name_z[0 .. name_z.len + 1]; var tag_pl: Value.Payload.U32 = .{ @@ -2453,40 +1992,23 @@ pub const DeclGen = struct { var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; const len_val = Value.initPayload(&len_pl.base); - try bw.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)}); - try dg.renderTypeAndName(bw, name_ty, .{ .identifier = "name" }, .Const, 0, .Complete); - try buffer.appendSlice(" = "); - try dg.renderValue(bw, name_ty, name_val, .Initializer); - try buffer.appendSlice(";\n return ("); - try dg.renderTypecast(bw, name_slice_ty); - try bw.print("){{{}, {}}};\n", .{ + try w.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)}); + try dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, .@"const", 0, .Complete); + try w.writeAll(" = "); + try dg.renderValue(w, name_ty, name_val, .Initializer); + try w.writeAll(";\n return ("); + try dg.renderTypecast(w, name_slice_ty); + try w.print("){{{}, {}}};\n", .{ fmtIdent("name"), try dg.fmtIntLiteral(Type.usize, len_val), }); - try buffer.appendSlice(" }\n"); + try w.writeAll(" }\n"); } - try buffer.appendSlice(" }\n while ("); - try dg.renderValue(bw, Type.bool, Value.true, .Other); - try buffer.appendSlice(") "); - _ = try airBreakpoint(bw); - try buffer.appendSlice("}\n"); - - const rendered = try buffer.toOwnedSlice(); - errdefer dg.typedefs.allocator.free(rendered); - const name = rendered[name_begin..name_end]; - - try dg.typedefs.ensureUnusedCapacity(1); - dg.typedefs.putAssumeCapacityNoClobber( - try enum_ty.copy(dg.typedefs_arena), - .{ .name = name, .rendered = rendered }, - ); - - return name; - } - - fn getTagNameFn(dg: *DeclGen, enum_ty: Type) ![]const u8 { - return dg.getTypedefName(enum_ty) orelse - try dg.renderTagNameFn(enum_ty); + try w.writeAll(" }\n while ("); + try dg.renderValue(w, Type.bool, Value.true, .Other); + try w.writeAll(") "); + _ = try airBreakpoint(w); + try w.writeAll("}\n"); } fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { @@ -2724,7 +2246,7 @@ pub fn genErrDecls(o: *Object) !void { const name_val = Value.initPayload(&name_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .Const, 0, .Complete); + try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .@"const", 0, .Complete); try writer.writeAll(" = "); try o.dg.renderValue(writer, name_ty, name_val, .StaticInitializer); try writer.writeAll(";\n"); @@ -2737,7 +2259,7 @@ pub fn genErrDecls(o: *Object) !void { const name_array_ty = Type.initPayload(&name_array_ty_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .Const, 0, .Complete); + try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .@"const", 0, .Complete); try writer.writeAll(" = {"); for (o.dg.module.error_name_list.items, 0..) |name, value| { if (value != 0) try writer.writeByte(','); @@ -2767,6 +2289,17 @@ fn genExports(o: *Object) !void { }; } +pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { + const writer = o.writer(); + switch (lazy_fn.key_ptr.*) { + .tag_name => _ = try o.dg.renderTagNameFn( + writer, + lazy_fn.value_ptr.fn_name, + lazy_fn.value_ptr.data.tag_name, + ), + } +} + pub fn genFunc(f: *Function) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2845,7 +2378,7 @@ pub fn genFunc(f: *Function) !void { w, local.ty, .{ .local = local_index }, - .Mut, + .mut, local.alignment, .Complete, ); @@ -2886,7 +2419,7 @@ pub fn genDecl(o: *Object) !void { try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal "); - try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); + try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .mut, o.dg.decl.@"align", .Complete); try fwd_decl_writer.writeAll(";\n"); try genExports(o); @@ -2896,7 +2429,7 @@ pub fn genDecl(o: *Object) !void { if (!is_global) try w.writeAll("static "); if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); - try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); + try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .mut, o.dg.decl.@"align", .Complete); if (o.dg.decl.@"linksection" != null) try w.writeAll(", read, write)"); try w.writeAll(" = "); try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); @@ -2908,13 +2441,13 @@ pub fn genDecl(o: *Object) !void { const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); - try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete); + try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .@"const", o.dg.decl.@"align", .Complete); try fwd_decl_writer.writeAll(";\n"); const w = o.writer(); if (!is_global) try w.writeAll("static "); if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); - try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete); + try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .@"const", o.dg.decl.@"align", .Complete); if (o.dg.decl.@"linksection" != null) try w.writeAll(", read)"); try w.writeAll(" = "); try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); @@ -3443,7 +2976,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { return CValue{ .undef = inst_ty }; } - const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; + const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut; const target = f.object.dg.module.getTarget(); const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target)); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local }); @@ -3460,7 +2993,7 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { return CValue{ .undef = inst_ty }; } - const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut; + const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut; const target = f.object.dg.module.getTarget(); const local = try f.allocAlignedLocal(elem_ty, mutability, inst_ty.ptrAlignment(target)); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local }); @@ -4937,7 +4470,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { writer, output_ty, local_value, - .Mut, + .mut, alignment, .Complete, ); @@ -4976,7 +4509,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { writer, input_ty, local_value, - .Const, + .@"const", alignment, .Complete, ); @@ -6474,7 +6007,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); try f.writeCValue(writer, local, .Other); - try writer.print(" = {s}(", .{try f.object.dg.getTagNameFn(enum_ty)}); + try writer.print(" = {s}(", .{try f.getTagNameFn(enum_ty)}); try f.writeCValue(writer, operand, .Other); try writer.writeAll(");\n"); diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 601c15abee..ad482024b7 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -290,11 +290,11 @@ pub const CType = extern union { } }; - const Promoted = struct { + pub const Promoted = struct { arena: std.heap.ArenaAllocator, set: Set, - fn gpa(self: *Promoted) Allocator { + pub fn gpa(self: *Promoted) Allocator { return self.arena.child_allocator; } @@ -345,11 +345,11 @@ pub const CType = extern union { } }; - fn promote(self: Store, gpa: Allocator) Promoted { + pub fn promote(self: Store, gpa: Allocator) Promoted { return .{ .arena = self.arena.promote(gpa), .set = self.set }; } - fn demote(self: *Store, promoted: Promoted) void { + pub fn demote(self: *Store, promoted: Promoted) void { self.arena = promoted.arena.state; self.set = promoted.set; } @@ -382,17 +382,17 @@ pub const CType = extern union { _ = promoted.arena.reset(.retain_capacity); } - pub fn shrinkToFit(self: *Store, gpa: Allocator) void { - self.map.shrinkAndFree(gpa, self.map.entries.len); - } - - pub fn shrinkAndFree(self: *Store, gpa: Allocator) void { + pub fn clearAndFree(self: *Store, gpa: Allocator) void { var promoted = self.promote(gpa); defer self.demote(promoted); promoted.set.map.clearAndFree(gpa); _ = promoted.arena.reset(.free_all); } + pub fn shrinkToFit(self: *Store, gpa: Allocator) void { + self.set.map.shrinkAndFree(gpa, self.set.map.count()); + } + pub fn move(self: *Store) Store { const moved = self.*; self.* = .{}; @@ -1252,8 +1252,8 @@ pub const CType = extern union { pub const HashContext64 = struct { store: *const Store.Set, - pub fn hash(_: @This(), cty: CType) u64 { - return cty.hash(); + pub fn hash(self: @This(), cty: CType) u64 { + return cty.hash(self.store.*); } pub fn eql(_: @This(), lhs: CType, rhs: CType) bool { return lhs.eql(rhs); diff --git a/src/link/C.zig b/src/link/C.zig index 7fb23b2642..8eb6fe16af 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -22,26 +22,19 @@ base: link.File, /// Instead, it tracks all declarations in this table, and iterates over it /// in the flush function, stitching pre-rendered pieces of C code together. decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclBlock) = .{}, -/// Stores Type/Value data for `typedefs` to reference. -/// Accumulates allocations and then there is a periodic garbage collection after flush(). -arena: std.heap.ArenaAllocator, /// Per-declaration data. const DeclBlock = struct { code: std.ArrayListUnmanaged(u8) = .{}, fwd_decl: std.ArrayListUnmanaged(u8) = .{}, + /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate + /// over each `Decl` and generate the definition for each used `CType` once. ctypes: codegen.CType.Store = .{}, - /// Each Decl stores a mapping of Zig Types to corresponding C types, for every - /// Zig Type used by the Decl. In flush(), we iterate over each Decl - /// and emit the typedef code for all types, making sure to not emit the same thing twice. - /// Any arena memory the Type points to lives in the `arena` field of `C`. - typedefs: codegen.TypedefMap.Unmanaged = .{}, + /// Key and Value storage use the ctype arena. + lazy_fns: codegen.LazyFnMap = .{}, fn deinit(db: *DeclBlock, gpa: Allocator) void { - for (db.typedefs.values()) |typedef| { - gpa.free(typedef.rendered); - } - db.typedefs.deinit(gpa); + db.lazy_fns.deinit(gpa); db.ctypes.deinit(gpa); db.fwd_decl.deinit(gpa); db.code.deinit(gpa); @@ -66,7 +59,6 @@ pub fn openPath(gpa: Allocator, sub_path: []const u8, options: link.Options) !*C errdefer gpa.destroy(c_file); c_file.* = C{ - .arena = std.heap.ArenaAllocator.init(gpa), .base = .{ .tag = .c, .options = options, @@ -85,8 +77,6 @@ pub fn deinit(self: *C) void { db.deinit(gpa); } self.decl_table.deinit(gpa); - - self.arena.deinit(); } pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void { @@ -101,44 +91,42 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes const tracy = trace(@src()); defer tracy.end(); + const gpa = self.base.allocator; + const decl_index = func.owner_decl; - const gop = try self.decl_table.getOrPut(self.base.allocator, decl_index); + const gop = try self.decl_table.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } - const fwd_decl = &gop.value_ptr.fwd_decl; const ctypes = &gop.value_ptr.ctypes; - const typedefs = &gop.value_ptr.typedefs; + const lazy_fns = &gop.value_ptr.lazy_fns; + const fwd_decl = &gop.value_ptr.fwd_decl; const code = &gop.value_ptr.code; + ctypes.clearRetainingCapacity(gpa); + lazy_fns.clearRetainingCapacity(); fwd_decl.shrinkRetainingCapacity(0); - ctypes.clearRetainingCapacity(module.gpa); - for (typedefs.values()) |typedef| { - module.gpa.free(typedef.rendered); - } - typedefs.clearRetainingCapacity(); code.shrinkRetainingCapacity(0); var function: codegen.Function = .{ - .value_map = codegen.CValueMap.init(module.gpa), + .value_map = codegen.CValueMap.init(gpa), .air = air, .liveness = liveness, .func = func, .object = .{ .dg = .{ - .gpa = module.gpa, + .gpa = gpa, .module = module, .error_msg = null, .decl_index = decl_index, .decl = module.declPtr(decl_index), - .fwd_decl = fwd_decl.toManaged(module.gpa), + .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, - .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), - .typedefs_arena = self.arena.allocator(), }, - .code = code.toManaged(module.gpa), + .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }, - .arena = std.heap.ArenaAllocator.init(module.gpa), + .lazy_fns = lazy_fns.*, + .arena = std.heap.ArenaAllocator.init(gpa), }; function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; @@ -146,91 +134,79 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes codegen.genFunc(&function) catch |err| switch (err) { error.AnalysisFail => { - try module.failed_decls.put(module.gpa, decl_index, function.object.dg.error_msg.?); + try module.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?); return; }, else => |e| return e, }; - fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); ctypes.* = function.object.dg.ctypes.move(); - typedefs.* = function.object.dg.typedefs.unmanaged; - function.object.dg.typedefs.unmanaged = .{}; + lazy_fns.* = function.lazy_fns.move(); + fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); code.* = function.object.code.moveToUnmanaged(); // Free excess allocated memory for this Decl. - fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len); - code.shrinkAndFree(module.gpa, code.items.len); - ctypes.shrinkAndFree(module.gpa); + ctypes.shrinkToFit(gpa); + lazy_fns.shrinkAndFree(gpa, lazy_fns.count()); + fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); + code.shrinkAndFree(gpa, code.items.len); } pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { const tracy = trace(@src()); defer tracy.end(); - const gop = try self.decl_table.getOrPut(self.base.allocator, decl_index); + const gpa = self.base.allocator; + + const gop = try self.decl_table.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } - const fwd_decl = &gop.value_ptr.fwd_decl; const ctypes = &gop.value_ptr.ctypes; - const typedefs = &gop.value_ptr.typedefs; + const fwd_decl = &gop.value_ptr.fwd_decl; const code = &gop.value_ptr.code; + ctypes.clearRetainingCapacity(gpa); fwd_decl.shrinkRetainingCapacity(0); - ctypes.clearRetainingCapacity(module.gpa); - for (typedefs.values()) |value| { - module.gpa.free(value.rendered); - } - typedefs.clearRetainingCapacity(); code.shrinkRetainingCapacity(0); const decl = module.declPtr(decl_index); var object: codegen.Object = .{ .dg = .{ - .gpa = module.gpa, + .gpa = gpa, .module = module, .error_msg = null, .decl_index = decl_index, .decl = decl, - .fwd_decl = fwd_decl.toManaged(module.gpa), + .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, - .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), - .typedefs_arena = self.arena.allocator(), }, - .code = code.toManaged(module.gpa), + .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }; object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { object.code.deinit(); - for (object.dg.typedefs.values()) |typedef| { - module.gpa.free(typedef.rendered); - } - object.dg.typedefs.deinit(); object.dg.ctypes.deinit(object.dg.gpa); object.dg.fwd_decl.deinit(); } codegen.genDecl(&object) catch |err| switch (err) { error.AnalysisFail => { - try module.failed_decls.put(module.gpa, decl_index, object.dg.error_msg.?); + try module.failed_decls.put(gpa, decl_index, object.dg.error_msg.?); return; }, else => |e| return e, }; + ctypes.* = object.dg.ctypes.move(); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); - ctypes.* = object.dg.ctypes; - object.dg.ctypes = .{}; - typedefs.* = object.dg.typedefs.unmanaged; - object.dg.typedefs.unmanaged = .{}; code.* = object.code.moveToUnmanaged(); // Free excess allocated memory for this Decl. - fwd_decl.shrinkAndFree(module.gpa, fwd_decl.items.len); - code.shrinkAndFree(module.gpa, code.items.len); - ctypes.shrinkAndFree(module.gpa); + ctypes.shrinkToFit(gpa); + fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); + code.shrinkAndFree(gpa, code.items.len); } pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { @@ -260,7 +236,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) sub_prog_node.activate(); defer sub_prog_node.end(); - const gpa = comp.gpa; + const gpa = self.base.allocator; const module = self.base.options.module.?; // This code path happens exclusively with -ofmt=c. The flush logic for @@ -271,19 +247,17 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const abi_define = abiDefine(comp); - // Covers defines, zig.h, typedef, and asm. - var buf_count: usize = 2; - if (abi_define != null) buf_count += 1; - try f.all_buffers.ensureUnusedCapacity(gpa, buf_count); + // Covers defines, zig.h, ctypes, asm. + try f.all_buffers.ensureUnusedCapacity(gpa, 4); if (abi_define) |buf| f.appendBufAssumeCapacity(buf); f.appendBufAssumeCapacity(zig_h); - const typedef_index = f.all_buffers.items.len; + const ctypes_index = f.all_buffers.items.len; f.all_buffers.items.len += 1; { - var asm_buf = f.asm_buf.toManaged(module.gpa); + var asm_buf = f.asm_buf.toManaged(gpa); defer asm_buf.deinit(); try codegen.genGlobalAsm(module, &asm_buf); @@ -294,7 +268,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) try self.flushErrDecls(&f); - // Typedefs, forward decls, and non-functions first. + // `CType`s, forward decls, and non-functions first. // Unlike other backends, the .c code we are emitting is order-dependent. Therefore // we must traverse the set of Decls that we are emitting according to their dependencies. // Our strategy is to populate a set of remaining decls, pop Decls one by one, @@ -321,11 +295,11 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) } } - f.all_buffers.items[typedef_index] = .{ - .iov_base = if (f.typedef_buf.items.len > 0) f.typedef_buf.items.ptr else "", - .iov_len = f.typedef_buf.items.len, + f.all_buffers.items[ctypes_index] = .{ + .iov_base = if (f.ctypes_buf.items.len > 0) f.ctypes_buf.items.ptr else "", + .iov_len = f.ctypes_buf.items.len, }; - f.file_size += f.typedef_buf.items.len; + f.file_size += f.ctypes_buf.items.len; // Now the code. try f.all_buffers.ensureUnusedCapacity(gpa, decl_values.len); @@ -338,31 +312,23 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) } const Flush = struct { - err_decls: DeclBlock = .{}, remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, - ctypes: CTypes = .{}, - typedefs: Typedefs = .{}, - typedef_buf: std.ArrayListUnmanaged(u8) = .{}, + ctypes: codegen.CType.Store = .{}, + ctypes_map: std.ArrayListUnmanaged(codegen.CType.Index) = .{}, + ctypes_buf: std.ArrayListUnmanaged(u8) = .{}, + + err_decls: DeclBlock = .{}, + + lazy_fns: LazyFns = .{}, + asm_buf: std.ArrayListUnmanaged(u8) = .{}, /// We collect a list of buffers to write, and write them all at once with pwritev 😎 all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{}, /// Keeps track of the total bytes of `all_buffers`. file_size: u64 = 0, - const CTypes = std.ArrayHashMapUnmanaged( - codegen.CType, - void, - codegen.CType.HashContext32, - true, - ); - - const Typedefs = std.HashMapUnmanaged( - Type, - void, - Type.HashContext64, - std.hash_map.default_max_load_percentage, - ); + const LazyFns = std.AutoHashMapUnmanaged(codegen.LazyFnKey, DeclBlock); fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void { if (buf.len == 0) return; @@ -372,11 +338,14 @@ const Flush = struct { fn deinit(f: *Flush, gpa: Allocator) void { f.all_buffers.deinit(gpa); - f.typedef_buf.deinit(gpa); - f.typedefs.deinit(gpa); + var lazy_fns_it = f.lazy_fns.valueIterator(); + while (lazy_fns_it.next()) |db| db.deinit(gpa); + f.lazy_fns.deinit(gpa); + f.err_decls.deinit(gpa); + f.ctypes_buf.deinit(gpa); + f.ctypes_map.deinit(gpa); f.ctypes.deinit(gpa); f.remaining_decls.deinit(gpa); - f.err_decls.deinit(gpa); } }; @@ -384,56 +353,36 @@ const FlushDeclError = error{ OutOfMemory, }; -fn flushTypedefs(self: *C, f: *Flush, typedefs: codegen.TypedefMap.Unmanaged) FlushDeclError!void { - if (typedefs.count() == 0) return; - const gpa = self.base.allocator; - const module = self.base.options.module.?; - - try f.typedefs.ensureUnusedCapacityContext(gpa, @intCast(u32, typedefs.count()), .{ - .mod = module, - }); - var it = typedefs.iterator(); - while (it.next()) |new| { - const gop = f.typedefs.getOrPutAssumeCapacityContext(new.key_ptr.*, .{ - .mod = module, - }); - if (!gop.found_existing) { - try f.typedef_buf.appendSlice(gpa, new.value_ptr.rendered); - } - } +fn flushCTypes(self: *C, f: *Flush, ctypes: codegen.CType.Store) FlushDeclError!void { + _ = self; + _ = f; + _ = ctypes; } fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { - const module = self.base.options.module.?; + const gpa = self.base.allocator; const fwd_decl = &f.err_decls.fwd_decl; const ctypes = &f.err_decls.ctypes; - const typedefs = &f.err_decls.typedefs; const code = &f.err_decls.code; var object = codegen.Object{ .dg = .{ - .gpa = module.gpa, - .module = module, + .gpa = gpa, + .module = self.base.options.module.?, .error_msg = null, .decl_index = undefined, .decl = undefined, - .fwd_decl = fwd_decl.toManaged(module.gpa), + .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, - .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }), - .typedefs_arena = self.arena.allocator(), }, - .code = code.toManaged(module.gpa), + .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code }; object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { object.code.deinit(); - object.dg.ctypes.deinit(module.gpa); - for (object.dg.typedefs.values()) |typedef| { - module.gpa.free(typedef.rendered); - } - object.dg.typedefs.deinit(); + object.dg.ctypes.deinit(gpa); object.dg.fwd_decl.deinit(); } @@ -443,16 +392,75 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { }; fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); - typedefs.* = object.dg.typedefs.unmanaged; - object.dg.typedefs.unmanaged = .{}; + ctypes.* = object.dg.ctypes.move(); code.* = object.code.moveToUnmanaged(); - try self.flushTypedefs(f, typedefs.*); - try f.all_buffers.ensureUnusedCapacity(self.base.allocator, 1); + try self.flushCTypes(f, ctypes.*); + try f.all_buffers.ensureUnusedCapacity(gpa, 2); f.appendBufAssumeCapacity(fwd_decl.items); f.appendBufAssumeCapacity(code.items); } +fn flushLazyFn( + self: *C, + f: *Flush, + db: *DeclBlock, + lazy_fn: codegen.LazyFnMap.Entry, +) FlushDeclError!void { + const gpa = self.base.allocator; + + const fwd_decl = &db.fwd_decl; + const ctypes = &db.ctypes; + const code = &db.code; + + var object = codegen.Object{ + .dg = .{ + .gpa = gpa, + .module = self.base.options.module.?, + .error_msg = null, + .decl_index = undefined, + .decl = undefined, + .fwd_decl = fwd_decl.toManaged(gpa), + .ctypes = ctypes.*, + }, + .code = code.toManaged(gpa), + .indent_writer = undefined, // set later so we can get a pointer to object.code + }; + object.indent_writer = .{ .underlying_writer = object.code.writer() }; + defer { + object.code.deinit(); + object.dg.ctypes.deinit(gpa); + object.dg.fwd_decl.deinit(); + } + + codegen.genLazyFn(&object, lazy_fn) catch |err| switch (err) { + error.AnalysisFail => unreachable, + else => |e| return e, + }; + + fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); + ctypes.* = object.dg.ctypes.move(); + code.* = object.code.moveToUnmanaged(); + + try self.flushCTypes(f, ctypes.*); + try f.all_buffers.ensureUnusedCapacity(gpa, 2); + f.appendBufAssumeCapacity(fwd_decl.items); + f.appendBufAssumeCapacity(code.items); +} + +fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError!void { + const gpa = self.base.allocator; + try f.lazy_fns.ensureUnusedCapacity(gpa, @intCast(Flush.LazyFns.Size, lazy_fns.count())); + + var it = lazy_fns.iterator(); + while (it.next()) |entry| { + const gop = f.lazy_fns.getOrPutAssumeCapacity(entry.key_ptr.*); + if (gop.found_existing) continue; + gop.value_ptr.* = .{}; + try self.flushLazyFn(f, gop.value_ptr, entry); + } +} + /// Assumes `decl` was in the `remaining_decls` set, and has already been removed. fn flushDecl( self: *C, @@ -460,8 +468,8 @@ fn flushDecl( decl_index: Module.Decl.Index, export_names: std.StringHashMapUnmanaged(void), ) FlushDeclError!void { - const module = self.base.options.module.?; - const decl = module.declPtr(decl_index); + const gpa = self.base.allocator; + const decl = self.base.options.module.?.declPtr(decl_index); // Before flushing any particular Decl we must ensure its // dependencies are already flushed, so that the order in the .c // file comes out correctly. @@ -472,10 +480,10 @@ fn flushDecl( } const decl_block = self.decl_table.getPtr(decl_index).?; - const gpa = self.base.allocator; - try self.flushTypedefs(f, decl_block.typedefs); - try f.all_buffers.ensureUnusedCapacity(gpa, 2); + try self.flushCTypes(f, decl_block.ctypes); + try self.flushLazyFns(f, decl_block.lazy_fns); + try f.all_buffers.ensureUnusedCapacity(gpa, 1); if (!(decl.isExtern() and export_names.contains(mem.span(decl.name)))) f.appendBufAssumeCapacity(decl_block.fwd_decl.items); } From 064b355912dd85bd06ee87101066ed0db2783796 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 20 Feb 2023 20:50:19 -0500 Subject: [PATCH 091/122] CBE: use CType for type definitions --- src/Compilation.zig | 2 +- src/codegen/c.zig | 1221 ++++++++++++++++++++++++---------------- src/codegen/c/type.zig | 984 +++++++++++++++++++++++--------- src/link/C.zig | 187 ++++-- 4 files changed, 1575 insertions(+), 819 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 9359d24dc3..aea1876747 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3273,7 +3273,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { .gpa = gpa, .module = module, .error_msg = null, - .decl_index = decl_index, + .decl_index = decl_index.toOptional(), .decl = decl, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = .{}, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index cf4c7ec21b..35c826e2d1 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -31,6 +31,7 @@ pub const CType = @import("c/type.zig").CType; pub const CValue = union(enum) { none: void, + new_local: LocalIndex, local: LocalIndex, /// Address of a local. local_ref: LocalIndex, @@ -38,6 +39,8 @@ pub const CValue = union(enum) { constant: Air.Inst.Ref, /// Index into the parameters arg: usize, + /// The payload field of a parameter + arg_array: usize, /// Index into a tuple's fields field: usize, /// By-value @@ -298,7 +301,7 @@ pub const Function = struct { const alignment = 0; const decl_c_value = try f.allocLocalValue(ty, alignment); const gpa = f.object.dg.gpa; - try f.allocs.put(gpa, decl_c_value.local, true); + try f.allocs.put(gpa, decl_c_value.new_local, true); try writer.writeAll("static "); try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .@"const", alignment, .Complete); try writer.writeAll(" = "); @@ -330,12 +333,12 @@ pub const Function = struct { .alignment = alignment, .loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1), }); - return CValue{ .local = @intCast(LocalIndex, f.locals.items.len - 1) }; + return CValue{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; } fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { const result = try f.allocAlignedLocal(ty, .mut, 0); - log.debug("%{d}: allocating t{d}", .{ inst, result.local }); + log.debug("%{d}: allocating t{d}", .{ inst, result.new_local }); return result; } @@ -349,7 +352,7 @@ pub const Function = struct { if (local.alignment >= alignment) { local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); _ = locals_list.swapRemove(i); - return CValue{ .local = local_index }; + return CValue{ .new_local = local_index }; } } } @@ -488,8 +491,8 @@ pub const Object = struct { pub const DeclGen = struct { gpa: std.mem.Allocator, module: *Module, - decl: *Decl, - decl_index: Decl.Index, + decl: ?*Decl, + decl_index: Decl.OptionalIndex, fwd_decl: std.ArrayList(u8), error_msg: ?*Module.ErrorMsg, ctypes: CType.Store, @@ -497,7 +500,7 @@ pub const DeclGen = struct { fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { @setCold(true); const src = LazySrcLoc.nodeOffset(0); - const src_loc = src.toSrcLoc(dg.decl); + const src_loc = src.toSrcLoc(dg.decl.?); dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args); return error.AnalysisFail; } @@ -816,7 +819,7 @@ pub const DeclGen = struct { empty = false; } - if (empty) try writer.print("{x}", .{try dg.fmtIntLiteral(Type.u8, Value.undef)}); + return writer.writeByte('}'); }, .Packed => return writer.print("{x}", .{try dg.fmtIntLiteral(ty, Value.undef)}), @@ -1287,7 +1290,6 @@ pub const DeclGen = struct { empty = false; } - if (empty) try writer.print("{}", .{try dg.fmtIntLiteral(Type.u8, Value.zero)}); try writer.writeByte('}'); }, .Packed => { @@ -1304,7 +1306,7 @@ pub const DeclGen = struct { const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); var eff_num_fields: usize = 0; - for (field_vals, 0..) |_, index| { + for (0..field_vals.len) |index| { const field_ty = ty.structFieldType(index); if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -1408,6 +1410,7 @@ pub const DeclGen = struct { return; } + var has_payload_init = false; try writer.writeByte('{'); if (ty.unionTagTypeSafety()) |tag_ty| { const layout = ty.unionGetLayout(target); @@ -1416,7 +1419,10 @@ pub const DeclGen = struct { try dg.renderValue(writer, tag_ty, union_obj.tag, initializer_type); try writer.writeAll(", "); } - try writer.writeAll(".payload = {"); + if (!ty.unionHasAllZeroBitFieldTypes()) { + try writer.writeAll(".payload = {"); + has_payload_init = true; + } } var it = ty.unionFields().iterator(); @@ -1428,8 +1434,8 @@ pub const DeclGen = struct { try writer.print(".{ } = ", .{fmtIdent(field.key_ptr.*)}); try dg.renderValue(writer, field.value_ptr.ty, Value.undef, initializer_type); break; - } else try writer.writeAll(".empty_union = 0"); - if (ty.unionTagTypeSafety()) |_| try writer.writeByte('}'); + } + if (has_payload_init) try writer.writeByte('}'); try writer.writeByte('}'); }, @@ -1452,337 +1458,61 @@ pub const DeclGen = struct { } fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind, export_index: u32) !void { - const fn_info = dg.decl.ty.fnInfo(); + const store = &dg.ctypes.set; + const module = dg.module; + + const fn_ty = dg.decl.?.ty; + const fn_cty_idx = try dg.typeToIndex(fn_ty, switch (kind) { + .Forward => .forward, + .Complete => .complete, + }); + + const fn_info = fn_ty.fnInfo(); if (fn_info.cc == .Naked) { switch (kind) { .Forward => try w.writeAll("zig_naked_decl "), .Complete => try w.writeAll("zig_naked "), } } - if (dg.decl.val.castTag(.function)) |func_payload| + if (dg.decl.?.val.castTag(.function)) |func_payload| if (func_payload.data.is_cold) try w.writeAll("zig_cold "); - const target = dg.module.getTarget(); - var ret_buf: LowerFnRetTyBuffer = undefined; - const ret_ty = lowerFnRetTy(fn_info.return_type, &ret_buf, target); - - try dg.renderType(w, ret_ty, kind); - try w.writeByte(' '); + const trailing = try renderTypePrefix( + dg.decl_index, + store.*, + module, + w, + fn_cty_idx, + .suffix, + CQualifiers.init(.{}), + ); + try w.print("{}", .{trailing}); if (toCallingConvention(fn_info.cc)) |call_conv| { try w.print("zig_callconv({s}) ", .{call_conv}); } - if (fn_info.alignment > 0 and kind == .Complete) try w.print(" zig_align_fn({})", .{fn_info.alignment}); - - try dg.renderDeclName(w, dg.decl_index, export_index); - try w.writeByte('('); - - var index: usize = 0; - for (fn_info.param_types) |param_type| { - if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - if (index > 0) try w.writeAll(", "); - const name = CValue{ .arg = index }; - try dg.renderTypeAndName(w, param_type, name, .@"const", 0, kind); - index += 1; + if (fn_info.alignment > 0 and kind == .Complete) { + try w.print(" zig_align_fn({})", .{fn_info.alignment}); } - if (fn_info.is_var_args) { - if (index > 0) try w.writeAll(", "); - try w.writeAll("..."); - } else if (index == 0) { - try dg.renderType(w, Type.void, kind); + try dg.renderDeclName(w, dg.decl_index.unwrap().?, export_index); + + try renderTypeSuffix(dg.decl_index, store.*, module, w, fn_cty_idx, .suffix); + + if (fn_info.alignment > 0 and kind == .Forward) { + try w.print(" zig_align_fn({})", .{fn_info.alignment}); } - try w.writeByte(')'); - if (fn_info.alignment > 0 and kind == .Forward) try w.print(" zig_align_fn({})", .{fn_info.alignment}); } fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { return dg.ctypes.indexToCType(idx); } - fn typeToCType(dg: *DeclGen, ty: Type) !CType { - return dg.ctypes.typeToCType(dg.gpa, ty, dg.module); + fn typeToIndex(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType.Index { + return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module, kind); } - fn typeToIndex(dg: *DeclGen, ty: Type) !CType.Index { - return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module); - } - - const CTypeFix = enum { prefix, suffix }; - const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); - const CTypeRenderTrailing = enum { - no_space, - maybe_space, - - pub fn format( - self: @This(), - comptime fmt: []const u8, - _: std.fmt.FormatOptions, - w: anytype, - ) @TypeOf(w).Error!void { - if (fmt.len != 0) - @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ - @typeName(@This()) ++ "'"); - comptime assert(fmt.len == 0); - switch (self) { - .no_space => {}, - .maybe_space => try w.writeByte(' '), - } - } - }; - fn renderTypePrefix( - dg: *DeclGen, - w: anytype, - idx: CType.Index, - parent_fix: CTypeFix, - qualifiers: CQualifiers, - ) @TypeOf(w).Error!CTypeRenderTrailing { - var trailing = CTypeRenderTrailing.maybe_space; - - const cty = dg.indexToCType(idx); - switch (cty.tag()) { - .void, - .char, - .@"signed char", - .short, - .int, - .long, - .@"long long", - ._Bool, - .@"unsigned char", - .@"unsigned short", - .@"unsigned int", - .@"unsigned long", - .@"unsigned long long", - .float, - .double, - .@"long double", - .bool, - .size_t, - .ptrdiff_t, - .uint8_t, - .int8_t, - .uint16_t, - .int16_t, - .uint32_t, - .int32_t, - .uint64_t, - .int64_t, - .uintptr_t, - .intptr_t, - .zig_u128, - .zig_i128, - .zig_f16, - .zig_f32, - .zig_f64, - .zig_f80, - .zig_f128, - => |tag| try w.writeAll(@tagName(tag)), - - .pointer, - .pointer_const, - .pointer_volatile, - .pointer_const_volatile, - => |tag| { - const child_idx = cty.cast(CType.Payload.Child).?.data; - try w.print("{}*", .{try dg.renderTypePrefix(w, child_idx, .prefix, CQualifiers.init(.{ - .@"const" = switch (tag) { - .pointer, .pointer_volatile => false, - .pointer_const, .pointer_const_volatile => true, - else => unreachable, - }, - .@"volatile" = switch (tag) { - .pointer, .pointer_const => false, - .pointer_volatile, .pointer_const_volatile => true, - else => unreachable, - }, - }))}); - trailing = .no_space; - }, - - .array, - .vector, - => { - const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type; - const child_trailing = try dg.renderTypePrefix(w, child_idx, .suffix, qualifiers); - switch (parent_fix) { - .prefix => { - try w.print("{}(", .{child_trailing}); - return .no_space; - }, - .suffix => return child_trailing, - } - }, - - .fwd_struct, - .fwd_union, - .anon_struct, - .packed_anon_struct, - => |tag| try w.print("{s} {}__{d}", .{ - switch (tag) { - .fwd_struct, - .anon_struct, - .packed_anon_struct, - => "struct", - .fwd_union => "union", - else => unreachable, - }, - fmtIdent(switch (tag) { - .fwd_struct, - .fwd_union, - => mem.span(dg.module.declPtr(cty.cast(CType.Payload.FwdDecl).?.data).name), - .anon_struct, - .packed_anon_struct, - => "anon", - else => unreachable, - }), - idx, - }), - - .@"struct", - .packed_struct, - .@"union", - .packed_union, - => return dg.renderTypePrefix( - w, - cty.cast(CType.Payload.Aggregate).?.data.fwd_decl, - parent_fix, - qualifiers, - ), - - .function, - .varargs_function, - => { - const child_trailing = try dg.renderTypePrefix( - w, - cty.cast(CType.Payload.Function).?.data.return_type, - .suffix, - CQualifiers.initEmpty(), - ); - switch (parent_fix) { - .prefix => { - try w.print("{}(", .{child_trailing}); - return .no_space; - }, - .suffix => return child_trailing, - } - }, - } - - var qualifier_it = qualifiers.iterator(); - while (qualifier_it.next()) |qualifier| { - try w.print("{}{s}", .{ trailing, @tagName(qualifier) }); - trailing = .maybe_space; - } - - return trailing; - } - fn renderTypeSuffix( - dg: *DeclGen, - w: anytype, - idx: CType.Index, - parent_fix: CTypeFix, - ) @TypeOf(w).Error!void { - const cty = dg.indexToCType(idx); - switch (cty.tag()) { - .void, - .char, - .@"signed char", - .short, - .int, - .long, - .@"long long", - ._Bool, - .@"unsigned char", - .@"unsigned short", - .@"unsigned int", - .@"unsigned long", - .@"unsigned long long", - .float, - .double, - .@"long double", - .bool, - .size_t, - .ptrdiff_t, - .uint8_t, - .int8_t, - .uint16_t, - .int16_t, - .uint32_t, - .int32_t, - .uint64_t, - .int64_t, - .uintptr_t, - .intptr_t, - .zig_u128, - .zig_i128, - .zig_f16, - .zig_f32, - .zig_f64, - .zig_f80, - .zig_f128, - => {}, - - .pointer, - .pointer_const, - .pointer_volatile, - .pointer_const_volatile, - => try dg.renderTypeSuffix(w, cty.cast(CType.Payload.Child).?.data, .prefix), - - .array, - .vector, - => { - switch (parent_fix) { - .prefix => try w.writeByte(')'), - .suffix => {}, - } - - try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len}); - try dg.renderTypeSuffix(w, cty.cast(CType.Payload.Sequence).?.data.elem_type, .suffix); - }, - - .fwd_struct, - .fwd_union, - .anon_struct, - .packed_anon_struct, - .@"struct", - .@"union", - .packed_struct, - .packed_union, - => {}, - - .function, - .varargs_function, - => |tag| { - switch (parent_fix) { - .prefix => try w.writeByte(')'), - .suffix => {}, - } - - const data = cty.cast(CType.Payload.Function).?.data; - - try w.writeByte('('); - var need_comma = false; - for (data.param_types) |param_type| { - if (need_comma) try w.writeAll(", "); - need_comma = true; - _ = try dg.renderTypePrefix(w, param_type, .suffix, CQualifiers.initEmpty()); - try dg.renderTypeSuffix(w, param_type, .suffix); - } - switch (tag) { - .function => {}, - .varargs_function => { - if (need_comma) try w.writeAll(", "); - need_comma = true; - try w.writeAll("..."); - }, - else => unreachable, - } - if (!need_comma) try w.writeAll("void"); - try w.writeByte(')'); - - try dg.renderTypeSuffix(w, data.return_type, .suffix); - }, - } + fn typeToCType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType { + return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind); } /// Renders a type as a single identifier, generating intermediate typedefs @@ -1803,9 +1533,19 @@ pub const DeclGen = struct { t: Type, _: TypedefKind, ) error{ OutOfMemory, AnalysisFail }!void { - const idx = try dg.typeToIndex(t); - _ = try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.initEmpty()); - try dg.renderTypeSuffix(w, idx, .suffix); + const store = &dg.ctypes.set; + const module = dg.module; + const idx = try dg.typeToIndex(t, .complete); + _ = try renderTypePrefix( + dg.decl_index, + store.*, + module, + w, + idx, + .suffix, + CQualifiers.init(.{}), + ); + try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix); } const IntCastContext = union(enum) { @@ -1939,24 +1679,28 @@ pub const DeclGen = struct { alignment: u32, _: TypedefKind, ) error{ OutOfMemory, AnalysisFail }!void { - if (alignment != 0) { - const abi_alignment = ty.abiAlignment(dg.module.getTarget()); - if (alignment < abi_alignment) { - try w.print("zig_under_align({}) ", .{alignment}); - } else if (alignment > abi_alignment) { - try w.print("zig_align({}) ", .{alignment}); - } - } + const store = &dg.ctypes.set; + const module = dg.module; - const idx = try dg.typeToIndex(ty); - try w.print("{}", .{try dg.renderTypePrefix(w, idx, .suffix, CQualifiers.init(.{ - .@"const" = switch (mutability) { - .mut => false, - .@"const" => true, - }, - }))}); + if (alignment != 0) switch (std.math.order(alignment, ty.abiAlignment(dg.module.getTarget()))) { + .lt => try w.print("zig_under_align({}) ", .{alignment}), + .eq => {}, + .gt => try w.print("zig_align({}) ", .{alignment}), + }; + + const idx = try dg.typeToIndex(ty, .complete); + const trailing = try renderTypePrefix( + dg.decl_index, + store.*, + module, + w, + idx, + .suffix, + CQualifiers.init(.{ .@"const" = mutability == .@"const" }), + ); + try w.print("{}", .{trailing}); try dg.writeCValue(w, name); - try dg.renderTypeSuffix(w, idx, .suffix); + try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix); } fn renderTagNameFn(dg: *DeclGen, w: anytype, fn_name: []const u8, enum_ty: Type) !void { @@ -2029,10 +1773,11 @@ pub const DeclGen = struct { fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void { switch (c_value) { .none => unreachable, - .local => |i| return w.print("t{d}", .{i}), + .local, .new_local => |i| return w.print("t{d}", .{i}), .local_ref => |i| return w.print("&t{d}", .{i}), .constant => unreachable, .arg => |i| return w.print("a{d}", .{i}), + .arg_array => |i| return dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }), .field => |i| return w.print("f{d}", .{i}), .decl => |decl| return dg.renderDeclName(w, decl, 0), .decl_ref => |decl| { @@ -2048,10 +1793,15 @@ pub const DeclGen = struct { fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void { switch (c_value) { .none => unreachable, - .local => |i| return w.print("(*t{d})", .{i}), + .local, .new_local => |i| return w.print("(*t{d})", .{i}), .local_ref => |i| return w.print("t{d}", .{i}), .constant => unreachable, .arg => |i| return w.print("(*a{d})", .{i}), + .arg_array => |i| { + try w.writeAll("(*"); + try dg.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }); + return w.writeByte(')'); + }, .field => |i| return w.print("f{d}", .{i}), .decl => |decl| { try w.writeAll("(*"); @@ -2078,7 +1828,7 @@ pub const DeclGen = struct { fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { switch (c_value) { .none, .constant, .field, .undef => unreachable, - .local, .arg, .decl, .identifier, .bytes => { + .new_local, .local, .arg, .arg_array, .decl, .identifier, .bytes => { try dg.writeCValue(writer, c_value); try writer.writeAll("->"); }, @@ -2205,10 +1955,491 @@ pub const DeclGen = struct { } }; -pub fn genGlobalAsm(mod: *Module, code: *std.ArrayList(u8)) !void { +const CTypeFix = enum { prefix, suffix }; +const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); +const CTypeRenderTrailing = enum { + no_space, + maybe_space, + + pub fn format( + self: @This(), + comptime fmt: []const u8, + _: std.fmt.FormatOptions, + w: anytype, + ) @TypeOf(w).Error!void { + if (fmt.len != 0) + @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ + @typeName(@This()) ++ "'"); + comptime assert(fmt.len == 0); + switch (self) { + .no_space => {}, + .maybe_space => try w.writeByte(' '), + } + } +}; +fn renderTypeName( + mod: *Module, + w: anytype, + idx: CType.Index, + cty: CType, + attributes: []const u8, +) !void { + switch (cty.tag()) { + else => unreachable, + + .fwd_anon_struct, + .fwd_anon_union, + => |tag| try w.print("{s} {s}anon__lazy_{d}", .{ + @tagName(tag)["fwd_anon_".len..], + attributes, + idx, + }), + + .fwd_struct, + .fwd_union, + => |tag| { + const owner_decl = cty.cast(CType.Payload.FwdDecl).?.data; + try w.print("{s} {s}{}__{d}", .{ + @tagName(tag)["fwd_".len..], + attributes, + fmtIdent(mem.span(mod.declPtr(owner_decl).name)), + @enumToInt(owner_decl), + }); + }, + } +} +fn renderTypePrefix( + decl: Decl.OptionalIndex, + store: CType.Store.Set, + mod: *Module, + w: anytype, + idx: CType.Index, + parent_fix: CTypeFix, + qualifiers: CQualifiers, +) @TypeOf(w).Error!CTypeRenderTrailing { + var trailing = CTypeRenderTrailing.maybe_space; + + const cty = store.indexToCType(idx); + switch (cty.tag()) { + .void, + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + ._Bool, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + .bool, + .size_t, + .ptrdiff_t, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => |tag| try w.writeAll(@tagName(tag)), + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => |tag| { + const child_idx = cty.cast(CType.Payload.Child).?.data; + const child_trailing = try renderTypePrefix( + decl, + store, + mod, + w, + child_idx, + .prefix, + CQualifiers.init(.{ .@"const" = switch (tag) { + .pointer, .pointer_volatile => false, + .pointer_const, .pointer_const_volatile => true, + else => unreachable, + }, .@"volatile" = switch (tag) { + .pointer, .pointer_const => false, + .pointer_volatile, .pointer_const_volatile => true, + else => unreachable, + } }), + ); + try w.print("{}*", .{child_trailing}); + trailing = .no_space; + }, + + .array, + .vector, + => { + const child_idx = cty.cast(CType.Payload.Sequence).?.data.elem_type; + const child_trailing = try renderTypePrefix( + decl, + store, + mod, + w, + child_idx, + .suffix, + qualifiers, + ); + switch (parent_fix) { + .prefix => { + try w.print("{}(", .{child_trailing}); + return .no_space; + }, + .suffix => return child_trailing, + } + }, + + .fwd_anon_struct, + .fwd_anon_union, + => if (decl.unwrap()) |decl_index| + try w.print("anon__{d}_{d}", .{ @enumToInt(decl_index), idx }) + else + try renderTypeName(mod, w, idx, cty, ""), + + .fwd_struct, + .fwd_union, + => try renderTypeName(mod, w, idx, cty, ""), + + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => |tag| { + try w.print("{s} {s}", .{ + @tagName(tag)["unnamed_".len..], + if (cty.isPacked()) "zig_packed(" else "", + }); + try renderAggregateFields(mod, w, store, cty, 1); + if (cty.isPacked()) try w.writeByte(')'); + }, + + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => return renderTypePrefix( + decl, + store, + mod, + w, + cty.cast(CType.Payload.Aggregate).?.data.fwd_decl, + parent_fix, + qualifiers, + ), + + .function, + .varargs_function, + => { + const child_trailing = try renderTypePrefix( + decl, + store, + mod, + w, + cty.cast(CType.Payload.Function).?.data.return_type, + .suffix, + CQualifiers.init(.{}), + ); + switch (parent_fix) { + .prefix => { + try w.print("{}(", .{child_trailing}); + return .no_space; + }, + .suffix => return child_trailing, + } + }, + } + + var qualifier_it = qualifiers.iterator(); + while (qualifier_it.next()) |qualifier| { + try w.print("{}{s}", .{ trailing, @tagName(qualifier) }); + trailing = .maybe_space; + } + + return trailing; +} +fn renderTypeSuffix( + decl: Decl.OptionalIndex, + store: CType.Store.Set, + mod: *Module, + w: anytype, + idx: CType.Index, + parent_fix: CTypeFix, +) @TypeOf(w).Error!void { + const cty = store.indexToCType(idx); + switch (cty.tag()) { + .void, + .char, + .@"signed char", + .short, + .int, + .long, + .@"long long", + ._Bool, + .@"unsigned char", + .@"unsigned short", + .@"unsigned int", + .@"unsigned long", + .@"unsigned long long", + .float, + .double, + .@"long double", + .bool, + .size_t, + .ptrdiff_t, + .uint8_t, + .int8_t, + .uint16_t, + .int16_t, + .uint32_t, + .int32_t, + .uint64_t, + .int64_t, + .uintptr_t, + .intptr_t, + .zig_u128, + .zig_i128, + .zig_f16, + .zig_f32, + .zig_f64, + .zig_f80, + .zig_f128, + => {}, + + .pointer, + .pointer_const, + .pointer_volatile, + .pointer_const_volatile, + => try renderTypeSuffix(decl, store, mod, w, cty.cast(CType.Payload.Child).?.data, .prefix), + + .array, + .vector, + => { + switch (parent_fix) { + .prefix => try w.writeByte(')'), + .suffix => {}, + } + + try w.print("[{}]", .{cty.cast(CType.Payload.Sequence).?.data.len}); + try renderTypeSuffix( + decl, + store, + mod, + w, + cty.cast(CType.Payload.Sequence).?.data.elem_type, + .suffix, + ); + }, + + .fwd_anon_struct, + .fwd_anon_union, + .fwd_struct, + .fwd_union, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => {}, + + .function, + .varargs_function, + => |tag| { + switch (parent_fix) { + .prefix => try w.writeByte(')'), + .suffix => {}, + } + + const data = cty.cast(CType.Payload.Function).?.data; + + try w.writeByte('('); + var need_comma = false; + for (data.param_types, 0..) |param_type, param_i| { + if (need_comma) try w.writeAll(", "); + need_comma = true; + const trailing = try renderTypePrefix( + decl, + store, + mod, + w, + param_type, + .suffix, + CQualifiers.init(.{}), + ); + try w.print("{}a{d}", .{ trailing, param_i }); + try renderTypeSuffix(decl, store, mod, w, param_type, .suffix); + } + switch (tag) { + .function => {}, + .varargs_function => { + if (need_comma) try w.writeAll(", "); + need_comma = true; + try w.writeAll("..."); + }, + else => unreachable, + } + if (!need_comma) try w.writeAll("void"); + try w.writeByte(')'); + + try renderTypeSuffix(decl, store, mod, w, data.return_type, .suffix); + }, + } +} +fn renderAggregateFields( + mod: *Module, + writer: anytype, + store: CType.Store.Set, + cty: CType, + indent: usize, +) !void { + try writer.writeAll("{\n"); + const fields = cty.fields(); + for (fields) |field| { + try writer.writeByteNTimes(' ', indent + 1); + switch (std.math.order(field.alignas.@"align", field.alignas.abi)) { + .lt => try writer.print("zig_under_align({}) ", .{field.alignas.getAlign()}), + .eq => {}, + .gt => try writer.print("zig_align({}) ", .{field.alignas.getAlign()}), + } + const trailing = try renderTypePrefix( + .none, + store, + mod, + writer, + field.type, + .suffix, + CQualifiers.init(.{}), + ); + try writer.print("{}{ }", .{ trailing, fmtIdent(mem.span(field.name)) }); + try renderTypeSuffix(.none, store, mod, writer, field.type, .suffix); + try writer.writeAll(";\n"); + } + try writer.writeByteNTimes(' ', indent); + try writer.writeByte('}'); +} + +pub fn genTypeDecl( + mod: *Module, + writer: anytype, + global_store: CType.Store.Set, + global_idx: CType.Index, + decl: Decl.OptionalIndex, + decl_store: CType.Store.Set, + decl_idx: CType.Index, + found_existing: bool, +) !void { + const global_cty = global_store.indexToCType(global_idx); + switch (global_cty.tag()) { + .fwd_anon_struct => if (decl != .none) { + try writer.writeAll("typedef "); + _ = try renderTypePrefix( + .none, + global_store, + mod, + writer, + global_idx, + .suffix, + CQualifiers.init(.{}), + ); + try writer.writeByte(' '); + _ = try renderTypePrefix( + decl, + decl_store, + mod, + writer, + decl_idx, + .suffix, + CQualifiers.init(.{}), + ); + try writer.writeAll(";\n"); + }, + + .fwd_struct, + .fwd_union, + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => |tag| if (!found_existing) { + switch (tag) { + .fwd_struct, + .fwd_union, + => { + const owner_decl = global_cty.cast(CType.Payload.FwdDecl).?.data; + _ = try renderTypePrefix( + .none, + global_store, + mod, + writer, + global_idx, + .suffix, + CQualifiers.init(.{}), + ); + try writer.writeAll("; // "); + try mod.declPtr(owner_decl).renderFullyQualifiedName(mod, writer); + try writer.writeByte('\n'); + }, + + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => { + const fwd_idx = global_cty.cast(CType.Payload.Aggregate).?.data.fwd_decl; + try renderTypeName( + mod, + writer, + fwd_idx, + global_store.indexToCType(fwd_idx), + if (global_cty.isPacked()) "zig_packed(" else "", + ); + try writer.writeByte(' '); + try renderAggregateFields(mod, writer, global_store, global_cty, 0); + if (global_cty.isPacked()) try writer.writeByte(')'); + try writer.writeAll(";\n"); + }, + + else => unreachable, + } + }, + + else => {}, + } +} + +pub fn genGlobalAsm(mod: *Module, writer: anytype) !void { var it = mod.global_assembly.valueIterator(); while (it.next()) |asm_source| { - try code.writer().print("__asm({s});\n", .{fmtStringLiteral(asm_source.*)}); + try writer.print("__asm({s});\n", .{fmtStringLiteral(asm_source.*)}); } } @@ -2279,14 +2510,16 @@ fn genExports(o: *Object) !void { defer tracy.end(); const fwd_decl_writer = o.dg.fwd_decl.writer(); - if (o.dg.module.decl_exports.get(o.dg.decl_index)) |exports| for (exports.items[1..], 0..) |@"export", i| { - try fwd_decl_writer.writeAll("zig_export("); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, @intCast(u32, 1 + i)); - try fwd_decl_writer.print(", {s}, {s});\n", .{ - fmtStringLiteral(exports.items[0].options.name), - fmtStringLiteral(@"export".options.name), - }); - }; + if (o.dg.module.decl_exports.get(o.dg.decl_index.unwrap().?)) |exports| { + for (exports.items[1..], 1..) |@"export", i| { + try fwd_decl_writer.writeAll("zig_export("); + try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, @intCast(u32, i)); + try fwd_decl_writer.print(", {s}, {s});\n", .{ + fmtStringLiteral(exports.items[0].options.name), + fmtStringLiteral(@"export".options.name), + }); + } + } } pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { @@ -2307,8 +2540,8 @@ pub fn genFunc(f: *Function) !void { const o = &f.object; const gpa = o.dg.gpa; const tv: TypedValue = .{ - .ty = o.dg.decl.ty, - .val = o.dg.decl.val, + .ty = o.dg.decl.?.ty, + .val = o.dg.decl.?.val, }; o.code_header = std.ArrayList(u8).init(gpa); @@ -2347,9 +2580,8 @@ pub fn genFunc(f: *Function) !void { // missing. These are added now to complete the map. Then we can sort by // alignment, descending. const free_locals = f.getFreeLocals(); - const values = f.allocs.values(); - for (f.allocs.keys(), 0..) |local_index, i| { - if (values[i]) continue; // static + for (f.allocs.keys(), f.allocs.values()) |local_index, value| { + if (value) continue; // static const local = f.locals.items[local_index]; log.debug("inserting local {d} into free_locals", .{local_index}); const gop = try free_locals.getOrPutContext(gpa, local.ty, f.tyHashCtx()); @@ -2398,10 +2630,10 @@ pub fn genDecl(o: *Object) !void { const tracy = trace(@src()); defer tracy.end(); - const tv: TypedValue = .{ - .ty = o.dg.decl.ty, - .val = o.dg.decl.val, - }; + const decl = o.dg.decl.?; + const decl_c_value: CValue = .{ .decl = o.dg.decl_index.unwrap().? }; + const tv: TypedValue = .{ .ty = decl.ty, .val = decl.val }; + if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime()) return; if (tv.val.tag() == .extern_fn) { const fwd_decl_writer = o.dg.fwd_decl.writer(); @@ -2415,11 +2647,9 @@ pub fn genDecl(o: *Object) !void { const is_global = o.dg.declIsGlobal(tv) or variable.is_extern; const fwd_decl_writer = o.dg.fwd_decl.writer(); - const decl_c_value = CValue{ .decl = o.dg.decl_index }; - try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal "); - try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .mut, o.dg.decl.@"align", .Complete); + try o.dg.renderTypeAndName(fwd_decl_writer, decl.ty, decl_c_value, .mut, decl.@"align", .Complete); try fwd_decl_writer.writeAll(";\n"); try genExports(o); @@ -2428,27 +2658,26 @@ pub fn genDecl(o: *Object) !void { const w = o.writer(); if (!is_global) try w.writeAll("static "); if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); - if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); - try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .mut, o.dg.decl.@"align", .Complete); - if (o.dg.decl.@"linksection" != null) try w.writeAll(", read, write)"); + if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); + try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .mut, decl.@"align", .Complete); + if (decl.@"linksection" != null) try w.writeAll(", read, write)"); try w.writeAll(" = "); try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); try w.writeByte(';'); try o.indent_writer.insertNewline(); } else { - const is_global = o.dg.module.decl_exports.contains(o.dg.decl_index); + const is_global = o.dg.module.decl_exports.contains(decl_c_value.decl); const fwd_decl_writer = o.dg.fwd_decl.writer(); - const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); - try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .@"const", o.dg.decl.@"align", .Complete); + try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .@"const", decl.@"align", .Complete); try fwd_decl_writer.writeAll(";\n"); const w = o.writer(); if (!is_global) try w.writeAll("static "); - if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); - try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .@"const", o.dg.decl.@"align", .Complete); - if (o.dg.decl.@"linksection" != null) try w.writeAll(", read)"); + if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); + try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .@"const", decl.@"align", .Complete); + if (decl.@"linksection" != null) try w.writeAll(", read)"); try w.writeAll(" = "); try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); try w.writeAll(";\n"); @@ -2460,8 +2689,8 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { defer tracy.end(); const tv: TypedValue = .{ - .ty = dg.decl.ty, - .val = dg.decl.val, + .ty = dg.decl.?.ty, + .val = dg.decl.?.val, }; const writer = dg.fwd_decl.writer(); @@ -2499,7 +2728,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, // zig fmt: off .constant => unreachable, // excluded from function bodies .const_ty => unreachable, // excluded from function bodies - .arg => airArg(f), + .arg => try airArg(f, inst), .breakpoint => try airBreakpoint(f.object.writer()), .ret_addr => try airRetAddr(f, inst), @@ -2748,13 +2977,14 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .c_va_start => return f.fail("TODO implement c_va_start", .{}), // zig fmt: on }; - if (result_value == .local) { - log.debug("map %{d} to t{d}", .{ inst, result_value.local }); - } - switch (result_value) { - .none => {}, - else => try f.value_map.putNoClobber(Air.indexToRef(inst), result_value), + if (result_value == .new_local) { + log.debug("map %{d} to t{d}", .{ inst, result_value.new_local }); } + try f.value_map.putNoClobber(Air.indexToRef(inst), switch (result_value) { + .none => continue, + .new_local => |i| .{ .local = i }, + else => result_value, + }); } } @@ -2979,10 +3209,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut; const target = f.object.dg.module.getTarget(); const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target)); - log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local }); + log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; - try f.allocs.put(gpa, local.local, false); - return CValue{ .local_ref = local.local }; + try f.allocs.put(gpa, local.new_local, false); + return CValue{ .local_ref = local.new_local }; } fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -2996,16 +3226,22 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut; const target = f.object.dg.module.getTarget(); const local = try f.allocAlignedLocal(elem_ty, mutability, inst_ty.ptrAlignment(target)); - log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.local }); + log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; - try f.allocs.put(gpa, local.local, false); - return CValue{ .local_ref = local.local }; + try f.allocs.put(gpa, local.new_local, false); + return CValue{ .local_ref = local.new_local }; } -fn airArg(f: *Function) CValue { +fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { + const inst_ty = f.air.typeOfIndex(inst); + const inst_cty = try f.object.dg.typeToIndex(inst_ty, .parameter); + const i = f.next_arg_index; f.next_arg_index += 1; - return .{ .arg = i }; + return if (inst_cty != try f.object.dg.typeToIndex(inst_ty, .complete)) + .{ .arg_array = i } + else + .{ .arg = i }; } fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3115,7 +3351,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { const ret_val = if (is_array) ret_val: { const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator())); try writer.writeAll("memcpy("); - try f.writeCValueMember(writer, array_local, .{ .field = 0 }); + try f.writeCValueMember(writer, array_local, .{ .identifier = "array" }); try writer.writeAll(", "); if (deref) try f.writeCValueDeref(writer, operand) @@ -3135,14 +3371,13 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { try f.writeCValue(writer, ret_val, .Other); try writer.writeAll(";\n"); if (is_array) { - try freeLocal(f, inst, ret_val.local, 0); + try freeLocal(f, inst, ret_val.new_local, 0); } } else { try reap(f, inst, &.{un_op}); - if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) { + if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() != .Naked) // Not even allowed to return void in a naked function. try writer.writeAll("return;\n"); - } } return CValue.none; } @@ -3344,7 +3579,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try f.renderTypecast(writer, src_ty); try writer.writeAll("))"); if (src_val == .constant) { - try freeLocal(f, inst, array_src.local, 0); + try freeLocal(f, inst, array_src.new_local, 0); } } else if (ptr_info.host_size != 0) { const host_bits = ptr_info.host_size * 8; @@ -3770,8 +4005,12 @@ fn airCall( modifier: std.builtin.CallModifier, ) !CValue { // Not even allowed to call panic in a naked function. - if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none; + if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none; + const gpa = f.object.dg.gpa; + const module = f.object.dg.module; + const target = module.getTarget(); + const writer = f.object.writer(); switch (modifier) { .auto => {}, @@ -3786,8 +4025,28 @@ fn airCall( const resolved_args = try gpa.alloc(CValue, args.len); defer gpa.free(resolved_args); - for (args, 0..) |arg, i| { - resolved_args[i] = try f.resolveInst(arg); + for (resolved_args, args) |*resolved_arg, arg| { + const arg_ty = f.air.typeOf(arg); + const arg_cty = try f.object.dg.typeToIndex(arg_ty, .parameter); + if (f.object.dg.indexToCType(arg_cty).tag() == .void) { + resolved_arg.* = .none; + continue; + } + resolved_arg.* = try f.resolveInst(arg); + if (arg_cty != try f.object.dg.typeToIndex(arg_ty, .complete)) { + var lowered_arg_buf: LowerFnRetTyBuffer = undefined; + const lowered_arg_ty = lowerFnRetTy(arg_ty, &lowered_arg_buf, target); + + const array_local = try f.allocLocal(inst, try lowered_arg_ty.copy(f.arena.allocator())); + try writer.writeAll("memcpy("); + try f.writeCValueMember(writer, array_local, .{ .identifier = "array" }); + try writer.writeAll(", "); + try f.writeCValue(writer, resolved_arg.*, .FunctionArgument); + try writer.writeAll(", sizeof("); + try f.renderTypecast(writer, lowered_arg_ty); + try writer.writeAll("));\n"); + resolved_arg.* = array_local; + } } const callee = try f.resolveInst(pl_op.operand); @@ -3804,9 +4063,7 @@ fn airCall( .Pointer => callee_ty.childType(), else => unreachable, }; - const writer = f.object.writer(); - const target = f.object.dg.module.getTarget(); const ret_ty = fn_ty.fnReturnType(); var lowered_ret_buf: LowerFnRetTyBuffer = undefined; const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); @@ -3841,7 +4098,7 @@ fn airCall( else => break :known, }; }; - name = f.object.dg.module.declPtr(fn_decl).name; + name = module.declPtr(fn_decl).name; try f.object.dg.renderDeclName(writer, fn_decl, 0); break :callee; } @@ -3851,22 +4108,11 @@ fn airCall( try writer.writeByte('('); var args_written: usize = 0; - for (args, 0..) |arg, arg_i| { - const ty = f.air.typeOf(arg); - if (!ty.hasRuntimeBitsIgnoreComptime()) continue; - if (args_written != 0) { - try writer.writeAll(", "); - } - if ((is_extern or std.mem.eql(u8, std.mem.span(name), "main")) and - ty.isCPtr() and ty.childType().tag() == .u8) - { - // Corresponds with hack in renderType .Pointer case. - try writer.writeAll("(char"); - if (ty.isConstPtr()) try writer.writeAll(" const"); - if (ty.isVolatilePtr()) try writer.writeAll(" volatile"); - try writer.writeAll(" *)"); - } - try f.writeCValue(writer, resolved_args[arg_i], .FunctionArgument); + for (resolved_args) |resolved_arg| { + if (resolved_arg == .none) continue; + if (args_written != 0) try writer.writeAll(", "); + try f.writeCValue(writer, resolved_arg, .FunctionArgument); + if (resolved_arg == .new_local) try freeLocal(f, inst, resolved_arg.new_local, 0); args_written += 1; } try writer.writeAll(");\n"); @@ -3879,11 +4125,11 @@ fn airCall( try writer.writeAll("memcpy("); try f.writeCValue(writer, array_local, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValueMember(writer, result_local, .{ .field = 0 }); + try f.writeCValueMember(writer, result_local, .{ .identifier = "array" }); try writer.writeAll(", sizeof("); try f.renderTypecast(writer, ret_ty); try writer.writeAll("));\n"); - try freeLocal(f, inst, result_local.local, 0); + try freeLocal(f, inst, result_local.new_local, 0); break :r array_local; }; @@ -4147,7 +4393,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { } if (operand == .constant) { - try freeLocal(f, inst, operand_lval.local, 0); + try freeLocal(f, inst, operand_lval.new_local, 0); } return local; @@ -4193,7 +4439,7 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { fn airUnreach(f: *Function) !CValue { // Not even allowed to call unreachable in a naked function. - if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none; + if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none; try f.object.writer().writeAll("zig_unreachable();\n"); return CValue.none; @@ -4667,7 +4913,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_reg = constraint[1] == '{'; if (is_reg) { try f.writeCValueDeref(writer, if (output == .none) - CValue{ .local_ref = local.local } + CValue{ .local_ref = local.new_local } else try f.resolveInst(output)); try writer.writeAll(" = "); @@ -4967,18 +5213,20 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc else => .none, }; - const FieldLoc = union(enum) { + const field_loc: union(enum) { begin: void, field: CValue, end: void, - }; - const field_loc = switch (struct_ty.tag()) { - .@"struct" => switch (struct_ty.containerLayout()) { - .Auto, .Extern => for (struct_ty.structFields().values()[index..], 0..) |field, offset| { - if (field.ty.hasRuntimeBitsIgnoreComptime()) break FieldLoc{ .field = .{ - .identifier = struct_ty.structFieldName(index + offset), - } }; - } else @as(FieldLoc, .end), + } = switch (struct_ty.tag()) { + .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) { + .Auto, .Extern => for (index..struct_ty.structFieldCount()) |field_i| { + if (!struct_ty.structFieldIsComptime(field_i) and + struct_ty.structFieldType(field_i).hasRuntimeBitsIgnoreComptime()) + break .{ .field = if (struct_ty.isSimpleTuple()) + .{ .field = field_i } + else + .{ .identifier = struct_ty.structFieldName(field_i) } }; + } else .end, .Packed => if (field_ptr_info.data.host_size == 0) { const target = f.object.dg.module.getTarget(); @@ -5003,27 +5251,15 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc try f.writeCValue(writer, struct_ptr, .Other); try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); return local; - } else @as(FieldLoc, .begin), + } else .begin, }, .@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout() == .Packed) { try f.writeCValue(writer, struct_ptr, .Other); try writer.writeAll(";\n"); return local; - } else if (field_ty.hasRuntimeBitsIgnoreComptime()) FieldLoc{ .field = .{ + } else if (field_ty.hasRuntimeBitsIgnoreComptime()) .{ .field = .{ .identifier = struct_ty.unionFields().keys()[index], - } } else @as(FieldLoc, .end), - .tuple, .anon_struct => field_name: { - const tuple = struct_ty.tupleFields(); - if (tuple.values[index].tag() != .unreachable_value) return CValue.none; - - var id: usize = 0; - break :field_name for (tuple.values, 0..) |value, i| { - if (value.tag() != .unreachable_value) continue; - if (!tuple.types[i].hasRuntimeBitsIgnoreComptime()) continue; - if (i >= index) break FieldLoc{ .field = .{ .field = id } }; - id += 1; - } else @as(FieldLoc, .end); - }, + } } else .end, else => unreachable, }; @@ -5076,8 +5312,11 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { }; const field_name: CValue = switch (struct_ty.tag()) { - .@"struct" => switch (struct_ty.containerLayout()) { - .Auto, .Extern => .{ .identifier = struct_ty.structFieldName(extra.field_index) }, + .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) { + .Auto, .Extern => if (struct_ty.isSimpleTuple()) + .{ .field = extra.field_index } + else + .{ .identifier = struct_ty.structFieldName(extra.field_index) }, .Packed => { const struct_obj = struct_ty.castTag(.@"struct").?.data; const int_info = struct_ty.intInfo(target); @@ -5135,13 +5374,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst, inst_ty); try writer.writeAll("memcpy("); - try f.writeCValue(writer, .{ .local_ref = local.local }, .FunctionArgument); + try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, .{ .local_ref = temp_local.local }, .FunctionArgument); + try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); try writer.writeAll(", sizeof("); try f.renderTypecast(writer, inst_ty); try writer.writeAll("));\n"); - try freeLocal(f, inst, temp_local.local, 0); + try freeLocal(f, inst, temp_local.new_local, 0); return local; }, }, @@ -5165,22 +5404,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("));\n"); if (struct_byval == .constant) { - try freeLocal(f, inst, operand_lval.local, 0); + try freeLocal(f, inst, operand_lval.new_local, 0); } return local; } else .{ .identifier = struct_ty.unionFields().keys()[extra.field_index], }, - .tuple, .anon_struct => blk: { - const tuple = struct_ty.tupleFields(); - if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none; - - var id: usize = 0; - for (tuple.values[0..extra.field_index]) |value| - id += @boolToInt(value.tag() == .unreachable_value); - break :blk .{ .field = id }; - }, else => unreachable, }; @@ -5765,7 +5995,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue } if (f.liveness.isUnused(inst)) { - try freeLocal(f, inst, local.local, 0); + try freeLocal(f, inst, local.new_local, 0); return CValue.none; } @@ -5808,7 +6038,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(");\n"); if (f.liveness.isUnused(inst)) { - try freeLocal(f, inst, local.local, 0); + try freeLocal(f, inst, local.new_local, 0); return CValue.none; } @@ -5905,7 +6135,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); - try freeLocal(f, inst, index.local, 0); + try freeLocal(f, inst, index.new_local, 0); return CValue.none; } @@ -6222,7 +6452,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); - try freeLocal(f, inst, it.local, 0); + try freeLocal(f, inst, it.new_local, 0); return accum; } @@ -6235,8 +6465,8 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const gpa = f.object.dg.gpa; const resolved_elements = try gpa.alloc(CValue, elements.len); defer gpa.free(resolved_elements); - for (elements, 0..) |element, i| { - resolved_elements[i] = try f.resolveInst(element); + for (resolved_elements, elements) |*resolved_element, element| { + resolved_element.* = try f.resolveInst(element); } { var bt = iterateBigTomb(f, inst); @@ -6275,46 +6505,47 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(")"); try writer.writeByte('{'); var empty = true; - for (elements, 0..) |element, index| { - if (inst_ty.structFieldValueComptime(index)) |_| continue; + for (elements, resolved_elements, 0..) |element, resolved_element, field_i| { + if (inst_ty.structFieldValueComptime(field_i)) |_| continue; if (!empty) try writer.writeAll(", "); - if (!inst_ty.isTupleOrAnonStruct()) { - try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))}); - } + + const field_name: CValue = if (inst_ty.isSimpleTuple()) + .{ .field = field_i } + else + .{ .identifier = inst_ty.structFieldName(field_i) }; + try writer.writeByte('.'); + try f.object.dg.writeCValue(writer, field_name); + try writer.writeAll(" = "); const element_ty = f.air.typeOf(element); try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { .Array => CValue{ .undef = element_ty }, - else => resolved_elements[index], + else => resolved_element, }, .Initializer); empty = false; } - if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)}); try writer.writeAll("};\n"); - var field_id: usize = 0; - for (elements, 0..) |element, index| { - if (inst_ty.structFieldValueComptime(index)) |_| continue; + for (elements, resolved_elements, 0..) |element, resolved_element, field_i| { + if (inst_ty.structFieldValueComptime(field_i)) |_| continue; const element_ty = f.air.typeOf(element); if (element_ty.zigTypeTag() != .Array) continue; - const field_name = if (inst_ty.isTupleOrAnonStruct()) - CValue{ .field = field_id } + const field_name: CValue = if (inst_ty.isSimpleTuple()) + .{ .field = field_i } else - CValue{ .identifier = inst_ty.structFieldName(index) }; + .{ .identifier = inst_ty.structFieldName(field_i) }; try writer.writeAll(";\n"); try writer.writeAll("memcpy("); try f.writeCValueMember(writer, local, field_name); try writer.writeAll(", "); - try f.writeCValue(writer, resolved_elements[index], .FunctionArgument); + try f.writeCValue(writer, resolved_element, .FunctionArgument); try writer.writeAll(", sizeof("); try f.renderTypecast(writer, element_ty); try writer.writeAll("));\n"); - - field_id += 1; } }, .Packed => { @@ -6332,7 +6563,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const bit_offset_val = Value.initPayload(&bit_offset_val_pl.base); var empty = true; - for (elements, 0..) |_, index| { + for (0..elements.len) |index| { const field_ty = inst_ty.structFieldType(index); if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -6381,13 +6612,6 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { empty = false; } - if (empty) { - try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); - try writer.writeByte(')'); - try f.writeCValue(writer, .{ .undef = inst_ty }, .Initializer); - } - try writer.writeAll(";\n"); }, }, @@ -7020,17 +7244,20 @@ fn isByRef(ty: Type) bool { } const LowerFnRetTyBuffer = struct { + names: [1][]const u8, types: [1]Type, values: [1]Value, - payload: Type.Payload.Tuple, + payload: Type.Payload.AnonStruct, }; fn lowerFnRetTy(ret_ty: Type, buffer: *LowerFnRetTyBuffer, target: std.Target) Type { if (ret_ty.zigTypeTag() == .NoReturn) return Type.initTag(.noreturn); if (lowersToArray(ret_ty, target)) { + buffer.names = [1][]const u8{"array"}; buffer.types = [1]Type{ret_ty}; buffer.values = [1]Value{Value.initTag(.unreachable_value)}; buffer.payload = .{ .data = .{ + .names = &buffer.names, .types = &buffer.types, .values = &buffer.values, } }; @@ -7086,7 +7313,7 @@ fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void { if (f.air.instructions.items(.tag)[ref_inst] == .constant) return; const c_value = (f.value_map.fetchRemove(ref) orelse return).value; const local_index = switch (c_value) { - .local => |l| l, + .local, .new_local => |l| l, else => return, }; try freeLocal(f, inst, local_index, ref_inst); @@ -7161,8 +7388,8 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { } fn noticeBranchFrees(f: *Function, pre_locals_len: LocalIndex, inst: Air.Inst.Index) !void { - for (f.locals.items[pre_locals_len..], 0..) |*local, local_offset| { - const local_index = pre_locals_len + @intCast(LocalIndex, local_offset); + for (f.locals.items[pre_locals_len..], pre_locals_len..) |*local, local_i| { + const local_index = @intCast(LocalIndex, local_i); if (f.allocs.contains(local_index)) continue; // allocs are not freeable // free more deeply nested locals from other branches at current depth diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index ad482024b7..d6424b1f27 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -110,10 +110,16 @@ pub const CType = extern union { pointer_const_volatile, array, vector, + fwd_anon_struct, + fwd_anon_union, fwd_struct, fwd_union, + unnamed_struct, + unnamed_union, + packed_unnamed_struct, + packed_unnamed_union, anon_struct, - packed_anon_struct, + anon_union, @"struct", @"union", packed_struct, @@ -183,14 +189,22 @@ pub const CType = extern union { .vector, => Payload.Sequence, + .fwd_anon_struct, + .fwd_anon_union, + => Payload.Fields, + .fwd_struct, .fwd_union, => Payload.FwdDecl, - .anon_struct, - .packed_anon_struct, - => Payload.Fields, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => Payload.Unnamed, + .anon_struct, + .anon_union, .@"struct", .@"union", .packed_struct, @@ -229,12 +243,53 @@ pub const CType = extern union { base: Payload, data: Data, - const Data = []const Field; - const Field = struct { + pub const Data = []const Field; + pub const Field = struct { name: [*:0]const u8, type: Index, - alignas: u32, + alignas: AlignAs, }; + pub const AlignAs = struct { + @"align": std.math.Log2Int(u32), + abi: std.math.Log2Int(u32), + + pub fn init(alignment: u32, abi_alignment: u32) AlignAs { + assert(std.math.isPowerOfTwo(alignment)); + assert(std.math.isPowerOfTwo(abi_alignment)); + return .{ + .@"align" = std.math.log2_int(u32, alignment), + .abi = std.math.log2_int(u32, abi_alignment), + }; + } + pub fn abiAlign(ty: Type, target: Target) AlignAs { + const abi_align = ty.abiAlignment(target); + return init(abi_align, abi_align); + } + pub fn fieldAlign(struct_ty: Type, field_i: usize, target: Target) AlignAs { + return init( + struct_ty.structFieldAlign(field_i, target), + struct_ty.structFieldType(field_i).abiAlignment(target), + ); + } + pub fn unionPayloadAlign(union_ty: Type, target: Target) AlignAs { + const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_payload_align = union_obj.abiAlignment(target, false); + return init(union_payload_align, union_payload_align); + } + + pub fn getAlign(self: AlignAs) u32 { + return @as(u32, 1) << self.@"align"; + } + }; + }; + + pub const Unnamed = struct { + base: Payload, + data: struct { + fields: Fields.Data, + owner_decl: Module.Decl.Index, + id: u32, + }, }; pub const Aggregate = struct { @@ -259,22 +314,23 @@ pub const CType = extern union { arena: std.heap.ArenaAllocator.State = .{}, set: Set = .{}, - const Set = struct { - const Map = std.ArrayHashMapUnmanaged(CType, void, HashContext32, true); + pub const Set = struct { + pub const Map = std.ArrayHashMapUnmanaged(CType, void, HashContext32, true); map: Map = .{}, - fn indexToCType(self: Set, index: Index) CType { + pub fn indexToCType(self: Set, index: Index) CType { if (index < Tag.no_payload_count) return initTag(@intToEnum(Tag, index)); return self.map.keys()[index - Tag.no_payload_count]; } - fn indexToHash(self: Set, index: Index) Map.Hash { - if (index < Tag.no_payload_count) return self.indexToCType(index).hash(self); + pub fn indexToHash(self: Set, index: Index) Map.Hash { + if (index < Tag.no_payload_count) + return (HashContext32{ .store = &self }).hash(self.indexToCType(index)); return self.map.entries.items(.hash)[index - Tag.no_payload_count]; } - fn typeToIndex(self: Set, ty: Type, target: Target, kind: Kind) ?Index { + pub fn typeToIndex(self: Set, ty: Type, target: Target, kind: Kind) ?Index { const lookup = Convert.Lookup{ .imm = .{ .set = &self, .target = target } }; var convert: Convert = undefined; @@ -298,21 +354,27 @@ pub const CType = extern union { return self.arena.child_allocator; } - fn cTypeToIndex(self: *Promoted, cty: CType) Allocator.Error!Index { + pub fn cTypeToIndex(self: *Promoted, cty: CType) Allocator.Error!Index { const t = cty.tag(); if (@enumToInt(t) < Tag.no_payload_count) return @intCast(Index, @enumToInt(t)); const gop = try self.set.map.getOrPutContext(self.gpa(), cty, .{ .store = &self.set }); if (!gop.found_existing) gop.key_ptr.* = cty; if (std.debug.runtime_safety) { - const key = self.set.map.entries.items(.key)[gop.index]; - assert(key.eql(cty)); + const key = &self.set.map.entries.items(.key)[gop.index]; + assert(key == gop.key_ptr); + assert(cty.eql(key.*)); assert(cty.hash(self.set) == key.hash(self.set)); } return @intCast(Index, Tag.no_payload_count + gop.index); } - fn typeToIndex(self: *Promoted, ty: Type, mod: *Module, kind: Kind) Allocator.Error!Index { + pub fn typeToIndex( + self: *Promoted, + ty: Type, + mod: *Module, + kind: Kind, + ) Allocator.Error!Index { const lookup = Convert.Lookup{ .mut = .{ .promoted = self, .mod = mod } }; var convert: Convert = undefined; @@ -337,9 +399,10 @@ pub const CType = extern union { .lookup = lookup.freeze(), .convert = &convert, }; - const key = self.set.map.entries.items(.key)[gop.index]; - assert(adapter.eql(ty, key)); - assert(adapter.hash(ty) == key.hash(self.set)); + const cty = &self.set.map.entries.items(.key)[gop.index]; + assert(cty == gop.key_ptr); + assert(adapter.eql(ty, cty.*)); + assert(adapter.hash(ty) == cty.hash(self.set)); } return @intCast(Index, Tag.no_payload_count + gop.index); } @@ -358,21 +421,25 @@ pub const CType = extern union { return self.set.indexToCType(index); } + pub fn indexToHash(self: Store, index: Index) Set.Map.Hash { + return self.set.indexToHash(index); + } + pub fn cTypeToIndex(self: *Store, gpa: Allocator, cty: CType) !Index { var promoted = self.promote(gpa); defer self.demote(promoted); return promoted.cTypeToIndex(cty); } - pub fn typeToCType(self: *Store, gpa: Allocator, ty: Type, mod: *Module) !CType { - const idx = try self.typeToIndex(gpa, ty, mod); + pub fn typeToCType(self: *Store, gpa: Allocator, ty: Type, mod: *Module, kind: Kind) !CType { + const idx = try self.typeToIndex(gpa, ty, mod, kind); return self.indexToCType(idx); } - pub fn typeToIndex(self: *Store, gpa: Allocator, ty: Type, mod: *Module) !Index { + pub fn typeToIndex(self: *Store, gpa: Allocator, ty: Type, mod: *Module, kind: Kind) !Index { var promoted = self.promote(gpa); defer self.demote(promoted); - return promoted.typeToIndex(ty, mod, .complete); + return promoted.typeToIndex(ty, mod, kind); } pub fn clearRetainingCapacity(self: *Store, gpa: Allocator) void { @@ -389,8 +456,16 @@ pub const CType = extern union { _ = promoted.arena.reset(.free_all); } - pub fn shrinkToFit(self: *Store, gpa: Allocator) void { - self.set.map.shrinkAndFree(gpa, self.set.map.count()); + pub fn shrinkRetainingCapacity(self: *Store, gpa: Allocator, new_len: usize) void { + self.set.map.shrinkRetainingCapacity(gpa, new_len); + } + + pub fn shrinkAndFree(self: *Store, gpa: Allocator, new_len: usize) void { + self.set.map.shrinkAndFree(gpa, new_len); + } + + pub fn count(self: Store) usize { + return self.set.map.count(); } pub fn move(self: *Store) Store { @@ -407,7 +482,37 @@ pub const CType = extern union { } }; + pub fn isPacked(self: CType) bool { + return switch (self.tag()) { + else => false, + .packed_unnamed_struct, + .packed_unnamed_union, + .packed_struct, + .packed_union, + => true, + }; + } + + pub fn fields(self: CType) Payload.Fields.Data { + return if (self.cast(Payload.Aggregate)) |pl| + pl.data.fields + else if (self.cast(Payload.Unnamed)) |pl| + pl.data.fields + else if (self.cast(Payload.Fields)) |pl| + pl.data + else + unreachable; + } + pub fn eql(lhs: CType, rhs: CType) bool { + return lhs.eqlContext(rhs, struct { + pub fn eqlIndex(_: @This(), lhs_idx: Index, rhs_idx: Index) bool { + return lhs_idx == rhs_idx; + } + }{}); + } + + pub fn eqlContext(lhs: CType, rhs: CType, ctx: anytype) bool { // As a shortcut, if the small tags / addresses match, we're done. if (lhs.tag_if_small_enough == rhs.tag_if_small_enough) return true; @@ -458,35 +563,52 @@ pub const CType = extern union { .pointer_const, .pointer_volatile, .pointer_const_volatile, - => lhs.cast(Payload.Child).?.data == rhs.cast(Payload.Child).?.data, + => ctx.eqlIndex(lhs.cast(Payload.Child).?.data, rhs.cast(Payload.Child).?.data), .array, .vector, - => std.meta.eql(lhs.cast(Payload.Sequence).?.data, rhs.cast(Payload.Sequence).?.data), + => { + const lhs_data = lhs.cast(Payload.Sequence).?.data; + const rhs_data = rhs.cast(Payload.Sequence).?.data; + return lhs_data.len == rhs_data.len and + ctx.eqlIndex(lhs_data.elem_type, rhs_data.elem_type); + }, - .fwd_struct, - .fwd_union, - => lhs.cast(Payload.FwdDecl).?.data == rhs.cast(Payload.FwdDecl).?.data, - - .anon_struct, - .packed_anon_struct, + .fwd_anon_struct, + .fwd_anon_union, => { const lhs_data = lhs.cast(Payload.Fields).?.data; const rhs_data = rhs.cast(Payload.Fields).?.data; if (lhs_data.len != rhs_data.len) return false; for (lhs_data, rhs_data) |lhs_field, rhs_field| { - if (lhs_field.type != rhs_field.type) return false; - if (lhs_field.alignas != rhs_field.alignas) return false; + if (!ctx.eqlIndex(lhs_field.type, rhs_field.type)) return false; + if (lhs_field.alignas.@"align" != rhs_field.alignas.@"align") return false; if (cstr.cmp(lhs_field.name, rhs_field.name) != 0) return false; } return true; }, + .fwd_struct, + .fwd_union, + => lhs.cast(Payload.FwdDecl).?.data == rhs.cast(Payload.FwdDecl).?.data, + + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => { + const lhs_data = lhs.cast(Payload.Unnamed).?.data; + const rhs_data = rhs.cast(Payload.Unnamed).?.data; + return lhs_data.owner_decl == rhs_data.owner_decl and lhs_data.id == rhs_data.id; + }, + + .anon_struct, + .anon_union, .@"struct", .@"union", .packed_struct, .packed_union, - => std.meta.eql( + => ctx.eqlIndex( lhs.cast(Payload.Aggregate).?.data.fwd_decl, rhs.cast(Payload.Aggregate).?.data.fwd_decl, ), @@ -496,10 +618,10 @@ pub const CType = extern union { => { const lhs_data = lhs.cast(Payload.Function).?.data; const rhs_data = rhs.cast(Payload.Function).?.data; - if (lhs_data.return_type != rhs_data.return_type) return false; if (lhs_data.param_types.len != rhs_data.param_types.len) return false; - for (lhs_data.param_types, rhs_data.param_types) |lhs_param_cty, rhs_param_cty| { - if (lhs_param_cty != rhs_param_cty) return false; + if (!ctx.eqlIndex(lhs_data.return_type, rhs_data.return_type)) return false; + for (lhs_data.param_types, rhs_data.param_types) |lhs_param_idx, rhs_param_idx| { + if (!ctx.eqlIndex(lhs_param_idx, rhs_param_idx)) return false; } return true; }, @@ -568,18 +690,30 @@ pub const CType = extern union { store.indexToCType(data.elem_type).updateHasher(hasher, store); }, + .fwd_anon_struct, + .fwd_anon_union, + => for (self.cast(Payload.Fields).?.data) |field| { + store.indexToCType(field.type).updateHasher(hasher, store); + hasher.update(mem.span(field.name)); + autoHash(hasher, field.alignas.@"align"); + }, + .fwd_struct, .fwd_union, => autoHash(hasher, self.cast(Payload.FwdDecl).?.data), - .anon_struct, - .packed_anon_struct, - => for (self.cast(Payload.Fields).?.data) |field| { - store.indexToCType(field.type).updateHasher(hasher, store); - hasher.update(mem.span(field.name)); - autoHash(hasher, field.alignas); + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => { + const data = self.cast(Payload.Unnamed).?.data; + autoHash(hasher, data.owner_decl); + autoHash(hasher, data.id); }, + .anon_struct, + .anon_union, .@"struct", .@"union", .packed_struct, @@ -599,7 +733,7 @@ pub const CType = extern union { } } - pub const Kind = enum { forward, complete, global, parameter }; + pub const Kind = enum { forward, forward_parameter, complete, global, parameter, payload }; const Convert = struct { storage: union { @@ -609,9 +743,11 @@ pub const CType = extern union { fwd: Payload.FwdDecl, anon: struct { fields: [2]Payload.Fields.Field, - pl: Payload.Fields, + pl: union { + forward: Payload.Fields, + complete: Payload.Aggregate, + }, }, - agg: Payload.Aggregate, }, value: union(enum) { tag: Tag, @@ -716,6 +852,66 @@ pub const CType = extern union { } }; + fn sortFields(self: *@This(), fields_len: usize) []Payload.Fields.Field { + const Field = Payload.Fields.Field; + const slice = self.storage.anon.fields[0..fields_len]; + std.sort.sort(Field, slice, {}, struct { + fn before(_: void, lhs: Field, rhs: Field) bool { + return lhs.alignas.@"align" > rhs.alignas.@"align"; + } + }.before); + return slice; + } + + fn initAnon(self: *@This(), kind: Kind, fwd_idx: Index, fields_len: usize) void { + switch (kind) { + .forward, .forward_parameter => { + self.storage.anon.pl = .{ .forward = .{ + .base = .{ .tag = .fwd_anon_struct }, + .data = self.sortFields(fields_len), + } }; + self.value = .{ .cty = initPayload(&self.storage.anon.pl.forward) }; + }, + .complete, .parameter, .global => { + self.storage.anon.pl = .{ .complete = .{ + .base = .{ .tag = .anon_struct }, + .data = .{ + .fields = self.sortFields(fields_len), + .fwd_decl = fwd_idx, + }, + } }; + self.value = .{ .cty = initPayload(&self.storage.anon.pl.complete) }; + }, + .payload => unreachable, + } + } + + fn initArrayParameter(self: *@This(), ty: Type, kind: Kind, lookup: Lookup) !void { + if (switch (kind) { + .forward_parameter => @as(Index, undefined), + .parameter => try lookup.typeToIndex(ty, .forward_parameter), + .forward, .complete, .global, .payload => unreachable, + }) |fwd_idx| { + if (try lookup.typeToIndex(ty, switch (kind) { + .forward_parameter => .forward, + .parameter => .complete, + .forward, .complete, .global, .payload => unreachable, + })) |array_idx| { + self.storage = .{ .anon = undefined }; + self.storage.anon.fields[0] = .{ + .name = "array", + .type = array_idx, + .alignas = Payload.Fields.AlignAs.abiAlign(ty, lookup.getTarget()), + }; + self.initAnon(kind, fwd_idx, 1); + } else self.init(switch (kind) { + .forward_parameter => .fwd_anon_struct, + .parameter => .anon_struct, + .forward, .complete, .global, .payload => unreachable, + }); + } else self.init(.anon_struct); + } + pub fn initType(self: *@This(), ty: Type, kind: Kind, lookup: Lookup) !void { const target = lookup.getTarget(); @@ -739,17 +935,23 @@ pub const CType = extern union { switch (t) { .void => unreachable, else => self.init(t), - .array => { - const abi_size = ty.abiSize(target); - const abi_align = ty.abiAlignment(target); - self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ - .len = @divExact(abi_size, abi_align), - .elem_type = tagFromIntInfo( - .unsigned, - @intCast(u16, abi_align * 8), - ).toIndex(), - } } }; - self.value = .{ .cty = initPayload(&self.storage.seq) }; + .array => switch (kind) { + .forward, .complete, .global => { + const abi_size = ty.abiSize(target); + const abi_align = ty.abiAlignment(target); + self.storage = .{ .seq = .{ .base = .{ .tag = .array }, .data = .{ + .len = @divExact(abi_size, abi_align), + .elem_type = tagFromIntInfo( + .unsigned, + @intCast(u16, abi_align * 8), + ).toIndex(), + } } }; + self.value = .{ .cty = initPayload(&self.storage.seq) }; + }, + .forward_parameter, + .parameter, + => try self.initArrayParameter(ty, kind, lookup), + .payload => unreachable, }, } }, @@ -782,165 +984,297 @@ pub const CType = extern union { else => unreachable, }), - .Pointer => switch (ty.ptrSize()) { - .Slice => { - var buf: Type.SlicePtrFieldTypeBuffer = undefined; - const ptr_ty = ty.slicePtrFieldType(&buf); - if (try lookup.typeToIndex(ptr_ty, kind)) |ptr_idx| { - self.storage = .{ .anon = .{ .fields = .{ - .{ - .name = "ptr", - .type = ptr_idx, - .alignas = ptr_ty.abiAlignment(target), - }, - .{ - .name = "len", - .type = Tag.size_t.toIndex(), - .alignas = Type.usize.abiAlignment(target), - }, - }, .pl = undefined } }; - self.storage.anon.pl = .{ - .base = .{ .tag = .anon_struct }, - .data = self.storage.anon.fields[0..2], - }; - self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; - } else self.init(.anon_struct); - }, + .Pointer => { + const info = ty.ptrInfo().data; + switch (info.size) { + .Slice => { + if (switch (kind) { + .forward, .forward_parameter => @as(Index, undefined), + .complete, .parameter, .global => try lookup.typeToIndex(ty, .forward), + .payload => unreachable, + }) |fwd_idx| { + var buf: Type.SlicePtrFieldTypeBuffer = undefined; + const ptr_ty = ty.slicePtrFieldType(&buf); + if (try lookup.typeToIndex(ptr_ty, kind)) |ptr_idx| { + self.storage = .{ .anon = undefined }; + self.storage.anon.fields[0] = .{ + .name = "ptr", + .type = ptr_idx, + .alignas = Payload.Fields.AlignAs.abiAlign(ptr_ty, target), + }; + self.storage.anon.fields[1] = .{ + .name = "len", + .type = Tag.uintptr_t.toIndex(), + .alignas = Payload.Fields.AlignAs.abiAlign(Type.usize, target), + }; + self.initAnon(kind, fwd_idx, 2); + } else self.init(switch (kind) { + .forward, .forward_parameter => .fwd_anon_struct, + .complete, .parameter, .global => .anon_struct, + .payload => unreachable, + }); + } else self.init(.anon_struct); + }, - .One, .Many, .C => { - const t: Tag = switch (ty.isVolatilePtr()) { - false => switch (ty.isConstPtr()) { - false => .pointer, - true => .pointer_const, - }, - true => switch (ty.isConstPtr()) { - false => .pointer_volatile, - true => .pointer_const_volatile, - }, - }; - if (try lookup.typeToIndex(ty.childType(), .forward)) |child_idx| { - self.storage = .{ .child = .{ .base = .{ .tag = t }, .data = child_idx } }; - self.value = .{ .cty = initPayload(&self.storage.child) }; - } else self.init(t); - }, + .One, .Many, .C => { + const t: Tag = switch (info.@"volatile") { + false => switch (info.mutable) { + true => .pointer, + false => .pointer_const, + }, + true => switch (info.mutable) { + true => .pointer_volatile, + false => .pointer_const_volatile, + }, + }; + + var host_int_pl = Type.Payload.Bits{ + .base = .{ .tag = .int_unsigned }, + .data = info.host_size * 8, + }; + const pointee_ty = if (info.host_size > 0) + Type.initPayload(&host_int_pl.base) + else + info.pointee_type; + + if (if (info.size == .C and pointee_ty.tag() == .u8) + Tag.char.toIndex() + else + try lookup.typeToIndex(pointee_ty, .forward)) |child_idx| + { + self.storage = .{ .child = .{ + .base = .{ .tag = t }, + .data = child_idx, + } }; + self.value = .{ .cty = initPayload(&self.storage.child) }; + } else self.init(t); + }, + } }, - .Struct, .Union => |zig_tag| if (ty.isTupleOrAnonStruct()) { + .Struct, .Union => |zig_tag| if (ty.containerLayout() == .Packed) { + if (ty.castTag(.@"struct")) |struct_obj| { + try self.initType(struct_obj.data.backing_int_ty, kind, lookup); + } else { + var buf: Type.Payload.Bits = .{ + .base = .{ .tag = .int_unsigned }, + .data = @intCast(u16, ty.bitSize(target)), + }; + try self.initType(Type.initPayload(&buf.base), kind, lookup); + } + } else if (ty.isTupleOrAnonStruct()) { if (lookup.isMutable()) { for (0..ty.structFieldCount()) |field_i| { const field_ty = ty.structFieldType(field_i); if (ty.structFieldIsComptime(field_i) or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; _ = try lookup.typeToIndex(field_ty, switch (kind) { - .forward, .complete, .parameter => .complete, + .forward, .forward_parameter => .forward, + .complete, .parameter => .complete, .global => .global, + .payload => unreachable, }); } + switch (kind) { + .forward, .forward_parameter => {}, + .complete, .parameter, .global => _ = try lookup.typeToIndex(ty, .forward), + .payload => unreachable, + } } - self.init(.anon_struct); + self.init(switch (kind) { + .forward, .forward_parameter => .fwd_anon_struct, + .complete, .parameter, .global => .anon_struct, + .payload => unreachable, + }); } else { - const is_struct = zig_tag == .Struct or ty.unionTagTypeSafety() != null; + const tag_ty = ty.unionTagTypeSafety(); + const is_tagged_union_wrapper = kind != .payload and tag_ty != null; + const is_struct = zig_tag == .Struct or is_tagged_union_wrapper; switch (kind) { - .forward => { + .forward, .forward_parameter => { self.storage = .{ .fwd = .{ .base = .{ .tag = if (is_struct) .fwd_struct else .fwd_union }, .data = ty.getOwnerDecl(), } }; self.value = .{ .cty = initPayload(&self.storage.fwd) }; }, - else => { - if (lookup.isMutable()) { - for (0..switch (zig_tag) { - .Struct => ty.structFieldCount(), - .Union => ty.cast(Type.Payload.Union).?.data.fields.count(), - else => unreachable, - }) |field_i| { - const field_ty = ty.structFieldType(field_i); - if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + .complete, .parameter, .global, .payload => if (is_tagged_union_wrapper) { + const fwd_idx = try lookup.typeToIndex(ty, .forward); + const payload_idx = try lookup.typeToIndex(ty, .payload); + const tag_idx = try lookup.typeToIndex(tag_ty.?, kind); + if (fwd_idx != null and payload_idx != null and tag_idx != null) { + self.storage = .{ .anon = undefined }; + var field_count: usize = 0; + if (payload_idx != Tag.void.toIndex()) { + self.storage.anon.fields[field_count] = .{ + .name = "payload", + .type = payload_idx.?, + .alignas = Payload.Fields.AlignAs.unionPayloadAlign(ty, target), + }; + field_count += 1; + } + if (tag_idx != Tag.void.toIndex()) { + self.storage.anon.fields[field_count] = .{ + .name = "tag", + .type = tag_idx.?, + .alignas = Payload.Fields.AlignAs.abiAlign(tag_ty.?, target), + }; + field_count += 1; + } + self.storage.anon.pl = .{ .complete = .{ + .base = .{ .tag = .@"struct" }, + .data = .{ + .fields = self.sortFields(field_count), + .fwd_decl = fwd_idx.?, + }, + } }; + self.value = .{ .cty = initPayload(&self.storage.anon.pl.complete) }; + } else self.init(.@"struct"); + } else if (kind == .payload and ty.unionHasAllZeroBitFieldTypes()) { + self.init(.void); + } else { + var is_packed = false; + for (0..switch (zig_tag) { + .Struct => ty.structFieldCount(), + .Union => ty.unionFields().count(), + else => unreachable, + }) |field_i| { + const field_ty = ty.structFieldType(field_i); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + + const field_align = Payload.Fields.AlignAs.fieldAlign( + ty, + field_i, + target, + ); + if (field_align.@"align" < field_align.abi) { + is_packed = true; + if (!lookup.isMutable()) break; + } + + if (lookup.isMutable()) { _ = try lookup.typeToIndex(field_ty, switch (kind) { - .forward => unreachable, - .complete, .parameter => .complete, + .forward, .forward_parameter => unreachable, + .complete, .parameter, .payload => .complete, .global => .global, }); } - _ = try lookup.typeToIndex(ty, .forward); } - self.init(if (is_struct) .@"struct" else .@"union"); + switch (kind) { + .forward, .forward_parameter => unreachable, + .complete, .parameter, .global => { + _ = try lookup.typeToIndex(ty, .forward); + self.init(if (is_struct) + if (is_packed) .packed_struct else .@"struct" + else if (is_packed) .packed_union else .@"union"); + }, + .payload => self.init(if (is_packed) + .packed_unnamed_union + else + .unnamed_union), + } }, } }, .Array, .Vector => |zig_tag| { - const t: Tag = switch (zig_tag) { - .Array => .array, - .Vector => .vector, - else => unreachable, - }; - if (try lookup.typeToIndex(ty.childType(), kind)) |child_idx| { - self.storage = .{ .seq = .{ .base = .{ .tag = t }, .data = .{ - .len = ty.arrayLenIncludingSentinel(), - .elem_type = child_idx, - } } }; - self.value = .{ .cty = initPayload(&self.storage.seq) }; - } else self.init(t); + switch (kind) { + .forward, .complete, .global => { + const t: Tag = switch (zig_tag) { + .Array => .array, + .Vector => .vector, + else => unreachable, + }; + if (try lookup.typeToIndex(ty.childType(), kind)) |child_idx| { + self.storage = .{ .seq = .{ .base = .{ .tag = t }, .data = .{ + .len = ty.arrayLenIncludingSentinel(), + .elem_type = child_idx, + } } }; + self.value = .{ .cty = initPayload(&self.storage.seq) }; + } else self.init(t); + }, + .forward_parameter, .parameter => try self.initArrayParameter(ty, kind, lookup), + .payload => unreachable, + } }, .Optional => { var buf: Type.Payload.ElemType = undefined; const payload_ty = ty.optionalChild(&buf); if (payload_ty.hasRuntimeBitsIgnoreComptime()) { - if (ty.optionalReprIsPayload()) - try self.initType(payload_ty, kind, lookup) - else if (try lookup.typeToIndex(payload_ty, kind)) |payload_idx| { - self.storage = .{ .anon = .{ .fields = .{ - .{ + if (ty.optionalReprIsPayload()) { + try self.initType(payload_ty, kind, lookup); + } else if (switch (kind) { + .forward, .forward_parameter => @as(Index, undefined), + .complete, .parameter, .global => try lookup.typeToIndex(ty, .forward), + .payload => unreachable, + }) |fwd_idx| { + if (try lookup.typeToIndex(payload_ty, switch (kind) { + .forward, .forward_parameter => .forward, + .complete, .parameter => .complete, + .global => .global, + .payload => unreachable, + })) |payload_idx| { + self.storage = .{ .anon = undefined }; + self.storage.anon.fields[0] = .{ .name = "payload", .type = payload_idx, - .alignas = payload_ty.abiAlignment(target), - }, - .{ + .alignas = Payload.Fields.AlignAs.abiAlign(payload_ty, target), + }; + self.storage.anon.fields[1] = .{ .name = "is_null", .type = Tag.bool.toIndex(), - .alignas = Type.bool.abiAlignment(target), - }, - }, .pl = undefined } }; - self.storage.anon.pl = .{ - .base = .{ .tag = .anon_struct }, - .data = self.storage.anon.fields[0..2], - }; - self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; + .alignas = Payload.Fields.AlignAs.abiAlign(Type.bool, target), + }; + self.initAnon(kind, fwd_idx, 2); + } else self.init(switch (kind) { + .forward, .forward_parameter => .fwd_anon_struct, + .complete, .parameter, .global => .anon_struct, + .payload => unreachable, + }); } else self.init(.anon_struct); } else self.init(.bool); }, .ErrorUnion => { - const payload_ty = ty.errorUnionPayload(); - if (try lookup.typeToIndex(payload_ty, switch (kind) { - .forward, .complete, .parameter => .complete, - .global => .global, - })) |payload_idx| { - const error_ty = ty.errorUnionSet(); - if (payload_idx == Tag.void.toIndex()) - try self.initType(error_ty, kind, lookup) - else if (try lookup.typeToIndex(error_ty, kind)) |error_idx| { - self.storage = .{ .anon = .{ .fields = .{ - .{ + if (switch (kind) { + .forward, .forward_parameter => @as(Index, undefined), + .complete, .parameter, .global => try lookup.typeToIndex(ty, .forward), + .payload => unreachable, + }) |fwd_idx| { + const payload_ty = ty.errorUnionPayload(); + if (try lookup.typeToIndex(payload_ty, switch (kind) { + .forward, .forward_parameter => .forward, + .complete, .parameter => .complete, + .global => .global, + .payload => unreachable, + })) |payload_idx| { + const error_ty = ty.errorUnionSet(); + if (payload_idx == Tag.void.toIndex()) { + try self.initType(error_ty, kind, lookup); + } else if (try lookup.typeToIndex(error_ty, kind)) |error_idx| { + self.storage = .{ .anon = undefined }; + self.storage.anon.fields[0] = .{ .name = "payload", .type = payload_idx, - .alignas = payload_ty.abiAlignment(target), - }, - .{ + .alignas = Payload.Fields.AlignAs.abiAlign(payload_ty, target), + }; + self.storage.anon.fields[1] = .{ .name = "error", .type = error_idx, - .alignas = error_ty.abiAlignment(target), - }, - }, .pl = undefined } }; - self.storage.anon.pl = .{ - .base = .{ .tag = .anon_struct }, - .data = self.storage.anon.fields[0..2], - }; - self.value = .{ .cty = initPayload(&self.storage.anon.pl) }; - } else self.init(.anon_struct); + .alignas = Payload.Fields.AlignAs.abiAlign(error_ty, target), + }; + self.initAnon(kind, fwd_idx, 2); + } else self.init(switch (kind) { + .forward, .forward_parameter => .fwd_anon_struct, + .complete, .parameter, .global => .anon_struct, + .payload => unreachable, + }); + } else self.init(switch (kind) { + .forward, .forward_parameter => .fwd_anon_struct, + .complete, .parameter, .global => .anon_struct, + .payload => unreachable, + }); } else self.init(.anon_struct); }, @@ -959,16 +1293,15 @@ pub const CType = extern union { .Fn => { const info = ty.fnInfo(); if (lookup.isMutable()) { - _ = try lookup.typeToIndex(info.return_type, switch (kind) { - .forward => .forward, - .complete, .parameter, .global => .complete, - }); + const param_kind: Kind = switch (kind) { + .forward, .forward_parameter => .forward_parameter, + .complete, .parameter, .global => .parameter, + .payload => unreachable, + }; + _ = try lookup.typeToIndex(info.return_type, param_kind); for (info.param_types) |param_type| { if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - _ = try lookup.typeToIndex(param_type, switch (kind) { - .forward => .forward, - .complete, .parameter, .global => unreachable, - }); + _ = try lookup.typeToIndex(param_type, param_kind); } } self.init(if (info.is_var_args) .varargs_function else .function); @@ -977,16 +1310,33 @@ pub const CType = extern union { } }; - fn copyFields(arena: Allocator, fields: Payload.Fields.Data) !Payload.Fields.Data { - const new_fields = try arena.dupe(Payload.Fields.Field, fields); - for (new_fields) |*new_field| { - new_field.name = try arena.dupeZ(u8, mem.span(new_field.name)); - new_field.type = new_field.type; + pub fn copy(self: CType, arena: Allocator) !CType { + return self.copyContext(struct { + arena: Allocator, + pub fn copyIndex(_: @This(), idx: Index) Index { + return idx; + } + }{ .arena = arena }); + } + + fn copyFields(ctx: anytype, old_fields: Payload.Fields.Data) !Payload.Fields.Data { + const new_fields = try ctx.arena.alloc(Payload.Fields.Field, old_fields.len); + for (new_fields, old_fields) |*new_field, old_field| { + new_field.name = try ctx.arena.dupeZ(u8, mem.span(old_field.name)); + new_field.type = ctx.copyIndex(old_field.type); + new_field.alignas = old_field.alignas; } return new_fields; } - pub fn copy(self: CType, arena: Allocator) !CType { + fn copyParams(ctx: anytype, old_param_types: []const Index) ![]const Index { + const new_param_types = try ctx.arena.alloc(Index, old_param_types.len); + for (new_param_types, old_param_types) |*new_param_type, old_param_type| + new_param_type.* = ctx.copyIndex(old_param_type); + return new_param_types; + } + + pub fn copyContext(self: CType, ctx: anytype) !CType { switch (self.tag()) { .void, .char, @@ -1032,8 +1382,8 @@ pub const CType = extern union { .pointer_const_volatile, => { const pl = self.cast(Payload.Child).?; - const new_pl = try arena.create(Payload.Child); - new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = pl.data }; + const new_pl = try ctx.arena.create(Payload.Child); + new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = ctx.copyIndex(pl.data) }; return initPayload(new_pl); }, @@ -1041,10 +1391,22 @@ pub const CType = extern union { .vector, => { const pl = self.cast(Payload.Sequence).?; - const new_pl = try arena.create(Payload.Sequence); + const new_pl = try ctx.arena.create(Payload.Sequence); new_pl.* = .{ .base = .{ .tag = pl.base.tag }, - .data = .{ .len = pl.data.len, .elem_type = pl.data.elem_type }, + .data = .{ .len = pl.data.len, .elem_type = ctx.copyIndex(pl.data.elem_type) }, + }; + return initPayload(new_pl); + }, + + .fwd_anon_struct, + .fwd_anon_union, + => { + const pl = self.cast(Payload.Fields).?; + const new_pl = try ctx.arena.create(Payload.Fields); + new_pl.* = .{ + .base = .{ .tag = pl.base.tag }, + .data = try copyFields(ctx, pl.data), }; return initPayload(new_pl); }, @@ -1053,36 +1415,38 @@ pub const CType = extern union { .fwd_union, => { const pl = self.cast(Payload.FwdDecl).?; - const new_pl = try arena.create(Payload.FwdDecl); - new_pl.* = .{ - .base = .{ .tag = pl.base.tag }, - .data = pl.data, - }; + const new_pl = try ctx.arena.create(Payload.FwdDecl); + new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = pl.data }; + return initPayload(new_pl); + }, + + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => { + const pl = self.cast(Payload.Unnamed).?; + const new_pl = try ctx.arena.create(Payload.Unnamed); + new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ + .fields = try copyFields(ctx, pl.data.fields), + .owner_decl = pl.data.owner_decl, + .id = pl.data.id, + } }; return initPayload(new_pl); }, .anon_struct, - .packed_anon_struct, - => { - const pl = self.cast(Payload.Fields).?; - const new_pl = try arena.create(Payload.Fields); - new_pl.* = .{ - .base = .{ .tag = pl.base.tag }, - .data = try copyFields(arena, pl.data), - }; - return initPayload(new_pl); - }, - + .anon_union, .@"struct", .@"union", .packed_struct, .packed_union, => { const pl = self.cast(Payload.Aggregate).?; - const new_pl = try arena.create(Payload.Aggregate); + const new_pl = try ctx.arena.create(Payload.Aggregate); new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ - .fields = try copyFields(arena, pl.data.fields), - .fwd_decl = pl.data.fwd_decl, + .fields = try copyFields(ctx, pl.data.fields), + .fwd_decl = ctx.copyIndex(pl.data.fwd_decl), } }; return initPayload(new_pl); }, @@ -1091,10 +1455,10 @@ pub const CType = extern union { .varargs_function, => { const pl = self.cast(Payload.Function).?; - const new_pl = try arena.create(Payload.Function); + const new_pl = try ctx.arena.create(Payload.Function); new_pl.* = .{ .base = .{ .tag = pl.base.tag }, .data = .{ - .return_type = pl.data.return_type, - .param_types = try arena.dupe(Index, pl.data.param_types), + .return_type = ctx.copyIndex(pl.data.return_type), + .param_types = try copyParams(ctx, pl.data.param_types), } }; return initPayload(new_pl); }, @@ -1118,8 +1482,14 @@ pub const CType = extern union { switch (convert.value) { .cty => |c| return c.copy(arena), .tag => |t| switch (t) { + .fwd_anon_struct, + .fwd_anon_union, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, .anon_struct, - .packed_anon_struct, + .anon_union, .@"struct", .@"union", .packed_struct, @@ -1149,31 +1519,44 @@ pub const CType = extern union { else arena.dupeZ(u8, ty.structFieldName(field_i)), .type = store.set.typeToIndex(field_ty, target, switch (kind) { - .forward, .complete, .parameter => .complete, + .forward, .forward_parameter => .forward, + .complete, .parameter => .complete, .global => .global, + .payload => unreachable, }).?, - .alignas = ty.structFieldAlign(field_i, target), + .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), }; c_field_i += 1; } - if (ty.isTupleOrAnonStruct()) { - const anon_pl = try arena.create(Payload.Fields); - anon_pl.* = .{ .base = .{ .tag = .anon_struct }, .data = fields_pl }; - return initPayload(anon_pl); - } + switch (t) { + .fwd_anon_struct => { + const anon_pl = try arena.create(Payload.Fields); + anon_pl.* = .{ .base = .{ .tag = t }, .data = fields_pl }; + return initPayload(anon_pl); + }, - const struct_pl = try arena.create(Payload.Aggregate); - struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .fields = fields_pl, - .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, - } }; - return initPayload(struct_pl); + .anon_struct, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => { + const struct_pl = try arena.create(Payload.Aggregate); + struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, + } }; + return initPayload(struct_pl); + }, + + else => unreachable, + } }, .Union => { - const fields = ty.unionFields(); - const fields_len = fields.count(); + const union_fields = ty.unionFields(); + const fields_len = union_fields.count(); var c_fields_len: usize = 0; for (0..fields_len) |field_i| { @@ -1185,7 +1568,7 @@ pub const CType = extern union { const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); var field_i: usize = 0; var c_field_i: usize = 0; - var field_it = fields.iterator(); + var field_it = union_fields.iterator(); while (field_it.next()) |field| { defer field_i += 1; if (!field.value_ptr.ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -1193,21 +1576,35 @@ pub const CType = extern union { fields_pl[c_field_i] = .{ .name = try arena.dupeZ(u8, field.key_ptr.*), .type = store.set.typeToIndex(field.value_ptr.ty, target, switch (kind) { - .forward => unreachable, - .complete, .parameter => .complete, + .forward, .forward_parameter => unreachable, + .complete, .parameter, .payload => .complete, .global => .global, }).?, - .alignas = ty.structFieldAlign(field_i, target), + .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), }; c_field_i += 1; } - const union_pl = try arena.create(Payload.Aggregate); - union_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .fields = fields_pl, - .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, - } }; - return initPayload(union_pl); + switch (kind) { + .forward, .forward_parameter => unreachable, + .complete, .parameter, .global => { + const union_pl = try arena.create(Payload.Aggregate); + union_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, + } }; + return initPayload(union_pl); + }, + .payload => if (ty.unionTagTypeSafety()) |_| { + const union_pl = try arena.create(Payload.Unnamed); + union_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .owner_decl = ty.getOwnerDecl(), + .id = 0, + } }; + return initPayload(union_pl); + } else unreachable, + } }, else => unreachable, @@ -1217,9 +1614,10 @@ pub const CType = extern union { .varargs_function, => { const info = ty.fnInfo(); - const recurse_kind: Kind = switch (kind) { - .forward => .forward, - .complete, .parameter, .global => unreachable, + const param_kind: Kind = switch (kind) { + .forward, .forward_parameter => .forward_parameter, + .complete, .parameter, .global => .parameter, + .payload => unreachable, }; var c_params_len: usize = 0; @@ -1232,13 +1630,13 @@ pub const CType = extern union { var c_param_i: usize = 0; for (info.param_types) |param_type| { if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - params_pl[c_param_i] = store.set.typeToIndex(param_type, target, recurse_kind).?; + params_pl[c_param_i] = store.set.typeToIndex(param_type, target, param_kind).?; c_param_i += 1; } const fn_pl = try arena.create(Payload.Function); fn_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .return_type = store.set.typeToIndex(info.return_type, target, recurse_kind).?, + .return_type = store.set.typeToIndex(info.return_type, target, param_kind).?, .param_types = params_pl, } }; return initPayload(fn_pl); @@ -1294,8 +1692,8 @@ pub const CType = extern union { const target = self.lookup.getTarget(); switch (t) { - .anon_struct, - .packed_anon_struct, + .fwd_anon_struct, + .fwd_anon_union, => { if (!ty.isTupleOrAnonStruct()) return false; @@ -1313,26 +1711,38 @@ pub const CType = extern union { const c_field = &c_fields[c_field_i]; c_field_i += 1; - if (!self.eqlRecurse( - ty.structFieldType(field_i), - c_field.type, - switch (self.kind) { - .forward, .complete, .parameter => .complete, - .global => .global, - }, - ) or !mem.eql( + if (!self.eqlRecurse(field_ty, c_field.type, switch (self.kind) { + .forward, .forward_parameter => .forward, + .complete, .parameter => .complete, + .global => .global, + .payload => unreachable, + }) or !mem.eql( u8, if (ty.isSimpleTuple()) std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable else ty.structFieldName(field_i), mem.span(c_field.name), - ) or ty.structFieldAlign(field_i, target) != c_field.alignas) - return false; + ) or Payload.Fields.AlignAs.fieldAlign(ty, field_i, target).@"align" != + c_field.alignas.@"align") return false; } return true; }, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => switch (self.kind) { + .forward, .forward_parameter, .complete, .parameter, .global => unreachable, + .payload => if (ty.unionTagTypeSafety()) |_| { + const data = cty.cast(Payload.Unnamed).?.data; + return ty.getOwnerDecl() == data.owner_decl and data.id == 0; + } else unreachable, + }, + + .anon_struct, + .anon_union, .@"struct", .@"union", .packed_struct, @@ -1350,19 +1760,27 @@ pub const CType = extern union { const info = ty.fnInfo(); const data = cty.cast(Payload.Function).?.data; - const recurse_kind: Kind = switch (self.kind) { - .forward => .forward, - .complete, .parameter, .global => unreachable, + const param_kind: Kind = switch (self.kind) { + .forward, .forward_parameter => .forward_parameter, + .complete, .parameter, .global => .parameter, + .payload => unreachable, }; - if (info.param_types.len != data.param_types.len or - !self.eqlRecurse(info.return_type, data.return_type, recurse_kind)) + if (!self.eqlRecurse(info.return_type, data.return_type, param_kind)) return false; - for (info.param_types, data.param_types) |param_ty, param_cty| { - if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; - if (!self.eqlRecurse(param_ty, param_cty, recurse_kind)) return false; + + var c_param_i: usize = 0; + for (info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + + if (c_param_i >= data.param_types.len) return false; + const param_cty = data.param_types[c_param_i]; + c_param_i += 1; + + if (!self.eqlRecurse(param_type, param_cty, param_kind)) + return false; } - return true; + return c_param_i == data.param_types.len; }, else => unreachable, @@ -1395,13 +1813,17 @@ pub const CType = extern union { const target = self.lookup.getTarget(); switch (t) { - .anon_struct, - .packed_anon_struct, + .fwd_anon_struct, + .fwd_anon_union, => { var name_buf: [ std.fmt.count("f{}", .{std.math.maxInt(usize)}) ]u8 = undefined; - for (0..ty.structFieldCount()) |field_i| { + for (0..switch (ty.zigTypeTag()) { + .Struct => ty.structFieldCount(), + .Union => ty.unionFields().count(), + else => unreachable, + }) |field_i| { const field_ty = ty.structFieldType(field_i); if (ty.structFieldIsComptime(field_i) or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; @@ -1410,18 +1832,37 @@ pub const CType = extern union { hasher, ty.structFieldType(field_i), switch (self.kind) { - .forward, .complete, .parameter => .complete, + .forward, .forward_parameter => .forward, + .complete, .parameter => .complete, .global => .global, + .payload => unreachable, }, ); hasher.update(if (ty.isSimpleTuple()) std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable else ty.structFieldName(field_i)); - autoHash(hasher, ty.structFieldAlign(field_i, target)); + autoHash( + hasher, + Payload.Fields.AlignAs.fieldAlign(ty, field_i, target).@"align", + ); } }, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => switch (self.kind) { + .forward, .forward_parameter, .complete, .parameter, .global => unreachable, + .payload => if (ty.unionTagTypeSafety()) |_| { + autoHash(hasher, ty.getOwnerDecl()); + autoHash(hasher, @as(u32, 0)); + } else unreachable, + }, + + .anon_struct, + .anon_union, .@"struct", .@"union", .packed_struct, @@ -1432,15 +1873,16 @@ pub const CType = extern union { .varargs_function, => { const info = ty.fnInfo(); - const recurse_kind: Kind = switch (self.kind) { - .forward => .forward, - .complete, .parameter, .global => unreachable, + const param_kind: Kind = switch (self.kind) { + .forward, .forward_parameter => .forward_parameter, + .complete, .parameter, .global => .parameter, + .payload => unreachable, }; - self.updateHasherRecurse(hasher, info.return_type, recurse_kind); + self.updateHasherRecurse(hasher, info.return_type, param_kind); for (info.param_types) |param_type| { if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - self.updateHasherRecurse(hasher, param_type, recurse_kind); + self.updateHasherRecurse(hasher, param_type, param_kind); } }, diff --git a/src/link/C.zig b/src/link/C.zig index 8eb6fe16af..262e4e4923 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -117,7 +117,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes .gpa = gpa, .module = module, .error_msg = null, - .decl_index = decl_index, + .decl_index = decl_index.toOptional(), .decl = module.declPtr(decl_index), .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, @@ -146,7 +146,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes code.* = function.object.code.moveToUnmanaged(); // Free excess allocated memory for this Decl. - ctypes.shrinkToFit(gpa); + ctypes.shrinkAndFree(gpa, ctypes.count()); lazy_fns.shrinkAndFree(gpa, lazy_fns.count()); fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); code.shrinkAndFree(gpa, code.items.len); @@ -176,7 +176,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi .gpa = gpa, .module = module, .error_msg = null, - .decl_index = decl_index, + .decl_index = decl_index.toOptional(), .decl = decl, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, @@ -204,7 +204,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi code.* = object.code.moveToUnmanaged(); // Free excess allocated memory for this Decl. - ctypes.shrinkToFit(gpa); + ctypes.shrinkAndFree(gpa, ctypes.count()); fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len); code.shrinkAndFree(gpa, code.items.len); } @@ -247,8 +247,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const abi_define = abiDefine(comp); - // Covers defines, zig.h, ctypes, asm. - try f.all_buffers.ensureUnusedCapacity(gpa, 4); + // Covers defines, zig.h, ctypes, asm, lazy fwd, lazy code. + try f.all_buffers.ensureUnusedCapacity(gpa, 6); if (abi_define) |buf| f.appendBufAssumeCapacity(buf); f.appendBufAssumeCapacity(zig_h); @@ -258,15 +258,15 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) { var asm_buf = f.asm_buf.toManaged(gpa); - defer asm_buf.deinit(); - - try codegen.genGlobalAsm(module, &asm_buf); - - f.asm_buf = asm_buf.moveToUnmanaged(); - f.appendBufAssumeCapacity(f.asm_buf.items); + defer f.asm_buf = asm_buf.moveToUnmanaged(); + try codegen.genGlobalAsm(module, asm_buf.writer()); + f.appendBufAssumeCapacity(asm_buf.items); } - try self.flushErrDecls(&f); + const lazy_indices = f.all_buffers.items.len; + f.all_buffers.items.len += 2; + + try self.flushErrDecls(&f.lazy_db); // `CType`s, forward decls, and non-functions first. // Unlike other backends, the .c code we are emitting is order-dependent. Therefore @@ -295,6 +295,30 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) } } + { + // We need to flush lazy ctypes after flushing all decls but before flushing any decl ctypes. + assert(f.ctypes.count() == 0); + try self.flushCTypes(&f, .none, f.lazy_db.ctypes); + + var it = self.decl_table.iterator(); + while (it.next()) |entry| + try self.flushCTypes(&f, entry.key_ptr.toOptional(), entry.value_ptr.ctypes); + } + + { + f.all_buffers.items[lazy_indices + 0] = .{ + .iov_base = if (f.lazy_db.fwd_decl.items.len > 0) f.lazy_db.fwd_decl.items.ptr else "", + .iov_len = f.lazy_db.fwd_decl.items.len, + }; + f.file_size += f.lazy_db.fwd_decl.items.len; + + f.all_buffers.items[lazy_indices + 1] = .{ + .iov_base = if (f.lazy_db.code.items.len > 0) f.lazy_db.code.items.ptr else "", + .iov_len = f.lazy_db.code.items.len, + }; + f.file_size += f.lazy_db.code.items.len; + } + f.all_buffers.items[ctypes_index] = .{ .iov_base = if (f.ctypes_buf.items.len > 0) f.ctypes_buf.items.ptr else "", .iov_len = f.ctypes_buf.items.len, @@ -318,17 +342,17 @@ const Flush = struct { ctypes_map: std.ArrayListUnmanaged(codegen.CType.Index) = .{}, ctypes_buf: std.ArrayListUnmanaged(u8) = .{}, - err_decls: DeclBlock = .{}, - + lazy_db: DeclBlock = .{}, lazy_fns: LazyFns = .{}, asm_buf: std.ArrayListUnmanaged(u8) = .{}, + /// We collect a list of buffers to write, and write them all at once with pwritev 😎 all_buffers: std.ArrayListUnmanaged(std.os.iovec_const) = .{}, /// Keeps track of the total bytes of `all_buffers`. file_size: u64 = 0, - const LazyFns = std.AutoHashMapUnmanaged(codegen.LazyFnKey, DeclBlock); + const LazyFns = std.AutoHashMapUnmanaged(codegen.LazyFnKey, void); fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void { if (buf.len == 0) return; @@ -338,10 +362,9 @@ const Flush = struct { fn deinit(f: *Flush, gpa: Allocator) void { f.all_buffers.deinit(gpa); - var lazy_fns_it = f.lazy_fns.valueIterator(); - while (lazy_fns_it.next()) |db| db.deinit(gpa); + f.asm_buf.deinit(gpa); f.lazy_fns.deinit(gpa); - f.err_decls.deinit(gpa); + f.lazy_db.deinit(gpa); f.ctypes_buf.deinit(gpa); f.ctypes_map.deinit(gpa); f.ctypes.deinit(gpa); @@ -353,26 +376,106 @@ const FlushDeclError = error{ OutOfMemory, }; -fn flushCTypes(self: *C, f: *Flush, ctypes: codegen.CType.Store) FlushDeclError!void { - _ = self; - _ = f; - _ = ctypes; +fn flushCTypes( + self: *C, + f: *Flush, + decl_index: Module.Decl.OptionalIndex, + decl_ctypes: codegen.CType.Store, +) FlushDeclError!void { + const gpa = self.base.allocator; + const mod = self.base.options.module.?; + + const decl_ctypes_len = decl_ctypes.count(); + f.ctypes_map.clearRetainingCapacity(); + try f.ctypes_map.ensureTotalCapacity(gpa, decl_ctypes_len); + + var global_ctypes = f.ctypes.promote(gpa); + defer f.ctypes.demote(global_ctypes); + + var ctypes_buf = f.ctypes_buf.toManaged(gpa); + defer f.ctypes_buf = ctypes_buf.moveToUnmanaged(); + const writer = ctypes_buf.writer(); + + const slice = decl_ctypes.set.map.entries.slice(); + for (slice.items(.key), 0..) |decl_cty, decl_i| { + const Context = struct { + arena: Allocator, + ctypes_map: []codegen.CType.Index, + cached_hash: codegen.CType.Store.Set.Map.Hash, + idx: codegen.CType.Index, + + pub fn hash(ctx: @This(), _: codegen.CType) codegen.CType.Store.Set.Map.Hash { + return ctx.cached_hash; + } + pub fn eql(ctx: @This(), lhs: codegen.CType, rhs: codegen.CType, _: usize) bool { + return lhs.eqlContext(rhs, ctx); + } + pub fn eqlIndex( + ctx: @This(), + lhs_idx: codegen.CType.Index, + rhs_idx: codegen.CType.Index, + ) bool { + if (lhs_idx < codegen.CType.Tag.no_payload_count or + rhs_idx < codegen.CType.Tag.no_payload_count) return lhs_idx == rhs_idx; + const lhs_i = lhs_idx - codegen.CType.Tag.no_payload_count; + if (lhs_i >= ctx.ctypes_map.len) return false; + return ctx.ctypes_map[lhs_i] == rhs_idx; + } + pub fn copyIndex(ctx: @This(), idx: codegen.CType.Index) codegen.CType.Index { + if (idx < codegen.CType.Tag.no_payload_count) return idx; + return ctx.ctypes_map[idx - codegen.CType.Tag.no_payload_count]; + } + }; + const decl_idx = @intCast(codegen.CType.Index, codegen.CType.Tag.no_payload_count + decl_i); + const ctx = Context{ + .arena = global_ctypes.arena.allocator(), + .ctypes_map = f.ctypes_map.items, + .cached_hash = decl_ctypes.indexToHash(decl_idx), + .idx = decl_idx, + }; + const gop = try global_ctypes.set.map.getOrPutContextAdapted(gpa, decl_cty, ctx, .{ + .store = &global_ctypes.set, + }); + const global_idx = + @intCast(codegen.CType.Index, codegen.CType.Tag.no_payload_count + gop.index); + f.ctypes_map.appendAssumeCapacity(global_idx); + if (!gop.found_existing) { + errdefer _ = global_ctypes.set.map.pop(); + gop.key_ptr.* = try decl_cty.copyContext(ctx); + } + if (std.debug.runtime_safety) { + const global_cty = &global_ctypes.set.map.entries.items(.key)[gop.index]; + assert(global_cty == gop.key_ptr); + assert(decl_cty.eqlContext(global_cty.*, ctx)); + assert(decl_cty.hash(decl_ctypes.set) == global_cty.hash(global_ctypes.set)); + } + try codegen.genTypeDecl( + mod, + writer, + global_ctypes.set, + global_idx, + decl_index, + decl_ctypes.set, + decl_idx, + gop.found_existing, + ); + } } -fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { +fn flushErrDecls(self: *C, db: *DeclBlock) FlushDeclError!void { const gpa = self.base.allocator; - const fwd_decl = &f.err_decls.fwd_decl; - const ctypes = &f.err_decls.ctypes; - const code = &f.err_decls.code; + const fwd_decl = &db.fwd_decl; + const ctypes = &db.ctypes; + const code = &db.code; var object = codegen.Object{ .dg = .{ .gpa = gpa, .module = self.base.options.module.?, .error_msg = null, - .decl_index = undefined, - .decl = undefined, + .decl_index = .none, + .decl = null, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, }, @@ -394,19 +497,9 @@ fn flushErrDecls(self: *C, f: *Flush) FlushDeclError!void { fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); ctypes.* = object.dg.ctypes.move(); code.* = object.code.moveToUnmanaged(); - - try self.flushCTypes(f, ctypes.*); - try f.all_buffers.ensureUnusedCapacity(gpa, 2); - f.appendBufAssumeCapacity(fwd_decl.items); - f.appendBufAssumeCapacity(code.items); } -fn flushLazyFn( - self: *C, - f: *Flush, - db: *DeclBlock, - lazy_fn: codegen.LazyFnMap.Entry, -) FlushDeclError!void { +fn flushLazyFn(self: *C, db: *DeclBlock, lazy_fn: codegen.LazyFnMap.Entry) FlushDeclError!void { const gpa = self.base.allocator; const fwd_decl = &db.fwd_decl; @@ -418,8 +511,8 @@ fn flushLazyFn( .gpa = gpa, .module = self.base.options.module.?, .error_msg = null, - .decl_index = undefined, - .decl = undefined, + .decl_index = .none, + .decl = null, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, }, @@ -441,11 +534,6 @@ fn flushLazyFn( fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); ctypes.* = object.dg.ctypes.move(); code.* = object.code.moveToUnmanaged(); - - try self.flushCTypes(f, ctypes.*); - try f.all_buffers.ensureUnusedCapacity(gpa, 2); - f.appendBufAssumeCapacity(fwd_decl.items); - f.appendBufAssumeCapacity(code.items); } fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError!void { @@ -456,8 +544,8 @@ fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError while (it.next()) |entry| { const gop = f.lazy_fns.getOrPutAssumeCapacity(entry.key_ptr.*); if (gop.found_existing) continue; - gop.value_ptr.* = .{}; - try self.flushLazyFn(f, gop.value_ptr, entry); + gop.value_ptr.* = {}; + try self.flushLazyFn(&f.lazy_db, entry); } } @@ -481,7 +569,6 @@ fn flushDecl( const decl_block = self.decl_table.getPtr(decl_index).?; - try self.flushCTypes(f, decl_block.ctypes); try self.flushLazyFns(f, decl_block.lazy_fns); try f.all_buffers.ensureUnusedCapacity(gpa, 1); if (!(decl.isExtern() and export_names.contains(mem.span(decl.name)))) From 1a1598daf08fabe2b7e154dfb94f0f53e3577895 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 2 Jan 2023 03:35:56 -0500 Subject: [PATCH 092/122] hash map: remove extra argument --- lib/std/hash_map.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 78fcf68b56..164b81d651 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -508,7 +508,7 @@ pub fn HashMap( /// If a new entry needs to be stored, this function asserts there /// is enough capacity to store it. pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult { - return self.unmanaged.getOrPutAssumeCapacityAdapted(self.allocator, key, ctx); + return self.unmanaged.getOrPutAssumeCapacityAdapted(key, ctx); } pub fn getOrPutValue(self: *Self, key: K, value: V) Allocator.Error!Entry { @@ -2130,7 +2130,7 @@ test "std.hash_map getOrPutAdapted" { try testing.expectEqual(map.count(), keys.len); inline for (keys, 0..) |key_str, i| { - const result = try map.getOrPutAdapted(key_str, AdaptedContext{}); + const result = map.getOrPutAssumeCapacityAdapted(key_str, AdaptedContext{}); try testing.expect(result.found_existing); try testing.expectEqual(real_keys[i], result.key_ptr.*); try testing.expectEqual(@as(u64, i) * 2, result.value_ptr.*); From b76fed82061823fb1ed1f4ab81296135858ce26c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 3 Jan 2023 00:22:39 -0500 Subject: [PATCH 093/122] zig.h: get no int128 path working on non-msvc --- lib/zig.h | 90 ++++++++++++++++++++++--------------------------------- 1 file changed, 36 insertions(+), 54 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 5929656985..5ee8e3dd76 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -1357,10 +1357,10 @@ typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128; #define zig_make_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) }) #define zig_make_i128(hi, lo) ((zig_i128){ .h##i = (hi), .l##o = (lo) }) -#if _MSC_VER +#if _MSC_VER /* MSVC doesn't allow struct literals in constant expressions */ #define zig_make_constant_u128(hi, lo) { .h##i = (hi), .l##o = (lo) } #define zig_make_constant_i128(hi, lo) { .h##i = (hi), .l##o = (lo) } -#else +#else /* But non-MSVC doesn't like the unprotected commas */ #define zig_make_constant_u128(hi, lo) zig_make_u128(hi, lo) #define zig_make_constant_i128(hi, lo) zig_make_i128(hi, lo) #endif @@ -1421,6 +1421,11 @@ static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { return lhs << rhs; } +static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { + zig_i128 sign_mask = lhs < zig_make_i128(0, 0) ? -zig_make_i128(0, 1) : zig_make_i128(0, 0); + return ((lhs ^ sign_mask) >> rhs) ^ sign_mask; +} + static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { return lhs << rhs; } @@ -1496,6 +1501,12 @@ static inline zig_u128 zig_shl_u128(zig_u128 lhs, uint8_t rhs) { return (zig_u128){ .hi = lhs.hi << rhs | lhs.lo >> (UINT8_C(64) - rhs), .lo = lhs.lo << rhs }; } +static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { + if (rhs == UINT8_C(0)) return lhs; + if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = zig_shr_i64(lhs.hi, 63), .lo = zig_shr_i64(lhs.hi, (rhs - UINT8_C(64))) }; + return (zig_i128){ .hi = zig_shr_i64(lhs.hi, rhs), .lo = lhs.lo >> rhs | (uint64_t)lhs.hi << (UINT8_C(64) - rhs) }; +} + static inline zig_i128 zig_shl_i128(zig_i128 lhs, uint8_t rhs) { if (rhs == UINT8_C(0)) return lhs; if (rhs >= UINT8_C(64)) return (zig_i128){ .hi = lhs.lo << (rhs - UINT8_C(64)), .lo = zig_minInt_u64 }; @@ -1527,14 +1538,14 @@ static inline zig_i128 zig_sub_i128(zig_i128 lhs, zig_i128 rhs) { } zig_extern zig_i128 __multi3(zig_i128 lhs, zig_i128 rhs); -static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { - return zig_bitcast_u128(__multi3(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); -} - static zig_i128 zig_mul_i128(zig_i128 lhs, zig_i128 rhs) { return __multi3(lhs, rhs); } +static zig_u128 zig_mul_u128(zig_u128 lhs, zig_u128 rhs) { + return zig_bitcast_u128(zig_mul_i128(zig_bitcast_i128(lhs), zig_bitcast_i128(rhs))); +} + zig_extern zig_u128 __udivti3(zig_u128 lhs, zig_u128 rhs); static zig_u128 zig_div_trunc_u128(zig_u128 lhs, zig_u128 rhs) { return __udivti3(lhs, rhs); @@ -1557,7 +1568,7 @@ static zig_i128 zig_rem_i128(zig_i128 lhs, zig_i128 rhs) { static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) { zig_i128 rem = zig_rem_i128(lhs, rhs); - return zig_add_i128(rem, (((lhs.hi ^ rhs.hi) & rem.hi) < INT64_C(0) ? rhs : zig_make_i128(0, 0))); + return zig_add_i128(rem, ((lhs.hi ^ rhs.hi) & rem.hi) < INT64_C(0) ? rhs : zig_make_i128(0, 0)); } static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) { @@ -1589,11 +1600,6 @@ static inline zig_i128 zig_max_i128(zig_i128 lhs, zig_i128 rhs) { return zig_cmp_i128(lhs, rhs) > INT32_C(0) ? lhs : rhs; } -static inline zig_i128 zig_shr_i128(zig_i128 lhs, uint8_t rhs) { - zig_i128 sign_mask = zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_sub_i128(zig_make_i128(0, 0), zig_make_i128(0, 1)) : zig_make_i128(0, 0); - return zig_xor_i128(zig_bitcast_i128(zig_shr_u128(zig_bitcast_u128(zig_xor_i128(lhs, sign_mask)), rhs)), sign_mask); -} - static inline zig_u128 zig_wrap_u128(zig_u128 val, uint8_t bits) { return zig_and_u128(val, zig_maxInt_u(128, bits)); } @@ -1716,50 +1722,28 @@ static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint #else /* zig_has_int128 */ -static inline bool zig_overflow_u128(bool overflow, zig_u128 full_res, uint8_t bits) { - return overflow || - zig_cmp_u128(full_res, zig_minInt_u(128, bits)) < INT32_C(0) || - zig_cmp_u128(full_res, zig_maxInt_u(128, bits)) > INT32_C(0); -} - -static inline bool zig_overflow_i128(bool overflow, zig_i128 full_res, uint8_t bits) { - return overflow || - zig_cmp_i128(full_res, zig_minInt_i(128, bits)) < INT32_C(0) || - zig_cmp_i128(full_res, zig_maxInt_i(128, bits)) > INT32_C(0); -} - static inline bool zig_addo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { - zig_u128 full_res; - bool overflow = - zig_addo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | - zig_addo_u64(&full_res.hi, full_res.hi, zig_addo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); - *res = zig_wrap_u128(full_res, bits); - return zig_overflow_u128(overflow, full_res, bits); + uint64_t hi; + bool overflow = zig_addo_u64(&hi, lhs.hi, rhs.hi, bits - 64); + return overflow ^ zig_addo_u64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); } -zig_extern zig_i128 __addoti4(zig_i128 lhs, zig_i128 rhs, int *overflow); static inline bool zig_addo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { - int overflow_int; - zig_i128 full_res = __addoti4(lhs, rhs, &overflow_int); - *res = zig_wrap_i128(full_res, bits); - return zig_overflow_i128(overflow_int, full_res, bits); + int64_t hi; + bool overflow = zig_addo_i64(&hi, lhs.hi, rhs.hi, bits - 64); + return overflow ^ zig_addo_i64(&res->hi, hi, zig_addo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); } static inline bool zig_subo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { - zig_u128 full_res; - bool overflow = - zig_subo_u64(&full_res.hi, lhs.hi, rhs.hi, 64) | - zig_subo_u64(&full_res.hi, full_res.hi, zig_subo_u64(&full_res.lo, lhs.lo, rhs.lo, 64), 64); - *res = zig_wrap_u128(full_res, bits); - return zig_overflow_u128(overflow, full_res, bits); + uint64_t hi; + bool overflow = zig_subo_u64(&hi, lhs.hi, rhs.hi, bits - 64); + return overflow ^ zig_subo_u64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); } -zig_extern zig_i128 __suboti4(zig_i128 lhs, zig_i128 rhs, int *overflow); static inline bool zig_subo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { - int overflow_int; - zig_i128 full_res = __suboti4(lhs, rhs, &overflow_int); - *res = zig_wrap_i128(full_res, bits); - return zig_overflow_i128(overflow_int, full_res, bits); + int64_t hi; + bool overflow = zig_subo_i64(&hi, lhs.hi, rhs.hi, bits - 64); + return overflow ^ zig_subo_i64(&res->hi, hi, zig_subo_u64(&res->lo, lhs.lo, rhs.lo, 64), bits - 64); } static inline bool zig_mulo_u128(zig_u128 *res, zig_u128 lhs, zig_u128 rhs, uint8_t bits) { @@ -1772,8 +1756,11 @@ zig_extern zig_i128 __muloti4(zig_i128 lhs, zig_i128 rhs, int *overflow); static inline bool zig_mulo_i128(zig_i128 *res, zig_i128 lhs, zig_i128 rhs, uint8_t bits) { int overflow_int; zig_i128 full_res = __muloti4(lhs, rhs, &overflow_int); + bool overflow = overflow_int != 0 || + zig_cmp_i128(full_res, zig_minInt_i(128, bits)) < INT32_C(0) || + zig_cmp_i128(full_res, zig_maxInt_i(128, bits)) > INT32_C(0); *res = zig_wrap_i128(full_res, bits); - return zig_overflow_i128(overflow_int, full_res, bits); + return overflow; } #endif /* zig_has_int128 */ @@ -1794,17 +1781,12 @@ static inline zig_u128 zig_shls_u128(zig_u128 lhs, zig_u128 rhs, uint8_t bits) { zig_u128 res; if (zig_cmp_u128(rhs, zig_make_u128(0, bits)) >= INT32_C(0)) return zig_cmp_u128(lhs, zig_make_u128(0, 0)) != INT32_C(0) ? zig_maxInt_u(128, bits) : lhs; - -#if zig_has_int128 - return zig_shlo_u128(&res, lhs, (uint8_t)rhs, bits) ? zig_maxInt_u(128, bits) : res; -#else - return zig_shlo_u128(&res, lhs, (uint8_t)rhs.lo, bits) ? zig_maxInt_u(128, bits) : res; -#endif + return zig_shlo_u128(&res, lhs, (uint8_t)zig_lo_u128(rhs), bits) ? zig_maxInt_u(128, bits) : res; } static inline zig_i128 zig_shls_i128(zig_i128 lhs, zig_i128 rhs, uint8_t bits) { zig_i128 res; - if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, zig_lo_i128(rhs), bits)) return res; + if (zig_cmp_u128(zig_bitcast_u128(rhs), zig_make_u128(0, bits)) < INT32_C(0) && !zig_shlo_i128(&res, lhs, (uint8_t)zig_lo_i128(rhs), bits)) return res; return zig_cmp_i128(lhs, zig_make_i128(0, 0)) < INT32_C(0) ? zig_minInt_i(128, bits) : zig_maxInt_i(128, bits); } From 828ac637b2703bfd3e40316a28e1f6b8315c6ed1 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 3 Jan 2023 00:29:31 -0500 Subject: [PATCH 094/122] MultiArrayList: delete pessimizing vector usage By factoring out the comptime parts of this computation, vectors are no longer useful in this function. --- lib/std/multi_array_list.zig | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index afdd6a5a8d..56b36aaa81 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -433,15 +433,9 @@ pub fn MultiArrayList(comptime S: type) type { } fn capacityInBytes(capacity: usize) usize { - if (builtin.zig_backend == .stage2_c) { - var bytes: usize = 0; - for (sizes.bytes) |size| bytes += size * capacity; - return bytes; - } else { - const sizes_vector: @Vector(sizes.bytes.len, usize) = sizes.bytes; - const capacity_vector = @splat(sizes.bytes.len, capacity); - return @reduce(.Add, capacity_vector * sizes_vector); - } + comptime var elem_bytes: usize = 0; + inline for (sizes.bytes) |size| elem_bytes += size; + return elem_bytes * capacity; } fn allocatedBytes(self: Self) []align(@alignOf(S)) u8 { From 25a3c933b9d708d907293b1a46b6661641ccf9ea Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 21 Feb 2023 02:21:39 -0500 Subject: [PATCH 095/122] CBE: fix test failures --- lib/zig.h | 8 ++++---- src/codegen/c.zig | 25 ++++++++++++++----------- test/stage2/cbe.zig | 12 ++++++------ 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 5ee8e3dd76..9a5e751f79 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -288,13 +288,13 @@ typedef char bool; #endif #if __STDC_VERSION__ >= 201112L -#define zig_noreturn _Noreturn void +#define zig_noreturn _Noreturn #elif zig_has_attribute(noreturn) || defined(zig_gnuc) -#define zig_noreturn __attribute__((noreturn)) void +#define zig_noreturn __attribute__((noreturn)) #elif _MSC_VER -#define zig_noreturn __declspec(noreturn) void +#define zig_noreturn __declspec(noreturn) #else -#define zig_noreturn void +#define zig_noreturn #endif #define zig_bitSizeOf(T) (CHAR_BIT * sizeof(T)) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 35c826e2d1..4ddfe3213a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1476,6 +1476,7 @@ pub const DeclGen = struct { } if (dg.decl.?.val.castTag(.function)) |func_payload| if (func_payload.data.is_cold) try w.writeAll("zig_cold "); + if (fn_info.return_type.tag() == .noreturn) try w.writeAll("zig_noreturn "); const trailing = try renderTypePrefix( dg.decl_index, @@ -2289,7 +2290,7 @@ fn renderTypeSuffix( w, param_type, .suffix, - CQualifiers.init(.{}), + CQualifiers.init(.{ .@"const" = true }), ); try w.print("{}a{d}", .{ trailing, param_i }); try renderTypeSuffix(decl, store, mod, w, param_type, .suffix); @@ -5737,26 +5738,28 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(writer, local, .Other); - const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen(); + const array_ty = f.air.typeOf(ty_op.operand).childType(); - try writer.writeAll(".ptr = "); + try f.writeCValueMember(writer, local, .{ .identifier = "ptr" }); + try writer.writeAll(" = "); + // Unfortunately, C does not support any equivalent to + // &(*(void *)p)[0], although LLVM does via GetElementPtr if (operand == .undef) { - // Unfortunately, C does not support any equivalent to - // &(*(void *)p)[0], although LLVM does via GetElementPtr var buf: Type.SlicePtrFieldTypeBuffer = undefined; try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); - } else { + } else if (array_ty.hasRuntimeBitsIgnoreComptime()) { try writer.writeAll("&("); try f.writeCValueDeref(writer, operand); try writer.print(")[{}]", .{try f.fmtIntLiteral(Type.usize, Value.zero)}); - } + } else try f.writeCValue(writer, operand, .Initializer); + try writer.writeAll("; "); + const array_len = array_ty.arrayLen(); var len_pl: Value.Payload.U64 = .{ .base = .{ .tag = .int_u64 }, .data = array_len }; const len_val = Value.initPayload(&len_pl.base); - try writer.writeAll("; "); - try f.writeCValue(writer, local, .Other); - try writer.print(".len = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)}); + try f.writeCValueMember(writer, local, .{ .identifier = "len" }); + try writer.print(" = {};\n", .{try f.fmtIntLiteral(Type.usize, len_val)}); + return local; } diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 6c0c5e03cf..e9750853a6 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -959,7 +959,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = a; \\} , - \\zig_extern void start(zig_u8 const a0); + \\zig_extern void start(uint8_t const a0); \\ ); ctx.h("header with multiple param function", linux_x64, @@ -967,19 +967,19 @@ pub fn addCases(ctx: *TestContext) !void { \\ _ = a; _ = b; _ = c; \\} , - \\zig_extern void start(zig_u8 const a0, zig_u8 const a1, zig_u8 const a2); + \\zig_extern void start(uint8_t const a0, uint8_t const a1, uint8_t const a2); \\ ); ctx.h("header with u32 param function", linux_x64, \\export fn start(a: u32) void{ _ = a; } , - \\zig_extern void start(zig_u32 const a0); + \\zig_extern void start(uint32_t const a0); \\ ); ctx.h("header with usize param function", linux_x64, \\export fn start(a: usize) void{ _ = a; } , - \\zig_extern void start(zig_usize const a0); + \\zig_extern void start(uintptr_t const a0); \\ ); ctx.h("header with bool param function", linux_x64, @@ -993,7 +993,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ unreachable; \\} , - \\zig_extern zig_noreturn start(void); + \\zig_extern zig_noreturn void start(void); \\ ); ctx.h("header with multiple functions", linux_x64, @@ -1009,7 +1009,7 @@ pub fn addCases(ctx: *TestContext) !void { ctx.h("header with multiple includes", linux_x64, \\export fn start(a: u32, b: usize) void{ _ = a; _ = b; } , - \\zig_extern void start(zig_u32 const a0, zig_usize const a1); + \\zig_extern void start(uint32_t const a0, uintptr_t const a1); \\ ); } From 32cf1d7cbf7852b7c66d1c026b0003690e9f7337 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 21 Feb 2023 17:14:45 +1100 Subject: [PATCH 096/122] std.compress.zstandard: fix error sets for streaming API --- lib/std/compress/zstandard/decode/huffman.zig | 28 +++++++++++++++---- lib/std/compress/zstandard/decompress.zig | 6 ++-- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/std/compress/zstandard/decode/huffman.zig b/lib/std/compress/zstandard/decode/huffman.zig index 68aac85320..2914198268 100644 --- a/lib/std/compress/zstandard/decode/huffman.zig +++ b/lib/std/compress/zstandard/decode/huffman.zig @@ -15,7 +15,12 @@ pub const Error = error{ EndOfStream, }; -fn decodeFseHuffmanTree(source: anytype, compressed_size: usize, buffer: []u8, weights: *[256]u4) !usize { +fn decodeFseHuffmanTree( + source: anytype, + compressed_size: usize, + buffer: []u8, + weights: *[256]u4, +) !usize { var stream = std.io.limitedReader(source, compressed_size); var bit_reader = readers.bitReader(stream.reader()); @@ -23,6 +28,7 @@ fn decodeFseHuffmanTree(source: anytype, compressed_size: usize, buffer: []u8, w const table_size = decodeFseTable(&bit_reader, 256, 6, &entries) catch |err| switch (err) { error.MalformedAccuracyLog, error.MalformedFseTable => |e| return e, error.EndOfStream => return error.MalformedFseTable, + else => |e| return e, }; const accuracy_log = std.math.log2_int_ceil(usize, table_size); @@ -46,7 +52,8 @@ fn decodeFseHuffmanTreeSlice(src: []const u8, compressed_size: usize, weights: * }; const accuracy_log = std.math.log2_int_ceil(usize, table_size); - const start_index = std.math.cast(usize, counting_reader.bytes_read) orelse return error.MalformedHuffmanTree; + const start_index = std.math.cast(usize, counting_reader.bytes_read) orelse + return error.MalformedHuffmanTree; var huff_data = src[start_index..compressed_size]; var huff_bits: readers.ReverseBitReader = undefined; huff_bits.init(huff_data) catch return error.MalformedHuffmanTree; @@ -54,7 +61,12 @@ fn decodeFseHuffmanTreeSlice(src: []const u8, compressed_size: usize, weights: * return assignWeights(&huff_bits, accuracy_log, &entries, weights); } -fn assignWeights(huff_bits: *readers.ReverseBitReader, accuracy_log: usize, entries: *[1 << 6]Table.Fse, weights: *[256]u4) !usize { +fn assignWeights( + huff_bits: *readers.ReverseBitReader, + accuracy_log: usize, + entries: *[1 << 6]Table.Fse, + weights: *[256]u4, +) !usize { var i: usize = 0; var even_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; var odd_state: u32 = huff_bits.readBitsNoEof(u32, accuracy_log) catch return error.MalformedHuffmanTree; @@ -173,7 +185,10 @@ fn buildHuffmanTree(weights: *[256]u4, symbol_count: usize) error{MalformedHuffm return tree; } -pub fn decodeHuffmanTree(source: anytype, buffer: []u8) !LiteralsSection.HuffmanTree { +pub fn decodeHuffmanTree( + source: anytype, + buffer: []u8, +) (@TypeOf(source).Error || Error)!LiteralsSection.HuffmanTree { const header = try source.readByte(); var weights: [256]u4 = undefined; const symbol_count = if (header < 128) @@ -185,7 +200,10 @@ pub fn decodeHuffmanTree(source: anytype, buffer: []u8) !LiteralsSection.Huffman return buildHuffmanTree(&weights, symbol_count); } -pub fn decodeHuffmanTreeSlice(src: []const u8, consumed_count: *usize) Error!LiteralsSection.HuffmanTree { +pub fn decodeHuffmanTreeSlice( + src: []const u8, + consumed_count: *usize, +) Error!LiteralsSection.HuffmanTree { if (src.len == 0) return error.MalformedHuffmanTree; const header = src[0]; var bytes_read: usize = 1; diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig index ffa01b94f1..a2ba59e688 100644 --- a/lib/std/compress/zstandard/decompress.zig +++ b/lib/std/compress/zstandard/decompress.zig @@ -64,7 +64,7 @@ pub const HeaderError = error{ BadMagic, EndOfStream, ReservedBitSet }; /// - `error.EndOfStream` if `source` contains fewer than 4 bytes /// - `error.ReservedBitSet` if the frame is a Zstandard frame and any of the /// reserved bits are set -pub fn decodeFrameHeader(source: anytype) HeaderError!FrameHeader { +pub fn decodeFrameHeader(source: anytype) (@TypeOf(source).Error || HeaderError)!FrameHeader { const magic = try source.readIntLittle(u32); const frame_type = try frameType(magic); switch (frame_type) { @@ -596,7 +596,9 @@ pub fn frameWindowSize(header: ZstandardHeader) ?u64 { /// Errors returned: /// - `error.ReservedBitSet` if any of the reserved bits of the header are set /// - `error.EndOfStream` if `source` does not contain a complete header -pub fn decodeZstandardHeader(source: anytype) error{ EndOfStream, ReservedBitSet }!ZstandardHeader { +pub fn decodeZstandardHeader( + source: anytype, +) (@TypeOf(source).Error || error{ EndOfStream, ReservedBitSet })!ZstandardHeader { const descriptor = @bitCast(ZstandardHeader.Descriptor, try source.readByte()); if (descriptor.reserved) return error.ReservedBitSet; From 765a6d34139771cb94c73e0af71b02db2f1e0f98 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Wed, 22 Feb 2023 00:11:20 +1100 Subject: [PATCH 097/122] std.compress.zstd: renamed from std.compress.zstandard --- lib/std/compress.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/compress.zig b/lib/std/compress.zig index 02e17474a1..7e81d9deba 100644 --- a/lib/std/compress.zig +++ b/lib/std/compress.zig @@ -6,7 +6,7 @@ pub const lzma = @import("compress/lzma.zig"); pub const lzma2 = @import("compress/lzma2.zig"); pub const xz = @import("compress/xz.zig"); pub const zlib = @import("compress/zlib.zig"); -pub const zstandard = @import("compress/zstandard.zig"); +pub const zstd = @import("compress/zstandard.zig"); pub fn HashedReader( comptime ReaderType: anytype, @@ -45,5 +45,5 @@ test { _ = lzma2; _ = xz; _ = zlib; - _ = zstandard; + _ = zstd; } From 05da5b32a820c031001098034840940964f41a81 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 20 Feb 2023 23:31:48 +0100 Subject: [PATCH 098/122] Sema: implement @fieldParentPtr for unions --- src/Sema.zig | 54 ++++++++----- src/arch/arm/CodeGen.zig | 5 ++ src/arch/wasm/CodeGen.zig | 4 +- src/codegen/c.zig | 6 ++ src/codegen/llvm.zig | 4 +- test/behavior/field_parent_ptr.zig | 81 +++++++++++++++++++ .../fieldParentPtr-non_struct.zig | 2 +- 7 files changed, 131 insertions(+), 25 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 41e5fdc20e..40a4a114b4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21482,24 +21482,32 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; - const struct_ty = try sema.resolveType(block, ty_src, extra.parent_type); + const parent_ty = try sema.resolveType(block, ty_src, extra.parent_type); const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime-known"); const field_ptr = try sema.resolveInst(extra.field_ptr); const field_ptr_ty = sema.typeOf(field_ptr); - if (struct_ty.zigTypeTag() != .Struct) { - return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(sema.mod)}); + if (parent_ty.zigTypeTag() != .Struct and parent_ty.zigTypeTag() != .Union) { + return sema.fail(block, ty_src, "expected struct or union type, found '{}'", .{parent_ty.fmt(sema.mod)}); } - try sema.resolveTypeLayout(struct_ty); + try sema.resolveTypeLayout(parent_ty); - const field_index = if (struct_ty.isTuple()) blk: { - if (mem.eql(u8, field_name, "len")) { - return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); - } - break :blk try sema.tupleFieldIndex(block, struct_ty, field_name, name_src); - } else try sema.structFieldIndex(block, struct_ty, field_name, name_src); + const field_index = switch (parent_ty.zigTypeTag()) { + .Struct => blk: { + if (parent_ty.isTuple()) { + if (mem.eql(u8, field_name, "len")) { + return sema.fail(block, src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); + } + break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, name_src); + } else { + break :blk try sema.structFieldIndex(block, parent_ty, field_name, name_src); + } + }, + .Union => try sema.unionFieldIndex(block, parent_ty, field_name, name_src), + else => unreachable, + }; - if (struct_ty.structFieldIsComptime(field_index)) { + if (parent_ty.zigTypeTag() == .Struct and parent_ty.structFieldIsComptime(field_index)) { return sema.fail(block, src, "cannot get @fieldParentPtr of a comptime field", .{}); } @@ -21507,23 +21515,29 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const field_ptr_ty_info = field_ptr_ty.ptrInfo().data; var ptr_ty_data: Type.Payload.Pointer.Data = .{ - .pointee_type = struct_ty.structFieldType(field_index), + .pointee_type = parent_ty.structFieldType(field_index), .mutable = field_ptr_ty_info.mutable, .@"addrspace" = field_ptr_ty_info.@"addrspace", }; - if (struct_ty.containerLayout() == .Packed) { - return sema.fail(block, src, "TODO handle packed structs with @fieldParentPtr", .{}); + if (parent_ty.containerLayout() == .Packed) { + return sema.fail(block, src, "TODO handle packed structs/unions with @fieldParentPtr", .{}); } else { - ptr_ty_data.@"align" = if (struct_ty.castTag(.@"struct")) |struct_obj| b: { - break :b struct_obj.data.fields.values()[field_index].abi_align; - } else 0; + ptr_ty_data.@"align" = blk: { + if (parent_ty.castTag(.@"struct")) |struct_obj| { + break :blk struct_obj.data.fields.values()[field_index].abi_align; + } else if (parent_ty.cast(Type.Payload.Union)) |union_obj| { + break :blk union_obj.data.fields.values()[field_index].abi_align; + } else { + break :blk 0; + } + }; } const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src); - ptr_ty_data.pointee_type = struct_ty; + ptr_ty_data.pointee_type = parent_ty; const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data); if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| { @@ -21540,11 +21554,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr field_name, field_index, payload.data.field_index, - struct_ty.fmt(sema.mod), + parent_ty.fmt(sema.mod), }, ); errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, struct_ty); + try sema.addDeclaredHereNote(msg, parent_ty); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index fc89b2e26b..01a1d6b7eb 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -2973,6 +2973,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const field_ptr = try self.resolveInst(extra.field_ptr); const struct_ty = self.air.getRefType(ty_pl.ty).childType(); + + if (struct_ty.zigTypeTag() == .Union) { + return self.fail("TODO implement @fieldParentPtr codegen for unions", .{}); + } + const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*)); switch (field_ptr) { .ptr_stack_offset => |off| { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b229a67e70..2f191fd834 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -4944,8 +4944,8 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { if (func.liveness.isUnused(inst)) return func.finishAir(inst, .none, &.{extra.field_ptr}); const field_ptr = try func.resolveInst(extra.field_ptr); - const struct_ty = func.air.getRefType(ty_pl.ty).childType(); - const field_offset = struct_ty.structFieldOffset(extra.field_index, func.target); + const parent_ty = func.air.getRefType(ty_pl.ty).childType(); + const field_offset = parent_ty.structFieldOffset(extra.field_index, func.target); const result = if (field_offset != 0) result: { const base = try func.buildPointerOffset(field_ptr, 0, .new); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 0beb00b236..399549da3a 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -5367,12 +5367,18 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { } const struct_ptr_ty = f.air.typeOfIndex(inst); + const field_ptr_ty = f.air.typeOf(extra.field_ptr); const field_ptr_val = try f.resolveInst(extra.field_ptr); try reap(f, inst, &.{extra.field_ptr}); const target = f.object.dg.module.getTarget(); const struct_ty = struct_ptr_ty.childType(); + + if (struct_ty.zigTypeTag() == .Union) { + return f.fail("TODO: CBE: @fieldParentPtr for unions", .{}); + } + const field_offset = struct_ty.structFieldOffset(extra.field_index, target); var field_offset_pl = Value.Payload.I64{ diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index aa794827a8..558556f108 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6020,8 +6020,8 @@ pub const FuncGen = struct { const field_ptr = try self.resolveInst(extra.field_ptr); const target = self.dg.module.getTarget(); - const struct_ty = self.air.getRefType(ty_pl.ty).childType(); - const field_offset = struct_ty.structFieldOffset(extra.field_index, target); + const parent_ty = self.air.getRefType(ty_pl.ty).childType(); + const field_offset = parent_ty.structFieldOffset(extra.field_index, target); const res_ty = try self.dg.lowerType(self.air.getRefType(ty_pl.ty)); if (field_offset == 0) { diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig index 14a2362f5d..b9e171db57 100644 --- a/test/behavior/field_parent_ptr.zig +++ b/test/behavior/field_parent_ptr.zig @@ -44,3 +44,84 @@ fn testParentFieldPtrFirst(a: *const bool) !void { try expect(base == &foo); try expect(&base.a == a); } + +test "@fieldParentPtr untagged union" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + try testFieldParentPtrUnion(&bar.c); + comptime try testFieldParentPtrUnion(&bar.c); +} + +const Bar = union(enum) { + a: bool, + b: f32, + c: i32, + d: i32, +}; + +const bar = Bar{ .c = 42 }; + +fn testFieldParentPtrUnion(c: *const i32) !void { + try expect(c == &bar.c); + + const base = @fieldParentPtr(Bar, "c", c); + try expect(base == &bar); + try expect(&base.c == c); +} + +test "@fieldParentPtr tagged union" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + try testFieldParentPtrTaggedUnion(&bar_tagged.c); + comptime try testFieldParentPtrTaggedUnion(&bar_tagged.c); +} + +const BarTagged = union(enum) { + a: bool, + b: f32, + c: i32, + d: i32, +}; + +const bar_tagged = BarTagged{ .c = 42 }; + +fn testFieldParentPtrTaggedUnion(c: *const i32) !void { + try expect(c == &bar_tagged.c); + + const base = @fieldParentPtr(BarTagged, "c", c); + try expect(base == &bar_tagged); + try expect(&base.c == c); +} + +test "@fieldParentPtr extern union" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + try testFieldParentPtrExternUnion(&bar_extern.c); + comptime try testFieldParentPtrExternUnion(&bar_extern.c); +} + +const BarExtern = extern union { + a: bool, + b: f32, + c: i32, + d: i32, +}; + +const bar_extern = BarExtern{ .c = 42 }; + +fn testFieldParentPtrExternUnion(c: *const i32) !void { + try expect(c == &bar_extern.c); + + const base = @fieldParentPtr(BarExtern, "c", c); + try expect(base == &bar_extern); + try expect(&base.c == c); +} diff --git a/test/cases/compile_errors/fieldParentPtr-non_struct.zig b/test/cases/compile_errors/fieldParentPtr-non_struct.zig index 0a2f46e5c9..7950c88537 100644 --- a/test/cases/compile_errors/fieldParentPtr-non_struct.zig +++ b/test/cases/compile_errors/fieldParentPtr-non_struct.zig @@ -7,4 +7,4 @@ export fn foo(a: *i32) *Foo { // backend=llvm // target=native // -// :3:28: error: expected struct type, found 'i32' +// :3:28: error: expected struct or union type, found 'i32' From 434c6f42cad5aef9112b576c685521a9763bef9b Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 21 Feb 2023 09:43:00 -0500 Subject: [PATCH 099/122] behavior: enable passing CBE tests --- test/behavior/align.zig | 6 +++++- test/behavior/asm.zig | 5 ----- test/behavior/int_comparison_elision.zig | 1 - test/behavior/lower_strlit_to_vector.zig | 1 - test/behavior/math.zig | 1 - test/behavior/struct.zig | 1 - test/behavior/vector.zig | 5 ----- 7 files changed, 5 insertions(+), 15 deletions(-) diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 162c798758..901ea3697a 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -551,7 +551,11 @@ test "align(N) on functions" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO this is not supported on MSVC + + // This is not supported on MSVC + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) { + return error.SkipZigTest; + } // function alignment is a compile error on wasm32/wasm64 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index f041963494..e9a01226b1 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -23,7 +23,6 @@ test "module level assembly" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (is_x86_64_linux) { try expect(this_is_my_alias() == 1234); @@ -36,7 +35,6 @@ test "output constraint modifiers" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO // This is only testing compilation. var a: u32 = 3; @@ -58,7 +56,6 @@ test "alternative constraints" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO // Make sure we allow commas as a separator for alternative constraints. var a: u32 = 3; @@ -75,7 +72,6 @@ test "sized integer/float in asm input" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO asm volatile ("" : @@ -125,7 +121,6 @@ test "struct/array/union types as input values" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO asm volatile ("" : diff --git a/test/behavior/int_comparison_elision.zig b/test/behavior/int_comparison_elision.zig index 5e13e00e83..ea26f02b7e 100644 --- a/test/behavior/int_comparison_elision.zig +++ b/test/behavior/int_comparison_elision.zig @@ -13,7 +13,6 @@ test "int comparison elision" { // TODO: support int types > 128 bits wide in other backends if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/lower_strlit_to_vector.zig b/test/behavior/lower_strlit_to_vector.zig index adbca8f0df..427379636e 100644 --- a/test/behavior/lower_strlit_to_vector.zig +++ b/test/behavior/lower_strlit_to_vector.zig @@ -7,7 +7,6 @@ test "strlit to vector" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO const strlit = "0123456789abcdef0123456789ABCDEF"; const vec_from_strlit: @Vector(32, u8) = strlit.*; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 8ab8614605..54263e1daf 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1463,7 +1463,6 @@ test "vector integer addition" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index 8a01d68587..348e269682 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1330,7 +1330,6 @@ test "struct field init value is size of the struct" { } test "under-aligned struct field" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index e983e0cfb0..191c7bf7eb 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -75,7 +75,6 @@ test "vector int operators" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -178,7 +177,6 @@ test "tuple to vector" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) { @@ -943,7 +941,6 @@ test "multiplication-assignment operator with an array operand" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { @@ -1247,7 +1244,6 @@ test "array operands to shuffle are coerced to vectors" { test "load packed vector element" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO @@ -1260,7 +1256,6 @@ test "load packed vector element" { test "store packed vector element" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO From 2737dce84f99a05af51b66fc12794b43dc10fa41 Mon Sep 17 00:00:00 2001 From: Komari Spaghetti Date: Tue, 21 Feb 2023 18:26:55 +0100 Subject: [PATCH 100/122] Introduce ChildProcess.collectOutput (#12295) All the code for this function already exists, but only ChildProcess.exec was allowed to use the code. --- lib/std/child_process.zig | 57 ++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 07dd1f27f5..c3bd53b880 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -197,6 +197,32 @@ pub const ChildProcess = struct { stderr: []u8, }; + /// Collect the output from the process's stdout and stderr. Will return once all output + /// has been collected. This does not mean that the process has ended. `wait` should still + /// be called to wait for and clean up the process. + /// + /// The process must be started with stdout_behavior and stderr_behavior == .Pipe + pub fn collectOutput( + child: ChildProcess, + stdout: *std.ArrayList(u8), + stderr: *std.ArrayList(u8), + max_output_bytes: usize, + ) !void { + debug.assert(child.stdout_behavior == .Pipe); + debug.assert(child.stderr_behavior == .Pipe); + if (builtin.os.tag == .haiku) { + const stdout_in = child.stdout.?.reader(); + const stderr_in = child.stderr.?.reader(); + + try stdout_in.readAllArrayList(stdout, max_output_bytes); + try stderr_in.readAllArrayList(stderr, max_output_bytes); + } else if (builtin.os.tag == .windows) { + try collectOutputWindows(child, stdout, stderr, max_output_bytes); + } else { + try collectOutputPosix(child, stdout, stderr, max_output_bytes); + } + } + fn collectOutputPosix( child: ChildProcess, stdout: *std.ArrayList(u8), @@ -297,8 +323,12 @@ pub const ChildProcess = struct { } } - fn collectOutputWindows(child: ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void { + fn collectOutputWindows(child: ChildProcess, stdout: *std.ArrayList(u8), stderr: *std.ArrayList(u8), max_output_bytes: usize) !void { const bump_amt = 512; + const outs = [_]*std.ArrayList(u8){ + stdout, + stderr, + }; const handles = [_]windows.HANDLE{ child.stdout.?.handle, child.stderr.?.handle, @@ -391,24 +421,6 @@ pub const ChildProcess = struct { child.env_map = args.env_map; child.expand_arg0 = args.expand_arg0; - try child.spawn(); - - if (builtin.os.tag == .haiku) { - const stdout_in = child.stdout.?.reader(); - const stderr_in = child.stderr.?.reader(); - - const stdout = try stdout_in.readAllAlloc(args.allocator, args.max_output_bytes); - errdefer args.allocator.free(stdout); - const stderr = try stderr_in.readAllAlloc(args.allocator, args.max_output_bytes); - errdefer args.allocator.free(stderr); - - return ExecResult{ - .term = try child.wait(), - .stdout = stdout, - .stderr = stderr, - }; - } - var stdout = std.ArrayList(u8).init(args.allocator); var stderr = std.ArrayList(u8).init(args.allocator); errdefer { @@ -416,11 +428,8 @@ pub const ChildProcess = struct { stderr.deinit(); } - if (builtin.os.tag == .windows) { - try collectOutputWindows(child, [_]*std.ArrayList(u8){ &stdout, &stderr }, args.max_output_bytes); - } else { - try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes); - } + try child.spawn(); + try child.collectOutput(&stdout, &stderr, args.max_output_bytes); return ExecResult{ .term = try child.wait(), From 98dd041d536aad1d4936353b4f5e4a2e0aab0fe1 Mon Sep 17 00:00:00 2001 From: Alexis Brodeur Date: Fri, 2 Sep 2022 20:56:36 -0400 Subject: [PATCH 101/122] Relax `std.sort.binarySearch` requirements Forcing the key to be of the same type as the sorted items used during the search is a valid use case. There, however, exists some cases where the key and the items are of heterogeneous types, like searching for a code point in ordered ranges of code points: ```zig const CodePoint = u21; const CodePointRange = [2]CodePoint; const valid_ranges = &[_]CodePointRange{ // an ordered array of ranges }; fn orderCodePointAndRange( context: void, code_point: CodePoint, range: CodePointRange ) std.math.Order { _ = context; if (code_point < range[0]) { return .lt; } if (code_point > range[1]) { return .gt; } return .eq; } fn isValidCodePoint(code_point: CodePoint) bool { return std.sort.binarySearch( CodePointRange, code_point, valid_ranges, void, orderCodePointAndRange ) != null; } ``` It is so expected that `std.sort.binarySearch` should therefore support both homogeneous and heterogeneous keys. --- lib/std/sort.zig | 54 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/lib/std/sort.zig b/lib/std/sort.zig index fa1e33e7ce..405ab658d1 100644 --- a/lib/std/sort.zig +++ b/lib/std/sort.zig @@ -6,10 +6,10 @@ const math = std.math; pub fn binarySearch( comptime T: type, - key: T, + key: anytype, items: []const T, context: anytype, - comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order, + comptime compareFn: fn (context: @TypeOf(context), key: @TypeOf(key), mid: T) math.Order, ) ?usize { var left: usize = 0; var right: usize = items.len; @@ -41,35 +41,69 @@ test "binarySearch" { }; try testing.expectEqual( @as(?usize, null), - binarySearch(u32, 1, &[_]u32{}, {}, S.order_u32), + binarySearch(u32, @as(u32, 1), &[_]u32{}, {}, S.order_u32), ); try testing.expectEqual( @as(?usize, 0), - binarySearch(u32, 1, &[_]u32{1}, {}, S.order_u32), + binarySearch(u32, @as(u32, 1), &[_]u32{1}, {}, S.order_u32), ); try testing.expectEqual( @as(?usize, null), - binarySearch(u32, 1, &[_]u32{0}, {}, S.order_u32), + binarySearch(u32, @as(u32, 1), &[_]u32{0}, {}, S.order_u32), ); try testing.expectEqual( @as(?usize, null), - binarySearch(u32, 0, &[_]u32{1}, {}, S.order_u32), + binarySearch(u32, @as(u32, 0), &[_]u32{1}, {}, S.order_u32), ); try testing.expectEqual( @as(?usize, 4), - binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32), + binarySearch(u32, @as(u32, 5), &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32), ); try testing.expectEqual( @as(?usize, 0), - binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32), + binarySearch(u32, @as(u32, 2), &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32), ); try testing.expectEqual( @as(?usize, 1), - binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32), + binarySearch(i32, @as(i32, -4), &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32), ); try testing.expectEqual( @as(?usize, 3), - binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32), + binarySearch(i32, @as(i32, 98), &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32), + ); + const R = struct { + b: i32, + e: i32, + + fn r(b: i32, e: i32) @This() { + return @This(){ .b = b, .e = e }; + } + + fn order(context: void, key: i32, mid: @This()) math.Order { + _ = context; + + if (key < mid.b) { + return .lt; + } + + if (key > mid.e) { + return .gt; + } + + return .eq; + } + }; + try testing.expectEqual( + @as(?usize, null), + binarySearch(R, @as(i32, -45), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order), + ); + try testing.expectEqual( + @as(?usize, 2), + binarySearch(R, @as(i32, 10), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order), + ); + try testing.expectEqual( + @as(?usize, 1), + binarySearch(R, @as(i32, -20), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order), ); } From 248fb40dcc5eb50cf19e711197c5d1b210abf1b3 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Tue, 21 Feb 2023 15:05:41 -0500 Subject: [PATCH 102/122] CBE: fix windows test failures --- lib/zig.h | 131 +++++++++++++++++++++-------------------- src/codegen/c.zig | 78 ++++++++++++------------ src/codegen/c/type.zig | 9 ++- test/behavior/asm.zig | 13 ++++ 4 files changed, 129 insertions(+), 102 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 9a5e751f79..d336ecb2e2 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -316,7 +316,7 @@ zig_extern void *memset (void *, int, size_t); /* ===================== 8/16/32/64-bit Integer Support ===================== */ -#if __STDC_VERSION__ >= 199901L +#if __STDC_VERSION__ >= 199901L || _MSC_VER #include #else @@ -1923,6 +1923,7 @@ typedef double zig_f16; #define zig_make_f16(fp, repr) fp #elif LDBL_MANT_DIG == 11 #define zig_bitSizeOf_c_longdouble 16 +typedef uint16_t zig_repr_c_longdouble; typedef long double zig_f16; #define zig_make_f16(fp, repr) fp##l #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc)) @@ -1959,6 +1960,7 @@ typedef double zig_f32; #define zig_make_f32(fp, repr) fp #elif LDBL_MANT_DIG == 24 #define zig_bitSizeOf_c_longdouble 32 +typedef uint32_t zig_repr_c_longdouble; typedef long double zig_f32; #define zig_make_f32(fp, repr) fp##l #elif FLT32_MANT_DIG == 24 @@ -1982,6 +1984,7 @@ typedef int32_t zig_f32; #if _MSC_VER #ifdef ZIG_TARGET_ABI_MSVC #define zig_bitSizeOf_c_longdouble 64 +typedef uint64_t zig_repr_c_longdouble; #endif #define zig_make_special_constant_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, ) #else /* _MSC_VER */ @@ -1995,6 +1998,7 @@ typedef double zig_f64; #define zig_make_f64(fp, repr) fp #elif LDBL_MANT_DIG == 53 #define zig_bitSizeOf_c_longdouble 64 +typedef uint64_t zig_repr_c_longdouble; typedef long double zig_f64; #define zig_make_f64(fp, repr) fp##l #elif FLT64_MANT_DIG == 53 @@ -2027,6 +2031,7 @@ typedef double zig_f80; #define zig_make_f80(fp, repr) fp #elif LDBL_MANT_DIG == 64 #define zig_bitSizeOf_c_longdouble 80 +typedef zig_u128 zig_repr_c_longdouble; typedef long double zig_f80; #define zig_make_f80(fp, repr) fp##l #elif FLT80_MANT_DIG == 64 @@ -2062,6 +2067,7 @@ typedef double zig_f128; #define zig_make_f128(fp, repr) fp #elif LDBL_MANT_DIG == 113 #define zig_bitSizeOf_c_longdouble 128 +typedef zig_u128 zig_repr_c_longdouble; typedef long double zig_f128; #define zig_make_f128(fp, repr) fp##l #elif FLT128_MANT_DIG == 113 @@ -2099,9 +2105,10 @@ typedef zig_i128 zig_f128; #ifdef zig_bitSizeOf_c_longdouble #ifdef ZIG_TARGET_ABI_MSVC -typedef double zig_c_longdouble; #undef zig_bitSizeOf_c_longdouble #define zig_bitSizeOf_c_longdouble 64 +typedef uint64_t zig_repr_c_longdouble; +typedef zig_f64 zig_c_longdouble; #define zig_make_c_longdouble(fp, repr) fp #else typedef long double zig_c_longdouble; @@ -2113,6 +2120,7 @@ typedef long double zig_c_longdouble; #undef zig_has_c_longdouble #define zig_has_c_longdouble 0 #define zig_bitSizeOf_c_longdouble 80 +typedef zig_u128 zig_repr_c_longdouble; #define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80 #define zig_bitSizeOf_repr_c_longdouble 128 typedef zig_i128 zig_c_longdouble; @@ -2126,21 +2134,18 @@ typedef zig_i128 zig_c_longdouble; #if !zig_has_float_builtins #define zig_float_from_repr(Type, ReprType) \ - static inline zig_##Type zig_float_from_repr_##Type(zig_##ReprType repr) { \ - return *((zig_##Type*)&repr); \ + static inline zig_##Type zig_float_from_repr_##Type(ReprType repr) { \ + zig_##Type result; \ + memcpy(&result, &repr, sizeof(result)); \ + return result; \ } -zig_float_from_repr(f16, u16) -zig_float_from_repr(f32, u32) -zig_float_from_repr(f64, u64) -zig_float_from_repr(f80, u128) -zig_float_from_repr(f128, u128) -#if zig_bitSizeOf_c_longdouble == 80 -zig_float_from_repr(c_longdouble, u128) -#else -#define zig_expand_float_from_repr(Type, ReprType) zig_float_from_repr(Type, ReprType) -zig_expand_float_from_repr(c_longdouble, zig_expand_concat(u, zig_bitSizeOf_c_longdouble)) -#endif +zig_float_from_repr(f16, uint16_t) +zig_float_from_repr(f32, uint32_t) +zig_float_from_repr(f64, uint64_t) +zig_float_from_repr(f80, zig_u128) +zig_float_from_repr(f128, zig_u128) +zig_float_from_repr(c_longdouble, zig_repr_c_longdouble) #endif #define zig_cast_f16 (zig_f16) @@ -2288,98 +2293,98 @@ zig_float_builtins(c_longdouble) // TODO: zig_msvc_atomic_load should load 32 bit without interlocked on x86, and load 64 bit without interlocked on x64 -#define zig_msvc_atomics(Type, suffix) \ - static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ - zig_##Type comparand = *expected; \ - zig_##Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ +#define zig_msvc_atomics(ZigType, Type, suffix) \ + static inline bool zig_msvc_cmpxchg_##ZigType(Type volatile* obj, Type* expected, Type desired) { \ + Type comparand = *expected; \ + Type initial = _InterlockedCompareExchange##suffix(obj, desired, comparand); \ bool exchanged = initial == comparand; \ if (!exchanged) { \ *expected = initial; \ } \ return exchanged; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_xchg_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedExchange##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_add_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedExchangeAdd##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_sub_##ZigType(Type volatile* obj, Type value) { \ bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ + Type new; \ + Type prev; \ while (!success) { \ prev = *obj; \ new = prev - value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ } \ return prev; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_or_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_or_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedOr##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_xor_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_xor_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedXor##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_and_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_and_##ZigType(Type volatile* obj, Type value) { \ return _InterlockedAnd##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomicrmw_nand_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_nand_##ZigType(Type volatile* obj, Type value) { \ bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ + Type new; \ + Type prev; \ while (!success) { \ prev = *obj; \ new = ~(prev & value); \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ } \ return prev; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_min_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_min_##ZigType(Type volatile* obj, Type value) { \ bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ + Type new; \ + Type prev; \ while (!success) { \ prev = *obj; \ new = value < prev ? value : prev; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ } \ return prev; \ } \ - static inline zig_##Type zig_msvc_atomicrmw_max_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline Type zig_msvc_atomicrmw_max_##ZigType(Type volatile* obj, Type value) { \ bool success = false; \ - zig_##Type new; \ - zig_##Type prev; \ + Type new; \ + Type prev; \ while (!success) { \ prev = *obj; \ new = value > prev ? value : prev; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, new); \ + success = zig_msvc_cmpxchg_##ZigType(obj, &prev, new); \ } \ return prev; \ } \ - static inline void zig_msvc_atomic_store_##Type(zig_##Type volatile* obj, zig_##Type value) { \ + static inline void zig_msvc_atomic_store_##ZigType(Type volatile* obj, Type value) { \ _InterlockedExchange##suffix(obj, value); \ } \ - static inline zig_##Type zig_msvc_atomic_load_##Type(zig_##Type volatile* obj) { \ + static inline Type zig_msvc_atomic_load_##ZigType(Type volatile* obj) { \ return _InterlockedOr##suffix(obj, 0); \ } -zig_msvc_atomics(u8, 8) -zig_msvc_atomics(i8, 8) -zig_msvc_atomics(u16, 16) -zig_msvc_atomics(i16, 16) -zig_msvc_atomics(u32, ) -zig_msvc_atomics(i32, ) +zig_msvc_atomics( u8, uint8_t, 8) +zig_msvc_atomics( i8, int8_t, 8) +zig_msvc_atomics(u16, uint16_t, 16) +zig_msvc_atomics(i16, int16_t, 16) +zig_msvc_atomics(u32, uint32_t, ) +zig_msvc_atomics(i32, int32_t, ) #if _M_X64 -zig_msvc_atomics(u64, 64) -zig_msvc_atomics(i64, 64) +zig_msvc_atomics(u64, uint64_t, 64) +zig_msvc_atomics(i64, int64_t, 64) #endif #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ - zig_##ReprType comparand = *((zig_##ReprType*)expected); \ - zig_##ReprType initial = _InterlockedCompareExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&desired), comparand); \ + ReprType comparand = *((ReprType*)expected); \ + ReprType initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, *((ReprType*)&desired), comparand); \ bool exchanged = initial == comparand; \ if (!exchanged) { \ *expected = *((zig_##Type*)&initial); \ @@ -2387,35 +2392,35 @@ zig_msvc_atomics(i64, 64) return exchanged; \ } \ static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - zig_##ReprType initial = _InterlockedExchange##suffix((zig_##ReprType volatile*)obj, *((zig_##ReprType*)&value)); \ + ReprType initial = _InterlockedExchange##suffix((ReprType volatile*)obj, *((ReprType*)&value)); \ return *((zig_##Type*)&initial); \ } \ static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ bool success = false; \ - zig_##ReprType new; \ + ReprType new; \ zig_##Type prev; \ while (!success) { \ prev = *obj; \ new = prev + value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, *((ReprType*)&new)); \ } \ return prev; \ } \ static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ bool success = false; \ - zig_##ReprType new; \ + ReprType new; \ zig_##Type prev; \ while (!success) { \ prev = *obj; \ new = prev - value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, *((zig_##ReprType*)&new)); \ + success = zig_msvc_cmpxchg_##Type(obj, &prev, *((ReprType*)&new)); \ } \ return prev; \ } -zig_msvc_flt_atomics(f32, u32, ) +zig_msvc_flt_atomics(f32, uint32_t, ) #if _M_X64 -zig_msvc_flt_atomics(f64, u64, 64) +zig_msvc_flt_atomics(f64, uint64_t, 64) #endif #if _M_IX86 @@ -2426,11 +2431,11 @@ static inline void zig_msvc_atomic_barrier() { } } -static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, uint32_t* arg) { +static inline void* zig_msvc_atomicrmw_xchg_p32(void** obj, void* arg) { return _InterlockedExchangePointer(obj, arg); } -static inline void zig_msvc_atomic_store_p32(void** obj, uint32_t* arg) { +static inline void zig_msvc_atomic_store_p32(void** obj, void* arg) { _InterlockedExchangePointer(obj, arg); } @@ -2448,11 +2453,11 @@ static inline bool zig_msvc_cmpxchg_p32(void** obj, void** expected, void* desir return exchanged; } #else /* _M_IX86 */ -static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, uint64_t* arg) { +static inline void* zig_msvc_atomicrmw_xchg_p64(void** obj, void* arg) { return _InterlockedExchangePointer(obj, arg); } -static inline void zig_msvc_atomic_store_p64(void** obj, uint64_t* arg) { +static inline void zig_msvc_atomic_store_p64(void** obj, void* arg) { _InterlockedExchangePointer(obj, arg); } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 4ddfe3213a..1db4fc5d51 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2058,6 +2058,7 @@ fn renderTypePrefix( .zig_f64, .zig_f80, .zig_f128, + .zig_c_longdouble, => |tag| try w.writeAll(@tagName(tag)), .pointer, @@ -2225,6 +2226,7 @@ fn renderTypeSuffix( .zig_f64, .zig_f80, .zig_f128, + .zig_c_longdouble, => {}, .pointer, @@ -3062,6 +3064,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } + const inst_ty = f.air.typeOfIndex(inst); const ptr_ty = f.air.typeOf(bin_op.lhs); const child_ty = ptr_ty.childType(); @@ -3076,7 +3079,9 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = &("); + try writer.writeAll(" = ("); + try f.renderTypecast(writer, inst_ty); + try writer.writeAll(")&("); if (ptr_ty.ptrSize() == .One) { // It's a pointer to an array, so we need to de-reference. try f.writeCValueDeref(writer, ptr); @@ -3902,32 +3907,31 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const inst_ty = f.air.typeOfIndex(inst); - const elem_ty = switch (inst_ty.ptrSize()) { - .One => blk: { - const array_ty = inst_ty.childType(); - break :blk array_ty.childType(); - }, - else => inst_ty.childType(), - }; + const elem_ty = inst_ty.elemType2(); - // We must convert to and from integer types to prevent UB if the operation - // results in a NULL pointer, or if LHS is NULL. The operation is only UB - // if the result is NULL and then dereferenced. const local = try f.allocLocal(inst, inst_ty); const writer = f.object.writer(); try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = ("); - try f.renderTypecast(writer, inst_ty); - try writer.writeAll(")(((uintptr_t)"); - try f.writeCValue(writer, lhs, .Other); - try writer.writeAll(") "); - try writer.writeByte(operator); - try writer.writeAll(" ("); - try f.writeCValue(writer, rhs, .Other); - try writer.writeAll("*sizeof("); - try f.renderTypecast(writer, elem_ty); - try writer.writeAll(")));\n"); + try writer.writeAll(" = "); + if (elem_ty.hasRuntimeBitsIgnoreComptime()) { + // We must convert to and from integer types to prevent UB if the operation + // results in a NULL pointer, or if LHS is NULL. The operation is only UB + // if the result is NULL and then dereferenced. + try writer.writeByte('('); + try f.renderTypecast(writer, inst_ty); + try writer.writeAll(")(((uintptr_t)"); + try f.writeCValue(writer, lhs, .Other); + try writer.writeAll(") "); + try writer.writeByte(operator); + try writer.writeAll(" ("); + try f.writeCValue(writer, rhs, .Other); + try writer.writeAll("*sizeof("); + try f.renderTypecast(writer, elem_ty); + try writer.writeAll(")))"); + } else try f.writeCValue(writer, lhs, .Initializer); + + try writer.writeAll(";\n"); return local; } @@ -5264,21 +5268,21 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc else => unreachable, }; - try writer.writeByte('&'); - switch (field_loc) { - .begin, .end => { - try writer.writeByte('('); - try f.writeCValue(writer, struct_ptr, .Other); - try writer.print(")[{}]", .{ - @boolToInt(field_loc == .end and struct_ty.hasRuntimeBitsIgnoreComptime()), - }); - }, - .field => |field| if (extra_name != .none) { - try f.writeCValueDerefMember(writer, struct_ptr, extra_name); - try writer.writeByte('.'); - try f.writeCValue(writer, field, .Other); - } else try f.writeCValueDerefMember(writer, struct_ptr, field), - } + if (struct_ty.hasRuntimeBitsIgnoreComptime()) { + try writer.writeByte('&'); + switch (field_loc) { + .begin, .end => { + try writer.writeByte('('); + try f.writeCValue(writer, struct_ptr, .Other); + try writer.print(")[{}]", .{@boolToInt(field_loc == .end)}); + }, + .field => |field| if (extra_name != .none) { + try f.writeCValueDerefMember(writer, struct_ptr, extra_name); + try writer.writeByte('.'); + try f.writeCValue(writer, field, .Other); + } else try f.writeCValueDerefMember(writer, struct_ptr, field), + } + } else try f.writeCValue(writer, struct_ptr, .Other); try writer.writeAll(";\n"); return local; } diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index d6424b1f27..bf148a8b87 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -102,6 +102,7 @@ pub const CType = extern union { zig_f64, zig_f80, zig_f128, + zig_c_longdouble, // Keep last_no_payload_tag updated! // After this, the tag requires a payload. pointer, @@ -127,7 +128,7 @@ pub const CType = extern union { function, varargs_function, - pub const last_no_payload_tag = Tag.zig_f128; + pub const last_no_payload_tag = Tag.zig_c_longdouble; pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; pub fn hasPayload(self: Tag) bool { @@ -177,6 +178,7 @@ pub const CType = extern union { .zig_f64, .zig_f80, .zig_f128, + .zig_c_longdouble, => @compileError("Type Tag " ++ @tagName(self) ++ " has no payload"), .pointer, @@ -557,6 +559,7 @@ pub const CType = extern union { .zig_f64, .zig_f80, .zig_f128, + .zig_c_longdouble, => false, .pointer, @@ -674,6 +677,7 @@ pub const CType = extern union { .zig_f64, .zig_f80, .zig_f128, + .zig_c_longdouble, => {}, .pointer, @@ -980,7 +984,7 @@ pub const CType = extern union { .f64 => .zig_f64, .f80 => .zig_f80, .f128 => .zig_f128, - .c_longdouble => .@"long double", + .c_longdouble => .zig_c_longdouble, else => unreachable, }), @@ -1374,6 +1378,7 @@ pub const CType = extern union { .zig_f64, .zig_f80, .zig_f128, + .zig_c_longdouble, => return self, .pointer, diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index e9a01226b1..b242374ef8 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -7,6 +7,7 @@ const is_x86_64_linux = builtin.cpu.arch == .x86_64 and builtin.os.tag == .linux comptime { if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_aarch64 and + !(builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) and // MSVC doesn't support inline assembly is_x86_64_linux) { asm ( @@ -24,6 +25,8 @@ test "module level assembly" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly + if (is_x86_64_linux) { try expect(this_is_my_alias() == 1234); } @@ -36,6 +39,8 @@ test "output constraint modifiers" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly + // This is only testing compilation. var a: u32 = 3; asm volatile ("" @@ -57,6 +62,8 @@ test "alternative constraints" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly + // Make sure we allow commas as a separator for alternative constraints. var a: u32 = 3; asm volatile ("" @@ -73,6 +80,8 @@ test "sized integer/float in asm input" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly + asm volatile ("" : : [_] "m" (@as(usize, 3)), @@ -122,6 +131,8 @@ test "struct/array/union types as input values" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly + asm volatile ("" : : [_] "m" (@as([1]u32, undefined)), @@ -146,6 +157,8 @@ test "asm modifiers (AArch64)" { if (builtin.target.cpu.arch != .aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support inline assembly + var x: u32 = 15; const double = asm ("add %[ret:w], %[in:w], %[in:w]" : [ret] "=r" (-> u32), From 436e99d13ba188412b8a431b69cc9ff29c6bec4a Mon Sep 17 00:00:00 2001 From: Ken Kochis Date: Tue, 21 Feb 2023 17:08:03 -0800 Subject: [PATCH 103/122] Close files in hashFileFallible --- src/Package.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Package.zig b/src/Package.zig index 68d67a6d62..d599aefe56 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -625,6 +625,7 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { var buf: [8000]u8 = undefined; var file = try dir.openFile(hashed_file.path, .{}); + defer file.close(); var hasher = Manifest.Hash.init(.{}); hasher.update(hashed_file.path); hasher.update(&.{ 0, @boolToInt(try isExecutable(file)) }); From bdb1e014a085fdd872886dddc66cfe1081887e5c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 22 Feb 2023 23:31:41 -0500 Subject: [PATCH 104/122] CBE: cleanup field access * Implement @fieldParentPtr on a union * Refactor field access to ensure that it is handled consistently * Remove `renderTypecast` as it is now behaves the same as `renderType` --- src/codegen/c.zig | 611 +++++++++++++++-------------- test/behavior/field_parent_ptr.zig | 3 - test/behavior/packed-struct.zig | 1 - 3 files changed, 315 insertions(+), 300 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index bfc6e6451b..b5269acc5c 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -39,7 +39,7 @@ pub const CValue = union(enum) { constant: Air.Inst.Ref, /// Index into the parameters arg: usize, - /// The payload field of a parameter + /// The array field of a parameter arg_array: usize, /// Index into a tuple's fields field: usize, @@ -50,6 +50,8 @@ pub const CValue = union(enum) { undef: Type, /// Render the slice as an identifier (using fmtIdent) identifier: []const u8, + /// Render the slice as an payload.identifier (using fmtIdent) + payload_identifier: []const u8, /// Render these bytes literally. /// TODO make this a [*:0]const u8 to save memory bytes: []const u8, @@ -416,12 +418,20 @@ pub const Function = struct { return f.object.dg.fail(format, args); } - fn renderType(f: *Function, w: anytype, t: Type) !void { - return f.object.dg.renderType(w, t, .Complete); + fn indexToCType(f: *Function, idx: CType.Index) CType { + return f.object.dg.indexToCType(idx); } - fn renderTypecast(f: *Function, w: anytype, t: Type) !void { - return f.object.dg.renderTypecast(w, t); + fn typeToIndex(f: *Function, ty: Type, kind: CType.Kind) !CType.Index { + return f.object.dg.typeToIndex(ty, kind); + } + + fn typeToCType(f: *Function, ty: Type, kind: CType.Kind) !CType { + return f.object.dg.typeToCType(ty, kind); + } + + fn renderType(f: *Function, w: anytype, t: Type) !void { + return f.object.dg.renderType(w, t); } fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, src_ty: Type, location: ValueRenderLocation) !void { @@ -532,7 +542,7 @@ pub const DeclGen = struct { try writer.writeByte('{'); } else { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeAll("){ .ptr = "); } @@ -559,7 +569,7 @@ pub const DeclGen = struct { const need_typecast = if (ty.castPtrToFn()) |_| false else !ty.eql(decl.ty, dg.module); if (need_typecast) { try writer.writeAll("(("); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } try writer.writeByte('&'); @@ -574,7 +584,7 @@ pub const DeclGen = struct { fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type, location: ValueRenderLocation) error{ OutOfMemory, AnalysisFail }!void { if (!ptr_ty.isSlice()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ptr_ty); + try dg.renderType(writer, ptr_ty); try writer.writeByte(')'); } switch (ptr_val.tag()) { @@ -589,90 +599,71 @@ pub const DeclGen = struct { try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl_index, location); }, .field_ptr => { - const ptr_info = ptr_ty.ptrInfo(); + const target = dg.module.getTarget(); const field_ptr = ptr_val.castTag(.field_ptr).?.data; - const container_ty = field_ptr.container_ty; - const index = field_ptr.field_index; - var container_ptr_ty_pl: Type.Payload.ElemType = .{ - .base = .{ .tag = .c_mut_pointer }, - .data = field_ptr.container_ty, - }; - const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); + // Ensure complete type definition is visible before accessing fields. + _ = try dg.typeToIndex(field_ptr.container_ty, .complete); - const FieldInfo = struct { name: []const u8, ty: Type }; - const field_info: FieldInfo = switch (container_ty.zigTypeTag()) { - .Struct => switch (container_ty.containerLayout()) { - .Auto, .Extern => FieldInfo{ - .name = container_ty.structFields().keys()[index], - .ty = container_ty.structFields().values()[index].ty, - }, - .Packed => if (ptr_info.data.host_size == 0) { - const target = dg.module.getTarget(); + var container_ptr_pl = ptr_ty.ptrInfo(); + container_ptr_pl.data.pointee_type = field_ptr.container_ty; + const container_ptr_ty = Type.initPayload(&container_ptr_pl.base); - const byte_offset = container_ty.packedStructFieldByteOffset(index, target); - var byte_offset_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = byte_offset, - }; - const byte_offset_val = Value.initPayload(&byte_offset_pl.base); - - var u8_ptr_pl = ptr_info; - u8_ptr_pl.data.pointee_type = Type.u8; - const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); - - try writer.writeAll("&(("); - try dg.renderTypecast(writer, u8_ptr_ty); - try writer.writeByte(')'); - try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); - return writer.print(")[{}]", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); - } else { - var host_pl = Type.Payload.Bits{ - .base = .{ .tag = .int_unsigned }, - .data = ptr_info.data.host_size * 8, - }; - const host_ty = Type.initPayload(&host_pl.base); - - try writer.writeByte('('); - try dg.renderTypecast(writer, ptr_ty); - try writer.writeByte(')'); - return dg.renderParentPtr(writer, field_ptr.container_ptr, host_ty, location); - }, + switch (fieldLocation( + field_ptr.container_ty, + ptr_ty, + @intCast(u32, field_ptr.field_index), + target, + )) { + .begin => try dg.renderParentPtr( + writer, + field_ptr.container_ptr, + container_ptr_ty, + location, + ), + .field => |field| { + try writer.writeAll("&("); + try dg.renderParentPtr( + writer, + field_ptr.container_ptr, + container_ptr_ty, + location, + ); + try writer.writeAll(")->"); + try dg.writeCValue(writer, field); }, - .Union => switch (container_ty.containerLayout()) { - .Auto, .Extern => FieldInfo{ - .name = container_ty.unionFields().keys()[index], - .ty = container_ty.unionFields().values()[index].ty, - }, - .Packed => { - return dg.renderParentPtr(writer, field_ptr.container_ptr, ptr_ty, location); - }, - }, - .Pointer => field_info: { - assert(container_ty.isSlice()); - break :field_info switch (index) { - 0 => FieldInfo{ .name = "ptr", .ty = container_ty.childType() }, - 1 => FieldInfo{ .name = "len", .ty = Type.usize }, - else => unreachable, + .byte_offset => |byte_offset| { + var u8_ptr_pl = ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + var byte_offset_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = byte_offset, }; + const byte_offset_val = Value.initPayload(&byte_offset_pl.base); + + try writer.writeAll("(("); + try dg.renderType(writer, u8_ptr_ty); + try writer.writeByte(')'); + try dg.renderParentPtr( + writer, + field_ptr.container_ptr, + container_ptr_ty, + location, + ); + try writer.print(" + {})", .{try dg.fmtIntLiteral(Type.usize, byte_offset_val)}); + }, + .end => { + try writer.writeAll("(("); + try dg.renderParentPtr( + writer, + field_ptr.container_ptr, + container_ptr_ty, + location, + ); + try writer.print(") + {})", .{try dg.fmtIntLiteral(Type.usize, Value.one)}); }, - else => unreachable, - }; - - if (field_info.ty.hasRuntimeBitsIgnoreComptime()) { - // Ensure complete type definition is visible before accessing fields. - try dg.renderType(std.io.null_writer, field_ptr.container_ty, .Complete); - - try writer.writeAll("&("); - try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); - try writer.writeAll(")->"); - switch (field_ptr.container_ty.tag()) { - .union_tagged, .union_safety_tagged => try writer.writeAll("payload."), - else => {}, - } - try writer.print("{ }", .{fmtIdent(field_info.name)}); - } else { - try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty, location); } }, .elem_ptr => { @@ -696,7 +687,7 @@ pub const DeclGen = struct { const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base); // Ensure complete type definition is visible before accessing fields. - try dg.renderType(std.io.null_writer, payload_ptr.container_ty, .Complete); + _ = try dg.typeToIndex(payload_ptr.container_ty, .complete); try writer.writeAll("&("); try dg.renderParentPtr(writer, payload_ptr.container_ptr, container_ptr_ty, location); @@ -763,18 +754,18 @@ pub const DeclGen = struct { .Pointer => if (ty.isSlice()) { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } try writer.writeAll("{("); var buf: Type.SlicePtrFieldTypeBuffer = undefined; const ptr_ty = ty.slicePtrFieldType(&buf); - try dg.renderTypecast(writer, ptr_ty); + try dg.renderType(writer, ptr_ty); return writer.print("){x}, {0x}}}", .{try dg.fmtIntLiteral(Type.usize, val)}); } else { try writer.writeAll("(("); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); }, .Optional => { @@ -791,7 +782,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -805,7 +796,7 @@ pub const DeclGen = struct { .Auto, .Extern => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -827,7 +818,7 @@ pub const DeclGen = struct { .Union => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -852,7 +843,7 @@ pub const DeclGen = struct { .ErrorUnion => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -865,7 +856,7 @@ pub const DeclGen = struct { .Array, .Vector => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1026,7 +1017,7 @@ pub const DeclGen = struct { return dg.renderValue(writer, ty, slice_val, location); } else { try writer.writeAll("(("); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeAll(")NULL)"); }, .variable => { @@ -1036,7 +1027,7 @@ pub const DeclGen = struct { .slice => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1059,7 +1050,7 @@ pub const DeclGen = struct { }, .int_u64, .one => { try writer.writeAll("(("); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); return writer.print("){x})", .{try dg.fmtIntLiteral(Type.usize, val)}); }, .field_ptr, @@ -1074,7 +1065,7 @@ pub const DeclGen = struct { .Array, .Vector => { if (location == .FunctionArgument) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1177,7 +1168,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1211,7 +1202,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1275,7 +1266,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1362,7 +1353,7 @@ pub const DeclGen = struct { if (!empty) try writer.writeAll(" | "); try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); if (bit_offset_val_pl.data != 0) { @@ -1385,7 +1376,7 @@ pub const DeclGen = struct { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } @@ -1396,11 +1387,11 @@ pub const DeclGen = struct { if (field_ty.hasRuntimeBits()) { if (field_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } else if (field_ty.zigTypeTag() == .Float) { try writer.writeByte('('); - try dg.renderTypecast(writer, ty); + try dg.renderType(writer, ty); try writer.writeByte(')'); } try dg.renderValue(writer, field_ty, union_obj.val, initializer_type); @@ -1509,9 +1500,11 @@ pub const DeclGen = struct { fn indexToCType(dg: *DeclGen, idx: CType.Index) CType { return dg.ctypes.indexToCType(idx); } + fn typeToIndex(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType.Index { return dg.ctypes.typeToIndex(dg.gpa, ty, dg.module, kind); } + fn typeToCType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType { return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind); } @@ -1524,16 +1517,10 @@ pub const DeclGen = struct { /// There are three type formats in total that we support rendering: /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | /// |---------------------|-----------------|---------------------| - /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// - fn renderType( - dg: *DeclGen, - w: anytype, - t: Type, - _: TypedefKind, - ) error{ OutOfMemory, AnalysisFail }!void { + fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { const store = &dg.ctypes.set; const module = dg.module; const idx = try dg.typeToIndex(t, .complete); @@ -1603,12 +1590,12 @@ pub const DeclGen = struct { if (needs_cast) { try w.writeByte('('); - try dg.renderTypecast(w, dest_ty); + try dg.renderType(w, dest_ty); try w.writeByte(')'); } if (src_is_ptr) { try w.writeByte('('); - try dg.renderTypecast(w, src_eff_ty); + try dg.renderType(w, src_eff_ty); try w.writeByte(')'); } try context.writeValue(dg, w, src_ty, location); @@ -1625,7 +1612,7 @@ pub const DeclGen = struct { try w.writeAll("(0, "); // TODO: Should the 0 go through fmtIntLiteral? if (src_is_ptr) { try w.writeByte('('); - try dg.renderTypecast(w, src_eff_ty); + try dg.renderType(w, src_eff_ty); try w.writeByte(')'); } try context.writeValue(dg, w, src_ty, .FunctionArgument); @@ -1646,28 +1633,11 @@ pub const DeclGen = struct { } } - /// Renders a type in C typecast format. - /// - /// This is guaranteed to be valid in a typecast expression, but not - /// necessarily in a variable/field declaration. - /// - /// There are three type formats in total that we support rendering: - /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | - /// |---------------------|-----------------|---------------------| - /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | - /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | - /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | - /// - fn renderTypecast(dg: *DeclGen, w: anytype, ty: Type) error{ OutOfMemory, AnalysisFail }!void { - try dg.renderType(w, ty, undefined); - } - /// Renders a type and name in field declaration/definition format. /// /// There are three type formats in total that we support rendering: /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) | /// |---------------------|-----------------|---------------------| - /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" | /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// @@ -1708,7 +1678,7 @@ pub const DeclGen = struct { const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); try w.writeAll("static "); - try dg.renderType(w, name_slice_ty, .Complete); + try dg.renderType(w, name_slice_ty); try w.writeByte(' '); try w.writeAll(fn_name); try w.writeByte('('); @@ -1742,7 +1712,7 @@ pub const DeclGen = struct { try w.writeAll(" = "); try dg.renderValue(w, name_ty, name_val, .Initializer); try w.writeAll(";\n return ("); - try dg.renderTypecast(w, name_slice_ty); + try dg.renderType(w, name_slice_ty); try w.print("){{{}, {}}};\n", .{ fmtIdent("name"), try dg.fmtIntLiteral(Type.usize, len_val), }); @@ -1787,6 +1757,10 @@ pub const DeclGen = struct { }, .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other), .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}), + .payload_identifier => |ident| return w.print("{ }.{ }", .{ + fmtIdent("payload"), + fmtIdent(ident), + }), .bytes => |bytes| return w.writeAll(bytes), } } @@ -1812,6 +1786,10 @@ pub const DeclGen = struct { .decl_ref => |decl| return dg.renderDeclName(w, decl, 0), .undef => unreachable, .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}), + .payload_identifier => |ident| return w.print("(*{ }.{ })", .{ + fmtIdent("payload"), + fmtIdent(ident), + }), .bytes => |bytes| { try w.writeAll("(*"); try w.writeAll(bytes); @@ -1829,7 +1807,7 @@ pub const DeclGen = struct { fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { switch (c_value) { .none, .constant, .field, .undef => unreachable, - .new_local, .local, .arg, .arg_array, .decl, .identifier, .bytes => { + .new_local, .local, .arg, .arg_array, .decl, .identifier, .payload_identifier, .bytes => { try dg.writeCValue(writer, c_value); try writer.writeAll("->"); }, @@ -3048,7 +3026,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(']'); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -3080,7 +3058,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll(")&("); if (ptr_ty.ptrSize() == .One) { // It's a pointer to an array, so we need to de-reference. @@ -3128,7 +3106,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(']'); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -3197,7 +3175,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(']'); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -3240,11 +3218,11 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); - const inst_cty = try f.object.dg.typeToIndex(inst_ty, .parameter); + const inst_cty = try f.typeToIndex(inst_ty, .parameter); const i = f.next_arg_index; f.next_arg_index += 1; - return if (inst_cty != try f.object.dg.typeToIndex(inst_ty, .complete)) + return if (inst_cty != try f.typeToIndex(inst_ty, .complete)) .{ .arg_array = i } else .{ .arg = i }; @@ -3281,7 +3259,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", (const char *)"); try f.writeCValue(writer, operand, .Other); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, src_ty); + try f.renderType(writer, src_ty); try writer.writeAll("))"); } else if (ptr_info.host_size != 0) { var host_pl = Type.Payload.Bits{ @@ -3310,11 +3288,11 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, src_ty); + try f.renderType(writer, src_ty); try writer.writeAll(")zig_wrap_"); try f.object.dg.renderTypeForBuiltinFnName(writer, field_ty); try writer.writeAll("(("); - try f.renderTypecast(writer, field_ty); + try f.renderType(writer, field_ty); try writer.writeByte(')'); const cant_cast = host_ty.isInt() and host_ty.bitSize(target) > 64; if (cant_cast) { @@ -3365,7 +3343,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { try f.writeCValue(writer, operand, .FunctionArgument); deref = false; try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, ret_ty); + try f.renderType(writer, ret_ty); try writer.writeAll("));\n"); break :ret_val array_local; } else operand; @@ -3440,7 +3418,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte('('); } else if (dest_c_bits <= 64) { try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeByte(')'); } @@ -3521,7 +3499,7 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { try writer.writeAll("memset("); try f.writeCValue(writer, dest_ptr, .FunctionArgument); try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)}); - try f.renderTypecast(writer, lhs_child_ty); + try f.renderType(writer, lhs_child_ty); try writer.writeAll("));\n"); } return CValue.none; @@ -3582,7 +3560,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { if (!is_array) try writer.writeByte('&'); try f.writeCValue(writer, array_src, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, src_ty); + try f.renderType(writer, src_ty); try writer.writeAll("))"); if (src_val == .constant) { try freeLocal(f, inst, array_src.new_local, 0); @@ -3641,13 +3619,13 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("(0, "); } else { try writer.writeByte('('); - try f.renderTypecast(writer, host_ty); + try f.renderType(writer, host_ty); try writer.writeByte(')'); } if (src_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeByte(')'); } try f.writeCValue(writer, src_val, .Other); @@ -3919,7 +3897,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { // results in a NULL pointer, or if LHS is NULL. The operation is only UB // if the result is NULL and then dereferenced. try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll(")(((uintptr_t)"); try f.writeCValue(writer, lhs, .Other); try writer.writeAll(") "); @@ -3927,7 +3905,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { try writer.writeAll(" ("); try f.writeCValue(writer, rhs, .Other); try writer.writeAll("*sizeof("); - try f.renderTypecast(writer, elem_ty); + try f.renderType(writer, elem_ty); try writer.writeAll(")))"); } else try f.writeCValue(writer, lhs, .Initializer); @@ -3992,7 +3970,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(".ptr = ("); var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try f.renderTypecast(writer, inst_ty.slicePtrFieldType(&buf)); + try f.renderType(writer, inst_ty.slicePtrFieldType(&buf)); try writer.writeByte(')'); try f.writeCValue(writer, ptr, .Other); try writer.writeAll("; "); @@ -4032,13 +4010,13 @@ fn airCall( defer gpa.free(resolved_args); for (resolved_args, args) |*resolved_arg, arg| { const arg_ty = f.air.typeOf(arg); - const arg_cty = try f.object.dg.typeToIndex(arg_ty, .parameter); - if (f.object.dg.indexToCType(arg_cty).tag() == .void) { + const arg_cty = try f.typeToIndex(arg_ty, .parameter); + if (f.indexToCType(arg_cty).tag() == .void) { resolved_arg.* = .none; continue; } resolved_arg.* = try f.resolveInst(arg); - if (arg_cty != try f.object.dg.typeToIndex(arg_ty, .complete)) { + if (arg_cty != try f.typeToIndex(arg_ty, .complete)) { var lowered_arg_buf: LowerFnRetTyBuffer = undefined; const lowered_arg_ty = lowerFnRetTy(arg_ty, &lowered_arg_buf, target); @@ -4048,7 +4026,7 @@ fn airCall( try writer.writeAll(", "); try f.writeCValue(writer, resolved_arg.*, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, lowered_arg_ty); + try f.renderType(writer, lowered_arg_ty); try writer.writeAll("));\n"); resolved_arg.* = array_local; } @@ -4077,7 +4055,7 @@ fn airCall( .none else if (f.liveness.isUnused(inst)) r: { try writer.writeByte('('); - try f.renderTypecast(writer, Type.void); + try f.renderType(writer, Type.void); try writer.writeByte(')'); break :r .none; } else r: { @@ -4132,7 +4110,7 @@ fn airCall( try writer.writeAll(", "); try f.writeCValueMember(writer, result_local, .{ .identifier = "array" }); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, ret_ty); + try f.renderType(writer, ret_ty); try writer.writeAll("));\n"); try freeLocal(f, inst, result_local.new_local, 0); break :r array_local; @@ -4280,7 +4258,7 @@ fn lowerTry( try writer.writeAll(", "); try f.writeCValueMember(writer, err_union, .{ .identifier = "payload" }); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, payload_ty); + try f.renderType(writer, payload_ty); try writer.writeAll("));\n"); } else { try f.writeCValue(writer, local, .Other); @@ -4313,7 +4291,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, operand, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, operand_ty); + try f.renderType(writer, operand_ty); try writer.writeAll("))"); } else { try f.writeCValue(writer, result, .Other); @@ -4362,7 +4340,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, dest_ty); + try f.renderType(writer, dest_ty); try writer.writeByte(')'); try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); @@ -4383,7 +4361,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", &"); try f.writeCValue(writer, operand_lval, .Other); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, dest_ty); + try f.renderType(writer, dest_ty); try writer.writeAll("));\n"); // Ensure padding bits have the expected value. @@ -4415,7 +4393,7 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst, Type.usize); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeAll(")zig_return_address();\n"); return local; } @@ -4426,7 +4404,7 @@ fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst, Type.usize); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeAll(")zig_frame_address();\n"); return local; } @@ -4558,11 +4536,11 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("switch ("); if (condition_ty.zigTypeTag() == .Bool) { try writer.writeByte('('); - try f.renderTypecast(writer, Type.u1); + try f.renderType(writer, Type.u1); try writer.writeByte(')'); } else if (condition_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeByte(')'); } try f.writeCValue(writer, condition, .Other); @@ -4591,7 +4569,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("case "); if (condition_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try f.renderTypecast(writer, Type.usize); + try f.renderType(writer, Type.usize); try writer.writeByte(')'); } try f.object.dg.renderValue(writer, condition_ty, f.air.value(item).?, .Other); @@ -5043,7 +5021,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -5127,6 +5105,62 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { } } +fn fieldLocation( + container_ty: Type, + field_ptr_ty: Type, + field_index: u32, + target: std.Target, +) union(enum) { + begin: void, + field: CValue, + byte_offset: u32, + end: void, +} { + return switch (container_ty.zigTypeTag()) { + .Struct => switch (container_ty.containerLayout()) { + .Auto, .Extern => for (field_index..container_ty.structFieldCount()) |next_field_index| { + if (container_ty.structFieldIsComptime(next_field_index)) continue; + const field_ty = container_ty.structFieldType(next_field_index); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; + break .{ .field = if (container_ty.isSimpleTuple()) + .{ .field = next_field_index } + else + .{ .identifier = container_ty.structFieldName(next_field_index) } }; + } else if (container_ty.hasRuntimeBitsIgnoreComptime()) .end else .begin, + .Packed => if (field_ptr_ty.ptrInfo().data.host_size == 0) + .{ .byte_offset = container_ty.packedStructFieldByteOffset(field_index, target) } + else + .begin, + }, + .Union => switch (container_ty.containerLayout()) { + .Auto, .Extern => { + const field_ty = container_ty.structFieldType(field_index); + if (!field_ty.hasRuntimeBitsIgnoreComptime()) + return if (container_ty.unionTagTypeSafety() != null and + !container_ty.unionHasAllZeroBitFieldTypes()) + .{ .field = .{ .identifier = "payload" } } + else + .begin; + const field_name = container_ty.unionFields().keys()[field_index]; + return .{ .field = if (container_ty.unionTagTypeSafety()) |_| + .{ .payload_identifier = field_name } + else + .{ .identifier = field_name } }; + }, + .Packed => .begin, + }, + .Pointer => switch (container_ty.ptrSize()) { + .Slice => switch (field_index) { + 0 => .{ .field = .{ .identifier = "ptr" } }, + 1 => .{ .field = .{ .identifier = "len" } }, + else => unreachable, + }, + .One, .Many, .C => unreachable, + }, + else => unreachable, + }; +} + fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.StructField, ty_pl.payload).data; @@ -5136,10 +5170,10 @@ fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue { return .none; } - const struct_ptr = try f.resolveInst(extra.struct_operand); + const container_ptr_val = try f.resolveInst(extra.struct_operand); try reap(f, inst, &.{extra.struct_operand}); - const struct_ptr_ty = f.air.typeOf(extra.struct_operand); - return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, extra.field_index); + const container_ptr_ty = f.air.typeOf(extra.struct_operand); + return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, extra.field_index); } fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue { @@ -5150,10 +5184,10 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue return .none; } - const struct_ptr = try f.resolveInst(ty_op.operand); + const container_ptr_val = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); - const struct_ptr_ty = f.air.typeOf(ty_op.operand); - return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index); + const container_ptr_ty = f.air.typeOf(ty_op.operand); + return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, index); } fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -5165,130 +5199,115 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } - const struct_ptr_ty = f.air.typeOfIndex(inst); + const target = f.object.dg.module.getTarget(); + const container_ptr_ty = f.air.typeOfIndex(inst); + const container_ty = container_ptr_ty.childType(); const field_ptr_ty = f.air.typeOf(extra.field_ptr); const field_ptr_val = try f.resolveInst(extra.field_ptr); try reap(f, inst, &.{extra.field_ptr}); - const target = f.object.dg.module.getTarget(); - const struct_ty = struct_ptr_ty.childType(); - - if (struct_ty.zigTypeTag() == .Union) { - return f.fail("TODO: CBE: @fieldParentPtr for unions", .{}); - } - - const field_offset = struct_ty.structFieldOffset(extra.field_index, target); - - var field_offset_pl = Value.Payload.I64{ - .base = .{ .tag = .int_i64 }, - .data = -@intCast(i64, field_offset), - }; - const field_offset_val = Value.initPayload(&field_offset_pl.base); - - var u8_ptr_pl = field_ptr_ty.ptrInfo(); - u8_ptr_pl.data.pointee_type = Type.u8; - const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); - const writer = f.object.writer(); - const local = try f.allocLocal(inst, struct_ptr_ty); + const local = try f.allocLocal(inst, container_ptr_ty); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, struct_ptr_ty); - try writer.writeAll(")&(("); - try f.renderTypecast(writer, u8_ptr_ty); + try f.renderType(writer, container_ptr_ty); try writer.writeByte(')'); - try f.writeCValue(writer, field_ptr_val, .Other); - try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.isize, field_offset_val)}); + + switch (fieldLocation(container_ty, field_ptr_ty, extra.field_index, target)) { + .begin => try f.writeCValue(writer, field_ptr_val, .Initializer), + .field => |field| { + var u8_ptr_pl = field_ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + try writer.writeAll("(("); + try f.renderType(writer, u8_ptr_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, field_ptr_val, .Other); + try writer.writeAll(" - offsetof("); + try f.renderType(writer, container_ty); + try writer.writeAll(", "); + try f.writeCValue(writer, field, .Other); + try writer.writeAll("))"); + }, + .byte_offset => |byte_offset| { + var u8_ptr_pl = field_ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + var byte_offset_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = byte_offset, + }; + const byte_offset_val = Value.initPayload(&byte_offset_pl.base); + + try writer.writeAll("(("); + try f.renderType(writer, u8_ptr_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, field_ptr_val, .Other); + try writer.print(" - {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); + }, + .end => { + try f.writeCValue(writer, field_ptr_val, .Other); + try writer.print(" - {}", .{try f.fmtIntLiteral(Type.usize, Value.one)}); + }, + } + + try writer.writeAll(";\n"); return local; } -fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue { - const writer = f.object.writer(); +fn fieldPtr( + f: *Function, + inst: Air.Inst.Index, + container_ptr_ty: Type, + container_ptr_val: CValue, + field_index: u32, +) !CValue { + const target = f.object.dg.module.getTarget(); + const container_ty = container_ptr_ty.elemType(); const field_ptr_ty = f.air.typeOfIndex(inst); - const field_ptr_info = field_ptr_ty.ptrInfo(); - const struct_ty = struct_ptr_ty.elemType(); - const field_ty = struct_ty.structFieldType(index); // Ensure complete type definition is visible before accessing fields. - try f.renderType(std.io.null_writer, struct_ty); + _ = try f.typeToIndex(container_ty, .complete); + const writer = f.object.writer(); const local = try f.allocLocal(inst, field_ptr_ty); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, field_ptr_ty); + try f.renderType(writer, field_ptr_ty); try writer.writeByte(')'); - const extra_name: CValue = switch (struct_ty.tag()) { - .union_tagged, .union_safety_tagged => .{ .identifier = "payload" }, - else => .none, - }; - - const field_loc: union(enum) { - begin: void, - field: CValue, - end: void, - } = switch (struct_ty.tag()) { - .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) { - .Auto, .Extern => for (index..struct_ty.structFieldCount()) |field_i| { - if (!struct_ty.structFieldIsComptime(field_i) and - struct_ty.structFieldType(field_i).hasRuntimeBitsIgnoreComptime()) - break .{ .field = if (struct_ty.isSimpleTuple()) - .{ .field = field_i } - else - .{ .identifier = struct_ty.structFieldName(field_i) } }; - } else .end, - .Packed => if (field_ptr_info.data.host_size == 0) { - const target = f.object.dg.module.getTarget(); - - const byte_offset = struct_ty.packedStructFieldByteOffset(index, target); - var byte_offset_pl = Value.Payload.U64{ - .base = .{ .tag = .int_u64 }, - .data = byte_offset, - }; - const byte_offset_val = Value.initPayload(&byte_offset_pl.base); - - var u8_ptr_pl = field_ptr_info; - u8_ptr_pl.data.pointee_type = Type.u8; - const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); - - if (!std.mem.isAligned(byte_offset, field_ptr_ty.ptrAlignment(target))) { - return f.fail("TODO: CBE: unaligned packed struct field pointer", .{}); - } - - try writer.writeAll("&(("); - try f.renderTypecast(writer, u8_ptr_ty); - try writer.writeByte(')'); - try f.writeCValue(writer, struct_ptr, .Other); - try writer.print(")[{}];\n", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); - return local; - } else .begin, + switch (fieldLocation(container_ty, field_ptr_ty, field_index, target)) { + .begin => try f.writeCValue(writer, container_ptr_val, .Initializer), + .field => |field| { + try writer.writeByte('&'); + try f.writeCValueDerefMember(writer, container_ptr_val, field); }, - .@"union", .union_safety_tagged, .union_tagged => if (struct_ty.containerLayout() == .Packed) { - try f.writeCValue(writer, struct_ptr, .Other); - try writer.writeAll(";\n"); - return local; - } else if (field_ty.hasRuntimeBitsIgnoreComptime()) .{ .field = .{ - .identifier = struct_ty.unionFields().keys()[index], - } } else .end, - else => unreachable, - }; + .byte_offset => |byte_offset| { + var u8_ptr_pl = field_ptr_ty.ptrInfo(); + u8_ptr_pl.data.pointee_type = Type.u8; + const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + var byte_offset_pl = Value.Payload.U64{ + .base = .{ .tag = .int_u64 }, + .data = byte_offset, + }; + const byte_offset_val = Value.initPayload(&byte_offset_pl.base); + + try writer.writeAll("(("); + try f.renderType(writer, u8_ptr_ty); + try writer.writeByte(')'); + try f.writeCValue(writer, container_ptr_val, .Other); + try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); + }, + .end => { + try f.writeCValue(writer, container_ptr_val, .Other); + try writer.print(" + {}", .{try f.fmtIntLiteral(Type.usize, Value.one)}); + }, + } - if (struct_ty.hasRuntimeBitsIgnoreComptime()) { - try writer.writeByte('&'); - switch (field_loc) { - .begin, .end => { - try writer.writeByte('('); - try f.writeCValue(writer, struct_ptr, .Other); - try writer.print(")[{}]", .{@boolToInt(field_loc == .end)}); - }, - .field => |field| if (extra_name != .none) { - try f.writeCValueDerefMember(writer, struct_ptr, extra_name); - try writer.writeByte('.'); - try f.writeCValue(writer, field, .Other); - } else try f.writeCValueDerefMember(writer, struct_ptr, field), - } - } else try f.writeCValue(writer, struct_ptr, .Other); try writer.writeAll(";\n"); return local; } @@ -5315,7 +5334,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); // Ensure complete type definition is visible before accessing fields. - try f.renderType(std.io.null_writer, struct_ty); + _ = try f.typeToIndex(struct_ty, .complete); const extra_name: CValue = switch (struct_ty.tag()) { .union_tagged, .union_safety_tagged => .{ .identifier = "payload" }, @@ -5362,7 +5381,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = zig_wrap_"); try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty); try writer.writeAll("(("); - try f.renderTypecast(writer, field_int_ty); + try f.renderType(writer, field_int_ty); try writer.writeByte(')'); const cant_cast = int_info.bits > 64; if (cant_cast) { @@ -5389,7 +5408,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("));\n"); try freeLocal(f, inst, temp_local.new_local, 0); return local; @@ -5411,7 +5430,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", &"); try f.writeCValue(writer, operand_lval, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("));\n"); if (struct_byval == .constant) { @@ -5442,7 +5461,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { } else try f.writeCValueMember(writer, struct_byval, field_name); if (is_array) { try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll("))"); } try writer.writeAll(";\n"); @@ -5510,7 +5529,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu const local = try f.allocLocal(inst, inst_ty); try f.writeCValue(w, local, .Other); try w.writeAll(" = ("); - try f.renderTypecast(w, inst_ty); + try f.renderType(w, inst_ty); try w.writeByte(')'); try f.writeCValue(w, operand, .Initializer); try w.writeAll(";\n"); @@ -5571,7 +5590,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, payload, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, payload_ty); + try f.renderType(writer, payload_ty); try writer.writeAll("));\n"); } return local; @@ -5691,7 +5710,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, payload, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, payload_ty); + try f.renderType(writer, payload_ty); try writer.writeAll("));\n"); } return local; @@ -5837,7 +5856,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeByte(')'); try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); @@ -5959,7 +5978,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll(";\n"); try writer.writeAll("if ("); try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); - try f.renderTypecast(writer, ptr_ty.childType()); + try f.renderType(writer, ptr_ty.childType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -5988,7 +6007,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll(";\n"); try f.writeCValue(writer, local, .Other); try writer.print(".is_null = zig_cmpxchg_{s}((zig_atomic(", .{flavor}); - try f.renderTypecast(writer, ptr_ty.childType()); + try f.renderType(writer, ptr_ty.childType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -6031,12 +6050,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { switch (extra.op()) { else => { try writer.writeAll("zig_atomic("); - try f.renderTypecast(writer, ptr_ty.elemType()); + try f.renderType(writer, ptr_ty.elemType()); try writer.writeByte(')'); }, .Nand, .Min, .Max => { // These are missing from stdatomic.h, so no atomic types for now. - try f.renderTypecast(writer, ptr_ty.elemType()); + try f.renderType(writer, ptr_ty.elemType()); }, } if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); @@ -6073,7 +6092,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = zig_atomic_load((zig_atomic("); - try f.renderTypecast(writer, ptr_ty.elemType()); + try f.renderType(writer, ptr_ty.elemType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -6096,7 +6115,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa const writer = f.object.writer(); try writer.writeAll("zig_atomic_store((zig_atomic("); - try f.renderTypecast(writer, ptr_ty.elemType()); + try f.renderType(writer, ptr_ty.elemType()); try writer.writeByte(')'); if (ptr_ty.isVolatilePtr()) try writer.writeAll(" volatile"); try writer.writeAll(" *)"); @@ -6138,7 +6157,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" += "); try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other); try writer.writeAll(") (("); - try f.renderTypecast(writer, u8_ptr_ty); + try f.renderType(writer, u8_ptr_ty); try writer.writeByte(')'); try f.writeCValue(writer, dest_ptr, .FunctionArgument); try writer.writeAll(")["); @@ -6514,7 +6533,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { .Auto, .Extern => { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = ("); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeAll(")"); try writer.writeByte('{'); var empty = true; @@ -6557,7 +6576,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(", "); try f.writeCValue(writer, resolved_element, .FunctionArgument); try writer.writeAll(", sizeof("); - try f.renderTypecast(writer, element_ty); + try f.renderType(writer, element_ty); try writer.writeAll("));\n"); } }, @@ -6602,11 +6621,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { try f.renderIntCast(writer, inst_ty, element, field_ty, .FunctionArgument); } else { try writer.writeByte('('); - try f.renderTypecast(writer, inst_ty); + try f.renderType(writer, inst_ty); try writer.writeByte(')'); if (field_ty.isPtrAtRuntime()) { try writer.writeByte('('); - try f.renderTypecast(writer, switch (int_info.signedness) { + try f.renderType(writer, switch (int_info.signedness) { .unsigned => Type.usize, .signed => Type.isize, }); diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig index b9e171db57..6bbd6ad7ef 100644 --- a/test/behavior/field_parent_ptr.zig +++ b/test/behavior/field_parent_ptr.zig @@ -48,7 +48,6 @@ fn testParentFieldPtrFirst(a: *const bool) !void { test "@fieldParentPtr untagged union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrUnion(&bar.c); @@ -75,7 +74,6 @@ fn testFieldParentPtrUnion(c: *const i32) !void { test "@fieldParentPtr tagged union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrTaggedUnion(&bar_tagged.c); @@ -102,7 +100,6 @@ fn testFieldParentPtrTaggedUnion(c: *const i32) !void { test "@fieldParentPtr extern union" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try testFieldParentPtrExternUnion(&bar_extern.c); diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index e1237a578b..85214bd7d8 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -603,7 +603,6 @@ test "packed struct initialized in bitcast" { test "pointer to container level packed struct field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; From 597e8011f7b2ea76755165fa5a09b2f725180268 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 22 Feb 2023 23:32:54 -0500 Subject: [PATCH 105/122] CType: fix lowering of generic function pointer --- src/codegen/c/type.zig | 29 +++++++++++++++++------------ test/behavior/pointers.zig | 1 - 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index bf148a8b87..04c0ea3003 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -1296,19 +1296,21 @@ pub const CType = extern union { .Fn => { const info = ty.fnInfo(); - if (lookup.isMutable()) { - const param_kind: Kind = switch (kind) { - .forward, .forward_parameter => .forward_parameter, - .complete, .parameter, .global => .parameter, - .payload => unreachable, - }; - _ = try lookup.typeToIndex(info.return_type, param_kind); - for (info.param_types) |param_type| { - if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; - _ = try lookup.typeToIndex(param_type, param_kind); + if (!info.is_generic) { + if (lookup.isMutable()) { + const param_kind: Kind = switch (kind) { + .forward, .forward_parameter => .forward_parameter, + .complete, .parameter, .global => .parameter, + .payload => unreachable, + }; + _ = try lookup.typeToIndex(info.return_type, param_kind); + for (info.param_types) |param_type| { + if (!param_type.hasRuntimeBitsIgnoreComptime()) continue; + _ = try lookup.typeToIndex(param_type, param_kind); + } } - } - self.init(if (info.is_var_args) .varargs_function else .function); + self.init(if (info.is_var_args) .varargs_function else .function); + } else self.init(.void); }, } } @@ -1619,6 +1621,7 @@ pub const CType = extern union { .varargs_function, => { const info = ty.fnInfo(); + assert(!info.is_generic); const param_kind: Kind = switch (kind) { .forward, .forward_parameter => .forward_parameter, .complete, .parameter, .global => .parameter, @@ -1764,6 +1767,7 @@ pub const CType = extern union { if (ty.zigTypeTag() != .Fn) return false; const info = ty.fnInfo(); + assert(!info.is_generic); const data = cty.cast(Payload.Function).?.data; const param_kind: Kind = switch (self.kind) { .forward, .forward_parameter => .forward_parameter, @@ -1878,6 +1882,7 @@ pub const CType = extern union { .varargs_function, => { const info = ty.fnInfo(); + assert(!info.is_generic); const param_kind: Kind = switch (self.kind) { .forward, .forward_parameter => .forward_parameter, .complete, .parameter, .global => .parameter, diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 2d55292916..ec4ff332cf 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -507,7 +507,6 @@ test "ptrCast comptime known slice to C pointer" { } test "ptrToInt on a generic function" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From d3c9bfada64230ad17badb739f15a492b4c2c065 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 22 Feb 2023 17:23:03 -0700 Subject: [PATCH 106/122] std.Build.WriteFileStep: integrate with cache system And additionally support writing files to source files. This means a custom build step in zig's own build.zig is no longer needed for copying zig.h because it is handled by WriteFileStep. --- build.zig | 31 +--- lib/std/Build/WriteFileStep.zig | 244 ++++++++++++++++++++++---------- 2 files changed, 175 insertions(+), 100 deletions(-) diff --git a/build.zig b/build.zig index 28109d8fed..a95c9dfb58 100644 --- a/build.zig +++ b/build.zig @@ -509,35 +509,8 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { run_opt.addArg("-o"); run_opt.addFileSourceArg(.{ .path = "stage1/zig1.wasm" }); - const CopyFileStep = struct { - const Step = std.Build.Step; - const FileSource = std.Build.FileSource; - const CopyFileStep = @This(); - - step: Step, - builder: *std.Build, - source: FileSource, - dest_rel_path: []const u8, - - pub fn init(builder: *std.Build, source: FileSource, dest_rel_path: []const u8) CopyFileStep { - return CopyFileStep{ - .builder = builder, - .step = Step.init(.custom, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make), - .source = source.dupe(builder), - .dest_rel_path = builder.dupePath(dest_rel_path), - }; - } - - fn make(step: *Step) !void { - const self = @fieldParentPtr(CopyFileStep, "step", step); - const full_src_path = self.source.getPath(self.builder); - const full_dest_path = self.builder.pathFromRoot(self.dest_rel_path); - try self.builder.updateFile(full_src_path, full_dest_path); - } - }; - - const copy_zig_h = try b.allocator.create(CopyFileStep); - copy_zig_h.* = CopyFileStep.init(b, .{ .path = "lib/zig.h" }, "stage1/zig.h"); + const copy_zig_h = b.addWriteFiles(); + copy_zig_h.addCopyFileToSource(.{ .path = "lib/zig.h" }, "stage1/zig.h"); const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm"); update_zig1_step.dependOn(&run_opt.step); diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 1621295ad8..3a30aba190 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -1,55 +1,117 @@ -const std = @import("../std.zig"); -const Step = std.Build.Step; -const fs = std.fs; -const ArrayList = std.ArrayList; - -const WriteFileStep = @This(); - -pub const base_id = .write_file; +//! WriteFileStep is primarily used to create a directory in an appropriate +//! location inside the local cache which has a set of files that have either +//! been generated during the build, or are copied from the source package. +//! +//! However, this step has an additional capability of writing data to paths +//! relative to the package root, effectively mutating the package's source +//! files. Be careful with the latter functionality; it should not be used +//! during the normal build process, but as a utility run by a developer with +//! intention to update source files, which will then be committed to version +//! control. step: Step, builder: *std.Build, -files: std.TailQueue(File), +/// The elements here are pointers because we need stable pointers for the +/// GeneratedFile field. +files: std.ArrayListUnmanaged(*File), +output_source_files: std.ArrayListUnmanaged(OutputSourceFile), + +pub const base_id = .write_file; pub const File = struct { - source: std.Build.GeneratedFile, - basename: []const u8, + generated_file: std.Build.GeneratedFile, + sub_path: []const u8, + contents: Contents, +}; + +pub const OutputSourceFile = struct { + contents: Contents, + sub_path: []const u8, +}; + +pub const Contents = union(enum) { bytes: []const u8, + copy: std.Build.FileSource, }; pub fn init(builder: *std.Build) WriteFileStep { - return WriteFileStep{ + return .{ .builder = builder, .step = Step.init(.write_file, "writefile", builder.allocator, make), .files = .{}, + .output_source_files = .{}, }; } -pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { - const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error"); - node.* = .{ - .data = .{ - .source = std.Build.GeneratedFile{ .step = &self.step }, - .basename = self.builder.dupePath(basename), - .bytes = self.builder.dupe(bytes), - }, +pub fn add(wf: *WriteFileStep, sub_path: []const u8, bytes: []const u8) void { + const gpa = wf.builder.allocator; + const file = gpa.create(File) catch @panic("OOM"); + file.* = .{ + .generated_file = .{ .step = &wf.step }, + .sub_path = wf.builder.dupePath(sub_path), + .contents = .{ .bytes = wf.builder.dupe(bytes) }, }; - - self.files.append(node); + wf.files.append(gpa, file) catch @panic("OOM"); } -/// Gets a file source for the given basename. If the file does not exist, returns `null`. -pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource { - var it = step.files.first; - while (it) |node| : (it = node.next) { - if (std.mem.eql(u8, node.data.basename, basename)) - return std.Build.FileSource{ .generated = &node.data.source }; +/// Place the file into the generated directory within the local cache, +/// along with all the rest of the files added to this step. The parameter +/// here is the destination path relative to the local cache directory +/// associated with this WriteFileStep. It may be a basename, or it may +/// include sub-directories, in which case this step will ensure the +/// required sub-path exists. +/// This is the option expected to be used most commonly with `addCopyFile`. +pub fn addCopyFile(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { + const gpa = wf.builder.allocator; + const file = gpa.create(File) catch @panic("OOM"); + file.* = .{ + .generated_file = .{ .step = &wf.step }, + .sub_path = wf.builder.dupePath(sub_path), + .contents = .{ .copy = source }, + }; + wf.files.append(gpa, file) catch @panic("OOM"); +} + +/// A path relative to the package root. +/// Be careful with this because it updates source files. This should not be +/// used as part of the normal build process, but as a utility occasionally +/// run by a developer with intent to modify source files and then commit +/// those changes to version control. +/// A file added this way is not available with `getFileSource`. +pub fn addCopyFileToSource(wf: *WriteFileStep, source: std.Build.FileSource, sub_path: []const u8) void { + wf.output_source_files.append(wf.builder.allocator, .{ + .contents = .{ .copy = source }, + .sub_path = sub_path, + }) catch @panic("OOM"); +} + +/// Gets a file source for the given sub_path. If the file does not exist, returns `null`. +pub fn getFileSource(wf: *WriteFileStep, sub_path: []const u8) ?std.Build.FileSource { + for (wf.files.items) |file| { + if (std.mem.eql(u8, file.sub_path, sub_path)) { + return .{ .generated = &file.generated_file }; + } } return null; } fn make(step: *Step) !void { - const self = @fieldParentPtr(WriteFileStep, "step", step); + const wf = @fieldParentPtr(WriteFileStep, "step", step); + + // Writing to source files is kind of an extra capability of this + // WriteFileStep - arguably it should be a different step. But anyway here + // it is, it happens unconditionally and does not interact with the other + // files here. + for (wf.output_source_files.items) |output_source_file| { + const basename = fs.path.basename(output_source_file.sub_path); + if (fs.path.dirname(output_source_file.sub_path)) |dirname| { + var dir = try wf.builder.build_root.handle.makeOpenPath(dirname, .{}); + defer dir.close(); + try writeFile(wf, dir, output_source_file.contents, basename); + } else { + try writeFile(wf, wf.builder.build_root.handle, output_source_file.contents, basename); + } + } // The cache is used here not really as a way to speed things up - because writing // the data to a file would probably be very fast - but as a way to find a canonical @@ -58,56 +120,96 @@ fn make(step: *Step) !void { // If, for example, a hard-coded path was used as the location to put WriteFileStep // files, then two WriteFileSteps executing in parallel might clobber each other. - // TODO port the cache system from the compiler to zig std lib. Until then - // we directly construct the path, and no "cache hit" detection happens; - // the files are always written. - // Note there is similar code over in ConfigHeaderStep. - const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); + var man = wf.builder.cache.obtain(); + defer man.deinit(); + // Random bytes to make WriteFileStep unique. Refresh this with // new random bytes when WriteFileStep implementation is modified // in a non-backwards-compatible way. - var hash = Hasher.init("eagVR1dYXoE7ARDP"); + man.hash.add(@as(u32, 0xd767ee59)); - { - var it = self.files.first; - while (it) |node| : (it = node.next) { - hash.update(node.data.basename); - hash.update(node.data.bytes); - hash.update("|"); + for (wf.files.items) |file| { + man.hash.addBytes(file.sub_path); + switch (file.contents) { + .bytes => |bytes| { + man.hash.addBytes(bytes); + }, + .copy => |file_source| { + _ = try man.addFile(file_source.getPath(wf.builder), null); + }, } } - var digest: [16]u8 = undefined; - hash.final(&digest); - var hash_basename: [digest.len * 2]u8 = undefined; - _ = std.fmt.bufPrint( - &hash_basename, - "{s}", - .{std.fmt.fmtSliceHexLower(&digest)}, - ) catch unreachable; - const output_dir = try self.builder.cache_root.join(self.builder.allocator, &.{ - "o", &hash_basename, - }); - var dir = fs.cwd().makeOpenPath(output_dir, .{}) catch |err| { - std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) }); - return err; - }; - defer dir.close(); - { - var it = self.files.first; - while (it) |node| : (it = node.next) { - dir.writeFile(node.data.basename, node.data.bytes) catch |err| { - std.debug.print("unable to write {s} into {s}: {s}\n", .{ - node.data.basename, - output_dir, - @errorName(err), - }); - return err; - }; - node.data.source.path = try fs.path.join( - self.builder.allocator, - &[_][]const u8{ output_dir, node.data.basename }, + if (man.hit() catch |err| failWithCacheError(man, err)) { + // Cache hit, skip writing file data. + const digest = man.final(); + for (wf.files.items) |file| { + file.generated_file.path = try wf.builder.cache_root.join( + wf.builder.allocator, + &.{ "o", &digest, file.sub_path }, ); } + return; + } + + const digest = man.final(); + const cache_path = "o" ++ fs.path.sep_str ++ digest; + + var cache_dir = wf.builder.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| { + std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); + return err; + }; + defer cache_dir.close(); + + for (wf.files.items) |file| { + const basename = fs.path.basename(file.sub_path); + if (fs.path.dirname(file.sub_path)) |dirname| { + var dir = try wf.builder.cache_root.handle.makeOpenPath(dirname, .{}); + defer dir.close(); + try writeFile(wf, dir, file.contents, basename); + } else { + try writeFile(wf, cache_dir, file.contents, basename); + } + + file.generated_file.path = try wf.builder.cache_root.join( + wf.builder.allocator, + &.{ cache_path, file.sub_path }, + ); + } + + try man.writeManifest(); +} + +fn writeFile(wf: *WriteFileStep, dir: fs.Dir, contents: Contents, basename: []const u8) !void { + // TODO after landing concurrency PR, improve error reporting here + switch (contents) { + .bytes => |bytes| return dir.writeFile(basename, bytes), + .copy => |file_source| { + const source_path = file_source.getPath(wf.builder); + const prev_status = try fs.Dir.updateFile(fs.cwd(), source_path, dir, basename, .{}); + _ = prev_status; // TODO logging (affected by open PR regarding concurrency) + }, } } + +/// TODO consolidate this with the same function in RunStep? +/// Also properly deal with concurrency (see open PR) +fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { + const i = man.failed_file_index orelse failWithSimpleError(err); + const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); + const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; + std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); + std.process.exit(1); +} + +fn failWithSimpleError(err: anyerror) noreturn { + std.debug.print("{s}\n", .{@errorName(err)}); + std.process.exit(1); +} + +const std = @import("../std.zig"); +const Step = std.Build.Step; +const fs = std.fs; +const ArrayList = std.ArrayList; + +const WriteFileStep = @This(); From 57f6adf85da58c0c91a036deaa614c30df010b78 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 00:28:49 -0500 Subject: [PATCH 107/122] CBE: implement c varargs Removed some backend test skip checks for things disabled in std. --- lib/zig.h | 1 + src/codegen/c.zig | 93 ++++++++++++++++++++++++++++++++++++-- test/behavior/var_args.zig | 15 +++--- 3 files changed, 97 insertions(+), 12 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index d336ecb2e2..67f64635c5 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -5,6 +5,7 @@ #endif #include #include +#include #include #include diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b5269acc5c..390e8f8f4e 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -211,6 +211,15 @@ const reserved_idents = std.ComptimeStringMap(void, .{ .{ "volatile", {} }, .{ "while ", {} }, + // stdarg.h + .{ "va_start", {} }, + .{ "va_arg", {} }, + .{ "va_end", {} }, + .{ "va_copy", {} }, + + // stddef.h + .{ "offsetof", {} }, + // windows.h .{ "max", {} }, .{ "min", {} }, @@ -2952,10 +2961,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), - .c_va_arg => return f.fail("TODO implement c_va_arg", .{}), - .c_va_copy => return f.fail("TODO implement c_va_copy", .{}), - .c_va_end => return f.fail("TODO implement c_va_end", .{}), - .c_va_start => return f.fail("TODO implement c_va_start", .{}), + .c_va_start => try airCVaStart(f, inst), + .c_va_arg => try airCVaArg(f, inst), + .c_va_end => try airCVaEnd(f, inst), + .c_va_copy => try airCVaCopy(f, inst), // zig fmt: on }; if (result_value == .new_local) { @@ -6862,6 +6871,82 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { return local; } +fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { + if (f.liveness.isUnused(inst)) return .none; + + const inst_ty = f.air.typeOfIndex(inst); + const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); + + const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; + if (param_len == 0) + return f.fail("CBE: C requires at least one runtime argument for varargs functions", .{}); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("va_start(*(va_list *)&"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(", "); + try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); + try writer.writeAll(");\n"); + return local; +} + +fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return .none; + } + + const inst_ty = f.air.typeOfIndex(inst); + const va_list = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = va_arg(*(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(", "); + try f.renderType(writer, f.air.getRefType(ty_op.ty)); + try writer.writeAll(");\n"); + return local; +} + +fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue { + const un_op = f.air.instructions.items(.data)[inst].un_op; + + const va_list = try f.resolveInst(un_op); + try reap(f, inst, &.{un_op}); + + const writer = f.object.writer(); + try writer.writeAll("va_end(*(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(");\n"); + return .none; +} + +fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue { + const ty_op = f.air.instructions.items(.data)[inst].ty_op; + if (f.liveness.isUnused(inst)) { + try reap(f, inst, &.{ty_op.operand}); + return .none; + } + + const inst_ty = f.air.typeOfIndex(inst); + const va_list = try f.resolveInst(ty_op.operand); + try reap(f, inst, &.{ty_op.operand}); + + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + try writer.writeAll("va_copy(*(va_list *)&"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(", *(va_list *)"); + try f.writeCValue(writer, va_list, .Other); + try writer.writeAll(");\n"); + return local; +} + fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 { return switch (order) { // Note: unordered is actually even less atomic than relaxed diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 97f90b559d..6431ca9470 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -96,10 +96,9 @@ fn doNothingWithFirstArg(args: anytype) void { test "simple variadic function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -124,8 +123,10 @@ test "simple variadic function" { } }; - try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); - try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); + if (builtin.zig_backend != .stage2_c) { // C doesn't support varargs without a preceding runtime arg. + try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); + try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); + } try std.testing.expectEqual(@as(c_int, 0), S.add(0)); try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1))); try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2))); @@ -134,10 +135,9 @@ test "simple variadic function" { test "variadic functions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } @@ -178,10 +178,9 @@ test "variadic functions" { test "copy VaList" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO - if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos and builtin.zig_backend == .stage2_llvm) { + if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) { // https://github.com/ziglang/zig/issues/14096 return error.SkipZigTest; } From a0d7fd162b7568c0291ebcfc561a2852c7077e15 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 05:16:23 -0500 Subject: [PATCH 108/122] CBE: support call attributes * Support always_tail and never_tail/never_inline with a comptime callee using clang * Support never_inline using gcc * Support never_inline using msvc Unfortunately, can't enable behavior tests because of the conditional support. --- lib/zig.h | 26 +++ src/codegen/c.zig | 496 ++++++++++++++++++++++++---------------------- src/link/C.zig | 35 ++-- src/target.zig | 1 + 4 files changed, 304 insertions(+), 254 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 67f64635c5..fb0573a049 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -78,6 +78,32 @@ typedef char bool; #define zig_cold #endif +#if zig_has_attribute(flatten) +#define zig_maybe_flatten __attribute__((flatten)) +#else +#define zig_maybe_flatten +#endif + +#if zig_has_attribute(noinline) +#define zig_never_inline __attribute__((noinline)) zig_maybe_flatten +#elif defined(_MSC_VER) +#define zig_never_inline __declspec(noinline) zig_maybe_flatten +#else +#define zig_never_inline zig_never_inline_unavailable +#endif + +#if zig_has_attribute(not_tail_called) +#define zig_never_tail __attribute__((not_tail_called)) zig_never_inline +#else +#define zig_never_tail zig_never_tail_unavailable +#endif + +#if zig_has_attribute(always_inline) +#define zig_always_tail __attribute__((musttail)) +#else +#define zig_always_tail zig_always_tail_unavailable +#endif + #if __STDC_VERSION__ >= 199901L #define zig_restrict restrict #elif defined(__GNUC__) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 390e8f8f4e..4d42758773 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -23,7 +23,6 @@ const libcFloatSuffix = target_util.libcFloatSuffix; const compilerRtFloatAbbrev = target_util.compilerRtFloatAbbrev; const compilerRtIntAbbrev = target_util.compilerRtIntAbbrev; -const Mutability = enum { @"const", mut }; const BigIntLimb = std.math.big.Limb; const BigInt = std.math.big.int; @@ -55,6 +54,8 @@ pub const CValue = union(enum) { /// Render these bytes literally. /// TODO make this a [*:0]const u8 to save memory bytes: []const u8, + /// A deferred call_always_tail + call_always_tail: void, }; const BlockData = struct { @@ -62,21 +63,22 @@ const BlockData = struct { result: CValue, }; -const TypedefKind = enum { - Forward, - Complete, -}; - pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue); pub const LazyFnKey = union(enum) { tag_name: Decl.Index, + never_tail: Decl.Index, + never_inline: Decl.Index, }; pub const LazyFnValue = struct { fn_name: []const u8, - data: union { + data: Data, + + pub const Data = union { tag_name: Type, - }, + never_tail: void, + never_inline: void, + }; }; pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue); @@ -314,7 +316,7 @@ pub const Function = struct { const gpa = f.object.dg.gpa; try f.allocs.put(gpa, decl_c_value.new_local, true); try writer.writeAll("static "); - try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .@"const", alignment, .Complete); + try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, alignment, .complete); try writer.writeAll(" = "); try f.object.dg.renderValue(writer, ty, val, .StaticInitializer); try writer.writeAll(";\n "); @@ -348,15 +350,13 @@ pub const Function = struct { } fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { - const result = try f.allocAlignedLocal(ty, .mut, 0); + const result = try f.allocAlignedLocal(ty, .{}, 0); log.debug("%{d}: allocating t{d}", .{ inst, result.new_local }); return result; } /// Only allocates the local; does not print anything. - fn allocAlignedLocal(f: *Function, ty: Type, mutability: Mutability, alignment: u32) !CValue { - _ = mutability; - + fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue { if (f.getFreeLocals().getPtrContext(ty, f.tyHashCtx())) |locals_list| { for (locals_list.items, 0..) |local_index, i| { const local = &f.locals.items[local_index]; @@ -451,11 +451,9 @@ pub const Function = struct { return f.object.dg.fmtIntLiteral(ty, val); } - fn getTagNameFn(f: *Function, enum_ty: Type) ![]const u8 { + fn getLazyFnName(f: *Function, key: LazyFnKey, data: LazyFnValue.Data) ![]const u8 { const gpa = f.object.dg.gpa; - const owner_decl = enum_ty.getOwnerDecl(); - - const gop = try f.lazy_fns.getOrPut(gpa, .{ .tag_name = owner_decl }); + const gop = try f.lazy_fns.getOrPut(gpa, key); if (!gop.found_existing) { errdefer _ = f.lazy_fns.pop(); @@ -464,11 +462,21 @@ pub const Function = struct { const arena = promoted.arena.allocator(); gop.value_ptr.* = .{ - .fn_name = try std.fmt.allocPrint(arena, "zig_tagName_{}__{d}", .{ - fmtIdent(mem.span(f.object.dg.module.declPtr(owner_decl).name)), - @enumToInt(owner_decl), - }), - .data = .{ .tag_name = try enum_ty.copy(arena) }, + .fn_name = switch (key) { + .tag_name, + .never_tail, + .never_inline, + => |owner_decl| try std.fmt.allocPrint(arena, "zig_{s}_{}__{d}", .{ + @tagName(key), + fmtIdent(mem.span(f.object.dg.module.declPtr(owner_decl).name)), + @enumToInt(owner_decl), + }), + }, + .data = switch (key) { + .tag_name => .{ .tag_name = try data.tag_name.copy(arena) }, + .never_tail => .{ .never_tail = data.never_tail }, + .never_inline => .{ .never_inline = data.never_inline }, + }, }; } return gop.value_ptr.fn_name; @@ -1457,24 +1465,31 @@ pub const DeclGen = struct { } } - fn renderFunctionSignature(dg: *DeclGen, w: anytype, kind: TypedefKind, export_index: u32) !void { + fn renderFunctionSignature( + dg: *DeclGen, + w: anytype, + fn_decl_index: Decl.Index, + kind: CType.Kind, + name: union(enum) { + export_index: u32, + string: []const u8, + }, + ) !void { const store = &dg.ctypes.set; const module = dg.module; - const fn_ty = dg.decl.?.ty; - const fn_cty_idx = try dg.typeToIndex(fn_ty, switch (kind) { - .Forward => .forward, - .Complete => .complete, - }); + const fn_decl = module.declPtr(fn_decl_index); + const fn_cty_idx = try dg.typeToIndex(fn_decl.ty, kind); - const fn_info = fn_ty.fnInfo(); + const fn_info = fn_decl.ty.fnInfo(); if (fn_info.cc == .Naked) { switch (kind) { - .Forward => try w.writeAll("zig_naked_decl "), - .Complete => try w.writeAll("zig_naked "), + .forward => try w.writeAll("zig_naked_decl "), + .complete => try w.writeAll("zig_naked "), + else => unreachable, } } - if (dg.decl.?.val.castTag(.function)) |func_payload| + if (fn_decl.val.castTag(.function)) |func_payload| if (func_payload.data.is_cold) try w.writeAll("zig_cold "); if (fn_info.return_type.tag() == .noreturn) try w.writeAll("zig_noreturn "); @@ -1485,7 +1500,7 @@ pub const DeclGen = struct { w, fn_cty_idx, .suffix, - CQualifiers.init(.{}), + .{}, ); try w.print("{}", .{trailing}); @@ -1493,16 +1508,37 @@ pub const DeclGen = struct { try w.print("zig_callconv({s}) ", .{call_conv}); } - if (fn_info.alignment > 0 and kind == .Complete) { - try w.print(" zig_align_fn({})", .{fn_info.alignment}); + switch (kind) { + .forward => {}, + .complete => if (fn_info.alignment > 0) + try w.print(" zig_align_fn({})", .{fn_info.alignment}), + else => unreachable, } - try dg.renderDeclName(w, dg.decl_index.unwrap().?, export_index); + switch (name) { + .export_index => |export_index| try dg.renderDeclName(w, fn_decl_index, export_index), + .string => |string| try w.writeAll(string), + } - try renderTypeSuffix(dg.decl_index, store.*, module, w, fn_cty_idx, .suffix); + try renderTypeSuffix( + dg.decl_index, + store.*, + module, + w, + fn_cty_idx, + .suffix, + CQualifiers.init(.{ .@"const" = switch (kind) { + .forward => false, + .complete => true, + else => unreachable, + } }), + ); - if (fn_info.alignment > 0 and kind == .Forward) { - try w.print(" zig_align_fn({})", .{fn_info.alignment}); + switch (kind) { + .forward => if (fn_info.alignment > 0) + try w.print(" zig_align_fn({})", .{fn_info.alignment}), + .complete => {}, + else => unreachable, } } @@ -1533,16 +1569,8 @@ pub const DeclGen = struct { const store = &dg.ctypes.set; const module = dg.module; const idx = try dg.typeToIndex(t, .complete); - _ = try renderTypePrefix( - dg.decl_index, - store.*, - module, - w, - idx, - .suffix, - CQualifiers.init(.{}), - ); - try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix); + _ = try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); + try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); } const IntCastContext = union(enum) { @@ -1655,9 +1683,9 @@ pub const DeclGen = struct { w: anytype, ty: Type, name: CValue, - mutability: Mutability, + qualifiers: CQualifiers, alignment: u32, - _: TypedefKind, + kind: CType.Kind, ) error{ OutOfMemory, AnalysisFail }!void { const store = &dg.ctypes.set; const module = dg.module; @@ -1668,71 +1696,12 @@ pub const DeclGen = struct { .gt => try w.print("zig_align({}) ", .{alignment}), }; - const idx = try dg.typeToIndex(ty, .complete); - const trailing = try renderTypePrefix( - dg.decl_index, - store.*, - module, - w, - idx, - .suffix, - CQualifiers.init(.{ .@"const" = mutability == .@"const" }), - ); + const idx = try dg.typeToIndex(ty, kind); + const trailing = + try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, qualifiers); try w.print("{}", .{trailing}); try dg.writeCValue(w, name); - try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix); - } - - fn renderTagNameFn(dg: *DeclGen, w: anytype, fn_name: []const u8, enum_ty: Type) !void { - const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); - - try w.writeAll("static "); - try dg.renderType(w, name_slice_ty); - try w.writeByte(' '); - try w.writeAll(fn_name); - try w.writeByte('('); - try dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, .@"const", 0, .Complete); - try w.writeAll(") {\n switch (tag) {\n"); - for (enum_ty.enumFields().keys(), 0..) |name, index| { - const name_z = try dg.gpa.dupeZ(u8, name); - defer dg.gpa.free(name_z); - const name_bytes = name_z[0 .. name_z.len + 1]; - - var tag_pl: Value.Payload.U32 = .{ - .base = .{ .tag = .enum_field_index }, - .data = @intCast(u32, index), - }; - const tag_val = Value.initPayload(&tag_pl.base); - - var int_pl: Value.Payload.U64 = undefined; - const int_val = tag_val.enumToInt(enum_ty, &int_pl); - - var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; - const name_ty = Type.initPayload(&name_ty_pl.base); - - var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_bytes }; - const name_val = Value.initPayload(&name_pl.base); - - var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; - const len_val = Value.initPayload(&len_pl.base); - - try w.print(" case {}: {{\n static ", .{try dg.fmtIntLiteral(enum_ty, int_val)}); - try dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, .@"const", 0, .Complete); - try w.writeAll(" = "); - try dg.renderValue(w, name_ty, name_val, .Initializer); - try w.writeAll(";\n return ("); - try dg.renderType(w, name_slice_ty); - try w.print("){{{}, {}}};\n", .{ - fmtIdent("name"), try dg.fmtIntLiteral(Type.usize, len_val), - }); - - try w.writeAll(" }\n"); - } - try w.writeAll(" }\n while ("); - try dg.renderValue(w, Type.bool, Value.true, .Other); - try w.writeAll(") "); - _ = try airBreakpoint(w); - try w.writeAll("}\n"); + try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); } fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { @@ -1771,6 +1740,7 @@ pub const DeclGen = struct { fmtIdent(ident), }), .bytes => |bytes| return w.writeAll(bytes), + .call_always_tail => return dg.fail("CBE: the result of @call(.always_tail, ...) must be returned directly", .{}), } } @@ -1804,6 +1774,7 @@ pub const DeclGen = struct { try w.writeAll(bytes); return w.writeByte(')'); }, + .call_always_tail => return dg.writeCValue(w, c_value), } } @@ -1816,7 +1787,16 @@ pub const DeclGen = struct { fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { switch (c_value) { .none, .constant, .field, .undef => unreachable, - .new_local, .local, .arg, .arg_array, .decl, .identifier, .payload_identifier, .bytes => { + .new_local, + .local, + .arg, + .arg_array, + .decl, + .identifier, + .payload_identifier, + .bytes, + .call_always_tail, + => { try dg.writeCValue(writer, c_value); try writer.writeAll("->"); }, @@ -1945,7 +1925,8 @@ pub const DeclGen = struct { const CTypeFix = enum { prefix, suffix }; const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict }); -const CTypeRenderTrailing = enum { +const Const = CQualifiers.init(.{ .@"const" = true }); +const RenderCTypeTrailing = enum { no_space, maybe_space, @@ -2004,8 +1985,8 @@ fn renderTypePrefix( idx: CType.Index, parent_fix: CTypeFix, qualifiers: CQualifiers, -) @TypeOf(w).Error!CTypeRenderTrailing { - var trailing = CTypeRenderTrailing.maybe_space; +) @TypeOf(w).Error!RenderCTypeTrailing { + var trailing = RenderCTypeTrailing.maybe_space; const cty = store.indexToCType(idx); switch (cty.tag()) { @@ -2147,7 +2128,7 @@ fn renderTypePrefix( w, cty.cast(CType.Payload.Function).?.data.return_type, .suffix, - CQualifiers.init(.{}), + .{}, ); switch (parent_fix) { .prefix => { @@ -2174,6 +2155,7 @@ fn renderTypeSuffix( w: anytype, idx: CType.Index, parent_fix: CTypeFix, + qualifiers: CQualifiers, ) @TypeOf(w).Error!void { const cty = store.indexToCType(idx); switch (cty.tag()) { @@ -2220,7 +2202,15 @@ fn renderTypeSuffix( .pointer_const, .pointer_volatile, .pointer_const_volatile, - => try renderTypeSuffix(decl, store, mod, w, cty.cast(CType.Payload.Child).?.data, .prefix), + => try renderTypeSuffix( + decl, + store, + mod, + w, + cty.cast(CType.Payload.Child).?.data, + .prefix, + .{}, + ), .array, .vector, @@ -2238,6 +2228,7 @@ fn renderTypeSuffix( w, cty.cast(CType.Payload.Sequence).?.data.elem_type, .suffix, + .{}, ); }, @@ -2272,17 +2263,10 @@ fn renderTypeSuffix( for (data.param_types, 0..) |param_type, param_i| { if (need_comma) try w.writeAll(", "); need_comma = true; - const trailing = try renderTypePrefix( - decl, - store, - mod, - w, - param_type, - .suffix, - CQualifiers.init(.{ .@"const" = true }), - ); - try w.print("{}a{d}", .{ trailing, param_i }); - try renderTypeSuffix(decl, store, mod, w, param_type, .suffix); + const trailing = + try renderTypePrefix(decl, store, mod, w, param_type, .suffix, qualifiers); + if (qualifiers.contains(.@"const")) try w.print("{}a{d}", .{ trailing, param_i }); + try renderTypeSuffix(decl, store, mod, w, param_type, .suffix, .{}); } switch (tag) { .function => {}, @@ -2296,7 +2280,7 @@ fn renderTypeSuffix( if (!need_comma) try w.writeAll("void"); try w.writeByte(')'); - try renderTypeSuffix(decl, store, mod, w, data.return_type, .suffix); + try renderTypeSuffix(decl, store, mod, w, data.return_type, .suffix, .{}); }, } } @@ -2316,17 +2300,9 @@ fn renderAggregateFields( .eq => {}, .gt => try writer.print("zig_align({}) ", .{field.alignas.getAlign()}), } - const trailing = try renderTypePrefix( - .none, - store, - mod, - writer, - field.type, - .suffix, - CQualifiers.init(.{}), - ); + const trailing = try renderTypePrefix(.none, store, mod, writer, field.type, .suffix, .{}); try writer.print("{}{ }", .{ trailing, fmtIdent(mem.span(field.name)) }); - try renderTypeSuffix(.none, store, mod, writer, field.type, .suffix); + try renderTypeSuffix(.none, store, mod, writer, field.type, .suffix, .{}); try writer.writeAll(";\n"); } try writer.writeByteNTimes(' ', indent); @@ -2347,25 +2323,9 @@ pub fn genTypeDecl( switch (global_cty.tag()) { .fwd_anon_struct => if (decl != .none) { try writer.writeAll("typedef "); - _ = try renderTypePrefix( - .none, - global_store, - mod, - writer, - global_idx, - .suffix, - CQualifiers.init(.{}), - ); + _ = try renderTypePrefix(.none, global_store, mod, writer, global_idx, .suffix, .{}); try writer.writeByte(' '); - _ = try renderTypePrefix( - decl, - decl_store, - mod, - writer, - decl_idx, - .suffix, - CQualifiers.init(.{}), - ); + _ = try renderTypePrefix(decl, decl_store, mod, writer, decl_idx, .suffix, .{}); try writer.writeAll(";\n"); }, @@ -2383,15 +2343,7 @@ pub fn genTypeDecl( .fwd_union, => { const owner_decl = global_cty.cast(CType.Payload.FwdDecl).?.data; - _ = try renderTypePrefix( - .none, - global_store, - mod, - writer, - global_idx, - .suffix, - CQualifiers.init(.{}), - ); + _ = try renderTypePrefix(.none, global_store, mod, writer, global_idx, .suffix, .{}); try writer.writeAll("; // "); try mod.declPtr(owner_decl).renderFullyQualifiedName(mod, writer); try writer.writeByte('\n'); @@ -2467,7 +2419,7 @@ pub fn genErrDecls(o: *Object) !void { const name_val = Value.initPayload(&name_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, .@"const", 0, .Complete); + try o.dg.renderTypeAndName(writer, name_ty, .{ .identifier = identifier }, Const, 0, .complete); try writer.writeAll(" = "); try o.dg.renderValue(writer, name_ty, name_val, .StaticInitializer); try writer.writeAll(";\n"); @@ -2480,7 +2432,7 @@ pub fn genErrDecls(o: *Object) !void { const name_array_ty = Type.initPayload(&name_array_ty_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, .@"const", 0, .Complete); + try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, Const, 0, .complete); try writer.writeAll(" = {"); for (o.dg.module.error_name_list.items, 0..) |name, value| { if (value != 0) try writer.writeByte(','); @@ -2503,7 +2455,7 @@ fn genExports(o: *Object) !void { if (o.dg.module.decl_exports.get(o.dg.decl_index.unwrap().?)) |exports| { for (exports.items[1..], 1..) |@"export", i| { try fwd_decl_writer.writeAll("zig_export("); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, @intCast(u32, i)); + try o.dg.renderFunctionSignature(fwd_decl_writer, o.dg.decl_index.unwrap().?, .forward, .{ .export_index = @intCast(u32, i) }); try fwd_decl_writer.print(", {s}, {s});\n", .{ fmtStringLiteral(exports.items[0].options.name), fmtStringLiteral(@"export".options.name), @@ -2513,13 +2465,85 @@ fn genExports(o: *Object) !void { } pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { - const writer = o.writer(); - switch (lazy_fn.key_ptr.*) { - .tag_name => _ = try o.dg.renderTagNameFn( - writer, - lazy_fn.value_ptr.fn_name, - lazy_fn.value_ptr.data.tag_name, - ), + const w = o.writer(); + const key = lazy_fn.key_ptr.*; + const val = lazy_fn.value_ptr; + const fn_name = val.fn_name; + switch (key) { + .tag_name => { + const enum_ty = val.data.tag_name; + + const name_slice_ty = Type.initTag(.const_slice_u8_sentinel_0); + + try w.writeAll("static "); + try o.dg.renderType(w, name_slice_ty); + try w.writeByte(' '); + try w.writeAll(fn_name); + try w.writeByte('('); + try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, 0, .complete); + try w.writeAll(") {\n switch (tag) {\n"); + for (enum_ty.enumFields().keys(), 0..) |name, index| { + const name_z = try o.dg.gpa.dupeZ(u8, name); + defer o.dg.gpa.free(name_z); + const name_bytes = name_z[0 .. name_z.len + 1]; + + var tag_pl: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, index), + }; + const tag_val = Value.initPayload(&tag_pl.base); + + var int_pl: Value.Payload.U64 = undefined; + const int_val = tag_val.enumToInt(enum_ty, &int_pl); + + var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; + const name_ty = Type.initPayload(&name_ty_pl.base); + + var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_bytes }; + const name_val = Value.initPayload(&name_pl.base); + + var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; + const len_val = Value.initPayload(&len_pl.base); + + try w.print(" case {}: {{\n static ", .{try o.dg.fmtIntLiteral(enum_ty, int_val)}); + try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, 0, .complete); + try w.writeAll(" = "); + try o.dg.renderValue(w, name_ty, name_val, .Initializer); + try w.writeAll(";\n return ("); + try o.dg.renderType(w, name_slice_ty); + try w.print("){{{}, {}}};\n", .{ + fmtIdent("name"), try o.dg.fmtIntLiteral(Type.usize, len_val), + }); + + try w.writeAll(" }\n"); + } + try w.writeAll(" }\n while ("); + try o.dg.renderValue(w, Type.bool, Value.true, .Other); + try w.writeAll(") "); + _ = try airBreakpoint(w); + try w.writeAll("}\n"); + }, + .never_tail, .never_inline => |fn_decl_index| { + const fn_decl = o.dg.module.declPtr(fn_decl_index); + const fn_cty = try o.dg.typeToCType(fn_decl.ty, .complete); + const fn_info = fn_cty.cast(CType.Payload.Function).?.data; + + const fwd_decl_writer = o.dg.fwd_decl.writer(); + try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); + try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ .string = fn_name }); + try fwd_decl_writer.writeAll(";\n"); + + try w.print("static zig_{s} ", .{@tagName(key)}); + try o.dg.renderFunctionSignature(w, fn_decl_index, .complete, .{ .string = fn_name }); + try w.writeAll(" {\n return "); + try o.dg.renderDeclName(w, fn_decl_index, 0); + try w.writeByte('('); + for (0..fn_info.param_types.len) |arg| { + if (arg > 0) try w.writeAll(", "); + try o.dg.writeCValue(w, .{ .arg = arg }); + } + try w.writeAll(");\n}\n"); + }, } } @@ -2529,6 +2553,7 @@ pub fn genFunc(f: *Function) !void { const o = &f.object; const gpa = o.dg.gpa; + const decl_index = o.dg.decl_index.unwrap().?; const tv: TypedValue = .{ .ty = o.dg.decl.?.ty, .val = o.dg.decl.?.val, @@ -2540,13 +2565,13 @@ pub fn genFunc(f: *Function) !void { const is_global = o.dg.declIsGlobal(tv); const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, 0); + try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 }); try fwd_decl_writer.writeAll(";\n"); try genExports(o); try o.indent_writer.insertNewline(); if (!is_global) try o.writer().writeAll("static "); - try o.dg.renderFunctionSignature(o.writer(), .Complete, 0); + try o.dg.renderFunctionSignature(o.writer(), decl_index, .complete, .{ .export_index = 0 }); try o.writer().writeByte(' '); // In case we need to use the header, populate it with a copy of the function @@ -2600,9 +2625,9 @@ pub fn genFunc(f: *Function) !void { w, local.ty, .{ .local = local_index }, - .mut, + .{}, local.alignment, - .Complete, + .complete, ); try w.writeAll(";\n "); } @@ -2628,7 +2653,7 @@ pub fn genDecl(o: *Object) !void { if (tv.val.tag() == .extern_fn) { const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll("zig_extern "); - try o.dg.renderFunctionSignature(fwd_decl_writer, .Forward, 0); + try o.dg.renderFunctionSignature(fwd_decl_writer, decl_c_value.decl, .forward, .{ .export_index = 0 }); try fwd_decl_writer.writeAll(";\n"); try genExports(o); } else if (tv.val.castTag(.variable)) |var_payload| { @@ -2639,7 +2664,7 @@ pub fn genDecl(o: *Object) !void { try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); if (variable.is_threadlocal) try fwd_decl_writer.writeAll("zig_threadlocal "); - try o.dg.renderTypeAndName(fwd_decl_writer, decl.ty, decl_c_value, .mut, decl.@"align", .Complete); + try o.dg.renderTypeAndName(fwd_decl_writer, decl.ty, decl_c_value, .{}, decl.@"align", .complete); try fwd_decl_writer.writeAll(";\n"); try genExports(o); @@ -2649,7 +2674,7 @@ pub fn genDecl(o: *Object) !void { if (!is_global) try w.writeAll("static "); if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); - try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .mut, decl.@"align", .Complete); + try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .{}, decl.@"align", .complete); if (decl.@"linksection" != null) try w.writeAll(", read, write)"); try w.writeAll(" = "); try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); @@ -2660,13 +2685,13 @@ pub fn genDecl(o: *Object) !void { const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); - try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .@"const", decl.@"align", .Complete); + try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, Const, decl.@"align", .complete); try fwd_decl_writer.writeAll(";\n"); const w = o.writer(); if (!is_global) try w.writeAll("static "); if (decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); - try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .@"const", decl.@"align", .Complete); + try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, Const, decl.@"align", .complete); if (decl.@"linksection" != null) try w.writeAll(", read)"); try w.writeAll(" = "); try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); @@ -2689,7 +2714,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { const is_global = dg.declIsGlobal(tv); if (is_global) { try writer.writeAll("zig_extern "); - try dg.renderFunctionSignature(writer, .Complete, 0); + try dg.renderFunctionSignature(writer, dg.decl_index.unwrap().?, .complete, .{ .export_index = 0 }); try dg.fwd_decl.appendSlice(";\n"); } }, @@ -2879,10 +2904,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .dbg_block_begin, .dbg_block_end, - => CValue{ .none = {} }, + => .none, .call => try airCall(f, inst, .auto), - .call_always_tail => try airCall(f, inst, .always_tail), + .call_always_tail => .call_always_tail, .call_never_tail => try airCall(f, inst, .never_tail), .call_never_inline => try airCall(f, inst, .never_inline), @@ -3199,9 +3224,12 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { return CValue{ .undef = inst_ty }; } - const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut; const target = f.object.dg.module.getTarget(); - const local = try f.allocAlignedLocal(elem_type, mutability, inst_ty.ptrAlignment(target)); + const local = try f.allocAlignedLocal( + elem_type, + CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }), + inst_ty.ptrAlignment(target), + ); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; try f.allocs.put(gpa, local.new_local, false); @@ -3216,9 +3244,12 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { return CValue{ .undef = inst_ty }; } - const mutability: Mutability = if (inst_ty.isConstPtr()) .@"const" else .mut; const target = f.object.dg.module.getTarget(); - const local = try f.allocAlignedLocal(elem_ty, mutability, inst_ty.ptrAlignment(target)); + const local = try f.allocAlignedLocal( + elem_ty, + CQualifiers.init(.{ .@"const" = inst_ty.isConstPtr() }), + inst_ty.ptrAlignment(target), + ); log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; try f.allocs.put(gpa, local.new_local, false); @@ -3336,10 +3367,19 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { var lowered_ret_buf: LowerFnRetTyBuffer = undefined; const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); - if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { - var deref = is_ptr; + const is_naked = if (f.object.dg.decl) |decl| decl.ty.fnCallingConvention() == .Naked else false; + const peek_operand = f.value_map.get(un_op); + if (if (peek_operand) |operand| operand == .call_always_tail else false) { + try reap(f, inst, &.{un_op}); + if (is_naked) { + try f.writeCValue(writer, peek_operand.?, .Other); + unreachable; + } + _ = try airCall(f, Air.refToIndex(un_op).?, .always_tail); + } else if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); + var deref = is_ptr; const is_array = lowersToArray(ret_ty, target); const ret_val = if (is_array) ret_val: { const array_local = try f.allocLocal(inst, try lowered_ret_ty.copy(f.arena.allocator())); @@ -3368,9 +3408,8 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { } } else { try reap(f, inst, &.{un_op}); - if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() != .Naked) - // Not even allowed to return void in a naked function. - try writer.writeAll("return;\n"); + // Not even allowed to return void in a naked function. + if (!is_naked) try writer.writeAll("return;\n"); } return CValue.none; } @@ -4004,13 +4043,6 @@ fn airCall( const target = module.getTarget(); const writer = f.object.writer(); - switch (modifier) { - .auto => {}, - .always_tail => return f.fail("TODO: C backend: call with always_tail attribute", .{}), - .never_tail => return f.fail("TODO: C backend: call with never_tail attribute", .{}), - .never_inline => return f.fail("TODO: C backend: call with never_inline attribute", .{}), - else => unreachable, - } const pl_op = f.air.instructions.items(.data)[inst].pl_op; const extra = f.air.extraData(Air.Call, pl_op.payload); const args = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra.end..][0..extra.data.args_len]); @@ -4060,7 +4092,10 @@ fn airCall( var lowered_ret_buf: LowerFnRetTyBuffer = undefined; const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); - const result_local: CValue = if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) + const result_local: CValue = if (modifier == .always_tail) r: { + try writer.writeAll("zig_always_tail return "); + break :r .none; + } else if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) .none else if (f.liveness.isUnused(inst)) r: { try writer.writeByte('('); @@ -4074,26 +4109,33 @@ fn airCall( break :r local; }; - var is_extern = false; - var name: [*:0]const u8 = ""; callee: { known: { const fn_decl = fn_decl: { const callee_val = f.air.value(pl_op.operand) orelse break :known; break :fn_decl switch (callee_val.tag()) { - .extern_fn => blk: { - is_extern = true; - break :blk callee_val.castTag(.extern_fn).?.data.owner_decl; - }, + .extern_fn => callee_val.castTag(.extern_fn).?.data.owner_decl, .function => callee_val.castTag(.function).?.data.owner_decl, .decl_ref => callee_val.castTag(.decl_ref).?.data, else => break :known, }; }; - name = module.declPtr(fn_decl).name; - try f.object.dg.renderDeclName(writer, fn_decl, 0); + switch (modifier) { + .auto, .always_tail => try f.object.dg.renderDeclName(writer, fn_decl, 0), + inline .never_tail, .never_inline => |mod| try writer.writeAll(try f.getLazyFnName( + @unionInit(LazyFnKey, @tagName(mod), fn_decl), + @unionInit(LazyFnValue.Data, @tagName(mod), {}), + )), + else => unreachable, + } break :callee; } + switch (modifier) { + .auto, .always_tail => {}, + .never_tail => return f.fail("CBE: runtime callee with never_tail attribute unsupported", .{}), + .never_inline => return f.fail("CBE: runtime callee with never_inline attribute unsupported", .{}), + else => unreachable, + } // Fall back to function pointer call. try f.writeCValue(writer, callee, .Other); } @@ -4704,14 +4746,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("register "); const alignment = 0; const local_value = try f.allocLocalValue(output_ty, alignment); - try f.object.dg.renderTypeAndName( - writer, - output_ty, - local_value, - .mut, - alignment, - .Complete, - ); + try f.object.dg.renderTypeAndName(writer, output_ty, local_value, .{}, alignment, .complete); try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["={".len .. constraint.len - "}".len]); try writer.writeAll("\")"); @@ -4743,14 +4778,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { if (is_reg) try writer.writeAll("register "); const alignment = 0; const local_value = try f.allocLocalValue(input_ty, alignment); - try f.object.dg.renderTypeAndName( - writer, - input_ty, - local_value, - .@"const", - alignment, - .Complete, - ); + try f.object.dg.renderTypeAndName(writer, input_ty, local_value, Const, alignment, .complete); if (is_reg) { try writer.writeAll(" __asm(\""); try writer.writeAll(constraint["{".len .. constraint.len - "}".len]); @@ -6278,7 +6306,9 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); try f.writeCValue(writer, local, .Other); - try writer.print(" = {s}(", .{try f.getTagNameFn(enum_ty)}); + try writer.print(" = {s}(", .{ + try f.getLazyFnName(.{ .tag_name = enum_ty.getOwnerDecl() }, .{ .tag_name = enum_ty }), + }); try f.writeCValue(writer, operand, .Other); try writer.writeAll(");\n"); diff --git a/src/link/C.zig b/src/link/C.zig index 262e4e4923..5663ba71e2 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -247,8 +247,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) const abi_define = abiDefine(comp); - // Covers defines, zig.h, ctypes, asm, lazy fwd, lazy code. - try f.all_buffers.ensureUnusedCapacity(gpa, 6); + // Covers defines, zig.h, ctypes, asm, lazy fwd. + try f.all_buffers.ensureUnusedCapacity(gpa, 5); if (abi_define) |buf| f.appendBufAssumeCapacity(buf); f.appendBufAssumeCapacity(zig_h); @@ -263,8 +263,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) f.appendBufAssumeCapacity(asm_buf.items); } - const lazy_indices = f.all_buffers.items.len; - f.all_buffers.items.len += 2; + const lazy_index = f.all_buffers.items.len; + f.all_buffers.items.len += 1; try self.flushErrDecls(&f.lazy_db); @@ -297,6 +297,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) { // We need to flush lazy ctypes after flushing all decls but before flushing any decl ctypes. + // This ensures that every lazy CType.Index exactly matches the global CType.Index. assert(f.ctypes.count() == 0); try self.flushCTypes(&f, .none, f.lazy_db.ctypes); @@ -305,30 +306,22 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) try self.flushCTypes(&f, entry.key_ptr.toOptional(), entry.value_ptr.ctypes); } - { - f.all_buffers.items[lazy_indices + 0] = .{ - .iov_base = if (f.lazy_db.fwd_decl.items.len > 0) f.lazy_db.fwd_decl.items.ptr else "", - .iov_len = f.lazy_db.fwd_decl.items.len, - }; - f.file_size += f.lazy_db.fwd_decl.items.len; - - f.all_buffers.items[lazy_indices + 1] = .{ - .iov_base = if (f.lazy_db.code.items.len > 0) f.lazy_db.code.items.ptr else "", - .iov_len = f.lazy_db.code.items.len, - }; - f.file_size += f.lazy_db.code.items.len; - } - f.all_buffers.items[ctypes_index] = .{ .iov_base = if (f.ctypes_buf.items.len > 0) f.ctypes_buf.items.ptr else "", .iov_len = f.ctypes_buf.items.len, }; f.file_size += f.ctypes_buf.items.len; + f.all_buffers.items[lazy_index] = .{ + .iov_base = if (f.lazy_db.fwd_decl.items.len > 0) f.lazy_db.fwd_decl.items.ptr else "", + .iov_len = f.lazy_db.fwd_decl.items.len, + }; + f.file_size += f.lazy_db.fwd_decl.items.len; + // Now the code. - try f.all_buffers.ensureUnusedCapacity(gpa, decl_values.len); - for (decl_values) |decl| - f.appendBufAssumeCapacity(decl.code.items); + try f.all_buffers.ensureUnusedCapacity(gpa, 1 + decl_values.len); + f.appendBufAssumeCapacity(f.lazy_db.code.items); + for (decl_values) |decl| f.appendBufAssumeCapacity(decl.code.items); const file = self.base.file.?; try file.setEndPos(f.file_size); diff --git a/src/target.zig b/src/target.zig index 001adad7c2..6d6933e9e7 100644 --- a/src/target.zig +++ b/src/target.zig @@ -723,6 +723,7 @@ pub fn supportsFunctionAlignment(target: std.Target) bool { pub fn supportsTailCall(target: std.Target, backend: std.builtin.CompilerBackend) bool { switch (backend) { .stage1, .stage2_llvm => return @import("codegen/llvm.zig").supportsTailCall(target), + .stage2_c => return true, else => return false, } } From 9d24d0354f83a7cd706726d47a2dc9ac092304e7 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 18:47:09 -0500 Subject: [PATCH 109/122] CBE: fix MSVC diagnostics generated by the behavior tests After this, the last MSVC warnings are in behavior/bugs/529.zig: behavior.c(37971): warning C4133: 'function': incompatible types - from 'A__8479 *' to 'A__8474 *' behavior.c(37974): warning C4133: 'function': incompatible types - from 'A__8480 *' to 'A__8474 *' --- lib/zig.h | 64 +++++++++++---------- src/codegen/c.zig | 140 +++++++++++++++++++++++----------------------- 2 files changed, 106 insertions(+), 98 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index fb0573a049..c10720d1bd 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -1076,7 +1076,7 @@ static inline void zig_vmulo_i16(uint8_t *ov, int16_t *res, int n, \ static inline int##w##_t zig_shls_i##w(int##w##_t lhs, int##w##_t rhs, uint8_t bits) { \ int##w##_t res; \ - if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, rhs, bits)) return res; \ + if ((uint##w##_t)rhs < (uint##w##_t)bits && !zig_shlo_i##w(&res, lhs, (uint8_t)rhs, bits)) return res; \ return lhs < INT##w##_C(0) ? zig_minInt_i(w, bits) : zig_maxInt_i(w, bits); \ } \ \ @@ -2410,39 +2410,47 @@ zig_msvc_atomics(i64, int64_t, 64) #define zig_msvc_flt_atomics(Type, ReprType, suffix) \ static inline bool zig_msvc_cmpxchg_##Type(zig_##Type volatile* obj, zig_##Type* expected, zig_##Type desired) { \ - ReprType comparand = *((ReprType*)expected); \ - ReprType initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, *((ReprType*)&desired), comparand); \ - bool exchanged = initial == comparand; \ - if (!exchanged) { \ - *expected = *((zig_##Type*)&initial); \ - } \ - return exchanged; \ + ReprType exchange; \ + ReprType comparand; \ + ReprType initial; \ + bool success; \ + memcpy(&comparand, expected, sizeof(comparand)); \ + memcpy(&exchange, &desired, sizeof(exchange)); \ + initial = _InterlockedCompareExchange##suffix((ReprType volatile*)obj, exchange, comparand); \ + success = initial == comparand; \ + if (!success) memcpy(expected, &initial, sizeof(*expected)); \ + return success; \ } \ static inline zig_##Type zig_msvc_atomicrmw_xchg_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - ReprType initial = _InterlockedExchange##suffix((ReprType volatile*)obj, *((ReprType*)&value)); \ - return *((zig_##Type*)&initial); \ + ReprType repr; \ + ReprType initial; \ + zig_##Type result; \ + memcpy(&repr, &value, sizeof(repr)); \ + initial = _InterlockedExchange##suffix((ReprType volatile*)obj, repr); \ + memcpy(&result, &initial, sizeof(result)); \ + return result; \ } \ static inline zig_##Type zig_msvc_atomicrmw_add_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - bool success = false; \ - ReprType new; \ - zig_##Type prev; \ - while (!success) { \ - prev = *obj; \ - new = prev + value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, *((ReprType*)&new)); \ - } \ - return prev; \ + ReprType repr; \ + zig_##Type expected; \ + zig_##Type desired; \ + repr = *(ReprType volatile*)obj; \ + memcpy(&expected, &repr, sizeof(expected)); \ + do { \ + desired = expected + value; \ + } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ + return expected; \ } \ static inline zig_##Type zig_msvc_atomicrmw_sub_##Type(zig_##Type volatile* obj, zig_##Type value) { \ - bool success = false; \ - ReprType new; \ - zig_##Type prev; \ - while (!success) { \ - prev = *obj; \ - new = prev - value; \ - success = zig_msvc_cmpxchg_##Type(obj, &prev, *((ReprType*)&new)); \ - } \ - return prev; \ + ReprType repr; \ + zig_##Type expected; \ + zig_##Type desired; \ + repr = *(ReprType volatile*)obj; \ + memcpy(&expected, &repr, sizeof(expected)); \ + do { \ + desired = expected - value; \ + } while (!zig_msvc_cmpxchg_##Type(obj, &expected, desired)); \ + return expected; \ } zig_msvc_flt_atomics(f32, uint32_t, ) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 4d42758773..5faf7e0b60 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -882,14 +882,14 @@ pub const DeclGen = struct { var literal = stringLiteral(writer); try literal.start(); const c_len = ty.arrayLenIncludingSentinel(); - var index: usize = 0; + var index: u64 = 0; while (index < c_len) : (index += 1) try literal.writeChar(0xaa); return literal.end(); } else { try writer.writeByte('{'); const c_len = ty.arrayLenIncludingSentinel(); - var index: usize = 0; + var index: u64 = 0; while (index < c_len) : (index += 1) { if (index > 0) try writer.writeAll(", "); try dg.renderValue(writer, ty.childType(), val, initializer_type); @@ -1089,8 +1089,8 @@ pub const DeclGen = struct { // First try specific tag representations for more efficiency. switch (val.tag()) { .undef, .empty_struct_value, .empty_array => { - try writer.writeByte('{'); const ai = ty.arrayInfo(); + try writer.writeByte('{'); if (ai.sentinel) |s| { try dg.renderValue(writer, ai.elem_type, s, initializer_type); } else { @@ -1098,13 +1098,19 @@ pub const DeclGen = struct { } try writer.writeByte('}'); }, - .bytes => { - try writer.print("{s}", .{fmtStringLiteral(val.castTag(.bytes).?.data)}); - }, - .str_lit => { - const str_lit = val.castTag(.str_lit).?.data; - const bytes = dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; - try writer.print("{s}", .{fmtStringLiteral(bytes)}); + .bytes, .str_lit => |t| { + const bytes = switch (t) { + .bytes => val.castTag(.bytes).?.data, + .str_lit => bytes: { + const str_lit = val.castTag(.str_lit).?.data; + break :bytes dg.module.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; + }, + else => unreachable, + }; + const sentinel = if (ty.sentinel()) |sentinel| @intCast(u8, sentinel.toUnsignedInt(target)) else null; + try writer.print("{s}", .{ + fmtStringLiteral(bytes[0..@intCast(usize, ty.arrayLen())], sentinel), + }); }, else => { // Fall back to generic implementation. @@ -1128,7 +1134,7 @@ pub const DeclGen = struct { } if (ai.sentinel) |s| { const s_u8 = @intCast(u8, s.toUnsignedInt(target)); - try literal.writeChar(s_u8); + if (s_u8 != 0) try literal.writeChar(s_u8); } try literal.end(); } else { @@ -1638,6 +1644,11 @@ pub const DeclGen = struct { try context.writeValue(dg, w, src_ty, location); } else if (dest_bits <= 64 and src_bits > 64) { assert(!src_is_ptr); + if (dest_bits < 64) { + try w.writeByte('('); + try dg.renderType(w, dest_ty); + try w.writeByte(')'); + } try w.writeAll("zig_lo_"); try dg.renderTypeForBuiltinFnName(w, src_eff_ty); try w.writeByte('('); @@ -2380,9 +2391,7 @@ pub fn genTypeDecl( pub fn genGlobalAsm(mod: *Module, writer: anytype) !void { var it = mod.global_assembly.valueIterator(); - while (it.next()) |asm_source| { - try writer.print("__asm({s});\n", .{fmtStringLiteral(asm_source.*)}); - } + while (it.next()) |asm_source| try writer.print("__asm({s});\n", .{fmtStringLiteral(asm_source.*, null)}); } pub fn genErrDecls(o: *Object) !void { @@ -2400,22 +2409,20 @@ pub fn genErrDecls(o: *Object) !void { o.indent_writer.popIndent(); try writer.writeAll("};\n"); - const name_prefix = "zig_errorName"; - const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + "_".len + max_name_len + 1); + const array_identifier = "zig_errorName"; + const name_prefix = array_identifier ++ "_"; + const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len); defer o.dg.gpa.free(name_buf); - std.mem.copy(u8, name_buf, name_prefix ++ "_"); + std.mem.copy(u8, name_buf, name_prefix); for (o.dg.module.error_name_list.items) |name| { - std.mem.copy(u8, name_buf[name_prefix.len + "_".len ..], name); - name_buf[name_prefix.len + "_".len + name.len] = 0; - - const identifier = name_buf[0 .. name_prefix.len + "_".len + name.len :0]; - const name_z = identifier[name_prefix.len + "_".len ..]; + std.mem.copy(u8, name_buf[name_prefix.len..], name); + const identifier = name_buf[0 .. name_prefix.len + name.len]; var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; const name_ty = Type.initPayload(&name_ty_pl.base); - var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_z }; + var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; const name_val = Value.initPayload(&name_pl.base); try writer.writeAll("static "); @@ -2432,7 +2439,7 @@ pub fn genErrDecls(o: *Object) !void { const name_array_ty = Type.initPayload(&name_array_ty_pl.base); try writer.writeAll("static "); - try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = name_prefix }, Const, 0, .complete); + try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = array_identifier }, Const, 0, .complete); try writer.writeAll(" = {"); for (o.dg.module.error_name_list.items, 0..) |name, value| { if (value != 0) try writer.writeByte(','); @@ -2440,7 +2447,7 @@ pub fn genErrDecls(o: *Object) !void { var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; const len_val = Value.initPayload(&len_pl.base); - try writer.print("{{" ++ name_prefix ++ "_{}, {}}}", .{ + try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ fmtIdent(name), try o.dg.fmtIntLiteral(Type.usize, len_val), }); } @@ -2457,8 +2464,8 @@ fn genExports(o: *Object) !void { try fwd_decl_writer.writeAll("zig_export("); try o.dg.renderFunctionSignature(fwd_decl_writer, o.dg.decl_index.unwrap().?, .forward, .{ .export_index = @intCast(u32, i) }); try fwd_decl_writer.print(", {s}, {s});\n", .{ - fmtStringLiteral(exports.items[0].options.name), - fmtStringLiteral(@"export".options.name), + fmtStringLiteral(exports.items[0].options.name, null), + fmtStringLiteral(@"export".options.name, null), }); } } @@ -2483,10 +2490,6 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, 0, .complete); try w.writeAll(") {\n switch (tag) {\n"); for (enum_ty.enumFields().keys(), 0..) |name, index| { - const name_z = try o.dg.gpa.dupeZ(u8, name); - defer o.dg.gpa.free(name_z); - const name_bytes = name_z[0 .. name_z.len + 1]; - var tag_pl: Value.Payload.U32 = .{ .base = .{ .tag = .enum_field_index }, .data = @intCast(u32, index), @@ -2499,7 +2502,7 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { var name_ty_pl = Type.Payload.Len{ .base = .{ .tag = .array_u8_sentinel_0 }, .data = name.len }; const name_ty = Type.initPayload(&name_ty_pl.base); - var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name_bytes }; + var name_pl = Value.Payload.Bytes{ .base = .{ .tag = .bytes }, .data = name }; const name_val = Value.initPayload(&name_pl.base); var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = name.len }; @@ -3459,15 +3462,17 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); + if (dest_c_bits < 64) { + try writer.writeByte('('); + try f.renderType(writer, inst_ty); + try writer.writeByte(')'); + } + const needs_lo = operand_int_info.bits > 64 and dest_bits <= 64; if (needs_lo) { try writer.writeAll("zig_lo_"); try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty); try writer.writeByte('('); - } else if (dest_c_bits <= 64) { - try writer.writeByte('('); - try f.renderType(writer, inst_ty); - try writer.writeByte(')'); } if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { @@ -4228,8 +4233,9 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { try genBodyInner(f, body); try f.object.indent_writer.insertNewline(); + // label might be unused, add a dummy goto // label must be followed by an expression, add an empty one. - try writer.print("zig_block_{d}:;\n", .{block_id}); + try writer.print("goto zig_block_{d};\nzig_block_{d}: (void)0;\n", .{ block_id, block_id }); return result; } @@ -4608,8 +4614,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { const last_case_i = switch_br.data.cases_len - @boolToInt(switch_br.data.else_body_len == 0); var extra_index: usize = switch_br.end; - var case_i: u32 = 0; - while (case_i < switch_br.data.cases_len) : (case_i += 1) { + for (0..switch_br.data.cases_len) |case_i| { const case = f.air.extraData(Air.SwitchBr.Case, extra_index); const items = @ptrCast([]const Air.Inst.Ref, f.air.extra[case.end..][0..case.data.items_len]); const case_body = f.air.extra[case.end + items.len ..][0..case.data.body_len]; @@ -4789,14 +4794,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); } } - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; - } + for (0..clobbers_len) |_| { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; } { @@ -4851,7 +4853,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("__asm"); if (is_volatile) try writer.writeAll(" volatile"); - try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i])}); + try writer.print("({s}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)}); } extra_i = constraints_extra_begin; @@ -4869,7 +4871,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(' '); if (!std.mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name}); const is_reg = constraint[1] == '{'; - try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint)}); + try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)}); if (is_reg) { try f.writeCValue(writer, .{ .local = locals_index }, .Other); locals_index += 1; @@ -4895,7 +4897,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_reg = constraint[0] == '{'; const input_val = try f.resolveInst(input); - try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint)}); + try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)}); try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { const input_local = CValue{ .local = locals_index }; locals_index += 1; @@ -4904,19 +4906,16 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeByte(')'); } try writer.writeByte(':'); - { - var clobber_i: u32 = 0; - while (clobber_i < clobbers_len) : (clobber_i += 1) { - const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); - // This equation accounts for the fact that even if we have exactly 4 bytes - // for the string, we still use the next u32 for the null terminator. - extra_i += clobber.len / 4 + 1; + for (0..clobbers_len) |clobber_i| { + const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + extra_i += clobber.len / 4 + 1; - if (clobber.len == 0) continue; + if (clobber.len == 0) continue; - if (clobber_i > 0) try writer.writeByte(','); - try writer.print(" {s}", .{fmtStringLiteral(clobber)}); - } + if (clobber_i > 0) try writer.writeByte(','); + try writer.print(" {s}", .{fmtStringLiteral(clobber, null)}); } try writer.writeAll(");\n"); @@ -5340,8 +5339,9 @@ fn fieldPtr( try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, byte_offset_val)}); }, .end => { + try writer.writeByte('('); try f.writeCValue(writer, container_ptr_val, .Other); - try writer.print(" + {}", .{try f.fmtIntLiteral(Type.usize, Value.one)}); + try writer.print(" + {})", .{try f.fmtIntLiteral(Type.usize, Value.one)}); }, } @@ -6448,10 +6448,9 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { // // Equivalent to: // reduce: { - // var i: usize = 0; // var accum: T = init; - // while (i < vec.len) : (i += 1) { - // accum = func(accum, vec[i]); + // for (vec) : (elem) { + // accum = func(accum, elem); // } // break :reduce accum; // } @@ -7162,8 +7161,9 @@ fn stringLiteral(child_stream: anytype) StringLiteral(@TypeOf(child_stream)) { return .{ .counting_writer = std.io.countingWriter(child_stream) }; } +const FormatStringContext = struct { str: []const u8, sentinel: ?u8 }; fn formatStringLiteral( - str: []const u8, + data: FormatStringContext, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype, @@ -7172,13 +7172,13 @@ fn formatStringLiteral( var literal = stringLiteral(writer); try literal.start(); - for (str) |c| - try literal.writeChar(c); + for (data.str) |c| try literal.writeChar(c); + if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel); try literal.end(); } -fn fmtStringLiteral(str: []const u8) std.fmt.Formatter(formatStringLiteral) { - return .{ .data = str }; +fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(formatStringLiteral) { + return .{ .data = .{ .str = str, .sentinel = sentinel } }; } fn undefPattern(comptime IntType: type) IntType { From 8ccdc74949e361b211dade90184ecb160c9340d8 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 19:40:50 -0500 Subject: [PATCH 110/122] CType: cleanup --- src/codegen/c/type.zig | 257 ++++++++++++++++++++--------------------- 1 file changed, 123 insertions(+), 134 deletions(-) diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index 04c0ea3003..bd4b6d9a8d 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -1056,7 +1056,7 @@ pub const CType = extern union { } }, - .Struct, .Union => |zig_tag| if (ty.containerLayout() == .Packed) { + .Struct, .Union => |zig_ty_tag| if (ty.containerLayout() == .Packed) { if (ty.castTag(.@"struct")) |struct_obj| { try self.initType(struct_obj.data.backing_int_ty, kind, lookup); } else { @@ -1068,9 +1068,13 @@ pub const CType = extern union { } } else if (ty.isTupleOrAnonStruct()) { if (lookup.isMutable()) { - for (0..ty.structFieldCount()) |field_i| { + for (0..switch (zig_ty_tag) { + .Struct => ty.structFieldCount(), + .Union => ty.unionFields().count(), + else => unreachable, + }) |field_i| { const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; _ = try lookup.typeToIndex(field_ty, switch (kind) { .forward, .forward_parameter => .forward, @@ -1086,14 +1090,22 @@ pub const CType = extern union { } } self.init(switch (kind) { - .forward, .forward_parameter => .fwd_anon_struct, - .complete, .parameter, .global => .anon_struct, + .forward, .forward_parameter => switch (zig_ty_tag) { + .Struct => .fwd_anon_struct, + .Union => .fwd_anon_union, + else => unreachable, + }, + .complete, .parameter, .global => switch (zig_ty_tag) { + .Struct => .anon_struct, + .Union => .anon_union, + else => unreachable, + }, .payload => unreachable, }); } else { const tag_ty = ty.unionTagTypeSafety(); const is_tagged_union_wrapper = kind != .payload and tag_ty != null; - const is_struct = zig_tag == .Struct or is_tagged_union_wrapper; + const is_struct = zig_ty_tag == .Struct or is_tagged_union_wrapper; switch (kind) { .forward, .forward_parameter => { self.storage = .{ .fwd = .{ @@ -1138,7 +1150,7 @@ pub const CType = extern union { self.init(.void); } else { var is_packed = false; - for (0..switch (zig_tag) { + for (0..switch (zig_ty_tag) { .Struct => ty.structFieldCount(), .Union => ty.unionFields().count(), else => unreachable, @@ -1181,10 +1193,10 @@ pub const CType = extern union { } }, - .Array, .Vector => |zig_tag| { + .Array, .Vector => |zig_ty_tag| { switch (kind) { .forward, .complete, .global => { - const t: Tag = switch (zig_tag) { + const t: Tag = switch (zig_ty_tag) { .Array => .array, .Vector => .vector, else => unreachable, @@ -1501,120 +1513,88 @@ pub const CType = extern union { .@"union", .packed_struct, .packed_union, - => switch (ty.zigTypeTag()) { - .Struct => { - const fields_len = ty.structFieldCount(); + => { + const zig_ty_tag = ty.zigTypeTag(); + const fields_len = switch (zig_ty_tag) { + .Struct => ty.structFieldCount(), + .Union => ty.unionFields().count(), + else => unreachable, + }; - var c_fields_len: usize = 0; - for (0..fields_len) |field_i| { - const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or - !field_ty.hasRuntimeBitsIgnoreComptime()) continue; - c_fields_len += 1; - } + var c_fields_len: usize = 0; + for (0..fields_len) |field_i| { + const field_ty = ty.structFieldType(field_i); + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; + c_fields_len += 1; + } - const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); - var c_field_i: usize = 0; - for (0..fields_len) |field_i| { - const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or - !field_ty.hasRuntimeBitsIgnoreComptime()) continue; + const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); + var c_field_i: usize = 0; + for (0..fields_len) |field_i| { + const field_ty = ty.structFieldType(field_i); + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or + !field_ty.hasRuntimeBitsIgnoreComptime()) continue; - fields_pl[c_field_i] = .{ - .name = try if (ty.isSimpleTuple()) - std.fmt.allocPrintZ(arena, "f{}", .{field_i}) - else - arena.dupeZ(u8, ty.structFieldName(field_i)), - .type = store.set.typeToIndex(field_ty, target, switch (kind) { - .forward, .forward_parameter => .forward, - .complete, .parameter => .complete, - .global => .global, - .payload => unreachable, - }).?, - .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), - }; - c_field_i += 1; - } + defer c_field_i += 1; + fields_pl[c_field_i] = .{ + .name = try if (ty.isSimpleTuple()) + std.fmt.allocPrintZ(arena, "f{}", .{field_i}) + else + arena.dupeZ(u8, switch (zig_ty_tag) { + .Struct => ty.structFieldName(field_i), + .Union => ty.unionFields().keys()[field_i], + else => unreachable, + }), + .type = store.set.typeToIndex(field_ty, target, switch (kind) { + .forward, .forward_parameter => .forward, + .complete, .parameter, .payload => .complete, + .global => .global, + }).?, + .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), + }; + } - switch (t) { - .fwd_anon_struct => { - const anon_pl = try arena.create(Payload.Fields); - anon_pl.* = .{ .base = .{ .tag = t }, .data = fields_pl }; - return initPayload(anon_pl); - }, + switch (t) { + .fwd_anon_struct, + .fwd_anon_union, + => { + const anon_pl = try arena.create(Payload.Fields); + anon_pl.* = .{ .base = .{ .tag = t }, .data = fields_pl }; + return initPayload(anon_pl); + }, - .anon_struct, - .@"struct", - .@"union", - .packed_struct, - .packed_union, - => { - const struct_pl = try arena.create(Payload.Aggregate); - struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .fields = fields_pl, - .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, - } }; - return initPayload(struct_pl); - }, + .unnamed_struct, + .unnamed_union, + .packed_unnamed_struct, + .packed_unnamed_union, + => { + const unnamed_pl = try arena.create(Payload.Unnamed); + unnamed_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .owner_decl = ty.getOwnerDecl(), + .id = if (ty.unionTagTypeSafety()) |_| 0 else unreachable, + } }; + return initPayload(unnamed_pl); + }, - else => unreachable, - } - }, + .anon_struct, + .anon_union, + .@"struct", + .@"union", + .packed_struct, + .packed_union, + => { + const struct_pl = try arena.create(Payload.Aggregate); + struct_pl.* = .{ .base = .{ .tag = t }, .data = .{ + .fields = fields_pl, + .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, + } }; + return initPayload(struct_pl); + }, - .Union => { - const union_fields = ty.unionFields(); - const fields_len = union_fields.count(); - - var c_fields_len: usize = 0; - for (0..fields_len) |field_i| { - const field_ty = ty.structFieldType(field_i); - if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; - c_fields_len += 1; - } - - const fields_pl = try arena.alloc(Payload.Fields.Field, c_fields_len); - var field_i: usize = 0; - var c_field_i: usize = 0; - var field_it = union_fields.iterator(); - while (field_it.next()) |field| { - defer field_i += 1; - if (!field.value_ptr.ty.hasRuntimeBitsIgnoreComptime()) continue; - - fields_pl[c_field_i] = .{ - .name = try arena.dupeZ(u8, field.key_ptr.*), - .type = store.set.typeToIndex(field.value_ptr.ty, target, switch (kind) { - .forward, .forward_parameter => unreachable, - .complete, .parameter, .payload => .complete, - .global => .global, - }).?, - .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), - }; - c_field_i += 1; - } - - switch (kind) { - .forward, .forward_parameter => unreachable, - .complete, .parameter, .global => { - const union_pl = try arena.create(Payload.Aggregate); - union_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .fields = fields_pl, - .fwd_decl = store.set.typeToIndex(ty, target, .forward).?, - } }; - return initPayload(union_pl); - }, - .payload => if (ty.unionTagTypeSafety()) |_| { - const union_pl = try arena.create(Payload.Unnamed); - union_pl.* = .{ .base = .{ .tag = t }, .data = .{ - .fields = fields_pl, - .owner_decl = ty.getOwnerDecl(), - .id = 0, - } }; - return initPayload(union_pl); - } else unreachable, - } - }, - - else => unreachable, + else => unreachable, + } }, .function, @@ -1710,14 +1690,19 @@ pub const CType = extern union { ]u8 = undefined; const c_fields = cty.cast(Payload.Fields).?.data; + const zig_ty_tag = ty.zigTypeTag(); var c_field_i: usize = 0; - for (0..ty.structFieldCount()) |field_i| { + for (0..switch (zig_ty_tag) { + .Struct => ty.structFieldCount(), + .Union => ty.unionFields().count(), + else => unreachable, + }) |field_i| { const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; + defer c_field_i += 1; const c_field = &c_fields[c_field_i]; - c_field_i += 1; if (!self.eqlRecurse(field_ty, c_field.type, switch (self.kind) { .forward, .forward_parameter => .forward, @@ -1728,8 +1713,11 @@ pub const CType = extern union { u8, if (ty.isSimpleTuple()) std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable - else - ty.structFieldName(field_i), + else switch (zig_ty_tag) { + .Struct => ty.structFieldName(field_i), + .Union => ty.unionFields().keys()[field_i], + else => unreachable, + }, mem.span(c_field.name), ) or Payload.Fields.AlignAs.fieldAlign(ty, field_i, target).@"align" != c_field.alignas.@"align") return false; @@ -1828,29 +1816,30 @@ pub const CType = extern union { var name_buf: [ std.fmt.count("f{}", .{std.math.maxInt(usize)}) ]u8 = undefined; + + const zig_ty_tag = ty.zigTypeTag(); for (0..switch (ty.zigTypeTag()) { .Struct => ty.structFieldCount(), .Union => ty.unionFields().count(), else => unreachable, }) |field_i| { const field_ty = ty.structFieldType(field_i); - if (ty.structFieldIsComptime(field_i) or + if ((zig_ty_tag == .Struct and ty.structFieldIsComptime(field_i)) or !field_ty.hasRuntimeBitsIgnoreComptime()) continue; - self.updateHasherRecurse( - hasher, - ty.structFieldType(field_i), - switch (self.kind) { - .forward, .forward_parameter => .forward, - .complete, .parameter => .complete, - .global => .global, - .payload => unreachable, - }, - ); + self.updateHasherRecurse(hasher, field_ty, switch (self.kind) { + .forward, .forward_parameter => .forward, + .complete, .parameter => .complete, + .global => .global, + .payload => unreachable, + }); hasher.update(if (ty.isSimpleTuple()) std.fmt.bufPrint(&name_buf, "f{}", .{field_i}) catch unreachable - else - ty.structFieldName(field_i)); + else switch (zig_ty_tag) { + .Struct => ty.structFieldName(field_i), + .Union => ty.unionFields().keys()[field_i], + else => unreachable, + }); autoHash( hasher, Payload.Fields.AlignAs.fieldAlign(ty, field_i, target).@"align", From 3a1cb62317073b8e599e604b74edf9d10f16d4a2 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 20:11:38 -0500 Subject: [PATCH 111/122] CBE: delete stage1 hacks --- src/codegen/c.zig | 212 +++++++++++++++++++++++----------------------- 1 file changed, 106 insertions(+), 106 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5faf7e0b60..7b09d489d1 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -309,7 +309,7 @@ pub const Function = struct { const val = f.air.value(inst).?; const ty = f.air.typeOf(inst); - const result = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { + const result: CValue = if (lowersToArray(ty, f.object.dg.module.getTarget())) result: { const writer = f.object.code_header.writer(); const alignment = 0; const decl_c_value = try f.allocLocalValue(ty, alignment); @@ -321,7 +321,7 @@ pub const Function = struct { try f.object.dg.renderValue(writer, ty, val, .StaticInitializer); try writer.writeAll(";\n "); break :result decl_c_value; - } else CValue{ .constant = inst }; + } else .{ .constant = inst }; gop.value_ptr.* = result; return result; @@ -346,7 +346,7 @@ pub const Function = struct { .alignment = alignment, .loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1), }); - return CValue{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; + return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; } fn allocLocal(f: *Function, inst: Air.Inst.Index, ty: Type) !CValue { @@ -363,7 +363,7 @@ pub const Function = struct { if (local.alignment >= alignment) { local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); _ = locals_list.swapRemove(i); - return CValue{ .new_local = local_index }; + return .{ .new_local = local_index }; } } } @@ -545,7 +545,7 @@ pub const DeclGen = struct { // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) { - return dg.writeCValue(writer, CValue{ .undef = ty }); + return dg.writeCValue(writer, .{ .undef = ty }); } // Chase function values in order to be able to reference the original function. @@ -2649,7 +2649,7 @@ pub fn genDecl(o: *Object) !void { defer tracy.end(); const decl = o.dg.decl.?; - const decl_c_value: CValue = .{ .decl = o.dg.decl_index.unwrap().? }; + const decl_c_value = .{ .decl = o.dg.decl_index.unwrap().? }; const tv: TypedValue = .{ .ty = decl.ty, .val = decl.val }; if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime()) return; @@ -3011,7 +3011,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -3037,7 +3037,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { !inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const ptr = try f.resolveInst(bin_op.lhs); @@ -3076,7 +3076,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -3117,7 +3117,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { !inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const slice = try f.resolveInst(bin_op.lhs); @@ -3156,7 +3156,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const slice_ty = f.air.typeOf(bin_op.lhs); @@ -3186,7 +3186,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); if (f.liveness.isUnused(inst) or !inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const array = try f.resolveInst(bin_op.lhs); @@ -3224,7 +3224,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { const elem_type = inst_ty.elemType(); if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) { - return CValue{ .undef = inst_ty }; + return .{ .undef = inst_ty }; } const target = f.object.dg.module.getTarget(); @@ -3236,7 +3236,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue { log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; try f.allocs.put(gpa, local.new_local, false); - return CValue{ .local_ref = local.new_local }; + return .{ .local_ref = local.new_local }; } fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3244,7 +3244,7 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { const elem_ty = inst_ty.elemType(); if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime()) { - return CValue{ .undef = inst_ty }; + return .{ .undef = inst_ty }; } const target = f.object.dg.module.getTarget(); @@ -3256,7 +3256,7 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue { log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local }); const gpa = f.object.dg.module.gpa; try f.allocs.put(gpa, local.new_local, false); - return CValue{ .local_ref = local.new_local }; + return .{ .local_ref = local.new_local }; } fn airArg(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3280,7 +3280,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue { (!ptr_info.@"volatile" and f.liveness.isUnused(inst))) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -3414,7 +3414,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { // Not even allowed to return void in a naked function. if (!is_naked) try writer.writeAll("return;\n"); } - return CValue.none; + return .none; } fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3422,7 +3422,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -3443,7 +3443,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -3532,7 +3532,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -3555,7 +3555,7 @@ fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue { try f.renderType(writer, lhs_child_ty); try writer.writeAll("));\n"); } - return CValue.none; + return .none; } fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { @@ -3564,7 +3564,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { const ptr_info = f.air.typeOf(bin_op.lhs).ptrInfo().data; if (!ptr_info.pointee_type.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const ptr_val = try f.resolveInst(bin_op.lhs); @@ -3690,7 +3690,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, src_val, .Other); } try writer.writeAll(";\n"); - return CValue.none; + return .none; } fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue { @@ -3699,7 +3699,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const lhs = try f.resolveInst(bin_op.lhs); @@ -3755,7 +3755,7 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const op = try f.resolveInst(ty_op.operand); @@ -3790,7 +3790,7 @@ fn airBinOp( try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const inst_ty = f.air.typeOfIndex(inst); @@ -3813,7 +3813,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const operand_ty = f.air.typeOf(bin_op.lhs); @@ -3853,7 +3853,7 @@ fn airEquality( if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const operand_ty = f.air.typeOf(bin_op.lhs); @@ -3909,7 +3909,7 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -3930,7 +3930,7 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const lhs = try f.resolveInst(bin_op.lhs); @@ -3971,7 +3971,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -4010,7 +4010,7 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const ptr = try f.resolveInst(bin_op.lhs); @@ -4097,7 +4097,7 @@ fn airCall( var lowered_ret_buf: LowerFnRetTyBuffer = undefined; const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); - const result_local: CValue = if (modifier == .always_tail) r: { + const result_local = if (modifier == .always_tail) r: { try writer.writeAll("zig_always_tail return "); break :r .none; } else if (!lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) @@ -4187,7 +4187,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { // Perhaps an additional compilation option is in order? //try writer.print("#line {d}\n", .{dbg_stmt.line + 1}); try writer.print("/* file:{d}:{d} */\n", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); - return CValue.none; + return .none; } fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4196,7 +4196,7 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { const function = f.air.values[ty_pl.payload].castTag(.function).?.data; const mod = f.object.dg.module; try writer.print("/* dbg func:{s} */\n", .{mod.declPtr(function.owner_decl).name}); - return CValue.none; + return .none; } fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4208,7 +4208,7 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{pl_op.operand}); const writer = f.object.writer(); try writer.print("/* var:{s} */\n", .{name}); - return CValue.none; + return .none; } fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4224,7 +4224,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { const result = if (inst_ty.tag() != .void and !f.liveness.isUnused(inst)) try f.allocLocal(inst, inst_ty) else - CValue{ .none = {} }; + .none; try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{ .block_id = block_id, @@ -4294,7 +4294,7 @@ fn lowerTry( if (!payload_has_bits) { if (!operand_is_ptr) { - return CValue.none; + return .none; } else { return err_union; } @@ -4303,7 +4303,7 @@ fn lowerTry( try reap(f, inst, &.{operand}); if (f.liveness.isUnused(inst)) { - return CValue.none; + return .none; } const target = f.object.dg.module.getTarget(); @@ -4359,7 +4359,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { } try writer.print("goto zig_block_{d};\n", .{block.block_id}); - return CValue.none; + return .none; } fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4369,7 +4369,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { // https://github.com/ziglang/zig/issues/13410 if (f.liveness.isUnused(inst) or !dest_ty.hasRuntimeBits()) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -4441,11 +4441,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { fn airBreakpoint(writer: anytype) !CValue { try writer.writeAll("zig_breakpoint();\n"); - return CValue.none; + return .none; } fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const writer = f.object.writer(); const local = try f.allocLocal(inst, Type.usize); try f.writeCValue(writer, local, .Other); @@ -4456,7 +4456,7 @@ fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { } fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const writer = f.object.writer(); const local = try f.allocLocal(inst, Type.usize); try f.writeCValue(writer, local, .Other); @@ -4474,7 +4474,7 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { try writeMemoryOrder(writer, atomic_order); try writer.writeAll(");\n"); - return CValue.none; + return .none; } fn airUnreach(f: *Function) !CValue { @@ -4482,7 +4482,7 @@ fn airUnreach(f: *Function) !CValue { if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention() == .Naked) return .none; try f.object.writer().writeAll("zig_unreachable();\n"); - return CValue.none; + return .none; } fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4514,7 +4514,7 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { deinitFreeLocalsMap(gpa, new_free_locals); new_free_locals.* = old_free_locals.move(); - return CValue.none; + return .none; } fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4579,7 +4579,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { try f.object.indent_writer.insertNewline(); - return CValue.none; + return .none; } fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -4691,7 +4691,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { f.object.indent_writer.popIndent(); try writer.writeAll("}\n"); - return CValue.none; + return .none; } fn asmInputNeedsLocal(constraint: []const u8, value: CValue) bool { @@ -4713,8 +4713,8 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const inputs = @ptrCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); extra_i += inputs.len; - const result: CValue = r: { - if (!is_volatile and f.liveness.isUnused(inst)) break :r CValue.none; + const result = r: { + if (!is_volatile and f.liveness.isUnused(inst)) break :r .none; const writer = f.object.writer(); const inst_ty = f.air.typeOfIndex(inst); @@ -4899,7 +4899,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const input_val = try f.resolveInst(input); try writer.print("{s}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)}); try f.writeCValue(writer, if (asmInputNeedsLocal(constraint, input_val)) local: { - const input_local = CValue{ .local = locals_index }; + const input_local = .{ .local = locals_index }; locals_index += 1; break :local input_local; } else input_val, .Other); @@ -4932,7 +4932,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { const is_reg = constraint[1] == '{'; if (is_reg) { try f.writeCValueDeref(writer, if (output == .none) - CValue{ .local_ref = local.new_local } + .{ .local_ref = local.new_local } else try f.resolveInst(output)); try writer.writeAll(" = "); @@ -4967,7 +4967,7 @@ fn airIsNull( if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -5017,7 +5017,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -5028,7 +5028,7 @@ fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { const payload_ty = opt_ty.optionalChild(&buf); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5069,7 +5069,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -5080,7 +5080,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) { - return CValue{ .undef = inst_ty }; + return .{ .undef = inst_ty }; } const local = try f.allocLocal(inst, inst_ty); @@ -5112,7 +5112,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { if (opt_ty.optionalReprIsPayload()) { if (f.liveness.isUnused(inst)) { - return CValue.none; + return .none; } const local = try f.allocLocal(inst, inst_ty); // The payload and the optional are the same value. @@ -5129,7 +5129,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); if (f.liveness.isUnused(inst)) { - return CValue.none; + return .none; } const local = try f.allocLocal(inst, inst_ty); @@ -5232,7 +5232,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{extra.field_ptr}); - return CValue.none; + return .none; } const target = f.object.dg.module.getTarget(); @@ -5355,13 +5355,13 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{extra.struct_operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); if (!inst_ty.hasRuntimeBitsIgnoreComptime()) { try reap(f, inst, &.{extra.struct_operand}); - return CValue.none; + return .none; } const target = f.object.dg.module.getTarget(); @@ -5512,7 +5512,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5549,7 +5549,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5560,7 +5560,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) { - if (!is_ptr) return CValue.none; + if (!is_ptr) return .none; const w = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); @@ -5591,7 +5591,7 @@ fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5637,7 +5637,7 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -5691,7 +5691,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); // Then return the payload pointer (only if it is used) - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const local = try f.allocLocal(inst, f.air.typeOfIndex(inst)); try f.writeCValue(writer, local, .Other); @@ -5702,7 +5702,7 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { } fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; return f.fail("TODO: C backend: implement airErrReturnTrace", .{}); } @@ -5720,7 +5720,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5758,7 +5758,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -5796,7 +5796,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -5812,7 +5812,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { // &(*(void *)p)[0], although LLVM does via GetElementPtr if (operand == .undef) { var buf: Type.SlicePtrFieldTypeBuffer = undefined; - try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); + try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(&buf) }, .Initializer); } else if (array_ty.hasRuntimeBitsIgnoreComptime()) { try writer.writeAll("&("); try f.writeCValueDeref(writer, operand); @@ -5834,7 +5834,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -5882,7 +5882,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const operand = try f.resolveInst(un_op); @@ -5910,7 +5910,7 @@ fn airUnBuiltinCall( if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -5942,7 +5942,7 @@ fn airBinBuiltinCall( if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const lhs = try f.resolveInst(bin_op.lhs); @@ -6065,7 +6065,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue if (f.liveness.isUnused(inst)) { try freeLocal(f, inst, local.new_local, 0); - return CValue.none; + return .none; } return local; @@ -6108,7 +6108,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try freeLocal(f, inst, local.new_local, 0); - return CValue.none; + return .none; } return local; @@ -6120,7 +6120,7 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{atomic_load.ptr}); const ptr_ty = f.air.typeOf(atomic_load.ptr); if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) { - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -6163,7 +6163,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa try f.object.dg.renderTypeForBuiltinFnName(writer, ptr_ty.childType()); try writer.writeAll(");\n"); - return CValue.none; + return .none; } fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { @@ -6206,7 +6206,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); try freeLocal(f, inst, index.new_local, 0); - return CValue.none; + return .none; } try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); @@ -6218,7 +6218,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, len, .FunctionArgument); try writer.writeAll(");\n"); - return CValue.none; + return .none; } fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { @@ -6238,7 +6238,7 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, len, .FunctionArgument); try writer.writeAll(");\n"); - return CValue.none; + return .none; } fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { @@ -6251,7 +6251,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const union_ty = f.air.typeOf(bin_op.lhs).childType(); const target = f.object.dg.module.getTarget(); const layout = union_ty.unionGetLayout(target); - if (layout.tag_size == 0) return CValue.none; + if (layout.tag_size == 0) return .none; try writer.writeByte('('); try f.writeCValue(writer, union_ptr, .Other); @@ -6259,7 +6259,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, new_tag, .Other); try writer.writeAll(";\n"); - return CValue.none; + return .none; } fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { @@ -6267,7 +6267,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const operand = try f.resolveInst(ty_op.operand); @@ -6277,7 +6277,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const target = f.object.dg.module.getTarget(); const layout = un_ty.unionGetLayout(target); - if (layout.tag_size == 0) return CValue.none; + if (layout.tag_size == 0) return .none; const inst_ty = f.air.typeOfIndex(inst); const writer = f.object.writer(); @@ -6295,7 +6295,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -6320,7 +6320,7 @@ fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const writer = f.object.writer(); @@ -6340,7 +6340,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[inst].ty_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ty_op.operand}); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); @@ -6356,13 +6356,13 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { } fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; return f.fail("TODO: C backend: implement airSelect", .{}); } fn airShuffle(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; return f.fail("TODO: C backend: implement airShuffle", .{}); } @@ -6372,7 +6372,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{reduce.operand}); - return CValue.none; + return .none; } const target = f.object.dg.module.getTarget(); @@ -6545,7 +6545,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } } - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const target = f.object.dg.module.getTarget(); @@ -6590,7 +6590,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const element_ty = f.air.typeOf(element); try f.writeCValue(writer, switch (element_ty.zigTypeTag()) { - .Array => CValue{ .undef = element_ty }, + .Array => .{ .undef = element_ty }, else => resolved_element, }, .Initializer); empty = false; @@ -6697,7 +6697,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{extra.init}); - return CValue.none; + return .none; } const union_ty = f.air.typeOfIndex(inst); @@ -6756,7 +6756,7 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { // The available prefetch intrinsics do not accept a cache argument; only // address, rw, and locality. So unless the cache is data, we do not lower // this instruction. - .instruction => return CValue.none, + .instruction => return .none, } const ptr = try f.resolveInst(prefetch.ptr); try reap(f, inst, &.{prefetch.ptr}); @@ -6766,11 +6766,11 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue { try writer.print(", {d}, {d});\n", .{ @enumToInt(prefetch.rw), prefetch.locality, }); - return CValue.none; + return .none; } fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue { - if (f.liveness.isUnused(inst)) return CValue.none; + if (f.liveness.isUnused(inst)) return .none; const pl_op = f.air.instructions.items(.data)[inst].pl_op; @@ -6807,7 +6807,7 @@ fn airFloatNeg(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const operand = try f.resolveInst(un_op); @@ -6829,7 +6829,7 @@ fn airUnFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVal const un_op = f.air.instructions.items(.data)[inst].un_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{un_op}); - return CValue.none; + return .none; } const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -6851,7 +6851,7 @@ fn airBinFloatOp(f: *Function, inst: Air.Inst.Index, operation: []const u8) !CVa const bin_op = f.air.instructions.items(.data)[inst].bin_op; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); - return CValue.none; + return .none; } const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); @@ -6878,7 +6878,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data; if (f.liveness.isUnused(inst)) { try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand }); - return CValue.none; + return .none; } const inst_ty = f.air.typeOfIndex(inst); const mulend1 = try f.resolveInst(bin_op.lhs); From c0671a92c7f200a3c32d03db7b7e342c3efdd1fe Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 20:12:02 -0500 Subject: [PATCH 112/122] CBE: simplify always_tail call logic It should be Sema's job to check this anyway. --- src/codegen/c.zig | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 7b09d489d1..a7a069c193 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -54,8 +54,6 @@ pub const CValue = union(enum) { /// Render these bytes literally. /// TODO make this a [*:0]const u8 to save memory bytes: []const u8, - /// A deferred call_always_tail - call_always_tail: void, }; const BlockData = struct { @@ -1751,7 +1749,6 @@ pub const DeclGen = struct { fmtIdent(ident), }), .bytes => |bytes| return w.writeAll(bytes), - .call_always_tail => return dg.fail("CBE: the result of @call(.always_tail, ...) must be returned directly", .{}), } } @@ -1785,7 +1782,6 @@ pub const DeclGen = struct { try w.writeAll(bytes); return w.writeByte(')'); }, - .call_always_tail => return dg.writeCValue(w, c_value), } } @@ -1798,16 +1794,7 @@ pub const DeclGen = struct { fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { switch (c_value) { .none, .constant, .field, .undef => unreachable, - .new_local, - .local, - .arg, - .arg_array, - .decl, - .identifier, - .payload_identifier, - .bytes, - .call_always_tail, - => { + .new_local, .local, .arg, .arg_array, .decl, .identifier, .payload_identifier, .bytes => { try dg.writeCValue(writer, c_value); try writer.writeAll("->"); }, @@ -2910,7 +2897,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, => .none, .call => try airCall(f, inst, .auto), - .call_always_tail => .call_always_tail, + .call_always_tail => .none, .call_never_tail => try airCall(f, inst, .never_tail), .call_never_inline => try airCall(f, inst, .never_inline), @@ -3365,20 +3352,15 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { const un_op = f.air.instructions.items(.data)[inst].un_op; const writer = f.object.writer(); const target = f.object.dg.module.getTarget(); + const op_inst = Air.refToIndex(un_op); const op_ty = f.air.typeOf(un_op); const ret_ty = if (is_ptr) op_ty.childType() else op_ty; var lowered_ret_buf: LowerFnRetTyBuffer = undefined; const lowered_ret_ty = lowerFnRetTy(ret_ty, &lowered_ret_buf, target); - const is_naked = if (f.object.dg.decl) |decl| decl.ty.fnCallingConvention() == .Naked else false; - const peek_operand = f.value_map.get(un_op); - if (if (peek_operand) |operand| operand == .call_always_tail else false) { + if (op_inst != null and f.air.instructions.items(.tag)[op_inst.?] == .call_always_tail) { try reap(f, inst, &.{un_op}); - if (is_naked) { - try f.writeCValue(writer, peek_operand.?, .Other); - unreachable; - } - _ = try airCall(f, Air.refToIndex(un_op).?, .always_tail); + _ = try airCall(f, op_inst.?, .always_tail); } else if (lowered_ret_ty.hasRuntimeBitsIgnoreComptime()) { const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); @@ -3412,7 +3394,8 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { } else { try reap(f, inst, &.{un_op}); // Not even allowed to return void in a naked function. - if (!is_naked) try writer.writeAll("return;\n"); + if (if (f.object.dg.decl) |decl| decl.ty.fnCallingConvention() != .Naked else true) + try writer.writeAll("return;\n"); } return .none; } From 1f3d9f79c19c225b2043a1d0355cbc74addcf03a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 20:29:59 -0500 Subject: [PATCH 113/122] CBE: apply some maybe payload cleanups --- src/codegen/c.zig | 46 +++++++++++++++++----------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a7a069c193..79e0bff237 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -5356,11 +5356,6 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { // Ensure complete type definition is visible before accessing fields. _ = try f.typeToIndex(struct_ty, .complete); - const extra_name: CValue = switch (struct_ty.tag()) { - .union_tagged, .union_safety_tagged => .{ .identifier = "payload" }, - else => .none, - }; - const field_name: CValue = switch (struct_ty.tag()) { .tuple, .anon_struct, .@"struct" => switch (struct_ty.containerLayout()) { .Auto, .Extern => if (struct_ty.isSimpleTuple()) @@ -5458,31 +5453,29 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { } return local; - } else .{ - .identifier = struct_ty.unionFields().keys()[extra.field_index], + } else field_name: { + const name = struct_ty.unionFields().keys()[extra.field_index]; + break :field_name if (struct_ty.unionTagTypeSafety()) |_| + .{ .payload_identifier = name } + else + .{ .identifier = name }; }, else => unreachable, }; - const is_array = lowersToArray(inst_ty, target); const local = try f.allocLocal(inst, inst_ty); - if (is_array) { + if (lowersToArray(inst_ty, target)) { try writer.writeAll("memcpy("); try f.writeCValue(writer, local, .FunctionArgument); try writer.writeAll(", "); - } else { - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); - } - if (extra_name != .none) { - try f.writeCValueMember(writer, struct_byval, extra_name); - try writer.writeByte('.'); - try f.writeCValue(writer, field_name, .Other); - } else try f.writeCValueMember(writer, struct_byval, field_name); - if (is_array) { + try f.writeCValueMember(writer, struct_byval, field_name); try writer.writeAll(", sizeof("); try f.renderType(writer, inst_ty); try writer.writeAll("))"); + } else { + try f.writeCValue(writer, local, .Other); + try writer.writeAll(" = "); + try f.writeCValueMember(writer, struct_byval, field_name); } try writer.writeAll(";\n"); return local; @@ -6700,7 +6693,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { return local; } - if (union_ty.unionTagTypeSafety()) |tag_ty| { + const field: CValue = if (union_ty.unionTagTypeSafety()) |tag_ty| field: { const layout = union_ty.unionGetLayout(target); if (layout.tag_size != 0) { const field_index = tag_ty.enumFieldIndex(field_name).?; @@ -6717,18 +6710,13 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { try f.writeCValue(writer, local, .Other); try writer.print(".tag = {}; ", .{try f.fmtIntLiteral(tag_ty, int_val)}); } - try f.writeCValue(writer, local, .Other); - try writer.print(".payload.{ } = ", .{fmtIdent(field_name)}); - try f.writeCValue(writer, payload, .Other); - try writer.writeAll(";\n"); - return local; - } + break :field .{ .payload_identifier = field_name }; + } else .{ .identifier = field_name }; - try f.writeCValue(writer, local, .Other); - try writer.print(".{ } = ", .{fmtIdent(field_name)}); + try f.writeCValueMember(writer, local, field); + try writer.writeAll(" = "); try f.writeCValue(writer, payload, .Other); try writer.writeAll(";\n"); - return local; } From f8aecef6705a75a4c35754bcac32c27602b84711 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 23 Feb 2023 21:18:26 -0500 Subject: [PATCH 114/122] CBE: implement the future Turns out f(...) will be supported one day. --- src/codegen/c.zig | 9 ++++----- test/behavior/var_args.zig | 11 ++++++++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 79e0bff237..1af14cb372 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6876,17 +6876,16 @@ fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.air.typeOfIndex(inst); const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); - const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; - if (param_len == 0) - return f.fail("CBE: C requires at least one runtime argument for varargs functions", .{}); const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); try writer.writeAll("va_start(*(va_list *)&"); try f.writeCValue(writer, local, .Other); - try writer.writeAll(", "); - try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); + if (param_len > 0) { + try writer.writeAll(", "); + try f.writeCValue(writer, .{ .arg = param_len - 1 }, .FunctionArgument); + } try writer.writeAll(");\n"); return local; } diff --git a/test/behavior/var_args.zig b/test/behavior/var_args.zig index 6431ca9470..cdfbcc9188 100644 --- a/test/behavior/var_args.zig +++ b/test/behavior/var_args.zig @@ -111,6 +111,12 @@ test "simple variadic function" { return @cVaArg(&ap, c_int); } + fn compatible(_: c_int, ...) callconv(.C) c_int { + var ap = @cVaStart(); + defer @cVaEnd(&ap); + return @cVaArg(&ap, c_int); + } + fn add(count: c_int, ...) callconv(.C) c_int { var ap = @cVaStart(); defer @cVaEnd(&ap); @@ -123,10 +129,13 @@ test "simple variadic function" { } }; - if (builtin.zig_backend != .stage2_c) { // C doesn't support varargs without a preceding runtime arg. + if (builtin.zig_backend != .stage2_c) { + // pre C23 doesn't support varargs without a preceding runtime arg. try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0))); try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024))); } + try std.testing.expectEqual(@as(c_int, 0), S.compatible(undefined, @as(c_int, 0))); + try std.testing.expectEqual(@as(c_int, 1024), S.compatible(undefined, @as(c_int, 1024))); try std.testing.expectEqual(@as(c_int, 0), S.add(0)); try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1))); try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2))); From 5f70c36fa88a17c045839b8b422e04db3b545426 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20S?= Date: Fri, 24 Feb 2023 13:00:03 +0100 Subject: [PATCH 115/122] fix RegQueryValueExW api --- lib/std/os/windows/advapi32.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/os/windows/advapi32.zig b/lib/std/os/windows/advapi32.zig index 6d7ea3f8e0..67234a26e0 100644 --- a/lib/std/os/windows/advapi32.zig +++ b/lib/std/os/windows/advapi32.zig @@ -21,10 +21,10 @@ pub extern "advapi32" fn RegOpenKeyExW( pub extern "advapi32" fn RegQueryValueExW( hKey: HKEY, lpValueName: LPCWSTR, - lpReserved: *DWORD, - lpType: *DWORD, - lpData: *BYTE, - lpcbData: *DWORD, + lpReserved: ?*DWORD, + lpType: ?*DWORD, + lpData: ?*BYTE, + lpcbData: ?*DWORD, ) callconv(WINAPI) LSTATUS; // RtlGenRandom is known as SystemFunction036 under advapi32 From 97b9facb98cffa05064315d69a11b96836aa5be3 Mon Sep 17 00:00:00 2001 From: matu3ba Date: Fri, 24 Feb 2023 19:27:02 +0100 Subject: [PATCH 116/122] compiler_rt: declutter int.zig, add mulXi3 tests (#14623) - Combine mulXi3 routines for follow-up cleanup. - DRY up Dwords and Twords - rename both to HalveInt and use instance * Justification: Not all processors have word size 32 bit. * remove test file from CMakeLists * DRY things. --- CMakeLists.txt | 8 +- lib/compiler_rt.zig | 3 +- lib/compiler_rt/common.zig | 18 ++++ lib/compiler_rt/int.zig | 57 ------------- lib/compiler_rt/mulXi3.zig | 101 ++++++++++++++++++++++ lib/compiler_rt/mulXi3_test.zig | 147 ++++++++++++++++++++++++++++++++ lib/compiler_rt/muldi3.zig | 71 --------------- lib/compiler_rt/muldi3_test.zig | 51 ----------- lib/compiler_rt/multi3.zig | 75 ---------------- lib/compiler_rt/multi3_test.zig | 53 ------------ lib/compiler_rt/shift.zig | 64 ++++++-------- 11 files changed, 294 insertions(+), 354 deletions(-) create mode 100644 lib/compiler_rt/mulXi3.zig create mode 100644 lib/compiler_rt/mulXi3_test.zig delete mode 100644 lib/compiler_rt/muldi3.zig delete mode 100644 lib/compiler_rt/muldi3_test.zig delete mode 100644 lib/compiler_rt/multi3.zig delete mode 100644 lib/compiler_rt/multi3_test.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index b761650385..925fd2d639 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -434,13 +434,12 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log10.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/log2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/modti3.zig" + "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulXi3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/muldi3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulo.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulsf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multf3.zig" - "${CMAKE_SOURCE_DIR}/lib/compiler_rt/multi3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/mulxf3.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negXi2.zig" "${CMAKE_SOURCE_DIR}/lib/compiler_rt/negv.zig" @@ -613,7 +612,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/src/link/tapi.zig" "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig" "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig" - "${CMAKE_SOURCE_DIR}/src/link/tapi/parse/test.zig" "${CMAKE_SOURCE_DIR}/src/link/tapi/yaml.zig" "${CMAKE_SOURCE_DIR}/src/main.zig" "${CMAKE_SOURCE_DIR}/src/mingw.zig" @@ -753,7 +751,7 @@ set(BUILD_ZIG2_ARGS --deps build_options -target "${HOST_TARGET_TRIPLE}" ) - + add_custom_command( OUTPUT "${ZIG2_C_SOURCE}" COMMAND zig1 ${BUILD_ZIG2_ARGS} @@ -771,7 +769,7 @@ set(BUILD_COMPILER_RT_ARGS --deps build_options -target "${HOST_TARGET_TRIPLE}" ) - + add_custom_command( OUTPUT "${ZIG_COMPILER_RT_C_SOURCE}" COMMAND zig1 ${BUILD_COMPILER_RT_ARGS} diff --git a/lib/compiler_rt.zig b/lib/compiler_rt.zig index b3fcd6cb80..1cae2a710e 100644 --- a/lib/compiler_rt.zig +++ b/lib/compiler_rt.zig @@ -13,8 +13,7 @@ comptime { _ = @import("compiler_rt/shift.zig"); _ = @import("compiler_rt/negXi2.zig"); _ = @import("compiler_rt/int.zig"); - _ = @import("compiler_rt/muldi3.zig"); - _ = @import("compiler_rt/multi3.zig"); + _ = @import("compiler_rt/mulXi3.zig"); _ = @import("compiler_rt/divti3.zig"); _ = @import("compiler_rt/udivti3.zig"); _ = @import("compiler_rt/modti3.zig"); diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index 40a770070d..ee8aec18cd 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -1,5 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); +const native_endian = builtin.cpu.arch.endian(); pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test) .Internal else .Weak; /// Determines the symbol's visibility to other objects. @@ -221,3 +222,20 @@ pub inline fn fneg(a: anytype) @TypeOf(a) { const negated = @bitCast(U, a) ^ sign_bit_mask; return @bitCast(F, negated); } + +/// Allows to access underlying bits as two equally sized lower and higher +/// signed or unsigned integers. +pub fn HalveInt(comptime T: type, comptime signed_half: bool) type { + return extern union { + pub const bits = @divExact(@typeInfo(T).Int.bits, 2); + pub const HalfTU = std.meta.Int(.unsigned, bits); + pub const HalfTS = std.meta.Int(.signed, bits); + pub const HalfT = if (signed_half) HalfTS else HalfTU; + + all: T, + s: if (native_endian == .Little) + extern struct { low: HalfT, high: HalfT } + else + extern struct { high: HalfT, low: HalfT }, + }; +} diff --git a/lib/compiler_rt/int.zig b/lib/compiler_rt/int.zig index 6a761807dd..47ff9e4c0c 100644 --- a/lib/compiler_rt/int.zig +++ b/lib/compiler_rt/int.zig @@ -16,7 +16,6 @@ pub const panic = common.panic; comptime { @export(__divmodti4, .{ .name = "__divmodti4", .linkage = common.linkage, .visibility = common.visibility }); @export(__udivmoddi4, .{ .name = "__udivmoddi4", .linkage = common.linkage, .visibility = common.visibility }); - @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility }); @export(__divmoddi4, .{ .name = "__divmoddi4", .linkage = common.linkage, .visibility = common.visibility }); if (common.want_aeabi) { @export(__aeabi_idiv, .{ .name = "__aeabi_idiv", .linkage = common.linkage, .visibility = common.visibility }); @@ -663,59 +662,3 @@ fn test_one_umodsi3(a: u32, b: u32, expected_r: u32) !void { const r: u32 = __umodsi3(a, b); try testing.expect(r == expected_r); } - -pub fn __mulsi3(a: i32, b: i32) callconv(.C) i32 { - var ua = @bitCast(u32, a); - var ub = @bitCast(u32, b); - var r: u32 = 0; - - while (ua > 0) { - if ((ua & 1) != 0) r +%= ub; - ua >>= 1; - ub <<= 1; - } - - return @bitCast(i32, r); -} - -fn test_one_mulsi3(a: i32, b: i32, result: i32) !void { - try testing.expectEqual(result, __mulsi3(a, b)); -} - -test "mulsi3" { - try test_one_mulsi3(0, 0, 0); - try test_one_mulsi3(0, 1, 0); - try test_one_mulsi3(1, 0, 0); - try test_one_mulsi3(0, 10, 0); - try test_one_mulsi3(10, 0, 0); - try test_one_mulsi3(0, maxInt(i32), 0); - try test_one_mulsi3(maxInt(i32), 0, 0); - try test_one_mulsi3(0, -1, 0); - try test_one_mulsi3(-1, 0, 0); - try test_one_mulsi3(0, -10, 0); - try test_one_mulsi3(-10, 0, 0); - try test_one_mulsi3(0, minInt(i32), 0); - try test_one_mulsi3(minInt(i32), 0, 0); - try test_one_mulsi3(1, 1, 1); - try test_one_mulsi3(1, 10, 10); - try test_one_mulsi3(10, 1, 10); - try test_one_mulsi3(1, maxInt(i32), maxInt(i32)); - try test_one_mulsi3(maxInt(i32), 1, maxInt(i32)); - try test_one_mulsi3(1, -1, -1); - try test_one_mulsi3(1, -10, -10); - try test_one_mulsi3(-10, 1, -10); - try test_one_mulsi3(1, minInt(i32), minInt(i32)); - try test_one_mulsi3(minInt(i32), 1, minInt(i32)); - try test_one_mulsi3(46340, 46340, 2147395600); - try test_one_mulsi3(-46340, 46340, -2147395600); - try test_one_mulsi3(46340, -46340, -2147395600); - try test_one_mulsi3(-46340, -46340, 2147395600); - try test_one_mulsi3(4194303, 8192, @truncate(i32, 34359730176)); - try test_one_mulsi3(-4194303, 8192, @truncate(i32, -34359730176)); - try test_one_mulsi3(4194303, -8192, @truncate(i32, -34359730176)); - try test_one_mulsi3(-4194303, -8192, @truncate(i32, 34359730176)); - try test_one_mulsi3(8192, 4194303, @truncate(i32, 34359730176)); - try test_one_mulsi3(-8192, 4194303, @truncate(i32, -34359730176)); - try test_one_mulsi3(8192, -4194303, @truncate(i32, -34359730176)); - try test_one_mulsi3(-8192, -4194303, @truncate(i32, 34359730176)); -} diff --git a/lib/compiler_rt/mulXi3.zig b/lib/compiler_rt/mulXi3.zig new file mode 100644 index 0000000000..3999681034 --- /dev/null +++ b/lib/compiler_rt/mulXi3.zig @@ -0,0 +1,101 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const testing = std.testing; +const common = @import("common.zig"); +const native_endian = builtin.cpu.arch.endian(); + +pub const panic = common.panic; + +comptime { + @export(__mulsi3, .{ .name = "__mulsi3", .linkage = common.linkage, .visibility = common.visibility }); + if (common.want_aeabi) { + @export(__aeabi_lmul, .{ .name = "__aeabi_lmul", .linkage = common.linkage, .visibility = common.visibility }); + } else { + @export(__muldi3, .{ .name = "__muldi3", .linkage = common.linkage, .visibility = common.visibility }); + } + if (common.want_windows_v2u64_abi) { + @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = common.linkage, .visibility = common.visibility }); + } else { + @export(__multi3, .{ .name = "__multi3", .linkage = common.linkage, .visibility = common.visibility }); + } +} + +pub fn __mulsi3(a: i32, b: i32) callconv(.C) i32 { + var ua = @bitCast(u32, a); + var ub = @bitCast(u32, b); + var r: u32 = 0; + + while (ua > 0) { + if ((ua & 1) != 0) r +%= ub; + ua >>= 1; + ub <<= 1; + } + + return @bitCast(i32, r); +} + +pub fn __muldi3(a: i64, b: i64) callconv(.C) i64 { + return mulX(i64, a, b); +} + +fn __aeabi_lmul(a: i64, b: i64) callconv(.AAPCS) i64 { + return mulX(i64, a, b); +} + +inline fn mulX(comptime T: type, a: T, b: T) T { + const word_t = common.HalveInt(T, false); + const x = word_t{ .all = a }; + const y = word_t{ .all = b }; + var r = switch (T) { + i64, i128 => word_t{ .all = muldXi(word_t.HalfT, x.s.low, y.s.low) }, + else => unreachable, + }; + r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; + return r.all; +} + +fn DoubleInt(comptime T: type) type { + return switch (T) { + u32 => i64, + u64 => i128, + i32 => i64, + i64 => i128, + else => unreachable, + }; +} + +fn muldXi(comptime T: type, a: T, b: T) DoubleInt(T) { + const DT = DoubleInt(T); + const word_t = common.HalveInt(DT, false); + const bits_in_word_2 = @sizeOf(T) * 8 / 2; + const lower_mask = (~@as(T, 0)) >> bits_in_word_2; + + var r: word_t = undefined; + r.s.low = (a & lower_mask) *% (b & lower_mask); + var t: T = r.s.low >> bits_in_word_2; + r.s.low &= lower_mask; + t += (a >> bits_in_word_2) *% (b & lower_mask); + r.s.low +%= (t & lower_mask) << bits_in_word_2; + r.s.high = t >> bits_in_word_2; + t = r.s.low >> bits_in_word_2; + r.s.low &= lower_mask; + t +%= (b >> bits_in_word_2) *% (a & lower_mask); + r.s.low +%= (t & lower_mask) << bits_in_word_2; + r.s.high +%= t >> bits_in_word_2; + r.s.high +%= (a >> bits_in_word_2) *% (b >> bits_in_word_2); + return r.all; +} + +pub fn __multi3(a: i128, b: i128) callconv(.C) i128 { + return mulX(i128, a, b); +} + +const v2u64 = @Vector(2, u64); + +fn __multi3_windows_x86_64(a: v2u64, b: v2u64) callconv(.C) v2u64 { + return @bitCast(v2u64, mulX(i128, @bitCast(i128, a), @bitCast(i128, b))); +} + +test { + _ = @import("mulXi3_test.zig"); +} diff --git a/lib/compiler_rt/mulXi3_test.zig b/lib/compiler_rt/mulXi3_test.zig new file mode 100644 index 0000000000..128f428af2 --- /dev/null +++ b/lib/compiler_rt/mulXi3_test.zig @@ -0,0 +1,147 @@ +const std = @import("std"); +const testing = std.testing; +const mulXi3 = @import("mulXi3.zig"); +const maxInt = std.math.maxInt; +const minInt = std.math.minInt; + +fn test_one_mulsi3(a: i32, b: i32, result: i32) !void { + try testing.expectEqual(result, mulXi3.__mulsi3(a, b)); +} + +fn test__muldi3(a: i64, b: i64, expected: i64) !void { + const x = mulXi3.__muldi3(a, b); + try testing.expect(x == expected); +} + +fn test__multi3(a: i128, b: i128, expected: i128) !void { + const x = mulXi3.__multi3(a, b); + try testing.expect(x == expected); +} + +test "mulsi3" { + try test_one_mulsi3(0, 0, 0); + try test_one_mulsi3(0, 1, 0); + try test_one_mulsi3(1, 0, 0); + try test_one_mulsi3(0, 10, 0); + try test_one_mulsi3(10, 0, 0); + try test_one_mulsi3(0, maxInt(i32), 0); + try test_one_mulsi3(maxInt(i32), 0, 0); + try test_one_mulsi3(0, -1, 0); + try test_one_mulsi3(-1, 0, 0); + try test_one_mulsi3(0, -10, 0); + try test_one_mulsi3(-10, 0, 0); + try test_one_mulsi3(0, minInt(i32), 0); + try test_one_mulsi3(minInt(i32), 0, 0); + try test_one_mulsi3(1, 1, 1); + try test_one_mulsi3(1, 10, 10); + try test_one_mulsi3(10, 1, 10); + try test_one_mulsi3(1, maxInt(i32), maxInt(i32)); + try test_one_mulsi3(maxInt(i32), 1, maxInt(i32)); + try test_one_mulsi3(1, -1, -1); + try test_one_mulsi3(1, -10, -10); + try test_one_mulsi3(-10, 1, -10); + try test_one_mulsi3(1, minInt(i32), minInt(i32)); + try test_one_mulsi3(minInt(i32), 1, minInt(i32)); + try test_one_mulsi3(46340, 46340, 2147395600); + try test_one_mulsi3(-46340, 46340, -2147395600); + try test_one_mulsi3(46340, -46340, -2147395600); + try test_one_mulsi3(-46340, -46340, 2147395600); + try test_one_mulsi3(4194303, 8192, @truncate(i32, 34359730176)); + try test_one_mulsi3(-4194303, 8192, @truncate(i32, -34359730176)); + try test_one_mulsi3(4194303, -8192, @truncate(i32, -34359730176)); + try test_one_mulsi3(-4194303, -8192, @truncate(i32, 34359730176)); + try test_one_mulsi3(8192, 4194303, @truncate(i32, 34359730176)); + try test_one_mulsi3(-8192, 4194303, @truncate(i32, -34359730176)); + try test_one_mulsi3(8192, -4194303, @truncate(i32, -34359730176)); + try test_one_mulsi3(-8192, -4194303, @truncate(i32, 34359730176)); +} + +test "muldi3" { + try test__muldi3(0, 0, 0); + try test__muldi3(0, 1, 0); + try test__muldi3(1, 0, 0); + try test__muldi3(0, 10, 0); + try test__muldi3(10, 0, 0); + try test__muldi3(0, 81985529216486895, 0); + try test__muldi3(81985529216486895, 0, 0); + + try test__muldi3(0, -1, 0); + try test__muldi3(-1, 0, 0); + try test__muldi3(0, -10, 0); + try test__muldi3(-10, 0, 0); + try test__muldi3(0, -81985529216486895, 0); + try test__muldi3(-81985529216486895, 0, 0); + + try test__muldi3(1, 1, 1); + try test__muldi3(1, 10, 10); + try test__muldi3(10, 1, 10); + try test__muldi3(1, 81985529216486895, 81985529216486895); + try test__muldi3(81985529216486895, 1, 81985529216486895); + + try test__muldi3(1, -1, -1); + try test__muldi3(1, -10, -10); + try test__muldi3(-10, 1, -10); + try test__muldi3(1, -81985529216486895, -81985529216486895); + try test__muldi3(-81985529216486895, 1, -81985529216486895); + + try test__muldi3(3037000499, 3037000499, 9223372030926249001); + try test__muldi3(-3037000499, 3037000499, -9223372030926249001); + try test__muldi3(3037000499, -3037000499, -9223372030926249001); + try test__muldi3(-3037000499, -3037000499, 9223372030926249001); + + try test__muldi3(4398046511103, 2097152, 9223372036852678656); + try test__muldi3(-4398046511103, 2097152, -9223372036852678656); + try test__muldi3(4398046511103, -2097152, -9223372036852678656); + try test__muldi3(-4398046511103, -2097152, 9223372036852678656); + + try test__muldi3(2097152, 4398046511103, 9223372036852678656); + try test__muldi3(-2097152, 4398046511103, -9223372036852678656); + try test__muldi3(2097152, -4398046511103, -9223372036852678656); + try test__muldi3(-2097152, -4398046511103, 9223372036852678656); +} + +test "multi3" { + try test__multi3(0, 0, 0); + try test__multi3(0, 1, 0); + try test__multi3(1, 0, 0); + try test__multi3(0, 10, 0); + try test__multi3(10, 0, 0); + try test__multi3(0, 81985529216486895, 0); + try test__multi3(81985529216486895, 0, 0); + + try test__multi3(0, -1, 0); + try test__multi3(-1, 0, 0); + try test__multi3(0, -10, 0); + try test__multi3(-10, 0, 0); + try test__multi3(0, -81985529216486895, 0); + try test__multi3(-81985529216486895, 0, 0); + + try test__multi3(1, 1, 1); + try test__multi3(1, 10, 10); + try test__multi3(10, 1, 10); + try test__multi3(1, 81985529216486895, 81985529216486895); + try test__multi3(81985529216486895, 1, 81985529216486895); + + try test__multi3(1, -1, -1); + try test__multi3(1, -10, -10); + try test__multi3(-10, 1, -10); + try test__multi3(1, -81985529216486895, -81985529216486895); + try test__multi3(-81985529216486895, 1, -81985529216486895); + + try test__multi3(3037000499, 3037000499, 9223372030926249001); + try test__multi3(-3037000499, 3037000499, -9223372030926249001); + try test__multi3(3037000499, -3037000499, -9223372030926249001); + try test__multi3(-3037000499, -3037000499, 9223372030926249001); + + try test__multi3(4398046511103, 2097152, 9223372036852678656); + try test__multi3(-4398046511103, 2097152, -9223372036852678656); + try test__multi3(4398046511103, -2097152, -9223372036852678656); + try test__multi3(-4398046511103, -2097152, 9223372036852678656); + + try test__multi3(2097152, 4398046511103, 9223372036852678656); + try test__multi3(-2097152, 4398046511103, -9223372036852678656); + try test__multi3(2097152, -4398046511103, -9223372036852678656); + try test__multi3(-2097152, -4398046511103, 9223372036852678656); + + try test__multi3(0x00000000000000B504F333F9DE5BE000, 0x000000000000000000B504F333F9DE5B, 0x7FFFFFFFFFFFF328DF915DA296E8A000); +} diff --git a/lib/compiler_rt/muldi3.zig b/lib/compiler_rt/muldi3.zig deleted file mode 100644 index c79713fed0..0000000000 --- a/lib/compiler_rt/muldi3.zig +++ /dev/null @@ -1,71 +0,0 @@ -//! Ported from -//! https://github.com/llvm/llvm-project/blob/llvmorg-9.0.0/compiler-rt/lib/builtins/muldi3.c - -const std = @import("std"); -const builtin = @import("builtin"); -const native_endian = builtin.cpu.arch.endian(); -const common = @import("common.zig"); - -pub const panic = common.panic; - -comptime { - if (common.want_aeabi) { - @export(__aeabi_lmul, .{ .name = "__aeabi_lmul", .linkage = common.linkage, .visibility = common.visibility }); - } else { - @export(__muldi3, .{ .name = "__muldi3", .linkage = common.linkage, .visibility = common.visibility }); - } -} - -pub fn __muldi3(a: i64, b: i64) callconv(.C) i64 { - return mul(a, b); -} - -fn __aeabi_lmul(a: i64, b: i64) callconv(.AAPCS) i64 { - return mul(a, b); -} - -inline fn mul(a: i64, b: i64) i64 { - const x = dwords{ .all = a }; - const y = dwords{ .all = b }; - var r = dwords{ .all = muldsi3(x.s.low, y.s.low) }; - r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; - return r.all; -} - -const dwords = extern union { - all: i64, - s: switch (native_endian) { - .Little => extern struct { - low: u32, - high: u32, - }, - .Big => extern struct { - high: u32, - low: u32, - }, - }, -}; - -fn muldsi3(a: u32, b: u32) i64 { - const bits_in_word_2 = @sizeOf(i32) * 8 / 2; - const lower_mask = (~@as(u32, 0)) >> bits_in_word_2; - - var r: dwords = undefined; - r.s.low = (a & lower_mask) *% (b & lower_mask); - var t: u32 = r.s.low >> bits_in_word_2; - r.s.low &= lower_mask; - t += (a >> bits_in_word_2) *% (b & lower_mask); - r.s.low +%= (t & lower_mask) << bits_in_word_2; - r.s.high = t >> bits_in_word_2; - t = r.s.low >> bits_in_word_2; - r.s.low &= lower_mask; - t +%= (b >> bits_in_word_2) *% (a & lower_mask); - r.s.low +%= (t & lower_mask) << bits_in_word_2; - r.s.high +%= t >> bits_in_word_2; - r.s.high +%= (a >> bits_in_word_2) *% (b >> bits_in_word_2); - return r.all; -} - -test { - _ = @import("muldi3_test.zig"); -} diff --git a/lib/compiler_rt/muldi3_test.zig b/lib/compiler_rt/muldi3_test.zig deleted file mode 100644 index 6e005d67c8..0000000000 --- a/lib/compiler_rt/muldi3_test.zig +++ /dev/null @@ -1,51 +0,0 @@ -const __muldi3 = @import("muldi3.zig").__muldi3; -const testing = @import("std").testing; - -fn test__muldi3(a: i64, b: i64, expected: i64) !void { - const x = __muldi3(a, b); - try testing.expect(x == expected); -} - -test "muldi3" { - try test__muldi3(0, 0, 0); - try test__muldi3(0, 1, 0); - try test__muldi3(1, 0, 0); - try test__muldi3(0, 10, 0); - try test__muldi3(10, 0, 0); - try test__muldi3(0, 81985529216486895, 0); - try test__muldi3(81985529216486895, 0, 0); - - try test__muldi3(0, -1, 0); - try test__muldi3(-1, 0, 0); - try test__muldi3(0, -10, 0); - try test__muldi3(-10, 0, 0); - try test__muldi3(0, -81985529216486895, 0); - try test__muldi3(-81985529216486895, 0, 0); - - try test__muldi3(1, 1, 1); - try test__muldi3(1, 10, 10); - try test__muldi3(10, 1, 10); - try test__muldi3(1, 81985529216486895, 81985529216486895); - try test__muldi3(81985529216486895, 1, 81985529216486895); - - try test__muldi3(1, -1, -1); - try test__muldi3(1, -10, -10); - try test__muldi3(-10, 1, -10); - try test__muldi3(1, -81985529216486895, -81985529216486895); - try test__muldi3(-81985529216486895, 1, -81985529216486895); - - try test__muldi3(3037000499, 3037000499, 9223372030926249001); - try test__muldi3(-3037000499, 3037000499, -9223372030926249001); - try test__muldi3(3037000499, -3037000499, -9223372030926249001); - try test__muldi3(-3037000499, -3037000499, 9223372030926249001); - - try test__muldi3(4398046511103, 2097152, 9223372036852678656); - try test__muldi3(-4398046511103, 2097152, -9223372036852678656); - try test__muldi3(4398046511103, -2097152, -9223372036852678656); - try test__muldi3(-4398046511103, -2097152, 9223372036852678656); - - try test__muldi3(2097152, 4398046511103, 9223372036852678656); - try test__muldi3(-2097152, 4398046511103, -9223372036852678656); - try test__muldi3(2097152, -4398046511103, -9223372036852678656); - try test__muldi3(-2097152, -4398046511103, 9223372036852678656); -} diff --git a/lib/compiler_rt/multi3.zig b/lib/compiler_rt/multi3.zig deleted file mode 100644 index 1918e8b976..0000000000 --- a/lib/compiler_rt/multi3.zig +++ /dev/null @@ -1,75 +0,0 @@ -//! Ported from git@github.com:llvm-project/llvm-project-20170507.git -//! ae684fad6d34858c014c94da69c15e7774a633c3 -//! 2018-08-13 - -const std = @import("std"); -const builtin = @import("builtin"); -const native_endian = builtin.cpu.arch.endian(); -const common = @import("common.zig"); - -pub const panic = common.panic; - -comptime { - if (common.want_windows_v2u64_abi) { - @export(__multi3_windows_x86_64, .{ .name = "__multi3", .linkage = common.linkage, .visibility = common.visibility }); - } else { - @export(__multi3, .{ .name = "__multi3", .linkage = common.linkage, .visibility = common.visibility }); - } -} - -pub fn __multi3(a: i128, b: i128) callconv(.C) i128 { - return mul(a, b); -} - -const v2u64 = @Vector(2, u64); - -fn __multi3_windows_x86_64(a: v2u64, b: v2u64) callconv(.C) v2u64 { - return @bitCast(v2u64, mul(@bitCast(i128, a), @bitCast(i128, b))); -} - -inline fn mul(a: i128, b: i128) i128 { - const x = twords{ .all = a }; - const y = twords{ .all = b }; - var r = twords{ .all = mulddi3(x.s.low, y.s.low) }; - r.s.high +%= x.s.high *% y.s.low +% x.s.low *% y.s.high; - return r.all; -} - -fn mulddi3(a: u64, b: u64) i128 { - const bits_in_dword_2 = (@sizeOf(i64) * 8) / 2; - const lower_mask = ~@as(u64, 0) >> bits_in_dword_2; - var r: twords = undefined; - r.s.low = (a & lower_mask) *% (b & lower_mask); - var t: u64 = r.s.low >> bits_in_dword_2; - r.s.low &= lower_mask; - t +%= (a >> bits_in_dword_2) *% (b & lower_mask); - r.s.low +%= (t & lower_mask) << bits_in_dword_2; - r.s.high = t >> bits_in_dword_2; - t = r.s.low >> bits_in_dword_2; - r.s.low &= lower_mask; - t +%= (b >> bits_in_dword_2) *% (a & lower_mask); - r.s.low +%= (t & lower_mask) << bits_in_dword_2; - r.s.high +%= t >> bits_in_dword_2; - r.s.high +%= (a >> bits_in_dword_2) *% (b >> bits_in_dword_2); - return r.all; -} - -const twords = extern union { - all: i128, - s: S, - - const S = if (native_endian == .Little) - extern struct { - low: u64, - high: u64, - } - else - extern struct { - high: u64, - low: u64, - }; -}; - -test { - _ = @import("multi3_test.zig"); -} diff --git a/lib/compiler_rt/multi3_test.zig b/lib/compiler_rt/multi3_test.zig deleted file mode 100644 index e9eafc05de..0000000000 --- a/lib/compiler_rt/multi3_test.zig +++ /dev/null @@ -1,53 +0,0 @@ -const __multi3 = @import("multi3.zig").__multi3; -const testing = @import("std").testing; - -fn test__multi3(a: i128, b: i128, expected: i128) !void { - const x = __multi3(a, b); - try testing.expect(x == expected); -} - -test "multi3" { - try test__multi3(0, 0, 0); - try test__multi3(0, 1, 0); - try test__multi3(1, 0, 0); - try test__multi3(0, 10, 0); - try test__multi3(10, 0, 0); - try test__multi3(0, 81985529216486895, 0); - try test__multi3(81985529216486895, 0, 0); - - try test__multi3(0, -1, 0); - try test__multi3(-1, 0, 0); - try test__multi3(0, -10, 0); - try test__multi3(-10, 0, 0); - try test__multi3(0, -81985529216486895, 0); - try test__multi3(-81985529216486895, 0, 0); - - try test__multi3(1, 1, 1); - try test__multi3(1, 10, 10); - try test__multi3(10, 1, 10); - try test__multi3(1, 81985529216486895, 81985529216486895); - try test__multi3(81985529216486895, 1, 81985529216486895); - - try test__multi3(1, -1, -1); - try test__multi3(1, -10, -10); - try test__multi3(-10, 1, -10); - try test__multi3(1, -81985529216486895, -81985529216486895); - try test__multi3(-81985529216486895, 1, -81985529216486895); - - try test__multi3(3037000499, 3037000499, 9223372030926249001); - try test__multi3(-3037000499, 3037000499, -9223372030926249001); - try test__multi3(3037000499, -3037000499, -9223372030926249001); - try test__multi3(-3037000499, -3037000499, 9223372030926249001); - - try test__multi3(4398046511103, 2097152, 9223372036852678656); - try test__multi3(-4398046511103, 2097152, -9223372036852678656); - try test__multi3(4398046511103, -2097152, -9223372036852678656); - try test__multi3(-4398046511103, -2097152, 9223372036852678656); - - try test__multi3(2097152, 4398046511103, 9223372036852678656); - try test__multi3(-2097152, 4398046511103, -9223372036852678656); - try test__multi3(2097152, -4398046511103, -9223372036852678656); - try test__multi3(-2097152, -4398046511103, 9223372036852678656); - - try test__multi3(0x00000000000000B504F333F9DE5BE000, 0x000000000000000000B504F333F9DE5B, 0x7FFFFFFFFFFFF328DF915DA296E8A000); -} diff --git a/lib/compiler_rt/shift.zig b/lib/compiler_rt/shift.zig index df6ce82059..4d8658dbc9 100644 --- a/lib/compiler_rt/shift.zig +++ b/lib/compiler_rt/shift.zig @@ -1,7 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); const Log2Int = std.math.Log2Int; -const native_endian = builtin.cpu.arch.endian(); const common = @import("common.zig"); pub const panic = common.panic; @@ -27,39 +26,24 @@ comptime { } } -fn Dwords(comptime T: type, comptime signed_half: bool) type { - return extern union { - const bits = @divExact(@typeInfo(T).Int.bits, 2); - const HalfTU = std.meta.Int(.unsigned, bits); - const HalfTS = std.meta.Int(.signed, bits); - const HalfT = if (signed_half) HalfTS else HalfTU; - - all: T, - s: if (native_endian == .Little) - extern struct { low: HalfT, high: HalfT } - else - extern struct { high: HalfT, low: HalfT }, - }; -} - // Arithmetic shift left: shift in 0 from right to left // Precondition: 0 <= b < bits_in_dword inline fn ashlXi3(comptime T: type, a: T, b: i32) T { - const dwords = Dwords(T, false); - const S = Log2Int(dwords.HalfT); + const word_t = common.HalveInt(T, false); + const S = Log2Int(word_t.HalfT); - const input = dwords{ .all = a }; - var output: dwords = undefined; + const input = word_t{ .all = a }; + var output: word_t = undefined; - if (b >= dwords.bits) { + if (b >= word_t.bits) { output.s.low = 0; - output.s.high = input.s.low << @intCast(S, b - dwords.bits); + output.s.high = input.s.low << @intCast(S, b - word_t.bits); } else if (b == 0) { return a; } else { output.s.low = input.s.low << @intCast(S, b); output.s.high = input.s.high << @intCast(S, b); - output.s.high |= input.s.low >> @intCast(S, dwords.bits - b); + output.s.high |= input.s.low >> @intCast(S, word_t.bits - b); } return output.all; @@ -68,24 +52,24 @@ inline fn ashlXi3(comptime T: type, a: T, b: i32) T { // Arithmetic shift right: shift in 1 from left to right // Precondition: 0 <= b < T.bit_count inline fn ashrXi3(comptime T: type, a: T, b: i32) T { - const dwords = Dwords(T, true); - const S = Log2Int(dwords.HalfT); + const word_t = common.HalveInt(T, true); + const S = Log2Int(word_t.HalfT); - const input = dwords{ .all = a }; - var output: dwords = undefined; + const input = word_t{ .all = a }; + var output: word_t = undefined; - if (b >= dwords.bits) { - output.s.high = input.s.high >> (dwords.bits - 1); - output.s.low = input.s.high >> @intCast(S, b - dwords.bits); + if (b >= word_t.bits) { + output.s.high = input.s.high >> (word_t.bits - 1); + output.s.low = input.s.high >> @intCast(S, b - word_t.bits); } else if (b == 0) { return a; } else { output.s.high = input.s.high >> @intCast(S, b); - output.s.low = input.s.high << @intCast(S, dwords.bits - b); + output.s.low = input.s.high << @intCast(S, word_t.bits - b); // Avoid sign-extension here output.s.low |= @bitCast( - dwords.HalfT, - @bitCast(dwords.HalfTU, input.s.low) >> @intCast(S, b), + word_t.HalfT, + @bitCast(word_t.HalfTU, input.s.low) >> @intCast(S, b), ); } @@ -95,20 +79,20 @@ inline fn ashrXi3(comptime T: type, a: T, b: i32) T { // Logical shift right: shift in 0 from left to right // Precondition: 0 <= b < T.bit_count inline fn lshrXi3(comptime T: type, a: T, b: i32) T { - const dwords = Dwords(T, false); - const S = Log2Int(dwords.HalfT); + const word_t = common.HalveInt(T, false); + const S = Log2Int(word_t.HalfT); - const input = dwords{ .all = a }; - var output: dwords = undefined; + const input = word_t{ .all = a }; + var output: word_t = undefined; - if (b >= dwords.bits) { + if (b >= word_t.bits) { output.s.high = 0; - output.s.low = input.s.high >> @intCast(S, b - dwords.bits); + output.s.low = input.s.high >> @intCast(S, b - word_t.bits); } else if (b == 0) { return a; } else { output.s.high = input.s.high >> @intCast(S, b); - output.s.low = input.s.high << @intCast(S, dwords.bits - b); + output.s.low = input.s.high << @intCast(S, word_t.bits - b); output.s.low |= input.s.low >> @intCast(S, b); } From c7f479c3cb1f8d876f2169dd5ee1390c46d9cdaa Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Fri, 24 Feb 2023 20:45:24 +0100 Subject: [PATCH 117/122] crypto/benchmark.zig: fix pointer capture of non pointer type (#14722) --- lib/std/crypto/benchmark.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index c52758b181..71c22f2b4c 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -178,7 +178,7 @@ pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime const sig = try key_pair.sign(&msg, null); var batch: [64]Signature.BatchElement = undefined; - for (batch) |*element| { + for (&batch) |*element| { element.* = Signature.BatchElement{ .sig = sig, .msg = &msg, .public_key = key_pair.public_key }; } From 1453a595aac86b0ca5017c084b5d36108ac414ae Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 24 Feb 2023 23:28:14 -0500 Subject: [PATCH 118/122] CBE: reuse locals with the same `CType` instead of `Type` Many `Type`s can correspond to the same `CType`, so this reduces the number of used locals by 27760 when compiling only-c. Also, disabled some tests that were only passing by accident and shouldn't really be considered working. --- src/codegen/c.zig | 136 +++++++++++++++++++++++++-------------- src/codegen/c/type.zig | 135 +++++++++++++++++--------------------- test/behavior/vector.zig | 2 + 3 files changed, 147 insertions(+), 126 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1af14cb372..e1fc715f8b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -82,15 +82,20 @@ pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue); const LoopDepth = u16; const Local = struct { - ty: Type, - alignment: u32, + cty_idx: CType.Index, /// How many loops the last definition was nested in. loop_depth: LoopDepth, + alignas: CType.AlignAs, + + pub fn getType(local: Local) LocalType { + return .{ .cty_idx = local.cty_idx, .alignas = local.alignas }; + } }; const LocalIndex = u16; +const LocalType = struct { cty_idx: CType.Index, alignas: CType.AlignAs }; const LocalsList = std.ArrayListUnmanaged(LocalIndex); -const LocalsMap = std.ArrayHashMapUnmanaged(Type, LocalsList, Type.HashContext32, true); +const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList); const LocalsStack = std.ArrayListUnmanaged(LocalsMap); const ValueRenderLocation = enum { @@ -296,10 +301,6 @@ pub const Function = struct { /// Needed for memory used by the keys of free_locals_stack entries. arena: std.heap.ArenaAllocator, - fn tyHashCtx(f: Function) Type.HashContext32 { - return .{ .mod = f.object.dg.module }; - } - fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue { const gop = try f.value_map.getOrPut(inst); if (gop.found_existing) return gop.value_ptr.*; @@ -339,10 +340,11 @@ pub const Function = struct { /// Skips the reuse logic. fn allocLocalValue(f: *Function, ty: Type, alignment: u32) !CValue { const gpa = f.object.dg.gpa; + const target = f.object.dg.module.getTarget(); try f.locals.append(gpa, .{ - .ty = ty, - .alignment = alignment, + .cty_idx = try f.typeToIndex(ty, .complete), .loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1), + .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)), }); return .{ .new_local = @intCast(LocalIndex, f.locals.items.len - 1) }; } @@ -355,14 +357,15 @@ pub const Function = struct { /// Only allocates the local; does not print anything. fn allocAlignedLocal(f: *Function, ty: Type, _: CQualifiers, alignment: u32) !CValue { - if (f.getFreeLocals().getPtrContext(ty, f.tyHashCtx())) |locals_list| { - for (locals_list.items, 0..) |local_index, i| { + const target = f.object.dg.module.getTarget(); + if (f.getFreeLocals().getPtr(.{ + .cty_idx = try f.typeToIndex(ty, .complete), + .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)), + })) |locals_list| { + if (locals_list.popOrNull()) |local_index| { const local = &f.locals.items[local_index]; - if (local.alignment >= alignment) { - local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); - _ = locals_list.swapRemove(i); - return .{ .new_local = local_index }; - } + local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); + return .{ .new_local = local_index }; } } @@ -1695,22 +1698,34 @@ pub const DeclGen = struct { qualifiers: CQualifiers, alignment: u32, kind: CType.Kind, + ) error{ OutOfMemory, AnalysisFail }!void { + const target = dg.module.getTarget(); + const alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)); + try dg.renderCTypeAndName(w, try dg.typeToIndex(ty, kind), name, qualifiers, alignas); + } + + fn renderCTypeAndName( + dg: *DeclGen, + w: anytype, + cty_idx: CType.Index, + name: CValue, + qualifiers: CQualifiers, + alignas: CType.AlignAs, ) error{ OutOfMemory, AnalysisFail }!void { const store = &dg.ctypes.set; const module = dg.module; - if (alignment != 0) switch (std.math.order(alignment, ty.abiAlignment(dg.module.getTarget()))) { - .lt => try w.print("zig_under_align({}) ", .{alignment}), + switch (std.math.order(alignas.@"align", alignas.abi)) { + .lt => try w.print("zig_under_align({}) ", .{alignas.getAlign()}), .eq => {}, - .gt => try w.print("zig_align({}) ", .{alignment}), - }; + .gt => try w.print("zig_align({}) ", .{alignas.getAlign()}), + } - const idx = try dg.typeToIndex(ty, kind); const trailing = - try renderTypePrefix(dg.decl_index, store.*, module, w, idx, .suffix, qualifiers); + try renderTypePrefix(dg.decl_index, store.*, module, w, cty_idx, .suffix, qualifiers); try w.print("{}", .{trailing}); try dg.writeCValue(w, name); - try renderTypeSuffix(dg.decl_index, store.*, module, w, idx, .suffix, .{}); + try renderTypeSuffix(dg.decl_index, store.*, module, w, cty_idx, .suffix, .{}); } fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { @@ -2589,36 +2604,27 @@ pub fn genFunc(f: *Function) !void { if (value) continue; // static const local = f.locals.items[local_index]; log.debug("inserting local {d} into free_locals", .{local_index}); - const gop = try free_locals.getOrPutContext(gpa, local.ty, f.tyHashCtx()); + const gop = try free_locals.getOrPut(gpa, local.getType()); if (!gop.found_existing) gop.value_ptr.* = .{}; try gop.value_ptr.append(gpa, local_index); } const SortContext = struct { - target: std.Target, - keys: []const Type, + keys: []const LocalType, - pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool { - const a_ty = ctx.keys[a_index]; - const b_ty = ctx.keys[b_index]; - return b_ty.abiAlignment(ctx.target) < a_ty.abiAlignment(ctx.target); + pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { + const lhs_ty = ctx.keys[lhs_index]; + const rhs_ty = ctx.keys[rhs_index]; + return lhs_ty.alignas.getAlign() > rhs_ty.alignas.getAlign(); } }; - const target = o.dg.module.getTarget(); - free_locals.sort(SortContext{ .target = target, .keys = free_locals.keys() }); + free_locals.sort(SortContext{ .keys = free_locals.keys() }); const w = o.code_header.writer(); for (free_locals.values()) |list| { for (list.items) |local_index| { const local = f.locals.items[local_index]; - try o.dg.renderTypeAndName( - w, - local.ty, - .{ .local = local_index }, - .{}, - local.alignment, - .complete, - ); + try o.dg.renderCTypeAndName(w, local.cty_idx, .{ .local = local_index }, .{}, local.alignas); try w.writeAll(";\n "); } } @@ -4486,7 +4492,7 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { const new_free_locals = f.getFreeLocals(); var it = new_free_locals.iterator(); while (it.next()) |entry| { - const gop = try old_free_locals.getOrPutContext(gpa, entry.key_ptr.*, f.tyHashCtx()); + const gop = try old_free_locals.getOrPut(gpa, entry.key_ptr.*); if (gop.found_existing) { try gop.value_ptr.appendSlice(gpa, entry.value_ptr.items); } else { @@ -4522,6 +4528,10 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { // that we can notice and use them in the else branch. Any new locals must // necessarily be free already after the then branch is complete. const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); + // Remember how many allocs there were before entering the then branch so + // that we can notice and make sure not to use them in the else branch. + // Any new allocs must be removed from the free list. + const pre_allocs_len = @intCast(LocalIndex, f.allocs.count()); const pre_clone_depth = f.free_locals_clone_depth; f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len); @@ -4552,7 +4562,7 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { try die(f, inst, Air.indexToRef(operand)); } - try noticeBranchFrees(f, pre_locals_len, inst); + try noticeBranchFrees(f, pre_locals_len, pre_allocs_len, inst); if (needs_else) { try genBody(f, else_body); @@ -4627,6 +4637,10 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { // we can notice and use them in subsequent branches. Any new locals must // necessarily be free already after the previous branch is complete. const pre_locals_len = @intCast(LocalIndex, f.locals.items.len); + // Remember how many allocs there were before entering each branch so that + // we can notice and make sure not to use them in subsequent branches. + // Any new allocs must be removed from the free list. + const pre_allocs_len = @intCast(LocalIndex, f.allocs.count()); const pre_clone_depth = f.free_locals_clone_depth; f.free_locals_clone_depth = @intCast(LoopDepth, f.free_locals_stack.items.len); @@ -4647,7 +4661,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { try genBody(f, case_body); } - try noticeBranchFrees(f, pre_locals_len, inst); + try noticeBranchFrees(f, pre_locals_len, pre_allocs_len, inst); } else { for (liveness.deaths[case_i]) |operand| { try die(f, inst, Air.indexToRef(operand)); @@ -7441,11 +7455,7 @@ fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_in const local = &f.locals.items[local_index]; log.debug("%{d}: freeing t{d} (operand %{d})", .{ inst, local_index, ref_inst }); if (local.loop_depth < f.free_locals_clone_depth) return; - const gop = try f.free_locals_stack.items[local.loop_depth].getOrPutContext( - gpa, - local.ty, - f.tyHashCtx(), - ); + const gop = try f.free_locals_stack.items[local.loop_depth].getOrPut(gpa, local.getType()); if (!gop.found_existing) gop.value_ptr.* = .{}; if (std.debug.runtime_safety) { // If this trips, it means a local is being inserted into the @@ -7504,14 +7514,40 @@ fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { map.deinit(gpa); } -fn noticeBranchFrees(f: *Function, pre_locals_len: LocalIndex, inst: Air.Inst.Index) !void { +fn noticeBranchFrees( + f: *Function, + pre_locals_len: LocalIndex, + pre_allocs_len: LocalIndex, + inst: Air.Inst.Index, +) !void { + const free_locals = f.getFreeLocals(); + for (f.locals.items[pre_locals_len..], pre_locals_len..) |*local, local_i| { const local_index = @intCast(LocalIndex, local_i); - if (f.allocs.contains(local_index)) continue; // allocs are not freeable + if (f.allocs.contains(local_index)) { + if (std.debug.runtime_safety) { + // new allocs are no longer freeable, so make sure they aren't in the free list + if (free_locals.getPtr(local.getType())) |locals_list| { + assert(mem.indexOfScalar(LocalIndex, locals_list.items, local_index) == null); + } + } + continue; + } // free more deeply nested locals from other branches at current depth assert(local.loop_depth >= f.free_locals_stack.items.len - 1); local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); try freeLocal(f, inst, local_index, 0); } + + for (f.allocs.keys()[pre_allocs_len..]) |local_i| { + const local_index = @intCast(LocalIndex, local_i); + const local = &f.locals.items[local_index]; + // new allocs are no longer freeable, so remove them from the free list + if (free_locals.getPtr(local.getType())) |locals_list| { + if (mem.indexOfScalar(LocalIndex, locals_list.items, local_index)) |i| { + _ = locals_list.swapRemove(i); + } + } + } } diff --git a/src/codegen/c/type.zig b/src/codegen/c/type.zig index bd4b6d9a8d..1f1a220cd2 100644 --- a/src/codegen/c/type.zig +++ b/src/codegen/c/type.zig @@ -251,38 +251,6 @@ pub const CType = extern union { type: Index, alignas: AlignAs, }; - pub const AlignAs = struct { - @"align": std.math.Log2Int(u32), - abi: std.math.Log2Int(u32), - - pub fn init(alignment: u32, abi_alignment: u32) AlignAs { - assert(std.math.isPowerOfTwo(alignment)); - assert(std.math.isPowerOfTwo(abi_alignment)); - return .{ - .@"align" = std.math.log2_int(u32, alignment), - .abi = std.math.log2_int(u32, abi_alignment), - }; - } - pub fn abiAlign(ty: Type, target: Target) AlignAs { - const abi_align = ty.abiAlignment(target); - return init(abi_align, abi_align); - } - pub fn fieldAlign(struct_ty: Type, field_i: usize, target: Target) AlignAs { - return init( - struct_ty.structFieldAlign(field_i, target), - struct_ty.structFieldType(field_i).abiAlignment(target), - ); - } - pub fn unionPayloadAlign(union_ty: Type, target: Target) AlignAs { - const union_obj = union_ty.cast(Type.Payload.Union).?.data; - const union_payload_align = union_obj.abiAlignment(target, false); - return init(union_payload_align, union_payload_align); - } - - pub fn getAlign(self: AlignAs) u32 { - return @as(u32, 1) << self.@"align"; - } - }; }; pub const Unnamed = struct { @@ -311,13 +279,57 @@ pub const CType = extern union { }; }; + pub const AlignAs = struct { + @"align": std.math.Log2Int(u32), + abi: std.math.Log2Int(u32), + + pub fn init(alignment: u32, abi_alignment: u32) AlignAs { + const actual_align = if (alignment != 0) alignment else abi_alignment; + assert(std.math.isPowerOfTwo(actual_align)); + assert(std.math.isPowerOfTwo(abi_alignment)); + return .{ + .@"align" = std.math.log2_int(u32, actual_align), + .abi = std.math.log2_int(u32, abi_alignment), + }; + } + pub fn abiAlign(ty: Type, target: Target) AlignAs { + const abi_align = ty.abiAlignment(target); + return init(abi_align, abi_align); + } + pub fn fieldAlign(struct_ty: Type, field_i: usize, target: Target) AlignAs { + return init( + struct_ty.structFieldAlign(field_i, target), + struct_ty.structFieldType(field_i).abiAlignment(target), + ); + } + pub fn unionPayloadAlign(union_ty: Type, target: Target) AlignAs { + const union_obj = union_ty.cast(Type.Payload.Union).?.data; + const union_payload_align = union_obj.abiAlignment(target, false); + return init(union_payload_align, union_payload_align); + } + + pub fn getAlign(self: AlignAs) u32 { + return @as(u32, 1) << self.@"align"; + } + }; + pub const Index = u32; pub const Store = struct { arena: std.heap.ArenaAllocator.State = .{}, set: Set = .{}, pub const Set = struct { - pub const Map = std.ArrayHashMapUnmanaged(CType, void, HashContext32, true); + pub const Map = std.ArrayHashMapUnmanaged(CType, void, HashContext, true); + const HashContext = struct { + store: *const Set, + + pub fn hash(self: @This(), cty: CType) Map.Hash { + return @truncate(Map.Hash, cty.hash(self.store.*)); + } + pub fn eql(_: @This(), lhs: CType, rhs: CType, _: usize) bool { + return lhs.eql(rhs); + } + }; map: Map = .{}, @@ -328,7 +340,7 @@ pub const CType = extern union { pub fn indexToHash(self: Set, index: Index) Map.Hash { if (index < Tag.no_payload_count) - return (HashContext32{ .store = &self }).hash(self.indexToCType(index)); + return (HashContext{ .store = &self }).hash(self.indexToCType(index)); return self.map.entries.items(.hash)[index - Tag.no_payload_count]; } @@ -905,7 +917,7 @@ pub const CType = extern union { self.storage.anon.fields[0] = .{ .name = "array", .type = array_idx, - .alignas = Payload.Fields.AlignAs.abiAlign(ty, lookup.getTarget()), + .alignas = AlignAs.abiAlign(ty, lookup.getTarget()), }; self.initAnon(kind, fwd_idx, 1); } else self.init(switch (kind) { @@ -1004,12 +1016,12 @@ pub const CType = extern union { self.storage.anon.fields[0] = .{ .name = "ptr", .type = ptr_idx, - .alignas = Payload.Fields.AlignAs.abiAlign(ptr_ty, target), + .alignas = AlignAs.abiAlign(ptr_ty, target), }; self.storage.anon.fields[1] = .{ .name = "len", .type = Tag.uintptr_t.toIndex(), - .alignas = Payload.Fields.AlignAs.abiAlign(Type.usize, target), + .alignas = AlignAs.abiAlign(Type.usize, target), }; self.initAnon(kind, fwd_idx, 2); } else self.init(switch (kind) { @@ -1125,7 +1137,7 @@ pub const CType = extern union { self.storage.anon.fields[field_count] = .{ .name = "payload", .type = payload_idx.?, - .alignas = Payload.Fields.AlignAs.unionPayloadAlign(ty, target), + .alignas = AlignAs.unionPayloadAlign(ty, target), }; field_count += 1; } @@ -1133,7 +1145,7 @@ pub const CType = extern union { self.storage.anon.fields[field_count] = .{ .name = "tag", .type = tag_idx.?, - .alignas = Payload.Fields.AlignAs.abiAlign(tag_ty.?, target), + .alignas = AlignAs.abiAlign(tag_ty.?, target), }; field_count += 1; } @@ -1158,11 +1170,7 @@ pub const CType = extern union { const field_ty = ty.structFieldType(field_i); if (!field_ty.hasRuntimeBitsIgnoreComptime()) continue; - const field_align = Payload.Fields.AlignAs.fieldAlign( - ty, - field_i, - target, - ); + const field_align = AlignAs.fieldAlign(ty, field_i, target); if (field_align.@"align" < field_align.abi) { is_packed = true; if (!lookup.isMutable()) break; @@ -1235,12 +1243,12 @@ pub const CType = extern union { self.storage.anon.fields[0] = .{ .name = "payload", .type = payload_idx, - .alignas = Payload.Fields.AlignAs.abiAlign(payload_ty, target), + .alignas = AlignAs.abiAlign(payload_ty, target), }; self.storage.anon.fields[1] = .{ .name = "is_null", .type = Tag.bool.toIndex(), - .alignas = Payload.Fields.AlignAs.abiAlign(Type.bool, target), + .alignas = AlignAs.abiAlign(Type.bool, target), }; self.initAnon(kind, fwd_idx, 2); } else self.init(switch (kind) { @@ -1273,12 +1281,12 @@ pub const CType = extern union { self.storage.anon.fields[0] = .{ .name = "payload", .type = payload_idx, - .alignas = Payload.Fields.AlignAs.abiAlign(payload_ty, target), + .alignas = AlignAs.abiAlign(payload_ty, target), }; self.storage.anon.fields[1] = .{ .name = "error", .type = error_idx, - .alignas = Payload.Fields.AlignAs.abiAlign(error_ty, target), + .alignas = AlignAs.abiAlign(error_ty, target), }; self.initAnon(kind, fwd_idx, 2); } else self.init(switch (kind) { @@ -1551,7 +1559,7 @@ pub const CType = extern union { .complete, .parameter, .payload => .complete, .global => .global, }).?, - .alignas = Payload.Fields.AlignAs.fieldAlign(ty, field_i, target), + .alignas = AlignAs.fieldAlign(ty, field_i, target), }; } @@ -1635,28 +1643,6 @@ pub const CType = extern union { } } - pub const HashContext64 = struct { - store: *const Store.Set, - - pub fn hash(self: @This(), cty: CType) u64 { - return cty.hash(self.store.*); - } - pub fn eql(_: @This(), lhs: CType, rhs: CType) bool { - return lhs.eql(rhs); - } - }; - - pub const HashContext32 = struct { - store: *const Store.Set, - - pub fn hash(self: @This(), cty: CType) u32 { - return @truncate(u32, cty.hash(self.store.*)); - } - pub fn eql(_: @This(), lhs: CType, rhs: CType, _: usize) bool { - return lhs.eql(rhs); - } - }; - pub const TypeAdapter64 = struct { kind: Kind, lookup: Convert.Lookup, @@ -1719,7 +1705,7 @@ pub const CType = extern union { else => unreachable, }, mem.span(c_field.name), - ) or Payload.Fields.AlignAs.fieldAlign(ty, field_i, target).@"align" != + ) or AlignAs.fieldAlign(ty, field_i, target).@"align" != c_field.alignas.@"align") return false; } return true; @@ -1840,10 +1826,7 @@ pub const CType = extern union { .Union => ty.unionFields().keys()[field_i], else => unreachable, }); - autoHash( - hasher, - Payload.Fields.AlignAs.fieldAlign(ty, field_i, target).@"align", - ); + autoHash(hasher, AlignAs.fieldAlign(ty, field_i, target).@"align"); } }, diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 191c7bf7eb..50fef7f646 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -1247,6 +1247,7 @@ test "load packed vector element" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var x: @Vector(2, u15) = .{ 1, 4 }; try expect((&x[0]).* == 1); @@ -1259,6 +1260,7 @@ test "store packed vector element" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO var v = @Vector(4, u1){ 1, 1, 1, 1 }; try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v); From 26196be344e971c26ed044a39b68d0420cd94b90 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 24 Feb 2023 16:02:01 -0700 Subject: [PATCH 119/122] rename std.Build.InstallRawStep to ObjCopyStep And make it not do any installation, only objcopying. We already have install steps for doing installation. This commit also makes ObjCopyStep properly integrate with caching. --- lib/std/Build.zig | 15 +-- lib/std/Build/CompileStep.zig | 18 ++- lib/std/Build/InstallRawStep.zig | 110 ----------------- lib/std/Build/ObjCopyStep.zig | 138 ++++++++++++++++++++++ lib/std/Build/Step.zig | 4 +- test/standalone/install_raw_hex/build.zig | 15 ++- 6 files changed, 167 insertions(+), 133 deletions(-) delete mode 100644 lib/std/Build/InstallRawStep.zig create mode 100644 lib/std/Build/ObjCopyStep.zig diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 678120847f..26919962e3 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -37,7 +37,7 @@ pub const FmtStep = @import("Build/FmtStep.zig"); pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); pub const InstallDirStep = @import("Build/InstallDirStep.zig"); pub const InstallFileStep = @import("Build/InstallFileStep.zig"); -pub const InstallRawStep = @import("Build/InstallRawStep.zig"); +pub const ObjCopyStep = @import("Build/ObjCopyStep.zig"); pub const CompileStep = @import("Build/CompileStep.zig"); pub const LogStep = @import("Build/LogStep.zig"); pub const OptionsStep = @import("Build/OptionsStep.zig"); @@ -1254,11 +1254,8 @@ pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); } -/// Output format (BIN vs Intel HEX) determined by filename -pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { - const raw = self.addInstallRaw(artifact, dest_filename, options); - self.getInstallStep().dependOn(&raw.step); - return raw; +pub fn addObjCopy(b: *Build, source: FileSource, options: ObjCopyStep.Options) *ObjCopyStep { + return ObjCopyStep.create(b, source, options); } ///`dest_rel_path` is relative to install prefix path @@ -1280,10 +1277,6 @@ pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []co return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); } -pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { - return InstallRawStep.create(self, artifact, dest_filename, options); -} - pub fn addInstallFileWithDir( self: *Build, source: FileSource, @@ -1771,7 +1764,7 @@ test { _ = InstallArtifactStep; _ = InstallDirStep; _ = InstallFileStep; - _ = InstallRawStep; + _ = ObjCopyStep; _ = CompileStep; _ = LogStep; _ = OptionsStep; diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 6477c20f6b..db663fc767 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -21,7 +21,7 @@ const VcpkgRoot = std.Build.VcpkgRoot; const InstallDir = std.Build.InstallDir; const InstallArtifactStep = std.Build.InstallArtifactStep; const GeneratedFile = std.Build.GeneratedFile; -const InstallRawStep = std.Build.InstallRawStep; +const ObjCopyStep = std.Build.ObjCopyStep; const EmulatableRunStep = std.Build.EmulatableRunStep; const CheckObjectStep = std.Build.CheckObjectStep; const RunStep = std.Build.RunStep; @@ -432,10 +432,6 @@ pub fn install(self: *CompileStep) void { self.builder.installArtifact(self); } -pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { - return self.builder.installRaw(self, dest_filename, options); -} - pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); a.builder.getInstallStep().dependOn(&install_file.step); @@ -506,6 +502,18 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); } +pub fn addObjCopy(cs: *CompileStep, options: ObjCopyStep.Options) *ObjCopyStep { + var copy = options; + if (copy.basename == null) { + if (options.format) |f| { + copy.basename = cs.builder.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); + } else { + copy.basename = cs.name; + } + } + return cs.builder.addObjCopy(cs.getOutputSource(), copy); +} + /// Deprecated: use `std.Build.addRunArtifact` /// This function will run in the context of the package that created the executable, /// which is undesirable when running an executable provided by a dependency package. diff --git a/lib/std/Build/InstallRawStep.zig b/lib/std/Build/InstallRawStep.zig deleted file mode 100644 index 014c44f287..0000000000 --- a/lib/std/Build/InstallRawStep.zig +++ /dev/null @@ -1,110 +0,0 @@ -//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy` -//! subcommand rather than containing an implementation directly. - -const std = @import("std"); -const InstallRawStep = @This(); - -const Allocator = std.mem.Allocator; -const ArenaAllocator = std.heap.ArenaAllocator; -const ArrayListUnmanaged = std.ArrayListUnmanaged; -const File = std.fs.File; -const InstallDir = std.Build.InstallDir; -const CompileStep = std.Build.CompileStep; -const Step = std.Build.Step; -const elf = std.elf; -const fs = std.fs; -const io = std.io; -const sort = std.sort; - -pub const base_id = .install_raw; - -pub const RawFormat = enum { - bin, - hex, -}; - -step: Step, -builder: *std.Build, -artifact: *CompileStep, -dest_dir: InstallDir, -dest_filename: []const u8, -options: CreateOptions, -output_file: std.Build.GeneratedFile, - -pub const CreateOptions = struct { - format: ?RawFormat = null, - dest_dir: ?InstallDir = null, - only_section: ?[]const u8 = null, - pad_to: ?u64 = null, -}; - -pub fn create( - builder: *std.Build, - artifact: *CompileStep, - dest_filename: []const u8, - options: CreateOptions, -) *InstallRawStep { - const self = builder.allocator.create(InstallRawStep) catch @panic("OOM"); - self.* = InstallRawStep{ - .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), - .builder = builder, - .artifact = artifact, - .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) { - .obj => unreachable, - .@"test" => unreachable, - .exe, .test_exe => .bin, - .lib => unreachable, - }, - .dest_filename = dest_filename, - .options = options, - .output_file = std.Build.GeneratedFile{ .step = &self.step }, - }; - self.step.dependOn(&artifact.step); - - builder.pushInstalledFile(self.dest_dir, dest_filename); - return self; -} - -pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource { - return std.Build.FileSource{ .generated = &self.output_file }; -} - -fn make(step: *Step) !void { - const self = @fieldParentPtr(InstallRawStep, "step", step); - const b = self.builder; - - if (self.artifact.target.getObjectFormat() != .elf) { - std.debug.print("InstallRawStep only works with ELF format.\n", .{}); - return error.InvalidObjectFormat; - } - - const full_src_path = self.artifact.getOutputSource().getPath(b); - const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename); - self.output_file.path = full_dest_path; - - try fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")); - - var argv_list = std.ArrayList([]const u8).init(b.allocator); - try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); - - if (self.options.only_section) |only_section| { - try argv_list.appendSlice(&.{ "-j", only_section }); - } - if (self.options.pad_to) |pad_to| { - try argv_list.appendSlice(&.{ - "--pad-to", - b.fmt("{d}", .{pad_to}), - }); - } - if (self.options.format) |format| switch (format) { - .bin => try argv_list.appendSlice(&.{ "-O", "binary" }), - .hex => try argv_list.appendSlice(&.{ "-O", "hex" }), - }; - - try argv_list.appendSlice(&.{ full_src_path, full_dest_path }); - _ = try self.builder.execFromStep(argv_list.items, &self.step); -} - -test { - std.testing.refAllDecls(InstallRawStep); -} diff --git a/lib/std/Build/ObjCopyStep.zig b/lib/std/Build/ObjCopyStep.zig new file mode 100644 index 0000000000..aea5b8975c --- /dev/null +++ b/lib/std/Build/ObjCopyStep.zig @@ -0,0 +1,138 @@ +const std = @import("std"); +const ObjCopyStep = @This(); + +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const ArrayListUnmanaged = std.ArrayListUnmanaged; +const File = std.fs.File; +const InstallDir = std.Build.InstallDir; +const CompileStep = std.Build.CompileStep; +const Step = std.Build.Step; +const elf = std.elf; +const fs = std.fs; +const io = std.io; +const sort = std.sort; + +pub const base_id: Step.Id = .objcopy; + +pub const RawFormat = enum { + bin, + hex, +}; + +step: Step, +builder: *std.Build, +file_source: std.Build.FileSource, +basename: []const u8, +output_file: std.Build.GeneratedFile, + +format: ?RawFormat, +only_section: ?[]const u8, +pad_to: ?u64, + +pub const Options = struct { + basename: ?[]const u8 = null, + format: ?RawFormat = null, + only_section: ?[]const u8 = null, + pad_to: ?u64 = null, +}; + +pub fn create( + builder: *std.Build, + file_source: std.Build.FileSource, + options: Options, +) *ObjCopyStep { + const self = builder.allocator.create(ObjCopyStep) catch @panic("OOM"); + self.* = ObjCopyStep{ + .step = Step.init( + base_id, + builder.fmt("objcopy {s}", .{file_source.getDisplayName()}), + builder.allocator, + make, + ), + .builder = builder, + .file_source = file_source, + .basename = options.basename orelse file_source.getDisplayName(), + .output_file = std.Build.GeneratedFile{ .step = &self.step }, + + .format = options.format, + .only_section = options.only_section, + .pad_to = options.pad_to, + }; + file_source.addStepDependencies(&self.step); + return self; +} + +pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource { + return .{ .generated = &self.output_file }; +} + +fn make(step: *Step) !void { + const self = @fieldParentPtr(ObjCopyStep, "step", step); + const b = self.builder; + + var man = b.cache.obtain(); + defer man.deinit(); + + // Random bytes to make ObjCopyStep unique. Refresh this with new random + // bytes when ObjCopyStep implementation is modified incompatibly. + man.hash.add(@as(u32, 0xe18b7baf)); + + const full_src_path = self.file_source.getPath(b); + _ = try man.addFile(full_src_path, null); + man.hash.addOptionalBytes(self.only_section); + man.hash.addOptional(self.pad_to); + man.hash.addOptional(self.format); + + if (man.hit() catch |err| failWithCacheError(man, err)) { + // Cache hit, skip subprocess execution. + const digest = man.final(); + self.output_file.path = try b.cache_root.join(b.allocator, &.{ + "o", &digest, self.basename, + }); + return; + } + + const digest = man.final(); + const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename }); + const cache_path = "o" ++ fs.path.sep_str ++ digest; + b.cache_root.handle.makePath(cache_path) catch |err| { + std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) }); + return err; + }; + + var argv = std.ArrayList([]const u8).init(b.allocator); + try argv.appendSlice(&.{ b.zig_exe, "objcopy" }); + + if (self.only_section) |only_section| { + try argv.appendSlice(&.{ "-j", only_section }); + } + if (self.pad_to) |pad_to| { + try argv.appendSlice(&.{ "--pad-to", b.fmt("{d}", .{pad_to}) }); + } + if (self.format) |format| switch (format) { + .bin => try argv.appendSlice(&.{ "-O", "binary" }), + .hex => try argv.appendSlice(&.{ "-O", "hex" }), + }; + + try argv.appendSlice(&.{ full_src_path, full_dest_path }); + _ = try self.builder.execFromStep(argv.items, &self.step); + + self.output_file.path = full_dest_path; + try man.writeManifest(); +} + +/// TODO consolidate this with the same function in RunStep? +/// Also properly deal with concurrency (see open PR) +fn failWithCacheError(man: std.Build.Cache.Manifest, err: anyerror) noreturn { + const i = man.failed_file_index orelse failWithSimpleError(err); + const pp = man.files.items[i].prefixed_path orelse failWithSimpleError(err); + const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; + std.debug.print("{s}: {s}/{s}\n", .{ @errorName(err), prefix, pp.sub_path }); + std.process.exit(1); +} + +fn failWithSimpleError(err: anyerror) noreturn { + std.debug.print("{s}\n", .{@errorName(err)}); + std.process.exit(1); +} diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index ff0ceb2a51..82c39ac2cc 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -21,7 +21,7 @@ pub const Id = enum { check_file, check_object, config_header, - install_raw, + objcopy, options, custom, @@ -42,7 +42,7 @@ pub const Id = enum { .check_file => Build.CheckFileStep, .check_object => Build.CheckObjectStep, .config_header => Build.ConfigHeaderStep, - .install_raw => Build.InstallRawStep, + .objcopy => Build.ObjCopyStep, .options => Build.OptionsStep, .custom => @compileError("no type available for custom step"), }; diff --git a/test/standalone/install_raw_hex/build.zig b/test/standalone/install_raw_hex/build.zig index b0f938a344..6ed515e381 100644 --- a/test/standalone/install_raw_hex/build.zig +++ b/test/standalone/install_raw_hex/build.zig @@ -3,6 +3,9 @@ const std = @import("std"); const CheckFileStep = std.Build.CheckFileStep; pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test the program"); + b.default_step.dependOn(test_step); + const target = .{ .cpu_arch = .thumb, .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, @@ -19,12 +22,14 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); - const test_step = b.step("test", "Test the program"); - b.default_step.dependOn(test_step); - - const hex_step = b.addInstallRaw(elf, "hello.hex", .{}); + const hex_step = elf.addObjCopy(.{ + .basename = "hello.hex", + }); test_step.dependOn(&hex_step.step); - const explicit_format_hex_step = b.addInstallRaw(elf, "hello.foo", .{ .format = .hex }); + const explicit_format_hex_step = elf.addObjCopy(.{ + .basename = "hello.foo", + .format = .hex, + }); test_step.dependOn(&explicit_format_hex_step.step); } From 477be90c0c18a473c5b0c84c0fbcc9ce41e47738 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 25 Feb 2023 00:18:30 -0500 Subject: [PATCH 120/122] CBE: replace locals list with a hash map Replace `ArrayList` with `ArrayHashMap` since we want to be able to remove by element. --- src/codegen/c.zig | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index e1fc715f8b..cf428d4bd6 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -94,7 +94,7 @@ const Local = struct { const LocalIndex = u16; const LocalType = struct { cty_idx: CType.Index, alignas: CType.AlignAs }; -const LocalsList = std.ArrayListUnmanaged(LocalIndex); +const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void); const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList); const LocalsStack = std.ArrayListUnmanaged(LocalsMap); @@ -362,10 +362,10 @@ pub const Function = struct { .cty_idx = try f.typeToIndex(ty, .complete), .alignas = CType.AlignAs.init(alignment, ty.abiAlignment(target)), })) |locals_list| { - if (locals_list.popOrNull()) |local_index| { - const local = &f.locals.items[local_index]; + if (locals_list.popOrNull()) |local_entry| { + const local = &f.locals.items[local_entry.key]; local.loop_depth = @intCast(LoopDepth, f.free_locals_stack.items.len - 1); - return .{ .new_local = local_index }; + return .{ .new_local = local_entry.key }; } } @@ -2606,7 +2606,7 @@ pub fn genFunc(f: *Function) !void { log.debug("inserting local {d} into free_locals", .{local_index}); const gop = try free_locals.getOrPut(gpa, local.getType()); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.append(gpa, local_index); + try gop.value_ptr.putNoClobber(gpa, local_index, {}); } const SortContext = struct { @@ -2622,7 +2622,7 @@ pub fn genFunc(f: *Function) !void { const w = o.code_header.writer(); for (free_locals.values()) |list| { - for (list.items) |local_index| { + for (list.keys()) |local_index| { const local = f.locals.items[local_index]; try o.dg.renderCTypeAndName(w, local.cty_idx, .{ .local = local_index }, .{}, local.alignas); try w.writeAll(";\n "); @@ -4494,11 +4494,11 @@ fn airLoop(f: *Function, inst: Air.Inst.Index) !CValue { while (it.next()) |entry| { const gop = try old_free_locals.getOrPut(gpa, entry.key_ptr.*); if (gop.found_existing) { - try gop.value_ptr.appendSlice(gpa, entry.value_ptr.items); - } else { - gop.value_ptr.* = entry.value_ptr.*; - entry.value_ptr.* = .{}; - } + try gop.value_ptr.ensureUnusedCapacity(gpa, entry.value_ptr.count()); + for (entry.value_ptr.keys()) |local_index| { + gop.value_ptr.putAssumeCapacityNoClobber(local_index, {}); + } + } else gop.value_ptr.* = entry.value_ptr.move(); } deinitFreeLocalsMap(gpa, new_free_locals); new_free_locals.* = old_free_locals.move(); @@ -7458,14 +7458,13 @@ fn freeLocal(f: *Function, inst: Air.Inst.Index, local_index: LocalIndex, ref_in const gop = try f.free_locals_stack.items[local.loop_depth].getOrPut(gpa, local.getType()); if (!gop.found_existing) gop.value_ptr.* = .{}; if (std.debug.runtime_safety) { - // If this trips, it means a local is being inserted into the - // free_locals map while it already exists in the map, which is not - // allowed. - assert(mem.indexOfScalar(LocalIndex, gop.value_ptr.items, local_index) == null); // If this trips, an unfreeable allocation was attempted to be freed. assert(!f.allocs.contains(local_index)); } - try gop.value_ptr.append(gpa, local_index); + // If this trips, it means a local is being inserted into the + // free_locals map while it already exists in the map, which is not + // allowed. + try gop.value_ptr.putNoClobber(gpa, local_index, {}); } const BigTomb = struct { @@ -7528,7 +7527,7 @@ fn noticeBranchFrees( if (std.debug.runtime_safety) { // new allocs are no longer freeable, so make sure they aren't in the free list if (free_locals.getPtr(local.getType())) |locals_list| { - assert(mem.indexOfScalar(LocalIndex, locals_list.items, local_index) == null); + assert(!locals_list.contains(local_index)); } } continue; @@ -7544,10 +7543,6 @@ fn noticeBranchFrees( const local_index = @intCast(LocalIndex, local_i); const local = &f.locals.items[local_index]; // new allocs are no longer freeable, so remove them from the free list - if (free_locals.getPtr(local.getType())) |locals_list| { - if (mem.indexOfScalar(LocalIndex, locals_list.items, local_index)) |i| { - _ = locals_list.swapRemove(i); - } - } + if (free_locals.getPtr(local.getType())) |locals_list| _ = locals_list.swapRemove(local_index); } } From ee6df50678d985947bfb85cf7bfefb25ab68cdfc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 25 Feb 2023 13:50:42 -0700 Subject: [PATCH 121/122] fix package hashes on Windows closes #14602 --- src/Package.zig | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/Package.zig b/src/Package.zig index d599aefe56..f0e389e7ef 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -493,6 +493,11 @@ fn fetchAndUnpack( // apply those rules directly to the filesystem right here. This ensures that files // not protected by the hash are not present on the file system. + // TODO: raise an error for files that have illegal paths on some operating systems. + // For example, on Linux a path with a backslash should raise an error here. + // Of course, if the ignore rules above omit the file from the package, then everything + // is fine and no error should be raised. + break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle }); }; @@ -546,7 +551,8 @@ fn unpackTarball( } const HashedFile = struct { - path: []const u8, + fs_path: []const u8, + normalized_path: []const u8, hash: [Manifest.Hash.digest_length]u8, failure: Error!void, @@ -554,7 +560,7 @@ const HashedFile = struct { fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool { _ = context; - return mem.lessThan(u8, lhs.path, rhs.path); + return mem.lessThan(u8, lhs.normalized_path, rhs.normalized_path); } }; @@ -590,8 +596,10 @@ fn computePackageHash( else => return error.IllegalFileTypeInPackage, } const hashed_file = try arena.create(HashedFile); + const fs_path = try arena.dupe(u8, entry.path); hashed_file.* = .{ - .path = try arena.dupe(u8, entry.path), + .fs_path = fs_path, + .normalized_path = try normalizePath(arena, fs_path), .hash = undefined, // to be populated by the worker .failure = undefined, // to be populated by the worker }; @@ -609,7 +617,7 @@ fn computePackageHash( for (all_files.items) |hashed_file| { hashed_file.failure catch |err| { any_failures = true; - std.log.err("unable to hash '{s}': {s}", .{ hashed_file.path, @errorName(err) }); + std.log.err("unable to hash '{s}': {s}", .{ hashed_file.fs_path, @errorName(err) }); }; hasher.update(&hashed_file.hash); } @@ -617,6 +625,24 @@ fn computePackageHash( return hasher.finalResult(); } +/// Make a file system path identical independently of operating system path inconsistencies. +/// This converts backslashes into forward slashes. +fn normalizePath(arena: Allocator, fs_path: []const u8) ![]const u8 { + const canonical_sep = '/'; + + if (fs.path.sep == canonical_sep) + return fs_path; + + const normalized = try arena.dupe(u8, fs_path); + for (normalized) |*byte| { + switch (byte.*) { + fs.path.sep => byte.* = canonical_sep, + else => continue, + } + } + return normalized; +} + fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { defer wg.finish(); hashed_file.failure = hashFileFallible(dir, hashed_file); @@ -624,10 +650,10 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { var buf: [8000]u8 = undefined; - var file = try dir.openFile(hashed_file.path, .{}); + var file = try dir.openFile(hashed_file.fs_path, .{}); defer file.close(); var hasher = Manifest.Hash.init(.{}); - hasher.update(hashed_file.path); + hasher.update(hashed_file.normalized_path); hasher.update(&.{ 0, @boolToInt(try isExecutable(file)) }); while (true) { const bytes_read = try file.read(&buf); From f6c934677315665c140151b8dd28a56f948205e2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 25 Feb 2023 15:21:18 -0700 Subject: [PATCH 122/122] std.Build.CompileStep.installConfigHeader: add missing step dependency --- lib/std/Build/CompileStep.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index db663fc767..ea2320cc89 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -454,6 +454,7 @@ pub fn installConfigHeader( options.install_dir, dest_rel_path, ); + install_file.step.dependOn(&config_header.step); cs.builder.getInstallStep().dependOn(&install_file.step); cs.installed_headers.append(&install_file.step) catch @panic("OOM"); }