authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-12 10:30:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-29 14:30:26-07:00
log1b88ce3b0cdeec7d23ded22e04aee8fc43f7bb5f
treecaf9a23e0588ea88c131cdb6102e029b19f04938
parent2d4e24dd6b1178efabb06928062836f059f2af00

Merge pull request #16788 from xxxbxxx/objcopy-step

build/ObjCopy: strip debug info to a separate elf file

2 files changed, 78 insertions(+), 25 deletions(-)

lib/std/Build/Step/ObjCopy.zig+47-2
...@@ -17,22 +17,40 @@ pub const base_id: Step.Id = .objcopy;...@@ -17,22 +17,40 @@ pub const base_id: Step.Id = .objcopy;
17pub const RawFormat = enum {17pub const RawFormat = enum {
18 bin,18 bin,
19 hex,19 hex,
20 elf,
21};
22
23pub const Strip = enum {
24 none,
25 debug,
26 debug_and_symbols,
20};27};
2128
22step: Step,29step: Step,
23input_file: std.Build.LazyPath,30input_file: std.Build.LazyPath,
24basename: []const u8,31basename: []const u8,
25output_file: std.Build.GeneratedFile,32output_file: std.Build.GeneratedFile,
33output_file_debug: ?std.Build.GeneratedFile,
2634
27format: ?RawFormat,35format: ?RawFormat,
28only_section: ?[]const u8,36only_section: ?[]const u8,
29pad_to: ?u64,37pad_to: ?u64,
38strip: Strip,
39compress_debug: bool,
3040
31pub const Options = struct {41pub const Options = struct {
32 basename: ?[]const u8 = null,42 basename: ?[]const u8 = null,
33 format: ?RawFormat = null,43 format: ?RawFormat = null,
34 only_section: ?[]const u8 = null,44 only_section: ?[]const u8 = null,
35 pad_to: ?u64 = null,45 pad_to: ?u64 = null,
46
47 compress_debug: bool = false,
48 strip: Strip = .none,
49
50 /// Put the stripped out debug sections in a separate file.
51 /// note: the `basename` is baked into the elf file to specify the link to the separate debug file.
52 /// see https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
53 extract_to_separate_file: bool = false,
36};54};
3755
38pub fn create(56pub fn create(
...@@ -51,10 +69,12 @@ pub fn create(...@@ -51,10 +69,12 @@ pub fn create(
51 .input_file = input_file,69 .input_file = input_file,
52 .basename = options.basename orelse input_file.getDisplayName(),70 .basename = options.basename orelse input_file.getDisplayName(),
53 .output_file = std.Build.GeneratedFile{ .step = &self.step },71 .output_file = std.Build.GeneratedFile{ .step = &self.step },
5472 .output_file_debug = if (options.strip != .none and options.extract_to_separate_file) std.Build.GeneratedFile{ .step = &self.step } else null,
55 .format = options.format,73 .format = options.format,
56 .only_section = options.only_section,74 .only_section = options.only_section,
57 .pad_to = options.pad_to,75 .pad_to = options.pad_to,
76 .strip = options.strip,
77 .compress_debug = options.compress_debug,
58 };78 };
59 input_file.addStepDependencies(&self.step);79 input_file.addStepDependencies(&self.step);
60 return self;80 return self;
...@@ -66,6 +86,9 @@ pub const getOutputSource = getOutput;...@@ -66,6 +86,9 @@ pub const getOutputSource = getOutput;
66pub fn getOutput(self: *const ObjCopy) std.Build.LazyPath {86pub fn getOutput(self: *const ObjCopy) std.Build.LazyPath {
67 return .{ .generated = &self.output_file };87 return .{ .generated = &self.output_file };
68}88}
89pub fn getOutputSeparatedDebug(self: *const ObjCopy) ?std.Build.LazyPath {
90 return if (self.output_file_debug) |*file| .{ .generated = file } else null;
91}
6992
70fn make(step: *Step, prog_node: *std.Progress.Node) !void {93fn make(step: *Step, prog_node: *std.Progress.Node) !void {
71 const b = step.owner;94 const b = step.owner;
...@@ -83,6 +106,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -83,6 +106,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
83 man.hash.addOptionalBytes(self.only_section);106 man.hash.addOptionalBytes(self.only_section);
84 man.hash.addOptional(self.pad_to);107 man.hash.addOptional(self.pad_to);
85 man.hash.addOptional(self.format);108 man.hash.addOptional(self.format);
109 man.hash.add(self.compress_debug);
110 man.hash.add(self.strip);
111 man.hash.add(self.output_file_debug != null);
86112
87 if (try step.cacheHit(&man)) {113 if (try step.cacheHit(&man)) {
88 // Cache hit, skip subprocess execution.114 // Cache hit, skip subprocess execution.
...@@ -90,12 +116,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -90,12 +116,18 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
90 self.output_file.path = try b.cache_root.join(b.allocator, &.{116 self.output_file.path = try b.cache_root.join(b.allocator, &.{
91 "o", &digest, self.basename,117 "o", &digest, self.basename,
92 });118 });
119 if (self.output_file_debug) |*file| {
120 file.path = try b.cache_root.join(b.allocator, &.{
121 "o", &digest, b.fmt("{s}.debug", .{self.basename}),
122 });
123 }
93 return;124 return;
94 }125 }
95126
96 const digest = man.final();127 const digest = man.final();
97 const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename });
98 const cache_path = "o" ++ fs.path.sep_str ++ digest;128 const cache_path = "o" ++ fs.path.sep_str ++ digest;
129 const full_dest_path = try b.cache_root.join(b.allocator, &.{ cache_path, self.basename });
130 const full_dest_path_debug = try b.cache_root.join(b.allocator, &.{ cache_path, b.fmt("{s}.debug", .{self.basename}) });
99 b.cache_root.handle.makePath(cache_path) catch |err| {131 b.cache_root.handle.makePath(cache_path) catch |err| {
100 return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) });132 return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) });
101 };133 };
...@@ -106,13 +138,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -106,13 +138,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
106 if (self.only_section) |only_section| {138 if (self.only_section) |only_section| {
107 try argv.appendSlice(&.{ "-j", only_section });139 try argv.appendSlice(&.{ "-j", only_section });
108 }140 }
141 switch (self.strip) {
142 .none => {},
143 .debug => try argv.appendSlice(&.{"--strip-debug"}),
144 .debug_and_symbols => try argv.appendSlice(&.{"--strip-all"}),
145 }
109 if (self.pad_to) |pad_to| {146 if (self.pad_to) |pad_to| {
110 try argv.appendSlice(&.{ "--pad-to", b.fmt("{d}", .{pad_to}) });147 try argv.appendSlice(&.{ "--pad-to", b.fmt("{d}", .{pad_to}) });
111 }148 }
112 if (self.format) |format| switch (format) {149 if (self.format) |format| switch (format) {
113 .bin => try argv.appendSlice(&.{ "-O", "binary" }),150 .bin => try argv.appendSlice(&.{ "-O", "binary" }),
114 .hex => try argv.appendSlice(&.{ "-O", "hex" }),151 .hex => try argv.appendSlice(&.{ "-O", "hex" }),
152 .elf => try argv.appendSlice(&.{ "-O", "elf" }),
115 };153 };
154 if (self.compress_debug) {
155 try argv.appendSlice(&.{"--compress-debug-sections"});
156 }
157 if (self.output_file_debug != null) {
158 try argv.appendSlice(&.{b.fmt("--extract-to={s}", .{full_dest_path_debug})});
159 }
116160
117 try argv.appendSlice(&.{ full_src_path, full_dest_path });161 try argv.appendSlice(&.{ full_src_path, full_dest_path });
118162
...@@ -120,5 +164,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -120,5 +164,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
120 _ = try step.evalZigProcess(argv.items, prog_node);164 _ = try step.evalZigProcess(argv.items, prog_node);
121165
122 self.output_file.path = full_dest_path;166 self.output_file.path = full_dest_path;
167 if (self.output_file_debug) |*file| file.path = full_dest_path_debug;
123 try man.writeManifest();168 try man.writeManifest();
124}169}
src/objcopy.zig+31-23
...@@ -104,9 +104,6 @@ pub fn cmdObjCopy(...@@ -104,9 +104,6 @@ pub fn cmdObjCopy(
104 fatal("unable to open '{s}': {s}", .{ input, @errorName(err) });104 fatal("unable to open '{s}': {s}", .{ input, @errorName(err) });
105 defer in_file.close();105 defer in_file.close();
106106
107 var out_file = try fs.cwd().createFile(output, .{});
108 defer out_file.close();
109
110 const elf_hdr = std.elf.Header.read(in_file) catch |err| switch (err) {107 const elf_hdr = std.elf.Header.read(in_file) catch |err| switch (err) {
111 error.InvalidElfMagic => fatal("not an ELF file: '{s}'", .{input}),108 error.InvalidElfMagic => fatal("not an ELF file: '{s}'", .{input}),
112 else => fatal("unable to read '{s}': {s}", .{ input, @errorName(err) }),109 else => fatal("unable to read '{s}': {s}", .{ input, @errorName(err) }),
...@@ -126,6 +123,17 @@ pub fn cmdObjCopy(...@@ -126,6 +123,17 @@ pub fn cmdObjCopy(
126 }123 }
127 };124 };
128125
126 const mode = mode: {
127 if (out_fmt != .elf or !only_keep_debug)
128 break :mode fs.File.default_mode;
129 if (in_file.stat()) |stat|
130 break :mode stat.mode
131 else |_|
132 break :mode fs.File.default_mode;
133 };
134 var out_file = try fs.cwd().createFile(output, .{ .mode = mode });
135 defer out_file.close();
136
129 switch (out_fmt) {137 switch (out_fmt) {
130 .hex, .raw => {138 .hex, .raw => {
131 if (strip_debug or strip_all or only_keep_debug)139 if (strip_debug or strip_all or only_keep_debug)
...@@ -345,7 +353,7 @@ const BinaryElfOutput = struct {...@@ -345,7 +353,7 @@ const BinaryElfOutput = struct {
345353
346 const shstrtab_shdr = (try section_headers.next()).?;354 const shstrtab_shdr = (try section_headers.next()).?;
347355
348 const buffer = try allocator.alloc(u8, @as(usize, @intCast(shstrtab_shdr.sh_size)));356 const buffer = try allocator.alloc(u8, @intCast(shstrtab_shdr.sh_size));
349 errdefer allocator.free(buffer);357 errdefer allocator.free(buffer);
350358
351 const num_read = try elf_file.preadAll(buffer, shstrtab_shdr.sh_offset);359 const num_read = try elf_file.preadAll(buffer, shstrtab_shdr.sh_offset);
...@@ -363,7 +371,7 @@ const BinaryElfOutput = struct {...@@ -363,7 +371,7 @@ const BinaryElfOutput = struct {
363371
364 newSection.binaryOffset = 0;372 newSection.binaryOffset = 0;
365 newSection.elfOffset = section.sh_offset;373 newSection.elfOffset = section.sh_offset;
366 newSection.fileSize = @as(usize, @intCast(section.sh_size));374 newSection.fileSize = @intCast(section.sh_size);
367 newSection.segment = null;375 newSection.segment = null;
368376
369 newSection.name = if (self.shstrtab) |shstrtab|377 newSection.name = if (self.shstrtab) |shstrtab|
...@@ -382,7 +390,7 @@ const BinaryElfOutput = struct {...@@ -382,7 +390,7 @@ const BinaryElfOutput = struct {
382390
383 newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr;391 newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr;
384 newSegment.virtualAddress = phdr.p_vaddr;392 newSegment.virtualAddress = phdr.p_vaddr;
385 newSegment.fileSize = @as(usize, @intCast(phdr.p_filesz));393 newSegment.fileSize = @intCast(phdr.p_filesz);
386 newSegment.elfOffset = phdr.p_offset;394 newSegment.elfOffset = phdr.p_offset;
387 newSegment.binaryOffset = 0;395 newSegment.binaryOffset = 0;
388 newSegment.firstSection = null;396 newSegment.firstSection = null;
...@@ -478,8 +486,8 @@ const HexWriter = struct {...@@ -478,8 +486,8 @@ const HexWriter = struct {
478 const MAX_PAYLOAD_LEN: u8 = 16;486 const MAX_PAYLOAD_LEN: u8 = 16;
479487
480 fn addressParts(address: u16) [2]u8 {488 fn addressParts(address: u16) [2]u8 {
481 const msb = @as(u8, @truncate(address >> 8));489 const msb: u8 = @truncate(address >> 8);
482 const lsb = @as(u8, @truncate(address));490 const lsb: u8 = @truncate(address);
483 return [2]u8{ msb, lsb };491 return [2]u8{ msb, lsb };
484 }492 }
485493
...@@ -508,14 +516,14 @@ const HexWriter = struct {...@@ -508,14 +516,14 @@ const HexWriter = struct {
508516
509 fn Data(address: u32, data: []const u8) Record {517 fn Data(address: u32, data: []const u8) Record {
510 return Record{518 return Record{
511 .address = @as(u16, @intCast(address % 0x10000)),519 .address = @intCast(address % 0x10000),
512 .payload = .{ .Data = data },520 .payload = .{ .Data = data },
513 };521 };
514 }522 }
515523
516 fn Address(address: u32) Record {524 fn Address(address: u32) Record {
517 assert(address > 0xFFFF);525 assert(address > 0xFFFF);
518 const segment = @as(u16, @intCast(address / 0x10000));526 const segment: u16 = @intCast(address / 0x10000);
519 if (address > 0xFFFFF) {527 if (address > 0xFFFFF) {
520 return Record{528 return Record{
521 .address = 0,529 .address = 0,
...@@ -540,7 +548,7 @@ const HexWriter = struct {...@@ -540,7 +548,7 @@ const HexWriter = struct {
540 fn checksum(self: Record) u8 {548 fn checksum(self: Record) u8 {
541 const payload_bytes = self.getPayloadBytes();549 const payload_bytes = self.getPayloadBytes();
542550
543 var sum: u8 = @as(u8, @intCast(payload_bytes.len));551 var sum: u8 = @intCast(payload_bytes.len);
544 const parts = addressParts(self.address);552 const parts = addressParts(self.address);
545 sum +%= parts[0];553 sum +%= parts[0];
546 sum +%= parts[1];554 sum +%= parts[1];
...@@ -574,10 +582,10 @@ const HexWriter = struct {...@@ -574,10 +582,10 @@ const HexWriter = struct {
574 var buf: [MAX_PAYLOAD_LEN]u8 = undefined;582 var buf: [MAX_PAYLOAD_LEN]u8 = undefined;
575 var bytes_read: usize = 0;583 var bytes_read: usize = 0;
576 while (bytes_read < segment.fileSize) {584 while (bytes_read < segment.fileSize) {
577 const row_address = @as(u32, @intCast(segment.physicalAddress + bytes_read));585 const row_address: u32 = @intCast(segment.physicalAddress + bytes_read);
578586
579 const remaining = segment.fileSize - bytes_read;587 const remaining = segment.fileSize - bytes_read;
580 const to_read = @as(usize, @intCast(@min(remaining, MAX_PAYLOAD_LEN)));588 const to_read: usize = @intCast(@min(remaining, MAX_PAYLOAD_LEN));
581 const did_read = try elf_file.preadAll(buf[0..to_read], segment.elfOffset + bytes_read);589 const did_read = try elf_file.preadAll(buf[0..to_read], segment.elfOffset + bytes_read);
582 if (did_read < to_read) return error.UnexpectedEOF;590 if (did_read < to_read) return error.UnexpectedEOF;
583591
...@@ -593,7 +601,7 @@ const HexWriter = struct {...@@ -593,7 +601,7 @@ const HexWriter = struct {
593 try Record.Address(address).write(self.out_file);601 try Record.Address(address).write(self.out_file);
594 }602 }
595 try record.write(self.out_file);603 try record.write(self.out_file);
596 self.prev_addr = @as(u32, @intCast(record.address + data.len));604 self.prev_addr = @intCast(record.address + data.len);
597 }605 }
598606
599 fn writeEOF(self: HexWriter) File.WriteError!void {607 fn writeEOF(self: HexWriter) File.WriteError!void {
...@@ -814,7 +822,7 @@ fn ElfFile(comptime is_64: bool) type {...@@ -814,7 +822,7 @@ fn ElfFile(comptime is_64: bool) type {
814 const need_strings = (idx == header.shstrndx);822 const need_strings = (idx == header.shstrndx);
815823
816 if (need_data or need_strings) {824 if (need_data or need_strings) {
817 const buffer = try allocator.alignedAlloc(u8, section_memory_align, @as(usize, @intCast(section.section.sh_size)));825 const buffer = try allocator.alignedAlloc(u8, section_memory_align, @intCast(section.section.sh_size));
818 const bytes_read = try in_file.preadAll(buffer, section.section.sh_offset);826 const bytes_read = try in_file.preadAll(buffer, section.section.sh_offset);
819 if (bytes_read != section.section.sh_size) return error.TRUNCATED_ELF;827 if (bytes_read != section.section.sh_size) return error.TRUNCATED_ELF;
820 section.payload = buffer;828 section.payload = buffer;
...@@ -935,7 +943,7 @@ fn ElfFile(comptime is_64: bool) type {...@@ -935,7 +943,7 @@ fn ElfFile(comptime is_64: bool) type {
935 const update = &sections_update[self.raw_elf_header.e_shstrndx];943 const update = &sections_update[self.raw_elf_header.e_shstrndx];
936944
937 const name: []const u8 = ".gnu_debuglink";945 const name: []const u8 = ".gnu_debuglink";
938 const new_offset = @as(u32, @intCast(strtab.payload.?.len));946 const new_offset: u32 = @intCast(strtab.payload.?.len);
939 const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1);947 const buf = try allocator.alignedAlloc(u8, section_memory_align, new_offset + name.len + 1);
940 @memcpy(buf[0..new_offset], strtab.payload.?);948 @memcpy(buf[0..new_offset], strtab.payload.?);
941 @memcpy(buf[new_offset..][0..name.len], name);949 @memcpy(buf[new_offset..][0..name.len], name);
...@@ -965,7 +973,7 @@ fn ElfFile(comptime is_64: bool) type {...@@ -965,7 +973,7 @@ fn ElfFile(comptime is_64: bool) type {
965 update.payload = payload;973 update.payload = payload;
966 update.section = section.section;974 update.section = section.section;
967 update.section.?.sh_addralign = @alignOf(Elf_Chdr);975 update.section.?.sh_addralign = @alignOf(Elf_Chdr);
968 update.section.?.sh_size = @as(Elf_OffSize, @intCast(payload.len));976 update.section.?.sh_size = @intCast(payload.len);
969 update.section.?.sh_flags |= elf.SHF_COMPRESSED;977 update.section.?.sh_flags |= elf.SHF_COMPRESSED;
970 }978 }
971 }979 }
...@@ -1032,7 +1040,7 @@ fn ElfFile(comptime is_64: bool) type {...@@ -1032,7 +1040,7 @@ fn ElfFile(comptime is_64: bool) type {
1032 dest.sh_info = sections_update[src.sh_info].remap_idx;1040 dest.sh_info = sections_update[src.sh_info].remap_idx;
10331041
1034 if (payload) |data|1042 if (payload) |data|
1035 dest.sh_size = @as(Elf_OffSize, @intCast(data.len));1043 dest.sh_size = @intCast(data.len);
10361044
1037 const addralign = if (src.sh_addralign == 0 or dest.sh_type == elf.SHT_NOBITS) 1 else src.sh_addralign;1045 const addralign = if (src.sh_addralign == 0 or dest.sh_type == elf.SHT_NOBITS) 1 else src.sh_addralign;
1038 dest.sh_offset = std.mem.alignForward(Elf_OffSize, eof_offset, addralign);1046 dest.sh_offset = std.mem.alignForward(Elf_OffSize, eof_offset, addralign);
...@@ -1110,7 +1118,7 @@ fn ElfFile(comptime is_64: bool) type {...@@ -1110,7 +1118,7 @@ fn ElfFile(comptime is_64: bool) type {
1110 .sh_flags = 0,1118 .sh_flags = 0,
1111 .sh_addr = 0,1119 .sh_addr = 0,
1112 .sh_offset = eof_offset,1120 .sh_offset = eof_offset,
1113 .sh_size = @as(Elf_OffSize, @intCast(payload.len)),1121 .sh_size = @intCast(payload.len),
1114 .sh_link = elf.SHN_UNDEF,1122 .sh_link = elf.SHN_UNDEF,
1115 .sh_info = elf.SHN_UNDEF,1123 .sh_info = elf.SHN_UNDEF,
1116 .sh_addralign = 4,1124 .sh_addralign = 4,
...@@ -1232,7 +1240,7 @@ const ElfFileHelper = struct {...@@ -1232,7 +1240,7 @@ const ElfFileHelper = struct {
1232 fused_cmd = null;1240 fused_cmd = null;
1233 }1241 }
1234 if (data.out_offset > offset) {1242 if (data.out_offset > offset) {
1235 consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@as(usize, @intCast(data.out_offset - offset))], .out_offset = offset } });1243 consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(data.out_offset - offset)], .out_offset = offset } });
1236 }1244 }
1237 consolidated.appendAssumeCapacity(cmd);1245 consolidated.appendAssumeCapacity(cmd);
1238 offset = data.out_offset + data.data.len;1246 offset = data.out_offset + data.data.len;
...@@ -1249,7 +1257,7 @@ const ElfFileHelper = struct {...@@ -1249,7 +1257,7 @@ const ElfFileHelper = struct {
1249 } else {1257 } else {
1250 consolidated.appendAssumeCapacity(prev);1258 consolidated.appendAssumeCapacity(prev);
1251 if (range.out_offset > offset) {1259 if (range.out_offset > offset) {
1252 consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@as(usize, @intCast(range.out_offset - offset))], .out_offset = offset } });1260 consolidated.appendAssumeCapacity(.{ .write_data = .{ .data = zeroes[0..@intCast(range.out_offset - offset)], .out_offset = offset } });
1253 }1261 }
1254 fused_cmd = cmd;1262 fused_cmd = cmd;
1255 }1263 }
...@@ -1286,7 +1294,7 @@ const ElfFileHelper = struct {...@@ -1286,7 +1294,7 @@ const ElfFileHelper = struct {
1286 var section_reader = std.io.limitedReader(in_file.reader(), size);1294 var section_reader = std.io.limitedReader(in_file.reader(), size);
12871295
1288 // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed.1296 // allocate as large as decompressed data. if the compression doesn't fit, keep the data uncompressed.
1289 const compressed_data = try allocator.alignedAlloc(u8, 8, @as(usize, @intCast(size)));1297 const compressed_data = try allocator.alignedAlloc(u8, 8, @intCast(size));
1290 var compressed_stream = std.io.fixedBufferStream(compressed_data);1298 var compressed_stream = std.io.fixedBufferStream(compressed_data);
12911299
1292 try compressed_stream.writer().writeAll(prefix);1300 try compressed_stream.writer().writeAll(prefix);
...@@ -1317,7 +1325,7 @@ const ElfFileHelper = struct {...@@ -1317,7 +1325,7 @@ const ElfFileHelper = struct {
1317 };1325 };
1318 }1326 }
13191327
1320 const compressed_len = @as(usize, @intCast(compressed_stream.getPos() catch unreachable));1328 const compressed_len: usize = @intCast(compressed_stream.getPos() catch unreachable);
1321 const data = allocator.realloc(compressed_data, compressed_len) catch compressed_data;1329 const data = allocator.realloc(compressed_data, compressed_len) catch compressed_data;
1322 return data[0..compressed_len];1330 return data[0..compressed_len];
1323 }1331 }