| author | |
| committer | |
| log | ceec2393cfbe20b0993bd7cccb43e930438281fe |
| tree | e0818d84bca8c67e23c81c721a241401f9b06afa |
| parent | bc982e65cf77aa6551029620ceaec8f3ae49202f |
| parent | c087525edae2db147c89c83513a0778f87bee30d |
| signature |
Stage1 stack traces on windows4 files changed, 136 insertions(+), 52 deletions(-)
std/coff.zig+37-5| ... | @@ -19,6 +19,7 @@ const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; | ... | @@ -19,6 +19,7 @@ const IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b; |
| 19 | const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; | 19 | const IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b; |
| 20 | 20 | ||
| 21 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; | 21 | const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; |
| 22 | const IMAGE_DEBUG_TYPE_CODEVIEW = 2; | ||
| 22 | const DEBUG_DIRECTORY = 6; | 23 | const DEBUG_DIRECTORY = 6; |
| 23 | 24 | ||
| 24 | pub const CoffError = error{ | 25 | pub const CoffError = error{ |
| ... | @@ -28,6 +29,7 @@ pub const CoffError = error{ | ... | @@ -28,6 +29,7 @@ pub const CoffError = error{ |
| 28 | MissingCoffSection, | 29 | MissingCoffSection, |
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 32 | // Official documentation of the format: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format | ||
| 31 | pub const Coff = struct { | 33 | pub const Coff = struct { |
| 32 | in_file: File, | 34 | in_file: File, |
| 33 | allocator: *mem.Allocator, | 35 | allocator: *mem.Allocator, |
| ... | @@ -120,6 +122,7 @@ pub const Coff = struct { | ... | @@ -120,6 +122,7 @@ pub const Coff = struct { |
| 120 | 122 | ||
| 121 | pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { | 123 | pub fn getPdbPath(self: *Coff, buffer: []u8) !usize { |
| 122 | try self.loadSections(); | 124 | try self.loadSections(); |
| 125 | |||
| 123 | const header = blk: { | 126 | const header = blk: { |
| 124 | if (self.getSection(".buildid")) |section| { | 127 | if (self.getSection(".buildid")) |section| { |
| 125 | break :blk section.header; | 128 | break :blk section.header; |
| ... | @@ -130,14 +133,32 @@ pub const Coff = struct { | ... | @@ -130,14 +133,32 @@ pub const Coff = struct { |
| 130 | } | 133 | } |
| 131 | }; | 134 | }; |
| 132 | 135 | ||
| 133 | // The linker puts a chunk that contains the .pdb path right after the | ||
| 134 | // debug_directory. | ||
| 135 | const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY]; | 136 | const debug_dir = &self.pe_header.data_directory[DEBUG_DIRECTORY]; |
| 136 | const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; | 137 | const file_offset = debug_dir.virtual_address - header.virtual_address + header.pointer_to_raw_data; |
| 137 | try self.in_file.seekTo(file_offset + debug_dir.size); | ||
| 138 | 138 | ||
| 139 | var file_stream = self.in_file.inStream(); | 139 | var file_stream = self.in_file.inStream(); |
| 140 | const in = &file_stream.stream; | 140 | const in = &file_stream.stream; |
| 141 | try self.in_file.seekTo(file_offset); | ||
| 142 | |||
| 143 | // Find the correct DebugDirectoryEntry, and where its data is stored. | ||
| 144 | // It can be in any section. | ||
| 145 | const debug_dir_entry_count = debug_dir.size / @sizeOf(DebugDirectoryEntry); | ||
| 146 | var i: u32 = 0; | ||
| 147 | blk: while (i < debug_dir_entry_count) : (i += 1) { | ||
| 148 | const debug_dir_entry = try in.readStruct(DebugDirectoryEntry); | ||
| 149 | if (debug_dir_entry.type == IMAGE_DEBUG_TYPE_CODEVIEW) { | ||
| 150 | for (self.sections.toSlice()) |*section| { | ||
| 151 | const section_start = section.header.virtual_address; | ||
| 152 | const section_size = section.header.misc.virtual_size; | ||
| 153 | const rva = debug_dir_entry.address_of_raw_data; | ||
| 154 | const offset = rva - section_start; | ||
| 155 | if (section_start <= rva and offset < section_size and debug_dir_entry.size_of_data <= section_size - offset) { | ||
| 156 | try self.in_file.seekTo(section.header.pointer_to_raw_data + offset); | ||
| 157 | break :blk; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 141 | 162 | ||
| 142 | var cv_signature: [4]u8 = undefined; // CodeView signature | 163 | var cv_signature: [4]u8 = undefined; // CodeView signature |
| 143 | try in.readNoEof(cv_signature[0..]); | 164 | try in.readNoEof(cv_signature[0..]); |
| ... | @@ -149,7 +170,7 @@ pub const Coff = struct { | ... | @@ -149,7 +170,7 @@ pub const Coff = struct { |
| 149 | 170 | ||
| 150 | // Finally read the null-terminated string. | 171 | // Finally read the null-terminated string. |
| 151 | var byte = try in.readByte(); | 172 | var byte = try in.readByte(); |
| 152 | var i: usize = 0; | 173 | i = 0; |
| 153 | while (byte != 0 and i < buffer.len) : (i += 1) { | 174 | while (byte != 0 and i < buffer.len) : (i += 1) { |
| 154 | buffer[i] = byte; | 175 | buffer[i] = byte; |
| 155 | byte = try in.readByte(); | 176 | byte = try in.readByte(); |
| ... | @@ -178,7 +199,7 @@ pub const Coff = struct { | ... | @@ -178,7 +199,7 @@ pub const Coff = struct { |
| 178 | try self.sections.append(Section{ | 199 | try self.sections.append(Section{ |
| 179 | .header = SectionHeader{ | 200 | .header = SectionHeader{ |
| 180 | .name = name, | 201 | .name = name, |
| 181 | .misc = SectionHeader.Misc{ .physical_address = try in.readIntLittle(u32) }, | 202 | .misc = SectionHeader.Misc{ .virtual_size = try in.readIntLittle(u32) }, |
| 182 | .virtual_address = try in.readIntLittle(u32), | 203 | .virtual_address = try in.readIntLittle(u32), |
| 183 | .size_of_raw_data = try in.readIntLittle(u32), | 204 | .size_of_raw_data = try in.readIntLittle(u32), |
| 184 | .pointer_to_raw_data = try in.readIntLittle(u32), | 205 | .pointer_to_raw_data = try in.readIntLittle(u32), |
| ... | @@ -222,6 +243,17 @@ const OptionalHeader = struct { | ... | @@ -222,6 +243,17 @@ const OptionalHeader = struct { |
| 222 | data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, | 243 | data_directory: [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]DataDirectory, |
| 223 | }; | 244 | }; |
| 224 | 245 | ||
| 246 | const DebugDirectoryEntry = packed struct { | ||
| 247 | characteristiccs: u32, | ||
| 248 | time_date_stamp: u32, | ||
| 249 | major_version: u16, | ||
| 250 | minor_version: u16, | ||
| 251 | @"type": u32, | ||
| 252 | size_of_data: u32, | ||
| 253 | address_of_raw_data: u32, | ||
| 254 | pointer_to_raw_data: u32, | ||
| 255 | }; | ||
| 256 | |||
| 225 | pub const Section = struct { | 257 | pub const Section = struct { |
| 226 | header: SectionHeader, | 258 | header: SectionHeader, |
| 227 | }; | 259 | }; |
std/debug.zig+10-3| ... | @@ -375,7 +375,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres | ... | @@ -375,7 +375,7 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres |
| 375 | const obj_basename = fs.path.basename(mod.obj_file_name); | 375 | const obj_basename = fs.path.basename(mod.obj_file_name); |
| 376 | 376 | ||
| 377 | var symbol_i: usize = 0; | 377 | var symbol_i: usize = 0; |
| 378 | const symbol_name = while (symbol_i != mod.symbols.len) { | 378 | const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { |
| 379 | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); | 379 | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); |
| 380 | if (prefix.RecordLen < 2) | 380 | if (prefix.RecordLen < 2) |
| 381 | return error.InvalidDebugInfo; | 381 | return error.InvalidDebugInfo; |
| ... | @@ -858,8 +858,10 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -858,8 +858,10 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { |
| 858 | const age = try pdb_stream.stream.readIntLittle(u32); | 858 | const age = try pdb_stream.stream.readIntLittle(u32); |
| 859 | var guid: [16]u8 = undefined; | 859 | var guid: [16]u8 = undefined; |
| 860 | try pdb_stream.stream.readNoEof(guid[0..]); | 860 | try pdb_stream.stream.readNoEof(guid[0..]); |
| 861 | if (version != 20000404) // VC70, only value observed by LLVM team | ||
| 862 | return error.UnknownPDBVersion; | ||
| 861 | if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) | 863 | if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) |
| 862 | return error.InvalidDebugInfo; | 864 | return error.PDBMismatch; |
| 863 | // We validated the executable and pdb match. | 865 | // We validated the executable and pdb match. |
| 864 | 866 | ||
| 865 | const string_table_index = str_tab_index: { | 867 | const string_table_index = str_tab_index: { |
| ... | @@ -903,13 +905,18 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { | ... | @@ -903,13 +905,18 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { |
| 903 | return error.MissingDebugInfo; | 905 | return error.MissingDebugInfo; |
| 904 | }; | 906 | }; |
| 905 | 907 | ||
| 906 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; | 908 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo; |
| 907 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; | 909 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; |
| 908 | 910 | ||
| 909 | const dbi = di.pdb.dbi; | 911 | const dbi = di.pdb.dbi; |
| 910 | 912 | ||
| 911 | // Dbi Header | 913 | // Dbi Header |
| 912 | const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader); | 914 | const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader); |
| 915 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team | ||
| 916 | return error.UnknownPDBVersion; | ||
| 917 | if (dbi_stream_header.Age != age) | ||
| 918 | return error.UnmatchingPDB; | ||
| 919 | |||
| 913 | const mod_info_size = dbi_stream_header.ModInfoSize; | 920 | const mod_info_size = dbi_stream_header.ModInfoSize; |
| 914 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | 921 | const section_contrib_size = dbi_stream_header.SectionContributionSize; |
| 915 | 922 |
std/os.zig+16| ... | @@ -2053,6 +2053,22 @@ pub fn accessC(path: [*]const u8, mode: u32) AccessError!void { | ... | @@ -2053,6 +2053,22 @@ pub fn accessC(path: [*]const u8, mode: u32) AccessError!void { |
| 2053 | } | 2053 | } |
| 2054 | } | 2054 | } |
| 2055 | 2055 | ||
| 2056 | /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. | ||
| 2057 | /// Otherwise use `access` or `accessC`. | ||
| 2058 | /// TODO currently this ignores `mode`. | ||
| 2059 | pub fn accessW(path: [*]const u16, mode: u32) windows.GetFileAttributesError!void { | ||
| 2060 | const ret = try windows.GetFileAttributesW(path); | ||
| 2061 | if (ret != windows.INVALID_FILE_ATTRIBUTES) { | ||
| 2062 | return; | ||
| 2063 | } | ||
| 2064 | switch (windows.kernel32.GetLastError()) { | ||
| 2065 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | ||
| 2066 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 2067 | windows.ERROR.ACCESS_DENIED => return error.PermissionDenied, | ||
| 2068 | else => |err| return windows.unexpectedError(err), | ||
| 2069 | } | ||
| 2070 | } | ||
| 2071 | |||
| 2056 | pub const PipeError = error{ | 2072 | pub const PipeError = error{ |
| 2057 | SystemFdQuotaExceeded, | 2073 | SystemFdQuotaExceeded, |
| 2058 | ProcessFdQuotaExceeded, | 2074 | ProcessFdQuotaExceeded, |
std/pdb.zig+73-44| ... | @@ -499,45 +499,78 @@ const Msf = struct { | ... | @@ -499,45 +499,78 @@ const Msf = struct { |
| 499 | 499 | ||
| 500 | const superblock = try in.readStruct(SuperBlock); | 500 | const superblock = try in.readStruct(SuperBlock); |
| 501 | 501 | ||
| 502 | // Sanity checks | ||
| 502 | if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic)) | 503 | if (!mem.eql(u8, superblock.FileMagic, SuperBlock.file_magic)) |
| 503 | return error.InvalidDebugInfo; | 504 | return error.InvalidDebugInfo; |
| 504 | 505 | if (superblock.FreeBlockMapBlock != 1 and superblock.FreeBlockMapBlock != 2) | |
| 506 | return error.InvalidDebugInfo; | ||
| 507 | if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos()) | ||
| 508 | return error.InvalidDebugInfo; | ||
| 505 | switch (superblock.BlockSize) { | 509 | switch (superblock.BlockSize) { |
| 506 | // llvm only supports 4096 but we can handle any of these values | 510 | // llvm only supports 4096 but we can handle any of these values |
| 507 | 512, 1024, 2048, 4096 => {}, | 511 | 512, 1024, 2048, 4096 => {}, |
| 508 | else => return error.InvalidDebugInfo, | 512 | else => return error.InvalidDebugInfo, |
| 509 | } | 513 | } |
| 510 | 514 | ||
| 511 | if (superblock.NumBlocks * superblock.BlockSize != try file.getEndPos()) | 515 | const dir_block_count = blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize); |
| 512 | return error.InvalidDebugInfo; | 516 | if (dir_block_count > superblock.BlockSize / @sizeOf(u32)) |
| 517 | return error.UnhandledBigDirectoryStream; // cf. BlockMapAddr comment. | ||
| 513 | 518 | ||
| 514 | self.directory = try MsfStream.init( | 519 | try file.seekTo(superblock.BlockSize * superblock.BlockMapAddr); |
| 520 | var dir_blocks = try allocator.alloc(u32, dir_block_count); | ||
| 521 | for (dir_blocks) |*b| { | ||
| 522 | b.* = try in.readIntLittle(u32); | ||
| 523 | } | ||
| 524 | self.directory = MsfStream.init( | ||
| 515 | superblock.BlockSize, | 525 | superblock.BlockSize, |
| 516 | blockCountFromSize(superblock.NumDirectoryBytes, superblock.BlockSize), | ||
| 517 | superblock.BlockSize * superblock.BlockMapAddr, | ||
| 518 | file, | 526 | file, |
| 519 | allocator, | 527 | dir_blocks, |
| 520 | ); | 528 | ); |
| 521 | 529 | ||
| 530 | const begin = self.directory.pos; | ||
| 522 | const stream_count = try self.directory.stream.readIntLittle(u32); | 531 | const stream_count = try self.directory.stream.readIntLittle(u32); |
| 523 | |||
| 524 | const stream_sizes = try allocator.alloc(u32, stream_count); | 532 | const stream_sizes = try allocator.alloc(u32, stream_count); |
| 525 | for (stream_sizes) |*s| { | 533 | defer allocator.free(stream_sizes); |
| 534 | |||
| 535 | // Microsoft's implementation uses u32(-1) for inexistant streams. | ||
| 536 | // These streams are not used, but still participate in the file | ||
| 537 | // and must be taken into account when resolving stream indices. | ||
| 538 | const Nil = 0xFFFFFFFF; | ||
| 539 | for (stream_sizes) |*s, i| { | ||
| 526 | const size = try self.directory.stream.readIntLittle(u32); | 540 | const size = try self.directory.stream.readIntLittle(u32); |
| 527 | s.* = blockCountFromSize(size, superblock.BlockSize); | 541 | s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize); |
| 528 | } | 542 | } |
| 529 | 543 | ||
| 530 | self.streams = try allocator.alloc(MsfStream, stream_count); | 544 | self.streams = try allocator.alloc(MsfStream, stream_count); |
| 531 | for (self.streams) |*stream, i| { | 545 | for (self.streams) |*stream, i| { |
| 532 | stream.* = try MsfStream.init( | 546 | const size = stream_sizes[i]; |
| 533 | superblock.BlockSize, | 547 | if (size == 0) { |
| 534 | stream_sizes[i], | 548 | stream.* = MsfStream{ |
| 535 | // MsfStream.init expects the file to be at the part where it reads [N]u32 | 549 | .blocks = [_]u32{}, |
| 536 | try file.getPos(), | 550 | }; |
| 537 | file, | 551 | } else { |
| 538 | allocator, | 552 | var blocks = try allocator.alloc(u32, size); |
| 539 | ); | 553 | var j: u32 = 0; |
| 554 | while (j < size) : (j += 1) { | ||
| 555 | const block_id = try self.directory.stream.readIntLittle(u32); | ||
| 556 | const n = (block_id % superblock.BlockSize); | ||
| 557 | // 0 is for SuperBlock, 1 and 2 for FPMs. | ||
| 558 | if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.BlockSize > try file.getEndPos()) | ||
| 559 | return error.InvalidBlockIndex; | ||
| 560 | blocks[j] = block_id; | ||
| 561 | } | ||
| 562 | |||
| 563 | stream.* = MsfStream.init( | ||
| 564 | superblock.BlockSize, | ||
| 565 | file, | ||
| 566 | blocks, | ||
| 567 | ); | ||
| 568 | } | ||
| 540 | } | 569 | } |
| 570 | |||
| 571 | const end = self.directory.pos; | ||
| 572 | if (end - begin != superblock.NumDirectoryBytes) | ||
| 573 | return error.InvalidStreamDirectory; | ||
| 541 | } | 574 | } |
| 542 | }; | 575 | }; |
| 543 | 576 | ||
| ... | @@ -574,7 +607,6 @@ const SuperBlock = packed struct { | ... | @@ -574,7 +607,6 @@ const SuperBlock = packed struct { |
| 574 | NumDirectoryBytes: u32, | 607 | NumDirectoryBytes: u32, |
| 575 | 608 | ||
| 576 | Unknown: u32, | 609 | Unknown: u32, |
| 577 | |||
| 578 | /// The index of a block within the MSF file. At this block is an array of | 610 | /// The index of a block within the MSF file. At this block is an array of |
| 579 | /// ulittle32_t’s listing the blocks that the stream directory resides on. | 611 | /// ulittle32_t’s listing the blocks that the stream directory resides on. |
| 580 | /// For large MSF files, the stream directory (which describes the block | 612 | /// For large MSF files, the stream directory (which describes the block |
| ... | @@ -584,45 +616,41 @@ const SuperBlock = packed struct { | ... | @@ -584,45 +616,41 @@ const SuperBlock = packed struct { |
| 584 | /// and the stream directory itself can be stitched together accordingly. | 616 | /// and the stream directory itself can be stitched together accordingly. |
| 585 | /// The number of ulittle32_t’s in this array is given by | 617 | /// The number of ulittle32_t’s in this array is given by |
| 586 | /// ceil(NumDirectoryBytes / BlockSize). | 618 | /// ceil(NumDirectoryBytes / BlockSize). |
| 619 | // Note: microsoft-pdb code actually suggests this is a variable-length | ||
| 620 | // array. If the indices of blocks occupied by the Stream Directory didn't | ||
| 621 | // fit in one page, there would be other u32 following it. | ||
| 622 | // This would mean the Stream Directory is bigger than BlockSize / sizeof(u32) | ||
| 623 | // blocks. We're not even close to this with a 1GB pdb file, and LLVM didn't | ||
| 624 | // implement it so we're kind of safe making this assumption for now. | ||
| 587 | BlockMapAddr: u32, | 625 | BlockMapAddr: u32, |
| 588 | }; | 626 | }; |
| 589 | 627 | ||
| 590 | const MsfStream = struct { | 628 | const MsfStream = struct { |
| 591 | in_file: File, | 629 | in_file: File = undefined, |
| 592 | pos: u64, | 630 | pos: u64 = undefined, |
| 593 | blocks: []u32, | 631 | blocks: []u32 = undefined, |
| 594 | block_size: u32, | 632 | block_size: u32 = undefined, |
| 595 | 633 | ||
| 596 | /// Implementation of InStream trait for Pdb.MsfStream | 634 | /// Implementation of InStream trait for Pdb.MsfStream |
| 597 | stream: Stream, | 635 | stream: Stream = undefined, |
| 598 | 636 | ||
| 599 | pub const Error = @typeOf(read).ReturnType.ErrorSet; | 637 | pub const Error = @typeOf(read).ReturnType.ErrorSet; |
| 600 | pub const Stream = io.InStream(Error); | 638 | pub const Stream = io.InStream(Error); |
| 601 | 639 | ||
| 602 | fn init(block_size: u32, block_count: u32, pos: u64, file: File, allocator: *mem.Allocator) !MsfStream { | 640 | fn init(block_size: u32, file: File, blocks: []u32) MsfStream { |
| 603 | var stream = MsfStream{ | 641 | const stream = MsfStream{ |
| 604 | .in_file = file, | 642 | .in_file = file, |
| 605 | .pos = 0, | 643 | .pos = 0, |
| 606 | .blocks = try allocator.alloc(u32, block_count), | 644 | .blocks = blocks, |
| 607 | .block_size = block_size, | 645 | .block_size = block_size, |
| 608 | .stream = Stream{ .readFn = readFn }, | 646 | .stream = Stream{ .readFn = readFn }, |
| 609 | }; | 647 | }; |
| 610 | 648 | ||
| 611 | var file_stream = file.inStream(); | ||
| 612 | const in = &file_stream.stream; | ||
| 613 | try file.seekTo(pos); | ||
| 614 | |||
| 615 | var i: u32 = 0; | ||
| 616 | while (i < block_count) : (i += 1) { | ||
| 617 | stream.blocks[i] = try in.readIntLittle(u32); | ||
| 618 | } | ||
| 619 | |||
| 620 | return stream; | 649 | return stream; |
| 621 | } | 650 | } |
| 622 | 651 | ||
| 623 | fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 { | 652 | fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 { |
| 624 | var list = ArrayList(u8).init(allocator); | 653 | var list = ArrayList(u8).init(allocator); |
| 625 | defer list.deinit(); | ||
| 626 | while (true) { | 654 | while (true) { |
| 627 | const byte = try self.stream.readByte(); | 655 | const byte = try self.stream.readByte(); |
| 628 | if (byte == 0) { | 656 | if (byte == 0) { |
| ... | @@ -642,11 +670,12 @@ const MsfStream = struct { | ... | @@ -642,11 +670,12 @@ const MsfStream = struct { |
| 642 | const in = &file_stream.stream; | 670 | const in = &file_stream.stream; |
| 643 | 671 | ||
| 644 | var size: usize = 0; | 672 | var size: usize = 0; |
| 645 | for (buffer) |*byte| { | 673 | var rem_buffer = buffer; |
| 646 | byte.* = try in.readByte(); | 674 | while (size < buffer.len) { |
| 647 | 675 | const size_to_read = math.min(self.block_size - offset, rem_buffer.len); | |
| 648 | offset += 1; | 676 | size += try in.read(rem_buffer[0..size_to_read]); |
| 649 | size += 1; | 677 | rem_buffer = buffer[size..]; |
| 678 | offset += size_to_read; | ||
| 650 | 679 | ||
| 651 | // If we're at the end of a block, go to the next one. | 680 | // If we're at the end of a block, go to the next one. |
| 652 | if (offset == self.block_size) { | 681 | if (offset == self.block_size) { |
| ... | @@ -657,8 +686,8 @@ const MsfStream = struct { | ... | @@ -657,8 +686,8 @@ const MsfStream = struct { |
| 657 | } | 686 | } |
| 658 | } | 687 | } |
| 659 | 688 | ||
| 660 | self.pos += size; | 689 | self.pos += buffer.len; |
| 661 | return size; | 690 | return buffer.len; |
| 662 | } | 691 | } |
| 663 | 692 | ||
| 664 | fn seekBy(self: *MsfStream, len: i64) !void { | 693 | fn seekBy(self: *MsfStream, len: i64) !void { |