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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 `#*(YO+Z&otKpR4(Kn}_@4 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 P_wp^=4h_z}=Y4tsjH}A% literal 0 HcmV?d00001 diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig new file mode 100644 index 0000000000..c1e9cef58c --- /dev/null +++ b/lib/std/compress/zstandard.zig @@ -0,0 +1,22 @@ +const std = @import("std"); + +pub const decompress = @import("zstandard/decompress.zig"); + +test "decompression" { + const uncompressed = @embedFile("testdata/rfc8478.txt"); + const compressed3 = @embedFile("testdata/rfc8478.txt.zst.3"); + const compressed19 = @embedFile("testdata/rfc8478.txt.zst.19"); + + 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); + 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); + try std.testing.expectEqualSlices(u8, uncompressed, buffer); +} diff --git a/lib/std/compress/zstandard/decompress.zig b/lib/std/compress/zstandard/decompress.zig new file mode 100644 index 0000000000..37fe722a84 --- /dev/null +++ b/lib/std/compress/zstandard/decompress.zig @@ -0,0 +1,1249 @@ +const std = @import("std"); +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 Table = types.compressed_block.Table; + +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); +} + +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; +} + +pub fn getFrameDecompressedSize(src: []const u8) !?usize { + switch (try frameType(src)) { + .zstandard => { + 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 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

    ;))ioC+9Dz*hQ)xn|5Fh`uFUIAQW&JY(kH{JUR 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

    =(4ur13_gQaIK#ph6k*88 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 z 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 delta 702676 zcmZquJBjhtlZlKx42i|Lj0_A6jH_A0H}aG-v9Z+E)zvXaHI6Bjch6Qkhdi){K#9E_9LWh@xExL7$jxS81) z7@62CSs0mFm{=JZ*f`i&7#Y|YnHX6Znb;Uv7@65wm_WL@7`Rv%*%&zZCx^0Yak8*5 zGBYwVGBHl>V3(h~g8hOZDB>6B7p`!{ly`rLrt+?CcDTjLeKo%-n1o z+{}z@Ol)kdj4Vt{j7+SPgE%!9nI~6qsxWd*p2zuSayM55BlG6RT>BXrStl>z(Pm`a ze3plUdGZy0pFl=`eGN@jz5oN=Km$Hu5ko^|9#JC|;~+i}Q9ckB5#{6HLCi}3M@goqk}rTId`_~a$zB^db_CKn3GsIai|@G!A3G4Sv(Ft9Nzuz&y? zI}?L83kw4~Cj$$o0u#^V4FaY{%NSQOb}&w3oxwVj^}Emyp`SuLOwT2_woR>{Epm#F ziL0KmPMN{+17o%lg9?KJgX4m2n9Ze^D{U)sxdiQ!%vN5?##HIc0Lam>AQnZIh>qtzdjT*+E=PY|geW z0Rde_CLUI9#}oan0**EUdJNo?WE}}Trq9CDC)ltm zF>an*FCoP^d-5y^YsRIUuSpazGR~UpE~U(PcyhLs3FFtv3#4orS5Lktb(v+U2)F)p zMk!8->Hn`Y^G&`j9a4Y)FbmUt#(FtWFg-ZQkgdd`!l1z7_<$`-X$_+zIDl9jKd@yf zt%dU%*t3+@!Fdzdvy|4uc?;OHls3S58`!gyHZnTi*)$mx*h(`QL4nHRcz``iX%j?I zy#kBlG4?E_&5Vu=RtyXZEKUsO%s1E|^b08c14?(WL&Rr5=^g9}ERGD;j7LE9l}(cc zG?^Hbo-vv;uK)?WV9x>>2J+2>h6VvmW(K7vjOI*NKynObOcy|gta7@#>MEBx^9c}# z!JPR4y8?^C6Gn694^UMN91sDf77hg#r&SQuKcJE`z><>#Wn~zjOwN;)Ne7vK21(%o z4h0s+8=NqAfJ|n30uo~|V|sw3?gkS31K3bTbEZE~g+GuaJ2*kknfwuKBcGg1J;=r* zoS-YI37!ie8tT*sU=57sOmCoyUcdxexFEV3xR5*s(#c@XJOLzkfGbPDRDs2D z4HwA$OrUUNuwvMQP|mc4OBJq{X%0w&!JKIcmja6diw4sHE;FVXT#ihNvlyp86c7`e zd_qpReiox6qarU4BR7b0WKf*N$gRNQxP&W9aS@{ei{li|EX9K$#vG0;#X}&*53Vc) zCP$`haKcewbQH)kWBLMe-3hKNM+QfoKinX9^>AlFld2PgIa3SBOa?QiKOmQYB69*a z$VrYXIO?;^m>Rep*F2dlpwGyl$nLlf$^r#W19z4qOOfJYkhK%IvJ_1m85BXzHFsoG z)Nrg<+ydfAJF+P*VRXDatwlg_DM(-|BeR2om7<(Nn4*q?o1(jdx1xiBhhm+Ar((T= zk75I(g0EsDqe6^g6Qe?Gy<#(?f}dgwqe7HoE29FxqN#$4VjH7ExMDk_g1=&>LWH8D z0OlVi%)=s$w^zf|z20LZD)zf{S93LXct)qk^4c zFQY=RVjrV|u3|rm0Ite?WDAfOnnpr|;N zQGr9zRY6EGOMzQ4S%Fb8MS)2%RY6~I8lys_;&es@CBqs}%SYGZc&ztK}556>Ag(72_2o6>Ak(951kCDX=)6;K~v(W2$GWpDw5-CqBJk z54%o1iw4sLHYHXT2L)D8V!Fbn#OC;KPMd%uqky?1VCiUpulIfFY(kzsnkIbqd!1_ee(CNm}uCIK1SLxI7O2V7S$PB&P?D8|&% zIr*c%@bq)%gxR!sw7HoaU{HZofz5GUcLyv$L4jlG_@A+mrCz~yGNZD5{l1UQ0**`q zI*yEmAW4w=#dn(o9N8d3yx^S6XvVYx&h$2qstAp zX)VYmh+(`eV3CiWH096>dNf}jG}!X3~JIJ%_FcA|+kt~ox0edAz zULFM#h~*pt4vJg~@rr^9sftVrDfNns3dxGx3LJ_i3a*MA3a}v6;?d=10_6%e$NrvH z0Y^@-sjwJY`KDRGksl(&%LvM0Ape7Nm?Oy5SC|zKg9GXUv*I>J9yM+S6AdN>B@ z;!t38-2ANpNn!~?;_|u%0Y_GFDDpBot^i5YGbwj7W=tpC1Ql2m7|oc@xPj8^ zF$P#E5`|uhOzH0waFjzS6c`lnl_Fa~8U)}P9FI-y5^(&$pa3mEPV~bIka`{?P*#5c z4{>ljKVVkkaOKryasY+*6J{lTN7ih|T2EdEM+QeG1%5}S62~VeCV(5TN=(^GjG$7_ zTiTH^OOaQB$&n|=@drp&k%`$sfyt2tTz2uh^D-(hfr>wV1r7yBYUUMG&}4Gt$x>ue z-~|=#yu1wDprV$;jHv@ujPsi@_3$XLICk(rgT|C??z%1kN0aH@=Y;i`uB@B)r~n3jNOMn~onq{?RnC~Ovh!bW*| z;5lIxjM}FaRGBEllL08n;7>QJK^h3uJ{vob5|06_*t)Q+MZl22NEM3$ zi{lK`AOZ0-m}amj%>f7J92TWzi~_a_a~KttfdwXjTbxs%Wv;dYi^3d6Go}lmfLO+8 z#&m^8ft|(i0?7NIy4RGgZ&H_lqbxXSu&o2JRi^JhCoGx-N*XMV9U$vqWv1f>NPKia zbA; zujhe>{vuEf0jwlEQRG#Ylx|jEee@+7Mb9W+@7RE5oUb zii!%+iUJB#7!_?4CNnCAC`@8hG*+0wsA#S*kx@}YVFII~ghD@~qOL+8qhfHqLNBAD zok96C%f~g|ELMx+UltK%mqMt%DqhhQ=6Qg2`LL;N1uR;T(qK`to zqNhTgqKATmqPK#(qML$_Vwgg`oT3$|C&&TL+AF}Rbqxzr&8N+y2MXi`NP*1a_yMAP z0a*DGWaUVWfJ0LdF$Im!BafQ}9JwIz32p@Js7EpP0w=V+!=k~o18n#n7RL|Qx&#DV z;8iiyK^sss?%)Kwe*;+0790**1hO4cXv4Dirh1T=Ad-ttpqPv7pc7!j&#*Xt!s?&{ zI5i#sYdpf@_ytuXD6HziP0nsmDZ>kP1+NlVK&Cr1`VF>sBg#N@c(B&0?IDPdG#a-0L=D}kykM-~NsSlds~kx`LJ zfzgq%P|=Z5k+F-2s&m{ANryK;^;>Sh--zHh+DWfTJC_Tw;2#9;wPQ0Ckun z9YOg4)DVC9zEQwY65dY!Pte1B-y`lm{r`bOXfGV0r*8!=A7xF$uVWMtLA* z+YNBg-C=PQfYdWDK;;^XgCnEjNpL-5@_qf+;f2-Lr@+-z1Fzy~L@;*nDzGRpnKAW%XtZFQzzb^2@Iu=%n8A3ty;HzZorDVG zFGvG{3Zogdx^@H=vY`G5C;%_cY7%gifdv((B98*N3xJE+yQGy zIlcnv69DT2M^*zXQaeh6sfAVX3|LbGtKwOZCa4Q`Eo?yVHeG&%=<9-AQ194*WEjcy z9mUmMJ*4wTZhc2_byrVWeFv%T9E~H0yvhYutMtH zdh}=lmzUk3@P|~Ou)KQ&6iJYg4QSq-L5(P>AE1UJEK2GJsG$gpl6r8?nE{TIIjoQ- z4b~|6F$<&lw-u>H2yXtZK#CF2P!fwHXsDY36s4e^<_fSaYgio_1Uz7wvz`~)09?V5 z1yToZs)7gA7GM|%QpTdev;b_>5>~i!S6)ai2btlx5;S-NE$5+4)uo`JGH8l|Hs*Rj({Vo z9+nYd`EUnPV7n@UW(Gl#%mmZB1FUxsST9^FQr7F=)QsNdX?hMHyK)5=6Bm%IsAth& zy27fs1l)eQz^b?u(rk4+fux4TaR#K_dx8}*RCR_`iAlf{)bL#bYWVUhK?X)vfZDfA zW=v~9G+H}%1E`(50@Th`W^h! z9)Kh039EpY;!aRo6l~2NQ1_D!*8OC2tY^zsoXe=d>d2O*xE!u(HJU26Y(;yB8o1>< z=C^^O7dkV>>d2X;z~;!Ajba&ecoV9I&5fa}M5{u;;dTC4D{PpMR{<=t1XOrI>JPBp4e$v?@R)52o8mdJ-3@Gt z=iv@swY~w?EdgulfN81+4ZvzJ^{^>k0PE^tQ@jY*b@@b-fFqJs6JWX^nx?QRUIJ^H zz@~T^uIX1FYJYMDk|sztegGL$nZX90bDF~@;Ej@h!M?n(wFx#?#w!mN*$WbZ^sK;M zSb=0Niw4sgHpK;C$E;veoCkNz9grp@A8bI?w1ur+@d{Yi1~$dLU|k?5f;w{Rzr*VT zXh-?Wr3L{HRGhVNigyaqO$Q_&FYmrdszpaEN7$g8Qq z4i!EC5{9HjUQn0a2t1q!a!m_p2oo;Du4n`{W-3S&sf!O8ngpqzI~^VgVDq)X>Q{h7 zA)x{8whAcJ)GI=yroMs?ra(Qn}t3ZThMP=6L=QTN;?*fb?LMhw6v&8`Ot zLsB)^WeVWoVUULDpeYkrWGS#KLUR97kSHu6IJGo1Gzh@PmO+}@HXwz*Qv*yIBsBvh z1t0n0g!VS-!Hp)z_m^8>Eh4b%q`b`4G`}CtB-~0)}Q5dX#9Y_@JOio2j@Yp=a{HLIqFE#<4dPhb`4ulMby#WbB3U+X!V*(BG zgGMsJ#wF4U7^6j&XFAZtN{Kx;v` z1i(Xbdq6D{@X+Q;NNL2YxDV2{QD8DtXF39Eqi~op9RSg2ZIu(Cw#oreTLms`JxHhv6wsWoCJU=0XO`l1 zMn@(^X>fysM}ymuK~WlP>nD(Aq+vl`M+U_R`0NVEtZq=D3Yn*X7PAXL?HF!^>D*b0 zvl$iG9J#X^#tE z-U}c*;67k+WU^v709y42YA~5G?Ex`C-ZEoCXxR!fnY$ib+kuTnEH-0Cs9JOeKJCKd zps)tj{u^vC``3W%hx-w1{{uD!7O*ePnC^g>td87SW=sezYe6RS!tIBz%LFZNM5tQ5 zy$M#UusA5JMYaD0-2Mq5`}tvE47UFR$Wk^({%kX*Hy|dfBY&0|6GF=_kja9R17bz% z5rS)u!z(cs2ZeR0*8PB6w+)nFP_1hKg)zjsKM3m(T6Tj>hSZv1laWHZ9-(UgQh1|? z#X(^`s{I}C=xzpimj}hW6Cn2UWScSdfS9a~JXvN;2rZ{UCPQjeu*t~wBUCMIh1X#$ z4hkDk?Vka+{}ZV7E`VbH0+6L_jsn?cOmjd?R`9AugqE!!lOg#PY%;R_2vw`kv_hLr zpw)02QSD#B&Lan@h>wB7pAW_U4IoR|9Qm@%nAU)ptd4wHW=sezJ3%Hxs#O*Tg^iGf zx{#%%^R3Es2v+44~t?*SYpjMm`eEt=rXYrp# zSbqatUO{H%wtz%oEjdRjgVg%UO#mC_7F>t}<4R=bWqQ4`fq8nJ% zD$vXXyg|hP72O9Cg%>-VifZtwT9A`&fg&Fg_+TfgflYb~5*3Blkf51rPDMSi)a_lU zl`TZ{El3nvMuR3rJis&CAPe?^=I-FWV^H(}n{o*x3K?Djhn^>R4jH6=CdjL>TDabk zQPC5uVG~FgvWfy)cpe6&G+}5=JO2Ow|35oerLcgH(qqQu3Xd6`{(%I;q!qV-Do#hx zv^i+D8f43jS$QlBW+{eugpovMaCqedTfbH1;@+fp23P=ZZIRZ$-#m9(- zGDw3tSi^acC^U^~fkj?|N-RNGJTfZYfV4^l1$-2LJA&IPAk%JvRKfDKBctL?h$=n- zAH_dlMJx)eW=t1A?HD#QrYoSf%LQ25#guJwL#KeF?sV|lsKX7Yv!|Sjao`mkAY0n! zHz2i1D;47$LF*(yGPD09WnD-35(<#ijb}*RCr9`a3y{=FWGS$3XM$2K%(tNAeGB3d zkaz!rk||hKH%JvPB1~>W6!8l9DE|Lexrrf9f*EVX#EH2 z2V1gfUOng4dCNEIk4$|KP<6Xc-J>mJ}p2 z$fL4`&jA(pC~!1vpbf7p#D!&VZ|QaEk^k1zFGnStoJnYy+(E4VHthcmZkqKe0)` zQ5vcZ?2Q)4d@d-gSwJf%n6g1rwcv3j#7r%8IT2J5XblaE;|&f_z%qeGs+h8&qdoP^ zg^-Yi&I&>ow}AA)#-^cDwO}D$M({8>iz8^J5oDzTXjlt%o{?9NN1Gd}477d)t_*4= zijDQ~Sx2z>(4lYW92Zy!%}REV^VzdeCuoqZgt!cvd>p}RTJX%{)Pqwv%&X9e98`Nj z!@iKI8@Nv#8Q~K*$X0`f(pVMPF#HJ(+bf{*3$i2|X5pcFP&R>NGtihYt0QlgB4~j+ zno*c&qAX(4~Aov1RR(BED3RPAGHU(%WSa@>6ToEw^HZ!IhAn&l6G2H?A z<_6q1Y|}S&2{~egV?QX2$der0)en-aqv39x`qiHlbgG+IB0Pf-L!&)`$!1JM&WmG~7{>NS{pIF-1)c_kJ2L6PiO zufXKUSf;>~<;al*TCS|XXV%fPL`q`>dXE2+S=)=8#L=`o{|LdSAP26sh9 zg~yD`6~Rl+J2(}=+bk6LSsWA?L2J}Cn5J+la4YaT%H%jQDR4M4fGnTEslmh|$j#tb z&sgZn%g7_mt-uJL00pb&aAZO#V-RFzP!e|KWl#`yT*aus0owA!;V6@*$OPJ;1Tw6L z6K*#Tvx5Q$$YuqeEG11%CI-b9j0&2L99badOAu24%zVtKz^EXcr6f@AD3PV0=_mnO zc@0V=0*(S%3Xd7hnHUrV967QSUP71(FTga1BV)EAXj!{VmLek$BR99gK}L|@1pF0P z6ok!~IyiYGxfK>O3Iyh&ElB0Y1X}8sHCd`iKvc_&X$!~?7G_Kv zKz`T&@&jlH$dv8y#x4Oz4Y0Er-%V~Tgk)&2t63D7967Q;PG>Guin3xj;RXp81(0`? zWF48^ATg)J=*Z|^$jhZ5=c*v<$l_MW%dN1C(Gj$QSz1v=VGg6?u_Fxv3Ue5x75PC6 zo1bRe1Qg6&S%v7l78L zYcO?iDlvkV)-!4_u|TvffofZTs*6Q~X$dD-=M0F>1sFQ#Ky}VQ)#(%9$Xw{iWP-PJIkGFNID)pyDDs1xE$hhY#>+F=phkyj;>^j6Wm4Xn zpf$3LRtz6N8vq%t7~Vj)96kYU^kdOvKH+B0bO*%dF=M&`qWR63u7K2nwtIs3kRsy* z7mw^@#R^@vA4|Ig9Cbk3Tcp&XsdWR0W;0{j0m50=1WHDr7mH18EaOakYQ~gQEzjd<1XQL(`_A!04!v<@g+= zjZwfriW@wC?a06b+Re=9Xpp7Cz@rRKZPO-$YJX5_t5*Vf2dRA6$_OeTc*MAAzv9J_ zQ8CgO)?fe?(okn8+Bz~STJng1T*hd|v;$Pq?EsZ@$_$PUSxQWk6YV7(n7}(0K^t<- zm;{(WJ9Y%J92czV5^%IY2{8sm21f-&(9T!}$0rQ_;QELu%TaFng-m`Crl(7$8}64C z<#AA8aOCu50U0AdJ>Y<>q`iDrmI8w#V~OL_C2ay(`V0)#44_KaQ9et7G0Ra=fiW9g zsv@EQ>>vpRCP#@Z$J=W_^#%u%qeHd|1CJai%$ZJ4n;cgo$q32r6Q*?vKzFmrfa($! z4W={fN@k#%l?A*P(v0bbn*s}DrpTQ6f|~-1!V>7FD}|+uplw(RERKw3Om{%LusT4c zq9bU}s6de8lbca_Y!*k*MpY#yg?df}MuA}PHYCup za8PtGD9VGjcJXB?sUwZ|9RO84pgrDaK=IMQoTVTy5Td~1c!n=a(EwC|G%za~g6I}z zMI%QBMPp{j4$pcf2n||ZX{2E6&f=h8s9@~KR+0rO{W#n!%pylw!3OD9pF<^W2skAb7aXjWnyr;zyNCVC_HAIF32t=GFhOGt$xR0hAbs* zBnNMB25nGh#N1Kq#OSD?z?i+ZZMhPoBd7(z;VrGi?#Q3zSW~FPslX08>wpQ=;9|-W zPysdEF1RUiL7Z{IO$n4pl@t`%K`j&}1ttZ~EHfq+1*UohMg^uUkRYhK<;hYKH)Go1 ztRU{lprD|^!~kk3u_&;w1-abm2}8qjB|9^w9nK0Y8cav{K)XjlEhr5~21N-4H5Lc3 zKQ)*b6eYM7#2tCE6$KQ;9XYZU^%T?`d9uNs8ATCTt3i>^kwHBGFmZ!b`pYWgA;B%Ox)l-b>Izoj9CgC zg^El(4BQOd;4lPjwqpmG33m|G9ZH-!4D}3-3?ASN%$TLX4$=o|5^_SF?AX9q1S$nV z?oi?cyD3YFNkI@Cbqb=OXagM!p#Tab1#XBa4@lGw)XKC|&;V`b)=*#swa*w8H9#d3 zH2xGc%$T+~^RU!&D+qzM5()qX6DUJ5J1Cg3fU*>5r-u@! z0#iL?$D$IW;|kCQXBKex9c;`CCQy&$0AH2@rz1<261xJUBTKdtqd+()@x5SD;&kN& zZ@PHHqzGCQbAvC-QCA=Ww?jZZ9!HStK_v&w!LIe-g;RA6+RcDWh6w_8eq z#qkG{V;CWhVT3#82f{Ib5RTEt=NNE14CEMaIRbVuvK_v#Gg1Uk>^f(!9uuYB(iqMAV z5kAmX%>#U(l?f~$<_2eTW^lRwn9-bhgEPolP_>|7AP}R#<;u(8*m88L0A#P}0X{`B zP=Yza2O5qM1C>TR3X%#+ki5uLU#}nr=?^hF*7GoOD~LI=WP^G>jE;=i+)AK~49$wr z(nAbZdN8^c@iJj6JR}vA6nH=-1}M|wEIg*zLAI{c!_Gob)Z$_025oi9Rs@Zjpp+tD z_bNVSWU2>wy0&syI-=tfkAq0y=y`3zW>5Krsw9 zK>-{+-1SO~3YrjI!e&ei+zQ5^($E-^%N!4Yi!dor0=sscAxlw&$w6T^q%#OgaE|{O z6?QW!>|zv91s#ZRlpzaLFM#sME>QZhVp#7A3K`G|I}GbxxfMh}stz+`IW{u=;Q}4i z+SWQ? zgJ^5*uz|L(~}X(nC) z(I>#>9WVenT^VvZ!3n6V&oC*m!(9zIW&>2qD6oUZG|C`j8rll%pfQa-7a$uBVPhKF zkTH#V=pOnHAZe6A4R%na396<*?E?kq_$y>k17!=nqn08UXsVdS@iJ1dfSOyNp$q#$ zB_=C|OW=SxV*nb>P-F*%$`J#|fK&Ym15ov)!F0et5p)vA0fc8k$__Ar4r$;}U{v5x zV3$^6%vNMqU{v7DQe<@01{aSzjx%I2f!nH}1f9(%#mFcRhK!8z4Dv{Tkx?ELIm~8E zOF*ZuEbanD!UDT`a4di$VFsv3VmD&~7fp<2OyH7f1=u|k?6L%cK#A83ysKgb_=t!# zOaif>RI$a(l~+!I#c>6~`;JRMY$j0C1)QBXn3*xn043`M$i`_fEnun#H6mF+wU{~R z{29=Y3Dnp*<`84|fQ({<8#Tk+jA@O#BNHTJF`F?RaaLe)gm~wGGr~8ZMk=Vay9Lyo zW&}5CSsZtOt>18%0pgtnmRaBubqCl#d%#Pt^%>V#^2&l7besWHq_Ai*FR%oSdP%q%j*9?Y+D1j?E};{gukIS z{D9fu$jEKQzyK=SKs#f>4VX)|S&pxFb_wV*K+XVjJY}2Zcz73*z%QFD$K%KXt8B6y zukWfyQu1JmVdiCxxNj^Fnnx$Em5s7pGm9d#7B zc%(rI8RX;3+mKX&e7t%)k^soZ>yZUOKHiTkaB*8#J(7=CZim_f_VHR65A5T;Fdo>) zC$=N|_#l*r@bP67TVPrtJ_dRC)^?bO>p?9(kdN;p$KG97OmtW~YASN^fNP)`kW>XK z!Xamc%wPf?5Ca|zp93jc8O@lcfMN?$#7?k9WQPu0NPD9JL^GN(wSbgOU;?Fo#|GOh zfgo_X$)dqD1yrQ+WjmI7f*Yd;K-C_2SbhSNBBvvRqY9)rop2qsI8}iZr_j>$1W1;s z(zG7)L=s0()Hd%$ib+t^-bNMxMeTiL0Z`PwM;2(@3yn)~TD}S6fn)YAjQ7Jvp`OL@ zEsP6}+8=w7qxK_|hltwdeMk;#+y}J<5w%_WU{MQ;+5Q7aI(iR4b#z!e>MC;a)PocA zRirWwQFB-^TmhAtJ+|O#13V_uVhgE0I&2}eMuRP=LUil^Ck=2sf@+NpCRbhtP=tao zXiVf7LzX}sc!XGi#qoj#_|za!wI;1p59)R*FlGyQg3^>e;~fhnMm`oMSy^TVP}@x- zo0pk~1(x4tBl`ptDW8!AK#}qtSpXC%?FW%OKNIG8aHM<$@#+~EtQozZHAabO1LV1WtnT=u#Oe-Q%79B*7l;wvZ?y3hj!-l^!0CbZK1TI0==4lb@e4~2SbYr6qo7iWyg0@e#Ss6~HjYt!tia-k25_kY?lXW&l?HG#9-P)ULvs%}v_J(x_Yp{T0t}pxF-`4!tlQxFDDdC%u8Q?>1jNIIirn|C&5OOyL)Ln8sajXm00|d2oK}tZQ zZqQ>Mz)Ct!z&nW`B`l5~7_&f?v?7xu=+Q7%h~aczBv6Jv7#FZd9&630J`g-VQ$XRbhw%LPwUU%3S3Fo8zJm_S`+P6ZBd zJ6D703nQqjm<1VP`M{{e4X*RWKqC-hppFZu`=rPT9`RLVQczY9Qa~R|tp^VpF+)dF zIYBc7+{_LNLJIc&yi5u_3hao`bCXu&1P@j!F)9j?9MB3v3XIu`+zN~e%2|pW;CX*u zMJ6Ub2WAHaMg=}MUQic{d-CEL8jPHi&&*J$2j%zc$Y}$V&etDDN-Ussz8P5nl+F($ z3tWXICvb(g_Bb?gfs^@07!RDx55johWPav&S3Odb`si^;sz;>r>nOIs^di#vz2j)< z-0|^AB$q!t33W*as1eR!#x%nkG0Z!`8gx)+J?O|yra9IMe4qglCUAH&fg%>vwou@O zOy?=GEATpk7yc;lLI!dbnH6|J<3yk?-303_CD0HusL9Cy?N3gy%W`}IntNpwhq7*b@3@S>vK_gM1Q6d({16El|%w|kC zY>|$OV^Ls6_H$oOPnrxMuU3+nQ)j_H%p7L?2dс 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 P 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 z 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

    L?96?ty{b(LiIkG?$QI34i<5Ba3G=uht zLVbu~DyWAd3a%p$fVa|tn>p85K}qolil*sl`8Y8Gh7_9ylY{~nXcvRh3r2J1AE4U+PfyP);1OpOpWar$<7_CdzymFDR)|5=A>OIGDOnN}L^HW=sb_ZHxumrpFhemN-|i+dO?C)wSpty3h(9pnFhfOy z>4%sikAet`gA;?|bH?cti+Cg$FHT=o#AD#RpBL2Wx}XTX9ZX3YTm^!r2iU+@gd7HW z0UT0Xp!y!t$=)IkN(yF78$h(!blGAaWoR$@j5yX53epRT{uAP6Ocy{U=z?w23yOJE zBoq~d1#Yu}lIaI=l&rL-m`4TLkNkrw^Q0JFHnm8g=#eXd_pzr)AiHCFObNV?Jx2oC zUj>KL^!^ffyJ-!oo~tE1`r?T43{_IPl*f!|!qMr$r94_9&~ge@#l%t`DaJL^*OcRd!D~kE%XoNMjCuO*6#Jm_WUr1>2@SD2G>nYs8^h9qhE}!WBH0BH)Aw zu0y~pr67ZG2^Boz+C7{gXRvT9fDQ?Obn^rtV$7f<`$7TIM0Lm(xHf%J1&=KYC{m|C zuHXS(3eI22W5Kw3x<@6rO<7zCZc}am@lH*@T?y$EuLTwP+|VL_11xkWNP?PzOi5rxiE=Yn4hZ~Xz zAA*bgJ1Cl_+gI}_YEEKCZo6W*kyf0+-e@B`d&~OjcB7bGPOX8 zX-SafFC-PEK_jVBpmOMkWEM#5jidl%Ah<&cawMW7szofI2$><3<;YT`qzJD{Ko!Mv za5J_=N>LbOPJ@)9hyshe;}Hf=MJY!XMP6nH1r7yaH(n+MHU(}4Y4GH->h#ze9#clC z>62@CEaV{l=>t+qT#&+Rh7`1x0J~uN%NiannHmK)1uiqD7mALI;0DMG#Vp6|PbLdE zf@TK|YI)LBf3QNzKTtXcHR}|!K*|-^9Fdf!PTyV2qrwGh07ASs{benWk^`h@JR_yR z25Q@#kOFB3hs_D8ECsH$N-r4&I>95#SEQiB#%NCTuH%t{&f`A;n=-wyjz>cFD7by} z1{R=bnwHh^s5wA-mS32IgJoP+^x{!u&k2Eyq zP%T0?WqMRSkD4qrk4t0s(A;_+BUwnFbPaY>r@yJ^k+K4Xqynh^-vCNbJETDgYJ+r^ zzyWYeat}-^I3khU;rQY4bcY5WHAcDV=?y&Q+Izst&)`xHIt~e5tetG&(S(-Ucch^` z8L;`&*&2BiL7jQcMjk~GNWbX|ijmXf8hPY}*U^9{2tiT6K-W@V#OakD>`ShoaJo3tr!G}3GG_fjhD~L>2Z{nE;8csUi z#3Snn$z&H~pp_il7H|m;F7-NOv1BqxU-5*DlE`$`W**g`jiCPW0~v7ERZ!s0QsQ&u z$WoF9?;Hiq!Mu>kQs8qG$WoF5PgpQ0uqp6?C2z=p8Hz6%6+lwpR^kttECuQ5bDDW% z85^hXYUYVIhh)hOve4AgA&ZtEx19I2B@2|M;4TMrU$g}sMbUB$`M&;^9@ro zxKRQsP(UGiK-P@uge*8nK?Yp`8)U|G0VH%o7L=ea$Yz030HPmx2d}%Pe`(>7hi0rN zcvZ6SfE)pxuX+Qrn8%Fi1<2wB+olJ%!ZX$%;!K&ouoa%MdgP!<6V>i|Wz^z?*w9vM*9s%wX5ttY4kZ*J$& z2B~<^&LeFk1)F^Mf}#jy7I@r(g&UHaKFGmylWqsR4bdVG4H&QkrYCgp*oZ(1zaDu= zXSf4Ii%;Ls!J}Lc=~OR~$C{x)l`SZdEs!^3S|JZlWLrS`dCZtLfNA8~%$#`#NZA2- zP{P|G59(;cnz<*iS}{GSlSc;H+`WRLVtP|2yt#V^Rc22oyt(^E9@?mdc=lH(yt(^D z9=Rl%uGqbP>_SG5Ku^HFm)&>axv8_h)uuH#iIr- zMW-l08=VjraCP&@NUY%mtyR)zoTH!wTCfYMYap_A-8^a{&=iQOucDhrMpDd?xljNy zE44)d+A@Y{T-(iK%lK*f>uw%7Xao5OsxM@F;0@$6s3r&Yz#GVSP-Uj~Aa(j5C_oy> z5c|&b@U$?lo*vlC6U(@I`ubk*w9EZo9xui{)3y4*qmhYy;Fe=gAGqas62v<-{YxK@ zA!F}!wSFFBM!o4#{X9O5{nMBC^ME!+pXldNV_Y@;eLs&Re(=I9*~2Pnd?G0;eOp6+?gmr-Gpw zlZOJQqly(n0)!W#!0D)J#ZUm^O`kS}M_UlI3Y^J7fm5HcMv0BZK|yBvr71in9L5Ul z0w;Nttf%u$<&kGJnXWgLr+}%Sd3s?NtLXHtQ+dQ0Cr&>*l}ATn6|(}T2Ga~iRs{}j zGo}TM3Y?A$7(vIn3r^!nl`;k`?BN8dc)+M6Z^rZjB>w^|KVce=w5T`Q~U>c7y=P@Ql76nenRZpfLT)`tgoq0NsIpeeG*3)^6cuxFj1Z}1$c4Sm! zo4#NPPX-56Bz5}B={!0j?Ys({jw=|m1Uh&XWE9vy^)BD^eb;$%rca&0!_RnZ`qCLZ zW*kfQw17AEOn*FsCm+U3ogOul2egrO!b~1nv1v>SoQ?+=vjk8~oPKyFPp+Pf0%)P` zG-hi?kTcN~@$hmhFgmhiDMl%u@ z8x%F1`i#?AukZ+^Ok)PES;6mM9wCq^FPLDafF>w=ctI)m4HLLHMv5w|-kmd-$ANME z^t*F;Oa!-a2pnTl;?`i2&|qRwh!WU3U1=VVp@^6Qrz6OnpkQiv%mCRgn?8?6mIJ)7 zhe1hXde1x_4MvgaTj%jOih`H^g4>7>k`y={A3S2n66l`JJ)g&(t(RAUQ$c=uzs=(>^;weLx;|`Eoeu3iYJLmIM%0YEP ziZ0$V1r!6OP7heXBf}5X0aG(QaVC$#^w|q|xEXt=PngLgp%Nv4nvSss&f^6ES%w`HRH?V?I*Mi4LscS*98PkCjXiOY7 z4Wu441~6|4PaMbyzUl8*@^HyP&4ES=g93_iQ>UL@&LhJC)tfr~<8q!#GhBX$6ums4 zc|_1;8Vj^&aA$D<=ZOU?cqAB)P2ahKCk(|S%_lm*=iGv_0t*oy*$7gP=@Aa7IjPeb zSMh*0GlN|YRSb5y)pVN~Jc`q&t>Uqk#p{RZ?^f|RGipp%yvma{ea31YV?$80Lh|5?o=V~ttJ;?w{sV7WnM9azou;59rJ`XGBJKT zU9pH$WV+)9o?s5BMC$Z88+Z&DCr>}SfyYYRP+$r#sQTp8V49Gm$N_4E%t%rcnJ%}H zN1ah*y4yw`W5#LIt2XlJFkYO#d?SxMm%JI%3Q%RfVB7Qy8+o)Dr%wN~kw;1orJhy* zk4W-zD}c-H9V}){2UtL<+KlN0RIAG-9&N^H(+f87NEzeNdV|G`=>d!5xigam6uTHf zO)w3nBaBK0)Aw%TQF2jXa!@dEWcFoo0Pj>baAeBnV`5}r1|qm zypW?jSTvbH(;W&d3M!xqiHSi$WqQLF9x+Ch=~kP0lo(Z}$8P44;!<(c0G$TRA~1b= z<7S=$1E>L@O*2?bPL9Q~&1P3nnXb5nN0w1#y8RX&8C6tAfQn@-X24uAeG89L zy$Wc2g#|jk!UC!#7(h{E;K-<8knSj7T;j-7rlgjqsH>pn$e68QP^f6+$e;*nX>n0L1Qz!IrE7`1vUlUECntFBhYcTYW|>LVarzJP+)gtP*77~S5Whm z&Q|17P*GrWoNlp|$FhC~uL8RQivouulOwYLoWtVCir}z0vLiSQj!Xy+BdQ)YgbXXP z9!6w6td2;^80sBa5So}#H6b~H30V^(svaccm{4Vq3}6P!@UViW3OF54@Ph(|)A7W^ z=?y!0SnE+72vG_R6;@=6QT)z~97HUr$`}!TLJ3I}Z!mzJ1hU6Rfy0pz**FH|aAiW4 zK?!jb??U2_hh=)f4jxfdAECGbw)E0t;y192D$X3I+-s*-+ndD6oN?!mD7Qz>=*1 zb`b|?waZLi(1mIS(>?d_$TLo#p1FrdwSE>as{*eAsICNev_SfJAZGD{me})h^DuIA zE9g2hmMH2f7&$VQC>bfJdGa!Wgo;2y3ToMkMm*qSQ{WYY5{rU?KQEI4rvl4b1r~UH zz@`AI573%MTjl1SdLBl)(}l2R!OP>!et+L3ub^U^Yq$Vh5)nZhKG!K@%HjrvVhHO^+Brt>6v_1*-MA1?EgYy_ZM1URps{L04cIWd2TxB~MAq zkugh2L_q|!yt72fOhH${%u|U?T7ku}k;PLC&k;joik;9QeQHsfd*+IcTfnABkm&L)6K~V%0dEmfQ1qBc!FzrEMXN(?rU^zw5 zYH(1U3kg(3RdBCE*pX2|*pbnD`htBt8jLE_PwwNfQL#5;x|5`!0&2QKe0Kvvfjr42 zFqc<}Tfu(1=6)U}UM+8MhL`}7mzo~2pGVKp%$=7J6w?YKSxO?H=r+qz5(mXwi#hX- zBn1}G>JLQ@9#(EgMnx${21PLiX~-$9AWwp?z&pO5$IHjejl}`30;GvekwZa5iC0k= zlyMXxK>#+5O_76#1!0;T#59G=nFMAbw<9FR(oqrrol=qka^Uv-d2 zR~c@NqBtV(q!q+md6}UhCq4bwL7r?zk?CoNcx*WpxE&cm*n9e#L*N$X`9nO;)+*p= z1rMHZI?e!%p+I~NDmg%&=K`fvX;5ZRU{jFJ2Jt}0fv{x>%%1Lhm`7V!SAj!;Cp%B6 zo6)g>#Z&1fqk_ov?!!D%+-3?Y3OpcaNSV;QZg zzz(e%MHIwAhkB_f2rGyvunWxRk#vRmUlrsli0@q$|Hdz5uK%am(?*fDZm0jS{Two5k;1^L4cL$YS3ewa2kMoE{f=as^5Ty?w6hy}k z(5T@;uoh0x^x_wgJ?v&oA3!w7z84_-I2~U=G<|?jC-}1j7JFNKD^T>NbS~ej6 ziztBn4+=|Z1rAVs&8xtct-vNQA8dz;0wnW6Wu&t~OH4qCk6S_2kughAb$Z$f9$igo z1r|jv(8-A1jG)NLR$>7SK|>o73UUhE*$RpZ+}Q%Nr|&tzqa>iHD5t>73JP-tMUeH= z-<{wQPnS~QP>{<~kW(o0mR6Kg;80M^QdCsn03{hN1#U$a1zv7NZXO1122kNDtRN05 z>{uKb6<8b@y`>d79P1U?6;u>NKxsyaTTvCVE(RQ^;K5QwHU)M?eg)O(IVX9{WW}H{ z#}BPm`9YN@XiQZB)Z5*Ek_U88?dy{~`s%8VB3X*6pjs&lT-bvKv*nl_6yzM4+;~Al zzu?+NZo17A!|++{(zAX|Yw4|G5z#C=M_*@|Kc+=^@pD%0Pd;*pbvq;XEi z6`;{waFYq#B~}8BhNzw9u@zdttHc5-soA(ehwduy3(THgdYUJYQDyq^(>zx43S6KT z6^a$S3LIIA{0dxPOBBxV=&*w=5tu$b_zaJi^kQB`7EnBco4pD=pvD&$xUncOd-{$u zJTkU&pcs_{hp>Y1T7~C~pk@$gf3vh>1E`i);8c(@V`@oJ-~=rdY5>uo6!!y^;tZw> zp5>9QM@!67W=vB+%DBv!CV*&=@{Sa6ncwjQ++LXgp->VxE5xEDARSz0Obb9XNaqZQ z&KVFb3m_CUX@gzu$OD>#gI6@$&+@1l%RDm5m?Ny13U^)Rkz=%;e)BSq3JYld3BM!tuJ9zDc~ zJZ|8D)e{iMA`Q=Cy5P+f9utT!XV!x}xPUoJU=1M;Du5SNb8#zxnof#b;22~FO*b)S zDS*8Vu@dLFEO-JGyci!e`3Blp%IWxkF)<5eXCRhgU`V>+;0BFAGC45SgU00;RoAX(~_*o=6dZ@*ooquDp!XKThY7nVztU z$837+3?2&w1s2fZ2v}!Vz+=wSy>9RrFy5G6eS=4V6XF^-MUm-CKwL3_wV;7f1(E4z zZ}4a{9-IF222V5Ny6Lqyc~td|F)8tCFiC*=KMLHA{}~FEq#ap76PCOhOe%`(+!{C^Ar;*sWnYD%5{{}y-(+VnP$lK3;zj|!v7^!ayq7S?xzr(D5hhJ%6(L@hXu7(pZKCjBKO7|6Lvh#`V)5+~v^_0mm`Oo1Bgt zm_egZ+ydLD%iQCU<=X%%lMgUwfkt2ic1(A_$D`$hQs6)w&Iz&%BXA)z8622$(>LGa zvD5=)+zF733(0w)RvzXu0m#Aw_UQ-L@F+~Ty3b=@zvake0fFnVGj&jPVa&0D=EuPS z!U%Q;YnGCT;|kEBCrpr_a6G}9<+uTH+6kxQ8P+T%aoE8qN|2#@PRA>(puhoVgX zai8Zq(}lCsKR)0IXF73qy4OPn394}NHK@i(ct0+?C2e=pkHb5FQWe#DgPM3ScqvO`k407rpHt=y!WIJ{GghxE$ z!r+ToIW?Gmupv$p-1UgZOaxZ(b1SeoGI)TZOTl>k}SH1w{BN!o$~$=>!`nyctUr#HQ;#je~q zH7^evc$yP5ZN{j4rNrw9ISWh z&2)Lrqb)|4Xng-1CCl8PAR4Dfz2K3qH&LKgc7~MrtlSEnjF8d-lA1y1W8#i2c>?9> zbm5mgO7$iRf(nSV3pq9fx0!g2z zyu<;@8!y(4gp3@P$?vV+!_DBHBibt_} z1ttYufhJC9#X?cR46cJg-f#pR9K+9m! zWFX-PqpCMNie9LR`v)aC`so`UMJ{8S1MOnr)MYrsrYH?+mYro&R8`>AXE@KMCdquBI@ z_dF%_pb4!d99fF;(A9VDid+gBio)PICt*-)j}bI>plAk{wsvGxRB>cblm>Ovpi@+! zX)7a0I}t=OIVcz@_&|H0CQ2*{e2z>dN?M>f02WYntEj2~I?M(%&^x{O15aSRsv~2G z0$Y|NQ%RPh8g#H4GKnCjpb8oy;Zo4ZQq%xVj)4aI!BsmONVSrdBgDimP__Pp6SM|Z zOM{6)0Md~9!>ObND)Aj1vX$6CW1a$Xpvesdb^$pBc?C{KXhS*yw0@HVG)~mbq#zDy zP2*@=vtcgRRnP*pw!mGs4$zTH3QX{p)(=p4a|-O8zW5`LBCK)8MUQaf!I7n)robm~fkR0PysnXm z0M;M5F?AIA=ktmz#(y!`dISm|y#D5ds*PSaw{GMirim8ZD=4rsvQ1UNNwIyP`+ zIfCyd*a#W*pnbv>kb&fLK~_k&SYvwQHy-hNF;Ig|3^c_BE-|?jRNX+6;Eor-4i!ce zMOV1MhdP0(TYNwG#^Fflz}Z_2*`qltU9+ju zQHNKIxK*3w0Xh}-Wy5V00eKhgt+1Xx8ii}KRnX)#*k~eIQ1Fd&^aJpfC6Ir(myg^((=7=MP;SpuzM56bfuC^$OEZ|K$;8Tsi&z zUmj7~G?u{84{8c%Fg5UiZV6Uo17+T9@EkoymZC7IVV|YQ$HO?i_#cm2{U&f_Jcox~ zDHAlcs=+ja2eh}5Crg0?(xewSFkRt4k7x)j+KNfl8Z zuqbeW+pQc5w6I@6)tZrIst#{qy_kZr0Ca2xX8#7BEP-BTSeZ&&hcGFqPM2fkm8us5 zHBE(~wHT0{%#>guYiRUoE|G^p0$bo{`RC2*O;jEP0T4BU~v!>VAWz@kvkC2)ro z)T(gE1`C5m*?;h4L3`H%pglPHj19a>pixRra2!!LM(UxHgxLD!U|)jLh6d9g9?&p~ z2Gb87(1s&qcPN1Ki6eueilQQDPX?#s0nlm33P?@&Biy)~?p}~G2QnYW3F4#;rJgGb^u>u!(|-YY{I4XfwS6r{fHcEP)l%U$F9OFkYN4$i|z-vKcg<23iJ^ z3LUPdVS9*2hz(!1X&6rm3Donr5&MN{Pw%I~= zM{M8)_baDoaqyb4t>97M61X^h83(U?)=ds2KBT&UhQlP&^*MPZ>rFtl59CNQ+V`Qr z%^^`xVRwKR(uHD!^r5Do<>XZajgmlI05Y}}Zm{nP6N zc;|9}#vT>81-49A7UXqdoH#v0kk0JWDhk_j$ z75Nl66{b6j@YXTjnSMxw*D)KZW}yoMu$R=A71Dce&uPbhxD-?@PV2g7N9Mj;DLPT0K^YITGymZ3Nq6jBze;% zOhB!1&?VAnt0$)Km*lmm2d|QVU;o7Ec!CdkK!QdiBH+po)R%Rv2VJHCt^twPRkZL! zLx<+BsRs=Mz?=gs4mmZL8u%Ug1XgiEZ@mJob)Zeq2w)r})gsO7rE-8-gNenFB}<@> z4boKPP+$Yi!kaO5@Pn#Ce$c64pQL%!8BL~3$?(b{E!&wux3Gf;od9<|XuZQEe$cM7 zNCgu|26qLvLeRQ)ScpO^7)UyUwEC$Uwd$Z##jsU1u&4zmF@Y7+V`X{8>md^;bNH3G zY2Xw>E@4!t2RjC`J_!;c;M#_&0fNsJ^`QHxpfTgfC$JTEw;SzJIcg%WSCD};xg4Q+yyEdA?`3^ zTEY(*>s!FDpg7%JfmhB0V@!`zgJ}tP%5MdSz#Ucvc11P?X>LVf1z4HItpN2L8_3We znCHN~0NRYXvncXvGHOiMQ{*+RH#TEh0rCU48Pgh2>9qsAoA3tse61DySpuiP%MJIy z$}MX5!gWB4h+zj9f@7T*93C1>9pEtQ0f(8Hg0KRcz@6!QO1u*F_5#Z#iMfvltjAA&_>(3+4lKs`M|nb)7bqZgc@t*(5KB@y8LTzm?` z;3;B0N5<(6D!jtb+21L28x93W<_z#)G-z#xBXs!|H*66)WR1ogZeGw{bnxwqoQ?~) zk(X+KHN%!_2wa>ltjY^o_h+HXtBy2(w1OLX{)$GeG|;>i4=CN}GOXg}W#pcIPnDOS zSxn%<^be}E>Y0L1k>&!82W$Y(+M+CZ*ut&E$IAke1TAxFQR5Y=zr_k2yad-AM5b?Y zlD7ibWt^}kKImAsEJeQQg;>0xfSc z0xi|xNd|=#%>xA-BVb!1Xp|Dgrn77E8YCHjYOg=6&@R*oPVf{DSyL*Y0v&UlNI`7+ zOikX{M94zaE1cl<@0^Y^IKf>aDq1u>T#MI?AGCH-fdyVPOrN90s|*^MXA`(M{iqhN zb1Hnc7~)e}xI{s0dagFFLgEDu=u&w|iCovxtAtHOQ+d?5B7@Zr~tz0;HRc#A;iO*gi42~U5e$IAoa7>4mm z3$ud`!@eQx$l%BTUjHm`lxw=;9A4S!&icF}jJ?w%^m)4(`=>wA=T%VbXI5alkwbyi@qz$ou9;n+pLzPm0$x$hA7DP4K>zg9lX+#P#~bhpF&*Wa9@xeu zJAHxyuLuv)VcZeZ1w(kPrWZ`%<(+=+F{jXUPD5T^#{TKzhP(jz0vl1h!6ZDB@LMJT+a)m{*>$ce<4^uN!0k^hRS|6Q<+P@V#OT4&Rr?yoVV3 zr>`;LRbxCc{jv#ftolS|1tx)$T;O98m_d{I7Z??pKs19ABY0o9EU0mCY$Is7A0*<-@C#NTy^G0Yw zBC|(SiJg~Kfz@#W`06(%#}h{<3xGV=0rrbfmcX9rPt19hIM%#q5^&TI*gKuig4fM< zGP5INA*ibl_OAk~;|Y;0&{^|hSqh8-%h*8S)*%Qs8FY>TtK$!$ERdl;1e6p6mQJ5& z!E431c={O&ULU>-ni+> zOm3y=Pc3=%8J|rTwc^#|IC!B+z)@SEczR+Tm-zJgIy{`yeZFw(PWL*=DLQ?v6)&eW zR0U+uxGOIsPc$fr91sBwqFl7%m0~gnt^dB0FfoA*&-qj^CPBk?}NGP81<` zLm1>+kc!LGqpW###Lj@z*A)RJc3x%$Mvzx82nbA@KF6B3m~q;40UKT|rhlx{ZESd* zU><3);jL9W3)Z%UUx}R;oDVnfE3zoCf}<9VjU335;zC88r{Vp-!p?wfmRln#sped0SyNx$LY*=yb_G( zrze>4h%io@ZfM6llX2Jd({{XSOlMf9JG|o*na*y{>&e(VJqW93vj}=@;yI zxfw4`zhlp<2lF^M2H!dIdN4klp6|eGVKnJMvw$Nn_;P|7qM&f?5C)&-%?b*}4q-E< z1tJQpjtfMx92bIAiU<@>U+BauKK+9OZ-OjTGbCO?rII7UjH%Oe9eJf?pt>MR!1onI zfa3m!5Gd}KIO1{HOptwSV3%#+C)#DD4p4m%8+ju@83V3;Dl>SZ9VCj&f2q^MoOw&c z!O4vY{A<6vD7QDI)d(*5OutAbh3cJ8BVa}paKMxTp{%f zt0Tw}V08kgrU$$8YB62rnjUD!BQkxmJFhBZ@ARGSyzz|v(%~C$G2uaW;tT0)CL6__LIF zL5Y_?OW+VEG##)j@PcD=gIJcp)#;I5yms>75Cn0#6u`%#u{s_Q25n~%Rp3z&5;#14 zn-{M!(<#>J2d{C9O#ko2E5P>V>|_Cf2h&Bpc@-INPItJ#B|6>5n^!^-?nqFOZUChl zXpl~?^#<2S^SpVr7%xvh>CNlT*gIX&2VA?)^?}s31?RX#eJ6;5jx+|{#xRi?R0)Ex z0vmM402AmqH&(|LP&FK&w16T9KfDA{V5c)ao8IZmtH-hLONW4?DkT3ZOuz5T>&*`p zf@YO~>9Nkd#?${R^O#S!UCYY_D>F9x@tScQ`qwJpXdqBL{i7dmK0lNP)fhN^!Yp3- z=>qe)*r%_5&CSl(JAJM{uax98CP-m^LkN_9L5T@;!!wJ(iRl;od36|1O#kZ-UX`R4 zz#FJ?4V+TJkuWBkr>Ucmu_-tDMdh$QvnqoeNe@gR0dA{w#rc)AIs( zbqr7?K&H(DO~*iL(FNf86EqV7N(&SCvjlETKNSeh=pO@l`x&QAZw%sfg0`H%=U8}6 zXUyZZ;RIzHMg>q~OC*?AL3ScDD7guOG9!yX6Pp60`rRN3x_aT}^uSEzm9T~I`ZM-V4-4T{5S$3A(Lh1BK@7Cl z?k3mt`6sx?@Z?l<5grlGTk7IR|#%NVHj%5(GApcbiv`sI}k^5fSk-G0CR^^IIoB( zQZvyNRJek|Mszx3DyIyWE(4nai#{WZz}@K+!+A9r&rIJH&TAxyq{D?9oag_B^9D2C zn;sa!tHg1NRe@ELd4t&WhxR<;(mx$(7umPnJP~CGt(2VJV0HkFAX>YPPKpL=0EKt!^%%GWCMC5;+ zKJNsVxbOxsQ0c_xc>83tfFrX4i@-NdNT*?UG}s*%qInGh4+tx;f)2(4m4m`r3VH&Y zK_wul>BOPHq`|}ix+#QJgDFD^-1Ny&;uhG%3O-V;L-9EysL(-RP<8M?czR$AuXw}_ zVI>xUU9kQeFPj37BZC5q29pBl1VzVsH)$m{0q|uv917(cObiNa0$*SqTV6Iu<^Z*y z99gmySR6UB1R(LZKydnj7+zUefip3VS5ESPAS4t(9uUkDc*X>B#uWi2w&@N|ywde- zW=tDI6j(v0u7j(<8KPN^ZznVeI0^_9E3hkYLyk29S6fOv0woZY8$i{<2GIG!6B`5^ znFWd!gd7D*90f`pnM;(|9KkoBXfQD-2`R8SPB=OhdFeO@B3mJ!@kxPPJM!hM2fJjA~oP!m(9KaA(CG?~V%$Obx1 z0h%l|m?khPu{dggrdt&`K#5BQbWWHC69c$a4mp-<`ob!1k?9Vs+-&lo>nCx0pLv=N zkIeL*cwVmQAL4k~quxF05O5SiO(P3LK*wxD(+E5@H9zhUaFh`!2AyOGNlm<1gJ%&) z9hTtH1lcOA0JRWXh%6Av5=fuEKb}`g0csgs6_)}B#7=PA8=6jpp;kdufg97V(-V_; zr5RbK7p&yfoX(!i%Ril+o10hIQ3Z6$6cYm|vw^xLY&?u`C7PffL^7`vWAF5mWZvt1 zdJ622&0L_4?fdDGDZDO>$EFvSaEndflESON_-y*s6ka`!wYM4s9JK_Br^kQc6ravJ zhnsV{b}FwoOgwdZT`I2&?}JT~1su14dXx$R52v3`3nIt zVpd1lmMd`xEQR-;*c_j1nha9I>IiC=tzuK)09_ahZs;8V^*;`XWbrU^J3iSoJuZz` zg9B>*$?1J*yy8qPN2f1O`bsDb=$EyD=0**WaSErk&^V-X<`_m-g zs3`Cd-0FBDqQuS%+Uw4hrO4&@X4CY^>AZ<@P$ehXU}x2_>ND;TQDS%HWdx}a%HZwi z=zQBG;HV_F`^EXVAwW&uY*fveM(XY$(1-hb01 z;HU|;{SU}?R_IBhj_)>2XU^iyuz@N;v3-Rg#P)ZaCJQLAJMLw4<7JWpo#y-j6zZQq zqC(P+pEgZjp2eHT`voNU;XBlUO4+=19Nz@86QpG3!FIgAvXB*515q~R$j@MJn~0oVoF1-wnz4AEzCWDvMMU9gbXNEXQ$bC5AFV8%Ey3Ot>j zPzW}tu8>!a{VCY9Po_^i0KPr%a3Qa|1GK6IHGVii6@37R2oV5{X@RORibk{+7V#QE zo1rYA4G1RF16OlvPe1>KmwkF4FAwMRzsI;arz;op8Zhpd9#YI}#<*j8cQLO5~fEP>oY=x^pQw zQX@*iO`{d1ys}cTrqKdHP|x;&Ah=dJAPDL+-Y?}f7CXuT3cxL(K{Kf9H;4*Mn?BKw zM{K%X880u>S&r!&KwZ+rGH{o)y$o#j^)g;LH<;Nsgv^+J2!R?}LRm_nzLS&!t7C^S zs42`Qu!h@>vCRss5{U@ae6^HuPDzdP!0q2C)fn8 zPoG%ME5dku`m%Cfd&bq%pOy2PGrpKESHUaBcxt+71+OZsBbiXaE5~?odVK}26=U!8 zT@@(p_?KLe)}3!9Y7<-^)C4z}KDUxLf^o<6Z9?wRr8%G)Qm6l|<_%_?I6bh2 zS43hqI0xN;6oIUcpbjFV0*AnX>9sYy-eQfQHsc4#@Q33Aku1>N83Grl->c!(U_3Q_ z!WS;l=`yvv{EQQ)>(uh z=k$Yhyut=uJ39m%)dh+bOcmG#rZ6jjhgTZV{ks{Yicz4L$zi&8HE;a%y|uhtYTzW= zA)4g~wF1fiaGM0~P2X9^D=i7N2Cl?Z0qhWgsng%q@k(<*%}<>!P|sVh2p?Jm?Tdq4 z;t4+Wlm(Jq*|?`C-sh6#-@uF?@z zr8uBAq)xYQFGP$dBbI)%Hg5H>WI)ab-Hc`uQW_o>hypPUUSB4(>pqNgBjmUzth1h z&p2`VpAKFnA#kofAOJeg6kL=D%$RP_$*abAdU|*#uPNiH>Aju2Ixr7hT*oak{cfeH<8$i80Rs|L_rU{_3e1cGxX}o%kk=FWYr%)!XJ?AoX*?)mC15HifDD)*n&tR@MuUJOlO9YQtP~=KBJ>{= z8wwyHP38^!(wgolyk>U>;QJ5;=Q3;JR zOcn50h58Otg&=xVrcUqf<(1-qMqujnwICie-cqNZ2l1fMl{)=nFRxTRy3wGi*AIeO z0&ls%opW9!FQG>brgP9^2~!1ngfXU0@9yK3;($h0>h!fB9yC5ur=LgReFX8K5tBMy zxSzK~7%`mz8P8`CI51sSo=1B6)_z`jzIR-Z29W}bf~dgk>HqKYig9lM7Y9FtK%-pX z5pIbIyf(5AV5iUVg4#J7gtHXD!&3sFvB@dZ3n%bqGfta+cLKOOpfHiw$UqcajDh>- z4%yHFAfy&Dct!!#Kz0BTE&`F$nxSBdjImlRXI0*k=K=^G~TN|_!5wP7|u zN6}dX&af(gF5CcxfdHhj2WoUSf!ZFRu{O{+E{njK>HjC8HompNW5ClZC-WLWdK40< zlkYlAAE5K@VpG6vUF|8n=}i61)6v%fNKHRKg_j?s8a$l)cM7V{G(inL@GL$5^t7qG zB2Xhlg}{T53XG0_&Q2Ckc+M!WoD($iD>Z%ZR9-=*kI-i0+o`CgY2b79>}kAuAf4dV z0xzb4UHx|&Z#rZD^wjCRN{s!}JErr>!5iv7gp@eUm^y?ZHLEbFp?+XGFZ@Ufo<29Sk zKbzN-aoTj(*}Te3pSY$AxbcWgubU0-fh?KL8wBa#8iw&o@qk8lK`ST}SOkthdO>z` zP-90Gl!~RNSI*(p2QP9|L5}NGwzt4KaW?9v48rsdAw@OwgUU7@1Mup zAbJv1w1MVAK*P|Wdg~yAz?A7B^LfiaQ_k~wH6*4$W(fI}SU_XCpxL+y0^pH(?ghLi zTwk~pSrkkalm)t{doJL$WLmIoddmV{Kc5-k^(%LTz}H)Y*R2Q~=HOumb+Ew;6u?V7 zSRHQ&WjTI13?AR$01W{O90a#Z|DfobF1L_Zk#W*=hlRX3BD=r}wg@8|JpI5zUR_;K zT?QIt`wCGZqQn9^H6<8cq6xal=O!rvCtI0TJ zdeI_Y9og?33apyU3j{%zj%b2>$OIa{KS{?#z=q%>p%x@ zv4D0XusL#MEAlCD3EbS?y_A=kk#Xzv{$;!&jEAN_S;ngbYodYkxxgA;P(HU>&TC`v z<5CNxT?86BgSKEm@cJvu*>c_j1*k4~=0?*D9x0VphG>B( z0uPC+U=E2RJ4j&-uO7!qkS;yY5|v3@qSN25;0=QbrB3%=$y?4iZTjJryy}cP(_gRT zRb<>cop%*)2;U(VP^APKeP-2QQked68LueJ%U!E@bs3*cw_47tGX2gfUI~NmGn#RF znFGnozd`C$U|xpmg7}7)K>?eWrBtALAgXZr8R{aicTa;X(S~{VodYlT^dBpDWv7cd z@+u1>IR>f@90sw|)zGQVnvQIad#mhE*#ado( zm}Kho-)q5(oj`>m(=pFvV)r(jNH@L-)E8Fh4k#1pm_?sdg8}AUQxzV z)A`o(7BTitZ(I)^-21s6ynH}n18E}1_8nKEhun4T5&bE6&pVUruT2-m731JnKx7xst*#Syz(Hc z;o7H8uiVTl%>mV&I(^P&-V(-X(*?KiI(c7a0wu{UqKaZ5dV?tFo;OhKtIKdw6nv7p zm;&fx>;LwJ-jKV<;Cwe|Hyymvj9=h8*YtT?5LNZbExd7z&!%f^<<+Wx^|M0&9O?>O zj)xiCl-Q+}K$n)Xlqm5zLNoINkt`){aEFA=j0xVs**v{Lz>!m+*l{}C1YT(+bw^G| z1_fSe*hCsgpO^wW=rT}lff~rf9K17hXnF&yfFo#?(ek2^#aRi`uW;#G1I z^XFw$;8KvvQep>9L`pjzVE|oKCauB5z^Wk4?Pv#Tw6Hqbn=!$iUU~nC%nB@^GyK2{2L*P3u7u)c z1(o@HpjD474hkZ!3JQ+wg^sMC8|@(L3k5E+f+Qh{2sE_E=y>>Vi-04Sz}4w#yLr_Z zuTAgW&8xvbVMBv}BY5UTBun5A=kz1Hc@-JYOnm*1&yt&5d~kvp);o;cpSPb|Zo1$haBEiW5U&?w&-B7W;Q5vQL%j8jJErp=29Imo z9p;q*4I&=qHDSCseZpbh44GeG|AH$7a3*RHRRp!?z@a}~H02SWTCtl{2;F!D%dg;-H2|SY1+t2g5Pv^bDD(uH0&+|&dw5Cp9aFo|Xrk4ejtZoQ^XYN1;XDM(9m~bm_gT{2He>lq9 z#W-zx^)X&!m}|Ej<2B-gj3+WE#5yu%DVR_Ha*WrS={MJO!{fa2j2EW|9!GdC;3N*u zZ3THw8tl0h;Hm}W!YL5XT>{C0dRCezcndh7DpRK~Ji#lX0_#SpLMquCB4$h*K*i(+ z;Veg}zSQYAPk`%uzLUH*Fn8m0$Q6)fpm}swM^LYV)e*@dpFnaThrB+?TObQn2@P8n z(6ASH8UmzY>U6s+ywV&{4XM);uYg@};S}#9BS=aBFCF7TT{^}EN)Z!;vIJV#9GMHj z9U>6#;`Aq{d5zd^zHAV1JTzVA46h8#7j|cOjnUIe{Tc8S$=ox%7BMsTH-M`r1xVi; zT!Ml}ry-$rLIi1aa6L#ZXk?B!#0C}{&`vE#AAIx?YCbf$7(mxBLaYJ1N+5mOIbJC>sNoPr;3Wf!pqT;(P=g25 zaDc9~b~wkIA@c{EqmF<^m%%ye06%z#0VKqZoI@l!#`C=9Vh!6m1RNm=3^E7|iY90R zn+uWy#VaiLM9V^zLma{j$~XvJ(2N7sl{#JN0+x)!t-uD#I2_=NQ*?nhk8#@chZlIY zVNoY^kvEX(FF2D(Pj9%$%ffhZdh12RJecoVUZv>^PV$P19o^a?;0T&SV08qILZhU} z2Ov3+%hfLN7C1mvLLIFN8n*xsi-EifiyCYh1!@XBqi|nFWN?H#9)awV0%uXs5=j(y z`~%5>+_CX8Z-FdSCDa|DoPp2)%^6S)@SHL23hyx^NQ~ml8KANfkuyNNi_`nA@)~JC za>mhS0RHfgNJy)8RQWtF+=7MK|>GVq=+kn z`~s;ZB7-y>Xb^A&Wf0iPbI$3fuJMM-LhXWviXv#N2VByf5XlmlI^E_vue2xB3b+z@ zRs=f?y0VH_5s`c0xe{tAOy~5%mAo3$Ph98KR04NwI6);#hcIaV1ytj82xTdN#!F_1 zW(myVn(lakOJutG4bbxN?Xzz13NrG6HNhA6PZwCgrNGJvx)gBwy_>w!jMJt=d;a{l zc%|9*gEQhBSXJ(Mi#N&^R(FH5C#dRu0ht!nU^>7Ln*4!wXkLhbIyB(+^bhb%{s)mP zfkV?D+~SpByg2>mEku(aJWHCJ&M7$E`!=}i9(|kF!mQ_b2Y8(?sPF|RbX^qVKK;RM-e@_f0nl&*ITVu5LFPYJ z9bRL`1Jl>v;q_o#I{n8TUPZ=>)1~h68Yo=mTr02`GR_2AtpEyJ(9S0C7N7$H0_&$| z-Q{(Y0d)#XI!ntzX1oAK22)Azsw+2;3o)fjuH$K8j=$rM;$4Vq7@={zG0R@Lng-+NL$rxgLSnDH}dQ z9h~L`tqf6|KH(8>F5jQClLZ{_eP|MJR0bJ$?g_7iAb0@>WbhumJbA}-lgHoz-tfn~ z8jO3UH$3K5;y4T1v!pGsclxTwylRZ1(=R^e6=l3M{W*wtX1d`cUb*QyPrz+A$0xk? zSnScz+5v7d!*;EK$M{4Qo-;arcnoTBLK>i;b!V4YAv?KDp7IJZo`DSUr#uClUHOzZ zn6ZEQ*{9&4fj3WiV>Lc;K<T6uNmJi zHYK*TP7I*II0k_Y)2}?^wPQRrUHCb#wo4;7sF8LAwC4e|-Ws&k0(AGIB0K2PXz(mO zbS*b1$G>2Nrc7|#6O{Ww^B|(ro1XJ(vx+M43#^~M={awl&|y$djvI87BTtqBmjau> z;ps{*c=ctDu`6&ZK(=Jf5C-)RSREG#XDRS1unROz&wasL2##IQ6vT$<&tLFHi6bUm zz?O-C_M?DoiFnCt1s+>u1o??EZ2H`nyb_Fur*C`7>%rJC{r^i|Yo=!I>5b31M5YJ4 z;+11;nV$cOw^SdT9^hdF^7#qS1UzWm7nJ2SnSTg4F@OsE1Hw+AkP?2)tEdCXj88y~ z1CYWO{2*7bYA`+ESK?l)^ny|00HZ<(Wc}Nv=?SlSrR3j$*7SikV9WrGs(^b)Y>@6N zNT7B4%-6hTjEkpVc+IQEczpWz*SyAzC#P$_;ZCBeuaT0dBZI(kc98p)fXV=n zwJX3~2=KVBK$gIn>5Jd+hBLNJ|NVwnhVkNbiMPC(u;zgKTV7qx%L4g&N(?Kl>P;tZN$l#tOuzvcNcf7&i5vSwRecto>u!|~iDDVgzo<9FQZy@7? z=^x+o7BcRdp8SDVgYnAro)6&Bb>@$}u128PQFzX%@S3qk0JNH1pK%IkO$K-^*#sd) z(5%!2(1r?dQU_1_Ot1RLtH#(qec?x51I7>2FMZ^-X1p<-?-Q>sC)ltTi~`H2yL{qR zVr-wD{)tzG@znI5PrTalH&|htCD^zXq7_+qz|Cb)%R*FvTVVb4YoB-nAtrz(m!=zh z=G9^RFg^A&uN!0Y^d+BpO&B|--~7z0z%Hu51@hapFT6U8r>5(E;k5@1T7TiKX6&E- z@(ZszWB+u)ue_>~6PXow6nHe41eEy9m?RW<92G$IC!4^;>Hc4NRqa24dW(#Ot_nPk z8d(CLIF!UeLk>ltp$Zz*}CxW_;i@WBS3VzyrG0L;*CzF>(6lue>JwyV#WY z6nGpvxUvLxu_=j6|Mrzvm+{o}27W%(>7L(sc^O5fhkxUhV_ZDF>>ICqy|jRo0*^l9 z0WKvLUM7&+_HZf6f#@Avii!$6j-bm^gcYO()-!{YU4Sb)!=)$%Qg(t%Q4DnX60?H> zivph;ivyDb=z#YUMJ`7rMOBatNYxT9MGdeD5ztL1JdQ88vLJ2@R017(e2iUzOF=OU zr1Aw9s6^m#{J@n3zE!D-TY*bK3@r75OGz9gDeib0JSZd%9uyMi1`X1&I5;vY@;fRk zs!mV+&Z`!RX6g;7sShwseE>1lkx_wP0h`H6;?j!z3f!Q9nduL{^UCN*f!x5OAZEtY zz^%ZeAZNz(hf9G+LD7uq2N%dw+*u033OtTKxUvK`OxO6qtIVh|-RB3dl&-23!vfHf z7bXQB#|hv7n8BST@BkF);2{E#_zJLYX@T|AC;#BJXB3%!`3J8JW5aZTpS&TA4bxM9 z@>(-CPha?xH=40!`rn_t+oapL6?j2WGlv^~@i!M%~3Zuf^*oUwa)*dN|*$!{DWy zo zwVY2$QzT1C%$LPMfyZ$HD<~*;Q^lB`$#)1s(-C zkmPYtaeaf4uR;oH9K0#X4@o!zKUk+{G4V-BLCu28iy-9ZG4V-p!i-CuevpZ8D&w^2 z;mmw$E|A$@a81Ocz~d;9CD6+4$X{s1uz^*9$8iHIc%?bG?qY`&L{JUxyo{jI3sf|< zPCv-Zr(6$}0M%(cjt5w?loY|O1Rl^0iW~yRITRE@Y2gH`lA#f#FE0ldka;Nf zzhKQ$(tz53fgwwQ4deg~kOMxjDoH~fAPsT=JIDb)zz$#&H~@A)1DldG*a1ISv$&O{ z;SRXKkfq21Y6I~oNKe1W!Y9mla{5yiK1J2l-~>2>O^E}fO@nCy8>@mMw;9s{HYNvH zD4m?H&B~|5xO%!DE1v?Nh=Q1cur31&w0f*z<&&ugjoC65LJHCYY)XoXY@iWwW(UyV zgaV%ezZ;8#BZGpN0)IAWdy#^ex3nU+f}#RHXiQd-O#xIrDDs1}fR-C;<>G6TJb+ALVO<%~yXX}ViK80OL9HLwt6s$blVE?f=C@4a%{Q)0` zZ^py`3hoW;SqdTwiVD&Kho{T4^EohfOiy6vlVLnHy_TI%hYd8OAO@aaR;fSVE%dHD1g+ovDk;j>`;F#RVFpN%rSG2+V00CG2YB_uN_>{f7s z!cI(J{qzW4K38q1YdxjcD!gEfPE z+%sLDkIx6m5iNXtri>q^@8aVFm5Oiq_&nsAK{oSfGQZ$7XJP=&?ejQZ;0D!aVhU0M zho=Yg^MM*T4g7q`jJ?yJ@biT-_Dr`D;FHyYtlk74bix5@4Ki3UoC38{LCaGhy-UdG zgut%pjRJg1j0dJK5#TdnJT?8M0G}M=l<7YN`1IK(GApnN?3k`A$S2FE1Uf`W33Or! zx8sQ=(<21=>=<>XPZ#8qXZx~wvVg#;>3anEeiMD@#*?ObbvM!fSkQSJWGXvpWAWj z#wO7E7d-}UK?ViztcxR)Ba0)8z)}(J8+J@;^~`n342}f? z9wWk+sI-kk;24tGwtWOc}eTi;D6&OY&$i zsVFg+F)a~NU{YXKV8}LOS^;A6OfMAWv(N!==L0W4TOg#sH}doey89*8{HaIlHOs!Sm58d(CDm_P?`Feor+ zGO;LXDlj?zV8~JsRA2yAixa1>7vmFSd^Y`<7@r}>jP^zWM}2|f>Ho#}QiY&ANQDKu zRBP(=GI72>#%a@8CHP#Vk1;7RYA}g_oje2NEDdw!35+18Z=0SX!KcEwetM?_pAX~4 z>DMIqq8TquH8ri)7Psk3qCDGDfXPj`~y zGZW?Z&r)JgV9Zuz23-NK06J%a+jDxa6rW>#KZwJuz~IQ_$SeTousE_JIBbsW2o8fI z6N1Bts)r3B!-}kj5m^tbBa$+PdPf$7CT3JkNKRlv*2IXa2gx`lR2d`#n87ljgEd$b zm>e%KW+^c%Fge~3oPI`{j{^}15M>H1pd{sp>QZDsFe3*K3$oi792pV*K?y|^FED_e z1GR_I1KBtR}A(2P)GEI0D5g zxPiFLW8~&m5Ow76FlJvMVq`4VM6! zuE@dU07>l%ypBvI3anX5Jk$Tn@rg0^O&62rQx`tWsKD&l06O^we50QNqrl01@~v>E%RKUCyXcALP>!^En1RNPl z6a^H-92rZL#1yzad6_^$MIa#s?&)4ieA=#{IO2fE=LAq)!+|X#n8C5hxYkLbO#u{_ zGlUeF!EwT@!0kDGmlB^E$aPA5!i*EAe^ugBkbwj)FQ_WL0VmHBKKd8Tht<}>TyhdN&lr-FzhBM5s>->c5&$2es= zj|QI<d|IAI5e7C3rX6h)XE z6c`k^K!>U*E2wyac3LTlD99>tD6qTnf{%jX0&PH76q&B6$(Jb#jwBWb1%3q)1&(Y5 zb_I@Xfr-oHqTp7N4aU*hpzbkT%esAJEvdu)x&mUfO*5jFYDC(&n>coId@hHlHEmjOp4s ze9FoY8(0;X75Ef{1*UR?bV({OD+p&PaVjumD=-L5onENJ2U;u9r^6S+IC1)G9X?~m zsng|k`CR!xLj~DN433Nn433Q6(~ETZCW|QWDR5>haOA;-rImQ5OX~5-i|~NTXK;z( z$dV1p?rZ{m(|z>#Jou+^D={eWXR|6Waw~8NOq{-0k1t3>ffhssRx9;r^_1h zc?nGBR$_Ey%vNGn-~xry#OdXRd^+Obw88*MD`KDwU!uUE!0kVMry-vjJ{!ha(|OJLQbgB)HZ?GqF$pLzf*S9PjsjT%N2a%#^A$?X;|3kW!U1Y-v*Z280(d!|qL&E`CPmn~l$EC!_!|n@SSDpVNqb6em{j( zlCfd>6-Pcd#z)hoo%r0DdRV5v^WxjicyPLmGoKUVq3Q9?d`*mpL6p+;J|8}@=~6Cy z(Tqo?m$~qnx;}V6S-|nl`^f?VQ$@IUFfnn~GlIHG0@+H;Dh#0Mkr!NwERNt+)}Wyh zM#nc?pjGamM9u+SR0`P3L04^Kbp!MBca(KIf0C&pXTw|nxbGv1nh-;>XZ@$hshFTN0_MJ&@9 z1tcV=&-YauPe1L;m&x>%ZMvBs-!jH2)9?E68Au&tQeqYO z$)>;pnpa@ZVB&DR!jPrFB=C!Ey1GA~A$PnY1ISIR0>9a&C;Rhhi$DdyN?$O6l>T9x zzQ~_XmFYX%^ppO4&P;#Vrt<~xNk}i81m3$B4|5j-=wf@v3k+ETlczfd@ELG}8<-TB1zMQEIY@y);2+!codJA`9FuRgg4PVjPk$7^XTx z>i|Bb=~jV!S}cE|T*c{~fqbfr4b#^L@`W?LoX#G^XT&&nyHyZhEFq1LA@*{1x8IK7IS6+u%tki5~Dx^JJ@8FB7uhK z+d}zV7#pVlhENV+e4v5;+Auz4xl7z&eI8(aCm7H*?hE6SlDx|Xx)>W&b1Hyc5TU^2 zcmurt^J5ra24nN|sBk`a#+K=e!ubLiH%#Ac$}a~Bs0Jp`E$d94(-R}*L>S|z8%FSn z>!vX%Ffi4Fioy~{iBd-fKNbha1?MIUXfS~j45Q-%#zMyfjG*DjCFiCWMDU5quYgNC zu7NNe4}pUA2VQ z7HEz%LEz}LSUy9Jcc8BLy*=1Buf@(eR^a`T_tK$byf@KA_H$W?JLAgVLRbc+~ zef8{4j2+YW#_}aGo|>)@2d=4BG_X$u9iQ6B4yxbu0aj2+V*6ZjORCW4lX zLdv`yOj(W{6Q>A(@;-;aLqxEGYhCq$mj@K?W({m(7~?A%EJp99%0g8 zVlZQB0G%7(z>+1flMS*00W>(q;s`ou1fdvoJS(U%35r$*1tx*M>G4T?@{E(G*C&BP z;%^H(XuRfa5}z{Tl<9)WV8IEk>?*of*%ZJ&zrdozWX5y@WZ4atECCdk{Qzx_0$Dcw zVJo{GW8ZXzHg;vksndTz%+XHavu5m@o}L0WygLPK_`VcA9hF8lP~HO#l?imP!vc{D zl-XGvEkFZ!AeZu`^2sw!o357%)|Z&dCx=LS*V`dU?{_=9rp*;LbLJncpmH4?Ff%~K zZUbn9-Ejt6mcS}zkVhOl*s?%#0tzgSCm29BusAMY0}Vtbbg(NkPM_Y=!EPiCN=)pq z5LRL}V>$pb>Hu4oK+E*TG(H`s4)$rC>={fKo=nf`WH)2nHGN?xdoN?}bp0;&4y8Zy4f8V`=;OSW(P&W z|890r8hwz-C&D;ux}8h} zoXHLzPykf~pt4G!Z@PCcyC&n@>5AEW>WuTI&+P>pd$E^YYWlQnK5fSM>FX@{Ris{n z(jT8dyb?1cDMLzH1qOl1({lKvIDUhq83p2}E9LNMGhUqTm&2!`x(SrxTzMI!6qrDH z1>AT8kGz71QW*v2urC)lI(>Q$-#3n1OTY(t#7`IC5S8WF0OIiq#7~#@l9l0jvmAT? zR{V5(FIicRdmx*51mdSRa)>H$L*@_^7(q34zn82!=P@Qw;VSTzZTfyx?t4_Oytk}^ z&>qm8NuXK+p)1f^R#x{2s37A7Ro)QKD)A_CD}awyW-wz)P+)d^z>wv5cYPaZGeP|H z+1|3U94|mTkh30eipuI9XlVwWQ46np<$2@~22?08gN9!nUxIAm6o{X$!zC)mu@Iz1 zP9T1|fsd>l$0d-Qm_YpWDv;dm>kR^q!UFNr7lL@#K~}MXirE4_IgaKHZJ=`j;-|j{ z$$bUMfkN(`kF1H>r*+^1!Qjpi2Mzo(fsa-Oja$P)s*qb$nxp4w1L%OK`03fcvWkqC zr}z5GYIB?b8Ne(MKmCBOtS3kRKWG^w=qIbjaq(9Nqk)KAug z<0fb~D!V}Z^o@S9;-Iqsl%K2>Lca~Ks0_#AF7U|(@zWDQytgp3+jvFwrEh_FY={UD zS71|McHF=OYP?+J6_w<83{n6p4)^=Zs&Om_8O0?KKmD!0tc=JWQ2lg(DNBJF)Rbe^ zV3HAtpDr69E6oA7#34Y|NCDnA+EBpj{*ryi+$x3sqUJLcwWsoiVL2W%GCkcXWS%G4U zAet?QG4wDyE7%CgeaUEnTD3$*Ul|?euI!x9Mkvkg0WHlM%r>_o^m6q#A ziwyw40AW$%EK5gWK15C9bhB8YszGCkHsRze7TX`Tj?j{u?q z5RZ{n6gr4mF?hwuDl&FW&xw&;qOcK^ zvFwu;wN+dV;vsS(L=R}Dp4qX1JxgE)=X6;aQALjRAY~v;?lPiQ9J}tfLRx)YGNN)E zZ6F?~oxD*-REc9RhzBZ3?tplAK#>U2w=hmNkm&^X^e9I0ie{9FDI&jMLmlGv*QVl zEP=_a)4dX8We{a`r2+?eQCW^7 zAdMhveh zJ+zhYD&w^2+HHKsj2EZJxAA2#PM?0FjjssQQ|#nZnjYKEr^YyK`os=C+35@0`Jx!7 zPyf@-=Zz^{$~b*GdnaEgM8<=0+Vo|ee0hx1r;B#+fzGr_>*9-NI>I(RU@D)=^w(W{ zIzlT!XSOjrT4Xu){)e_~E>7hWnQq(7$IJU<>0|*%CP&cZ)(58P7pL-xaa&|LE&wS6 z#WhHwBxw4>APc-`!0`iC^^Kq~0Hu_RQ~5-v-|ps9Wt=pfv4>BVan*GB9zI)vpo%@3!OW^eM{7vd?rGVK?6O^jv85xn?aEV zYB1FI@hK~JutOch;-J6`8XQstb(O#huY*!H$nQt{_(G*%4u|BQ2Vh6sV9XLY%{^Uk z8K3lY|9(DpjtLu}4VAclK6e>#Uj)3&krmW|0~-o&${g(HQ{tJj6B_6Y0w1Qo@8@$t zR1ww__@od`mDmY<3fgCmP8M)v0QE>%z_mWaP>w7Cgqu%-f(qp36%+X685d1IF##4r zqSLu2@~JY;m~J?cFOu=>_U?&%QjCm!)0a&G_kK=I;>!|T2MYKL3|Wo~LD2}RU>w%+ ziEuN68||zLpmYxs-~$Ud?wZ;OIxi}Iy2DyN(dn}%^YQY&I1Kd5@hM86CKynsj@i*6Tj2EcMbr397}rg|K8;UN5>?Ry29P5` zLo=tR3r^>=u-V3;$O>wbFoF(UWYl2dP+|dZDPk2k#^iX10X%lb3~Kr@WPw*|u!E+3 z9Um}ef%+k>)A{_umVwGuQ0WgzGQ13+`Wu{hUod6~{AGitevmkLaK(&i2NS5^Wy*3~ zg`pMX7Lc~d(=})CwF*J=r7_5bOrWB4(ewi|!0GMX3~>Bt&g2VWJUhK^CZ9j!_37tk z@@X;lP5(I)oZw_<@ul!wWm8}V6}$yzOcm4nrtpa~30$APY!;u1!UtBc&nGZ~%>BR$ z4s}rH2c9y2&Ek_&h6Nuu^Ezb1G6G29Y*5JsDv7LT^J(k9Ku(xYKO;0Tg9;6hCU`QP zI-5^P{0&B;8h0>eIbu_QFcn<>qnIi-hfm1>n^Pb;8Px;u^q(;YoRvG~ zfP;419B|NnoC8k!j&u3EboVeTvVcY@Ih0tzse=VN1bK`}2{aU`2x==yfL7+Q3Vdgq zzGE(*ALGaA?DP2KV%~x}7yJUSU<9QC@bCjTrJm!41{6qG0aSXKG0gx~0W%o09RGuK zf*O91bma=#?G92R0NNnNuD~X6c6#GHK3m54>HFvLiPysu8Wx4%fk|c!CLK^;7gRz+ z)0I5bdT@wpWFbt3XaMzv!LAZm#Hhfm&zJ!|)CScJ5S1EC(?jR;2{E3To-v0S%@WDuj_kZ|G!tz`fgp8_+cH!kE0;{h)sgj9srraxZD=P7@F zVHfCp6qHDTRC*1JSpw&$doSXX<+ub=1S;qY7V#-EE}A}R5jb@mSOiWTzZdbvF`k_s zwU{rJsdeh~gNyk<7jAu6%xBJcX}ZP|K5fQ`=^y&}M5bpf;WK0eS3RQB*Dv8?h1Iq4 zAZeNDHlw=MtW;wnFDMch1@Vqa0L_^^C^tVg-WH{b~6o8sqg3I_c7#B^qS_bx0#xg!D zt{KdrIG)3(#4^2L8K2nnL(BLa7|%}cp2(*2gw>Qrrue8JX%GSrnNa z4@@sy&KFt#ds?f2A`_1ox8waQ%^;R2w+a0Bex>6 z;|GwL^-Mgx+>V@z%#KGEf*r!c?Ktxq*lKQwL$*Bt^SHPjxfGclPaFmFI3aSco`VhK z;C8%t8Z6EZal~1ODmHFMCPik))2G04tPm&dpV0zxE(=7Z?&DivE;Gcyn-CroMCrN> zV4pEU-24B2^Yn$A7=@>EtmIQ>d^TNYC10vUF{pfI01+&Z)-emHH6TzjedS8N0&VCN zDkzJBW>Y}D7i3ki68 zcQv0QW8ZZD)!>T4Vk4iPFr%YQmJ&B;xgV2_0=Rx&IDN}%zNw5CrhBd7^Jcs>ecBp6 zXTA@t3LL(m;tEuQ&zk;x4PPMhuBn~V1-9~uO%Ghl*THyU`mMEmEsU3@$FJinWocm( zI5PdlIzC0li0Qx9@mVTE)O#irrPh|SM z^?bsP_ZM}6hCX3M7HF+ANP~bPXek$n<=6?*!Ylw&0VyvxfX5UJz-`&}e4vXwq&I*w zaM%WL@&0ZrpZ@f#8~8wD*NET<{}I4+v5v5C)vasBkdO?)DZ5z`NB0yW+?@k#JJID=@sP2azXPl^pv z5QDwx4Xz^%vK*&_T78^I{%~Yc>HTlnOdPA#4uwuR4+Z8a#Egg{nGOh2@RPfQf# z1&{~cFhMFwgDl5`3#Y%`!l%I0xnMf~Rz6k6Mbk~Uf=i0zt>D0$w-pw6K8)Sdt+(-c zFkYPAvW-tgyoU|cssPoM44_?A;MpW5fe+L7Z{yQv+%*0DHoj=4^W4+r1(~G~z39B{ zd>NwuVFMjZ;HEb(hXS*}@#(L(^QlVw14)6JSWqdDPeDD~=^8uuR3#>ZY6(!O2bBdi z9|f`mu1-(g!KbRzhb9YZVM$~OT;)~--)aV$G6Bt|I5G(QV4J>e2cMjIC%Y2VBxdj& z1!zy~4>m<6a6$u}0K;I$qyd%zRfXX5;T)L-u1)9O$*0PAZo1)4J_W}3=|MaBJQ$m% z&)&%=$9Q4-uAO{y8DCD1*u|$R3H8lKCYUuK?+9cGyqZ1@Bn|ZrLK@^Bkc+2Z*~O@8**i{t7n^>S}=((`|S2NpXPAuXAJ;_%c0yH=i2go9S)4`4kyHPG7N` zubr`dy1^d4K*mkeoA&VOgC_8pL9=q8dLTpK==1}7__T%JbHkho>i&cJjL)b4+r#I` z_-?wxUOrjIGt=Yt@~Laj0i{d^Go}rox@ZGqmSYPf+!5XYX`jHDCGcVThP`~CL*Os& z<&);@0gZ7p34COl&i7Izm+|BDb43M>weQ>Slqlo#UwPYyE( zJe&S~AD9Ie-E{mfDKM^@ z{{AqZ2Gd{e=^NgNIB>oIwK$ms{%}wK`9>s`^Wa(V2-P3%>G5wxq&O#h1)aeQl6`%I zPm1%zsbhR67nI8VYA#2n-EGg@$LvH)l`DMOaP zKX|i@QegDr8w*AgAK7K~V3DfVM;FF%teuD2Rgq_2< zV!GE!K5fR8)5}isIWc~izVjrXGUL1HcTe(JFg8yYJH=9u)@0@oSTkMb9G^N= zOaLSyA+Tn8;5oj2#%0?dp5tp_WL!Hv?E;?*R0#t}35UR1EZWvie|v#%BIEMu56|(5 zPM>m-Pl;)@@bnF5`NSC4O+SB;&qJh{M}ZMk^)rHOUMCFdr!!99cY#k4WH_THvj9k1 zLSWtYs7rhYnHkqlm$}BL!80Fp!nCb`9s{@I{OJML_;iIqJ^*bbW71>}0J$eZVEyz7 z*Z3?z7BGRk2~3*I1t6IUf%VhxT;tPb+&!J+I$tiElR=x4LHqWy>wI>MOi!0gH{35P zGX4E~5i_QzOQsq~Nlth8AmR_n*GvM>rWf7h^M@<@mC4UF{ozeMRgehBExuHQy3$*G zK_F4EaqIF}xu*O4g)_M5yb&%~yfoao^$7RXWC`$f3XnEyo#{92D3D&cIdx zaDaAsT$mnphfk4l@${NIe6dXbS*PE=!>7UYm1%naM-gd}udwzDFB2$BDliGGW}m+A zqlhGD7wEihE>KZ+|D%W+1*9*+<_IbWuT9sw$7e6~ z5y?C@#~qAW3T%!u7_$U!Fi+?HETYWxk!iaAXAv<}oq?Z4>_IXUKZ}Sl-C&-+5~}j- zXAvpJ*6A-ki)b@Wp5A|-PeN)dWWWqz4I|9i(+}R~lVkik{lR@cWh8Gw$}@I>7p&9g zei2awdHlc^5vl1R5BQWpq4EEVhy+ME8@Ox+Ewoi&RA2^$g7#MtX~wV94?f^4XY883 z|EowDggZQ7-v4JT|;M(-e?;_gMc^~s}Gd4_@e9UJBs{Nqu;Rkz< zSzz__w8wmMAX5T=h{!ML!_Fqak|12zIMiq(^ovJe)RD!Y#^iBWxQ$#i5S>hY%=J??Gr4ZCR6NlK1mMfoZrXk zbfIKbU!CV^|ybN`5#F|L_@|2f}$#)H%AU+~E?-kCo41)m*b)AWUZMPxW&)=hiK zC(C$fy4*`X8P28qT0v9kADO1J{S%RAJTyJ|C0`Ze^Xd0q@|iF_V4OZ%nO}Cg;VZsW z#*Ndbzv7GF0?*rkvSHu!pRf3Q7*9-hd(BtK_-y))*L)s~o2N6r;nQW@GF|@-pD5$z z>8@}13@pJ~lt6owm<85xf!6%!GKjF$DS+t?_!QoE+ld&}3$IDfj%JH86ZCQxQ) z1SMgV3jGZ?q(W~4NrPhN-a9@omO1Q-ywelz@<}KOG_WbKfmRYRm@yS7uz^}z0%zD2 zITfIXg`7A%J?%Z8s`?yuMIOh#Ws?OI*%i11z^g4-92D3b8^Gg~Y@kI33Ty%m(>J~6 zv*SO?tpF}k6xj6{83c|_|NoxPPHF~d^pqXsK+qYS%!Ry=1veZ5OQ(l_0O$L%4}5M+ zzqzOH`@kp5_;vc74}7+ud<0bvX+<&#te!sc6QA&O`;UCwj9;gFf8;E3!RSmC*p9d_&klL8yG+t9(Jz~iuh+f1t|4Bg5WTXy)0*C`CfFSaq z01EufCp>-DXRwc#eTIb6yU%>mQd2>CKt(*v(WrWOet=y5gDJ~VL*VOlr!NqlU0?X5 zIXU62Ee4yJ;GQRR9FrJ!z@++Sz%gv+SFBoMk?HGfj z6)--ZuK$nk8RP5e|BuT`bAh%!Dlj^V3A~tY{hzO#@#XZ*|M`L#UruG?_hfuIHGp4< z{Rum0;3HzX<9|M}sr~#Wj4!7i;a6kqnfjXFm+24tbSp-FDW<>d)593~Tm~6ONe7fvxPO0e+OxW0_?|&`HKm9W!zXN0UbQ30iLpg{t4<2#QekoQ* z#w;@?1qJYEqXMHq_w;5aej~=0)3-74t1xy?zsoS@_K)dcZpF zfOLTDdjLLNfQXDIC1(JHhvk#zUeR7_#GMhr)#tGOEbQk?#|9{t@j!}{{fn^ zzX9^0BZI(mu*bm;;RJ6zW7c50!7T84`UZA>Wr?q_8ADzU(26!@aI1(>VAAy0?EK1% zucz~K@M|+ZnQqO&@5A_VdItx;0?3D}I6w(t`WFuVSZLs_VNzlN4I_Y77b!3byqTWP z$uFl33G_WMxdWg;5aRP~S15AB!xi zFTab}D|o=Og6C_%ao#^&nU7xulwUmg_#GHuPVeI5cV&Dx{Q@6SFuolEVP&A<^hX3o?;OAEu8{JxAYr&~$z`!l|s-X+0b zEV|{`WYF^BSv=rw4TxZI?3gYj$*<4&X1bFkzb500={b`8a*Q*kcT0jTI3>v+%lLA- zh7`XS18tfT1;Qqr!SJ>*I|4#{k#l+0OQN)3bJ5Bvt_}CE|LWs^In!e$N?55FBm~_0y-7I z@dpEFe;SkH1kf@Lfe+xKdkUKpD`$@9B1zMQ^Ao_`6`5BBL%3j9u>+&NEy-&E!iJ9H4nm6r|FcLxuPFgpHY z5ctVH{k;OegxWK(D`b?ILAzTNSOk8tgVkv;NhmTaFghA!DKQFsVV|z6$Zsn0n_YoL zkrlMVfCHr3kxSq!`}8tJesfSwvtE&35|ndJDe_ARzGGKp28|A|gVjk0yqo@2kzZa8 zEDZ950yCsO0jXgEErMng_|87vNQvK==`;KGbR~WxM#lZqXDRdhF+Q69QkmbI>CBnw zW-9y&AUZ;Y-;?pt^hGNC{t9ppf|rjoTQh<;4lp@tfRewcz>DdEs{AsHd#3BD^2;;6 znC_>_FD><#9a{Z@^)N#9FgRW~Grd`r--P82JCnmSdr@f)Mn_FY27$lq(;p-YiA?{b z%CE@ya=M5be<0IO_UY+r`~i$FrteVW4`zHYokyL27vqQN$JP0R8DCGA*5EH^d^CNs z27dzMlj&bH_>Jnn!bVy_bC#eQmBWn52UO%UItmKR<52|Ny$%vmfR4w4w?t3P*^Ik3j6}+bx>jf%?WETaVT;rKsPu{1(%E>Sd>m*s>v@X1SvUXkmRSI(ByY9 zh13&k7?qfKL1UU7;KC46nD#&gHJCaW6+!jd43MOwpumgij#~WILLb3Jt_re|eOmld z!btuFcQ*xou}|Nv#qZ1Xn|(T$HouP01h89OkTrT}^V@*r+qC)Bg>JDz2V>#hp1xn3 z|EA7wb_E7U25Ux8smp4{!~jl03XG0y0$6k$(c%9AGI5_SznCy0;21#>01ChbSod@;RTpWmAC!}QJi{3?vEr{B=$2kjB~1EQu)S1{nW2j$H~1AYz0H`BWe_zf7} zOy6z5587!YY{>5fFUT`M*&TEZIieuXH{@@Fmj?~tQU#QiAPQI@CH-;6m(y<;@%w-* zQ8VUOtiQ2qvVfz3gIBs|DY5D>FeovBH|Bs6CWD&-lcR!X5vbb_YFan5J2HY&u0A6JXoyIIiA9MC zw8C7Mfk}xa%b%CA9@Mza1`VsT7xIE;6`nPzXk~X~f*8gF z>Wu0$awstBGH^KxloWxklVB)xY-M!gWrpM#fh+}9fi`wWW{3_EUM2-beMSM04k56P zfB*k86gt+gIKbczxsU<8-;!0Jo!yZIqDKZaYsLWbCz~Bfv{vH(V44|8Fn(<39-kGjr#;;*|IL^5{1+D=c0 z(wZ%x^*^8;N{~H(p!?(%ShJNtTT56yr4>O3UO6y1I4-$6-QS#F3FOLm`HYg>OCiQ9 zunIIzpKi`C%Lf;5WGn+&Va_i+J)nS*o3Um3Lvwy9K_vMsh!P$PepwWA))g?yfh;&% zz$i66&w}5Y={L*t`>LX*(;ryyn}dcrFWz7jovvreuLt79S@KIUHczj#baH@)Czu|una*LwuY)V}rx#iAgNI=;Y+yE2V4kjN!*9;mJw4He--oeh z`Z^naNygskCv5lw82hG6+VX=AYp}BAk7K*Yq{Jq$c=|kB{?&|2roUY+tu?*Gj$b%@ z8@s>^4p6s(1(eVj%$QDq#%@>~PcUW)Y-isi(9OXk2-<@KI>N?`=>kOZ0%MlI4)(nQ zOL%w$5Rx}Qms+wof|d#Hntsm?>;!##enrN<>4EnAz5-j>L3<-X1Kli|%n|}ir?0c; zm#~3}DkyO(uxK)CC~+uoDsTwQ5eA(B;x4d^M}Yym<4*KS3B_QF)o|F%7I@;Y8`0LPzMvJ&kL61;Nb!7_G1uO zHvNMGzmn`S9?;MfcougBlL8BH2hiM}A4k9>mo~r3rU2S;2-@ZYvYi8TSSAZ-cfbXZc@|j;ERGkLvIJgmfqbvQWC0p+ zvJvQ-zS)sqnf)DTH0%e{^z+XAvf|5mcsM}8$!f-Q0kTc%0&|wYrRjoB{E3V`(~F(> zHH262D6lv_V9s)6DT3KJeVr4(He>hnTTc9-6EF1b`NgMOIrDQt!d-59vNOLV&*g6L zDZik3gf-JUocYxmyQXh&=C@&7IsL6OzrONHW(5|<1{TnPBajY%59p8xmLl--S5P4- z&@(ouxfgt3%`NDanJ^G7DtdcIKZcGbm2GP znhqMm0`aF#f9=A5h;hU8b*}ujj2ouEapm`9+%VnD4a_TXXWTG7 z#T_iT!JR*pal>>r4=@$u!Jo*uVfsD@Ps9_EyP+Z>phKw7g z+j#RAF>aW?9>RMF;f45sZJ*==mSgn=t2Flo^H%wS9R5W5+bWWi5Aq1mDT1RAFQiRT9`J$jhJFFt)kD!9bB7{@O+y)J=Y)^!F4Xa^`fa_&H* z15yzrfL5}CW@rR%uz`j_7(mVkF<6v9_A9VDf@WeEy{A7&;P+yjFkL&5UxBe}x=$j% zq!dyFWq{V_f)^3KV8{}1*@em)tH(JY|t z;VhtoPO=mkco?|Bts{1U#nY1crIlA9(#Q%%kl~J?BRJNu2`u4JfToh^cFFwGVn;wj z_z;m*JfPj~pa%Zp>G{e0l8jx`JCpf?q^5$-rDiFD>RSbNs|M2_kYBzg^Sd(kO}B=} zvB>no6n+h!85|%x^%>ik1ZGTMnu3-Pj1@q#x(uC6Q19w8bRn4COajZMho$m6L6Ray zjEYWwm&(taRDMN~bs+ac(>4nzZSa9|QyRZC*L60ii>FQxOyidpxr(F`+~5c0 zf`&AHNxoiCQO~TwbOm(0LihCLY5aPOQ>R}`<2PgMna-QeugweI-~&n;*i*L2^y}&T zg1q24Bd{t#Z0X;4dRqp+9^;hhTQm6e*d~KkfD24NXTvWx{eK3(31i=MlT27UM{as? zCcir4gbVAf^h4HHB&NL22RX>$3T|mDYgT+n^=vi~`d^3O<0< zc7vFIz|6bTZ)fwHGA^1fn8Pp6xOTc>4u2ZcI-cnt#05pBug&4llKA&VJ#n8g2 zz^=e##uNeCC?v3cx_&PIe({b+@Ci$HCkAuo7mNz*jt!5dv*z(fazA+jI!!`g1CJ8p z^t>j1>FF(b{Mw8gr*FvP_g31(1L}jYgI1y`useb}%MuD~0-Je2b5c{7KpooY8}s>1 z88=N2&gVC0+%mm8pI?%3>-44h{Em{FctGt6Xz!gJG)@6FLSWnU|M~oieA{_I-B{RC z9I!cJU@d%5EgTB`3Tz6T0z0Nx7Vw)e?wr1^fZqYM+N^+IhjG_*nL;oxwva!Iarg8S zh5Vv)uyra!FX_Z9&s3MNpZ zVCPm~2e+pWPj_kH4`R8+s>BCY;{{d&PB`q`3QP+88cYn(d)1@_?y!PdoeCV_!=QL! zTsAP56T)RtPylmT1@27OZ{*izIReTK59|0lEQ*|n(49W98JwS1H}eNT@)N>j4rr1P*fw3Hg^c$W0+JfJN zx%t6U5DH9=%#Iuav$l(L@h334HGo!ESO|d5;oDdp&#k zO^l9$T(5xGsA&P-`2=^b1Sq;>VCI6(XGxg8zL#Hu;{`|`$dvQF{CY;OKs-ByDdq~y zpu?14zE%L4q5?Bz0m#?W_51iGI99)c`q96S-z4bP3)GVm%)nb(;BL_XnW6(T1#~nq z+>d5xe*6hC3FNw~Aaf6b;zSGKI#ckfdAPX-AahM%=7JVB!Ob;AGxr_HB#^n${ro0I zd!Ka(I7%VRHBkUDd?6Zj=K7J*cOEaaQWZxTEQ6gWsp z$q+u>4sl8V$R{B%V?d)G@NhPSSPxJ8`$0y544yiX-^92blwy?;jx|tVb_8uMf+epA zkiju9gKvP{_h%x%1jkyCMvyTwllV>QZ-RK52xIgiXNbTT{}QVfkK^Q3crcTE)efH z1HxoY$jK{kA6I}(u7R06ff=;hykH8y1V;-jn@pI(ZxXr%+t!e})GX3%;=xaTK;jh+HGdIhr4YG_991{nr2T6-G5N$6To7M4R8 zt*XEbT8;`gdIs3&IdG#lARDcUX7o9bVIZToOyf5({S68pq@1Fn!0ZUxbq7lf3&2J% zfg8O8Wb|~|>HHEL%Rw~($ROM4{NnXjRyGJY%7T-qGCU13od6}l6UP# zUb1MRxgTVhBG}=w2%CR^Z2rNV<=FbILBLT9EG+|-u7?~F20BqW%W>Q71_4KTu)H)> zp23W%1GFupgC)!H8c13JEG>nQo&b`bz>?+I-Pj1a#S9dvk_hP;An6$_S&r>3O#+TK zVBHc3>AVFX`2{Rlj{R*-0*-oMd2xjN3Xt>)mMq6*8=3_ib-~i0gLK$fhEgv z;?iaTM>(*xC_?uRkn|3gEXU)ZG6{4N(8QVi;v7#VwhB0cVqgP^xAkTN=wi_L>E}Vb zO-Gvq96>7Ifq0L$H-U|v&O3`=JoWDPW?1;}BP@9Vvg8FzmgAwGRGt`M6 zK++#rvK(J8Z-GhkB6R-%N&jHUa%|ku1g_Mlch2J1Go9Vo0qT9in{%MWHXFdn;sy(- z+yZT)(O}vFPZkZ}BU9eY;+N1^57KG?4hn9BJsqIo#tzmj$H&v#1RNhQfKHy8&2L_R z2yEOD&{50G8sL4Rpe{eWzHXS_3fTt>S&qXCn#u;PKxcuLemlU)a}V4d6F}~OB~S1v z)@Drb%Dn}{Fh*W@`3urG{rzly362hsGSF#S{B!sX7!OT%n8Pp4xO{rd9DW6b-|ssF z9Fa<6&>SRq9U|=1m^pL!CFlLc&9j@Ng=s41>!(?fDIwP0VKbHEz5BOD3zLmqXx8m z3TpZekn|3=EXPaVJ78&?1)=)@NcsR zpL1JbB^r1M1n6uGMqLJWf#uU5&gHjZTs~cH9>1K@1yFH|)VyL;0G-GP4ZsI%peitK z9>0XcT?}eYUphOt?Y&;Vg_W}Zh&lqnZtl) zPA^C&=ma#$`TQ!3H>TUo=ND(3Ha%=Uzk~PGbKw25?A(qg?}N77ItnW?^RRI{?wisk zpvVN;NDSI_^KNFd01q>_<6Vdv0r0{a$9D)G6Sw2y=?~}gTQapAoi4M0UxMi-)AZQq z%!<>)7x13}^#?)wpukZCB3J~LY&ToT-@?dvYWwa*{9??E@26i{%D+q4iP2F(fh~J& z+j1o~N6>w(9NydKFXQJ2&26k-&fm=#I^B8&|6QHEj0#+!6-$g7Obm*gkRx?j6&S(C z-ZCk0I&x%#>lzk;P$kLfXE>!)r)RF@uM(IFzuH6rwCf;r`{$MXD;Ys zbcc2PevFT&_pjr()SPj>O8|7huL6@lWS$>182f|=cKjl!`^o`2c=pKjkL&ow8IMkv zGhmTtJU-pdfJL6Efn|DN9i#R1Sq3aT)A!dfu7Hd5%@g6BzM+3rfz98uI4HnO z^WDO4Ao3M#%oIk@NF-cl;ud}d(NnCT?H8KNGeE0H;8Mr7@J|sv!w)`7o&hxd$pD&j z0=nSN$FzYOE;=`XkQXRW z!@vi&g7=IFfDSL20#ed9ed-Q=WyVv}ckbY~2i>@HUW!w6I{!|7F{Xab>4MkzMWzP` zN^wqi*~uTx4l$H-`i6x964UqX)G3`m`{K=Je+yZEzZpt>Nt)j% zUcZ}PN*Jmhq5^dJx8v05Yj*Qb0IdYs!(VFk1{QT7-Jn_!w9OW@G)93Hbj=y$*cmQ& zP{cxI4HQ@%4YC9-PJgq9Uz+jQbdJ6J@tn6#w+J|12VL7%fXq#ueq%2`=!gdPef%>d zrZI!>GDBX&#HPR{Fm3wwef)Ne`=@`~$6vwUas)mY&fxgx@budK{H}~krXSwVZ_0Rj z`p^CR(yGUpl$arj5VS6kB}?D|Y)cc1186W5v>2*&y5#|W7p5;9)9VlLOEB%`1<|6@ z*B#*JVLUc{_W}MnjJu{s9OTz#JU+eUAio;psp+c^@~1HFnl5mNUx)GdblXGxT8yWr zXB^_U7oW(iz^1^a!NdT%2pe>n2Ad-XCpWWyX5cnZH-QXy{4Btd%P)vic0-M0a=}|}d4cM-- zf!Py|@@p~fpT7GjzdPfB>CDIYbs4WuH$28~$9Q9U!7+Yk#%0sD9OKtwygvQ@G5!>$ z>s-?l!~{jAM;_;QVgntVE3kC>!sGnnj4!6|JkDCEYLL!0;@o$dvQQ_2l0VYA}Blz$q|^U6Dh9Q=oNv(@Fk7#$D5|oaEPKJU*T26u*Wr(W zE1c#xW_&R{PZI5%~=<0Y`kWtaF5i67k80V#2y14!Twv%r+;{+IbBIA-k! zk3|+w&%ex{DFk^zp#vIng6NnE-AM*oEy%3FG=tHM3DiFX%?3L{jY*v@bp>p( z{}uijjBlpDxWX^XICVPPReoK;t85DFkPHsmNyRE~e7eh3eiOzQ(<`s?8%lyBmtCJR zMu~}~UV#aeD%mxdA_S&QKYW$n5R&WJHJLr2xgNCUirEpGyacZCt217lZgGwOFk}Dp zKiBxB4JLw`E07`?bTJ}$&n3v+>;gY{z(p%K*MNmt71#xiPj|o0ucUqve5f@qsHMmT zD$`$Zfy*+`u@j*6urLFrPq@x+&Uj(^iR=8~j8~^C+~7B4I>S2sf{w8G^sF1;YOn7G zzb|On^W7W#D{Y%Nq1&-sc|n#$ql+v8RSI{fg{ro z-{eFP68|lJU&gJ|V{h?`%U(DGNr;RLJnW#t6uf?V&!#2;M*)E|(v^G2D4u2yk9PZ!YpCQ)* z+UdoLw9^ZG-2%G;___tI>2a4?#I~=#%kRYqY1T4>o3+f2pcxbn@97-(`A;!@5^azx-nI7`rQbo$z%#J5Oib45b0(5_p0<*w3&>XVk)ai_m_~iwzaDWz@ zf)+G0X)v(}^iMZ<#IMA7YkKe_enrOH(@P)m>odNdzVs3Qf6!Hhpt;{wkNFK5pH9E? zm_Lef|8(;w{1%K)rx!fo*J6AkjZ3NwET(qXDSW1C2^gfmXREK!>1%_V?Xj%5uC0Qm80UJpIK}{$ekv zHfXK})pu~+uuKh3^BPRp41s8awuqKK<3GT7YBhJP)Y@_5dhcs0%xW#f58vBMC1Gm{(Qze)9qjKhk8w9R^SCo zEARb)9=0H zcXGYO3e99lGEAUM!=(UH=Ex{;9G4144JHRgK?N>>{nJxl@kcY>nSSsUzYEV4E3?rKeZC;g@HeH+{w%@CxJ`Z}{UFpG`M^%WtIqYhIIpqliGU z0`xSN8c>f6v^;_tG%MgZaXz?1UOau`TmB3esA5RW@iM^l)PfhsgSIU}&R@jP4$%OO zYP)y*^BIp#zxR$mRp{m91_4J)fnxAs*r4MHYo>?2=TDb}ibM2)3r?_e1Zt;Wf6s5o z*gKu`1OHS`aC4RsbOO}!5B$!Ir>0MgW*46R?*sp0##z%BedGt-Mt1BYe~tWYE>LHS zMTwc01=75S6uYe82BpC9>4~5Cl_U>CPD5Y&L@6F#%t5} zed1SPygdE!Cw?`?E7Q3?^UDZM*w7&02%0@-1s#6MC~#%E`DcE4P>1L;Qr zHkQ@#2S^v_G6}{kfoot*f6z2d=lH^}Bz74r*TV$uMnVmo?(l_QO6&?)ZVI|w!54mM zHzbcWFl9M13S8j@N|O$6y}-0 zul$CLx289L<+o+LJpJHTehszj9I%iFE%#y70NtRa!BhcS%fJqDR2LU=!$O0v*Q%KE;3u%!P>ayW1O$ExZ*Tw3&(6qrYa6WtYgI|N=3TUWTO`v$X+)w@(At(=$zFk4PN~RxJ zFRU_s!%zMmP{;5WezxbnJywvH8zrh*6_&5JT%`>2b89~eTK}Q0E zI$hJ46ayuZKsCv4QQ+qE z>2KLKP4D`{@6EV*`n5m&mW&gp3;g95Wt=lz?JvI^p35kp z&vOx$CJR9NrOX0*rkgVh zXfp1dp2#eq!gzRk8?%5J$Ln>CpxX_uPCvpdpat6U3ZnK*mtqmnOqqSV1#|-2CP4*I zCI>BFRbX~}^R5N7u^%i5I)N3`s$zERSkMd?1vPLXqTO>F1RS{pz-mFMA1tcC<_IxZ zfkogO=k(<)0va6KzBYk|O7~8`&LZH-cxbvTtAGg~#3r!aAcgU)0$LnTKne{6_D-M3 zDxl8~u~mT;bigJ^$$3@*ZH`8e5)*;F)0x-=%ov|ew`3ErW;`^#luf{tAL1YdR!|8K z(y)_FK%3+2mnP6O>)z>a*aS2gk4+b27XY;n5;z4^#m$&bfHK?(rYy(5vl|2)RRoG1 z85P;4f0!p=I{iJnfSwRk7&$@fa|rY^PMf};L%^Kz*!1rl0vU|YrYCR;I7SpJa4E1m zP66F`#0t8;Mu|93zs4(|&GGVQ z3uq|3cseJaK!O{T2XV41FEjEG4%AXkP;JH}@P$K>QGo;0BjRx6$Wme!=w?#ja0CsZ z2|S*@o=+f<8zulcn1GvKpqBC2^iF;OX~qrHm+}i(F!oNr$1fl!It`>v0kjkoS-qfu zK&$06W@|5cV^{<(LE>Pd_gppu_lN`X2!S3-P~9Ob!Yhx(qg; zevqI7hX#{{61%{$>E40@7K~4(w+ISoGTxiMPEbGs#l;r|1*S0`o1P&gpw75^`UD{X zaTMuILIP&SGno~@`5knHwVS{r0oay4UIuO?XEVZ`1_}vjVF44dzf2$(g@8thp`i+n z%2Z(iGtfxZTwwtV6x$vM3mCbBj(AoCormZE8pr{ktOV&I3S{%KFtac)F@i2bWK-k- z9~!L)x~|3%#M=QnP7`!egd>Y0hvTv7ks<=pQjd8+hA6P7rr+;xbU1APErE zA_E-|!v;E-MPTameiuP$1*ipZ6`-aTI9EYbrB9E3E+8cgH5#G{+9KQ_CeXt;ZMvkWid1 zP%XnYef@I*&gn-b1>)rT*`RG1@a9+Wh3wOqKx_RN6u^UPQUbP&&!^W*3EW`3I6XpI zK$~&S^cHCWGsacZcS{RM>O<0^P&OYE6C(pR=&~r#PFc|Pa*jwneNNC-ViH*b7p61I z2w26g1MTI4?4VF!7I+OBUtrN-GB9Ig04=}=-&Mr|8pZ?Plw|=jkOk6XhYuZq`DYMA z3Cti19I_Qy1XfL-B_klkcy0Pt83A4PIn1D2qavp3pOKK5&L}HjWw?|XIy2)68BtIG z4Un)1eCJVQ1vwua0t-MJzCh=?DzI|zo1P;pu$HlZx|E!NY#3;W22}bnLGQoPU{Ww+ z(f~y$6KJQYMwY-DHYF(q&b3OCfv~Nv3M`;u8Aw2}XY(;JGB9yFF(^nWNY*MzDo83w zg-!336EI?Intnh|z?SjY^nY>!c|yN=VeaH*RNzn$6qq)>PF_HY@!0g4@&c_)e|V=W zCKy^7d!Sn2 z(irXmwN6s(>Qn{^^HQ1zf~Ffd-mcKq&>hBEV52OMy?|@OBwB0UgjW z1fJ>wu8ha0_p1vyFdmtHL0v$f@$fVa0X@d`)Acn3bQn)fkI?|zRi_~^gK^q)aZQ0E zjQ!K^XbPzCfKw)@vB(A*G}jW4W4t(BTT8%}Wfz;k`sq1Z0?QfSPZ!Y^P~b*5V!`wR zZ2?K9-<;ElB!RdxN0-#abFdYFixkHHb2)eQs9AE;| zn0UBYLDzgw4>T2!;AU|IEx2G%;1%efepW}oOdb-{2N;!DdD%f>y@%127c}|=zAaAR z(sU(VfpEsA=~cP{x{RNuuh11xWjr?hoUTAV({JAC_IhC3x9bZuv4NIk{^6beMo&PJ z=`Zhec6|XQh{|j>(CWm0ywhjv3xKApw(ASDDgNgL?bT;d-~hGxz!?*iiV^<4F@3+I zfCOX1^l}3ML&nDGD-8tX6u|*_ff01gGAlTDgH{eJFerdh6=)sosp&5a1k@OBOy@Tg zaA0hj9$+Y-$vAO(m7#zFWApUch62`%z0GSLabf;Sy3q&)v zOrL5jkO*q28Vl%hH0^Eyt)D5Lu45vQ3FD;e$N@fCTLdSR&So9fdlvqK7wT?Df0{gc|nF-i~ z64fko0d2;q(~p@8$eA7I1zp;w!L)%9yakvARMUaBwK8TY@GA%itOAvypp?nr$dd({ zgae($Ah3G6l!bsZFTxu%8+}3EBzd%l(680bUN!G&HF71a71=YA}I@59iqlv@!kXovvmN zF3cnC1@ssvPVcY>r;nBP;Pmm-9-KaoIS9xx9-jWx0UV$rj^F@wauiS#1P3S=C^#TN zd1-p3qkuKzr|DZA1(X;Yrr&S`r&um00XbQ4ihaQZnn#1C*cr^Az~NBf6*x8B)d`$h zbDRW{gr+fp`t1ygte~2m9bB0_a1!ukJT`s89Rbbh;m!hQ4r2T!lb649LA;wli!cx?J3R{_v4 zqobREsm^SWd)UB@Ft7tbZB($G5oSyk3M`=6cE`maHF^TY(-*l3WVu3hKwQAf0Lm4h z8UWqRnED|)pl%Ly7nlV)z4NWRfVgxM=w?q)nowdkW0C=_oMi%E!V5|o@*V=|MlBmV z1RSkFt^~DiS-{5>!UDIzj7bC3$plwHjt4<1K`rDR9s=n$P<;@aL3I{FJ1mHyQ3Nst z#e~%9(VhaJ3HmNi0U5?=)0cY+w1EaT*?7PMn`T}DI=aU|4In0gV@!&yjt<#M;0=`m z_mR$22CWBBVgv276u3XV-%CJI@CK6u0y}6}Btc2Qj44Bb9pqmHM(}O&54{DX7~7`*@fJ{EY@06YBVfR|c)FjDfHC9Z z=}kTYvxFKz({7-#OLp*x9=kxpbUj}IEyh>VBYg$*7++2A@D(r-Tmm^&5wshOAxnW> zV9E5ez5;t0mrn2Y6VPW|I(@gFfF|RO=}-Lx@)(y+5Ahe!W?VYG!C%0W@y7I1{sO6t z*QXl<2q=T(0w8h$0w#<%rf&@p=oGsSa>5D7WpfH#pbKggm<5(j4+#{oWL!GEFHit9 zRem;5KvNq$N4SDDO9`~$2(FS{Z zdl)ZHe-bL7$#`)(UzmWA4YZN!4jO%T+yGhx0lt@r9U`!U6XL)boZ#zU*&PpXW+^Eu zu!B{unqCtoAi=nJ`qVH1E5^msFN6t%GcKO45f1inWVk?#EU4KJo#H`;O8N{V<0X@cx(-|WKbY(%eE|N^tQgl#=ZX~2U|c-iI8s2Dankg}NC7RzNz;2G1$l= z9Xt?!{a^t5iv?=45X9F9kY%UKMF~hTu9|KcC6LOvbo%@#a6&sD1x{!`qXcXiCr&qu z7SQ2a#jL>YI70wrFtb3{^nz#s3C1JSJEH}R7$;5N6D=So+ysjA69QQZyr5wn(D>@E z>2D$O3NZqajL)Z6#R#YyfTKi-*^$j1bX$cZpF1xrC@%?QDKROqJ3ipg0(F$w9Ty0K zDr_c!uIU$J1oRnqO=pY+TV)a}kivJJO#xiRffmvUJfFTSRzQQXYx?AMra zWx@LdfdaN;ObUDg>!+tC3b=vTLIUfjZ%h<0Wjr?hc_Mfa(LYJRBH`F==vW22;|_jM zQu!d9rNjcN2*8EV2Vpa&1`!2z#|Dus$Gaf4?4VgG#{>MJH6y6%Iz${96+tfS5Xo|U z22u+uik~M5sQUz2Pr%h*06EI<%INc&m zKuh5UE3{P!?)$PrRy2Xe8y#)3l(+@0mE-Vh z0Y_Bo+~} z1iW-+fY)!jKfF{yysbdW(r&=N54Vqs>1>(gfx2&gkooPMA{KvW)F+&S_=he<$d5?{qLJG>1Op3??N`1ukFESzj7V4B$pS z6Qq%EPy|kLGl~Q(JT{#I-#8DNaNq#VnJTdeRDw4Cn=u^#nS6jL%W)q_i3)gB11@<2 zBng`BcnXpf6eyl9RVeg3kQ`Qlf;zxpPmp2srW!6i-(v708!_ng(|d z7u-Ey=R!?_OQSkh9cmUt8Z>YOI-&%;;}*1i1{6S`4Yy~eE0qa^frd69Wd^fA+w_() zfvIfMm=&3MSf)>$D{kEX+P$>=1CY6)0BV04*3+ z-~i1AfocQ}&~(5KCQyG5JS@!t8su`^a0$AoY*~dszBg1a#DTmFC`Q0q-3$syCMlqp z0?`L5wPDVztOPsrMWuiSYbH4ux~w=yy^ zLvs+j3aH8foz>|06+C73XJ{Jy+G8S>Dko+@{D_@cUKGeG4@ZtRV|>! zxMVtOjet4h#p$*+0xpclrc2fd7);+(BcRRrZ2G+#0VDlu^IF023R>I9NtywvGp^#XGkk4@iPFOVkJz=^21Kqqss zWPyeam_VgBXxPBKL7)mUvn@CMLW6)C$0g9jo&sn>yip(t#!H>v(I}wL*gJhkqreiz zW7A`s1R^=6fOe2r2^3G?*CY@Jb!wquo)>@yVAC(1iVT^%j9##{TJ3TLcuvH-oMvfK1V{XfSbr z)=KgS{NkN{wnd;&^c05zvp%DYB4{TI_?SNh7Vi1egIfjkMUF8k@oF%sfXYLLEYRi( z6@jDEr?mM3-~msK#2k;=w!hUplKK$1y0Z* zYmNs&>KFw;r5CF}4Mf2YkRhPW_D7+T(?iJ`wDIuEmz$^eaAhJV1pXn&; zbpE%364MuT2pBRRn|`H3Ku@|CbizI}sJXz*4GM0CEXO8DUtNB>VwZpzWAAj6P62nO zhI7+vI|U>;W^U^OU47d#eO{-4Jm{3OP60W_-syKc1ytlg>kbvz%$TMyDsX}uPvC1R z`4l)oZ6$#j(-pb|#2I_1TXYHNSU|6wmq#2_$_dVDiVz=zTm?R%1MDi$(qxGAl<9N2 z1SEvPu7=4of=*3hoPMB7Ku!rI6hIRppdf%~g_=B_xm%!!&yhiaM}Z?-Nl1Z5LCAA@ zbGLvWr9|@5(3o-iFC-81&Drd(uU5R^Mb}?p~(>4IEYTn1CPH*TH&}9QH_Y`QJ zzPVRGQKT0%5e6DD;{=UvsxW{u`t%pQ0;n^hb_ylK0WE+`4X=eeWD=(-!$dRqcr2w)lZj!)sK18Vm65Gx^ zS)iFw?HH3HD7i2Sv@uJ83K9uN8PGKR4JIkb@fYB`p_WZwGetnw{TQe*rNAfvl4Js< z8z%7irHU*(jNG82mrwAvapQ^w5)ZUs8tU~ps-m>|LpX%srzfOyjdrV1$AoB~br zF>5kQfETiY%QvVd1t!O%3<4`exV;z|K{cH~wi1g9XiN@VP&zUST%MjkRltJ-zGQCy zQ~_PaYt!FO6;NT^FkNVxKz#ibR?wV`CbI#kUjkk|WdZ7Hf-jSUbTgm|EX+YC!Gdmz zRbc>+GJ;EMJI4o)8w3P4iy)lzjzM5C689~Gz(Nsjb&xNa1RilnftF!AG78-12Osgw z&0OdT?o%-dtegIOnt&{0|8&Xe0_q&C>!E9ZJf;g6GVNiT{()IQWP1N}0qJDO3LVJG z9oS^3nt_M;^##pKqMg56%$K;<)^(Nx;!UVDI$LGX#tf zYl9rJK}MZt6;Kp($Od&BRxmm)e-B;IlR8sCpMN&UDf|kM&0*g-r!SrofC0y%ISrsKs>Rda%o9i#fbw8jnQ{7r zG6Cu70UQFH(>KfpFO?CSBcR5(VY=-ca5-Nw2Vv4J&;lYQ0f6Ief>5%-O0M8HLyXpkaOkclHz?$h4$MlK+7=@?*Unnq< zqvLlIXyw$x=`$7y$TPm5zGD%1BJbi7uvZzE3m9qIlQMYTqSUW@$U4h)dH=edl?lNL91z4K@E5X76owyQGv+mB5MTH z8ShTFStFpp2HpbjV0zLT0WHS$(|gwl=rZ1$zI%D`3RjdIK*?PTz8so(24(q{F ztnupw^cnX|?_V!q!?Vl~13DU+OMz41&i24f0_IGNQ>S-s5vXIF zG@WCsfC}TJ>AE0l+Vr5U0_rT&I2Daa+^z&N<=ZC&d^SB|r+}5&de9y<3jxq3$P`b0fFyK&k3c^Enaz;pDomgy%ttm)_uVU?&iG(@>0W^f z47oph1r$MBgZCkf_1!0s%y@MA#(e@BFq`i06VPV60I^kIzknX&`RVri5jyhr3uG}K zoqlsaLI?W+gbs@XU>)fP5IUwEz~WAkg918?52rgG6sTl8I(_#+gz{Gh!9Mjpgb=DZ zgvEqkhXnK)&rR1lEHE9zrxy+js53sA{^PK~Y{m!EryLPbV>~l`_Ynbm#)s2?9}#e1 zJT=|ssDK^gf$7yp1zZ`|Pd|86z>4wk^gl-h%;dqNkF1)^A6TK)Jgegm&^(JHPnIK- z!20Qd#{{fJj)Afo_~r`*FpDWm;MDX5#{`TSo2OqnCScEaar*5TA*tzl$H7HX@Nt1o z#_s8NkAo(9r=1Xp;dpisT$UD3PdOow1mmSnKX(E&JwNRvcqUEuq(F-Vv||XGo&g_I zE1|&RD3K-5JbmX$0UenxLC{RMjuI;`=t3(N4JHl9EIxSZjq#L#2FH9*U6_X z;Dy04AZpL__EQ3`{&zNm@BRd@>jj7sfYkZm`w<{5fmbb{-A`b(E5JKU zAT0ri8IYEM^l1TYj;Y_8K*yQvo$hy9K!+bHkBXmn6jVDEIjGXhSGN2eE_5wK;PIDP#Y0ZBo~_#GQ)>VyNd z?CI6?n`Z=c7>`Y7I}5I*^Uexbi8fyC5O7og56oDAM&Ew0WC=`}zW1zv1jk~K6e!o< zJu8s!1yu;iz2H$8xOzmU2M@yhU@>FrU?z3z?HFkdh`VWX~vDy%P$B7xjhG6$;biS6%SfMzyX>R6#y?x z;bn3JjR-?TcCjgPGdn1-D{#BgWf$_+6 z)k^|;_1{1v6C9e%6PQ3*3$#x~fz9#u$z}mZW&s`0%o#W|K&gvWfz6Riz)gWepK%Q% zXmOeb(+Wl;K|oWQH;23G`RK&>x!1s+gGia}uV^vEj$pdFrl zR|L$W!37S7Ci4$isDSms)k1f9ftDJB&4XCM3rYPOn6tp40=f(fJUA$@j!gl{9{H=_ zIYEo70`>l#>lz%>Cg z##7VVuYqH91w`;2L{Rm*fHUK<>6zETIq>3j0qeMy9UTIW%Al%Pfeq5b=5YML0-6IV zfs9}`fbvoUYnJ0GkV??n3J%8=ETG9UZcvpC&T}8Yc@A73;~5FY_&>; zBcmedH2xP1S&r{PYC)kHbypx&7N!&8SHy@bXkXjZ=>^BcB!!_GAS$56-aHop_38Te z1X8p?l@(}x*9Qjhs3o^Fs4EM}J1mZSu7C~+a-4Yua(uwPdjd9$$EN?j2VP?nbzi_p zcfrFJNQwbX8G)AfeBb~L?|>Esfwy`%9s?<`1@GNpp04;w-eCI6`vN9XP<;?bft(H9 zO7f9odZW9fgfLVaL>}sByZyq7)0-a%WHC;g{_cT*J>#0`dJhG(*uESF&lko$6fj|2 zH+}L$fmE%{TnY>d?ADAtpm7=&PzxLEC4op#G)h2sRf0wbH&0i3B+$-yV*08_0!oah zrk{NzP{w#_y31n$4WR>wEw?O=23d|5&Q7m;EZ`{#-JNUBBm=oyLdT5B2DIk&?DPkZ z1!_P$nx6=W3r^t!Z>j~2qkvYA33N|yeIj5Y3OX(Tv{DD`9`KAeqrk@L=bi{OOKt)c zdu;lQB}&XJ^$N_a3XBSD8cYQO)23%V6|iPJHhtMs@G5nlX9DJShfjA1I0^`WR^fv; zz(OX_U>j&btLx#b@UMcD>VlP8fL4Vu3rv~b_)I{8qvZ^AZe!syflL{wiI5-%4cUS! zK;$*%l2C)-DnMIT8yG>^F6y~}v@p~-h%_|L?nH{HgVvRUwz7!65Kv^CKi&L=Kr82j zBjEL9S6QbwL<);fzw<&MfN|G!-Iw70M%YUMZM8k1bIDmD+vGugY{<=+knto1CdVrb z0`o<<_fKE`Qow~Vd-|J~0*Z{srt??Ih)-92B@n>)Ynk| zDlKhn@S-pUs7kn+9gGU#yQ>{1b$5XFj}=cp`%xekWC7=N?e_wlQBYeT(E!a_ z(Cq{d7_tz>D(GI57YtdLw&K(|z3;t%6bIDa)ae`EgFWr=K|qsn-}I~x0^YJu8KJYk zpy&tJoXE4Jmp;G(#4=&w#0JpOaVTN2f)P^Huz^kzgZpz2NF}pCu>!lJK#8M3sUvfV z5|d-k(W&6`m6X^Om>eg-IM9Q6xdlo)wi8|Xj? zf%NIqKY@L!{#ig-r4xKm%K^|N4`}2UzUlD<3lnoabDc7S;{%525B~_5OAqok0Ow(Ks?HFzGUYj+0_6lm<;#pv@`2{Vd?a_-wk>7XclPX3zmZ zastKE^S%hg2|;-f_d<4Z-unVxCz<&G5zaT@JiwIZvy&u%aO)3pD_rm65-y@44+_R1zptzUJn8)W!Rw$q4+@U z8PJ-N-yGAgd=pS)+W_i^b*KKvJ_CGzJga zI1JwW1&@EwLN_Mxnk3MuhXR|x&FKQa1tdZHjM>4~fNqj7{Vm`qdy7?x13c9RTI&VM z23bmAqgX)~8BCx2TR=nMGH3@L8)#u=321-U=p}9{q}DGO_@_1ARo_R zRs3t`J*p6(36|kKz%00EJe_|Tku6CU`IKk>(pZsbYomPy_8ANlJVm7^-O}2 zj4fbu7^h8t%p|DIcy2mBv!D^<#pzDWf~t%+re`nGN0x z^;s@+3ap=gkyX%}@%VIEHbEuEE$U{l~$;8OsZv4S16)2o0@ z&`10NuL5WiO@Y<1ffKZ1kwt-5fm7h{^vi65L5vrtE3yj8PIqP(lwv$TJ&s*aoAKiG zE_Rq@d)dL3adLny)8P=55CL1(z=^PoL*U}{2o6CH#*5PzatMkt9-h9PLok%_)O21> zK^v(CHqfyGKUfsGKn`F94LO1a9zl1!CvysFFixA^!zl908jZ5WqK z*WeNiVtg>YnoCex241SNI_`iQzzUvxZJ54^OHhmP#Pqvdf}oYFJluj3jAy2+a)XTw z;1<+jJTtu(#JfFx1-GCl(LH zjyoVJMuFn#e*^?GWS|Nm2@ljZL8zZPy+Ke=N*Jmhq5{;4aGW}Qt)L)i6M&_Vpe!3` zN<`q|^cW#Q(1!6gA+RG}2?=VuL(K-YDA*C}Q}%#v7tjFptU!l!HGq~nfO=95j9HG$ zKn*C+QU?~0R0rsKJ7$4d(_@4MB}Aa6bbwYqK%E5kLeTVE!h+l!P`gs6zZDjgKU;Z0d9a5kWb|)am{rf)ZH0m6#}@FnyMYAZQC3o2Z~60@?@9<(gXRjNBn7P*_e`HADQLjBXZmqT!3f3! z(-ouyH5m^~_mUDcW;`^#PD(JH@#gdo5<>D~Hw8dvG1V(Dg8D_E+iF4Gw;9u=r3I}S z+op$03u-X7O>dAEOwnD#rpN~x^#TvR8Yu87u!D{+T*IcsAIQrFo{C61Z4!% z7@tkAkP+17`0~D4z)?(~c=}oy!7j#Q(*fVn?6rg&^+M#1JqRquhd<@2yRWYgNC^rd+&6B=4?Uj)(Z@vAre$|J3vVh)YRC)nB_PP zq!yG*OyvZ#WTCb|@&I_zJ!l6X*fN2s)Az^;N=rfwhbsXs9bN$LihYn1lop1X2a!gM zfGAG4mlxEBc{6o-nY>^dN2)XH&YSR(SOC^$Xw{in9aw`z{tP|8nS>K zgrdMC@J0aCtw$T3nF!K*ar%B0K?TND(;uh^W{S-MM-q=BGpJR>0b0zhz$!3ndc3Nj z9OJR+b*h4(0hx!Yf<_UiLH8m+N)7075A2|3i4uoEi30d4rv%U{dxk8>Zy<%BMdP6Q z4&2LvD*+AKfZ`W4Xw&cp+SATf6HHNnnhuE_P${T@Y>7bn^!I9l65ddw;i}*^fSrQq z$FVE0J33@THq#*41$0eVL}9Jmbab z$20|XA?<~QPk*i@=+Ag@x`U~p0mQI90fJ$S$EVvH2&yt3pFZJ=j5On+>5T@0qSFsv zk#S-?Jbi7Tpz?I}t1{x#r|AgVNJ9o&#X!UKpd+mmK${jJHR74+Z*>Gg2e}IB3Q9Ac znXaoVD9dE`e3k zKj;d2qPQ_n5A4Pdmt;V0{AemD%(#5|e?37*#>3Mc^#v6fmrw7zEE9mD2dBRnS540j z1ZxHPjB(ZUi-Ce-3h;rCJK%v2LC~ViiS=eY=| zO!rBXOlO=n{i3lTXm`(lV?kNQ%hP2|1T`6tPj@!~r;S__K`BsM5Im>?nxs}>R^U|N z5@?$~+eA=JAL(2GPzi*55COPLk2ofW1$4Lb^baOzDG^I7FGh*w#h_Rgn_h1wIFIq- zbUAZD(AG>xb3rMl3umUsnhRPn?w>xxTu>3j-EA(Y4WeI|3xZDRWVH}fW^9F z67{qYv|`*cy~RRM8N^*_A*ctUZ(0b-GG3hCX(=cJ;)+@dsxmfAx3+}HW?Q1jF1Hj^ zl?1hQbs0`EDlxk%a5^zW7J-(fFgTt$GySQhpf2Oq>B3fmD%LC66d576nuAAw9T^L) z7?^lC&`wui=LQY!f@{?upnV!Yz@363D?w#0kTsxXV+=~u9SvkfrfQw5j^ybRlbSc$irWI?7^q9qhEmDb|9j>t&|y1!?F3Ejp6%@bcges<+A4qtS|MALFwaqG1RZnh41};1OH_=m9MPkW)gWpa#KJfDSMOjo{wnoDMoE1!i9A zbZrN4ZGZ0_zvlE04uXz4%R%S)gK9)jpLGIbmOzaHc$h8#yeotz8Iz#d90pwr$tK(qD& zCZN;&!H027o9^!<*v2?*`ZXs((Ac<;GdN@`oCQtoCWAsn3LIP-&;Y;{vg<)gAt7r3 znwNuz?0%30$S>fKg_;QU3+U8In8C1+g&G7`!48R00a(aF%}bsB%tf$Y6nu&x11R(t zfCds3SU^LG)4N;+7cwnjpRSnAB0Sx}O;DY2+jIp#K}F?l>;lt;1)d5EJmXOSUCOS& z1|C~AV_E>Jnn3No?bFw|2`b8OXP??H@EoKFbT%ba5o{z?V8`?~Zi0$zJJ=V`6}UhB zw4b0X+yFktr$Wj1>GMgr|f&!bPMwT%ngTPE-P%MEi^#&cy3=(-Z-OgW7 zlJUj#1@3}!j9aJgcNbJ=+&X=}zo3}-3}FQ}M~7?$Hce(wl-^*-(qv{3crpEqzn~Q3 zz3KWM;KoCNhoB8(!}PTtg7S9-;Kjb6l}rL>rZag78YrD&Rp8WTe8Q;2!OH?#hYg+vX9I2P zdBEt%s=zI9dwPf$xZhLnC8)r7d-?(|L4C%R(=T`lx-g!aF6u34%y??Lr?;RyDRJ7{*`vFZP zGCQ&hteWN{Xves8y1kE}EaTGY529q`K&w(}eFU8t*H1qHq4x31Mli0J9^)%$&UkOS zzksaR^i{rsmUxoGF`|+~A2G>c4*AL9ILXQ3BrG{R_ZQTYI?E5*-M|5=mY8)JxWJd< zLDy312MCI>cV1}{5cn-T-6ufMn#lIkGL-hyGEn=;f$`RKp&-F!jHjmW3=))MJ9DR9 zK#^%W<9xO&(>DeS7T5P|0Nr`PlfmtHZzGtM&JDT%!|}@%FfR?l`*R)4ONH>Zt_Jf` zAiUFOKt}Q;LwL>Sz`P^~@66fuo%os;C5tDWOn?rt4%<$o{1+O!d<)vY-k*} zBda2_X2e%_1C@kKB{p$`< zd9(#=rW=HJ@)p<>SBROd*T8Zv^$^$WS_rn#8Di)n@Es>SP7vPd%U}x~xgB{GnH_iE z0rMQV9fcH`9lJZg#@IvTTK|Equ!Dr?#1CMeEyRYC5Pdcf$2495n_|sf?to3-7^!Z^8|_ z-NkWV8(5z)w#h1firE(C*oH_vJ$wFeg7s8W)@YX?i(vZOaydSJm3KGEg zA##$Cbg=-!lK@%B?0EM#*m`krgw{J=hH%9oF}h|EI3bEclHONHvJ`>%e%U;*pM)X2 zwGY5NAxLbuK&%ktc9d0Qc035-2|z4d4T%_jhz)C>gKgjgTklxE<|)|sybxckyAO67 z55$L)=Yf@SL*yEFf_YpJr_F zn8M2KxEI7SWdfBjJS^Ofo#0pjl|Gh5o z@%(hjC_x*>3)91*1ZOh#PyY}l2%3eGh!(VDygWT9S}>bw!M5r9qXkzmKAfHrBN)aw zYx@2e!3e%>pqW8d=)#lc>8i2dA!YwqK@Gw6yomGf*c4a3e}uf@>aWr6a6hP)^fOl&_ZfzHdU>x9;p#a`i?syHP40JV?d6i(M3{(@m z7mlPCx*8Iy9-;y|z$o)eUS&E@wO}#hvFQ!fg0UQ@*MpCZE1v$eS}+O5OP%gtBM3S* ztFA^6bZXY@8o^f3sac@Y(?B?f>Me|E2+T?sU5NvI6#Sl1GG734V%D;>8y2v-i&9b2i6HnT0oZ# z@PN)F0nG?8!q!7OWDCI72!n5x;RdMyZT}5_1Iyay1@wAp&`&H2wJ%9 z(kQ6Fczk+lqo4`n^y#x21!Wl9rf+Wq52^__2~K6aFnwi{pcCW8>2I0@RT-B}7ity+ z9qb$1EI66*()5qbf@=5|{_=x1`hZrFOq*`kA}9q~OwIyYHpSsMff+P}2;MpY3M7GN z(~DaKofxl8-_|0SD7}gsbb9j%;o~E*I>y~Stu^eMk zVw#@NBp@=Kr&G|2F_ zp8|sdhbHq57Epo(ohb`ndgnNG`nGOCea36kpLGjr@Pkf~;c$Ea4l6E!Ytv z5!7K^HGM;mpa|oE=|_76?U|NxPG8U~C^KEDS1^$A+VqlMaEvwf3(8L4(<`XNxPJQm zUO{cfW7E0&1iP%y@q=w(RpJDf?Hn3RKUkHZju7Z)Q{sdz@PaQ%=V9Rn&8iDbn|`5B zP?ho2bg%k;*Hf1gL+N-1q=Xf8=Hv>A3<;dV zy%v~$ewv^J z^ovskr9cJJR6!%gRnz6C2}&@16PRu}O;D5Z)b!+Of+~!wr*}*fG!$D5I)e_px)5~5 z3V3C>z&{?Cg)cxVS54=g4$-eZ9jrfax}X?jmEnWwxzhy|8COj2pDrlMcys#F>EQJ~ zC#Hj!g#Df_sBH1{T8Dt6tm83APl0MaEszD`p6~G9H`0Zw7dVTydtLvF-2k(A5nft>B4o@KKIRplcP_6*$e98W=(6 zsxf9cwqED}-Ny`C2O$AkGQuV>W%|UKg5n$tK@y-L_6;)y(}SS~!s8CrT-53A2cSs< zbPM1*IYB4kfu|I+W(i6OL#=^GLu-8S(wptGz)NqGXA4>)Ja=HWpt0=@kmo>)>rown z#dEJgDtW+*lQmF0_Y))m_Z-wfh#SB&{?KLJ;6TIYIj9A2ooJqeS_6?r_FULpu;&iV z71U(BHvP?9!AQm#)7|C?YA|+B&z~o#%{XiN?0JIOj2ox3&ll_$nS84SG|UP=I>rvVG7LQHCjq(jjvdrOVHG$${mpzqH7RhzgB@|i0=pylhy@mb z1JiXD2ztnY*Dt}YyJG?^l?5#-0woTCEP>P0CoK@PWIQ$f%mUEZ`?Q6E>Wqh{Yb+Gh zgxqk*uE{(B+}^-)!=d=})`f!n5<7T7TRot==Rn3oddlmkuUROl#5i&K`Gw#~ytfMl zl^MIH3oa6@W4t(h!XiOE#>><9FA|Ia-Bu{OSkMe~^3MuE(DfsG77JP#{hAAHesY60 zIVx~FPT&UbO6FGJ)?k{zZN~IMP=VX=gfNF-s3h1nU zBr_0G<2a2xNgi_DhN9II(CJi$@CLT!K*p{EEP25`P$Je;Aje-h-aDp;6A7p zhlr$3U$jhcJJS!Y>Cwvtt>k-I6u9*nFYrL>U(m^`9H9D_6T0hZ`j+K_WsK9NYp)Ol zon;e?@V?=#=?wyoLIR+(*}+~{ViBkT_3@GX{T`$Qbc+|K0*gS+bhz)OpnBoHmx6}g z57z1XR|-l=K{dkVIT7-oSAzZBwn`9mtIp9?f}k@Y)Xxi=OwYS7$T>abyrA%O*VTgJ zjL)X`pBGdWMsm*HS-gr*XcY5?1K`X{-)2FQw zOk|uh{l^-?3XXOEn*}NbWJ!_qyCS&*XDeDBk zXiQ{Q07u*pP9-rjrUouYMgy7Inw`txmq>5SK>`)?PNwub70#0js6f~W$w;{(1d0qB`eBA~!^Ji(I%G7Fl) zpys4bU$$Lvvhguy1#VCQBn>*2SV~|SlY)|hsDhRT69?!mBa`FjP$7*9>#wpY-Z@znHRdj%~S&rLVkCunDQnge8gkDwAK zFAFHA3W6HNCwM?)1Ai98c5V%(4nZYJP~-{hoxXUVV2%*j3kw86+bFmlFK}fE%$TmY zUr>bc^mMcRf{J2LPYHsDP5D8Yh8t8J^~#b9%upqt};Ku}u!GLr(gV}}qZpt&8t2xdV8yF&=g zUA+ecZN<(AfI?yly4-^Uf-dz>8L>EsnTMNOK^EdUCD3(m3c?C(0zIGEaW~oeCoM1m_G0-2skpAWGPKwd{EGd@yzsR2L-JdPfb@m zBZ!?$@pOUmP3MSjLW9qJ|rm12|3nDRN%&R#>0X-j1Q*k z9Ts$CJUhMMu%I{N)#-Z<3%W60o6dYhPzQ8E_7OpM#v9X)3X1Zd72sjwRuEAT5;!Zs z!^k~-!C^sZ#+%c3fK=Q9sSr3Tzzxczpktvl75D{CO;*D1&uh4fzG$% z5;!s4=D46N~ro z>V%+_@*U8j$&7_84xo~T8*<32AZWV^s144hz$);cV|w@rK^4aH(`!x$sxeNTzW9V- zEaUp=j3))>ih%PSx8no$ECopgZpRDY{SBK=3Tm@~WCRvXe|%EVM)erzsy}Ff-vBzK z5!_`%*XD9c&`uIr=>n)yNd;zsMbl@W5;SGpJ^kV-K~=_!(-#~Ul$F7;{)pF!R?{`^MX3oT{l|<9G_ol5l|2YU4h2o$Od9CLAEi1 zD+GZO1#ZvP($pc1~~- zD`>F{_w)yqt23LyI~739m6++IxCyE=b$ZtYa2rMTqM#b%vFR=s1wnU? zq+bMYL?Ab)mVjEDQpiDNMPyJxbwcV>PzM0g$Z2PsHa+sPpd}mhTHzU&1ywDPt_%>sW3O_up}i>N9g8eB_nfY22i2~-3fGd`nSu1S&YY~CteZs7elHF88w*_6!jGt z9VeWfe(Z{1G-KCvrK^IzjOVr&Ton{yM7~~}8+7^NwXf}aEp7|%^F zWEM)Eu6RRGjPdexiyMOO#A(pJDX7k~3G9{`tV%2-S!Q@kP=@j7biZ4IV%#TK$#JgJ zZ9!{LnO1*W(17vc^bNNKt?@gX?Hp)7H0Sh%j|AhUProDR!gGuX)I>YO3aaI&Kffbr z23m7^S5OOL4)_i}2?a2P6kdAF%mU}8PrNI*526gb{||ex&bTMIknz}btNY+WV9|X+ z1CB}4Taa=OsErL1N}cZcKoE48+U*B|Mi8UHH}pv;fGK1burLUmW1D{AhG5+Etq%pQ z7*9|C@K8{dv46VQBSBk;Ry?jMd@Pte{r@9DO=46Q5LN_AJH1Z?1-TD^PAg{ywbV~c zU-<;)2|_Mue=2Cjcx?Kir-FqXm%z<~7Pjddo(O6awi)CRgBOA<)5V?(T60_mYwjUY zvnu1Z>0vJfeVJO0PG9;$P?fQ3`so*f=EPaP?xkQpWB+uySArFcXQoeoC8)-DbNaql zf|iUIrvG>)XhobU_g)JMGH#jv;We=uuDuZiZO&zVE11W34m5Gh14_p?*aY5BZ+t7L z&Gd_R`ntD*vc%b5{!Y-Car5-m?;sj)ydzfQ!uNt|O#eBipL{Q9LY!4AKM2Y&o}Yf~ zgP=EY8s>i%WSf5Yqo5f<<@dh{rc7V);63krjdLjEk6#?iQ^ z1$-XFbj2@%SukGe^vPeq!}uq@2)Yn-1vqQ%_$s)J(SLgUHzHEe{cnQRjMt_|esQwfbCC*NjZIMP+&BFRs}O-Y4U{ZE!F-xc2wI%82y}yL zc`$`iXRtu)jHu}|*o7o`CxYh3L9JPE6MZkckS62#>Cf4PK*L4i973Q`ym}5HE5r5~ z@GB1?!&uDVkqRcHw&rAz638$XGh`U6SPH5Q(x?L8cnH=l02{%A>VeB6jbN?i6avj^ zIdBPyGftfz!6oFxxMX^uh=kVkqg+CQ_1oALL92&B^^6(Q3`PZZ(8!>`Hg-kOmM9kR zDC+`{=mN$pkSOTnfEA3OJ6u>57{KE=0?UMVD=@;DixeP-aD#*wY!g@oUK7j=nmS?! z@5eywM-)kw4#q5j?b9>3h17huv+rK84Zc#Twz78tyL6g}-krzZcDDo*VfX=cJ zP~aDU$XF=ygJcX81r!)SmJ2Ef39Ovn$1fx)a9UV_0VF2?A|wRXPv6cjWTWr`w4@$% z&I2@1Cx9a61L%4w#|cbX0$ZmG3kZQ$!CMOmsY`E#?8sB#Q{Yt)P~cZ!2d#!@*JO?m zI5)jkKnS!FeujWhE#t-Mx zGoiE4GdDmB`xRK6>ILphXAuI*gY({ic-AT*hP5KgtPNa=ZfFstDTCyF*$i3&u;Gt|cP`n%|C> z5dv+Yu9Xo2-Jk?!n;1OY{(NJFM^k6xJ(W^j4iwG1?2N}%|<)wo2CNJxB zLm44m#@^{58(5|>@i0$skP&j6Zml5X$vAC#tAbFF)-}*M!Mx0%^L)V97lM`_EZ_re zdfCGSUIWAcIsj@1X!k9nqL7%|MSjpKL2zcvP+$Wc5~#oiUXI8pFq;{q4Rmr8XeA-& zzBz$Q{Gi?9>>wiw%$OJyAX}dWE=`YB6mnzf0bNPDTv5n{@$&RHib5*3m-#_Pg3B7H zN|1G+3rrNiIT*B?0kq}%3O~rEKaAkRY@jMHP4`t2l4ra$Jy%J{1auRT61WKFQWml> zT6+L;t2$^u3=?QK4(QTN*f~`vn80_>vN_&h%5r=SQmH0TJiSC&sMrCj4;q{dDB2No zC~P3_VKXIly1I(cBw@&j7Hps+KU5eLn89m~&Zr0(NZbeAxe6K{0)-WL&;z<3Ttrm} zv<9hIRY;R#`->LP{q*1)4TEJ6v(8X)h}&Fw86BripXVZAFx^>AD4KEF^!aK+DvbN5 z?=|NzoBm5pNRRR0bUOj+uvPFMgwo*c9tmD90<9kh>(6EtcxgI$5saRz&q<9v_; zS?Ju>dP52bP3;Zjh2kRS)|G=*sY!8-lCu8!;Tm3q>*>pMKp~D28#?bQ2RHUB=_nlT3un7>`e%X(9xwM{b!2nKGW5 zE?_ET!?<|5uc?q4kEoaFR~OeXFNWAm8Fm}7C)akKSLYoOSxohy?5+S_ z_X0ja7PPKJfz|Q$bXyxC(A`f_HsH+IZX=|}(R`;xz)?q_c=}!&pu*M(}ocC02pI&}IbaC?#+?3SC?Vwa3j) zNRMOxPkP|03nCzLJ(l z(7)P22y`;kZ3iLHW;_QMA@S(~jzZFm>!<5D3OO_Gm|g&)mQMfUBBVY2zN3&X`UDlZK`Yq6SCRJ#3iJztSPI+%{nIZy38^ynP5mV02NxlAh>aT4CxF;sYaN-{1i%(YFs|4h>MF#- z$hdTRl$%h5)Cyh&R!0Uah7@klHhvAJ2qk9F=7R)*UDMCFfz4-f7cyrJ1$85*2evYb zPVa4E- z2Fyn>0L}v&Fdt?>j-Sw5-h-fJc8(lbpzRtbr>FS~l`y`Xe%oJ2mSw&O_s;1PdW1z; z9eJ_@&Q0&@k&>9M7a%0TyAsp_QUFcCffkJf2M9$mE}gzEKuBNa6zFyiHce&+NFE1w z>zN%PE83o1UfM)P)LjMz;vHLA$PAopapN>RVS>FYejfK#}8|d^NC1y}<1a9YSU;^JE#|Ao7*zqe! zEogl-mjbiE2M*9ST@5CXiW5u$A2Id;^1x2pgtt40;)+DKk4X?VO0(;GvCgoU9_g(w3}fjCZ` z9(Y7ZWx7k#Oiu_G zvSe(VJ}X?v8+2IC5g~)=Vi7_{jL)WfMhKZ2AGy&X;3x+=G#S*th3xN{!VJx)W=x=C zU_i^rKu5DY0VxHw9yUe@rFuX$Lfp*@x(+}9w7ws7wi6;$!0Y@YK&QK~f|~k{P$M9k zpq_Oslu)1E8Yz^b0-h>UU{GMOW@G>*dj{lV4H!Y^Ih;8&oi|FzjInLHOO%kGCv3+7 z8+cVJWTybLA{(gZ06UdILV*>Oc|e;LK=*Wl9i*VdY{rDT@!)opkOO?<0kUz^^`pUk z*;&y-(2YswqJ_f5kAp6{1sxpD30mF>I;Y%`BTJxZx^)b=ZBQE{q|DefePN6c=+L{{ zAi=Kbvav#-6N;Q-g|bX%vq3Lg0w0P9ITL^h^|+>56ee;^HUIH3y3xf}Hb^slK$O3AtJGOun2?`X0 zoCrF6tp>8V)d6&NGI+a#;|!1rP;zT|ZGs4YY29(-AuP z3~IW};hMfPUP5MibE1$S`y4LNMt7O%2jV3}rms&F;$u8BeP5!Gwix)dUQWa+Ku*vR znHQ)3PZW}1JT_e-30%fCBncUC-1yW1zHeswjwGRUAt(>xY)D(cFj=UdaoY5Q$wIP> z2d3Xo7E1Jpab}>r3z^YgKl1fo+rr+ zIWXwdbm251O~#eeZPSE6NAiWG38^ujnqHeG1UfWrUz(6S@<#eeGAp^#1)4elC_&qF2%)Fq`=wQj>7C1FsIaA1t z@yzt7Od&PKmD3wDg|r!OPhXoU6vTLHT9%MIIqTpKs}eIW=y1Fntl);A zBb&gg>1%U@v>8`TznLSX$hd0y?;Ih}Vg4uWyW7TC@%@O=9FTp>ld9qa=4xfH-<8Y?(_z(*HB4lfee zHC-=HNRkaQNH<+TRYF{1E9hLI9}HPa3<^vNteVUMAVNam-u9Y2A!SC!71I~w3z-S_ zY?&+|u!0TLvJG(MWpp4C8q+D0*?YaDEqMqtm9K)2VElM7$YP){ce$v8sp*VEX6{~jO(Ur6bpe) zVvH>ol4D#oy{1@56>?VA(qf@l8BoCr8Wmvx6(sDA4;ZqPcoo>eN6H_XE>|KX&bWEH zSqZo|QdT0Q$hdj>j1nPF#?{jwl?Zi0rX|?H(-Q2CpySIpytmht3P~`6rU=V~<}sd` zZdNXIUw<#70vD(Q!lS`7fl-kYa?c$%=-_0=EbuLNpz|^~vK9G2WuQRlbe9UDLZ-h0 z)3;X$nF%0WLC37XA`m+LZ-vk_roRH)r&bE7F)}UKHhpiEkXJ+}cxSB%c%%u`XcIUL zzAO&3kp|SH1GRt+K$jV@Ixg7OC1A$nqQD9|Iod&i6?{4$E9fW^PJw>{kWF<=;PZtA zvIGuKkFOR|@q^rf90I-r5@9;{LPREzM?gmhv1%{{C~*iJV{&A)VhC1XQ{VuNq(N1K zw&mUc-@C>kaA^9)Y9VvRsna=YgtjvtpMJ1LNRRQv^ba*c*^I}h$J7eRG9H{>RV!r3 zczpVrS|J0$6QJ!ZESk&_kanlz$?0!vh0Hjna)I){z~<@Nbwa9a9S2$j1lCUvs}m~2 z8Zr0lg!ma5L#My57wTpVonGA_bXN-&F`)jKA~z&n*g;u_1#}r8H|XX;(CNRRP;Y7! zT8I%B+l`xq*qGSnY@RG2ux7e_i;ykj+Ud0|LK7M1Oy_SEie`+Qp4}?c!nkkyhgKmo zMp@9+h>D=~Rty?U;2l%UAS*aQZFPar?au8&oS^LH+act@cw%~AhmbMjvFR1Ngc}&U zr#p8F*)v|7-rOk!S_84AQ^<#L#&pImAvLjH(9xCP;+{i;$v}Y#G?XB4X1Z;cP!{9l z=_|T~tQj{>f6^s%15~zl3(1Pl;sd1$R?xn;1B{@9d_ddi1P)C9*e&G7IAyv;kB~KE z-}I6mp&Z6D(;xN-$uf3K|KB5I#CUbOQLhkaAx~s4xQ;s5D`X|M7_>7%5quL3=#m`{ z(6ocVl<9(fLJ}OuKysi;*|1M2UlytoQh$N3qCsewI(=`SkhCyV14Ie5#g+&<`jfj~ zD4%iK^y+>g(Bkt={X!<(8$hFA99arn0)5jz_6w;q?wKw*K}ebLz;xRQLe{Fsc@;Q8 z8CroEbiT8|D$uwe_}DT>9?%>Srv_7k!0PF9CkUA^Zk&E;f{>~lxCh0l$qYF?6uOF@ z1=@w;m|lN?UyPYmU^lok#Sgw?6jWY4f(^)17MKShIkDnx@qBoIQ zfmwkYbebiotq95tT+n$}VbBgBP@+Q~1lTx9D1h=c33l;|j_^<`&dPY#p zv4HyOkbXOe3qEQYvQI!z;K1~_$wK{%8>YXTEELZ8V7l8BAtlDc(=(?CSuu7@Uou4~ zpM4hxD34#6o(qkAuq<}=~2^#W^?Y~U~&MNB{RLiMMz}2&58+392q;NC(jl#wK@hG+y^^_(Td>-WC|U$p+ncg)=NSpD~^lfv5+!>Ee|2Id- zfpOP#`?*3E9B068f5J39aK4bp^vQFDggHQ+bp=6z<$C zPiI>obe(bi^m_}0f(;J9&RpOHx&H!lmV%hT5k^p;USI~dX;>Y1FlT}8BGP5J#4K=h zdg4MMMaJXPn?Y3P^o@&!tfxO%D71qS%D%ElXuTfXP|#Vc3ap?mK9|5rURde|C+8c? zSzsA~&g~l)3+Xd5zMuYZiI5UsDCneYxZHHXr9#ITJEz}RDx}BQIh}o(P!eP4_N-+> zvltmex3jJg%4cNkoL;(8D4a2L``ML3wTzO-n3OmactFVrJ^LlE7U~o|$P4n^9%g7Y zY5^);PEKcBBcv(@if&NVz<|0h)MkxPk@X>7#I?fA3ZQdY!LC<;`U_l7f@WsH;~d~2 zX1M(HmurNo89S$^t`*V~@dMeu0Ww_#3z+H4*9rxQZe#{YT|txLStq0qOUsVygk%{{ zPLE$FBq4ejtojL>>YjB%24Wqc!UJ^w8)&N#!WEa+2`OnDV^UB61q~~x25$jvhX=JS zIkFU(p~)H=SJP$I3uTK!%8@Nhs9`l{y^x0J6>#D`f+lryz0hQ+t}jeVY~UsaG=v*B z2sw!MgI(Rij4E|)gOHl&c5uA(Fe`B&>k`~31Uh}paHEh2Vbi9dmn7uEHVTZFzd?wB69Rmc!>w}jaAnOlYU z881#>xm8GsWfz;k`swGl3Ry!~eA|TdgirG*Fgr4pfX-?G6|xT50xi@1w+SUMzMsBf zn~*r;r|BoQ2`MrC=A8a!n~)c1l-y!FcqFK0yHF70!Rgnx3xV#@lHMU?CW@MA!0p6o zOw$_<3Q0__+abiqIBk0G4k10p{^@&m2zfFdp3b&YNQLphbnTr&{^H;Z3PGhaE9lk{ z*o|J-rcc}{B*k=ucY5CrA(`nXcM2IW9-aPcr;wHuqy`dnhwkl0u6ONrfg3Z+b_p49 z^sHzGU#mC$-Y%hZAt(>l8UamRhVKS1X!~88Kd&c0kCM@!E8|148PIzosV`@QY5bJ|M&^0ycO8cpeKB zT^- z?2{+Ag7!^LZ#X2RCbou6i7Nm!Jj4pR!5wtIhX82yX3rt;Ajsb%LaB_;rduBtGS@or zvt7WEQ=k}B&43SP{{X!0C@$umBj?r~fQbkK6waUsxd_6^5{4zhr*#+fcBDJ(g??}QK!WB>HoCxkTM z$D{;+da#g7XE{J;Mhi@we)j~pHOzQYXandB`OPPVEEOPE%Yuq^$n~rs7N|E3N*)SX z0(++OoDwo++%w(jl#rwSMJ9n+?4X__59neG(846p6@lQfRROphiz4KJCq)i$?TDPS z4xIu|pS?dNWEIr}I@Q|%wE0(o1+o!>)vr-kxi=0idjbPhaAGCqLxE5H|ID}a{k zf$j!aIsL*pA<&JOg6D;78IMi(KMyXewx1WWFk1^6AU6e%rEg#aHJ3nV48sP~e=wRc zbud9gtAi;EHWpqyUG;)cwk%XH#5dsKbc7L8r?0&rBrS|Kc+Sf0ICc8%3qnqebEeB( z6!I1Vx3?R>?QJH|QX7E-)2lBE88Kd-zV4!sHshMjI6MjWVS^5-Yv5q2WK1?G^fkgm(Xa(c|im6wG%o%&9pS>bvz}Pzd z-xZ-;#$D5Mt_qnjZlAvJs*nTYk?D`F3i*JJ*t#aneb=xF*7nTFo9-TKn?>t0PLO* z;80-)t&i`Yo_<3}K^W{GHVvi^jIgz_d#BI3A*3h)@kRraA`9ebV`fK|EG1rnzUh~5 z2-%tRgJ;jDFewSJI4CeE2!J{h93cCkmv(~tN1*Ym2}}rcU2h603Pa3Yz@#Xk06y6r zWMt({Ax*}~(-+?q3I=ti_#GJqCh=lqTK-!?9-!-{ZwVQ4G|Xrca8wm2o<8xGP$rC* zI-TjZ&>hgOGO!8LpWPO6MevO8fNRExJ3>u@JCRO?V*#II^7@XD9^b3TNCk-Qk{)G2`*+CHI8<7|%~Xd`}2; z^US+@LXxo49eD2xsWWb$Zh9Zw4GF(56s-X6)w5|b9{`_)0=lS20W_IskOl47Prn4x zvt#!+JM5K?69pC0xADb^maAVRnEfsm5O3og(h%q&@;pqBsJ@>8k^LEo*{CA0W%}NSLY9oPrhk7Z6wcT&-T#r01mo1{8IOeW82hJReHaV42r#J zPr#*2#uFh!j-{uYAxU826QNWgC=XI@fc8~Soqi`$L}9wcQ=t~d3DfsJ71H8ZdZ0zX z5p>Gb`=>%#Fkb5P#AiY#piTCy`ivJqN38RL?vPebXku6$$#;|AU= z$2t3j7O%g zcq635cx3wNH{izT$2Z`3-|$PwfTR0RlYpZPDC@r!N{8`Mr@a#bZCsl4PRLjuW_kyc z8Pf$uP<(NM7WgV~I)au;ADRB-9oTI4_d-^1wF@9>!RxC)+bm&f6W)W>Hoq6L5rUb! z1FTkoOW@V?i|>Ub7>`eX^i{)K*w%@U82M#aD+{PQbaGFXhsa&`f~XUZt7=$77As#0y@&{$Y&u79*}VY=fUS@UzlF_Sx9)g^cNvP z#v{{pzX*k~pI*`-;5c*J^sX;LplfE^^B;ldo@e{n3Z`)5Hd&UFPzx@;nM03N&g{|O|KBi9uWeO+{;s(&t zh~GlevY_`iZA)R4S8fdHxESA;qfN^xD5d>_VV8{J;X5Nd-kM zlfYfh>9PNWq(qAq#1)tz3tiYDvC0IB)we=2)6cO9J5GNkD$F}w|DTWmW9oE=e?lUR z;AoYeUhq$d9TB%6hk>FHzJvrckPa~p7PpK3f#cTnzmUEh$Xg1CfaY|BRC3_M-~?t) zZ~8A}&$ws$q5nb}9Jk+t+ZB7KfBG+^EB)c|WC2AX1<(P2t}G6YU@iw}QPhXWQ>}#M zm_9t78YL{xczkM=uoL6)se6T;VL`~oC=A+aTgWJ!00|t`>3Uy>1uHV8QK2!Mj; z2P-Uiz{wn39@Oy0{p03==5U zCV(^g2ha^;pqp|*UIo=WlR;{9z`=5Y1LD;w&?;gAI7l2B6}dp=H*c2XL6BN#O(X$I zHQ|y_E8xC`+9dFCx*fZ)v<=jBxHPC}n82GQfLNac4t$7BsJ5g|pUN&giE-L=4Gv)~ ze(+L%S6)WY`F~6TE2bxM2+J|9oZi48tjD-&`g#swY5ui+tpbh`pjFVkSpvTWrr+cc zHf6j#ou5-!i}Cn$D^6hpo{7u~oQ@MXvlO@mx~7+L3VX7F4!aO|HT@{3FzE8jXPm-n zjF+bKatSvx?wUT4OIU~T!1R4w!ikKhrYmp@D=5stlhdjcTj7z6W^9qA*-a@YBwjOQ* zFM)#;M^GN5mSdbY{WhO)7vrw!Y5ZWHP39NYV>~eZ5WjG!>jMtNav<=9&MZ7k(7*;Q zP<7=6EvE%nh{&Ej!3|2g;Ikyao{bTJc~)4TnH$uu1Qm3k)&=C4OVDf&r{e{XE=W1c z37St4xWXo|eELBFVSNc`2!U2cJOHTzANIf^aC|z4psIFA!zfV9yV^To|;or&3%Pw6kZrxbQ@ANb4K6Nc;dVbdmUUdkNvKjQ!KU zN(ieng72->kb(6pJd}9Nm;w|ab9ihDj0#)=6Q?Ii3WMfiTP1~UWkA;pfjS@u7(vzB z2~YzaGW^6TaAf*DNny}QtNc>J35-jp7f69~=R_%CbH17eh=B;1?8|zDY*dhOu!v)KE@YVR>E@<=xS zFkYN~SymW&zbl)ZFz9~QAURiB>Kbn6bQ;|-Q9fiu%zD+`0V|6(e_pj%>{RfIu{)bdn>L5tLO zs0f2rX}wkv1~0;5)n{A-T7(4}?OXv}gvBqgYPz1PFz8O3I8|W{RGUB-)q+wfXnpCK z=?heaLF>his|thGi+xrV4q`kt-Azr{k#W`Z4mIHt##UHD5my%mT>=aB{# zRt+Ww&?qTrtrBQhcT-<%R!4`G(d$as|J&Tz#-6r9ac?d4bUnV@R}Vq&@L)g z#|7J_hiC}<*3SS>YQ;b&j|{RPOMJj1CSc{P8cY$8rC!XCHL#GCKA^Ev7Bi*-Q0ij< zjT3@aL9l{$#DR9gvFbC{;4;w>G`zQg3W?SGeXqN%FXoW9CuhA6FX52OXg{E*7{jS#WDT76sOqq zD_X)rjI*Xc*Alj4+%sKGTUecA`)hDtbno;~ZD9j}>7apP3(!TpjG)!=Gp0|~7H+A3 zJOezrz%2q^jS8wt95o%EENBGHI3Nq!INrF`02Y))sN8p^L%`9@vF%KUfWRMNWaa9P zv!;PB)j<~2acrImv8|txk*Qvp!BJuQ3>{%J#{TK&b%br0esWId)fLWQ?4MqyE37IG zD<&1pm^7FinCcZL`U*^rZy3P0tJ~-cgRTON)fZOvx(H2}1t5b!3!}kN3bGlz+F1bX1yEXU z0Nu}pdhf!&Qyl`1LXNLaK}LMf=nKn$!h=hJOM{8Sj7dO&3v@mmcqiWU|N6pWjBV4! z41__~)7l#d8!#T2UI5~~nm*G&*o5)T^m7K_LjHw;urlM@={$zQt)Q8W$%f#PLQznG z3p7Clb{>}_D2P}T*ah}XzilXN#JFcVpOLVR_#yCWEAW8zMJ6Ra1#Sgi1!jT6(*uo! z)fkUVuP_2vMk|ek=Q19h?rtnx%D8m;USn{bbkA5gjInvTsR_8;NiY%iWIQ^3m5Fd6 zG*$K?VuVQJ8<-&~G6ctDi{ zxSg)R<@katOW@sfe_LUb>Fnmh5{&PrE0_y|ZsayI7w%*{F#UkJuo&ag>DSG{cKt9H zu97$a+9t^(%&ou$noR*!y&!)JES=tEA?(Nae)>%dVH?Ja)5R=>Eg3IO53m%LVLUZG z*HT!E@x$~PAnMfg!wwu%py%(DlZyyu6_31`VV-@?<$PGBnf+JfE&)4|cGxy|4lgcsVdQ9SEGCUTH7f zqz?%#0Z?ci0BKZ!$bvV(aA`6h01wdegQRz`W(nM!uH^uBiJJr1CFu^rCXBbI&v6iT zL^b=RgRnZ|^XY<)V7qM`h0~$&0=5a#=Y!fbeYc~qE90l>j85Qqv2y~)ONA5Ib5os! zjTk>qKj8#6=d+V=pw>}N1upO=M`rL00C>_L?aF% zh@T?dQT?=pLy3tOWZD7_BtI>1M>p+^yKoBQ(&>gC!d{G@rq_4~yE9&#e#8To%YS)* zbGe(Ruo~m>>A9ZBxg4}u8FV2dB$t1gzQ9x1NBoO`0u!jc$K`l{2izZ5;8frMwfDS) z>llwtZ}Ji@fuGP?6JNjZKwG8uGCqT@FZ&B*Qe{NkM(_i@ugLWr~1b{oO z<^jUmwhf?pKaj7uK)zxD1>OTtPhEjofnPyDU?pg`8F;{|vjKQm|zKWLj22k2@_r-p{QRsp0n|BPFw z7X%3_>EGj0VBpqdW-w<0sl`%%9|4_)uHd)`avJ*dgF)cYr@ukMpye6K!NNKmFP=6C zII0K~PoEwvoGb+8!FnJ}pvyi!$S6+N4G|V&2OljYaCy2{h%o4k+f5Wqh`-wF}7 zWn4C0B2?H*{5ltOWP+C&v^^7a%sxw&z~-R1pA@p#28x!-bWFo59CRz#BKr0)Kg?KMfaFVmvUNBSP3+dc%{+0s`QAf(@}zk`1(R zXYur`2w^?3iOh~i7#xp+f|0RM8obDxL*U}{brHhSZ0pz*K)a+CwKhXd^f_>3rFUB-vgr$q_J zG2LRD-uRDIY&uW0a4_TE=?T%oLGsH*xQ{R~G1oKJDKj`4WGiv0Feor-GD#@%Dlmag zE4&yjEU$C{aiTAm0ZHr+Ep*p2b@^a%;VMT{4xGbajzjs>(z z1dluvB??@U+!j7OL z3XdlV8#2C}{xeC~kL?(ff{4Jr>AuOrvg$7d6dA!fm=(kXAm%V)GiPG5usGwK=_`|k zBcz^jDRDz~w=*gTgNDw)Cy^_03tXBmogxfcXyKP4Y{4PfQ0+zQ+Zq5|zaUGo}}e3c{Mq3W_SA*>I*T&`Cf60>`FbN)8B`7?RRDEAGA>0i*=QR^NAQYV` zY;E-7T8jYaiW&uBN6-W-#E(;$6)-(F`Fe|hqlQ2+DAWo$p!!m$J7x)ss36Adaq3H* zUYUgyC_JD5;j@q6$#0(3s@`ojZVRLi+T+ zTwy5=sK(Uk8*+sWb$VGqLA-)Ri31e&EDC%IoS=mWpvc2?E^nSNXkDOoo^S@o9+3SC z0>#s3=LzS+c&XC`^3e4Og~d7tSbMlwM{@# z1T>Gv;0QX&-oDUL)lEr6T2b7Q!SUsi=>kQ<@{D_?8x#qP%S3_l6ptbsh~iLWSCB+r z#+O_KE|4b_3A@CA&Z2>?-xCJSYAS)&<_Rl+mKT6x2$X9Nwl{#z6Dd{@Q4oj316&O} z$KD310d2?!t?B~jRc?Wj=?2BZ6_U_s1RY$4Y!M_ID|q_0d^RRElukt>G7Wx}D7+o7{6pc01()R#H@w*@pgaCN$2xv)524v*g9e7gAQ0(+Q5_r zs!+g1F05oYz+%P(FLzIX)Ea{hLr{QTy#gtlg*BK!>VB{|GAfEF2!qx+IsOBw1L09V2Wvj`en;M~B=04cQ~cB0ysI(>H) zQt872j>oT6!fA}hrpHtZd)7~5L*A0X0xOwWkjf&kCUD6N8$$(c_TvQIFvSFR0JtP4 zQY+}lFJ=LFcAl(YGJ;B;1I$Y7(BctXnqw*4*VG74h1WS? zw@&w~74~L4GksRAu!bx+skwrVA^=qmpjHKF368Mi2Iee*dDHLL3P)=^WCB?Q+5}C& zs-!yMD~zY6o7aPf-b+5Or+& zffnH;#%I%&TZN7FH-VZTngYcNETBUzSU^WlfZ8eGGYm4!m?{)lKx1-_cR|W{1&XJi zX&26tfog*103^Lrr*pRnOA15PLsURl9M5wRP@EpoCY&V+KDB|78+2ll8Iyz~gCgj( z9|_PB`rB>7pqtd7F3f5dHrBs3vlV=Hkpc_obPAjc1T9fOyq&PsE85kVToSA;6TiAl}{B+hH zVR@l5XC@1P4k2U&_vt`Kj7&G{5q4)hKfSI;Sc~!W^p!orW{jt%Kj;ybU_3DWXOD0& z){}f#VXIZaJwd)eAr6j7pwbF76RsF2BszUjudox-(B1&@Oe*PE<#UEPu$jRA7Rv4FL5H z_e^h@D6B67=^?Xa^D#3pGK1D7fKv_!sIUEM`uT~%l8h6lznmy6BXWcdbe-fCCPmP7 zp*NVa6j%kCrb|u|)?qw8-E9(hB}m~U;atX+>GvlI%ZP0e0N*Oeti;O8uD}So`0)p` zz_jUtlflDiMw5lj;Y;ZXCJW1mZ3U}e!h%r00JI``(PUxJN#Z9a3!5^op8jvLumz~( zoFW{^IDdNQ6k%IQ`05H?Rwf4p*kTdTLYDhegjGbq>l!#5H-J{4fmVov&Q+Q&K2=x& zv9!T{s<1rc+UfCAg(Zw8GCMIS@;fmo3V=5c?uISh;00Y-37R`)a-0dOUpPVIRUDek zCs?N2O%WEGzJIE)fWj(f1y%u2F64mEi-4}f#!u5zW(n(wf))iSaB46yC~=!HaVUVz zeLsEqEMXJI&C~D90#7b|nX7PHe<{5wR0eekW-)W46_nD zFN*>r$SEg4iSYg$VLKJ*lD{qi1#W3GrW>I3OVD+S3ZNy5CzwHp=Q_?67Gaz=J!~#G z71qoZ23=mccCN4q&`^}-9fWfpdFCn)*Z&J#9=BtK5j`HkR}|KMZ2 zrgzLkPJSEa3F|Vhoqlhguq@-5>3`-4+c7?zZap7dZI{g#wgcsMPz>yvFDxzJ3ObvC zQ+H2V0zm^VRgp#?dui_voS(Wd0+$|zro$1-6?CdOIQxmF14Fg~1azCu_>|1hHhs77Hb$x;B_ zCc5hCs;gBB?9vLH0*686GNUFFgE_N>0yC&dz#uSd`lJ=Yri>4#pI9OMma%{Oww2&j zH1}2tt2#{t9Z5vlT(Vi%i1G0B{>{RUjHjnx+$^ljeqz&P0fFlr(=U7%5Sh-uMVO26|khNBU$&m-N{sC0t@d&)0 zuD?@Q(GBcIP9!%%&Sd2R-J-(?+LnGC?9w01N{lQH;QXinTHy}%Gx#7UBa?_Teo!xIEpyV-v(YtGJRo(kOCv*2p6WmP$g@3bqF|$IZoXLQSu>9&>FNb zYmYGWz_)~W!7#?-(|7F=mKFuAn*;5pPyk*10b2ILktJ|s`l~&{pc7$r_6jR99TAuw zARsF`J!P+O0OOA7TlWfUGWJiuzgO6v@#1v(eZrs<#Vqy-gAM_ovrkxyqqC(MJUBD` z+&6{0JgBUkXk2xq@#CT@<`Gdl`jJKx$Jt(ZkcxSrG zAz>B9Q`0>U2^*?|;u=)BgRlZS^q>?_E0B?a2|Q>D+Q%laY5KxL!m^BA)At_|t`&wf z71`Z+L0iW_hebl64BMhv>`z@9?t$uciV|CxMS`0*^<4)PX833o|AM1vbzsFrj0@nnIA%%OR`4AUdY| z9urovJIJBHrq7sy%Op^*6m%E>=-3I>ddH&AvkDKI(CIXivhMd6@eq>Cn*KyiMANdZj10V#rA{>A(N%wq;E z-{t@jpz~!|G?-)rx|l)d8y;W=7q(2GLkI*qr-xk~x#U!r)sfGcOC9SkBl2 z+R6%Q>F?kG&6Tlafpnf>QDS!GWt382P+$U`<=Dflz~nfA8GO$n%(!cpg*DVqbAXgz zVF6o&N3rS^VR;^~T}&EG7gz*NOb@sstf3DUVOHQ5c!9JR6OS>=uL#Sl&j5Sw4M98K zUJ+K1?qgO2jibVR!3esk5#|GptHOSaGp3hc6;>0*=EDtFg%g+#oSiOoO*ous$Jy!G z*MtpKP~!atYZhv}cd&tO4xN7Bny`u-Jf4rRf&zEGyg6s5GhG)pR6~#FEo=%* zj_~+h!KT2Wz$$QTdgOIsb;f7Y8?OsnX*E7?1DBBs{2-<5S)h$HObX0qOgq>Wm_X}! z9cP17fabiPUKcKwgsO*>qPz@nZ6Bu>-Vl}+hAM_gLmMjdZwQz3LXKT#0!?)ZbWT^k zDSU_N!kOuew}hn_H%^ziCG5r6J3alDa1hg(Gt+n95|-z=3Ud1iaGl>f{n;&H(9x6< zw}nkqdO`cym^7F&%orJXKnKk@B8@wMMmQZWoS9y5TUe226)2s7mUS`;^iH39TUbRG zBmx?vnZO9XWQ7TQ$qJ-k6#^~cXL4M?2uk`h7_$T}Pk(n?SdFoFy67EY9iDX{=YY?Q z6zHAqdq-G>v3GjW9pO;M-svaq2rGg-^ZJgkI>`AVcZC%sz#F8MSacb}92v9um>3wD zK!>beI5XYnuCOd)-}J1z!k|kaX51AH0qJ>nS6D_0JOrV^#A3#@0hASYfaVfGC;bZa zPFJ}noMy0*4Rj=^fC8hVNDgQh_y<-+Mg?X^jx5lHDGUM-X;8j{N>AT)PuPgDZ~EJN z!s?8D(?#zKFJkPQzT>{IE#u+o-|h=LD;)+e0$@;J64)UK&YhqpKBEFtHiQpaI1v3n zxSp|Z`t1k86)cAtxmQeQd?c(QsKBnknytW+r^MpOsKDaL=smsXp|CttANTZy6C_+1 zXHNh0P}mrYDy@kUQquz-3G;!J22PZ4V4OC6;UnRdjD6GhKNS|6p7>ZehOuw@zQ@9! z7^hC(_(V94ZyL7}Bgir~1=ehViPL4D3R^MFh*m|#w-Z_g1bzy0 zBQ1431S;a$9d|;C`00$#z*Fd&&xGR`Crs;|!)OP>F8;QdkF6qUXL8p00*c zqBA=_K`qgl9X~LEa`tqsSHi{$umW7vocRKi0<$ALW8VSsCY+sK`${-ScnPG@45}fR z9ZxWWu4J74=#{XJz#Lf4pTVra?6~6Wbh+2Uii};;9bOB61eM3eZy@Dy!W&^3#%I%O z-v~eC0Po0W2dx5mD{Ldz!Kc9Ncz`WSf!VQx2}G`70g)TnvOrDb=?C5ln=_uC{^PB% z93NOCvj)=%HYIj}6Vp}R3Fkw!|G?1x0<8VS^o{R?MWi7~Q2=BEJIL$?F!=(!Y4pK6 zVFkvE(;43j>xqDzq5(R)0d%G!vj)=-7J*~ao!<*j_Bze0z^u=>giDEq7jz5{*r6Is z3%HaR6nH_C7A)XyKZ^tCqyT|wOrUFWA>LsS0G)LVI@y5Pv4I^lMy&7wk4>u6+dl~B z*}SB{y6J`=g*BP}u}+WqC~U#lF@5Ss@HRKW&%!R#B|ZteGQOG~_eofVaoY6ePr^!! zuct5jB&^NYG5yjfn7U5JH`8Z-7FJ?>JAL10VM%NFOgk?tCfF z5klyvu!YI&_t4Gj>xGRzl3Kqc2B?dOIS+?9GsxL;F&>rM2S(L zXS&dDVQa?T>3+Y3rrn{od1Rexc{tJQR#f5Iw^lcyX01DCNe|AaLeXHReY2QFjx z{}c9MWMgs^$x>vT$|cgsxOQrfh%~5}6^0hGQjk>cxM17VJtCk|cIqn;L?O!xDHN}u z7qSl^h3s-h5kw&?Y0i8ARII`a*)t&CgtOCsF^ZrSvO6G!Y&??)QX$&`N`ouTPM^&r zq9EGE3`$iKSd>^m(;v)^Ye2hsre9zZ@nB?}F2gL+0xEwOF^hoP!{?br)b!88?ttZG z1O?Xvl#~x@nX+Uli7GHVf;Q!I3w)fe!XlEvxMO-Ri-?gBq*=lOT0F<>ID-{bl3ipG ziDSGv-GEg@0<@xwRm7X|;`FJkBA~O+cd?3OGd`QH#wMc2v2{W#E;|FC4o@=khIGS^4tt?8UKL|u?ZHm1ejTa37c7PeIPqPdTOWtxD)3jJx*eK|#Xco3!gf^E~Ea*9+iKAaxMC1TAuYx*oM5d;2X zOiCgeOe!Ex>|qf&I{hY>h#X_z^dDRzP6mJ8PX;YTC{_S1e+JJ9F*{yhgJftHGo}u9 zP`YE!a_sytS-??Ppm=%~w@8@+R2S6ApgV22V5I>_bNVzM5h)I+melD=JR(a}&oe!xfkvK{34vq#9I;C9sJ&OjnZ@k(u7lFCr`pH3c37 z%t(e!oxY!6M4AJtHFf$UevxV5`v!PG*G7PhRRW#g3Oc|Tv?CBSvjgh#3cQ~_M?fTy z@x%0Y0wT*8-%p<^DAEDCbyi5kln>JG0Ht~bc7gusi9#ZlOcVH~3$Tia^3DKPW}wB$ zAgO&qBG#al`@e-mWFQ+AAem*$bY)=?J;vG7gM>wbS&lI&GEAQ(D`GzVfv|`v(+}S1 z;vyoTi8^Ny5m&}l(>p~(tOU1#&U02`*I<$Wjcf1DOgYnpOZ3&Tl zt2;2Gz-=C8&;kC_m>@w6vJ%?tVFBgD58#Lfbyg%4xCA1nUy%?|W!uio?Fd>`AR*!g zX%Dgr?3nH=i3nJBP{4u&mq?1ZF@Bl;T2jPFWC9;3BSF(IGiWKIz_ICuQX-%_vPw$C z!el3?-je_ySjP#=6))J~S?UGojx10e`GPIW@d`*SG<8W&XOtERae``xCoIs8d}zi5 znF6^B9+Zh7dBKe71t?b_8J0SIy0pkV#p-nFZRbEGrVNG!dzuWKh5sj?+ctL{u0*O}CU2(FU0)C*sR^Z2D$7kxs_X({1HN zWOU&%4vq_6Q2P^{*3lzk0=WIzCNH9gJs?lWi%e$v$~!$)K}1@ffT7c;D~NzwnTHfa zEEp$DH&g=W^-Lv^PNr|X)AuQgC^Jr*{zy?oiTyRNBV!>i({y_|5mhvm-+8BhQ4&$C ze+x@Zphe^0L<4p^D7V2@dGdmy4J-<7IUpr96jiV^kDLHcfUANDVDbj2(GRXT&#)`8 z@G?6xD6%L_pQ0?H0ZE2@0t==eQ5La)sw%kf)kQq!Mlc0y6 zfpmb@V<@l*%$we#E;13MR$N0wjd9*|I}Hf;B82-xLu4uA)#>vzMPwM~P2a6462mxe zx`Y;(D%BD>%-B2KQd`8DeI7SxRJvjMSsf9D>9e&(1dvx(fJZ$poSD8?TSSWS;`AHZ zA`=-mPS4T-n{r%7#0;Baeq9lD##PhJbw%75=S{EE712jl1sgcouPfru*fO18PXshc zuBQifrnjDm0?13bdLp0=)Ds}0YxP8&8COjIs3#(4G!HZ=!>++}ffY0=!wMPefUgDt zEv$C5cURzWw08&1zkpYcIO>ZiFfN~-q%We$xO{q#zK9CQicR_=%KXc@71(tdT0qCw zfR#Sc7XjU~CSf3A&p3U0gn@`6*L285zpMh2r#BmjSTeqwzSlrRnepB9hXx{wVvV54 z1~o`P=lX(Ff|gv&n=WA}0@`J2Whf%g_-cBTp@^(o3;3WMcFz3)71%*{E3i90;LH-( z%I?VE4!Y#lK?!`1*Ez#L&m26xcmv>>m*(+E4a0*?Y;s}n;oG)$3YcoldQ zcoldA<_Lo&FPxeF&`d;@al!QeW+I7<^QI@7i)1j)n|{h%M455T^mpbWpj%nkEku-! zL6=@Q-r&koVga3$t^kUb16)}GU}4AxD0ascT%i4EEZ_s~K{+_q0yS@d&Y%ZH(>x0i zIZ!n1vk;MEoHYHeg@|_GWIjj6LMsL)9wu(b=8h)t-HUn*p!AOzl?2`F`GG4-g@Icb zybarN&aTM6Po5s87xU7h~LMkI`J(R6QHFtx~5q?qyUbTK=TFvh#n zbL~W0Oct>zFhZxCz=NKkTCjmROW-goXi+7zV+V7V5~Bh$Xpb!@3os}!3LKuUYLBpE z)$~pj-dlSSW5$Wo6&*wr881$Eb`ViPyBmG)^fCt#c>(Yl00y94c&r*s8Ulx=FLe-6 zW;{0Cph!S``U3}%0LEw24IM@FI1bN&-rk$;D3UG&kfi1=`5ls3EHHt!PLN{ z2--si8aQE8U~^;?_{Tau*jWU$n7Geb1hnGuqqB&G(QlArKpQhyK@&5egBzH#K>Z|E z&@Ar@CNriVpxNmkOj(XybDISmK{pCVyNKjFK=nbK4W4wK09v-?_<{-1LjujBv1%}3 zGbMF8m#c_0TIlq-is*4}I@2QHc#lD#cseL}1Yul=MXtPz(QeV^d;t{QsXBR0M<8ZLosYDuafRkVQe2JsS_GvQ=Ogm^NM1Lqv&j>U0Ya z5o5;5({nsTK>H3SdVsqdt35;%7#pUa@eq+2{1_KcFY*T)#uy;d#dvLcPk>0H z_+?H-W`r|92jrb#%@VjgT_sS&0d%tuCp3H>fTUiqLM(j1nk8^(dSf8i!dZb}Lp6g$ z)-$e~elti!mvP;6_Fxee#>LZhf<-1Uu9&_zSVW(3#`L!kN;X6!iLq;XLx_knW7qU0 zAtFy1yQX)AiY$|Z1U@?`URXfO;{>j-1%M8v1hdyn_X`uzums-<3R(mKQL&57RS|qF z6*E)4BLnDS2uH?ZCH5i(#v(_?LIoxT4oAk~LPy3z>FMjjM5c&;U{wSi_X(<_K$nav zuz)8DKTMAd7x4idg8*LD0zL)-e6cuWRoR|!5kJUD4xrC-?A%Na3ZOM<_oGDQ87EHvA0;Bs*Te?9qFaGoV99iyXc0qD(vFW7 zkz|}Py)s%vj`8O78POsdj4P)fj22NB2Ol8CQm?=UiZbx251`SQnbZGAi-4{U(~1Er z4UPflg0dJ9HO5)f=f;RkV_ywAW>?_ybgNhqX{puBpb<;ZZbk-kW(5Ui&=r=~}NB8ba513;_f7(pWypaK$d`a1aB=jmMuBK(XOr_V|dF=3oK{d|Im7URq5 zzY;`17aT|>il|Cl1%-|PsHR|2UEVeYs!Rtsrtc3Ik(oX#QAC>Y z;q+~ZB0jRGpf`dVV6l{A`h#R45e`rx&yfYa(`I@=k_aE;#p&@$BHBz7nWr0kmk|*a zxDHN60*aswH4-4}K^Kp1O%hqixNLfOvWPk3wCVlHB4UiorY}ntagd$H1?p&l%mvqK zkdRVf16}bbuyi_eiii&5vgrmXBAF5pmEhVR(#!xYm17fFHhp7?h&Vi1Wngjq^d~7I z8jO3U^QMZZGhUo-nku5ee(gxJfWSZD=`pDypasHZsUnIJyV&@c7+Dy&!AlOo!%fip zcGsthG%~hKH%t>zVB9-BC`}}iar*Q%X(GC|+d<1(*pRO{1ziQ&kF=SD6}$}-w4a1Q z0k%#9wE2ToVA^!CbP+?wXVZ07^2J&9f{yM2t%n2cWWKauGg>oY~-g3b#&G6*aLHA6u&5)6(E z;0aX62Mlh~T1+g?3=9qS0vCjx85kHCVnA&o21kx;ZDs}+kN^Y22YZ2w!caB?LlUUv zcX7IYmWY<|v z#$D5QXN&YQHcj`;5dp2Z%E}SZW&AXKMh>_!wI@d;oN@Makz5fA#@^|^xgwx5sB3aX zau}aYf0-+y#yESrK%R&t(_h}{-gzRRtzdb1BKb`Jc&Fdb1GhJR=ZUm2{pX$DoG)U- zcxw8Ad=b!v;kWZeCV)n|pf{{?Ix#59g4Xm3OqfI|r?1zM>II;Rtq8x*n> zBo%}NnzQvZLEh#toV(1p~h0>#r!i$&65ywvGSibbX}PMdC3A|eC2ha;jyL{sV* zy8@d6pBd8z&}r#Am_cVrgAPdL6=<41yF^5W@%r=~B_b+}d#2wl5piQYFkPio#6ln3 zz2XHWZ6?r#MxY5>1zv$8yoww=;Nn{tTwS~X-!35_aA10OsYnvXg1_L!RtKklEfwiz zTtB_BOvHe3{q#*`BFcQ=h=Z*92SwcDG7(G0UDIXDMe-RNr}vhN$TDu6zPenbj`7&^ zygU){>5dg55kmiXl_1NRLF;t66@&z)O3NRRf&L3aM)cXlET(kk5MD!T&Q;PA6zui3i;0KFmB_eY&8mE@Cq|D8_r5*t1m}3M zE|GY~6VuDOMC2f+KC^>zl)$O!3%W!M7?)4K*aeEYY2Dz6Gw&9e0f{(eIar*1U;@?r z;5Y-Vgy2-*R^SpiJYBFyL`?`5RSnD_HK3@P?%5+Eul#m@i-6+h2VX!8H5;097!*K_0Qd#O5>P|ovY`4JEPJ3= zM3Mt)QtEV!+lNJ_ALtV?(tf(6O(093fx()A&ykfIbZx66XI7R1gClz}WWIq>phN+3 zeDy$izwBvpVKCw$jl?a?Z~Rg zwiFatPrI) zTfh!wfvDX40IZT3qH^})c98W<5T_lz1-71%+mT0+$?-gx2O6|xHe&)cOPS1=Sh#gT zEJjDh=@%x5a0&H1o+<#^G-AdCI;w@yamM56ZzhOnGH#tNHc>>E@!E8ki6Y^Qo2Jj1 zD6&^;&6def#o*0K>L1}K)nVQ&@weppYQbc=1C%HOpM2-znUTfI%$Ao zs>mM3S<{bB75UFSi!4+zM<8 zTnaFep6SWcMXW#xz1n=Ycncxn2{=_2loJ<|neh)4)1FoU%!F*-6TFgh}N zZ?~KwV#LV!VS3I?5naYj(`U^TvEXh1O*4VkF9~>0zdKXJm+`}N?O7s9Oufsd`_B>y z6pV+S`T%kcqXL7#jOiO^iHNg2;^uapzA=OabfuIun~dY6$>3Rb6xAA_@D`Xnoq4th zd}Ct5^v3yu!W;&U|C<^F1RACX%oYh`y6|NB;@KiDa#NU@>cLeTlP2>CMkN-f|NsC0 z=MUortsl7XWIF2{k!Ta><`Gw3Mn|Nj8#A|c34oUMgI38hDl&k^rcW?(D}k1l9i&#vbWx&ESeeGP46^u}UzIh^RA?)vr+oqc=5OLw1#-RwBr~xfj6u3CO zb%97B5kJpTH)nIQ`EG5go?u(=}I$ z*f1WNuJevBMqUV%Cm6sTDn@~0OaedIxOqV*7`H$caLjY5ndpchf3n-Kh zt`$jOy1_lYFN(#G=_dE|gHbGIjJKxut`l)&ygmK=IuQ@XJJTiBi^!_<|LYKNWI{I! zJjm$yquvfGm;T2Yp;GBFnL49mKBbVjDy>xmJQxv;vdB-RYhiL=+%(Fe_*yy1=#Rr5i-- z8RMsa+#n*xcyIcd4I(NC9>+#7Z*DA$xYX7S5F=qW%Oh+)AIl<(YUqzx7Bi;%+|v!> zSd=~2fqVR*$#FB#xe~14Mb3~S3p8D!z~~6tf(AX3mPOziw;~&lJi_-sSU^%NSpqw! z&xvD^WP`|0H`pd3H~nTD3pY||Ai3#B9E+?PBAmeHf%dSPF--tn`!#_z%kk77@G%(i z({18eWSFjTPmhXckyc#^6@~c|d~++Z3)uy(aZjHe&mzt7enta0MyKzOXAx&?nSMQ< zMV<{5l>*nM|A}WYW4t$g|7Hi7X9_ zm!|JaWKm-5oSwT?M2Yd*^a)!E0`b%5CPRW?cQRPZ z^jpa+G8`XfHV8QK3dB$UpUk2Hx?>r%WnY0=pl`Zf3X3b_rRo2+i%5e4Zej|HDvBEq zra;{IGlfNsaou!mxzuO_AC(+5S#HDp2B80LO$*8jFbtct`&U&MXB``3y_he|L&FGsaJM+$AE# zcyD^lE)fmJ`04-BSdsEcd@ z&4nD`%5nrn1!z2kLtyf>3>JN+2i(&QGg#yquS^fjV3B2d$UVIvgGH3_!F1j|A`&2f zEP%)!$Y4?7fRzE$-)69=NUma5;BjKCQ3UNVf=oDodV1^v*QRgSBcdVzE(Rn)hv`ay z<^X0*kIQ5UVw^pFV=&`I-NC+BU?)4MAZ7*4MnY8X0{O24wCzrl8GQCR1L&|o zf!*w&3X;_kB*Uw~Bd}-s#Qh?E7{#UpH2SVj|FIvOu*DCEI7>YQm7eT~5>E-HihKHp z92P~!&gs(*i0GKk5q9Neb6j<*Spc*glNDM)UEl$Y6N2JdU>mzN;|(4KR>un<_c`9+ z$r89R{rdqCHIeP?7ECWdG7rEqFL*#FwHh82Q4`t0Zpr)uB=Z3*^MfZ#;MVl)gCd%Y zyQT}~v8XfNp1${>h!x|V=|2vND2l@(2Ab<#d6{^mxgDoZH#j6B!LbokPx1@IPY*aG zqAGO_l@vw-z zBHT_UMbPdEa6XV^asbWbg0R4x>4}F$%uTkjgU$A2_CI*D1Rilu_b*^kDYQI*{Rnki?+;jHP7g)Cx>cc(`bvLNYeE@aVS+%Yz&4^xfgUC^92tK$s5EP?-Q(*ur) zAbIzH5sLyM$QK?H(P7*;{oFATaIx823@KhV6tfhtJqFb^qL8{|?{N`sP^<-(u!u0; zoqqqgh!Tj~Rl;J$*g4(qgou_9tRhulb^O2wYV}>4UUfo5K?hq*ihvS3bdd~5wIfRr zq`>kk1sCrxPl)I;{a~9uuarfT)saEq2ix>hr7X&zS&sLmEHw?wfAUA>QkBOBm@`##T_pC@GWBl~>XGO%A{<2L!eil5m^zp2Sq}WwxGvYr{E+pAxHCJap0h1dE6R zf|!2do}SmuA~rq#ya*@9R8W=8Ef7Dw;Jk>0=oQcbIgr?6aeU6;1_^?>=S36{ehRH& zaTjicRGpxyCQu~U&|uKcy#(QsM-^?EUHX1Ii|m_Wzl4M%spMIjztwj zJJzw-Ft$vut7B2oYG6}fb-c=8#`Hr7boJ>6AxFmSwNC&4GdBx#uz_o?3Bp-QERHJ< zPd`(~BF(hm@bs5;ETE0m>h&zLj4!9V*0ZR{qt@1-gLy$aJ3-AZQ1o+8@2H1l@YVG! znzD1)oenUB@vEf!cB0)7LgYT1;meSd>74_Mw4Af#V@4l2`=dr%N_M z+EwO_EDE5~B(jmkfN{?Bu0|GJi6`8!^vn)gJqW&rL4i$R*7Wm@uzFr$`i86ED&_oD z5m8WHu4saIZgvw(Flc+#hb9(N#!b`Jnpq%?L}<%odS)|=9jIyYu!Tir`rc+1S;phj zZ#T0fa35t`&g!^|kz3#)_jI=w7H9AYEDxuDxG5qsUEqcYCrH!U78WCrV;{D#n1JYy zH${Y}Yqhe-Fdm=o-pb+wIuodsMOpDaXqXF83Gp%@?%ACooh9&;d-}~*7B!BiAZ4Ix zh@*`q5N=*x8;dC8HIT*HAhC^YEao8kRU3;Ri2is}M11<*TOy#l{<7Lx3_(FVx1A*x zDknUhzk@{(qF-dXX$OlqT;l9)aI5VDh}txLc?SzM~xmg`Ah-C>}Vge7)fNouc2%MR2)6F7<)(D0a z0ZamS!TBBB2%gx@0=iCqRX2+)nqtu8B-nl2)9-hK8&kRWMN~n-q|pOOBi=nMs*LNV z7xu7#+WUeJL}WP*fzlYO6J&-K^wueO?QB~GI5K-Xx2x|572*gjf>t#`acyxmV z=zxW@)AM>+G{hi&VR3L|0Nw1Jt;D9lqQK@keN8Wm5#y!l_j_546rXV`ArYJq)AvpQH>cyDKunUG$PxxO>4mTv(+6QtAwE74F_ByW zY8xVr5P*&$14YaA>2^=SIXCvHh%Dpt>D5m~WDo&8|EY)!B`SUkU9$QC$Y#g&Y8|XnZ;b{4XEXb)SiZQ>#uW9Pn--Xg&HQasBlcY+zPI= zr*l6S@nGCEJ>nz|rZ`o{MM;pX7#4#_)oAVm}zN1SU_v@?6A` zv1hv23lTrj&)lH-U1kN)?hnvVR|g~Lww<`WeCpbB*dg1zHh{!8FlITnfVROf3dF-q z1g(#r09xJ5GJWG-KF}JZi+lN`IeIpFeK!IC8uIwbq#gc258CMkJ2o;MrDC zFzuWk_f|xjW5QSPNW;$Qjc-MyIZvEw2FvkH=R3+L#kprUM3Qg1;ZZ&*j@1)dz_Qa{ zfpk8--UQ+Cz7x^qoCZo6OaeRkrms25XUw#VZ~DWdd`g^0Km!?!AYqp=d8$M(Q+AHnEpuhl{;b+if=1^d(tx;lA zU~~HZpCN2|@q3Xp(D7o&aC*%hzF1I;`Su+?4aUyteIG?+K%*_t z)+A_V5tJA2PT%`cM2e#eRQPcT#81EfQ3P~{Q|(GRBB8RYL8E`5svc^N0NAap z;CZ~e)4qdCI{EJ+vW!or+kQt3F*Yz|30#|=`CY_9e$Fj$f{ur^c|g607vL42pk;^W zryuw(BFnJ=qzF_8NImAWL~=Gi*gBB24S#^`3ituGEANL$4P)bUg(rONj2ow~c)}-d ziB$K3X3#;mRb_#u+ytJ0`a67};glQ9SxTUB63~HiYyzOFT!C5O^t7jZ((pkpc7eX> zAAgFdM!a0zBw)tW0qSjl4*UQ&(gapPZ)TgonB}-)=VSr!C@ttD{slWH3xGF4b$}0? z1SKO#hlm$c34+}M+B(4~&@?^omxzqOZcwlyjR{W|{3jy9_+~zA{8~%P$1Vo=Tn57G{_`yZF=q>NC)}BbG~_?mV3hsJ{iV4)91e6vtw+U zzVNSz48l6bmwYlzPq?SczvPqVSh}xOz!8+M*#3#gGd`KF_D@8OWhM)EB50~dTA~S* z?LhScGq{Y07KP`aMPVCAmRTTv`jUSlo{V#*zxyX5qX_CKK$m}lt}6S$kR@=29elGK zXbXztiNn*i|BI;d&tX^OcI;a=SwN9Zfm2}9^!WcG;*1T`%m0hm@gD_6G$_W{^cfih zj!r-FU&Kyw2B@+|sz4!qB6fl6Q>8`a7(1t0iMlZEm|72J&l9ypN`SDc_U_boAld1> zjG}57((D3vr@JwN`)DDnQpflD$MFvzFv^*SqKODri10W>_n6ezv zod_u=K+_=LVnUu-lvDB`DDaS4IuJGN0@tTIGJ{=`z%1%whHwdJ>k!164;-)+h-2XaaS=pd5@CeP`Kk#ZtTADN~bM(~MGS7aA`#&HXDmA9C{ z$LahWqH>%YKs;W7k4)30y<}xL-z*1Rr3T{Kd&$aj-0STSaO4sAIK6>GRFNArt?3Bb zmhgppdcT*fI_EJa1<)~NU%996N9Ddp<;r`@DhTai)?fmyOcy}t3iOth)ju)~d;!Bp zCM5<)K33uZ%~(R~rvwFN@EXRu>)SwO!AGX)v%O_yIbMLcAcx)O6qVCE06K6CM8trv_39^V&;Nx^{E>U^Tg&;k0AfFoe$jWhC0!e~GrIJfjp5ylQ z2GB<2kJA@`lwJo}#tKsESimR8(F|&6f_9C+14(`bNrLR2{?13%MEw(Jf=osL;Tv%U zCh#OCs|FKjpDQfD3b{q4IeS3Y+Gv6d&i0j6WV}4R*H>1X^90BUW{~IsUs+F1=osKf zrs;xyvTB?cLAPLVf@JOeWc4^#f(8;eK-^M4Srg8i2U-Li*+JZmezM|B@3^O*@{_fK z^$$Kyx8@a<|&bk z5Fl&B0dwv2jeMdKjPItO;uF4u`BfgtdL9=8lDJolEa5| z68OeF-7r>Gm17%79jFA^D=liLv>L=i%9IdY9pJsvp!15paZi_(5mn?^4^jux9=J>l{xQ#q7YQBOkWr$8_4v7 zdwP_ts1@Uf>H6`qvLLQsysWY82J}?KtN@C?9UNH#uehhrjhB^>-GZqEbUp@D$;EhC zU5=fgGyqC2d2*tf*ffBKNKSBM2|Q<=?v)@bgH-P3CCD0?Yy%bH+=w&;2@4L;WXdV< zULX!oJV9GR%#JrWKncNKUQ~|r2uLR=6sP}4kX7ZJ07|0Z6sDRetHN>nR|ja(%g5;l zc;e$ zd-`q#QGNEc9gxL39~DGRSx&PF9GgC&M$Ulg3-|OGMNu`z!0B(RxLA)Ow(&><>W!>c22FF)bt=FQAdf7kd5!4+6&aqWKm!e_`=Ptz&u?xO3;G) zs35eN$L$R2gFu#>O*g2M||7CTs?iax~LpT__n$z=r~VV4N+HV z@Q5(@TqPC-MrlRRfG{&?JtLFAoawb1qE=bgJ;=)zAj>tFY(Q&5n88=DgSr{hrcW>sb&Na82RT*l z7-)Y#E2wn`9u}DZVlq2UV9gS^!e-9=g9R=#19Z3%sD_0cNwW%k>W*UvYnB<)0+1Bw zbW2b?Eno%3lfI#-BIESwfrg?^j4jir7>a5!Hcj7eD4GRY2&!!)YQ?x~dYX}FFJtfY zFGiwWj5DX#8;e#l_D%n3EUG9qgB^S}6}u7(iv#F*8c;pwxPdK8VCHla6Hy(;nbQ+Z zM9mrdrq471dwaKus1f6=>F-QLK`U+?O-1dvU^A!!P18F~MfDixOy6NDDkI&*10Ah{ z_N|#g!=j+!DuKS~?@dM38Rt$HHxrd-oHyOvOtgscTci>*Wx7^hEXu@-e;oIKstTJ)m80w&OU=n`#^{CgYJHTOmcya(N$p1c1$Q#0?nW? z*@=2H_D%P*6P0J|pPpwYDkb`t9oB>AWdyBNVsvB`csG5nooFKCi|POEL{-^eu!D}n z<(a-wNJwJ3v%P30R*8@HqJ^T**cF&GnHdzptC>K@(<(3uJeywbAgagscKb31Q71;m zH`8A_iaImCoUZF6s>FC}dVrJY1jZNBA3BN3F}|7RENa8}cDk*zsJYlna8fc+V&Y{2 z*VDV4MSYpRvQNM1436xd&Z6y%FQzxTi25)-ntskj)C45S;wl=;^o4zT zva6_t=p%O6R0+67V04re_{u(ggR5u&|B7L2E+KlTuH7kkMLp5p=SWd$um0*#q53Y?nm;3?`Y{1RNvF+sP7 zFe|VLJefYvQ&fs^!t|Y2b7EKg*2rFViy8$%>-c6VH5ml9Y0S;Oo@VV899hKmF z6Bq^hrziP{stUhj2VI>AJ}(Zm1=Ued;Kg*tukv!!cle0Pu-sx*Vw=t%CMq*Mdkm|`WatQY3X;cq{hp^&FrAS1iJi( zQGrE?Rp8=uCO^?+u{Yq9rlZ8n3%Z`25j6g!A@F*7wV$Xv$hX;nqB_$*_=y%VzMP)y zFB;4Ea{57kQ7y(N)8F`uN-|EE&K3X;q@Vy%b;h&Ps{%w#7%xs=A0Qej0WoF%$TFJVww~ zFM%i1CkBC|XHAf(ALGmEzk@_o80Sq_2o{wEnPwj>YRmXxdTp?%3**b_hk`|SgGB2? zM3qF~Vb2R{m4Y@G3%r@WHAK{c@y+yiA)?Yy1uEbhzrh=$83o=U#-8c7!$q|jXHRF35LE`5q!S^k!+2wQWQ3?M@EgK_`#*O8*}jIXA9M~V6ifc8~qDKR-VC@6pk&*`h8 zM2#6YOurW;Dk}gIf{xuuD}lD=az~5m;u_dvOofc=Xk^1Zu)6FbS;W0Tt*h zpkuTR6rhJ87-WIYKVbwp(?Xz|1C*{o(>F{4%cp0>i|V^B^qpuniX!~?o) zf*Ex1`t|8538J-(x3(`z5N%~-yfIxPNz{t_7>grQ3238_z_aPONuo)NbGBaqQA}%i zroT3jG?<=}BI?9_msx?qQ6@`)QDFM?jVYq)91mWB4p|X6I{iV4s4L_C>1wH>@{G@? zyQPYnG2WbBn<_e=am94mG*N5D7uzG#L>(D9=02SaIv-`j^hN2S?u;9!ze*Q%V>~q7 zFhf+H@#^%z4ACgLC(kAeI0_1E;DL_k^D-!KDR3&Vg3jd@csTuJhNw2j50Dxmfeq9D zWQbaV_LgRfS};DEo}VeI#kha^tV~hRRqjVXf*+^<%@mbjjGQi(CF;#_1GKqFTwufW z%q&qW#y!)QWQmr`Z8$esz)=XrLQc@(){Z+EvjiSax6BsR<~Ryc!z8d_dQP^eB;%3k zZP}tGjCZE*&K9+1+&KMvwrB(65PS<$&44KrxuFF ziQHm^H5$O}XHc?TF#UR==tag?(^nRW%5l8rQ(#kI5m-O{e39r*rjAF`50;4vGc`P# zKEGI0hGWU2$pQ+T0!ODGE*5oXd^4T9L{yq_>vZ)JQB}sb(|t=s6-D0hfp#NsWr0?V zF@Ojbf%nr}%SAO9KTKa&F1ld4cd6(U##hrHloGH_nni(AVDt2^WuiumTc>N3i>iRE zC>K>hv4R6+1rLf9w;5ke@2WsHeM5!lPVtUMh7G@hT8xXPmsE*raZLC=S-??6V8irfRiYk@8>hdo61C>o3KCR6 z2v%2%$}rxaKBHRHQsoc}$WaDLV7HklaXZd{jK%0cx-Fo&xR2AnR*UL!Tm@}9a^+>1 zp14y~c)DGUs0hb3kiilH8>UCqh|a3M|tb>qJGS|E&>~oPMW9l#_A) z^k+4qRg90PC)SDvGCrKHP%o;_aRrpP`2{vie^m=k+!O0XRi=B?i5f6|nqFBa8p8N^ z`nfv1%0nP3Wu`MVi0U)0m~PY{8o}}W*JJ@lK7kF>CpCZ*=Gg|(IF4^1L1BRn(~TR! zf_aUic^u6>Qv@7E1U5{6(kQCPcx3wTBcfW<4Vpx`81GECZW49J;-!pEQJ(4RnnaBl zKTm(yB-$!>?DiA^M>$ZYf-Y15?OFiMFh5`dWvZ5DQGJf*AT^*&wYyo=1B;<=+C_P$ z>$ixWL{7x~t)fbd>!%yFimE}~B7US;eU7ey)jMuh5 z?G&|OWZXYpu3J=r@zr$uZcz!w_0uD|MR!X=ld_T|Xqy@N7I^c{u$}pD1YWrAfbN3d?ssMfT|n zSBvUSKiMxD!EuPikwKBeamIAb38Jcu`=|R)5Y1xx!8d*P1krk?pM2YGCW^{43N<`} zUH7D*;>eNZ*zst3?j+GpNU<&@aCG{=NuvFXTc`I;7TwSEi*LIB6wy^ozxk&Bo+7%F z;}1AUJ2)T?+&@E9ayr{IQCZ7wZzcTP_hP>>clIz4TMXe;Bk>2GF; z$}n!-&N)-miV>vPb(Ux?Nb!+bqJ|t>j!za)5EnQ){qHQ%WpdkiK-oA3bg&Ss;|EZQ z=6HfL%TXV6n32HN?FVOzer4o10n#EOaCG{c^5B&x%>Z94B_uuruXi>?Cu^x6`L z$-zrSRUomXAaHbh{Zdg5M#gQ^JC=#cF>c+ye3_^{Bge9zlLZ_>h1FF<5i#jr{pRT<| zR0)#lganRm4_zb5%gEWl4?VDAdfXb(zbfF}8!wo%1h(*iMvFiTMqcotWybA2>qKQ4 zA(?Ocy7i)u895;Kp5b2}vqqG0`piwDl01;GzQT`i%7o3L3XE^3Z`>^U7gB-T;0M=H z7NAJB5qLlS&=z=3+|IaFw1|=M#PpJFqE?Krrmx>7sw}l19MFGQlt9Ul6?E5tq6oCE ze6vk-FDNm=>dFJ#MbkO1Je(|`z%Fofy8aH)X#NwRbjS{kttFh(KkgHinJ%zXlz;k> z9ioaH9T3Hh)8Fn84Pbmd-EOC-7e^bYeAN)xFnz*KSoAZl6cw3%WS6M?bb(!>@=SmD zrW@`O^$~>Rn-(7M7^A4bU%u&myF~Sv{_#!Uw@WmM@xXM&-JB z)Aou6@ig&+3Y!)-3@Ntht$RiJ8Jnl~?h=)rzG1JZGUMjySN4kfaeM+fL{4DCbgg}& zo{Sr(2gXPVPjBBRn#9;N{nb8EJ;trmCHIS(GrpZ3xL?!&5?!F_G*%6!3Q%;_2)v&@ zbw4P&w(r|7s>jGQ<kznN6*(+AUv2?NP#aWHLC#&lrtus|4XC05Y2@0&qrj@kJV8)NZ@TPZ(XiP!>HJJSu0irPcmUx)D7iv^-`(_M~=YBO$}?r=;rMQ+L=Q1j#u z7Xt%mcoA}ljw7QxFN*?$R#1BK5kC@pkcbQj~H>1U6N${PNg(*{}#Vh&n+ z4Nf|s%TYN5=72j7j2cWDilED1nLw!=bXuFh?dj4dL{$_PF)J|ZGT4BwZdYIuxWfnP zi?ArLyYn)FJ2ZEv$Da^&WxO|i=?PI=#{1JBpAhY4d^$br07)8J$^B#MNJrIZl8EsG>}mM5`ili zL5W}uqvN{mpU#MGVq|QYe(;K@jBplcNfYFt4ra%L3<@j)t<$fZ6IEtx1IterIWH>2 z*g9SPyr?o``*iQ~qE3t*)BDbgf|dhrI4>$E2yWhjnp@1E{tW2!_xtBXjTF1UDg29& z5*zp!#tkB%BI!5iNIO;yrVl~_-P4UOh#E4sPH(&_Dm=aXf~Ww@#w8cPHa6Z66`p`Iu9bg@uH{-Oz0Ct$nd79@N|hw zq5?2+*Gpg{A}%3JodFSAjU@CMBJ>YQ$nG*&W8h`DM&US6Sb`3m1g&0%hUF53{PZUf zE#HxZ%&&k=@wx&xWqKDx=;3cs;prQ$z+9@w*gE|Kk}89%V2#dKMQs^-L4gRSKw)<2 zs;C5G-}Kj4ML|b7fWt=X8dzxKMN#4DVGuSrAetcT+1J2Ik3*=>5GwGpsPJ^7>tK!E z*F|+0`=(c12di5Gp}-D&3{mh0BBXHxtPUKd88^UM8*hO1tc6fu*SvwqfL$YT6RgVc zCRkN0gzCI0swxalS7Hh*j?5)lN}wBc1&&TXcvIAfd(SCQe+o2`-8lW<&BuDsL1q++oIx-)Qu#k29euxTQmoB`sy7~Ee)_kIia=G8$oD-{~!cP@D1Qx z_d-wsbkacnN-;Uc*6E6OL}jL*pD(64{rVkIK_ReqBpVtTAy)9+6?K+@6ogR2kw4CE1$Q)2V>cTxyQO1YU{U3!GL|M=L+5H2)wp zz44)_*z~lAqQZ=mL4hJWefmRD8&LJ!@JQ5vankgSk3>P?b>Wey=k&Zsq5_Olrq@3b zHAIn#VVpP}oRAzIi}EnGLrRLw$D$65H>a<9EINaubJ-L~vkWO{rfv`unZDwQs0qi( zSJ2Usz0<^Gr*Hl*D#8icjJ81}OJL9R3;#t$rziA@aZR^5GsOJ6IYVQH~!NAfbxkqLBGBG|oX;Q?CA3Gpy`cx(|-V&{c>2kuIc2&>}@LC7-fIn&?25CvT+ zCizm-P#BBvrpLY%6}Oqg5B39On3tzXK?sT-|$jYm$7sDy_cf8 zsvt*#w(JOHL3_GjSAx_cyUE}c*iBKdL{%6&r`Nv{RbZSueZebHIsSS43LGH&p~+(U z*;k@gA|OMNQ$i1~68m&UV=*;uR!2};fjHyrD^Y{#HLpea80Ubr8AySHO@T>Z9zUon zpv!O?>={Li)A)Yu?%VVPMNOoO;mik(R)#m>5UgfWv3^<2Ty@b_#!IFczybU_o9-Fx26kz z5|x{NrbMpk|>@>vi1nXT8Gb~{EQ2y2YeRwfVhks96n%|oq;Gh@L5z6;xf=s;KR?N zW{|Mv7dSdy{tL{VxnJAZIKs{ zrD2X}bquVo0>zNjbe3rM`(;vO}`^ABaTBH*gc9{hO$&AaVeJ{Wg8?H&G){ zo8+6Q0psTBir+J{ zjGfar{sg=7$xl%Oj?Lis_yg5y^h;C}YFZ0B#KozUK%DKZ|JYAt5M zcxP&Xm_6ghsT;)X8JA4`Am+{3Io*a)%#00G1POeZUco3P!?<<&R7Npf#%0qFGm5D) zexCl4Q4Hh`9wsqej*}pFC_)+>pw4JFlUN=HI7UHb2P3nX0pqplM$BRf9M>mI5pV>l zyx=AVQVDK7d}kIDV4OFdgGEf1@zr!S7O|a-o2EZt5d#mau}y!-DyBR=n^nw}a|3>I!U_zMB)~T+8Wp zTw;bCdqFb#C^A7DJ3t+NP@hMdTg*%H7>grgwh{|ya?p_jv?_>8fkogypCa3ILl!ZK z>GycV*rw0n7UO}q5|s8gaEl#bhYZCmnSPK*Om?~fml)slWjtacj9aJg<`L6pd^`O$ zhH&1uu7n5h~oSw)JYmdrKpUW?%%h)*mG{2ao)CzEY-@~iK z2HN88*uk3xD$c-7SUCZ(7C~t-+38UNV$zJ8rB%qmrJ|Qs)RJGFsgvDfFMs*2`$uq8=zDgME z8L&;ygvIz7H&6d240f`F2-pqC0aPI(CdIOvpNDz+IT0~2mKBTwt0C?>Dx`_ zxX~+dF@00e;sr=?#0akLKogU!poWtGq9TOIDDi_T7=A}4fm_r4B*dy1Z%scbA=bpb z;o@Wg&=e|Y$5WW3m?h)7>EHygR#Hp_#(pm;Cd;@UBCaJRCeOHGx|b9HcaP~69Y|JI?IVUavT8(Y6)zZ-XjMp z52vq_69bJKB`g*bnSNhU3^e!!jvG69F&&Ogpv&t71U5|ns30afeW$XRz;poxF%iZ+ z)7Qw0nR6TlNrNViUdw|m&{h7xQnlOsn5$Rtg|u2NGHGeL7zxsn*@WMxzb-ByB`4R?^cvRDYmmws56%}q=UTeudoDNw-DwCKMV9rTWHs-h{-ZepMF+FOp38{`U@4YaK@L@9aP1{8Rt!pP!&@a+yts4 zKY-SUI8Fo|SovytkE)m+=&~kNF%8hv%TrY`H^%GJb=1UqMGmnr)r0bfff6@}GEw4m zoH6~ZnwTQv=IQU%#NrsYO!re4bC%e`51Y_t2Q`Lv2xch=Do6_KpT1IEOaU}Gb6Q=j z9y-3Mp~Ma{O9yOLjE0yoM=z-20u3Zj*8tZ94>iO*IktfWL4*7VLAmL(G{v|W_fKD{ zDW)Lv1Kzk~RS?u*5>QlubQ2UL1h!6ps41q+xMe!8mRLXI%jt8q#F7{rr)i66GB!@v z(iSsdd^$Z^Tg+Rg5uE(*fJPEH7339I9cMsB-ERmAyq7F`b z-i)uOPt_6AV7xVbpN`m6#{JVnbj8e=7CoBYqbsJ(zTnYh0ewb>=?itmgr{HB6_aIr zx&6JaST7^f9oFfJx?*?nV#Pp_bHxLtpuuU1a zO}94^Gnj5-C?>?Toqzgy12Ns{rG{c6P>#X$-G*WUj7O)RF%+|9Yyc_No$l~jl#NMX z2Z&?B_-1;Ek(dVK*6DMN#H^LJ@xXm12nu)wNL;EYaSCkbQQ{D|#X8-=NK9wCnz5J? zn7#Of#|l7<1bbL?LtA_U2+Tj60{tn2Xu79d2zF zaNIwAp}Ck9usrYOi#OqOx; z^a4vU2gY5~H(HA2Fz%f$XC>CbxNrJ$D=~A9#cQWP%B+Rf;ACfDEtbi#7bK`Iuwgnv z&}6#2jTk@U{^|NQVhM}~rcbaDo6C4;x}L3=1mouE?zUnD96g|g2YdoYr|-8F>*ik4 z3o)2SiGBKo1TpF91$JVfHe#=xSQ5umkOdY38>Y{702hk;9mMq54+v&CG78+8?q?5b zEKWDD7BilH&|XX*pHa5c3mn8$Ip(dKBH(C`VjifFMVMqVJ;PBNx4 zo9PJ8bVV02&FM#7#B9Vifev(TV%SPyO<@%29N}3E(xw*Wcpcm zF;k8iZzl^lnh9)}&gB6vw!tG$kse|?9M?fwK(ju*9^i;s?*XcHYXU)8ZwaI0jOkxI z#oRdmf=oCI^591=P~rodz0ga{gX8r2DFTj0pn4D?;qNV`Ep#5_asi~t9FFO{zG6z# z*LjQaLOLL#(=U39>2Q1lc|=)Y!*ooK6!^fLI(>tWm>0)CkY&)(Ou6X_zED~5>B+ug zh8zz-HcAU@m_ElB7FIITKlzF&bNm6xfZFb|e&DdO_Y*T=y8}9#DH%(6s5R-?@ z{D})RPVa#T9t#ljW_rmtJ@<{M&a@ygZpKa1y#vL{8TU@Nj}?=ielJiAbnG!tkXVB7 zK2W-wA(*AW1zG~ipu{Awe|mk880d@@pI|ZR>5qfN|BGa3r z#9aAN)CjDdemzPo2q~7GqQ(3{bI_Bc#S)ns9!>uiEoRNLlLs_6`$rf&H#;$2OoegN z^rRRubHacEGDfTv5|eg}o2J*rirH|i1=UTUhQXm&P|lgo6DQ^-zKMs2 z1=K|Wk1}zC0)!jfxXz3dTful^dToT5-SmKXF*Qh_NKaRY6BD0q7bwQV0&@3s{x~rW zFjp9C5a>22L=a6&0H<;OC^6~jwh3a~px|Hu#nA-NVACc(G|G<_yKePxE&QpR)B{WHZR823)k$Q0Yfcy_vSmYB2bJW%VjgBNtZ2Zv(| zBWT(f)ExoujoUQ6IZMotshxlN`dl%c>5sC+#0{}Gqkn*p1!o1#kE1uEr)y=4=`tRj z9+55P$JjZ2dA67%8HPyafDLia5px3_TazQ^%&`I#`k;kHH*>_ixaNWzmTN?m*rvbB z6;q!ck}D?7_-cAVu9z+3+v)3a#mt#rvw{4f%M0!yDuLII3(T9Ym?s9h%<^5aSm5-I zJTV85@r7a*(=+qMq^HZ|i-|DqpKh2hmc#?8>47`#GY>H-) z7|-3DUr-{ZuCfn0Rsd>V3xZ0u4?(KUY?lv^x#r4F~)7v zeagimrt4IRNlm{~DrU&oIi0UeteFF{%H@pU^aa&oLDM%Bh)GUoDHjujgvJ};EF})d z8PgTZ#k4@7;ae`Y5ajv~<=`-os1U2?fHZ@a@IwsHsS=Z%&Q&SK&$wl}RHc|LJ$-Slm>%QH>6dE7+!^Ohm#PzU zWjs1P0Yq(`zN}8nnsM9oM|EPVEZcbmZcXQ|7c*nrG2N+NERpfn^d3Ms8pRwL4^OXZ6mw@h zJpE9km^$O>>8~2awm=4r83h`rFKZIh22BoLZW7aBoHDIhtb*fD*AxLqZGjEb8=Ap| z+4)v6@#znn#k@K8FP|ddXeqE^x^9P<-1Nn*peDq0u=LaxF&~btE2e-h<^W5B1|`8V zUaexr96v!CtuZx*Fz%nO(gtxsV4GMp$7zrephgPVhD&V-8${c|O>n(-u_!Lckf;Kq zz~t$Z+QlYw`~v9$E%emwK)ATCLrjfv|MaaL5aS5F?o1wFXkcez*03^A%K*avHDfu}@u zxG#djUJKOlW1N2Hh^XZB4Siy)j7O$#>jSkjrh;Zg`TOC9+4qadFt$&R=@*j=K5+uE zrr7a^P?qC?6G)>DJ7Am-LXc612H`BnE$CX-z*K>TA(ot&ezsrCgmKn%rU_!UjIX9U zOc0Z{d(DPem;|2CR08co08NuAu!6=k6*wIG>mB>*LCbP@9GTpt6@)-ToZ!sg!#llj zqL}9NLleX_8K+KvGeOLnaXN@Hm>w}vOpvj2de%fSAI5Fdw@(z4<=oDr#O=rgItu#k zM6rCPUHsDvT*Q>77fcdU;e91yr@yS3ID}&MtXf73;A{_MD;n+rV)Sw5tHr&5)ms@O$2DF)fZ)AQ{kpg9Wp}-aZ6Ua0ldVnduU9 zz=IMtbHo%Gk4}%9Bi70Bdf5~KM^jh^LvBg=%mtUNlje$bIfG~DVD1Kud_p&VvN}RW zKbaiCMF~vB8-B>xC}<6bBDVr)$ZX;C_<3S#`V&roR#3vC9X7=QUX}uGc)bu*1WlbY zD{y8ja0pDFzGa@69%C11C|i+7gJ}YvB52PKi@?I^0`tYx1TjpT?mizpl$SamtPPrS zVM|(i7>`bmSs+%XbeCCyAGEa{(sGvsFS!PdIV&(a+9|Loa0?up{$+ufB4g`x>1AS) z)AbjMNipu9?zK=%jq%j zzK_o$u>!fZps0}sWghs_LKX!M(DlLmpf!oN7K!O|oCc`@jXn!576WZ8(OwKrwF!&G zK-*(hfOxm2-(4(b!UI_m4(bT-GESFs7L%TCu|!OR@$U4*C1RG#izYH&+~cC>+_R-kJ-G&S_C6ia42I{nj1F?+TH;AJn<%~y#T!X_8< zR)HrMyH||)c#rRkb@GJ37*FPaCHr-&gn6Btn z9?-s0aDfH72@$+LW$X5$)neX^Vq3x2%7b$@L=K#{w_jc(mcht)e7em#v1N=WraxXM zX2^Jay6AebD8`f13)YL}F`k%yf4$gR#uL+fH;DB!o|rDXQEWEjiRn8xicMuaG2MTY zSSaI(=}R|>Rq&qRQDgyaQv)?5k5AXyEH<6-#Pma(#Y#BAGR)kLAE(=I5vyf9G5x_- zG4bhFwun{2*r{8^svzvi5K4S{_cpN%#uL-OY!k~Dya&E+iN!&IMS)#`NnkzmbbV$H zCC0GXe7~{?9OLvP+;X~Y3q`)k2bG!QwYjksbh@Cuz@h1x`^8M{LC5)OFr8r3U{X*5-H=l6CM|G`NrOqkar?1m&|y`V zc|evuU{qpdaZun;U=!#DT|Ubu(8H|2BG5bi?tU>Nxg#7PHE(dK(LErhs&;}4q~;4m z4Xfi*2GCvinC4X+5VPZ$exU)>h)SKl?|_&Ruq%C~@yU1ZT@o&mZ*4vV8F92ARIoAIbaz)?p4eghlmI0n$hKo-aeI}DC16tV=; zrwbkulj1lIQp+fiI-NC+T@m6S30~wor6x|VIwWSqcx?LGLt>yi(ghBSnZ!Ta-T~US zR}7w-0})W8LHBPcfi7rcRbVp%l_ZWA7_uCHfmE{z6oWTBDzParf-Z{%U#IS1#uVYm zs0dn-2D)ahX$Sa#l;Y_b1rjN$P)p$6U;<EE-Hmb_t|U*Eu34Apx}ku9Qsyee+d3OIs%0=n|<1_StBc<`;a5oSyo3M`H<7_uCnfYb;G6i_I4m9SfT)w(I|Kw;rbnC*Q)1jTz50ZhHq&RW=_^i%$ub_Ae(Z!;D&IvWDFrUj z@gR%}90J#-Tb>j%WV|#z`=pqXA^7%h(5g%YHpkm1n?YFzwBHSM6Db#Xzc}Qo2QJWI z3cJ|W3M`+#@1&R)HXPpsCX52OX z+!-+)##PgQpAnOjy2#`xU+BmTx;q_|bHJzQLGM;JJ}cJ2xNiEMvtqtt+d#+aDKTp> zNoX*!fbUIKVinjrUH+UH=;W5KLQUp|~fj1$;?!7#BQA}NQ1t(~e0cbm}V+T`~0vG7;}$KMPB9n(cF ziy1R+pYCy4%-d@_?5J)Q2havwqzgVl>sS@o!I3A>k90dVJLoD^kl(DgDsRLth_DX@dif&{r2l!BQAU^*+znIIP^g0Bf@ z2T8JnMr)Y`=JSECG3P;v8-Z*5;?w_J72{{zF`eg{n6lualN|z%Mvm`JbO;Fi5aw=X zn!1rAjq%=e2}TY%#`V)5{^3(*JUso?HL)X1k2tn(xGomP$oOD7`%N)@wg>!*tUR*Q zV{eN|P7l2)rpowWdi6~)JH`jocij|I5xdK(z~Cs61uk?!gX4-^3fux~rhmC9rloS2 z5p*Do0+Rxd0-FHX9BI&rP7GEIl?u#`Ul_7LwYAeNF+)&oU3yE*MC&0x{LCf>kWx?? z_=uketnCH^=uDW${EDm!3<8g)U%n;g#Q0#k*ljUK#s||QZi~4wKA1lLwwMt&*d{U1 z&BIL73!^!Or@y}~X3F?ry6PP3en6Tg@4oAj9 zP&MZGfC)55d3?I*LorpJ1;`rG9*Tv7H0*yUCMtu_06GweMc_CO)Q6z8QjQPBBqfmb zGVusLy0166lBNfzm0f}Zl7E@)q#X7z4k(d&yA^RSS1GUOqub)^oA#5GN`7lcp~;06pc^CR2dgcKlD^A z1>`_Z76%0Z1@PkD57SMbi5WtaNKCJPCdSLSV0!N}u~-asKc9(Z^B*{i)a%=Ec6#x1 zF)PLe)7L#06Jz?%GX2R*t`(a6N1GBhXN!p|)c10rpS$ITH;)GA&+4R1bVk+3xgPip5rC1usFQ8U8 zcvt`w{Kc=t3_!u(|58jDw^otYVquK8rf0v#t(cbhe-<7AP<95LVyM6-@SlZ;pPPZ3 z0aC0sz7^A8d@z00TQModC)1BYc<3Patl&aQ<-M4h=#I0{E!}@WN0+={$x>hwcsM=(y_mTfa!CZz{(=Qu zZgE1{N(=(9;+CHWY(7}Q^gHjx3_v&ieE>Vn{{z@*O&`RJ#K5I1^yY0Q1s+(bbPA&6 zFN8AqC^mud&h&#H#oU?Rvrg}3X3+=Hdze}57!ObX&CDXDf0)s6)zwv3s}xwI6}X^G zCTRsWfkmKW)fmBzr3?ko=_Lvb0(W?(GYUvZPLE?@k)8hYlUN|*wCUcT#WWb-PcQi_ zmcuxG`oqs+^^EVQCw>ug;h4su$mYnP$U1$)S23~ahrftLFixK?^Ht0ds(^9Y^u=Gr zszKMveiJKz2+B`i|4mGlaoY5&-^7X-r%(6zE@sHMVtdthvG2_C-xqa(&IMPt1t%_4GCW z#H<-#Pk-@G%##0fFQ|j(xNPbGaY| zI$w}gU=ipHK~6KKA7Tp3ARn)gfcRJebS&Qli7bJa{Gem?)(9!F@NQt7-XJ5cJpE0r zIFC0X{K4k^5X*AB1Uma2e3&UDb?|P0g#QW&(0Rodq_PB-!Xp~4Nkic4^h`!^8Kk5F zmxBk~0!Hz0t`5*8D9nxw0`I3YGKq(2En))=jtG1J%^iZP_6tl|N~{VD3T%$Mz(-a) zHe7&)RU?zQ4BOh)Rsn&J(-$*|TX6l`)Fj}@2{!8~lei^UTN@~-!F&y7aZ9eZI~xQX zSsa-J-cQd4@joH>>p=W=1pgz5zh-F@OuY$Y~q$2cWyL6 zi)AS=C-onFBIaYRYOOBIY+Ch84KTUT7@$P_KEAVN0 zJBZi0xd|e70>qnj6MXpPr|Fy=;+7oG+gl-WULfA#gHT>Khj=OIL_1D#ZN|6Lbvea# z_`nC6&JYKsGIoJ?)6+S{O&Q-!p9`TbbBg;jzMZbjC9cKza=IUvxOD&`Jxv2;rWaCK zj`u+6Nf2~6E;Kz&gQTYilA!c-LjpNH!8P@OiWkr^*8<$)V$NXxiEA(}oL!I=lcOVKAF++^y3Brfv^12tp&s__--x$-5%vQ>3s`WtPv#E zv=Fq--*MJMh`Qq-u?G-!n_fcH@d}Dt2t)4Mal8YPT5+REK;Rqy^azl|dQfOEJN^Sn zv|McgODq-?k7j%}{lB2NA;#q{UBokBywvF{gv7TozL*{_3@S{gR|~_^ zHK?d(Pyn4)xm8%)p7HJUufpJztRRAtlG{O%F+n2B@h&KEzz1bRQ*t|~yU6VLLJE|U zLG=?eQtAS0<27UYBL*sqZ%A}-5u7*zFwPWQhcA}*o)241g%6oU%x3sMTqj-YgU z0i+OgAp;JD;1V7bKMz0(LDiC*sCYR4Ebs|Wj$hY;3zRq0*NBQ+a`hmJx%Z;tmi&#N zcwlk_rC~>Afj865#l$Tb7fvq_6W8MWe6ayk6n*EPzF15gbiO5|3Ysn;4o<_i;^2U) z6$b~@3UP7kbVTxK1_czzO&9?Mmr)e>3a{6jA)yDV!WluU?HwJT~AV6i1F=oM@eyUbx@@UjvG+>a)m^ez&cPZ z$*jTjLduM30;q-r*NoHaCB?NM1sx!3g<$-xuK84<5&?knJBe(Aa`` zc)F&PxU|$y*g4L;FnPEq;-tibe7iyQCnMmhD_~b~7J2SqUzCl{tj`8jE_tN6xlHl+-AOULLfR6BW1ewpE zz$UP8y1b0Ilo&`1bV}d~2}nBv6eR*Lr~Au@t1`ZvUMeGQm5A_InF6!p0f{UDSP(Hg zK9EGtAW+AagFSKsTiChsmhqH>n(ZKsOF=a~s5L1dEA9b0nO|1i&IQrdsfXC61UkkW z(%1nxUWrwKMF8Y#P=*0D7z7q_E3m?y1ZhFd=ai6|{#907kn!bob~%(nwgHq}K{XUS z_G0Bw%1*cfc=Sz?6Srpi!9V??oVYQfL{XF%2W{E)lNXm@d^bH^9vtM$$sWJA&%Fm(%abW4h!3QVBENN)gi~AO&!jWGjeUa)D!Xfl!tb>-33I;^Go43akR% z?24fD4oS)|`|c`;J0Vq|V2^=}I|C}wKv%EevJYNs6e^0#)+4eyG)5d>EkNx`@YXAU z=G?#yIgka2qzx@H8z7l}0ys&4GX0CglLZ8R!ke{lr@_)Uytrs!obIY9&L0HM3<%S} z>Co{7C<}pZae+148z7Agh&?YPvIG#-CESWnprR9$>hhGtBiTWF1QhE!aVI z4~WC7EN%gp@d0sQGTqAJW}G07BZI(i{^_ScT#ykEE~g4imJuZD0pfyX!Q6HgaWlq+ z)B9D$W#HNFhnN}D3~@+5N8ru$4=UnbtP8mW{_sy1P!l&``pZAPP*q%(fUbk87`mL) z#4Q+~Os`fGcjVb{c(Q<^i~=NQoKO>2G6S{mK$-r5pczwxq$5ND6X@ja21(c%f-uXp9er_S#&^>L^u?tl ze!#}hc{362wi5U{y-8m@7-Z{peTeP9^igcj#%6n#g0uo?hpx4OxR!jg04zeXxfOVI z85k6$6__1Cy;ueXae;T!TMWeA8C#~GF#x;mnSr=GW5aYdLvbx`L^YQ+-PTN8LX!`4 zBorSszBLiv1uZDb0^JM%PW>QP*BFYsDE#9GWhogYZcvibV3JVeRp0|L!K!owzD|E& zC~l;%4}QT7H0-iL?L=@>@`n^C8jOs@wK(8ilN2NIKu~yWHxicz(Kn35os1w2pcZi@ zR^DvTdF{-Opv1*3fCyDktb&pPEGcHBH6sq!4*62^yyGJl1F`F0BGjn!GT1j69Y!Jy2a-T*wa|tZ@ImG!@qaRdvun zO@^ftxCf2Q#3#Ty40AuV2skRkIt)-=>hv6Q@vZDHAR`jf9sY}pGQOQ|XCbc5xNv%w zg}4;syXkkr#Pz2?6&B|gN3>F)%?SlimtxWMTNdIK=1U-JrWBYRK@|~;0F>{k#3HT8 z;Rx<{-{@);P+*bf0gt~y>gx~FYpukUrmwaX=QcYFny-W?-?_d)!13XlMyPT}R!7i1 zHlQ{vlA_!18U!30r@yfj*I;}(y&zm%Yr4IaxBw`|p>-zMnaFL8kJjSCptHuU#5Fm( zL7jC_*|^S1T+$UW&;yzWc_9J1j2+Q_2NyFS!3m&F2e<@8wL*bU0aR}CSc|K&L$3W{ zntpD!xcGE`YjIv-s0ggW23@&5z3{h;@bo5YaZNES)_}&C4qA&#iz1pKFu#C0zEE$l z+K3zKw+Xj%W_>Mk!!y^}zFD0`CUQD;M z6_@1r0+I){8scrm+n9cFPk(MJZp8R*x|E%`BIDcXHg@8Q?$Hl{pIR3fzv2 z*^reg;O>sVGgg=*b0DL33nU>nIX(bs2U)SiPF#=c-gZ!D4m_%L$4=awzhNe*l5^a= z1$<%fo9RmS;^zEoK(!R3 z1RNzHNk3B%!htW>;K`Y;=p?Q@{l0^EA56pd?pDxPF0^wE<)uy+cM{*t_-wj zZU~p%n$ZF}stal_l$SbvwL8MlIWv)!KzXUt1w6&iA$R>iV*zhG!R>c#FL6o6m(yLn z#I1Z02@Z69BB)&pD{kP4BmokI@NW5h(9LL~NTXg*odVCNpYalx*?CfV6;iiA^u}5zm4pk;5ArK&YK!&?5f2spyh0SA-^R@pcGLx8w1~F0q=Z( z3JXwo6c)SF9|np`DuOPYcLZGr1NkVLHPNG9@vpen^!YO40-&l6nsi_W?EsCEfv?w$ z4HB2Y;-IYyy969Ty}s$e-~nIW5b%(SW{9{o*ByieLzDXU}u8U1<0k~DjQs> zg9b}3RETS{gA}m{bWisU7gvH*q)te6D9DwdtOoBt_eY3J%7FZfoJK$@;2mhj2yq!C z6F@Ep$$tdJ1!$aaLZ-L~4@d;oWuD$2Aug&68gWAFBEu|)^^iddrIE@gm^?j|&4c;KS#4s)J9`W}G@kqw@>4A~r=8WyrJ0ry{89SyQhERVa#p4+lPLGWemk^bIpDtEpMjDtK-Xe{#$e%|mW~mZM+#4n4?yzpth*pdTt*C0aD&1dB!8Ph z;Oq4GOmS%}a(6*;6EejmIAB3HeM6?Ws0C=u1~sfeCVhb1cnqIK1zp_(PF9Za)Tk0G z-V1L>TJV7b;99J>E)TfO0u76?2sBOSj{}VxfdlF6K5jIPFEB7I(mxB#7HF zE}Gt*AZ`XpQJ@3xA#LH8uoU$z0Z)pOXpqQKVpm|HN1{SXbf8cH`R6+5=1Nw9kEA6m zNfWXXRY@{tqEbr{*X2b@RI8Y$3&e?wL$V&|LQHVnO<$QJZUQ=PF-6>*@f|3?5lmxl zm}v}Ny}*h(P|Ka!u|XnB;1!1>b0MfS0F4wuW<@~b?mzga%fyIFfTxpC5(%hp1`5CT zpzs4VhT=2DMW-jEiL+q|JQBwS8lGncjf`B7G-J8} z>g(J9R~a9&#bp!;nmJuJN8Fh4-Sos9aPxdYj<`7EqUk$w#6e54?&gTQ@_#rySwMq{ z0kkoS+405U>4v%DHjFLP^K!+F6&gT|!K(~rObetyw=L>3-VjsbaAeG0>-7IWbF)Cl z^np0STY=Imbi@JFDZe0=<=6r0N`q?lz#?(c=@atBS)`V-!-k|_iXc;+ z0w1Tt6^X9KPz37T-pLo2kDtTtbbuj@7qlu0WWx-Q4d7lT)M^fJ1rHhsWOke&1fE!j z%P>2Fv}k~eW0)8y`+)?%+-MbW1ohNGj_^a(2y-a1Mt1NpCd8T#l39-6JPkDzrXF5T zgUqyj03Ka@AqJX%{{kODgsBDh&Efhv91k-1gHj&Y!63i$7mCZ6x50<=VA^0qV<_g` z0Iw?80a~%3!IUE~d3sWzxDDgW>GKQ4r4WsagM}!AU@#Nmj%F(oH^dU9@Mb|^k+`H5 zW<F0{Xr5IbLzbq2hWqdhZyci|nfRZREI$)Lf^z34B zX?+ZL8s|2!ZhESHk26oUf5CW&Cca@6E@S~M0)3=t2D}(BIP!52s zN?{dvIsHYcxFsTPHOo*u32KReJPD86xH3q?5$Xw05eAZnhx+s~aV?{t@FEVTUk@qg zKsq{*3;-I(2W5cipUcD*>k;}GLFf0wCV9oOkY=dh7K2u`K@0&!79u&poP=m$!nA-R zSS$fhWBLFqyFodXQQ+-#`wDSwby%mU0W=j3D%Dni zlMP~W71|}5SOG3=FI0$|bFE@lUr8sDZt3#!@1LMo-jg^pe3~lCtoDb^v z!j}m42Z@WyHAo@P!NJt50`*lvBV8ahF`zaCqD=%-11=umW`leRYB@ui+Mtk!w?iO9 zFfTyf0VNJD#}kJ^SGzfW5YH00HodY++>Ehr`i3g;D8~Nj+|}ZijQ!JHAyhqt+F31b z&UXJugMh$<>EA%?hGi`vwsDQPIosvwtsr&*5_@fpcnIT*>Chz-a<$;b27&(b!dh_| z#+TE<9e6%QaRCq178a->4GTD=Ry-)FfTk|M3kHzpGC^Sk(gG?b9qJ$nA0AZTVMcgb znO<5a9>q9u`h_}iOU8-Qx$41`3xukx7tb>UHz0S2WGV3~a6m>u!0h;g2Q=%vkx^W1x^aVe7UQDn^BTk@87EEO(I8$18^ZZ}v_Zg86E@fb<)u!) z(I|eN@!j+lP2f?Jmrde|s%Jp+x4cLl4$y8YuxAyR9e0R;>W!b=)AgIhB{H{yhN6&1 zn;AeCYJw(q1SZ2v7SJi{$U|PcFg1a;w=)+y8WdSE9FkD5b!3E%&4QY&pt0HM57{Ne zIQD~df(plT&Ej#4FQ)6ZfQJ@5Tf~hR7f!Ef5jWrg@6S@=25nI~J$*-uxB}z5={H-z z8Aqa3T$AzTbf;Ev7f`_pjT%syvO@}KzH~t=O4PyRVI}-@sSa@+UT_;5wBSTMOJLn} zzBX|q#`V)3+r&X=buld^detr?@xc)aguJ;(CnJrWa;sMzt20pA9QVqATf#U_zGy$#Z1369+)K_w3a_41KfZUY>Zr1%kP8FKn;I0e+CRW)Op0bb^OczS=gxH3CvMYiLK z!_#+mi+lSX168Wb8ca9D%$OdCDKI-e5Q9$cgZ8LF2k4j`L6*a+NhavcalBlhItL_k z2-Hdh4PyrOh)aqtWrr!?1eFF-;HjbM^*!RE@|!@{qJt;#!S+D7;07JY+B4HP_lT=X zOc#LZ=LT)Phgc4p;rY-bE@{3RZaS#o1KAF)1fT&05(66rx|^ez6!!Fq>w{LlL!H77b0#c}!^55p(msN> z5foSiU{QHO%#7)R7-T3L6q(bx`o-lr-hko=ynM4?Tv8GdN8tSfNb!{1FRsG4YI;Y% zxIHK@o$MEvbD076FlhBPvm+$A!io`CY=L&1^JFQqDzJdj&akgs4apRPSYT!!(; zbdL$*>L7y)Cy2{2&X_)7g1Dm87r3`Tt0WGHX9=8xr%(6+z=KJH)dDE>7p41X%##GD+NmyJO-M0Y}hmfWWos?UTgKq;D|8 z0+|_>B053Kt3c)AjY;CBjEAT5O%@l^236CbZuAXtNOu-g6eDh)XHZ~f0kv%~ykjxlXNq_TXp_~VDdM^y`r;IEOU4(|xu=3F zWA&-x?u-kk=T8L>8C z%9OiPK+P+#D#(lpsFVlyk<6IDOur^EiOg3=ZdRn;th^TpH%u z`sw1Hq`S7h8)PR7;`9aLT{{V+lM$|yhlQKRp4;&rIG908a*)E97gRQY`XbDr=_P?B z?4aJR(*Xu^rs*9s#C7YZfQ({=8>PS^FiQZ@aw8CGb3mGz;hK4vxq0lk9Uo(L2dHqt zbjS488R7;Z3qaxdN~%}9=5;^wjCcHA|6$4qfe*ru!JGch_;4H8+7-$5gwpiL3h zv&2Dj)1kA(t+Y_P4M1?*C=ifQvu0t>3Z5#}(E8u0pfXfVL!*MK?>pkV;GEoi+Z zkW>0VOC~^Z3sOXkMMzz1m__J*g-qIkOh;6NP`80ohCbs934xyJO7q2o(3U40dC-E` zYa#^YK?-D7UM3!E$TF421>)6c8o$130Nr^8T_geJrB1)IQ2Zox`93$&VEFWh3&h1G zz;j`sF?jG8I;cae02)|)H~se_anNnDGK<08Ot;05asxVC1R5#I>a7`ru9p8{#hOzPtl*jIZ5}csG$LWrHQL+vwFN5UaGd&f{#H&$9vl?KdSq+ex zi0J~$#kHkDT{*`I;8_N64G*64gpOc^EeChRr!EJF(EjD(R-lII@8#kOjD6E(R*3sC zPMV&*LR^utV|wojaXZFs)6c9BSCj9D3`oSo`jDUu`v6=@fX2s9PZwM%E-O6`q=-=f zNfBrm6Qp4BbkCLIvK)(`3a00+6jzXYaAvZABZDJ@Bcs3vSY`wF`aqNXE2pnnDXz{l z71Snr0X`>v^Yr^G!Si(@tHeVY-%Zb2C0@>h=*aW3PJeJjTwyxzYH>mNMoBsO5Qny6tLl+2k&eB4!jtpmyXAaWkeLf}q}kV3uPaNDVi#8jyp2fQJ+wh-Epx z!R7~$Z6H6~{WE>A*Yw&$dFqzAsaX#MZ2Um-Wv33Y$3EZ77wnki@2h>Xi9Vj6N z8lZDrBW`3p`*w=}WXCSZX%~>1xQ;Jpb%DxmcxZ#n1|>RX#|z?F0)N@2uUjK70E2YWwgG4cudH_0NH^2puZE;WRq89QIJ>Q zR^S&nJ3U~nxU$uIkpH*^N;^n#u?My*NJ;Eo}E5vowz#V?CHDKp=2YF-V2gQ z8AaXTrbq6@O>6}*?1MPL!z z^og6rH5u1W@7O4=3mJdr1g*w`w7_40`Xi7%GAyvo%92W~)B8Z1y8dny7XbB_p~V`k zn1B^)@Ii2Ji4EEr1Zq@Dpw3IdG(86Os6Y*km`&nMjPIu3-z08=*hj{~s|DIerZU}m zv$#0p+v(Ap#nt&h{VkBGko`u}Cu|ni5?{{_>UwK3Ul0iZZ;t>Kv!DTpiFpmqi|1ODDKB5 z4{<;8E*zLD*o544t!?5tGN51s<+20fO5A2l4?x{#P>IB#z#;H%`lfAQuRVuQGTX(o z7`vvoZx`2sHG#Kp7dPNI3(8fXbrtWoi%SP1+8tVu782%$OfB#PC}>0xRI-43*N%(= zU*Lr;Oe1Vib2`Xiq^1T;{w=5!1hq%rZx>bIa7zGb0sv;0CX>KdkZ(j4KvOTsu>(^9Uu1#9&v!s+6z*qG zV;h?(@HEP_OI%+Rk^Hnk%~O5G1EAKD+b(gVdV~}xd4O72yFry9@_4f*NDpXs1~eim z2^r&uK)|`h*a1mgzfo zqpax%HK9NQ*pM~-_jZe0Fus^BzDHb34%FrVWi-%^T@Vc^!Qg9i!Sg$y_D?7x*}}@8 zH=u(jhq=Lo0h)n}9{fJF0kN1kt058aN zJOG-ZSAewEprr&mw3L8U1E8knl6~M(;^sbaH^#Tq750Oha9;Z%O}MuG;tCv-K{*A~ zlw7x8Tvnk4I@Y4V;&|jSWCR9z*yY*u*ZajKIhKR;fL0sv91z#xXaMm*i(wrOh)XK1 zg6yydb+cHIa+wCSzyXDP^#O4iK2&wnmmUx|!i)`d(2xu$xxr)O=K+-Ds0K9_78}#` z4vI^=BN7a{JUpI35+FBnBgGa-3}hMHun7mnB|T9biijGRVpv8%n9Sq=S#}KB{Q}CN z`iwKg1$w4`Iw+pacKmy@fWVyT$%n+v*}gmnvlko^4?%6D?~np@RX`JW;5}|{rwbki zHxO(OW2`LxAqYxEkd?(#4ukis>^&@Q1zPn0?}mX#;gI+K9uNoZI0EneWffS%rsxE! zWu>wldqE`_X!P6Uh`0{p%IS$m#KkP(p$YHSf@bV@gJKd@8=iz!9RV& z5pgMQcs(L;ZTi(C;%1Bu(|L}HYjeQMe*2^1(x4ti%u#VEczYH+;oNjo+(zNb{4UU$ ztf(U+kV!VsZ1;`n_l}Coa@+-}0wqc2V<_|YEM`nAB%px_TB`|~zh8e09GJI{f%`R5 z$Hf)oKvgYhybP=o+$@&NQeYRD3m!@2nrqAfA9J?TIXO=M>VRyW1PRHqgAPZ6OpBsA9^`h2uAOJZxxuq;0;}1lAG|0o204EU z>g4Hf&xosw{o)3wv#r3B%T@bfmTrl1Gf_M<7Or%dqQL+K`!CsXaNnwfZDR4keOb0QJj!epg}xoq=dx84I1nNxm=&) z5vb<`O5)!yikmVnnXY?D+>xo}==6e1;Y_QVLP!!sI`I>Qj*G-(3-xM>^$332F*D=sodVGN8z9*cfpyh|{@lipMZMnx1}BJXs9GSquui0t==;xG65K_L>#N$qEb#!U|#n_uv@9Ig?ngWzAI%WY5(t~_cuQQA>k$uhKYiHrjdtQ9(@SoN$1uK{e({$0RKsVWkznwN z0erA|76t_lN0uxFZh_aVN^n8&b|ObEfv?l2-xhCXd_P_Kj(8s9+v#0*#BGJ|^S~4` zDzGZ>EAR>|oPO($xHA8TN04*snLw8#{CG5-=dQSf;3M!s>);VGB~}G)fk)F#?~3~| zJ>r45ZsuL_Fv!jTPz#7dplkZ?yW-W1FQ=E^1MjJra}PYdwe_C3i8QFh2Q`~IB+Zy6 zfEO=7+f%=|r?cD_cVT=x-SfVX*iAj=2VOxJoKE{oVH;r~EfQXP?TKxy}eSeD}_(DE5@ z+JYo%P|8!7UeF~jJAKUqacQv+tO_i?ir}OAet;K=E)W78CVcmSxE$lV>3<%8TYkNF6NG!J9nF zpi~2rIStC>$Qclli9rVmFgt>l&mor_pqvlV1Z!@=jZ*?`^sEOjgNInC21@-f$8v+q z1(1=@SyHG3%s5yl% z*fBl-vA7-MoayTxi_3wI7B~muE}G8!MBFocF>LJ=xMT(GDqh9Q0}h%A;GPNSY>_4S z)F^NWEa!!G-wmJ~Ty$U;s^R!<({b zsvtq$2HKGf&fJi&f+(t&$P$2fiWIZJO&(-p;HH4u9*~InAejZ)bB|RO_&`4J+MMa7 z&%}j9AS1XJB*16Zg3D5YRnuob6Su5i09wzDoZ~`yTHy;;dW$zTL^C0LLwXDe|UC*w45cg1dy7E zASV*E-0{!ZdPsX0VlJxP(5)A^?S+>r%%D|ikQMou1}TG@v8)g$gItAb9%$Yd?2+l= zFU1w1>GTI`I_-HWZprZmR8E2#LuX%#i`U;^hGu$Dq=V`t@X{pEge_?70KIU+NS5#< z3O@Io*%1=)C>e@Z5$b=8R+gdygFC2-2QT_>fVEU09_Lkr#XNe653Yl_6}SX?At$Fm zJiY3bxIg2v>AzlyTQV-2Zt@yd8G~lNSOmJJPkSxyB=Qov{|&UOl^3=__swhY&OWX; z;EtKu8*nSE@{PDT=R--wGbc27U?MqG~Z<@D!o#O)C))%D(rOEZ3*?)6q2d1t6B zXiz{Z%kefSltD*bHoO%_-rfn5$EYb}!JUf_;ILSLvf3J^1r{08U%VBMWbByk^iJG~ zanAJCcj7uogP`(I2f!C(8NL@6o&MmRI15t47^dhiC@+H!v@?7!E(zM;0u6kaJbZ%- zNFKBv4ox03q7wgJTs#_l04OABLi$H&r3q-B8KpE~f;tni3kGVm0#Z1Dx|>UGM8_ zbJs72)s4LJ3ZNsPAsG=Vhw&;vN*YK6fySu8iwvQThFOQC!IhVpM;_8%fan1Y=Ogta zK*?ARl8lvDBq5DhS;&qnkmq2TWxC@>aY<=-)ypdb^F1tEOfUT?UWAzQW%&e7#-L^F zjBlsse-g*od)04UyAxD`Oxr1^u&Oh+_%UdR|E#57RF53i8GtD|m+v4V$IrhoV< zt_vD@lK&*~_yT6Njb1drXfDE$p z{SX&d!)To>kTPRx096B^#JC)!7PK11`-iwTya6czX#n}b_8G`Ipko|h)hjW6cn8u18tj_!OI!vNVTQlO zr5L|XzxYdBh6mKegN(`wT$}#$m$(_@^63V@!F7<&Z*=*R-{NK_h(e8-TY(>(I6#RN zsgU9YuM`3a!ZY!`-{N|VpQiKv5f4Yr%M)NjnV>-j&`@UMA8=j|}=?6i_ zQ!Sc){lB;euK)^b^kNdE3Ldnt86~u*+cHXUIw3WLL7ULwTu|QzG_wXC z4nc0Pg3DZxVes|`=nw`;(4uQZK?rjesQEy&i~Jd1PPb-~0B_cXMmEeG6z_3>yvHc; zlY9CaCJ6~8n5W={31lE!BFk|OXyYWPNCj=R$rA;{aHk>8*2 z1t~BMFF>UjXc*FmSwd0=;a!+K`e+I-NCF(m(t&8qA>B z1?{7HIo+OBqJ-%Y$MmhN5-E_o_E^Dp?Xfz777%lIPuF3SIK#MN`cF2A=A`*d3ap@h ztE>X3$_KRoR|qIDDDZ=Z<#<7(e(A@TlCXCajcW_83GESSmibG;a;A+rh5>kJfmls^e zgJ#zuOL`80mtcQ^rwU%sS_&!Ty_wLc;8lPexH+FwLYwjB^wXRuDG;U&egv*Omjv=D zLNNJlASZ(w+Cf|rkqY1=VOGMoL^Fc?2XY!LtxiA9CDF+NPt`Hp5>o77JwN%U*K$jU zBgXCKa7!TX=>;VyQ1XX$6%dPSKvsZc;8wikmdN9L#37|1pTSHPvnuXSL_sGb!6b?<_2x>V{~K`_$JKF4`wScIWjsj z3Cx=Qkw-$@^fRjhlLixqA~T5MQDk9uP+$SoKMD*2Ye3UlJXwmMyXqLT6 z6&M88PPgTi_{F$qdIFz>#Pk3@34X?dAhtT=f$1H560@0RF->RWVpp5~-k3#!v0-`+ zzl0`J1IzS-%q+^&<4jn@7!Oas$S)zqczAkYFS9V?tZ4!gxr|Gu2Z~5oOvk_k|T0Kr8_eAtA7S`Z;Y0YsMGTHz-TUGH#uIK}bTL z@&5D=LK3!&_ogc>ln6H*CE$iDXl}TI?1rm^-EfTvH(Vp&hFfTExP|P7+l1Y4 zhX^;^A>fAVXl}TU?1meJ-Efl#H{3*WgYa~F6$xWVZZMjDD_4kX`g#=!c1U5ySSavp z`hFD&MXv4a-2%^f6c~7zr>~QfkYU}yK2zZSbZ%7%8OB}HHB==mfXSV0*{K!Md!BFmVOLEyOX^hS9J320%6CC8ndepgpQ5|ZPDr>m<= zm_dAJB@EhF%nG`!Pyk*8PG7Aq5y1(%b5eo9k-=j+kA{RTwB%-jmE64ucg=>ni*eWV z?HX81#Qo^u|4#$CM2ygskZ0UKy+RXKA_`AGsR?r-O3{25Eg{^6CIn&D4-8oXJEl+5 zlDNcjpG$#p`bQZFDQH!~xN^F!Jh)WCQeoUft}v$0)R8bkiioF35z&wE@LZ^eu|&i( zLJ{!-B_g==B(zy>GAVIS*ICObGd)yKqL>ZDU9K>F;SzSa>2LHTb}(L?zEoc#+Hmd3 z7SJiH#n8gkaRoE@(pm8Bxht5>n0A1+=kH+7a@-A4C@WAr-OxayKnAJ}vd@B-2U+*j z>3a<%q&cAKQ>Q;LkT7BFoi1u9VZ<@{W{ZHM62uUZ>9VF0K`^1z>3a>q>IBRsbf(K0 zNh~&8G^s_vkyilZG!0PV#_6<;AcZ0@Q`w9qVq~Ctq3+{SV0DBWF?D*Kv4k`SR8#8o zg~l*@MW!#Xkm%MlP+)bm$WmfcV0ARe5@=#mV1sTw6auZkVRdxK1}hM_Jbj*tL^4O; zkyZgmVVLb;KS70%{WN8|gPDXX$GMJX0Y^0y#ZVz+#fv$vFo8D2@^Vh!XeA-dG4X7R zfTM;$@$`G<5@|4A>U19q3D99K0_GB6m(Sr}1=^ehzRy#EMc_NnbZ<)u9sYHo4m*-y zo27&r|5Y@>-IfwsjN7I^vy`x4+&Ep{N+MUNgHM4Kd^am->ihy@mcX3p^QE?)_y2wY-=# zm{=fZH1tiMU?brzGy!DK4JOcGs;rJ1m_YWtwUN-{*aoskSD<*hvaLiCjF&pS*A`}v z$n?v$5)(m|rP)cCV|adzoy1bcsndh(B~CIPpDyDdp~CoLx}$@Hg~(4H&~&Vg5;OR; z0#*$s3xR3VdmSW98IMiZb&~|$LOsDv(!}!Uy$#tZB~#R(x}iys0i+*Hpcs-qz0qA#LKvzMq855h^FepXMT}dfzjl(4ikS!+ zkptaQjC4maEBx+a12ZNI@O&VbqXul7EHCKRHPH3M(6kHYLs9|w`epD%$LtCm0tcoi zI!h>OK(-@_fi5LR(go$uV9rwF1YbKlar!D}2`M9(dv1VA2o?n{#~sWdi$Rw`?_hzj zAa-#oa4SG;`Q|L4!F8E)EqEh_z>?`2E)q(N2c~F^x3Wwl8k4jZ*`SW;JD4Hz@^Fjf@ON3tAyC}&#n@B zw$}u}8HNFJt1lAo0%I1$<x^98Lv;*08s~~2Y5(0>p)bqI4JNr$`?9vx$}ZAuD-#T1)IcW z25Sdh}Q zamEkRZ+c2-=uG2O;)YC12q|zm{$K_L2p9NDSuRJ&t&T{x=z2+nuz`Bk0!ybW_(;e~ zgYVyjTrPbR9LOvV3S5peSh56ePT%h(Vaa%F`cE$jKgI{sJ-j8dl%PkeLjqEP8Gh8d zG`AA70_bSqJ=3pvOQPo|Hi94({0dx-H^Ba45jZeC*jK`qaoY5Wz7m=+ zpB(U&&}Y0k{b0C+1eY-AvhWYgSpv7G3;9VHNZtYYN?K8bN0i&?2!qp6P#OYB-%|LZ3q!FYGNn7@P*&jf$7>+9i~_~eTSFxB>!Ef)N>EfcC?+ufX z0bSG@CgIC-kXsQH^go!h1XfLF4hNSk>fzvlub6N|k_BbH=_|q|Bp~Tlk@5EQ$KlAy zm5-F+oB=C!3ioYSU|yXKUyM#@$Phw7ztIz zJ=61IBvf?vusJa(iYRdDGq$iPaf2r193QY|DKIIpDKP0WR0{m&QD6pTzy>yfmDBgd zNa#V5U^?To>7lU_MiMoUGvZlo329-dC?ZB3 zr%t!}C9F6-Do!GgaoY5gaT1SsCo(H=g3jLp?VZ0ieMh`R662of(g_mUjK`+CCrE&- zT%RCe$oYRtlYrw%27%(~FB2qEg<)K{;f_7QAc$nW2}=J5HVcD@DRl z^tAx!xIrEzX3)_u;5HA3z_jTBsS?uc*RM7U2>ceFZX3xi&Em))aBKR+R0#>OsT-R> zcN*w1a6e>aVyS1YQ)Y1dz%bo#x3CCmo2>rxxeftGK7nEd76n$v6AW2Oi~@H#K^<2O zCJ!^F2nANh8w^>F&F4EHy&4t`CLRSwff~4q1O-;d2Mk$`lRzpILFPCLfQ~F?1Qj&k zUXOBB)ROf+5RsD@Y-uKry(1#X6nMT{2D*>Ii7aGr-&;aBq4~o`hsQ)OLh4 z$Pr-I3jBbNgv0DYa}Zn;Xm|_M%tRDJFdZ2Rtd2-7gXjPiLXMDNoPNw*ayt75hAb5Z z?)K@uhs5L|VQ4aaeZGVq;3x$O1O-sH1}Q8Z%$Nccz#VwU z41o*#il8m5*%Eq8-vy@c$Oad(FS8}= zMVIm_fQPIZKuu+Ew~s~O=ycm02|cDCo2KXHNXQF1GJsly*-9)R%2Qfk{q(sx5;{!3 z1g7s%lLeL5e{v+0x#l#2lPdQ{mg#+m#N;4h2;JN;v>gc!$tkT|pp zAPH3j@dh*)Kx!fBL>Q_TA`SH=sMS9`DqlhYVjo7Fh3!Mm01nyM2f6!zMfF6 z5Qp(HfJy>T!GTo2!!i~YgCM$~-jVqwuQL5ci9{shvFS#o5}I7|_qKpm$`&&@OmmkM zpI%cc;lcqGOPzkAR65buCR?wdHVcv34O*1)6bVnG(o2F zn88zd%-|zqIJ~E)R7m_}Y?&TcDUrt5I{j#+gd1babe<}SZ;~>U2H4=u5Ez^J3NK`Vm zPA{#M*u*$xx<;LZ4&#LBp>-14jFYD~)=3yKPMN-?PC|vTfBMZj303Kd%nIzFu0Nv! zlVig<@KK;Mw{;0PG6?LQE?F;O#5isGJ8l8->B;pHF$%|+6j&741Sat+u!Eur+=Kc5B;cqeuy^|XdI@7$NW)jmjm3f40kot9eBLy8A~Q?i@^t+M2@S^M(<2%rlo*ds zuWpb~R5`?{z@fmXz^TBcz#`Bj=*U>e%K$#{4pbI^Mk_%#032wL&}G~@{au4ZALI4u zwT%+4e1}*R*fp3qKm?EDjO`a1B`TO0Cri#sJ;85d9A*(qVp_+a|SP6<)QJ=58{B=i|CPB-t8Fl0P4 zJ+n(9kn!U5-CYu(;ur5nk<^k32?Qb!#O|^$0)F8x=W8l zEaSxKi+UuCn5Oeie|Jw<7PL-&`u6WV61I$t)2EyENyIYFoIbfvB3WrBFK96zg94Ml zWFZ9x1tv$498iy#DI3CP6qr0+qF*6q%m&hL4Lw0km9zL163j zlxY&Wj8msinkJ#nxP1DqX%b3|hqvFICSlCT*f?EqhD12yuIZUGBoY`WOg}Y4LXPq9 z^p_yPW78dHN|=JuFgqw|fHDfRK(n9%yFR0h5({{95<6tbgF@33CfVgcvlKSQMDD1YR(5UuI(Bt!D(aI}Ngx7*#-? zWf0gr{oH&B6UJB5nHNZ?fsUU9&B`-+Pd8d1;mbH-dcy*VYL1VfQyQ5B-c4s-C=txK zb^2XJS$}SUECm+OqU z%a=%Kb4*;&1ZqcLoqlVHggM`gkIkSZ*q{Mgftk$H3y<*0Oix%NB0Sx1sf23%@~a?O z9uaOw9z|xy%@Ce2x1+Ekv*VAO4FZZRJVM-#LW<0eFTb{cc!ChQd*@q0JOOS;5k+Rl zrbS>LKewZRBD3Sf?O=U;5HlA+c)So^3xvnR?Z~Id?D(SoG3aJZ9&T<&2}Ned50}9_ zE{LHsCx8v*3|Wrzph~8HTp^Jm0o4F$&43mMDS%3QfvM9AS4v27K-H#Bx0@`e z$Jjgl;7W-Zj8msat&(tNd_H~NDhVUTW799Ll2B$mHvQ`=2~Zn2Vzq>&-Y-z1WCS4a;63OyVJ#d$>Dj;i3pYFIuLQ)v21)|86 zml@PH(&rFRnBKcaqL*>Qbm_Gc7N81|U7s;Si5XOOupoM~?2a5+0@J1!u9YZeoHqUG zT8SveW78ehNfhPVB7R(>m@=MXH2)=AR!InMr@GKWSl*{X@i8a&p{4Q?58L(@iKtt zWkEy%C&cPFp8YnTdI4H1z{C}7O zv=hae5ws`=dU=}#W1R5A&bF|13n9i)g=D1+n^r@R9l!az6gIpE@-ed(aX!@y56519J@f0L+ zkc$ycU<38i1$J{lSC+#4HC=bJL?9^W8#YU*OSB6rF+zfs5#+ZV&@qT$yZ3LFFo|9Z zwtEkw5+hi917nuJTs8$ZP>V)D0V$jqK^|uUwT#&`n07FlF&$t8`vA0(`wXbr&TPhX z0!*)UY5;*J%clEnk&sro%?9$^1H2YY@7*GyqH-AQlsEWQUfm+0s&bG6WabzAD&@9H zsHz+Xo7uvI&wJC;w@S#VoB*rrVNznk9T3x3Zj~@nJqcDh2d|kN(+xLDh}f`cFwI~B zUGl;P8k@orvY_09LvDJ&Hi=Zm71OtElbFSRWl}4sQl5U1kwc8JXL{at2{Xpt>GDh* z^QXJ)kO*SjIDPsKi7v*I)0K8gL^7V7Ub9mom+|EES34zw85c~q+a(di*fD+LE(t}B zMY~!A9K{5Rr|;S&kp$zVPFLA2VF)TNSoIk#z}ZXzH2B2=>WMMJ+Rw$iB`QJfXC_b< z1dp94FoE08|94BgXFN9j{T_)tp?|zeJmBO8YM5{-2n$S`p1)T@3Up)X0SUF~8}>@X zGyUhCF1SxZpYham$9)pQjMJuv?vwD5|HJ{Bo?>xO;8fstWKiG+Wt>M0ZqlI1P$7Yh z(+}*E=woc0?!R9`mGRj0()|+oy8n2gdz#^vfzHO@1Ep0qZg8FkXFVnjrT~Fs(*+Mm zv_b7wp1$XR1fSR`R?u)?52F(3&SNgnqz%}?4-QBKGVYpgbWp;8v1xkpK?!@tW78KO zln7(&oc`;egry8*nD7IWl7K5OvjS+*q#!7yAl~&qB%#Q7YY`15n6Lx!jkd8^k>H z=rVRp4?iJcDE0<)y2S$ySR0HD)MygeJ$>E@2`R?r={rwI$T3Z0njWhrEI<9n2?;mG zYt!vdN+=3It{Pwlt%n75G}@*YoRm;w?3h0Nq=XUUf$67CN@y{jnSTDDu*`J9QxYOf zw^*lN_%0wa-TIV78sn+yi%v-xFil{Z&Uuzuf${kCucstb8MjWCJ}uG0cz*hV(-Ovv z%coyCEn&vKth*7MHw4c}h%z3Su6{-$j`892wlfkojHjkIu8poovlKW%X9R$I+H0meoRf%R zJUo5&ISE~MNI}&zec?F?(dkdlNr-Z+5d;kaOkkSMXewyOcyYResUXy*JUu~>`5!LH zfXx4BDk#jje)@ksK}W{J(;f8%K_hg1mu1AJ*I$rOWo(#!OIcP{Vks|Z00P`yw@?68 z7YeKbN7)p4r}L}GickM|K|+La{dBgA65Wgkrcb&kq0RJPVEX=x5_*hlr@y->p#b6v zT#`_g`!4`0UO-2m@PPZm>jV{;KnzFLvMhmJ(}OQbI5FOvKJAi(Bjfq$H!ewdFg8qA zyeuKlICr|^WeHb)&`~Lhtl({;9H2R`sh1^m!PC&pW?VmA;EIGJzm+aB{zI=>Qx4Tw&@MmBy=5T zTmI9N&5k`=6Ig}9oQ()0$;1W1G{oOSQdB)?@ z`L0XIGd`HEcU>Zhal`b9*Cl2_cox%BZb)b`ZkXPGLt-*aDEcN?XySDVx#>%8O0+ZH zo33+9LQmirlM<^2lLV;XV^d%f*g8GymV_E(*Yt_EBqSL(PG5aXLRxMT8zeASFlH$+ zD=<1fVfe$v0P-}m0+Ya`=}&G+7&1Pao^Vtw-typ^4sekV+VIK%BB0AFSU~dt0ws_U z4$yc$=%jqmsQE3B2GEG-`=eqRYEbQv)*q;0tign7O8Ru0I}(z@P@NF9&>=y=Kk};6 zd+$grWPC7P|E@$JW9#(ByAr{S52j1qlQ0$7AgIUz@rpr~0;2##lySrKfO`@}jIG-b z-;*$7WNe-O>%PPth!(l&Umr*aFm9O6_E4gMv2}aWgoYld z;|dz3VlZQx0GfW8!I-7Q4;ju9IK-*I>bL;BV1olxbO}tDUi=1JY@K@}q04x9`ky!8 zF^E?$BwQ#e4kzI+4jCs+mv}9qN7y&AczrWn@r6V-f;xTgo!F{#zWI9 zzexl$hHgLmO(K+u@xyfGpWw!W>re1Z>Dr$X7G}*iI>77UAhU9yL43&ECwNHy2BR6% z3(%qI-~kQBEXUO#)u6m9{YxU>8>$zQctHdE;N}ObBWO7d+N>GGB+#%3!W4);XrjOR zOQK%}G<*umt*i>{pgBMWMX(|i6%NV69!a49eee3-rt!eIe(F8qc;#e=fTNz{l9L?*0)K?3FZd^+#&~i1@qZGAOwDZ5-zQ3o zGW}>fdH;jlrC^KkKk4a$T^Z+(VZN^vAYuF?;8DC9b#wKaSczF6l zHc1z?ql};%o?^fqTV~L#2a~`?Rvz$HHGwQp1r?#hDDZB&H@jpSs2jVPU2-Pl!|A>p zlJ<3aCU?5jZ+sm{U?su#XQkR*;~?2D&dufkU8o zx)-OUtHGc5lfm;)pveu$Kn$zn3`WQjMK&|06`;a&1!I6>#Sq1a0ncmuD~s+Yx;r_Revj}QUL2` z1noRx1&w?>K(nKDdK0%~5cbk?vmkM$<$R3N5+;HuEg>Sm7&lMX;g{6s3RM8PMu9=# z)$~+;$qdG=)6elshBLmOt|lP4i1EYplLC_48DC9r6qKxC+&cZAprk#7=RCbpNYavV z>-611l8THUr{5KlT+i}~QGs!~qmZQB^mW3L_MjOmVM%$R-#a=49Jw9efvDR%Is^n- zrpt>+>a&L`a40Ye%%2`AB6*N;{&aCsNlnK2(;Y=6K}GZ&QAuzS?I12$4VqloBOwVY zq6H)+Wtr;d{s#9pkPC+Gplq(|*l`M6Fd)mkfXM8G%D~GPImhisn$gP_3CHRCTLc_? z_qPZL{1@gv!o|hc z@Ti1>5<4%WBZDHl0-G*_68HcU(1De^rz^@xYBS!N?kgi%&3J11Nf}8krrAQ%#pNYU z8GEPu%1a($d^Y`qtfV(cG+sfnY5IOSNjb*G>G$L$%^CMk7n7F+4LBevWZXYJPC;@G zVmtB!}`h_b(svKMpf!66tN|G6jyQX(5Ng6XAp1xm6Qj6)I;Plr@lAuvz zMP*4T#skxBlqEeF&rfevmegWAHGPA!q&DOH>35VRWgy*%2h#YJ$DOqa~@vd5lScN#Hvh_q*vIKgftq573g7GCl!Hc^m@8 zppujUL_muqmcju)&IOd#t|pRTJd zDb4}am^$4@TT)LRQQ0UkfM#=Eynx?`%;5Oo#q^ollCtc7Cqjn3_G(L7NWm6VCxBY= zT#g?YKx^e*Fo2fC@#{!RaU5Y|a!}wASTo&FM^Z_DHJ<_lXhGZ_CM8yudIeTi1xC=+ z+72cGL=xk2>;SixSp^PEZ_<%8VLU#4yN;wk%TYFg#nZWTCEe^!FX<2fPsA(mN`nVD zxg2*efoDP$*g#9V8M6e|;I?RbpRS|=Cup*cTZvoX>GbWolA(-Cr}OGbS~6~%?ye_k z#&~Laot~t+NE2vK1+-j`1GKUde5Jzt={xl#eHkB2&z&wPK3z^mqCGB zmjN`n$mO^KhoeMuii#*5Qm8Azrw zu9_ZTD5-?mzicv;R5S152K8lUFoNa^!1q)tuqm)O{$R)w*eD39oLNAtz(8Hi2gqH` ziPP^JO3E-!oc_m9(tvUAbX_A!C&qo#3ydUFvnN9*q$SLm7{J|n6$Wkra3jJ|!0`&G z=d@mg8(t`iIQH%Xcc!?-L28)5^%|oFlaBx_#l}J#`PtAC5tPnZKua40_Dpv$mQ?3h z{-p_Y(aGNF`NonanBLrM4EN>)G;e}-F`;?$2C_FNPZuzelu?|_20Hrm1E{@-aJaDJ zPN>5ta!&U(k(9OujUb%{-we$Lny}^M24yJ*B@Rc3AO}d01GG7f&5TI_w(&+GOMyjT z=k&!Ul4|^*bwd`Q4Lt@B!5b!$Dva#Y|C&gOGcDONUCdN6lA9wovcZfRObkkF zii{9D*cG`H*ueJGgNFMVSQQw#9q%(_D=|8C7+4j!ATnHzEZ#*5915K7 zMW8a8O_6E(#Hq59^-SE34;ZrfSQwd@Ss1t#7?|oEL4*DbU~?VsGq_3XGqNZ#DX=>- zfYyF6z=fCv77DxaGDtZxfacc)779Buc)AjZYDP_F1|@Coxa{u(vI=+^mmq$(rjm0L2J6EJKp6NpRQ&lDZ+SXy0w+0JmcBv(N>b) zQac3|81)%BKrUp|W#ED?Mgx!SAFz`2WSl&m%UaTsr3X|lx?4*oFM zT2X<3yc9MdOJEotv zlayneHT|8P*ZB?4Udl0O}MoDuNbsfdU7!SX)P)&ntu=Vg%5g48C202{eA5D4O$TU9f?a_PRKq&1 z1*roC1G|f4x&qXEhz~%+zOc{+TO*J@z0XBb$`@)jTov33s48$Di5Ju^22X>UF?E2- zrVhp|m~9Z9&=6w$ATwQkA{!`8C@>22@<9*kWpPjd=>koLgO(R6FeoqzOq;&{gN&gT zyjl`s4GElt`_k+R*ZiGNBcq<$SRAcJ&h3=BVywLU15T9;8=q}03 z$ar%4Q4h&h#@^}Po{}1jho_f#N}4jhp1#Uc(vtDm^e3L+*mm}kG}eFG)C@`Vpf!%r ziQg%pxj!}yrVKNt3I#S$6TtC1NST#D@${u$l36lPO>ie7>76=V+gnmn7^)tk0^&5# zdVYoJ<=&Fzpt(YC$wIj|usJ0b2L(`tz>*~}4H^$j0@J3~`AEvLPn`k|=jn@lB#lM> zPiYWv6atOzYBGab6b{(}AEv+bk<5~T%0TP`jpD);eB_w^uvY{$GP2ND60~N+%1^R~ z=|1ChMlNBng_PIU69k5+ubdD+jvucv_(3G!#jR>DfV&*BLiYcMg_xV|+fnD_GKq@%i+< z!IBY(UN)OIXsUr1)Eg35#0Q&Q15GtpfTtR!+l5Gi#w5c+Btc`6Wg(Jsp#31Bk`~j? zhDh=9gLf&TZV%j+z>9Q1ToZN`t5LpW&9D6OBo+b{}TZY zhT9R63UJLb3#Y&npPMiKMR?>=b^K`>FNoU4s(@Wzd4H!31Ulu3n!Z>aE>o`ey#wF9a z<0UN_uTOW4msDe%H$5j_vNG&C7j$p}G*|*!SFr?iY7wh7qXMX1!49&X!Hg*YT7u34 zHJ_1HI&N4xS-?@_4;KTt9m1}_1X^;udAfCiq^!`==)*O6o8!nckl$ z>CC%?k%t*{YAfijW{&BCAP;Yz{y$ODjwoj&BuP35ft@h}vL*uRj!j9D${=?zgGPxg zvOq%xNs{V}o2PRo6KQf*GR))+C?@YomQ+?-!U*aXKMirBL`@iK4|P%KUGqUV-H9IRH^u;N~U;24TI-@sF~<1*f9-;>i{*mFEC^YOr8EG zRZ>zIYCJ?5T7b=S5m1@#lO~zYIBoitG)Z;FiPP_-NlHi_VN>7&UGBjTYQl&paw~9x z<~X?oR!!$kmy}~%JzXbVQc@Jr@8krHYJgTO9pIS0KT$}0dSSYx1mp4PUFnh$j7O(G zPM36NJT+ZAL(-BR+5w%Oogpd1xCU$x8hFF zaf0YfNqNTk)2lNjePEr(`ET(DOBi2G|C1%@#kg*|d$y#S%sMv6l4%7cCeTh2 z(7I*lR%$l)=@Y%!MW)9Gv$Jx7`Y@nzgs$nUvn9hAPfusck&I+qH$5gtGK8^f`o^NXB~LK!nBH3?sm!=|`ovGKmAo1xR{vuS4Nv3GQr$Eyak<*5m+uMA_h4K1+)O+24faPcBjDo>DA?uQH&F&Un`gF6o8$E08y}6kZR^pU<0j(RA2)g_{65ktO0H&u?TFKez`)@5F!r>Gd9pMLTsAM7I1mF zN=YBa71OgTCDj-=PM=gM8Nk>*{c)wF2IKnaJXMnVMju$AYBz#f{%o4e9pLskBgnNU z7_$`EKqt+ zgRp_kQ2`W)pyldJ0&9igwy-&BAjH=R3*6^YU;q_=;DH-~_0wx>BqhWU;cS6Wd~f>d z8cA2iCDT9FNX}<`Hhp5Pq_8Na<97($pT4tJG6UV!AEB<^?p-HY$HaJS`l$xV8ph+( z-5VuuFm9c$&?G6rIA^+LlcXx+vFY(mlAs-y2Cb4-qO*^52sj!E6f3ZUiW_#v7ocgw z>GiFW5*+72QlPSPeye1@3{)Ycr~}t{3Rwye^;4%GY?hRUsZX8m+a@`QaoY6q7RdlK z+nyeV+ol22Jq2RhOcdLo3XyDstB2YKRS&oAL7Suk)3F1Aa`2(RK*U{K%y4J0#xsy>4(fmPEwB&CEea)NYsFe-6^ zt6`A%blnbc^%UPEX)=9xhomv%v*`w{lA6;cIwb`qE`pp6>fx}16Dqso1JH5{fxFWk zJ0(R-9)M(3z#%+`F-wU9sU!G+(TwQ_s1F3{3jScsa%?*a-E}p;RWfG!(@sfY4yfU& z)4C)@Oz&~RdNkmUBuM`UMg?}q4~$ukPiq&n`)64ye)K@QRpzq)XDu zaT>ECGiV0~vo#}&0wkPxK@~H257I)xeac!T|2~+jmRK zFix5t(=BPi*fV`>Pr#JLS&K94<3|m*q4%#5g3QA3hB9q?isfPOo=n)}Q{kU(y1s7T#Wuo*)^;*f9OXAxRC; zg%J-XNLDbepPqeKQi<{Y^uEKA{-AA;_YX^EGA^C&c|_8hcO{#Y0+ZtbCeRSu(diS9 zNCq*kpZ?^Cq&?%&=_*Gh6-DNN_CSFqQ5;zmIUMJG1}$r!er7kH^7NXck``SoMGnUu5FR79Bby?JW9xMG zW0Fcto$S-Kk4b7Mu6fWZ;P|k$O+bNNmw`hXe5w!==%^tDCdUoTSptiummHHc=C}&F zKY&9Ze)@)El5(OaxnbP~CQw!4cz`)eVDj_}QzRwzA43%)6tICj%bX>EU6amnNlA{6 zP)*aJRiE@{uWWV+4?$rP5QLIUyA7i^c*o4$6MBrj;& z2WTS=ljroCCnTd8w@;U!E-AwSy>29adelkDP{!@kH=LAIW9*zRI9XO?`sD*m zA552&0$Z_DQdIB;s{)hb4c08jrF$DdC)UPKw>l*$$8qIRGlZ82;=RAz3_8gsmADrpox~yJkqvRawXmDx^Vc&Om#EKrIOtB?f^-ppk9R zX8MYSI?A`VZ1hd#!N{W4$!qSjtm0Vrtg_4sls?|`oo!$p-gjGr{7lx| zH{DT9)ReJtdO{z&CW!lQ2cwkiPCjTi1aw?B6KL@&s{&g#=yW6nHqYt1wlHdglqT$C zRA9O-G+nTtT@6GVY-N-MiRY_}g83hIGAc9Pp1xaMR1PG3TU}IYy5Ag0r^pG@+CXcX z%t5Uj(8xHW;{uQfp8!O}kwFo(KV2-_F~^ga!I8o79)kj-BU6dvf)A6yi`tZ!vXwv= zsNZ7%A7G%!qQK zs`|);L)i&-P>I6?-o>iG%zcQRkp(n+xqvZSiAe=?OB;iuK#8M3sUw32xVH})wKiiC zPyp$0)D_qa8wvpRi5UcrfpQ*_I*I9g^Ca0gR-FRJZR&KHd6LQ;(|cP$F`YWyeV(MD z?+cyey70?oJWVWnggSWdx7rfriN$KhzuS zBxM;FPQN}+(u5bTm%(w$-RTnZCFk<2**O{HT+pI{E7K3omz3nV2r2-W1X8EZUMwl= z=!4`5W(6kj$|A?J3|WrMj1CLTrb3>gP#X(h~!EDj)R92Q7Qa=@LF zxIj`F!RuKdDKC`@^Bgmi15B$cFW8Ak7f5O_E}Z^ufuyX|vPVrI>kz6HSRDDY6&MBH zPT#mfQcS$_XFDhl!Q`Q-cIxzdD7L z0d=z`?F1)!NJ1!nFm>`bhnt$NcPmV!I{8B38Pane#rLqsH= zUMgurT<&2Zkb9(=Sp*hNPg^EwiWapimPt<2y#k5a6^vN|@1fiI5K)_;zyuyzhevJp za!L6Fl&FPdsLKplj=ZR`%L9pB4n_3Xog$#Y1j_!P^E<%HsS6aCKttJxEdOdbavpoX zKvH_T!3s%Md>PGug`_6q!s(SOBxR+s#x6TJb_wS+L)56-0FAmvPDs=-I8FeS+u*X3 z(aE6A$)J7u!nKm(2Ag3Ue!xYg0;9ljXkiIHe;9nG18Cab5nfnkuSAaSB`YQM5P24| z#|gc#oc?hoa-LnbPEw9>;dG-_lKN;7o3~1`jd8;Cud5`F=)3^MFSFwg#w^%TLW~M9 zClx3#g99EOy-!z5$|oXx0;--kKu6Mp&K(gzie6?=dC9843_4$#9WC?1qZeii_@V{S zAu#YVG}CTr_BNuNf2Q z{$7nNMC`p>D`|?ztZM5d?a7P1qw6Fs&|;5ky<`pJgz4?;CHL3=fyCYkY_SIqS6KAH zgAg9U9iY$xt)^hsWk?29gpi;B-vR=T*e48Gjxx9-Rv%WKfs18~n4JJJegY^mx}lZz zmknq!E36KSF$H!7RssCcEGe`CWFlz6p8;t7d$s^9+Bbm2H-Kz{Ribd8fY#+PWGj#u z?Yx^LGk6-f2P`u)R_!P6gXmK0-LI{o`* zl&%V^0Jy6nGd+5Xq#5J->65ld+M68}aAX9}FN3Zd1yz{fwG*HQ{{^Nj&~dT?Sh_N- z@UD#Tbla_xs@#zN%&}$x1-9w?k4lPKFGKItu);evpyir~P7P>d2c%Q;wN(H#+{Qkg z|Cl7`GLIWuB{h-yHf&h?Hp{0gZId)+{5n1VxTN0nmTi)Lj4P&J-X>|!xDwRekeaT$ z9nsqQINfi%q%Wuk@^Sk7?UMQ;ppXR}mkAmiWb%|&^miOn7%SiKR8`dSp5a4AM%1V3w$ZC zA~R^p1Xk(J0EOucHn7z6{GF2W9MHbX$LZ5RJZN9#O_t^4gmzdyGJ$$5 za@m(2!W&1 zeNlJ~dnLU@PYWtAfwnLRoB=VWfL3P-9G!lDuVf(O(dmZ!BrO@wOwZmYIiK<9bng9N zp6h-|4aUvW^Y%-MNt_kr;Q?94puhmyJa`qdLPg;0^f~(_RTz&>KM0|oK`8D6U_pZe zk_v2~X?THi)58u(YBS!M-gH3H*c`ll4Z8Y>QQ)K?q^-ta#?%4oZGm?5GJ!_X1rDMG z?wbRWl8jx`*$zt1W;`=}(Lu@Z`VXw!0;dE)J#>A>D@;nvMWEtd;H;nmtH4=7kb`Tz zK}SUhKm+;)6C|V+7z9qSfqD$!{sLr0oxnLzWPk@Pz=6%kt-u5xc>&K{F@bmwm_a8x z%ob$g2DJbg92ZQlI3(#mU?U?Kg$N705MklLGcv;Xb-LCul)?l1z{qlxfsv2X*B_IV z7k!UqVB|S?V8j4AFaqz$@`9G#F@c9pu5(TT!x)c&2S%W!j~s}8I9*Z_Wc*G^F^oZxkJBwrNy@_pMZg7(JZc$( zIw}Hou{noL+HS(uVQq^sT2Q0~yaxXFnrp$QU}^ z@{FXMY(EcZ`=34|4|vNys1>Hf1Rfx8

    -}O@Lo<`qVR$A&hUPzd9qC!uW2w|5-^% z#y8tD&q}f|GQOOicTUoT@#XZz=Oj%TUrxVyPST9=<#d7bV9Ncxqy^Jg_UVo1B{dme zPQQ0qQg-@@^OE`!Z`c)>zy}#J^NK34gQmwdm>SpwUQg%0AgK@1;c!86BIC>HHWwvT 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

    704ja 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 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"); }

    ^tw2h4^^gLh~t$o_&uW;r#Cr}y!jub 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