| ... | @@ -247,15 +247,27 @@ const ComputeCompareExpected = struct { | ... | @@ -247,15 +247,27 @@ const ComputeCompareExpected = struct { |
| 247 | | 247 | |
| 248 | const Check = struct { | 248 | const Check = struct { |
| 249 | kind: Kind, | 249 | kind: Kind, |
| | 250 | payload: Payload, |
| | 251 | data: std.ArrayList(u8), |
| 250 | actions: std.ArrayList(Action), | 252 | actions: std.ArrayList(Action), |
| 251 | | 253 | |
| 252 | fn create(allocator: Allocator, kind: Kind) Check { | 254 | fn create(allocator: Allocator, kind: Kind) Check { |
| 253 | return .{ | 255 | return .{ |
| 254 | .kind = kind, | 256 | .kind = kind, |
| | 257 | .payload = .{ .none = {} }, |
| | 258 | .data = std.ArrayList(u8).init(allocator), |
| 255 | .actions = std.ArrayList(Action).init(allocator), | 259 | .actions = std.ArrayList(Action).init(allocator), |
| 256 | }; | 260 | }; |
| 257 | } | 261 | } |
| 258 | | 262 | |
| | 263 | fn dumpSection(allocator: Allocator, name: [:0]const u8) Check { |
| | 264 | var check = Check.create(allocator, .dump_section); |
| | 265 | const off: u32 = @intCast(check.data.items.len); |
| | 266 | check.data.writer().print("{s}\x00", .{name}) catch @panic("OOM"); |
| | 267 | check.payload = .{ .dump_section = off }; |
| | 268 | return check; |
| | 269 | } |
| | 270 | |
| 259 | fn extract(self: *Check, phrase: SearchPhrase) void { | 271 | fn extract(self: *Check, phrase: SearchPhrase) void { |
| 260 | self.actions.append(.{ | 272 | self.actions.append(.{ |
| 261 | .tag = .extract, | 273 | .tag = .extract, |
| ... | @@ -305,6 +317,13 @@ const Check = struct { | ... | @@ -305,6 +317,13 @@ const Check = struct { |
| 305 | dyld_lazy_bind, | 317 | dyld_lazy_bind, |
| 306 | exports, | 318 | exports, |
| 307 | compute_compare, | 319 | compute_compare, |
| | 320 | dump_section, |
| | 321 | }; |
| | 322 | |
| | 323 | const Payload = union { |
| | 324 | none: void, |
| | 325 | /// Null-delimited string in the 'data' buffer. |
| | 326 | dump_section: u32, |
| 308 | }; | 327 | }; |
| 309 | }; | 328 | }; |
| 310 | | 329 | |
| ... | @@ -513,6 +532,11 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void { | ... | @@ -513,6 +532,11 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void { |
| 513 | self.checkExact(label); | 532 | self.checkExact(label); |
| 514 | } | 533 | } |
| 515 | | 534 | |
| | 535 | pub fn dumpSection(self: *CheckObject, name: [:0]const u8) void { |
| | 536 | const new_check = Check.dumpSection(self.step.owner.allocator, name); |
| | 537 | self.checks.append(new_check) catch @panic("OOM"); |
| | 538 | } |
| | 539 | |
| 516 | /// Creates a new standalone, singular check which allows running simple binary operations | 540 | /// Creates a new standalone, singular check which allows running simple binary operations |
| 517 | /// on the extracted variables. It will then compare the reduced program with the value of | 541 | /// on the extracted variables. It will then compare the reduced program with the value of |
| 518 | /// the expected variable. | 542 | /// the expected variable. |
| ... | @@ -564,13 +588,44 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -564,13 +588,44 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 564 | } | 588 | } |
| 565 | | 589 | |
| 566 | const output = switch (self.obj_format) { | 590 | const output = switch (self.obj_format) { |
| 567 | .macho => try MachODumper.parseAndDump(step, chk.kind, contents), | 591 | .macho => try MachODumper.parseAndDump(step, chk, contents), |
| 568 | .elf => try ElfDumper.parseAndDump(step, chk.kind, contents), | 592 | .elf => try ElfDumper.parseAndDump(step, chk, contents), |
| 569 | .coff => return step.fail("TODO coff parser", .{}), | 593 | .coff => return step.fail("TODO coff parser", .{}), |
| 570 | .wasm => try WasmDumper.parseAndDump(step, chk.kind, contents), | 594 | .wasm => try WasmDumper.parseAndDump(step, chk, contents), |
| 571 | else => unreachable, | 595 | else => unreachable, |
| 572 | }; | 596 | }; |
| 573 | | 597 | |
| | 598 | // Depending on whether we requested dumping section verbatim or not, |
| | 599 | // we either format message string with escaped codes, or not to aid debugging |
| | 600 | // the failed test. |
| | 601 | const fmtMessageString = struct { |
| | 602 | fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(formatMessageString) { |
| | 603 | return .{ .data = .{ |
| | 604 | .kind = kind, |
| | 605 | .msg = msg, |
| | 606 | } }; |
| | 607 | } |
| | 608 | |
| | 609 | const Ctx = struct { |
| | 610 | kind: Check.Kind, |
| | 611 | msg: []const u8, |
| | 612 | }; |
| | 613 | |
| | 614 | fn formatMessageString( |
| | 615 | ctx: Ctx, |
| | 616 | comptime unused_fmt_string: []const u8, |
| | 617 | options: std.fmt.FormatOptions, |
| | 618 | writer: anytype, |
| | 619 | ) !void { |
| | 620 | _ = unused_fmt_string; |
| | 621 | _ = options; |
| | 622 | switch (ctx.kind) { |
| | 623 | .dump_section => try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}), |
| | 624 | else => try writer.writeAll(ctx.msg), |
| | 625 | } |
| | 626 | } |
| | 627 | }.fmtMessageString; |
| | 628 | |
| 574 | var it = mem.tokenizeAny(u8, output, "\r\n"); | 629 | var it = mem.tokenizeAny(u8, output, "\r\n"); |
| 575 | for (chk.actions.items) |act| { | 630 | for (chk.actions.items) |act| { |
| 576 | switch (act.tag) { | 631 | switch (act.tag) { |
| ... | @@ -585,7 +640,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -585,7 +640,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 585 | \\========= but parsed file does not contain it: ======= | 640 | \\========= but parsed file does not contain it: ======= |
| 586 | \\{s} | 641 | \\{s} |
| 587 | \\====================================================== | 642 | \\====================================================== |
| 588 | , .{ act.phrase.resolve(b, step), output }); | 643 | , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); |
| 589 | } | 644 | } |
| 590 | }, | 645 | }, |
| 591 | | 646 | |
| ... | @@ -600,7 +655,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -600,7 +655,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 600 | \\========= but parsed file does not contain it: ======= | 655 | \\========= but parsed file does not contain it: ======= |
| 601 | \\{s} | 656 | \\{s} |
| 602 | \\====================================================== | 657 | \\====================================================== |
| 603 | , .{ act.phrase.resolve(b, step), output }); | 658 | , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); |
| 604 | } | 659 | } |
| 605 | }, | 660 | }, |
| 606 | | 661 | |
| ... | @@ -614,7 +669,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -614,7 +669,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 614 | \\========= but parsed file does contain it: ======== | 669 | \\========= but parsed file does contain it: ======== |
| 615 | \\{s} | 670 | \\{s} |
| 616 | \\=================================================== | 671 | \\=================================================== |
| 617 | , .{ act.phrase.resolve(b, step), output }); | 672 | , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); |
| 618 | } | 673 | } |
| 619 | }, | 674 | }, |
| 620 | | 675 | |
| ... | @@ -629,7 +684,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -629,7 +684,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 629 | \\========= but parsed file does not contain it: ======= | 684 | \\========= but parsed file does not contain it: ======= |
| 630 | \\{s} | 685 | \\{s} |
| 631 | \\====================================================== | 686 | \\====================================================== |
| 632 | , .{ act.phrase.resolve(b, step), output }); | 687 | , .{ act.phrase.resolve(b, step), fmtMessageString(chk.kind, output) }); |
| 633 | } | 688 | } |
| 634 | }, | 689 | }, |
| 635 | | 690 | |
| ... | @@ -660,7 +715,7 @@ const MachODumper = struct { | ... | @@ -660,7 +715,7 @@ const MachODumper = struct { |
| 660 | } | 715 | } |
| 661 | }; | 716 | }; |
| 662 | | 717 | |
| 663 | fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { | 718 | fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 664 | const gpa = step.owner.allocator; | 719 | const gpa = step.owner.allocator; |
| 665 | var stream = std.io.fixedBufferStream(bytes); | 720 | var stream = std.io.fixedBufferStream(bytes); |
| 666 | const reader = stream.reader(); | 721 | const reader = stream.reader(); |
| ... | @@ -731,7 +786,7 @@ const MachODumper = struct { | ... | @@ -731,7 +786,7 @@ const MachODumper = struct { |
| 731 | } | 786 | } |
| 732 | } | 787 | } |
| 733 | | 788 | |
| 734 | switch (kind) { | 789 | switch (check.kind) { |
| 735 | .headers => { | 790 | .headers => { |
| 736 | try dumpHeader(hdr, writer); | 791 | try dumpHeader(hdr, writer); |
| 737 | | 792 | |
| ... | @@ -764,7 +819,7 @@ const MachODumper = struct { | ... | @@ -764,7 +819,7 @@ const MachODumper = struct { |
| 764 | if (dyld_info_lc == null) return step.fail("no dyld info found", .{}); | 819 | if (dyld_info_lc == null) return step.fail("no dyld info found", .{}); |
| 765 | const lc = dyld_info_lc.?; | 820 | const lc = dyld_info_lc.?; |
| 766 | | 821 | |
| 767 | switch (kind) { | 822 | switch (check.kind) { |
| 768 | .dyld_rebase => if (lc.rebase_size > 0) { | 823 | .dyld_rebase => if (lc.rebase_size > 0) { |
| 769 | const data = bytes[lc.rebase_off..][0..lc.rebase_size]; | 824 | const data = bytes[lc.rebase_off..][0..lc.rebase_size]; |
| 770 | try writer.writeAll(dyld_rebase_label ++ "\n"); | 825 | try writer.writeAll(dyld_rebase_label ++ "\n"); |
| ... | @@ -805,7 +860,7 @@ const MachODumper = struct { | ... | @@ -805,7 +860,7 @@ const MachODumper = struct { |
| 805 | return step.fail("no exports data found", .{}); | 860 | return step.fail("no exports data found", .{}); |
| 806 | }, | 861 | }, |
| 807 | | 862 | |
| 808 | else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(kind)}), | 863 | else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}), |
| 809 | } | 864 | } |
| 810 | | 865 | |
| 811 | return output.toOwnedSlice(); | 866 | return output.toOwnedSlice(); |
| ... | @@ -1633,14 +1688,14 @@ const ElfDumper = struct { | ... | @@ -1633,14 +1688,14 @@ const ElfDumper = struct { |
| 1633 | const dynamic_section_label = "dynamic section"; | 1688 | const dynamic_section_label = "dynamic section"; |
| 1634 | const archive_symtab_label = "archive symbol table"; | 1689 | const archive_symtab_label = "archive symbol table"; |
| 1635 | | 1690 | |
| 1636 | fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { | 1691 | fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 1637 | return parseAndDumpArchive(step, kind, bytes) catch |err| switch (err) { | 1692 | return parseAndDumpArchive(step, check, bytes) catch |err| switch (err) { |
| 1638 | error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, kind, bytes), | 1693 | error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, check, bytes), |
| 1639 | else => |e| return e, | 1694 | else => |e| return e, |
| 1640 | }; | 1695 | }; |
| 1641 | } | 1696 | } |
| 1642 | | 1697 | |
| 1643 | fn parseAndDumpArchive(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { | 1698 | fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 1644 | const gpa = step.owner.allocator; | 1699 | const gpa = step.owner.allocator; |
| 1645 | var stream = std.io.fixedBufferStream(bytes); | 1700 | var stream = std.io.fixedBufferStream(bytes); |
| 1646 | const reader = stream.reader(); | 1701 | const reader = stream.reader(); |
| ... | @@ -1702,13 +1757,13 @@ const ElfDumper = struct { | ... | @@ -1702,13 +1757,13 @@ const ElfDumper = struct { |
| 1702 | var output = std.ArrayList(u8).init(gpa); | 1757 | var output = std.ArrayList(u8).init(gpa); |
| 1703 | const writer = output.writer(); | 1758 | const writer = output.writer(); |
| 1704 | | 1759 | |
| 1705 | switch (kind) { | 1760 | switch (check.kind) { |
| 1706 | .archive_symtab => if (ctx.symtab.items.len > 0) { | 1761 | .archive_symtab => if (ctx.symtab.items.len > 0) { |
| 1707 | try ctx.dumpSymtab(writer); | 1762 | try ctx.dumpSymtab(writer); |
| 1708 | } else return step.fail("no archive symbol table found", .{}), | 1763 | } else return step.fail("no archive symbol table found", .{}), |
| 1709 | | 1764 | |
| 1710 | else => if (ctx.objects.items.len > 0) { | 1765 | else => if (ctx.objects.items.len > 0) { |
| 1711 | try ctx.dumpObjects(step, kind, writer); | 1766 | try ctx.dumpObjects(step, check, writer); |
| 1712 | } else return step.fail("empty archive", .{}), | 1767 | } else return step.fail("empty archive", .{}), |
| 1713 | } | 1768 | } |
| 1714 | | 1769 | |
| ... | @@ -1785,10 +1840,10 @@ const ElfDumper = struct { | ... | @@ -1785,10 +1840,10 @@ const ElfDumper = struct { |
| 1785 | } | 1840 | } |
| 1786 | } | 1841 | } |
| 1787 | | 1842 | |
| 1788 | fn dumpObjects(ctx: ArchiveContext, step: *Step, kind: Check.Kind, writer: anytype) !void { | 1843 | fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void { |
| 1789 | for (ctx.objects.items) |object| { | 1844 | for (ctx.objects.items) |object| { |
| 1790 | try writer.print("object {s}\n", .{object.name}); | 1845 | try writer.print("object {s}\n", .{object.name}); |
| 1791 | const output = try parseAndDumpObject(step, kind, ctx.data[object.off..][0..object.len]); | 1846 | const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]); |
| 1792 | defer ctx.gpa.free(output); | 1847 | defer ctx.gpa.free(output); |
| 1793 | try writer.print("{s}\n", .{output}); | 1848 | try writer.print("{s}\n", .{output}); |
| 1794 | } | 1849 | } |
| ... | @@ -1806,7 +1861,7 @@ const ElfDumper = struct { | ... | @@ -1806,7 +1861,7 @@ const ElfDumper = struct { |
| 1806 | }; | 1861 | }; |
| 1807 | }; | 1862 | }; |
| 1808 | | 1863 | |
| 1809 | fn parseAndDumpObject(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { | 1864 | fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 1810 | const gpa = step.owner.allocator; | 1865 | const gpa = step.owner.allocator; |
| 1811 | var stream = std.io.fixedBufferStream(bytes); | 1866 | var stream = std.io.fixedBufferStream(bytes); |
| 1812 | const reader = stream.reader(); | 1867 | const reader = stream.reader(); |
| ... | @@ -1859,7 +1914,7 @@ const ElfDumper = struct { | ... | @@ -1859,7 +1914,7 @@ const ElfDumper = struct { |
| 1859 | var output = std.ArrayList(u8).init(gpa); | 1914 | var output = std.ArrayList(u8).init(gpa); |
| 1860 | const writer = output.writer(); | 1915 | const writer = output.writer(); |
| 1861 | | 1916 | |
| 1862 | switch (kind) { | 1917 | switch (check.kind) { |
| 1863 | .headers => { | 1918 | .headers => { |
| 1864 | try ctx.dumpHeader(writer); | 1919 | try ctx.dumpHeader(writer); |
| 1865 | try ctx.dumpShdrs(writer); | 1920 | try ctx.dumpShdrs(writer); |
| ... | @@ -1878,7 +1933,13 @@ const ElfDumper = struct { | ... | @@ -1878,7 +1933,13 @@ const ElfDumper = struct { |
| 1878 | try ctx.dumpDynamicSection(shndx, writer); | 1933 | try ctx.dumpDynamicSection(shndx, writer); |
| 1879 | } else return step.fail("no .dynamic section found", .{}), | 1934 | } else return step.fail("no .dynamic section found", .{}), |
| 1880 | | 1935 | |
| 1881 | else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(kind)}), | 1936 | .dump_section => { |
| | 1937 | const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); |
| | 1938 | const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name}); |
| | 1939 | try ctx.dumpSection(shndx, writer); |
| | 1940 | }, |
| | 1941 | |
| | 1942 | else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}), |
| 1882 | } | 1943 | } |
| 1883 | | 1944 | |
| 1884 | return output.toOwnedSlice(); | 1945 | return output.toOwnedSlice(); |
| ... | @@ -2176,6 +2237,11 @@ const ElfDumper = struct { | ... | @@ -2176,6 +2237,11 @@ const ElfDumper = struct { |
| 2176 | } | 2237 | } |
| 2177 | } | 2238 | } |
| 2178 | | 2239 | |
| | 2240 | fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { |
| | 2241 | const data = ctx.getSectionContents(shndx); |
| | 2242 | try writer.print("{s}", .{data}); |
| | 2243 | } |
| | 2244 | |
| 2179 | inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { | 2245 | inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { |
| 2180 | const shdr = ctx.shdrs[shndx]; | 2246 | const shdr = ctx.shdrs[shndx]; |
| 2181 | return getString(ctx.shstrtab, shdr.sh_name); | 2247 | return getString(ctx.shstrtab, shdr.sh_name); |
| ... | @@ -2300,7 +2366,7 @@ const ElfDumper = struct { | ... | @@ -2300,7 +2366,7 @@ const ElfDumper = struct { |
| 2300 | const WasmDumper = struct { | 2366 | const WasmDumper = struct { |
| 2301 | const symtab_label = "symbols"; | 2367 | const symtab_label = "symbols"; |
| 2302 | | 2368 | |
| 2303 | fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { | 2369 | fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { |
| 2304 | const gpa = step.owner.allocator; | 2370 | const gpa = step.owner.allocator; |
| 2305 | var fbs = std.io.fixedBufferStream(bytes); | 2371 | var fbs = std.io.fixedBufferStream(bytes); |
| 2306 | const reader = fbs.reader(); | 2372 | const reader = fbs.reader(); |
| ... | @@ -2317,7 +2383,7 @@ const WasmDumper = struct { | ... | @@ -2317,7 +2383,7 @@ const WasmDumper = struct { |
| 2317 | errdefer output.deinit(); | 2383 | errdefer output.deinit(); |
| 2318 | const writer = output.writer(); | 2384 | const writer = output.writer(); |
| 2319 | | 2385 | |
| 2320 | switch (kind) { | 2386 | switch (check.kind) { |
| 2321 | .headers => { | 2387 | .headers => { |
| 2322 | while (reader.readByte()) |current_byte| { | 2388 | while (reader.readByte()) |current_byte| { |
| 2323 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { | 2389 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { |
| ... | @@ -2330,7 +2396,7 @@ const WasmDumper = struct { | ... | @@ -2330,7 +2396,7 @@ const WasmDumper = struct { |
| 2330 | } else |_| {} // reached end of stream | 2396 | } else |_| {} // reached end of stream |
| 2331 | }, | 2397 | }, |
| 2332 | | 2398 | |
| 2333 | else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(kind)}), | 2399 | else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), |
| 2334 | } | 2400 | } |
| 2335 | | 2401 | |
| 2336 | return output.toOwnedSlice(); | 2402 | return output.toOwnedSlice(); |
| ... | @@ -2364,7 +2430,7 @@ const WasmDumper = struct { | ... | @@ -2364,7 +2430,7 @@ const WasmDumper = struct { |
| 2364 | => { | 2430 | => { |
| 2365 | const entries = try std.leb.readULEB128(u32, reader); | 2431 | const entries = try std.leb.readULEB128(u32, reader); |
| 2366 | try writer.print("\nentries {d}\n", .{entries}); | 2432 | try writer.print("\nentries {d}\n", .{entries}); |
| 2367 | try dumpSection(step, section, data[fbs.pos..], entries, writer); | 2433 | try parseSection(step, section, data[fbs.pos..], entries, writer); |
| 2368 | }, | 2434 | }, |
| 2369 | .custom => { | 2435 | .custom => { |
| 2370 | const name_length = try std.leb.readULEB128(u32, reader); | 2436 | const name_length = try std.leb.readULEB128(u32, reader); |
| ... | @@ -2393,7 +2459,7 @@ const WasmDumper = struct { | ... | @@ -2393,7 +2459,7 @@ const WasmDumper = struct { |
| 2393 | } | 2459 | } |
| 2394 | } | 2460 | } |
| 2395 | | 2461 | |
| 2396 | fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { | 2462 | fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { |
| 2397 | var fbs = std.io.fixedBufferStream(data); | 2463 | var fbs = std.io.fixedBufferStream(data); |
| 2398 | const reader = fbs.reader(); | 2464 | const reader = fbs.reader(); |
| 2399 | | 2465 | |